From 5b72ca51c6a8369ac9fddcf74424a17554725bfb Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Fri, 20 May 2022 12:52:58 -0700 Subject: [PATCH] Dockerfile and docker-compose.yml (#36) --- .gitignore | 3 ++ Makefile | 8 ++++ docker/Dockerfile | 37 +++++++++++++++++ docker/docker-compose.yml | 16 ++++++++ docker/root/defaults/config.yaml | 53 +++++++++++++++++++++++++ docker/root/defaults/subscriptions.yaml | 7 ++++ docker/root/etc/cont-init.d/30-config | 11 +++++ examples/kodi_music_videos_config.yaml | 36 ++++++++++------- setup.cfg | 9 +++++ setup.py | 4 ++ src/ytdl_sub/cli/main_args_parser.py | 6 +-- src/ytdl_sub/main.py | 9 ++++- 12 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 Makefile create mode 100644 docker/Dockerfile create mode 100644 docker/docker-compose.yml create mode 100644 docker/root/defaults/config.yaml create mode 100644 docker/root/defaults/subscriptions.yaml create mode 100644 docker/root/etc/cont-init.d/30-config create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 5a4d370b..8c83760e 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,6 @@ dmypy.json *.DS_Store .idea/ + +docker/*.whl +docker/root/*.whl diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f5ec2086 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ + +wheel: + python setup.py bdist_wheel +docker: wheel + cp dist/*.whl docker/root/ + sudo docker build --no-cache -t ytdl-sub:0.1 docker/ + +.PHONY: wheel docker diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..47d8e234 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,37 @@ +FROM ghcr.io/linuxserver/baseimage-alpine:3.15 + +############################################################################### +# YTDL-SUB INSTALL + +COPY root/ / +RUN apk update && \ + apk add \ + vim \ + gcc \ + g++ \ + make \ + libffi-dev \ + openssl-dev && \ + apk del \ + python3 \ + py3-pip && \ + apk add --repository=http://dl-3.alpinelinux.org/alpine/edge/main/ \ + python3=3.10.4-r0 \ + py3-setuptools=59.4.0-r0 && \ + apk add --repository=http://dl-3.alpinelinux.org/alpine/edge/community/ \ + py3-pip=22.0.4-r0 && \ + mkdir -p /config && \ + pip install --no-cache-dir ytdl_sub-*.whl && \ + rm ytdl_sub-*.whl + +############################################################################### +# CONTAINER CONFIGS + +ENV EDITOR="nano" \ +HOME="/config" + +VOLUME /config + + + + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 00000000..a305aeed --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,16 @@ +version: "2.1" +services: + ytdl-sub: + image: ytdl-sub:latest + container_name: ytdl-sub + environment: + - PUID=1000 + - PGID=1000 + - TZ=America/Los_Angeles + volumes: + - :/config + - :/tv_shows # optional + - :/movies # optional + - :/music_videos # optional + - :/music # optional + restart: unless-stopped diff --git a/docker/root/defaults/config.yaml b/docker/root/defaults/config.yaml new file mode 100644 index 00000000..4b3a4135 --- /dev/null +++ b/docker/root/defaults/config.yaml @@ -0,0 +1,53 @@ +configuration: + working_directory: '/tmp/ytdl-sub-downloads' + +# All example presets are included in this default config.yaml, +# feel free to change it as you see fit. For a detailed explanation +# of the examples, see https://ytdl-sub.readthedocs.io/en/latest/examples.html +presets: + + ############################################################################# + # Downloads single videos and stores them as Kodi/Jellyfin/Emby music videos, + # which all live in a single directory. + yt_music_video: + youtube: + download_strategy: "video" + + ytdl_options: + ignoreerrors: True + + output_options: + output_directory: "{music_video_directory}" + file_name: "{music_video_name}.{ext}" + thumbnail_name: "{music_video_name}.jpg" + + convert_thumbnail: + to: "jpg" + + nfo_tags: + nfo_name: "{music_video_name}.nfo" + nfo_root: "musicvideo" + tags: + artist: "{artist}" + title: "{title}" + album: "Music Videos" + year: "{upload_year}" + + overrides: + music_video_directory: "/music_videos" + music_video_name: "{sanitized_artist} - {sanitized_title}" + # artist: # FILL THIS OUT IN THE SUBSCRIPTION + + ############################################################################# + # Downloads playlists and stores all videos as Kodi/Jellyfin/Emby music + # videos, which all live in a single directory. + yt_music_video_playlist: + preset: "yt_music_video" + youtube: + download_strategy: "playlist" + + output_options: + maintain_download_archive: True + + ytdl_options: + break_on_existing: True diff --git a/docker/root/defaults/subscriptions.yaml b/docker/root/defaults/subscriptions.yaml new file mode 100644 index 00000000..b6012c5c --- /dev/null +++ b/docker/root/defaults/subscriptions.yaml @@ -0,0 +1,7 @@ +rammstein_music_videos: + preset: "yt_music_video_playlist" + youtube: + playlist_id: "PLVTLbc6i-h_iuhdwUfuPDLFLXG2QQnz-x" + overrides: + artist: "Rammstein" + diff --git a/docker/root/etc/cont-init.d/30-config b/docker/root/etc/cont-init.d/30-config new file mode 100644 index 00000000..cc58f75d --- /dev/null +++ b/docker/root/etc/cont-init.d/30-config @@ -0,0 +1,11 @@ +#!/usr/bin/with-contenv bash + +# copy config +[[ ! -e /config/config.yaml ]] && \ + cp /defaults/config.yaml /config/config.yaml +[[ ! -e /config/subscriptions.yaml ]] && \ + cp /defaults/subscriptions.yaml /config/subscriptions.yaml + +# permissions +chown -R abc:abc \ + /config diff --git a/examples/kodi_music_videos_config.yaml b/examples/kodi_music_videos_config.yaml index 6eb7aac8..ece44f76 100644 --- a/examples/kodi_music_videos_config.yaml +++ b/examples/kodi_music_videos_config.yaml @@ -16,12 +16,12 @@ configuration: working_directory: '.ytdl-sub-downloads' presets: - yt_music_video_playlist: - # Youtube playlists are our source/download strategy. However, this - # can be overwritten to download music videos from a 'channel' or a - # single 'video' + yt_music_video: + # A single YouTube video is our source/download strategy. However, this + # can be overwritten to download music videos from a "playlist", as we + # will see in a preset below youtube: - download_strategy: "playlist" + download_strategy: "video" # For advanced YTDL users only. # It is wise to leave ignoreerrors=True to avoid errors for things @@ -32,16 +32,10 @@ presets: # For each video downloaded, set the file and thumbnail name here. # We set both with {music_video_name}, which is a variable we define in # the overrides section further below to represent consistent naming format. - # - # Another field worth mentioning is maintain_download_archive=True. This - # is generally a good thing to enable with playlists because it will - # store previously downloaded video ids to tell YTDL not to re-download - # them on a successive invocation. output_options: - output_directory: "{music_video_directory}" + output_directory: "{music_video_directory}/{sanitized_artist}" file_name: "{music_video_name}.{ext}" thumbnail_name: "{music_video_name}.jpg" - maintain_download_archive: True # Always convert the video thumbnail to jpg # TODO: always convert all thumbnails to jpg in code @@ -70,7 +64,19 @@ presets: # It is not always ideal to download all of an artist's music videos. # Maybe you only like one song of theirs. We can reuse our preset above # to download a single video instead. - yt_music_video: - preset: "yt_music_video_playlist" + yt_music_video_playlist: + preset: "yt_music_video" youtube: - download_strategy: "video" + download_strategy: "playlist" + + # Setting maintain_download_archive=True is generally a good thing to enable + # with playlists and channels because it will store previously downloaded + # video ids to tell YTDL not to re-download them on a successive invocation. + output_options: + maintain_download_archive: True + + # Setting break_on_existing=True is also a good thing anytime you are using + # a download archive, because it will tell the downloader to stop trying + # to download videos that have already been downloaded. + ytdl_options: + break_on_existing: True diff --git a/setup.cfg b/setup.cfg index 8883e44d..145b8682 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,8 @@ console_scripts = [options] package_dir = = src +packages=find: + install_requires = argparse==1.4.0 dicttoxml==1.7.4 @@ -30,6 +32,9 @@ install_requires = PyYAML==6.0 yt-dlp==2022.4.8 +[options.packages.find] +where=src + [options.extras_require] test = coverage[toml]==6.3.2 @@ -42,3 +47,7 @@ lint = docs = sphinx==4.5.0 sphinx-rtd-theme==1.0.0 +build = + setuptools + wheel + diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..7f1a1763 --- /dev/null +++ b/setup.py @@ -0,0 +1,4 @@ +from setuptools import setup + +if __name__ == "__main__": + setup() diff --git a/src/ytdl_sub/cli/main_args_parser.py b/src/ytdl_sub/cli/main_args_parser.py index 310ddf30..fc626e0d 100644 --- a/src/ytdl_sub/cli/main_args_parser.py +++ b/src/ytdl_sub/cli/main_args_parser.py @@ -3,7 +3,7 @@ import argparse ################################################################################################### # GLOBAL PARSER parser = argparse.ArgumentParser( - description="YoutubeDL-Subscribe: Download and organize your favorite media hassle-free." + description="ytdl-sub: Automate download and adding metadata with YoutubeDL" ) parser.add_argument( "-c", @@ -21,8 +21,8 @@ subscription_parser.add_argument( "subscription_paths", metavar="SUBPATH", nargs="*", - help="path to subscription files, uses config.yaml if not provided", - default="config.yaml", + help="path to subscription files, uses subscriptions.yaml if not provided", + default="subscriptions.yaml", ) ################################################################################################### # DOWNLOAD PARSER diff --git a/src/ytdl_sub/main.py b/src/ytdl_sub/main.py index c68e546e..9b346432 100644 --- a/src/ytdl_sub/main.py +++ b/src/ytdl_sub/main.py @@ -59,7 +59,14 @@ def _download_subscription_from_cli(config: ConfigFile, extra_args: List[str]) - def main(): - """Entrypoint for ytdl-subscribe""" + """ + Entrypoint for ytdl-subscribe + """ + # If no args are provided, print help and exit + if len(sys.argv) < 2: + parser.print_help() + return + args, extra_args = parser.parse_known_args() config: ConfigFile = ConfigFile.from_file_path(args.config)