diff --git a/Dockerfile b/Dockerfile index 09c0e90..d220d8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,8 @@ FROM ubuntu:xenial ADD https://github.com/just-containers/s6-overlay/releases/download/v2.0.0.1/s6-overlay-amd64.tar.gz /tmp/ -RUN tar xzf /tmp/s6-overlay-amd64.tar.gz -C / --exclude='./bin' && tar xzf /tmp/s6-overlay-amd64.tar.gz -C /usr ./bin +RUN tar xzf /tmp/s6-overlay-amd64.tar.gz -C / && \ + rm /tmp/s6-overlay-amd64.tar.gz RUN useradd apps @@ -17,7 +18,7 @@ RUN apt-get update && \ RUN add-apt-repository ppa:mc3man/xerus-media && \ curl -sL https://deb.nodesource.com/setup_14.x | bash - && \ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ - echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list + echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list RUN apt-get update && \ apt-get install -yqq \ @@ -35,13 +36,12 @@ WORKDIR /app/ RUN npm install && \ npm audit fix - COPY . /app/ - RUN apt-get purge curl -y && \ apt-get clean -y && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* +ENTRYPOINT ["/init"] CMD ["/app/entrypoint.sh"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd43fa4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Omar Trigui + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile index 047d22f..45af38d 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,15 @@ -### Boilerplate & repository setup {{{1 - SHELL := bash .DELETE_ON_ERROR: .SHELLFLAGS := -eu -o pipefail -c MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules -IMAGE := omartrigui/url-to-video +IMAGE := cipheredbytes/url-to-video .PHONY: docker.build docker.build: docker build -t "${IMAGE}" . - docker images | grep "${IMAGE}" + docker image inspect "${IMAGE}" --format='{{.Size}}' | numfmt --to=iec-i --format='%10f' .PHONY: format.js @@ -22,16 +20,16 @@ format.js: .PHONY: docker.run docker.run: docker run --rm \ - -v "/tmp:/output" \ - -e URL=https://www.youtube.com/watch?v=PBYKqvDK8d8 \ - -e DURATION=20 \ - -e OUTPUT_FILENAME=forever_tracey.mp4 \ - "${IMAGE}" + -v /tmp:/recordings/ \ + -e URL=https://www.youtube.com/watch?v=PBYKqvDK8d8 \ + -e DURATION=20 \ + -e OUTPUT_FILENAME=forever_tracey.mp4 \ + "${IMAGE}" .PHONY: docker.push docker.push: docker push "${IMAGE}" -.PHONY: kubectl.run -kubectl.run: - kubectl create -f k8s/job.yaml +.PHONY: k8s.run +k8s.run: + kubectl create -f ./k8s/job.yaml diff --git a/README.md b/README.md index 18d2afc..b135477 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,50 @@ ## Url-to-Video ## Overview -url-to-video generator allows you to create a video from html source. +Url-to-Video generator allows you to create a video from html source. ## Features -* Full **Docker** integration (Docker based). -* K8s Job deployment. -* **Xvfb** in-memory display server for UNIX-like operating system that enables you to run graphical applications without a display.. +* Full **Docker** integration (based on [s6-overlay](https://github.com/just-containers/s6-overlay) hypervisor). +* **Xvfb** in-memory display server for UNIX-like operating system that enables you to run graphical applications without a display. * **Ffmpeg** a complete, cross-platform solution to record, convert and stream audio and video. -* **Google chrome** a cross-platform web browser -* **Node.js®** is a JavaScript runtime built on Chrome's V8 JavaScript engine. -* **Node.js®** is a JavaScript runtime built on Chrome's V8 JavaScript engine. +* **Google chrome** a cross-platform web browser. +* **Node.js®** a JavaScript runtime built on Chrome's V8 JavaScript engine. +* **Pulseaudio** a sound server that provides a number of features on top of the low-level audio interface ALSA on Linux. +## Comparison with the existing solutions +Most of the existing open source solution render web pages badly and they mainly lack audio support. +This solution rised to mitigate these issues. +## How to test it +```bash +docker run --rm \ + -v /tmp/:/recordings/ \ + -e URL=https://www.youtube.com/watch?v=PBYKqvDK8d8 \ + -e DURATION=20 \ + -e OUTPUT_FILENAME=forever_tracey.mp4 \ + cipheredbytes/url-to-video +``` -## How to test it +You will notice a new file created in the following destination [/tmp/forever_tracey.mp4](https://github.com/OmarTrigui/url-to-video/raw/master/samples/forever_tracey.mp4). -Go to the directory where you want to create your project and run: +## Development + +Go to the project directory and run the following command: ```bash +git https://github.com/OmarTrigui/url-to-video.git && cd url-to-video/ make docker.build docker.run ``` -You will notice a new file created in the following destination `/tmp/forever_tracey.mp4` - - ## TODO list -- [ ] Support pub/sub pattern (SQS queue) -- [ ] Add K8s deployments / docker compose +- [ ] Add E2E tests +- [ ] Tweak recording performance +- [ ] Add Pub/Sub support (either [SQS](https://aws.amazon.com/sqs/) or [Kafka](https://kafka.apache.org/)) -## Calling all contributors! -Are you eager to use your skills to do good and aid humanity? We are building a very ambitious software and your contribution could make a huge difference. +## Contributing +Contributions are always welcome, whether adding/suggesting new features, bug fixes, +documenting new file formats or simply editing some grammar. \ No newline at end of file diff --git a/contributors.md b/contributors.md new file mode 100644 index 0000000..4f208df --- /dev/null +++ b/contributors.md @@ -0,0 +1,4 @@ +# Contributors to html-to-video +Includes all git commit authors. Aliases are GitHub user names. + +* OmarTrigui \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index f25c6fb..c62cef0 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -20,13 +20,11 @@ launch_xvfb() { fi fi - # Set defaults if the user did not specify envs. export DISPLAY=${XVFB_DISPLAY:-:1} local screen=${XVFB_SCREEN:-0} local resolution=${XVFB_RESOLUTION:-1280x720x24} local timeout=${XVFB_TIMEOUT:-5} - # Start and wait for either Xvfb to be fully up or we hit the timeout. Xvfb "${DISPLAY}" -screen "${screen}" "${resolution}" & local loopCount=0 until xdpyinfo -display "${DISPLAY}" > /dev/null 2>&1 @@ -45,12 +43,10 @@ launch_pulseaudio(){ pulseaudio -D --exit-idle-time=-1 & } - log_i() { log "[INFO] ${@}" } - log_e() { log "[ERROR] ${@}" } diff --git a/k8s/job.yaml b/k8s/job.yaml index 15ce34e..14a7fdc 100644 --- a/k8s/job.yaml +++ b/k8s/job.yaml @@ -1,13 +1,16 @@ apiVersion: batch/v1 kind: Job metadata: - name: url2video + name: url-to-video spec: template: spec: containers: - - name: url2video - image: omartrigui/url-to-video + - name: url-to-video + image: cipheredbytes/url-to-video + volumeMounts: + - name: recordings + mountPath: /recordings/ env: - name: URL value: "https://www.youtube.com/watch?v=PBYKqvDK8d8" @@ -16,4 +19,8 @@ spec: - name: OUTPUT_FILENAME value: "forever_tracey.mp4" restartPolicy: Never - backoffLimit: 2aa + volumes: + - name: recordings + persistentVolumeClaim: + claimName: recordings-pv-claim + backoffLimit: 2 diff --git a/main.js b/main.js index 7b9b6e8..4534dd8 100644 --- a/main.js +++ b/main.js @@ -50,38 +50,41 @@ function getStartRecordingCommand () { '-strict -2 ' + '-ar 44100 ' + `-t ${process.env.DURATION} ` + - `/output/${process.env.OUTPUT_FILENAME}` + `/recordings/${process.env.OUTPUT_FILENAME}` } -async function init () { - let client - try { - exec(getStartChromeCommand()) +async function startChrome () { + exec(getStartChromeCommand()) - await waitOn(opts) + await waitOn(opts) - client = await CDP() + const client = await CDP() - const { - Network, - Page - } = client + const { Network, Page } = client - Network.requestWillBeSent((params) => { - console.log(params.request.url) - }) + Network.requestWillBeSent((params) => { + console.log(`Requested URL: ${params.request.url}`) + }) - await Network.enable() - await Page.enable() - await Page.navigate({ - url: process.env.URL - }) - await Page.loadEventFired() + await Network.enable() + await Page.enable() + await Page.navigate({ + url: process.env.URL + }) + await Page.loadEventFired() - console.log('All assets are loaded') - await exec(getStartRecordingCommand()) - console.log('Recording is completed') + console.log('All assets are loaded') +} +async function fireRecorder () { + await exec(getStartRecordingCommand()) + console.log('Recording completed') +} + +async function init () { + try { + await startChrome() + await fireRecorder() } catch (err) { console.error(err) } finally { diff --git a/package.json b/package.json index 5ad94be..aa3cb53 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "author": "", - "license": "ISC", + "author": "Omar Trigui", + "license": "MIT", "dependencies": { "chrome-remote-interface": "^0.28.2", "wait-on": "^5.0.1" diff --git a/samples/forever_tracey.mp4 b/samples/forever_tracey.mp4 new file mode 100644 index 0000000..e07ba2e Binary files /dev/null and b/samples/forever_tracey.mp4 differ