├── .dockerignore ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── markdown.yml ├── .gitignore ├── .markdownlint.json ├── Dockerfile ├── LICENSE ├── docker-compose.yml ├── entrypoint.sh ├── readme.md └── title.png /.dockerignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .git 3 | docker-compose.yml 4 | *.md 5 | readme 6 | title.png -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @qdm12 -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [open source license of this project](../LICENSE). 4 | 5 | ## Submitting a pull request 6 | 7 | 1. [Fork](https://github.com/qdm12/youtube-dl-docker/fork) and clone the repository 8 | 1. Create a new branch `git checkout -b my-branch-name` 9 | 1. Modify the code 10 | 1. Ensure the docker build succeeds `docker build .` 11 | 1. Commit your modifications 12 | 1. Push to your fork and [submit a pull request](https://github.com/qdm12/youtube-dl-docker/compare) 13 | 14 | ## Resources 15 | 16 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 17 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [qdm12] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | release: 4 | types: 5 | - published 6 | push: 7 | branches: 8 | - master 9 | paths: 10 | - .github/workflows/ci.yml 11 | - cmd/** 12 | - internal/** 13 | - pkg/** 14 | - .dockerignore 15 | - .golangci.yml 16 | - Dockerfile 17 | - go.mod 18 | - go.sum 19 | pull_request: 20 | paths: 21 | - .github/workflows/ci.yml 22 | - cmd/** 23 | - internal/** 24 | - pkg/** 25 | - .dockerignore 26 | - .golangci.yml 27 | - Dockerfile 28 | - go.mod 29 | - go.sum 30 | 31 | jobs: 32 | verify: 33 | runs-on: ubuntu-latest 34 | permissions: 35 | actions: read 36 | contents: read 37 | env: 38 | DOCKER_BUILDKIT: "1" 39 | steps: 40 | - uses: actions/checkout@v3 41 | 42 | - uses: reviewdog/action-misspell@v1 43 | with: 44 | locale: "US" 45 | level: error 46 | exclude: | 47 | ./internal/storage/servers.json 48 | *.md 49 | 50 | - name: Build final image 51 | run: docker build -t final-image . 52 | 53 | publish: 54 | if: | 55 | github.repository == 'qdm12/youtube-dl-docker' && 56 | ( 57 | github.event_name == 'push' || 58 | github.event_name == 'release' || 59 | (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') 60 | ) 61 | needs: [verify] 62 | permissions: 63 | actions: read 64 | contents: read 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v3 68 | 69 | # extract metadata (tags, labels) for Docker 70 | # https://github.com/docker/metadata-action 71 | - name: Extract Docker metadata 72 | id: meta 73 | uses: docker/metadata-action@v4 74 | with: 75 | flavor: | 76 | latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} 77 | images: | 78 | qmcgaw/youtube-dl-alpine 79 | tags: | 80 | type=ref,event=pr 81 | type=semver,pattern={{major}}.{{minor}}.{{patch}} 82 | type=semver,pattern={{major}}.{{minor}} 83 | type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }} 84 | type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} 85 | 86 | - uses: docker/setup-qemu-action@v2 87 | - uses: docker/setup-buildx-action@v2 88 | 89 | - uses: docker/login-action@v2 90 | with: 91 | username: qmcgaw 92 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 93 | 94 | - name: Short commit 95 | id: shortcommit 96 | run: echo "::set-output name=value::$(git rev-parse --short HEAD)" 97 | 98 | - name: Build and push final image 99 | uses: docker/build-push-action@v4.1.1 100 | with: 101 | platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7 102 | labels: ${{ steps.meta.outputs.labels }} 103 | build-args: | 104 | CREATED=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} 105 | COMMIT=${{ steps.shortcommit.outputs.value }} 106 | VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} 107 | tags: ${{ steps.meta.outputs.tags }} 108 | push: true 109 | -------------------------------------------------------------------------------- /.github/workflows/markdown.yml: -------------------------------------------------------------------------------- 1 | name: Markdown 2 | on: 3 | push: 4 | branches: 5 | - master 6 | paths: 7 | - readme.md 8 | - .github/workflows/markdown.yml 9 | jobs: 10 | markdown: 11 | if: github.repository == 'qdm12/youtube-dl-docker' 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: DavidAnson/markdownlint-cli2-action@v11 20 | with: 21 | globs: "**.md" 22 | config: .markdownlint.json 23 | 24 | - uses: reviewdog/action-misspell@v1 25 | with: 26 | locale: "US" 27 | level: error 28 | pattern: | 29 | *.md 30 | 31 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 32 | with: 33 | use-quiet-mode: yes 34 | 35 | - uses: peter-evans/dockerhub-description@v3 36 | if: github.repository == 'qdm12/youtube-dl-docker' 37 | with: 38 | username: qmcgaw 39 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 40 | repository: qmcgaw/youtube-dl-alpine 41 | short-description: Youtube-dl in an Alpine container 42 | readme-filepath: readme.md 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false 3 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG ALPINE_VERSION=3.18 2 | 3 | FROM alpine:${ALPINE_VERSION} 4 | ARG BUILD_DATE 5 | ARG VCS_REF 6 | ARG YOUTUBE_DL_OVERWRITE= 7 | LABEL \ 8 | org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \ 9 | org.opencontainers.image.created=$BUILD_DATE \ 10 | org.opencontainers.image.revision=$VCS_REF \ 11 | org.opencontainers.image.version="${YOUTUBE_DL_OVERWRITE}" \ 12 | org.opencontainers.image.url="https://github.com/qdm12/youtube-dl-docker" \ 13 | org.opencontainers.image.documentation="https://github.com/qdm12/youtube-dl-docker/blob/master/README.md" \ 14 | org.opencontainers.image.source="https://github.com/qdm12/youtube-dl-docker" \ 15 | org.opencontainers.image.title="youtube-dl-docker" \ 16 | org.opencontainers.image.description="Download with youtube-dl using command line arguments or configuration files" 17 | HEALTHCHECK --interval=10m --timeout=10s --retries=1 CMD [ "$(wget -qO- https://duckduckgo.com 2>/dev/null)" != "" ] || exit 1 18 | ENV AUTOUPDATE=no \ 19 | GOTIFYURL= \ 20 | GOTIFYTOKEN= 21 | ENTRYPOINT ["/entrypoint.sh"] 22 | CMD ["-h"] 23 | COPY entrypoint.sh / 24 | RUN apk add -q --progress --update --no-cache ca-certificates ffmpeg python3 && \ 25 | rm -rf /var/cache/apk/* 26 | RUN ln -s /usr/bin/python3 /usr/local/bin/python && \ 27 | LATEST=${YOUTUBE_DL_OVERWRITE:-latest} && \ 28 | wget "https://github.com/yt-dlp/yt-dlp/releases/$LATEST/download/yt-dlp" -O /usr/local/bin/youtube-dl && \ 29 | chown 1000 /entrypoint.sh /usr/local/bin/youtube-dl && \ 30 | chmod 555 /entrypoint.sh && \ 31 | chmod 777 /usr/local/bin/youtube-dl 32 | USER 1000 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Quentin McGaw 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 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | youtube-dl: 4 | image: qmcgaw/youtube-dl-alpine 5 | container_name: youtube-dl 6 | network_mode: bridge 7 | volumes: 8 | - ./downloads:/downloads 9 | command: | 10 | https://www.youtube.com/watch?v=HagVnWAeGcM 11 | -o "/downloads/%(title)s-%(duration)s.%(ext)s" 12 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | printf " =========================================\n" 4 | printf " =========================================\n" 5 | printf " ========== YOUTUBE-DL CONTAINER =========\n" 6 | printf " =========================================\n" 7 | printf " =========================================\n" 8 | printf " == by github.com/qdm12 - Quentin McGaw ==\n\n" 9 | 10 | exitIfNotIn(){ 11 | # $1 is the name of the variable to check - not the variable itself 12 | # $2 is a string of comma separated possible values 13 | var="$(eval echo "\$$1")" 14 | for value in ${2//,/ } 15 | do 16 | if [ "$var" = "$value" ]; then 17 | return 0 18 | fi 19 | done 20 | printf "Environment variable $1=$var must be one of the following: " 21 | for value in ${2//,/ } 22 | do 23 | printf "$value " 24 | done 25 | printf "\n" 26 | exit 1 27 | } 28 | 29 | exitOnError(){ 30 | # $1 must be set to $? 31 | status=$1 32 | message=$2 33 | [ "$message" != "" ] || message="Error!" 34 | if [ $status != 0 ]; then 35 | printf "$message (status $status)\n" 36 | exit $status 37 | fi 38 | } 39 | 40 | if [ ! -z "$GOTIFYURL" ]; then 41 | wget -qO- "$GOTIFYURL/version" &> /dev/null 42 | if [ $? != 0 ]; then 43 | printf "WARNING: Cannot communicate with Gotify server ($?)\n" 44 | fi 45 | fi 46 | exitIfNotIn AUTOUPDATE "yes,no" 47 | [ "$AUTOUPDATE" = "no" ] || youtube-dl -U 48 | YTDL_VERSION=$(youtube-dl --version) 49 | PYTHON_VERSION=$(python3 --version 2>&1 | cut -d " " -f 2) 50 | FFMPEG_VERSION=$(ffmpeg -version | head -n 1 | grep -oE 'version [0-9]+\.[0-9]+(\.[0-9]+){0,1}' | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+){0,1}') 51 | printf "Youtube-dl version: $YTDL_VERSION" 52 | printf "\nPython version: $PYTHON_VERSION" 53 | printf "\nFFMPEG version: $FFMPEG_VERSION" 54 | printf "\n\n" 55 | test -w "/downloads" 56 | if [ $? != 0 ]; then 57 | echo "WARNING: /downloads is not writable, you might want to fix its ownership and/or permissions" 58 | fi 59 | youtube-dl "$@" 60 | status=$? 61 | if [ ! -z "$GOTIFYURL" ]; then 62 | wget \ 63 | --header="X-Gotify-Key: $GOTIFYTOKEN" \ 64 | --header "accept: application/json" \ 65 | --header "Content-Type: application/json" \ 66 | --post-data "{ \"message\": \"Youtube-DL `hostname` finished ($status)\", \"priority\": 1, \"title\": \"Youtube-DL\"}" \ 67 | "$GOTIFYURL/message" &> /dev/null 68 | fi 69 | printf "\n =========================================\n" 70 | printf " Youtube-dl exit with status $status\n" 71 | printf " =========================================\n" 72 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Docker Youtube-DL Alpine 2 | 3 | *Download with [**yt-dlp**](https://github.com/yt-dlp/yt-dlp) using command line arguments or configuration files* 4 | 5 | [![Docker Youtube-DL](https://github.com/qdm12/youtube-dl-docker/raw/master/title.png)](https://hub.docker.com/r/qmcgaw/youtube-dl-alpine/) 6 | 7 | [![Build status](https://github.com/qdm12/youtube-dl-docker/actions/workflows/ci.yml/badge.svg)](https://github.com/qdm12/youtube-dl-docker/actions/workflows/ci.yml/badge.svg) 8 | [![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/youtube-dl-alpine.svg)](https://hub.docker.com/r/qmcgaw/youtube-dl-alpine) 9 | [![Docker Stars](https://img.shields.io/docker/stars/qmcgaw/youtube-dl-alpine.svg)](https://hub.docker.com/r/qmcgaw/youtube-dl-alpine) 10 | [![Docker Automated](https://img.shields.io/docker/automated/qmcgaw/youtube-dl-alpine.svg)](https://hub.docker.com/r/qmcgaw/youtube-dl-alpine) 11 | 12 | [![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/youtube-dl-docker.svg)](https://github.com/qdm12/youtube-dl-docker/issues) 13 | [![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/youtube-dl-docker.svg)](https://github.com/qdm12/youtube-dl-docker/issues) 14 | [![GitHub issues](https://img.shields.io/github/issues/qdm12/youtube-dl-docker.svg)](https://github.com/qdm12/youtube-dl-docker/issues) 15 | 16 | [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/qmcgaw) 17 | 18 | ## Features 19 | 20 | - Works with command line arguments to *youtube-dl* 21 | - Compatible with AMD64, 386, ARM v6/v7/v8 CPU architectures 22 | - Small Docker image based on 23 | - [Alpine 3.18](https://alpinelinux.org) 24 | - [Youtube-dl](https://github.com/yt-dlp/yt-dlp) 25 | - [ffmpeg 6.0](https://pkgs.alpinelinux.org/package/v3.18/community/x86_64/ffmpeg) 26 | - [Ca-Certificates](https://pkgs.alpinelinux.org/package/v3.18/main/x86_64/ca-certificates) 27 | - [Python 3.11.4](https://pkgs.alpinelinux.org/package/v3.18/main/x86_64/python3) 28 | - The container updates youtube-dl at launch if `-e AUTOUPDATE=yes` 29 | - Docker healthcheck downloading `https://duckduckgo.com` with `wget` every 10 minutes 30 | - You can receive a notification on your Android device through Gotify when the *youtube-dl* has finished 31 | 32 | ## Setup 33 | 34 | 1. Run the container with 35 | 36 | ```bash 37 | docker run -d -v $(pwd)/yourdir:/downloads qmcgaw/youtube-dl-alpine \ 38 | https://www.youtube.com/watch?v=HagVnWAeGcM \ 39 | -o "/downloads/%(title)s.%(ext)s" 40 | ``` 41 | 42 | or use [docker-compose.yml](https://github.com/qdm12/youtube-dl-docker/blob/master/docker-compose.yml) with 43 | 44 | ```bash 45 | docker-compose up -d 46 | ``` 47 | 48 | 1. See the [youtube-dl command line options](https://github.com/rg3/youtube-dl/blob/master/README.md#options) 49 | 1. By default the container runs as user ID `1000` in order to not run as `root`. 50 | If you encounter permission issues with your bind mounted `yourdir` directory, either: 51 | - Change the ownership and permission of your directory to match user id `1000`: 52 | 53 | ```sh 54 | chown 1000 yourdir && chmod 700 yourdir 55 | ``` 56 | 57 | - Run the container with the user ID owning `yourdir`, for example with the Docker flag `--user=1030`. 58 | 59 | ### Environment variables 60 | 61 | | Environment variable | Default | Description | 62 | | --- | --- | --- | 63 | | `AUTOUPDATE` | `no` | Updates youtube-dl to the latest version at launch | 64 | | `GOTIFYURL` | | Gotify server URL address (i.e. `http://192.168.1.5:8000` or `https://a.com/gotify`) | 65 | | `GOTIFYTOKEN` | | Gotify server Token | 66 | 67 | ### Downloads directory permission issues 68 | 69 | You can either: 70 | 71 | - Change the ownership and permissions of `./downloads` on your host with: 72 | 73 | ```sh 74 | chown 1000 -R ./downloads 75 | chmod 700 ./downloads 76 | chmod -R 600 ./downloads/* 77 | ``` 78 | 79 | - Launch the container as `root` user using `--user=root` 80 | 81 | ### Authenticate with Netrc file 82 | 83 | To authenticate you can bind mount a [`.netrc` file](https://github.com/ytdl-org/youtube-dl/blob/master/README.md#authentication-with-netrc-file). 84 | 85 | You should, on your host, set the right ownership and permissions as follow: 86 | 87 | ```sh 88 | chown 1000:1000 .netrc 89 | chmod 600 .netrc 90 | ``` 91 | 92 | And then bind mount it with `-v "$(pwd)/.netrc:/.netrc:ro"` and youtube-dl will pick it up. 93 | 94 | ## TODOs 95 | 96 | - [ ] Healthcheck to check ydl logs 97 | - [ ] Colors in terminal 98 | -------------------------------------------------------------------------------- /title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdm12/youtube-dl-docker/fe764f1d6703a72a53cba43e2cecfa40245f5184/title.png --------------------------------------------------------------------------------