# ytdl-sub
## Automate downloading and metadata generation with YoutubeDL.
[<img src="https://img.shields.io/badge/readthedocs-link-blue?logo=readthedocs">](https://ytdl-sub.readthedocs.io/en/latest/index.html)
[<img src="https://img.shields.io/discord/994270357957648404?logo=Discord">](https://discord.gg/v8j9RAHb4k)
[![codecov](https://img.shields.io/codecov/c/github/jmbannon/ytdl-sub)](https://app.codecov.io/gh/jmbannon/ytdl-sub)
![Code Qaulity](https://img.shields.io/badge/pylint-10%2F10-brightgreen)
![Checks](https://img.shields.io/github/checks-status/jmbannon/ytdl-sub/master)
![License](https://img.shields.io/github/license/jmbannon/ytdl-sub?color=blue)

`ytdl-sub` is a command-line tool that downloads media via 
[yt-dlp](https://github.com/yt-dlp/yt-dlp)
and prepares it for your favorite media player
([Kodi](https://github.com/xbmc/xbmc), 
[Jellyfin](https://github.com/jellyfin/jellyfin), 
[Plex](https://github.com/plexinc/pms-docker),
[Emby](https://github.com/plexinc/pms-docker),
modern music players).

We recognize that everyone stores their 
media differently. Our approach for file and metadata formatting is to provide
maximum flexibility while maintaining simplicity.

### YouTube Channels as TV Shows in Jellyfin
![jellyfin](https://user-images.githubusercontent.com/10107080/182677243-b4184e51-9780-4094-bd40-ea4ff58555d0.PNG)

### Music Videos and Concerts in Jellyfin
![jelly_mv](https://user-images.githubusercontent.com/10107080/182677256-43aeb029-0c3f-4648-9fd2-352b9666b262.PNG)

### Music Videos and Concerts in Kodi
![kodi](https://user-images.githubusercontent.com/10107080/182677268-d1bf2ff0-9b9c-4a04-98ec-443a67ada734.png)

### SoundCloud Albums and Singles in MusicBee
![sc_mb](https://user-images.githubusercontent.com/10107080/182685415-06adf477-3dd3-475d-bbcd-53b0152b9f0a.PNG)


## How it Works
`ytdl-sub` uses YAML configs to define a layout for how you want media to look
after it is downloaded. See our
[walk-through guide](https://github.com/jmbannon/ytdl-sub/wiki)
on how to get started. Ready-to-use
[example configurations](https://github.com/jmbannon/ytdl-sub/tree/master/examples)
can be found here alongside our
[readthedocs](https://ytdl-sub.readthedocs.io/en/latest/config.html#)
for detailed information on config fields.


### Config
The `config.yaml` defines how our downloads will look. For this example, let us
download YouTube channels and generate metadata to look like TV shows using
ytdl-sub's prebuilt presets. No additional plugins or programs are needed for
Kodi, Jellyfin, Plex, or Emby to recognize your downloads. This can also be
used to download any yt-dlp supported URL, including YouTube playlists, Bitchute channels, etc.


```yaml
# Set the working directory which will be used to stage downloads
# before placing them in your desired output directory.
configuration:
  working_directory: '.ytdl-sub-downloads'

# Presets are where you create 'sub-configs' that can can be
# merged together to dictate what is downloaded, how to format it,
# and what metadata to generate.
presets:

  # Let us create a preset called `only_recent_videos` that will
  # only download recent videos in the last 2 months.
  only_recent_videos:

    # Use the `date_range` plugin to specify ytdl-sub to only
    # download videos after today MINUS {download_range}, which
    # is an override variable that we can alter per channel.
    date_range:
      after: "today-{download_range}"

    # Any yt-dlp argument can be passed via ytdl-sub. Let us set
    # yt-dlp's `break_on_reject` to True to stop downloading after
    # any video is rejected. Videos will be rejected if they are
    # uploaded after our {download_range}.
    ytdl_options:
      break_on_reject: True

    # Deletes any videos uploaded after {download_range}.
    output_options:
      keep_files_after: "today-{download_range}"

    # Set the override variable {download_range} to 2months.
    # This will serve as our default value. We can override
    # this per channel or in a child preset.
    overrides:
      download_range: "2months"

  ####################################################################

  # Now let us create a preset that downloads videos and formats
  # as TV shows.
  tv_show:

    # Presets can inherit all attributes from other presets. Our
    # `tv_show` preset will inherit these presets built into ytdl-sub.
    preset:
      # Let us specify all the TV show by date presets to support all
      # players. You only need to specify one, but this ensures
      # compatibility with all players.
      - "kodi_tv_show_by_date"
      - "jellyfin_tv_show_by_date"
      - "plex_tv_show_by_date"
      # Now we choose a preset that defines how our seasons and
      # episode numbers look.
      - "season_by_year__episode_by_month_day"

    # Set override variables that will be applicable to all downloads
    # in main presets.
    overrides:
      tv_show_directory: "/tv_shows"  # Replace with desired directory

```

### Subscriptions
The `subscriptions.yaml` file is where we define content to download using
presets in the `config.yaml`. Each subscription can overwrite any field used
in a preset.
```yaml
# The name of our subscription. Let us create one to download
# ALL of Rick A's videos
rick_a:
  # Inherit our `tv_show` preset we made above
  preset:
    - "tv_show"
  
  # Set override variables to set the channel URL and the
  # name we want to give the TV show.
  overrides:
    tv_show_name: "Rick A"
    url: "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw"

# Let us make another subscription that will only download Rick A's
# video's in the last 2 weeks.
rick_a_recent:
  # Inherit our `tv_show` AND `only_recent_videos` preset
  # Bottom-most presets take precedence.
  preset:
    - "tv_show"
    - "only_recent_videos"
  
  # Set override variables for this subscription. Modify the
  # `download_range` to only download and keep 2 weeks' worth
  # of videos.
  overrides:
    tv_show_name: "Rick A"
    url: "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw"
    download_range: "2weeks"

```
The download can now be performed using:
```shell
ytdl-sub sub subscriptions.yaml
```
To preview what your output files before doing any downloads, you can dry run using:
```shell
ytdl-sub --dry-run sub subscriptions.yaml
```

### One-time Download
There are things we will only want to download once and never again. Anything
you can define in a subscription can be defined using CLI arguments. This
example is equivalent to the subscription example above:
```shell
ytdl-sub dl \
    --preset "tv_show" \
    --overrides.tv_show_name "Rick A" \
    --overrides.url: "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw"
```

#### Download Aliases
In the `config.yaml`, we can define aliases to make `dl` commands shorter.
```yaml
configuration:
  dl_aliases:
    tv: "--preset tv_show"
    name: "--overrides.tv_show_name"
    url: "--overrides.url"
    
```
The above command can now be shortened to
```shell
ytdl-sub dl \
    --tv \
    --name "Rick A" \
    --url "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw"
```

### Output
After `ytdl-sub` runs, the end result will download and format the channel
files into something ready to be consumed by your favorite media player or
server.
```
/path/to/tv_shows/Rick Aß
  /Season 2021
    s2021.e0317 - Pattys Day Video-thumb.jpg
    s2021.e0317 - Pattys Day Video.mp4
    s2021.e0317 - Pattys Day Video.nfo
  /Season 2022
    s2022.e1225 - Merry Christmas-thumb.jpg
    s2022.e1225 - Merry Christmas.mp4
    s2022.e1225 - Merry Christmas.nfo
  poster.jpg
  fanart.jpg
  tvshow.nfo
```

### Beyond TV Shows
The above example made heavy-use of `ytdl-sub` prebuilt presets and hides many
features that are offered. `ytdl-sub` strives to support _any_ use case that first requires
a download via yt-dlp. Use `ytdl-sub` to download, format, and convert media for your media
player to recognize downloads as:
- Movies
- TV shows
  - From a single channel or playlist
  - From multiple channels or playlists
  - From individual videos
- Extracted audio as podcasts
- Music videos
- Music, including:
  - Individual songs
  - Albums
  - Discographies

## Installation

The ytdl-sub docker image uses
[LinuxServer's](https://www.linuxserver.io/)
[base alpine image](https://github.com/linuxserver/docker-baseimage-alpine).
It looks, feels, and operates like other LinuxServer images. This is the 
recommended way to use ytdl-sub.

The docker image is intended to be used as a console. For automating 
`subscriptions.yaml` downloads to pull new media, see
[this guide](https://ytdl-sub.readthedocs.io/en/latest/getting_started.html#setting-up-automated-downloads)
on how set up a cron job in the docker container.

### Docker Compose
```yaml
services:
  ytdl-sub:
    image: ghcr.io/jmbannon/ytdl-sub:latest
    container_name: ytdl-sub
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Los_Angeles
    volumes:
      - <path/to/ytdl-sub/config>:/config
      - <path/to/tv_shows>:/tv_shows # optional
      - <path/to/movies>:/movies # optional
      - <path/to/music_videos>:/music_videos # optional
      - <path/to/music>:/music # optional
    restart: unless-stopped
```
### Docker CLI
```commandline
docker run -d \
    --name=ytdl-sub \
    -e PUID=1000 \
    -e PGID=1000 \
    -e TZ=America/Los_Angeles \
    -v <path/to/ytdl-sub/config>:/config \
    -v <OPTIONAL/path/to/tv_shows>:/tv_shows \
    -v <OPTIONAL/path/to/movies>:/movies \
    -v <OPTIONAL/path/to/music_videos>:/music_videos \
    -v <OPTIONAL/path/to/music>:/music \
    --restart unless-stopped \
    ghcr.io/jmbannon/ytdl-sub:latest
```

### Building Docker Image Locally
Run `make docker` in the root directory of this repo to build the image. This
will build the python wheel and install it in the Dockerfile.

### Virtualenv
With a Python 3.10 virtual environment, you can clone and install the repo using
```commandline
git clone https://github.com/jmbannon/ytdl-sub.git
cd ytdl-sub

pip install -e .
```

## Contributing
There are many ways to contribute, even without coding. Please take a look in
our [Github Issues](https://github.com/jmbannon/ytdl-sub/issues) to ask
questions, submit a feature request, or pick up a bug.
