youtube-dl
34 |35 | Enter a video URL to download the video to the server. The URL can be 36 | from YouTube or 37 | any other supported site. The server will automatically download the highest quality version 42 | available. 43 |
44 | 82 |├── .github ├── dependabot.yml └── workflows │ └── docker-image.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── requirements.txt ├── templates └── index.html ├── youtube-dl-server.png └── youtube-dl-server.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'pip' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | tags: 8 | - 'v*' 9 | pull_request: 10 | branches: 11 | - 'main' 12 | 13 | jobs: 14 | docker: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | - name: Docker meta 20 | id: meta 21 | uses: docker/metadata-action@v5 22 | with: 23 | images: | 24 | kmb32123/youtube-dl-server 25 | tags: | 26 | type=ref,event=branch 27 | type=ref,event=pr 28 | type=semver,pattern={{version}} 29 | type=semver,pattern={{major}}.{{minor}} 30 | type=raw,value=latest,enable={{is_default_branch}} 31 | - name: Set up QEMU 32 | uses: docker/setup-qemu-action@v3 33 | - name: Set up Docker Buildx 34 | uses: docker/setup-buildx-action@v3 35 | - name: Login to Docker Hub 36 | if: github.event_name != 'pull_request' 37 | uses: docker/login-action@v3 38 | with: 39 | username: ${{ secrets.DOCKERHUB_USERNAME }} 40 | password: ${{ secrets.DOCKERHUB_TOKEN }} 41 | - name: Build and push 42 | uses: docker/build-push-action@v5 43 | with: 44 | context: . 45 | platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 46 | push: ${{ github.event_name != 'pull_request' }} 47 | tags: ${{ steps.meta.outputs.tags }} 48 | labels: ${{ steps.meta.outputs.labels }} 49 | cache-from: type=gha 50 | cache-to: type=gha,mode=max 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pdm 86 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 87 | #pdm.lock 88 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 89 | # in version control. 90 | # https://pdm.fming.dev/#use-with-ide 91 | .pdm.toml 92 | 93 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 94 | __pypackages__/ 95 | 96 | # Celery stuff 97 | celerybeat-schedule 98 | celerybeat.pid 99 | 100 | # SageMath parsed files 101 | *.sage.py 102 | 103 | # Environments 104 | .env 105 | .venv 106 | env/ 107 | venv/ 108 | ENV/ 109 | env.bak/ 110 | venv.bak/ 111 | 112 | # Spyder project settings 113 | .spyderproject 114 | .spyproject 115 | 116 | # Rope project settings 117 | .ropeproject 118 | 119 | # mkdocs documentation 120 | /site 121 | 122 | # mypy 123 | .mypy_cache/ 124 | .dmypy.json 125 | dmypy.json 126 | 127 | # Pyre type checker 128 | .pyre/ 129 | 130 | # pytype static type analyzer 131 | .pytype/ 132 | 133 | # Cython debug symbols 134 | cython_debug/ 135 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v3.2.0 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-toml 7 | - id: check-yaml 8 | - id: end-of-file-fixer 9 | - id: trailing-whitespace 10 | - repo: https://github.com/astral-sh/ruff-pre-commit 11 | rev: v0.3.4 12 | hooks: 13 | - id: ruff 14 | args: [--fix] 15 | - id: ruff-format 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.typeCheckingMode": "basic", 3 | "[python]": { 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll": "explicit", 7 | "source.organizeImports": "explicit" 8 | }, 9 | "editor.defaultFormatter": "charliermarsh.ruff" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # youtube-dl-server Dockerfile 3 | # 4 | # https://github.com/manbearwiz/youtube-dl-server-dockerfile 5 | # 6 | 7 | FROM python:alpine 8 | 9 | RUN apk add --no-cache \ 10 | ffmpeg \ 11 | tzdata 12 | 13 | RUN mkdir -p /usr/src/app 14 | WORKDIR /usr/src/app 15 | 16 | COPY requirements.txt /usr/src/app/ 17 | RUN apk --update-cache add --virtual build-dependencies gcc libc-dev make \ 18 | && pip install --no-cache-dir -r requirements.txt \ 19 | && apk del build-dependencies 20 | 21 | COPY . /usr/src/app 22 | 23 | EXPOSE 8080 24 | 25 | VOLUME ["/youtube-dl"] 26 | 27 | CMD ["uvicorn", "youtube-dl-server:app", "--host", "0.0.0.0", "--port", "8080"] 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kevin Brey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://hub.docker.com/r/kmb32123/youtube-dl-server/) 2 | [](https://hub.docker.com/r/kmb32123/youtube-dl-server/) 3 | [](https://raw.githubusercontent.com/manbearwiz/youtube-dl-server/master/LICENSE) 4 |  5 | 6 | # youtube-dl-server 7 | 8 | Very spartan Web and REST interface for downloading youtube videos onto a server. [`starlette`](https://github.com/encode/starlette) + [`yt-dlp`](https://github.com/yt-dlp/yt-dlp). 9 | 10 | ![screenshot][1] 11 | 12 | ## Running 13 | 14 | ### Docker CLI 15 | 16 | This example uses the docker run command to create the container to run the app. Here we also use host networking for simplicity. Also note the `-v` argument. This directory will be used to output the resulting videos 17 | 18 | ```shell 19 | docker run -d --net="host" --name youtube-dl -v /home/core/youtube-dl:/youtube-dl kmb32123/youtube-dl-server 20 | ``` 21 | 22 | ### Docker Compose 23 | 24 | This is an example service definition that could be put in `docker-compose.yml`. This service uses a VPN client container for its networking. 25 | 26 | ```yml 27 | youtube-dl: 28 | image: "kmb32123/youtube-dl-server" 29 | network_mode: "service:vpn" 30 | volumes: 31 | - /home/core/youtube-dl:/youtube-dl 32 | restart: always 33 | ``` 34 | 35 | ### Python 36 | 37 | If you have python ^3.6.0 installed in your PATH you can simply run like this, providing optional environment variable overrides inline. 38 | 39 | ```shell 40 | YDL_UPDATE_TIME=False python3 -m uvicorn youtube-dl-server:app --port 8123 41 | ``` 42 | 43 | In this example, `YDL_UPDATE_TIME=False` is the same as the command line option `--no-mtime`. 44 | 45 | ## Usage 46 | 47 | ### Start a download remotely 48 | 49 | Downloads can be triggered by supplying the `{{url}}` of the requested video through the Web UI or through the REST interface via curl, etc. 50 | 51 | #### HTML 52 | 53 | Just navigate to `http://{{host}}:8080/youtube-dl` and enter the requested `{{url}}`. 54 | 55 | #### Curl 56 | 57 | ```shell 58 | curl -X POST --data-urlencode "url={{url}}" http://{{host}}:8080/youtube-dl/q 59 | ``` 60 | 61 | #### Fetch 62 | 63 | ```javascript 64 | fetch(`http://${host}:8080/youtube-dl/q`, { 65 | method: "POST", 66 | body: new URLSearchParams({ 67 | url: url, 68 | format: "bestvideo" 69 | }), 70 | }); 71 | ``` 72 | 73 | #### Bookmarklet 74 | 75 | Add the following bookmarklet to your bookmark bar so you can conviently send the current page url to your youtube-dl-server instance. 76 | 77 | ```javascript 78 | javascript:!function(){fetch("http://${host}:8080/youtube-dl/q",{body:new URLSearchParams({url:window.location.href,format:"bestvideo"}),method:"POST"})}(); 79 | ``` 80 | 81 | ## Implementation 82 | 83 | The server uses [`starlette`](https://github.com/encode/starlette) for the web framework and [`youtube-dl`](https://github.com/rg3/youtube-dl) to handle the downloading. The integration with youtube-dl makes use of their [python api](https://github.com/rg3/youtube-dl#embedding-youtube-dl). 84 | 85 | This docker image is based on [`python:alpine`](https://registry.hub.docker.com/_/python/) and consequently [`alpine:3.8`](https://hub.docker.com/_/alpine/). 86 | 87 | [1]:youtube-dl-server.png 88 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | This service isn't intended to be exposed to the internet but if you find somehting that you think should be addressed, you can message me on twitter, [@manbearwiz](https://twitter.com/manbearwiz). 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiofiles==24.1.0 2 | Jinja2==3.1.6 3 | python-multipart==0.0.9 4 | starlette==0.43.0 5 | uvicorn[standard]==0.34.2; platform_machine == 'x86_64' 6 | uvicorn==0.34.0; platform_machine != 'x86_64' 7 | yt-dlp==2025.5.22 8 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 15 | 24 | 25 |35 | Enter a video URL to download the video to the server. The URL can be 36 | from YouTube or 37 | any other supported site. The server will automatically download the highest quality version 42 | available. 43 |
44 | 82 |