diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2125228f..00d2db55 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -102,11 +102,33 @@ jobs: source /opt/env/bin/activate coverage run -m pytest tests/e2e/soundcloud && coverage xml -o /opt/coverage/soundcloud/coverage.xml + test-bandcamp: + runs-on: ubuntu-22.04 + needs: test-build + permissions: + contents: read + + steps: + - uses: actions/checkout@v3 + + - name: Restore Python build cache + uses: actions/cache@v3 + with: + path: /opt/env + key: ${{github.sha}}-env + + - name: Run e2e soundcloud tests with coverage + run: | + sudo apt-get update + sudo apt-get install -y ffmpeg + source /opt/env/bin/activate + coverage run -m pytest tests/e2e/bandcamp && coverage xml -o /opt/coverage/bandcamp/coverage.xml + - name: Save coverage uses: actions/cache@v3 with: - path: /opt/coverage/soundcloud - key: ${{github.sha}}-coverage-soundcloud + path: /opt/coverage/bandcamp + key: ${{github.sha}}-coverage-bandcamp test-youtube: runs-on: ubuntu-22.04 @@ -169,6 +191,7 @@ jobs: needs: [ test-unit, test-soundcloud, + test-bandcamp, test-youtube, test-plugins ] @@ -188,6 +211,12 @@ jobs: path: /opt/coverage/soundcloud key: ${{github.sha}}-coverage-soundcloud + - name: Restore bandcamp test coverage + uses: actions/cache@v3 + with: + path: /opt/coverage/bandcamp + key: ${{github.sha}}-coverage-bandcamp + - name: Restore youtube test coverage uses: actions/cache@v3 with: @@ -204,4 +233,4 @@ jobs: uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} - files: /opt/coverage/unit/coverage.xml,/opt/coverage/soundcloud/coverage.xml,/opt/coverage/youtube/coverage.xml,/opt/coverage/plugins/coverage.xml \ No newline at end of file + files: /opt/coverage/unit/coverage.xml,/opt/coverage/soundcloud/coverage.xml,/opt/coverage/bandcamp/coverage.xml,/opt/coverage/youtube/coverage.xml,/opt/coverage/plugins/coverage.xml \ No newline at end of file diff --git a/examples/music_audio_config.yaml b/examples/music_audio_config.yaml new file mode 100644 index 00000000..70bc40fb --- /dev/null +++ b/examples/music_audio_config.yaml @@ -0,0 +1,195 @@ +# This config builds presets to download and format the following use-cases: +# - YouTube +# - Songs +# - From a single video +# - Albums +# - From a video, where each song is represented by chapters +# - From a playlist where each video is a song on the album +# - Discographies +# - From a channel or a channel's set of playlists +# - Bandcamp +# - Track, Album, Artist URL as a Discography +# - Soundcloud +# - Track, Album, Artist URL as a Discography +# +# All files will look like: +# +# music_directory/ +# Artist/ +# [2022] Some Single/ +# 01 - Some Single.mp3 +# folder.jpg +# [2023] Latest Album/ +# 01 - Track Title.mp3 +# 02 - Another Track.mp3 +# folder.jpg +# +# The idea is to format files under music_directory as `Artist/Album/Song`. Singles that do not +# belong to an album will be represented like albums with a single track. +# +# Each file will be properly tagged regardless of file extension (suppers flac, ogg, mp3, etc) +# and should show up nicely in music players that take advantage of tags. +configuration: + working_directory: '.ytdl-sub-downloads' + umask: "002" + +presets: + + # The `base` preset that represents how all files will be structured after downloading. + # Every other preset afterwards will inherit this preset. + base: + # Store all music under our music_directory (to be set as an override variable). + # Store each resulting file with its full track path, and treat thumbnails as the album covers. + # + # Maintain a download archive. This will produce a hidden file in your music directory + # containing an archive of all audio previously downloaded. This makes it so we do not + # re-download files we already have. + output_options: + output_directory: "{music_directory}" + file_name: "{track_full_path}" + thumbnail_name: "{album_cover_path}" + maintain_download_archive: True + + # Set break_on_existing to True. This will stop fetching any more metadata once we + # hit a video/audio that we already have downloaded. + ytdl_options: + break_on_existing: True + + # Extract any audio from files using this codec and quality. + audio_extract: + codec: "mp3" + quality: 320 + + # Set these music tags on every resulting audio file. + # It is recommended to keep most of this as-is, and use override + # variables to set them to be what you want. + music_tags: + tags: + artist: "{track_artist}" + artists: "{track_artist}" + albumartist: "{track_artist}" + albumartists: "{track_artist}" + title: "{track_title}" + album: "{track_album}" + track: "{track_number}" + tracktotal: "{track_total}" + year: "{track_year}" + genre: "{track_genre}" + + # For every configurable field, make it an override variable, + # so we can carefully tune different use-cases by only modifying override variables. + overrides: + # Track Overrides. By default, set overrides to make each song its own album + track_title: "{title}" + track_album: "{title}" + track_artist: "{channel}" + track_number: "1" + track_number_padded: "01" + track_total: "1" + track_year: "{upload_year}" + track_genre: "Unset" + # Filename Overrides + track_file_name: "{track_number_padded} - {track_title_sanitized}.{ext}" + album_file_name: "folder.{thumbnail_ext}" + # Directory Name Overrides + music_directory: "OVERRIDE THIS WITH YOUR MUSIC DIRECTORY" + artist_dir: "{track_artist_sanitized}" + album_dir: "[{track_year}] {track_album_sanitized}" + # Full Filepath Overrides + track_full_path: "{artist_dir}/{album_dir}/{track_file_name}" + album_cover_path: "{artist_dir}/{album_dir}/{album_file_name}" + +#################################################################################################### + + # Make the 'single` preset accept a single URL using the override variable 'url' + # Each audio file will reside in its own album. + single: + # Inherit from `base` + preset: "base" + + download: + download_strategy: "url" + url: "{url}" + +#################################################################################################### + + # Make the 'albums_from_playlists' preset format audio files to reside under albums, where + # each album is a playlist. If a file downloaded using this preset is not part of a playlist, + # it will default to how it'd look as a `single`. + albums_from_playlists: + # Inherit from single + preset: "single" + + # Override various track properties using playlist variables. + overrides: + track_album: "{playlist_title}" + track_number: "{playlist_index}" + track_number_padded: "{playlist_index_padded}" + track_total: "{playlist_count}" + track_year: "{playlist_max_upload_year}" + +#################################################################################################### + + # Make the 'albums_from_chapters' preset format audio files to reside under an album, where the + # video itself is an album containing all the songs in it represented by chapters. + albums_from_chapters: + # Inherit from single + preset: "single" + + # Embed chapters if present. If no chapters are present, allow parsing comments that contain + # timestamps to each song, and use those as chapters. + chapters: + embed_chapters: True + allow_chapters_from_comments: True + + # Split each file by its chapters. If a file does not have chapters, simply 'pass' on it and + # process the next audio/video file. + split_by_chapters: + when_no_chapters: "pass" + + # Override various track properties using chapter variables. + overrides: + track_title: "{chapter_title}" # Chapter title is the track title + track_album: "{title}" # Video's title is the album title + track_number: "{chapter_index}" + track_number_padded: "{chapter_index_padded}" + track_total: "{chapter_count}" + +#################################################################################################### + + # Make the 'soundcloud_discography' preset specially made for ripping SoundCloud artists. + # We will use the 'multi_url' approach to download both albums and non-album tracks + soundcloud_discography: + preset: "base" + + # Download using the multi_url strategy + download: + download_strategy: "multi_url" + urls: + # The first URL will be all the artist's tracks. + # Treat these as singles - an album with a single track + - url: "{sc_artist_url}/tracks" + variables: + sc_track_album: "{title}" + sc_track_number: "1" + sc_track_number_padded: "01" + sc_track_total: "1" + sc_track_year: "{upload_year}" + # Set the second URL to the artist's albums. If a track belongs to both + # to an album and tracks (in the URL above), it will resolve to this + # URL and include the album metadata we set below. + - url: "{sc_artist_url}/albums" + variables: + sc_track_album: "{playlist_title}" + sc_track_number: "{playlist_index}" + sc_track_number_padded: "{playlist_index_padded}" + sc_track_total: "{playlist_count}" + sc_track_year: "{playlist_max_upload_year}" + + # Override various track properties using playlist variables. + overrides: + track_album: "{sc_track_album}" + track_number: "{sc_track_number}" + track_number_padded: "{sc_track_number_padded}" + track_total: "{sc_track_total}" + track_year: "{sc_track_year}" \ No newline at end of file diff --git a/examples/music_audio_from_videos.yaml b/examples/music_audio_from_videos.yaml deleted file mode 100644 index 153a9034..00000000 --- a/examples/music_audio_from_videos.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# Extracts audio from YouTube videos, converts it to mp3, and adds audio tags to it. -configuration: - working_directory: '.ytdl-sub-downloads' - -presets: - song: - download: - download_strategy: "url" - url: "{url}" - - output_options: - output_directory: "{music_directory}" - file_name: "{custom_track_name_sanitized}.{ext}" - - audio_extract: - codec: "mp3" - quality: 128 - - music_tags: - embed_thumbnail: False # Set to True to embed album art - tags: - artist: "{custom_artist_name}" - artists: "{custom_artist_name}" - albumartist: "{custom_artist_name}" - albumartists: "{custom_artist_name}" - title: "{custom_track_name}" - album: "{custom_album_name}" - track: "{custom_track_number}" - year: "{upload_year}" - genre: "Unset" - - overrides: - music_directory: "/path/to/music" - custom_track_name: "{title}" - custom_album_name: "Singles" - custom_artist_name: "{channel}" - custom_track_number: "1" - - # TODO: make a playlist of individual songs into an album. Need playlist_title - song_playlist: - preset: "song" - overrides: - custom_track_number: "{playlist_index}" - - album_from_chapters: - preset: "song" - output_options: - file_name: "{custom_album_name_sanitized}/{chapter_index_padded} - {custom_track_name_sanitized}.{ext}" - thumbnail_name: "{custom_album_name_sanitized}/folder.{thumbnail_ext}" - - chapters: - embed_chapters: True - - split_by_chapters: - when_no_chapters: "pass" - - overrides: - custom_track_name: "{chapter_title}" - custom_album_name: "{title}" # Have the video name be the album - custom_track_number: "{chapter_index}" diff --git a/examples/music_audio_subscriptions.yaml b/examples/music_audio_subscriptions.yaml new file mode 100644 index 00000000..1c9f7861 --- /dev/null +++ b/examples/music_audio_subscriptions.yaml @@ -0,0 +1,138 @@ +# Define any number of subscriptions in a single file, or use multiple +# files to organize (i.e by use-case, website, genre, etc) +# +# Each example show-cases every use case: +# - YouTube +# - Songs +# - From a single video +# - Albums +# - From a video, where each song is represented by chapters +# - From a playlist where each video is a song on the album +# - Discographies +# - From a channel or a channel's set of playlists +# - Bandcamp +# - Track, Album, Artist URL as a Discography +# - Soundcloud +# - Track, Album, Artist URL as a Discography +# +#################################################################################################### +# YOUTUBE - PLAYLIST OF ALBUMS +# See https://www.youtube.com/playlist?list=PLBsm_SagFMmdWnCnrNtLjA9kzfrRkto4i +# +# Downloads the GameChops Albums' playlist. Each video in the playlist is an album with +# chapters representing songs. +game_chops: + preset: "albums_from_chapters" + + # Perform regex to extract title from the chapter's title to not include the track number. + # Configuring this hard is a bit overkill, but I'm a perfectionist :-) + regex: + from: + chapter_title: + match: + - "^\\d+\\.\\.(.*)" # Captures 'title' from '1..title' + capture_group_names: + - "captured_track_title" + capture_group_defaults: + - "{chapter_title}" + + # Explicitly set the URL, track artist, and genre. + # Override various track properties using the regex variables we extracted. + overrides: + url: "https://www.youtube.com/playlist?list=PLBsm_SagFMmdWnCnrNtLjA9kzfrRkto4i" + track_artist: "GameChops" + track_genre: "Lofi" + track_title: "{captured_track_title}" + + +#################################################################################################### +# YOUTUBE - MANY ALBUMS FROM MANY ARTISTS UPLOADED BY A CHANNEL +# See https://www.youtube.com/@heavymetalofeasternbloc +# +# Downloads the entire 'Heavy Metal from the Eastern Bloc' channel. Each video they upload +# is an album from Eastern Europe with chapters representing songs. +eastern_bloc: + # Inherit albums from chapters + preset: "albums_from_chapters" + + # Perform regex on many fields (title, description, chapter title) to extract as much + # metadata as possible + regex: + skip_if_match_fails: False # Error if regex match fails + from: + title: + match: + - "^(.*) - (.*) \\|\\|.*" # Captures artist, album from 'artist - album ||...' + - "^(.*) - (.*) \\[.*" # Captures artist, ablum from 'artist - album [...' + - "^(.*) - (.*)" # Captures artist, ablum from 'artist - album' + capture_group_names: + - "captured_track_artist" + - "captured_track_album" + description: + match: + - "Genre:\\s*(.*)\\s*\n(?:Rec.+|Year):\\s*.*(\\d{4})\\s*\\n" # Captures genre and recorded year + capture_group_names: + - "captured_track_genre" + - "captured_track_year" + chapter_title: + match: + - "^(?:\\d+\\.\\s*|)(.*)" # Captures title from '1. title' + capture_group_names: + - "captured_track_title" + capture_group_defaults: + - "{chapter_title}" + + # Explicitly set the URL. + # Override various track properties using the regex variables we extracted. + overrides: + url: "https://www.youtube.com/@heavymetalofeasternbloc" + track_artist: "{captured_track_artist}" + track_album: "{captured_track_album}" + track_title: "{captured_track_title}" + track_year: "{captured_track_year}" + track_genre: "Eastern Bloc {captured_track_genre}" + + +#################################################################################################### +# BANDCAMP - DISCOGRAPHY +# See https://emilyharpist.bandcamp.com/ +# +# Downloads the GameChops Albums' playlist. Each video in the playlist is an album with +# chapters representing songs. +emily_hopkins: + preset: "albums_from_playlists" + regex: + from: + title: + match: + - "^Emily Hopkins - (.*)" # Captures 'title' from 'Emily Hopkins - title' + capture_group_names: + - "captured_track_title" + capture_group_defaults: + - "{title}" + + # Explicitly set the URL, track artist, and genre. + # Override various track properties using the regex variables we extracted. + overrides: + url: "https://emilyharpist.bandcamp.com/" + track_artist: "Emily Hopkins" + track_genre: "Lofi" + track_title: "{captured_track_title}" + + +#################################################################################################### +# SOUNDCLOUD - DISCOGRAPHY +# See https://soundcloud.com/jessebannon +# +# Downloads my acoustic 'album' and various tracks from SoundCloud using the soundcloud +# discography preset. I'm not very good so keep your expectations low :-) +jmbannon: + # Inherit soundcloud_discography preset + preset: "soundcloud_discography" + + # Explicitly set the SoundCloud artist url, track artist, and genre. + overrides: + sc_artist_url: "sc_artist_url" + track_artist: "jmbannon" + track_genre: "Acoustic" + diff --git a/examples/soundcloud_discography_config.yaml b/examples/soundcloud_discography_config.yaml deleted file mode 100644 index cf7c33da..00000000 --- a/examples/soundcloud_discography_config.yaml +++ /dev/null @@ -1,88 +0,0 @@ -# This example shows how to download and format a Soundcloud artist's -# discography with tags and album art. We will configure this to make -# the output directory formatted as: -# -# /path/to/music -# /The Be Sharps -# /[2020] My first upload -# 01 - My first upload.mp3 -# folder.jpg -# /[2021] My first album -# 01 - Track one.mp3 -# 02 - Track two.mp3 -# folder.jpg -# /Another artist -# ... -# -configuration: - working_directory: '.ytdl-sub-downloads' - -presets: - soundcloud_discography: - # Download using the multi_url strategy - download: - download_strategy: "multi_url" - urls: - # The first URL will be all the artist's tracks. - # Treat these as singles - an album with a single track - - url: "{artist_url}/tracks" - variables: - track_number: "1" - track_number_padded: "01" - track_count: "1" - album: "{title}" - album_sanitized: "{title_sanitized}" - album_year: "{upload_year}" - # Set the second URL to the artist's albums. If a track belongs both - # to an album and tracks (in the URL above), it will resolve to this - # URL and include the album metadata we set below. - - url: "{artist_url}/albums" - variables: - track_number: "{playlist_index}" - track_number_padded: "{playlist_index_padded}" - track_count: "{playlist_count}" - album: "{playlist_title}" - album_sanitized: "{playlist_title_sanitized}" - album_year: "{playlist_max_upload_year}" - - # For advanced YTDL users only; set any YTDL parameter here. - # Let us set the format to download the best mp3. - ytdl_options: - format: 'bestaudio[ext=mp3]' - - # For each song downloaded, set the file and thumbnail name here. - # The output directory stores all artists in a shared {music_directory}. - # We store the audio file in its respective {album_directory_name} folder - # with the track number and title. The (thumbnail) album art is stored - # in the album folder. - # - # Set maintain_download_archive=True. This will tell YTDL not to - # re-download them on a successive invocation. - output_options: - output_directory: "{music_directory}" - file_name: "{album_path}/{track_file_name}.{ext}" - thumbnail_name: "{album_path}/folder.{thumbnail_ext}" - maintain_download_archive: True - - # For each song downloaded, populate the audio file with music tags. - # Tagging should work with most audio file formats. See - # https://ytdl-sub.readthedocs.io/en/latest/config.html#music-tags - # for more details. - music_tags: - tags: - artist: "{artist}" - albumartist: "{artist}" - title: "{title}" - album: "{album}" - track: "{track_number}" - year: "{album_year}" - genre: "Unset" - - # Overrides is a section where we can define our own variables, and use them in - # any other section. We define our music directory and album directory names here, - # which gets reused above for the audio file name and album art path. - overrides: - album_directory_name: "[{album_year}] {album_sanitized}" - track_file_name: "{track_number_padded} - {title_sanitized}" - album_path: "{artist_sanitized}/{album_directory_name}" - music_directory: "/path/to/music" \ No newline at end of file diff --git a/src/ytdl_sub/validators/url_validator.py b/src/ytdl_sub/validators/url_validator.py deleted file mode 100644 index 828c2e3f..00000000 --- a/src/ytdl_sub/validators/url_validator.py +++ /dev/null @@ -1,204 +0,0 @@ -from typing import Any -from typing import Optional -from urllib.parse import parse_qs -from urllib.parse import urlparse - -from ytdl_sub.validators.validators import StringValidator - - -class YoutubeVideoUrlValidator(StringValidator): - - _expected_value_type_name = "Youtube video url" - - @classmethod - def _get_video_id(cls, url: str) -> Optional[str]: - """ - Examples: - - https://youtu.be/SA2iWivDJiE - https://www.youtube.com/watch?v=_oPAwA_Udwc&feature=feedu - https://www.youtube.com/embed/SA2iWivDJiE - https://www.youtube.com/v/SA2iWivDJiE?version=3&hl=en_US - https://www.youtube.com/shorts/ucYmEqmlhFw - - Returns - ------- - The video id if it is parsed correctly. None if parsing fails - """ - # If the url doesn't contain youtube or youtu.be, assume it is invalid - if "youtube.com" not in url and "youtu.be" not in url: - return None - - # If https:// is not present, urlparse will not work - if not url.startswith("https://"): - url = f"https://{url}" - - query = urlparse(url) - if query.hostname in ("youtu.be", "www.youtu.be"): - return query.path[1:] - if query.hostname in ("youtube.com", "www.youtube.com"): - if query.path == "/watch": - parsed_q = parse_qs(query.query) - if "v" in parsed_q: - return parsed_q["v"][0] - if query.path.startswith(("/embed/", "/v/", "/shorts/")): - return query.path.split("/")[2] - - return None - - def __init__(self, name: str, value: Any): - super().__init__(name, value) - - self._video_id = self._get_video_id(value) - if not self._video_id: - raise self._validation_exception(f"'{value}' is not a valid Youtube video url.") - - @property - def video_url(self) -> str: - """ - Returns - ------- - Full video URL - """ - return f"https://youtube.com/watch?v={self._video_id}" - - -class YoutubePlaylistUrlValidator(StringValidator): - - _expected_value_type_name = "Youtube playlist url" - - @classmethod - def _get_playlist_id(cls, url: str) -> Optional[str]: - """ - Examples: - - https://www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc - """ - # If the url doesn't contain youtube, assume it is invalid - if "youtube.com" not in url: - return None - - # If https:// is not present, urlparse will not work - if not url.startswith("https://"): - url = f"https://{url}" - - query = urlparse(url) - if query.hostname in ("youtube.com", "www.youtube.com"): - if query.path == "/playlist": - parsed_q = parse_qs(query.query) - if "list" in parsed_q: - return parsed_q["list"][0] - - return None - - def __init__(self, name: str, value: Any): - super().__init__(name, value) - - self._playlist_id = self._get_playlist_id(value) - if not self._playlist_id: - raise self._validation_exception(f"'{value}' is not a valid Youtube playlist url.") - - @property - def playlist_url(self) -> str: - """ - Returns - ------- - Full playlist URL - """ - return f"https://youtube.com/playlist?list={self._playlist_id}" - - -class YoutubeChannelUrlValidator(StringValidator): - - _expected_value_type_name = "Youtube channel url" - - @classmethod - def _get_channel_url(cls, url: str) -> Optional[str]: - """ - Examples: - - https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw - - https://www.youtube.com/user/username - - https://www.youtube.com/c/channel_name - - https://youtube.com/channel_name - """ - # If the url doesn't contain youtube, assume it is invalid - if "youtube.com" not in url: - return None - - # If https:// is not present, urlparse will not work - if not url.startswith("https://"): - url = f"https://{url}" - - query = urlparse(url) - if query.hostname in ("youtube.com", "www.youtube.com"): - query_path_split = query.path.split("/") - if any(query.path.startswith(qpath) for qpath in ("/channel/", "/user/", "/c/")): - # Ensure there is "/path/id".split('/') = ['', 'path', 'id'] at least three - if len(query_path_split) < 3 or len(query_path_split[2]) == 0: - return None - return f"https://youtube.com{query.path}" - if len(query_path_split) == 2 and len(query_path_split[1]) > 0: - # For channels that do not feature a 'c' or 'channel' - # Ensure length of channel string is at least one - return f"https://youtube.com{query.path}" - - return None - - def __init__(self, name: str, value: Any): - super().__init__(name, value) - - self._channel_url = self._get_channel_url(value) - if not self._channel_url: - raise self._validation_exception(f"'{value}' is not a valid Youtube channel url.") - - @property - def channel_url(self) -> str: - """ - Returns - ------- - Full channel URL - """ - return self._channel_url - - -class SoundcloudUsernameUrlValidator(StringValidator): - - _expected_value_type_name = "Soundcloud username url" - - @classmethod - def _get_channel_url(cls, url: str) -> Optional[str]: - """ - Examples: - - https://www.soundcloud.com/artist_name - """ - # If the url doesn't contain soundcloud, assume it is invalid - if "soundcloud.com" not in url: - return None - - # If https:// is not present, urlparse will not work - if not url.startswith("https://"): - url = f"https://{url}" - - query = urlparse(url) - if query.hostname in ("soundcloud.com", "www.soundcloud.com"): - if len(query.path) > 2: # /artist_name - username = query.path[1:].split("/")[0] # strip extra paths or slashes - username = username.split("?")[0] # strip extra arguments - return f"https://soundcloud.com/{username}" - - return None - - def __init__(self, name: str, value: Any): - super().__init__(name, value) - - self._username_url = self._get_channel_url(value) - if not self._username_url: - raise self._validation_exception(f"'{value}' is not a valid Soundcloud username url.") - - @property - def username_url(self) -> str: - """ - Returns - ------- - Full artist URL - """ - return self._username_url diff --git a/tests/e2e/bandcamp/__init__.py b/tests/e2e/bandcamp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/e2e/bandcamp/test_bandcamp.py b/tests/e2e/bandcamp/test_bandcamp.py new file mode 100644 index 00000000..88cc6b3a --- /dev/null +++ b/tests/e2e/bandcamp/test_bandcamp.py @@ -0,0 +1,61 @@ +import pytest +from expected_download import assert_expected_downloads +from expected_transaction_log import assert_transaction_log_matches + +from ytdl_sub.subscriptions.subscription import Subscription + + +@pytest.fixture +def subscription_dict(output_directory): + return { + "preset": "albums_from_playlists", + "regex": { + "skip_if_match_fails": False, # Error if regex match fails + "from": { + "title": { + "match": ["^(.*) - (.*)"], # Captures 'title' from 'Emily Hopkins - title' + "capture_group_names": [ + "captured_track_artist", + "captured_track_title", + ], + } + }, + }, + "ytdl_options": { + "max_downloads": 15, + }, + "date_range": {"before": "20230101"}, + "overrides": { + "url": "https://funkypselicave.bandcamp.com/", + "track_title": "{captured_track_title}", + "track_artist": "{captured_track_artist}", + "music_directory": output_directory, + }, + } + + +class TestBandcamp: + @pytest.mark.parametrize("dry_run", [True, False]) + def test_download_artist_url( + self, + subscription_dict, + music_audio_config, + output_directory, + dry_run, + ): + discography_subscription = Subscription.from_dict( + preset_dict=subscription_dict, + preset_name="jb", + config=music_audio_config, + ) + transaction_log = discography_subscription.download(dry_run=dry_run) + assert_transaction_log_matches( + output_directory=output_directory, + transaction_log=transaction_log, + transaction_log_summary_file_name="bandcamp/test_artist_url.txt", + ) + assert_expected_downloads( + output_directory=output_directory, + dry_run=dry_run, + expected_download_summary_file_name="bandcamp/test_artist_url.json", + ) diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index aadbb1d8..1e387f0f 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -79,18 +79,10 @@ def channel_as_tv_show_config(working_directory) -> ConfigFile: ) -@pytest.fixture -def soundcloud_discography_config(working_directory) -> ConfigFile: - return _load_config( - config_path="examples/soundcloud_discography_config.yaml", - working_directory=working_directory, - ) - - @pytest.fixture() -def youtube_audio_config(working_directory) -> ConfigFile: +def music_audio_config(working_directory) -> ConfigFile: return _load_config( - config_path="examples/music_audio_from_videos.yaml", working_directory=working_directory + config_path="examples/music_audio_config.yaml", working_directory=working_directory ) diff --git a/tests/e2e/plugins/test_audio_extract.py b/tests/e2e/plugins/test_audio_extract.py index 19ef7ae2..1a3563c7 100644 --- a/tests/e2e/plugins/test_audio_extract.py +++ b/tests/e2e/plugins/test_audio_extract.py @@ -8,9 +8,7 @@ from ytdl_sub.subscriptions.subscription import Subscription @pytest.fixture def single_song_preset_dict(output_directory): return { - "preset": "song", - "download": {"url": "https://www.youtube.com/watch?v=2lAe1cqCOXo"}, - "output_options": {"output_directory": output_directory}, + "preset": "single", # test multi-tags "music_tags": {"embed_thumbnail": True, "tags": {"genres": ["multi_tag_1", "multi_tag_2"]}}, # download the worst format so it is fast @@ -18,21 +16,27 @@ def single_song_preset_dict(output_directory): "format": "worst[ext=mp4]", "postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility }, + "overrides": { + "url": "https://www.youtube.com/watch?v=2lAe1cqCOXo", + "music_directory": output_directory, + }, } @pytest.fixture def multiple_songs_preset_dict(output_directory): return { - "preset": "song_playlist", - "download": {"url": "https://youtube.com/playlist?list=PL5BC0FC26BECA5A35"}, - "output_options": {"output_directory": output_directory}, + "preset": "albums_from_playlists", "audio_extract": {"codec": "vorbis", "quality": 140}, # download the worst format so it is fast "ytdl_options": { "format": "worst[ext=mp4]", "postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility }, + "overrides": { + "url": "https://youtube.com/playlist?list=PL5BC0FC26BECA5A35", + "music_directory": output_directory, + }, } @@ -40,13 +44,13 @@ class TestAudioExtract: @pytest.mark.parametrize("dry_run", [True, False]) def test_audio_extract_single_song( self, - youtube_audio_config, + music_audio_config, single_song_preset_dict, output_directory, dry_run, ): subscription = Subscription.from_dict( - config=youtube_audio_config, + config=music_audio_config, preset_name="single_song_test", preset_dict=single_song_preset_dict, ) @@ -66,13 +70,13 @@ class TestAudioExtract: @pytest.mark.parametrize("dry_run", [True, False]) def test_audio_extract_multiple_songs( self, - youtube_audio_config, + music_audio_config, multiple_songs_preset_dict, output_directory, dry_run, ): subscription = Subscription.from_dict( - config=youtube_audio_config, + config=music_audio_config, preset_name="multiple_songs_test", preset_dict=multiple_songs_preset_dict, ) diff --git a/tests/e2e/plugins/test_music_tags.py b/tests/e2e/plugins/test_music_tags.py index 2cec1b9b..c6cecf21 100644 --- a/tests/e2e/plugins/test_music_tags.py +++ b/tests/e2e/plugins/test_music_tags.py @@ -29,12 +29,12 @@ def single_song_video_dict(output_directory): class TestMusicTags: def test_music_tags_errors_on_video( self, - youtube_audio_config, + music_audio_config, single_song_video_dict, output_directory, ): subscription = Subscription.from_dict( - config=youtube_audio_config, + config=music_audio_config, preset_name="single_song_test", preset_dict=single_song_video_dict, ) diff --git a/tests/e2e/plugins/test_split_by_chapters.py b/tests/e2e/plugins/test_split_by_chapters.py index 1320bc3a..4151ab71 100644 --- a/tests/e2e/plugins/test_split_by_chapters.py +++ b/tests/e2e/plugins/test_split_by_chapters.py @@ -12,15 +12,16 @@ from ytdl_sub.utils.exceptions import ValidationException @pytest.fixture def yt_album_as_chapters_preset_dict(output_directory): return { - "preset": "album_from_chapters", - "download": {"url": "https://www.youtube.com/watch?v=zeR2_YjlXWA"}, - # override the output directory with our fixture-generated dir - "output_options": {"output_directory": output_directory, "maintain_download_archive": True}, + "preset": "albums_from_chapters", # download the worst format so it is fast "ytdl_options": { "format": "worst[ext=mp4]", "postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility }, + "overrides": { + "url": "https://www.youtube.com/watch?v=zeR2_YjlXWA", + "music_directory": output_directory, + }, } @@ -33,7 +34,7 @@ def yt_album_as_chapters_with_regex_preset_dict(yt_album_as_chapters_preset_dict "from": { "chapter_title": { "match": r"\d+\. (.+)", - "capture_group_names": "captured_chapter_title", + "capture_group_names": "captured_track_title", "capture_group_defaults": "{chapter_title}", }, "title": { @@ -42,8 +43,8 @@ def yt_album_as_chapters_with_regex_preset_dict(yt_album_as_chapters_preset_dict "(.+) - (.+)", ], "capture_group_names": [ - "captured_artist", - "captured_album", + "captured_track_artist", + "captured_track_album", ], "capture_group_defaults": [ "{channel}", @@ -53,9 +54,9 @@ def yt_album_as_chapters_with_regex_preset_dict(yt_album_as_chapters_preset_dict } }, "overrides": { - "custom_track_name": "{captured_chapter_title}", - "custom_album_name": "{captured_album}", - "custom_artist_name": "{captured_artist}", + "track_title": "{captured_track_title}", + "track_album": "{captured_track_album}", + "track_artist": "{captured_track_artist}", }, }, ) @@ -66,13 +67,13 @@ class TestSplitByChapters: @pytest.mark.parametrize("dry_run", [True, False]) def test_video_with_chapters( self, - youtube_audio_config, + music_audio_config, yt_album_as_chapters_preset_dict, output_directory, dry_run, ): subscription = Subscription.from_dict( - config=youtube_audio_config, + config=music_audio_config, preset_name="split_by_chapters_video", preset_dict=yt_album_as_chapters_preset_dict, ) @@ -96,13 +97,13 @@ class TestSplitByChapters: @pytest.mark.parametrize("dry_run", [True, False]) def test_video_with_chapters_and_regex( self, - youtube_audio_config, + music_audio_config, yt_album_as_chapters_with_regex_preset_dict, output_directory, dry_run, ): subscription = Subscription.from_dict( - config=youtube_audio_config, + config=music_audio_config, preset_name="split_by_chapters_with_regex_video", preset_dict=yt_album_as_chapters_with_regex_preset_dict, ) @@ -123,7 +124,7 @@ class TestSplitByChapters: @pytest.mark.parametrize("when_no_chapters", ["pass", "drop", "error"]) def test_video_with_no_chapters_and_regex( self, - youtube_audio_config, + music_audio_config, yt_album_as_chapters_with_regex_preset_dict, output_directory, dry_run, @@ -138,7 +139,7 @@ class TestSplitByChapters: ) subscription = Subscription.from_dict( - config=youtube_audio_config, + config=music_audio_config, preset_name="split_by_chapters_with_regex_video", preset_dict=yt_album_as_chapters_with_regex_preset_dict, ) diff --git a/tests/e2e/soundcloud/test_soundcloud_discography.py b/tests/e2e/soundcloud/test_soundcloud_discography.py index 4115b7f3..f435245e 100644 --- a/tests/e2e/soundcloud/test_soundcloud_discography.py +++ b/tests/e2e/soundcloud/test_soundcloud_discography.py @@ -14,8 +14,8 @@ def subscription_dict(output_directory): "format": "worst[ext=mp3]", }, "overrides": { - "artist": "j_b", - "artist_url": "https://soundcloud.com/jessebannon", + "track_artist": "j_b", + "sc_artist_url": "https://soundcloud.com/jessebannon", "music_directory": output_directory, }, } @@ -31,14 +31,14 @@ class TestSoundcloudDiscography: def test_discography_download( self, subscription_dict, - soundcloud_discography_config, + music_audio_config, output_directory, dry_run, ): discography_subscription = Subscription.from_dict( preset_dict=subscription_dict, preset_name="jb", - config=soundcloud_discography_config, + config=music_audio_config, ) transaction_log = discography_subscription.download(dry_run=dry_run) assert_transaction_log_matches( diff --git a/tests/resources/expected_downloads_summaries/bandcamp/test_artist_url.json b/tests/resources/expected_downloads_summaries/bandcamp/test_artist_url.json new file mode 100644 index 00000000..76755a56 --- /dev/null +++ b/tests/resources/expected_downloads_summaries/bandcamp/test_artist_url.json @@ -0,0 +1,19 @@ +{ + ".ytdl-sub-jb-download-archive.json": "9f5ca17dc0d842ac4c296054c0011ace", + "El Jazzy Chavo/[2022] S950 Funk/01 - Spark it.mp3": "6f21b6f977609382fa9b9fa9817b28cc", + "El Jazzy Chavo/[2022] S950 Funk/02 - Astronomikal Funk.mp3": "870199f5e75ea170051122f7220e2f42", + "El Jazzy Chavo/[2022] S950 Funk/03 - Impulse.mp3": "3a14e5e034016ad131186caf94be00da", + "El Jazzy Chavo/[2022] S950 Funk/04 - September.mp3": "c58520ef3f7f8944151ee0fb95fb0ac5", + "El Jazzy Chavo/[2022] S950 Funk/05 - Interlune.mp3": "5a7365a253ac4a5af54e550508a40b66", + "El Jazzy Chavo/[2022] S950 Funk/06 - Ninjutsu Techniques.mp3": "07b3f74ef78cb0c869c86dded054b1e9", + "El Jazzy Chavo/[2022] S950 Funk/07 - Space Funk Flow.mp3": "fef74e222258c4845e8b51e05acd240f", + "El Jazzy Chavo/[2022] S950 Funk/08 - Cellar Groove.mp3": "743927c47df60e811d367f5ef969faaf", + "El Jazzy Chavo/[2022] S950 Funk/09 - Solar Wind.mp3": "dd97643e3dc5decefb543da3a6e31356", + "El Jazzy Chavo/[2022] S950 Funk/10 - Interlune II.mp3": "37717cbd8f837657047616743937595b", + "El Jazzy Chavo/[2022] S950 Funk/11 - Ghetto Anthem.mp3": "54089f39836d469f391f6850926a392d", + "El Jazzy Chavo/[2022] S950 Funk/12 - Street Degree.mp3": "ddd3dc74d9bc07b988e4e4da363624dd", + "El Jazzy Chavo/[2022] S950 Funk/13 - Trouble World.mp3": "90dacf561b52852312f9e3f06084bfca", + "El Jazzy Chavo/[2022] S950 Funk/14 - Relaxation.mp3": "12dc503e0f758ae0b962e941727f272b", + "El Jazzy Chavo/[2022] S950 Funk/15 - Journey From Within.mp3": "c593e610ac6853b3795781b6650fc59e", + "El Jazzy Chavo/[2022] S950 Funk/folder.jpg": "62e51365dd309e76a6323f927f6fcc94" +} \ No newline at end of file diff --git a/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_video.json b/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_video.json index 722ce4c0..d6d127fc 100644 --- a/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_video.json +++ b/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_video.json @@ -1,15 +1,15 @@ { - ".ytdl-sub-split_by_chapters_video-download-archive.json": "7001417f7a398959f3296ece81575a77", - "Alfa Mist - Nocturne [Full Album]/01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3": "87d036987c4903e62e4f5001ea38a49a", - "Alfa Mist - Nocturne [Full Album]/02 - 02. Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3": "96975f139dcc87a05dec03ab4f839d94", - "Alfa Mist - Nocturne [Full Album]/03 - 03. Blaze (Feat. Kaya Thomas - Dyke).mp3": "58df7bb835d05e3cb0e28abb4c784a5e", - "Alfa Mist - Nocturne [Full Album]/04 - 04. What If (Interlude).mp3": "d8313eb888dc3c8109623c84139280e7", - "Alfa Mist - Nocturne [Full Album]/05 - 05. No Peace (Feat. Tom Misch).mp3": "4c6ee32b371e6e5374c722f4c854a965", - "Alfa Mist - Nocturne [Full Album]/06 - 06. Closer (Feat. Lester Duval).mp3": "d54280bca884ddc1a0614b00c2d70ea3", - "Alfa Mist - Nocturne [Full Album]/07 - 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3": "61943dfb29ca40069969790773bf3cd9", - "Alfa Mist - Nocturne [Full Album]/08 - 08. Dreams (Feat. Carmody).mp3": "c37001fb9f3c0cca1bffdf3b2ae4a0fc", - "Alfa Mist - Nocturne [Full Album]/09 - 09. Dreaming (Interlude) (Feat. Racheal Ofori).mp3": "5334aa0af6ce2ec1283806cf7d9a15c1", - "Alfa Mist - Nocturne [Full Album]/10 - 10. Hopeful (Feat. Jordan Rakei).mp3": "526d70f88ab03b972c06243f164d4b0e", - "Alfa Mist - Nocturne [Full Album]/11 - 11. Sunrise (Pillows) (Feat. Emmavie).mp3": "8376927e5cecf6bed63f016649b77354", - "Alfa Mist - Nocturne [Full Album]/folder.jpg": "bd3685acc53072e591bae2505ecb0648" + ".ytdl-sub-split_by_chapters_video-download-archive.json": "5e7a31ec4a73e71696f8f3ba9eab9303", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3": "29d09da874133ce5c5f7459c2fa6cd85", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/02 - 02. Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3": "d5432e6a4f7af9810b0e7c8eebe4898a", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/03 - 03. Blaze (Feat. Kaya Thomas - Dyke).mp3": "cf03da6688a73373f9295dc595156e11", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/04 - 04. What If (Interlude).mp3": "b7b943b6f3395c05433b8532364d63d6", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/05 - 05. No Peace (Feat. Tom Misch).mp3": "0d9719268900d4abcd8c0f51ad3af92c", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/06 - 06. Closer (Feat. Lester Duval).mp3": "bbd9ec616a0f7d3c5d13c04bb118681e", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/07 - 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3": "2e59ecdc0f8050bcf190a7898d7ae968", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/08 - 08. Dreams (Feat. Carmody).mp3": "e58966f3326b876f0ab97459b681f76f", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/09 - 09. Dreaming (Interlude) (Feat. Racheal Ofori).mp3": "d6939215b73457fce4e026f8c9b57ecc", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/10 - 10. Hopeful (Feat. Jordan Rakei).mp3": "82d0e7d8070fd15d71c2e11c0285d426", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/11 - 11. Sunrise (Pillows) (Feat. Emmavie).mp3": "3edb11e8cd5f270dd42ee62d9a9aa4ff", + "Proved Records/[2017] Alfa Mist - Nocturne [Full Album]/folder.jpg": "bd3685acc53072e591bae2505ecb0648" } \ No newline at end of file diff --git a/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt b/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt index cfcf6f35..1fc7f0fb 100644 --- a/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt +++ b/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt @@ -1,5 +1,5 @@ { - ".ytdl-sub-split_by_chapters_with_regex_video-download-archive.json": "11367ac13068646e35a53d25deeba834", - "Oblivion Mod "Falcor" p.1/01 - Oblivion Mod "Falcor" p.1.mp3": "dfcf5aed1742bd55e562a0d93e555f01", - "Oblivion Mod "Falcor" p.1/folder.jpg": "fb95b510681676e81c321171fc23143e" + ".ytdl-sub-split_by_chapters_with_regex_video-download-archive.json": "ed212171889707bf65462878248fc0b8", + "Project Zombie/[2010] Oblivion Mod \"Falcor\" p.1/01 - Oblivion Mod \"Falcor\" p.1.mp3": "a3c01f164eeca4541aeed49264d2fc8c", + "Project Zombie/[2010] Oblivion Mod \"Falcor\" p.1/folder.jpg": "fb95b510681676e81c321171fc23143e" } \ No newline at end of file diff --git a/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_video.json b/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_video.json index 77cb9a09..cf27b650 100644 --- a/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_video.json +++ b/tests/resources/expected_downloads_summaries/plugins/split_by_chapters_with_regex_video.json @@ -1,15 +1,15 @@ { - ".ytdl-sub-split_by_chapters_with_regex_video-download-archive.json": "5f281991d441f406b0d721b250e74b79", - "Nocturne/01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3": "1f51d6809509904b51ad8930d9bb49b0", - "Nocturne/02 - Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3": "16ba92db30c7199373855d0ed6d2cdde", - "Nocturne/03 - Blaze (Feat. Kaya Thomas - Dyke).mp3": "21af1894554dc61f437b191a6702c274", - "Nocturne/04 - What If (Interlude).mp3": "b6cab2cfcbcb3807eb8176281960e9be", - "Nocturne/05 - No Peace (Feat. Tom Misch).mp3": "56ae7656dc6edca4e55e0afc9422002a", - "Nocturne/06 - Closer (Feat. Lester Duval).mp3": "93e7025813924670595032c3a2038d94", - "Nocturne/07 - Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3": "b757d50d7ad154fe36d13ae72a05a744", - "Nocturne/08 - Dreams (Feat. Carmody).mp3": "de1e9313623368a9c7e63a08d77ca54b", - "Nocturne/09 - Dreaming (Interlude) (Feat. Racheal Ofori).mp3": "264e0a6c3e572e05daffc38bf8178226", - "Nocturne/10 - Hopeful (Feat. Jordan Rakei).mp3": "e72408d03604ff928acf6ce4b383895c", - "Nocturne/11 - Sunrise (Pillows) (Feat. Emmavie).mp3": "146d981480be3bf28726a27443c1695a", - "Nocturne/folder.jpg": "bd3685acc53072e591bae2505ecb0648" + ".ytdl-sub-split_by_chapters_with_regex_video-download-archive.json": "cb82ae5ed9d7a41aea15446254c93738", + "Alfa Mist/[2017] Nocturne/01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3": "7be57c4dde9ba2c3fa72e9cc65b0f586", + "Alfa Mist/[2017] Nocturne/02 - Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3": "5ee032da9965c51913fcfa0319d061bd", + "Alfa Mist/[2017] Nocturne/03 - Blaze (Feat. Kaya Thomas - Dyke).mp3": "82c1f35bdccfb4ec146f33661ac94d6b", + "Alfa Mist/[2017] Nocturne/04 - What If (Interlude).mp3": "040b38c249e478d8832dcea8bfeca17f", + "Alfa Mist/[2017] Nocturne/05 - No Peace (Feat. Tom Misch).mp3": "06f7e57fecdc7b4d2e37c7652791ce19", + "Alfa Mist/[2017] Nocturne/06 - Closer (Feat. Lester Duval).mp3": "91a03449c33bf9dcff5a9654a8525626", + "Alfa Mist/[2017] Nocturne/07 - Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3": "54c26621b8d4e74f37217c836479a077", + "Alfa Mist/[2017] Nocturne/08 - Dreams (Feat. Carmody).mp3": "2704327ac5998086e77a77da164e2afd", + "Alfa Mist/[2017] Nocturne/09 - Dreaming (Interlude) (Feat. Racheal Ofori).mp3": "75b8a26d3099aa2d9ba488b33c4eb1e7", + "Alfa Mist/[2017] Nocturne/10 - Hopeful (Feat. Jordan Rakei).mp3": "0bf0979c99c55ef986efaba55c0dc858", + "Alfa Mist/[2017] Nocturne/11 - Sunrise (Pillows) (Feat. Emmavie).mp3": "78950fbfe459de8e9aa951ab6fa153bf", + "Alfa Mist/[2017] Nocturne/folder.jpg": "bd3685acc53072e591bae2505ecb0648" } \ No newline at end of file diff --git a/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_playlist.json b/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_playlist.json index 2ff866ba..890aca91 100644 --- a/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_playlist.json +++ b/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_playlist.json @@ -1,5 +1,7 @@ { - "Jesse's Minecraft Server [Trailer - Feb.1].ogg": "e0ab90ea97f26738195111de2fbf0f5f", - "Jesse's Minecraft Server [Trailer - Feb.27].ogg": "814bdd627fbd4b55017341ffdec9da9d", - "Jesse's Minecraft Server [Trailer - Mar.21].ogg": "90303a47a312c27cf8eed56bf09aa526" + ".ytdl-sub-multiple_songs_test-download-archive.json": "54237df5e00d1598dfd39f341ee03d75", + "Project Zombie/[2011] Jesse's Minecraft Server/01 - Jesse's Minecraft Server [Trailer - Mar.21].ogg": "5657c5b92f8980b20d8bbee0fdc7e5d8", + "Project Zombie/[2011] Jesse's Minecraft Server/02 - Jesse's Minecraft Server [Trailer - Feb.27].ogg": "a2a3a34e02e26a6c0265530d4499473b", + "Project Zombie/[2011] Jesse's Minecraft Server/03 - Jesse's Minecraft Server [Trailer - Feb.1].ogg": "b2d6388b4ddf8e3fbca042cb123672c3", + "Project Zombie/[2011] Jesse's Minecraft Server/folder.jpg": "e7830aa8a64b0cde65ba3f7e5fc56530" } \ No newline at end of file diff --git a/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_single.json b/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_single.json index d0e545a8..879f3cdc 100644 --- a/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_single.json +++ b/tests/resources/expected_downloads_summaries/plugins/test_audio_extract_single.json @@ -1,3 +1,5 @@ { - "YouTube Rewind 2019: For the Record | #YouTubeRewind.mp3": "05bd1d218f5a0c942ddb1448d939a1dc" + ".ytdl-sub-single_song_test-download-archive.json": "22c1431e35033d777a674c4802bcc786", + "YouTube/[2019] YouTube Rewind 2019: For the Record | #YouTubeRewind/01 - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp3": "7affc0ecf01f36de9cee1a7aafd776eb", + "YouTube/[2019] YouTube Rewind 2019: For the Record | #YouTubeRewind/folder.jpg": "50ee47c80f679029f5d3503bb91b045a" } \ No newline at end of file diff --git a/tests/resources/expected_downloads_summaries/soundcloud/test_soundcloud_discography.json b/tests/resources/expected_downloads_summaries/soundcloud/test_soundcloud_discography.json index f3ce14cd..3504f1a1 100644 --- a/tests/resources/expected_downloads_summaries/soundcloud/test_soundcloud_discography.json +++ b/tests/resources/expected_downloads_summaries/soundcloud/test_soundcloud_discography.json @@ -1,19 +1,19 @@ { ".ytdl-sub-jb-download-archive.json": "1a99156e9ece62539fb2608416a07200", - "j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3": "bffbd558e12c6a9e029dc136a88342c4", + "j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3": "a8955bccae34c39e64d70a021c95e6d4", "j_b/[2021] Baby Santana's Dorian Groove/folder.jpg": "967892be44b8c47e1be73f055a7c6f08", - "j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3": "038db58aebe2ba875b733932b42a94d6", + "j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3": "ec1d40fee597da35f5dc0126438efe54", "j_b/[2021] Purple Clouds/folder.jpg": "967892be44b8c47e1be73f055a7c6f08", - "j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3": "e145f0a2f6012768280c38655ca58065", - "j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3": "60c8b8817a197a13e4bb90903af612c5", - "j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3": "8265b7e4f79878af877bc6ecd9757efe", - "j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3": "accf46b76891d2954b893d0f91d82816", - "j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3": "e1f584f523336160d5c1104a61de77f3", - "j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3": "f6885b25901177f0357649afe97328cc", - "j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3": "fa057f221cbe4cf2442cd2fdb960743e", - "j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3": "7794ae812c64580e2ac8fc457d5cc85f", - "j_b/[2022] Acoustic Treats/09 - Finding Home.mp3": "adbf02eddb2090c008eb497d13ff84b9", - "j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3": "65bb10c84366c71498161734f953e93d", - "j_b/[2022] Acoustic Treats/11 - Untold History.mp3": "6904b2918e5dc38d9a9f72d967eb74bf", + "j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3": "768e148a2b24ed2de299ca4899d8e533", + "j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3": "a9cd2d2176f94fdad8965c1fbbc3396a", + "j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3": "f3c74e7e8595505d15a384226d0c906e", + "j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3": "d13a3008d9a39da3e5fdcd0c279718e8", + "j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3": "3a40d1108c4f6a0d004eea2151001d66", + "j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3": "8224406bb95e100f4ebcdf7fec3db1c0", + "j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3": "1ea96a8581eb81e45564617f75bd467e", + "j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3": "df14e3b41386cf408b4d1549428a17a5", + "j_b/[2022] Acoustic Treats/09 - Finding Home.mp3": "f52df647e1f5cf15df720d62b5ad64b1", + "j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3": "429ac1c752822230803c1a452987e492", + "j_b/[2022] Acoustic Treats/11 - Untold History.mp3": "9322445a6a7cdd506038b8714cb1731f", "j_b/[2022] Acoustic Treats/folder.jpg": "967892be44b8c47e1be73f055a7c6f08" } \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/bandcamp/test_artist_url.txt b/tests/resources/transaction_log_summaries/bandcamp/test_artist_url.txt new file mode 100644 index 00000000..e85cfcae --- /dev/null +++ b/tests/resources/transaction_log_summaries/bandcamp/test_artist_url.txt @@ -0,0 +1,186 @@ +Files created: +---------------------------------------- +{output_directory} + .ytdl-sub-jb-download-archive.json +{output_directory}/El Jazzy Chavo/[2022] S950 Funk + 01 - Spark it.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Spark it + track: 1 + tracktotal: 15 + year: 2022 + 02 - Astronomikal Funk.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Astronomikal Funk + track: 2 + tracktotal: 15 + year: 2022 + 03 - Impulse.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Impulse + track: 3 + tracktotal: 15 + year: 2022 + 04 - September.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: September + track: 4 + tracktotal: 15 + year: 2022 + 05 - Interlune.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Interlune + track: 5 + tracktotal: 15 + year: 2022 + 06 - Ninjutsu Techniques.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Ninjutsu Techniques + track: 6 + tracktotal: 15 + year: 2022 + 07 - Space Funk Flow.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Space Funk Flow + track: 7 + tracktotal: 15 + year: 2022 + 08 - Cellar Groove.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Cellar Groove + track: 8 + tracktotal: 15 + year: 2022 + 09 - Solar Wind.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Solar Wind + track: 9 + tracktotal: 15 + year: 2022 + 10 - Interlune II.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Interlune II + track: 10 + tracktotal: 15 + year: 2022 + 11 - Ghetto Anthem.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Ghetto Anthem + track: 11 + tracktotal: 15 + year: 2022 + 12 - Street Degree.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Street Degree + track: 12 + tracktotal: 15 + year: 2022 + 13 - Trouble World.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Trouble World + track: 13 + tracktotal: 15 + year: 2022 + 14 - Relaxation.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Relaxation + track: 14 + tracktotal: 15 + year: 2022 + 15 - Journey From Within.mp3 + Music Tags: + album: S950 Funk + albumartist: El Jazzy Chavo + albumartists: El Jazzy Chavo + artist: El Jazzy Chavo + artists: El Jazzy Chavo + genre: Unset + title: Journey From Within + track: 15 + tracktotal: 15 + year: 2022 + folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video-dry-run.txt b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video-dry-run.txt index 1b42d8b5..484abb08 100644 --- a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video-dry-run.txt +++ b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video-dry-run.txt @@ -2,7 +2,7 @@ Files created: ---------------------------------------- {output_directory} .ytdl-sub-split_by_chapters_video-download-archive.json -{output_directory}/Alfa Mist - Nocturne [Full Album] +{output_directory}/Proved Records/[2017] Alfa Mist - Nocturne [Full Album] 01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3 From Chapter Split: Warning: Dry-run assumes embedded chapters with no modifications @@ -17,6 +17,7 @@ Files created: genre: Unset title: 01. Intro (Feat. Racheal Ofori & Barney Artist) track: 1 + tracktotal: 11 year: 2017 02 - 02. Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -32,6 +33,7 @@ Files created: genre: Unset title: 02. Answers (Feat. Rick David & Kaya Thomas - Dyke) track: 2 + tracktotal: 11 year: 2017 03 - 03. Blaze (Feat. Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -47,6 +49,7 @@ Files created: genre: Unset title: 03. Blaze (Feat. Kaya Thomas - Dyke) track: 3 + tracktotal: 11 year: 2017 04 - 04. What If (Interlude).mp3 From Chapter Split: @@ -62,6 +65,7 @@ Files created: genre: Unset title: 04. What If (Interlude) track: 4 + tracktotal: 11 year: 2017 05 - 05. No Peace (Feat. Tom Misch).mp3 From Chapter Split: @@ -77,6 +81,7 @@ Files created: genre: Unset title: 05. No Peace (Feat. Tom Misch) track: 5 + tracktotal: 11 year: 2017 06 - 06. Closer (Feat. Lester Duval).mp3 From Chapter Split: @@ -92,8 +97,9 @@ Files created: genre: Unset title: 06. Closer (Feat. Lester Duval) track: 6 + tracktotal: 11 year: 2017 - 07 - 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 + 07 - 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: Warning: Dry-run assumes embedded chapters with no modifications Source Title: Alfa Mist - Nocturne [Full Album] @@ -107,6 +113,7 @@ Files created: genre: Unset title: 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori) track: 7 + tracktotal: 11 year: 2017 08 - 08. Dreams (Feat. Carmody).mp3 From Chapter Split: @@ -122,6 +129,7 @@ Files created: genre: Unset title: 08. Dreams (Feat. Carmody) track: 8 + tracktotal: 11 year: 2017 09 - 09. Dreaming (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: @@ -137,6 +145,7 @@ Files created: genre: Unset title: 09. Dreaming (Interlude) (Feat. Racheal Ofori) track: 9 + tracktotal: 11 year: 2017 10 - 10. Hopeful (Feat. Jordan Rakei).mp3 From Chapter Split: @@ -152,6 +161,7 @@ Files created: genre: Unset title: 10. Hopeful (Feat. Jordan Rakei) track: 10 + tracktotal: 11 year: 2017 11 - 11. Sunrise (Pillows) (Feat. Emmavie).mp3 From Chapter Split: @@ -167,5 +177,6 @@ Files created: genre: Unset title: 11. Sunrise (Pillows) (Feat. Emmavie) track: 11 + tracktotal: 11 year: 2017 folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video.txt b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video.txt index 5616f6e5..5b82af0b 100644 --- a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video.txt +++ b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_video.txt @@ -2,7 +2,7 @@ Files created: ---------------------------------------- {output_directory} .ytdl-sub-split_by_chapters_video-download-archive.json -{output_directory}/Alfa Mist - Nocturne [Full Album] +{output_directory}/Proved Records/[2017] Alfa Mist - Nocturne [Full Album] 01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3 From Chapter Split: Source Title: Alfa Mist - Nocturne [Full Album] @@ -16,6 +16,7 @@ Files created: genre: Unset title: 01. Intro (Feat. Racheal Ofori & Barney Artist) track: 1 + tracktotal: 11 year: 2017 02 - 02. Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -30,6 +31,7 @@ Files created: genre: Unset title: 02. Answers (Feat. Rick David & Kaya Thomas - Dyke) track: 2 + tracktotal: 11 year: 2017 03 - 03. Blaze (Feat. Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -44,6 +46,7 @@ Files created: genre: Unset title: 03. Blaze (Feat. Kaya Thomas - Dyke) track: 3 + tracktotal: 11 year: 2017 04 - 04. What If (Interlude).mp3 From Chapter Split: @@ -58,6 +61,7 @@ Files created: genre: Unset title: 04. What If (Interlude) track: 4 + tracktotal: 11 year: 2017 05 - 05. No Peace (Feat. Tom Misch).mp3 From Chapter Split: @@ -72,6 +76,7 @@ Files created: genre: Unset title: 05. No Peace (Feat. Tom Misch) track: 5 + tracktotal: 11 year: 2017 06 - 06. Closer (Feat. Lester Duval).mp3 From Chapter Split: @@ -86,8 +91,9 @@ Files created: genre: Unset title: 06. Closer (Feat. Lester Duval) track: 6 + tracktotal: 11 year: 2017 - 07 - 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 + 07 - 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: Source Title: Alfa Mist - Nocturne [Full Album] Segment: 17:57 - 20:05 @@ -100,6 +106,7 @@ Files created: genre: Unset title: 07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori) track: 7 + tracktotal: 11 year: 2017 08 - 08. Dreams (Feat. Carmody).mp3 From Chapter Split: @@ -114,6 +121,7 @@ Files created: genre: Unset title: 08. Dreams (Feat. Carmody) track: 8 + tracktotal: 11 year: 2017 09 - 09. Dreaming (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: @@ -128,6 +136,7 @@ Files created: genre: Unset title: 09. Dreaming (Interlude) (Feat. Racheal Ofori) track: 9 + tracktotal: 11 year: 2017 10 - 10. Hopeful (Feat. Jordan Rakei).mp3 From Chapter Split: @@ -142,6 +151,7 @@ Files created: genre: Unset title: 10. Hopeful (Feat. Jordan Rakei) track: 10 + tracktotal: 11 year: 2017 11 - 11. Sunrise (Pillows) (Feat. Emmavie).mp3 From Chapter Split: @@ -156,5 +166,6 @@ Files created: genre: Unset title: 11. Sunrise (Pillows) (Feat. Emmavie) track: 11 + tracktotal: 11 year: 2017 folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt index 83862bf3..98f22142 100644 --- a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt +++ b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_no_chapters_video_pass.txt @@ -2,8 +2,8 @@ Files created: ---------------------------------------- {output_directory} .ytdl-sub-split_by_chapters_with_regex_video-download-archive.json -{output_directory}/Oblivion Mod "Falcor" p.1 - 01 - Oblivion Mod "Falcor" p.1.mp3 +{output_directory}/Project Zombie/[2010] Oblivion Mod "Falcor" p.1 + 01 - Oblivion Mod "Falcor" p.1.mp3 Music Tags: album: Oblivion Mod "Falcor" p.1 albumartist: Project Zombie @@ -13,5 +13,6 @@ Files created: genre: Unset title: Oblivion Mod "Falcor" p.1 track: 1 + tracktotal: 1 year: 2010 folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video-dry-run.txt b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video-dry-run.txt index f86ef1f5..1e39241c 100644 --- a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video-dry-run.txt +++ b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video-dry-run.txt @@ -2,7 +2,7 @@ Files created: ---------------------------------------- {output_directory} .ytdl-sub-split_by_chapters_with_regex_video-download-archive.json -{output_directory}/Nocturne +{output_directory}/Alfa Mist/[2017] Nocturne 01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3 From Chapter Split: Warning: Dry-run assumes embedded chapters with no modifications @@ -17,6 +17,7 @@ Files created: genre: Unset title: Intro (Feat. Racheal Ofori & Barney Artist) track: 1 + tracktotal: 11 year: 2017 02 - Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -32,6 +33,7 @@ Files created: genre: Unset title: Answers (Feat. Rick David & Kaya Thomas - Dyke) track: 2 + tracktotal: 11 year: 2017 03 - Blaze (Feat. Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -47,6 +49,7 @@ Files created: genre: Unset title: Blaze (Feat. Kaya Thomas - Dyke) track: 3 + tracktotal: 11 year: 2017 04 - What If (Interlude).mp3 From Chapter Split: @@ -62,6 +65,7 @@ Files created: genre: Unset title: What If (Interlude) track: 4 + tracktotal: 11 year: 2017 05 - No Peace (Feat. Tom Misch).mp3 From Chapter Split: @@ -77,6 +81,7 @@ Files created: genre: Unset title: No Peace (Feat. Tom Misch) track: 5 + tracktotal: 11 year: 2017 06 - Closer (Feat. Lester Duval).mp3 From Chapter Split: @@ -92,8 +97,9 @@ Files created: genre: Unset title: Closer (Feat. Lester Duval) track: 6 + tracktotal: 11 year: 2017 - 07 - Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 + 07 - Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: Warning: Dry-run assumes embedded chapters with no modifications Source Title: Alfa Mist - Nocturne [Full Album] @@ -107,6 +113,7 @@ Files created: genre: Unset title: Delusions: Rumination (Interlude) (Feat. Racheal Ofori) track: 7 + tracktotal: 11 year: 2017 08 - Dreams (Feat. Carmody).mp3 From Chapter Split: @@ -122,6 +129,7 @@ Files created: genre: Unset title: Dreams (Feat. Carmody) track: 8 + tracktotal: 11 year: 2017 09 - Dreaming (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: @@ -137,6 +145,7 @@ Files created: genre: Unset title: Dreaming (Interlude) (Feat. Racheal Ofori) track: 9 + tracktotal: 11 year: 2017 10 - Hopeful (Feat. Jordan Rakei).mp3 From Chapter Split: @@ -152,6 +161,7 @@ Files created: genre: Unset title: Hopeful (Feat. Jordan Rakei) track: 10 + tracktotal: 11 year: 2017 11 - Sunrise (Pillows) (Feat. Emmavie).mp3 From Chapter Split: @@ -167,5 +177,6 @@ Files created: genre: Unset title: Sunrise (Pillows) (Feat. Emmavie) track: 11 + tracktotal: 11 year: 2017 folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video.txt b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video.txt index e6233833..4ecea103 100644 --- a/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video.txt +++ b/tests/resources/transaction_log_summaries/plugins/split_by_chapters_with_regex_video.txt @@ -2,7 +2,7 @@ Files created: ---------------------------------------- {output_directory} .ytdl-sub-split_by_chapters_with_regex_video-download-archive.json -{output_directory}/Nocturne +{output_directory}/Alfa Mist/[2017] Nocturne 01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3 From Chapter Split: Source Title: Alfa Mist - Nocturne [Full Album] @@ -16,6 +16,7 @@ Files created: genre: Unset title: Intro (Feat. Racheal Ofori & Barney Artist) track: 1 + tracktotal: 11 year: 2017 02 - Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -30,6 +31,7 @@ Files created: genre: Unset title: Answers (Feat. Rick David & Kaya Thomas - Dyke) track: 2 + tracktotal: 11 year: 2017 03 - Blaze (Feat. Kaya Thomas - Dyke).mp3 From Chapter Split: @@ -44,6 +46,7 @@ Files created: genre: Unset title: Blaze (Feat. Kaya Thomas - Dyke) track: 3 + tracktotal: 11 year: 2017 04 - What If (Interlude).mp3 From Chapter Split: @@ -58,6 +61,7 @@ Files created: genre: Unset title: What If (Interlude) track: 4 + tracktotal: 11 year: 2017 05 - No Peace (Feat. Tom Misch).mp3 From Chapter Split: @@ -72,6 +76,7 @@ Files created: genre: Unset title: No Peace (Feat. Tom Misch) track: 5 + tracktotal: 11 year: 2017 06 - Closer (Feat. Lester Duval).mp3 From Chapter Split: @@ -86,8 +91,9 @@ Files created: genre: Unset title: Closer (Feat. Lester Duval) track: 6 + tracktotal: 11 year: 2017 - 07 - Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 + 07 - Delusions: Rumination (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: Source Title: Alfa Mist - Nocturne [Full Album] Segment: 17:57 - 20:05 @@ -100,6 +106,7 @@ Files created: genre: Unset title: Delusions: Rumination (Interlude) (Feat. Racheal Ofori) track: 7 + tracktotal: 11 year: 2017 08 - Dreams (Feat. Carmody).mp3 From Chapter Split: @@ -114,6 +121,7 @@ Files created: genre: Unset title: Dreams (Feat. Carmody) track: 8 + tracktotal: 11 year: 2017 09 - Dreaming (Interlude) (Feat. Racheal Ofori).mp3 From Chapter Split: @@ -128,6 +136,7 @@ Files created: genre: Unset title: Dreaming (Interlude) (Feat. Racheal Ofori) track: 9 + tracktotal: 11 year: 2017 10 - Hopeful (Feat. Jordan Rakei).mp3 From Chapter Split: @@ -142,6 +151,7 @@ Files created: genre: Unset title: Hopeful (Feat. Jordan Rakei) track: 10 + tracktotal: 11 year: 2017 11 - Sunrise (Pillows) (Feat. Emmavie).mp3 From Chapter Split: @@ -156,5 +166,6 @@ Files created: genre: Unset title: Sunrise (Pillows) (Feat. Emmavie) track: 11 + tracktotal: 11 year: 2017 folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/test_audio_extract_playlist.txt b/tests/resources/transaction_log_summaries/plugins/test_audio_extract_playlist.txt index ab4be55f..df0fbecd 100644 --- a/tests/resources/transaction_log_summaries/plugins/test_audio_extract_playlist.txt +++ b/tests/resources/transaction_log_summaries/plugins/test_audio_extract_playlist.txt @@ -1,31 +1,11 @@ Files created: ---------------------------------------- {output_directory} - Jesse's Minecraft Server [Trailer - Feb.1].ogg + .ytdl-sub-multiple_songs_test-download-archive.json +{output_directory}/Project Zombie/[2011] Jesse's Minecraft Server + 01 - Jesse's Minecraft Server [Trailer - Mar.21].ogg Music Tags: - album: Singles - albumartist: Project Zombie - albumartists: Project Zombie - artist: Project Zombie - artists: Project Zombie - genre: Unset - title: Jesse's Minecraft Server [Trailer - Feb.1] - track: 3 - year: 2011 - Jesse's Minecraft Server [Trailer - Feb.27].ogg - Music Tags: - album: Singles - albumartist: Project Zombie - albumartists: Project Zombie - artist: Project Zombie - artists: Project Zombie - genre: Unset - title: Jesse's Minecraft Server [Trailer - Feb.27] - track: 2 - year: 2011 - Jesse's Minecraft Server [Trailer - Mar.21].ogg - Music Tags: - album: Singles + album: Jesse's Minecraft Server albumartist: Project Zombie albumartists: Project Zombie artist: Project Zombie @@ -33,4 +13,30 @@ Files created: genre: Unset title: Jesse's Minecraft Server [Trailer - Mar.21] track: 1 - year: 2011 \ No newline at end of file + tracktotal: 3 + year: 2011 + 02 - Jesse's Minecraft Server [Trailer - Feb.27].ogg + Music Tags: + album: Jesse's Minecraft Server + albumartist: Project Zombie + albumartists: Project Zombie + artist: Project Zombie + artists: Project Zombie + genre: Unset + title: Jesse's Minecraft Server [Trailer - Feb.27] + track: 2 + tracktotal: 3 + year: 2011 + 03 - Jesse's Minecraft Server [Trailer - Feb.1].ogg + Music Tags: + album: Jesse's Minecraft Server + albumartist: Project Zombie + albumartists: Project Zombie + artist: Project Zombie + artists: Project Zombie + genre: Unset + title: Jesse's Minecraft Server [Trailer - Feb.1] + track: 3 + tracktotal: 3 + year: 2011 + folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/plugins/test_audio_extract_single.txt b/tests/resources/transaction_log_summaries/plugins/test_audio_extract_single.txt index 1cb85a75..153bcaf3 100644 --- a/tests/resources/transaction_log_summaries/plugins/test_audio_extract_single.txt +++ b/tests/resources/transaction_log_summaries/plugins/test_audio_extract_single.txt @@ -1,9 +1,11 @@ Files created: ---------------------------------------- {output_directory} - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp3 + .ytdl-sub-single_song_test-download-archive.json +{output_directory}/YouTube/[2019] YouTube Rewind 2019: For the Record | #YouTubeRewind + 01 - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp3 Embedded Thumbnail, Music Tags: - album: Singles + album: YouTube Rewind 2019: For the Record | #YouTubeRewind albumartist: YouTube albumartists: YouTube artist: YouTube @@ -14,4 +16,6 @@ Files created: - multi_tag_2 title: YouTube Rewind 2019: For the Record | #YouTubeRewind track: 1 - year: 2019 \ No newline at end of file + tracktotal: 1 + year: 2019 + folder.jpg \ No newline at end of file diff --git a/tests/resources/transaction_log_summaries/soundcloud/test_soundcloud_discography.txt b/tests/resources/transaction_log_summaries/soundcloud/test_soundcloud_discography.txt index 3e51150d..86015ee3 100644 --- a/tests/resources/transaction_log_summaries/soundcloud/test_soundcloud_discography.txt +++ b/tests/resources/transaction_log_summaries/soundcloud/test_soundcloud_discography.txt @@ -7,10 +7,13 @@ Files created: Music Tags: album: Baby Santana's Dorian Groove albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: Baby Santana's Dorian Groove track: 1 + tracktotal: 1 year: 2021 folder.jpg {output_directory}/j_b/[2021] Purple Clouds @@ -18,10 +21,13 @@ Files created: Music Tags: album: Purple Clouds albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: Purple Clouds track: 1 + tracktotal: 1 year: 2021 folder.jpg {output_directory}/j_b/[2022] Acoustic Treats @@ -29,99 +35,132 @@ Files created: Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20160426 184214 track: 1 + tracktotal: 11 year: 2022 02 - 20160502 123150.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20160502 123150 track: 2 + tracktotal: 11 year: 2022 03 - 20160504 143832.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20160504 143832 track: 3 + tracktotal: 11 year: 2022 04 - 20160601 221234.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20160601 221234 track: 4 + tracktotal: 11 year: 2022 05 - 20160601 222440.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20160601 222440 track: 5 + tracktotal: 11 year: 2022 06 - 20170604 190236.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20170604 190236 track: 6 + tracktotal: 11 year: 2022 07 - 20170612 193646.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20170612 193646 track: 7 + tracktotal: 11 year: 2022 08 - 20170628 215206.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: 20170628 215206 track: 8 + tracktotal: 11 year: 2022 09 - Finding Home.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: Finding Home track: 9 + tracktotal: 11 year: 2022 10 - Shallow Water WIP.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: Shallow Water WIP track: 10 + tracktotal: 11 year: 2022 11 - Untold History.mp3 Music Tags: album: Acoustic Treats albumartist: j_b + albumartists: j_b artist: j_b + artists: j_b genre: Unset title: Untold History track: 11 + tracktotal: 11 year: 2022 folder.jpg \ No newline at end of file diff --git a/tests/unit/validators/test_url_validator.py b/tests/unit/validators/test_url_validator.py deleted file mode 100644 index e592e231..00000000 --- a/tests/unit/validators/test_url_validator.py +++ /dev/null @@ -1,155 +0,0 @@ -import re - -import pytest - -from ytdl_sub.utils.exceptions import ValidationException -from ytdl_sub.validators.url_validator import SoundcloudUsernameUrlValidator -from ytdl_sub.validators.url_validator import YoutubeChannelUrlValidator -from ytdl_sub.validators.url_validator import YoutubePlaylistUrlValidator -from ytdl_sub.validators.url_validator import YoutubeVideoUrlValidator - - -class TestYoutubeVideoUrlValidator: - @pytest.mark.parametrize( - "url", - [ - "youtu.be/dQw4w9WgXcQ", - "www.youtu.be/dQw4w9WgXcQ", - "https://youtu.be/dQw4w9WgXcQ", - "https://www.youtu.be/dQw4w9WgXcQ", - "https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=feedu", - "https://www.youtube.com/embed/dQw4w9WgXcQ", - "https://www.youtube.com/v/dQw4w9WgXcQ?version=3&hl=en_US", - "https://www.youtube.com/shorts/dQw4w9WgXcQ", - ], - ) - def test_youtube_video_url_validator_success(self, url): - video_url = YoutubeVideoUrlValidator(name="unit test", value=url).video_url - assert video_url == "https://youtube.com/watch?v=dQw4w9WgXcQ" - - @pytest.mark.parametrize( - "bad_url", - [ - "utube.com/watch?v=sdfsadf", - "youtube.com/nope?v=sdfsadf", - "youtube.com", - "youtube.com/watch", - "youtube.com/watch?v=", - "youtu.be/", - "youtu.be", - "youtube.com/shorts", - ], - ) - def test_youtube_video_url_validator_fail(self, bad_url): - expected_error_msg = f"'{bad_url}' is not a valid Youtube video url." - with pytest.raises(ValidationException, match=re.escape(expected_error_msg)): - YoutubeVideoUrlValidator(name="unit test", value=bad_url) - - -class TestYoutubePlaylistUrlValidator: - @pytest.mark.parametrize( - "url", - [ - "youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc", - "www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc", - "https://youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc", - "https://www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc", - ], - ) - def test_youtube_playlist_url_validator_success(self, url): - playlist_url = YoutubePlaylistUrlValidator(name="unit test", value=url).playlist_url - assert ( - playlist_url == "https://youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc" - ) - - @pytest.mark.parametrize( - "bad_url", - [ - "youpoop.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc", - "youtube.com/playlistlist=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc", - "youtube.com", - "youtube.com/playlist/asdfsdfsdf", - "youtube.com/playlist?list=", - "youtube.com/playlist", - ], - ) - def test_youtube_playlist_url_validator_fail(self, bad_url): - expected_error_msg = f"'{bad_url}' is not a valid Youtube playlist url." - with pytest.raises(ValidationException, match=re.escape(expected_error_msg)): - YoutubePlaylistUrlValidator(name="unit test", value=bad_url) - - -class TestYoutubeChannelUrlValidator: - @pytest.mark.parametrize( - "url, expected_url", - [ - ( - "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - "https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - ), - ( - "https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - "https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - ), - ( - "youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - "https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - ), - ( - "www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - "https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", - ), - ( - "https://www.youtube.com/c/RickastleyCoUkOfficial", - "https://youtube.com/c/RickastleyCoUkOfficial", - ), - ( - "https://www.youtube.com/user/videogamedunkey", - "https://youtube.com/user/videogamedunkey", - ), - ( - "https://youtube.com/extracredits", - "https://youtube.com/extracredits", - ), - ], - ) - def test_youtube_channel_url_validator_success(self, url, expected_url): - channel_url = YoutubeChannelUrlValidator(name="unit test", value=url).channel_url - assert channel_url == expected_url - - @pytest.mark.parametrize( - "bad_url", - [ - "www.youtube.com/cha/UCuAXFkgsw1L7xaCfnd5JJOw", - "www.nopetube.com/channel/asdfdsf", - "www.youtube.com/channel/", - "www.youtube.com/channell/asdfasdf", - ], - ) - def test_youtube_channel_url_validator_fail(self, bad_url): - expected_error_msg = f"'{bad_url}' is not a valid Youtube channel url." - with pytest.raises(ValidationException, match=re.escape(expected_error_msg)): - YoutubeChannelUrlValidator(name="unit test", value=bad_url) - - -class TestSoundcloudUsernameUrlValidator: - @pytest.mark.parametrize( - "url", - [ - "soundcloud.com/poop", - "www.soundcloud.com/poop", - "https://soundcloud.com/poop", - "https://www.soundcloud.com/poop", - "https://www.soundcloud.com/poop/albums", - "https://www.soundcloud.com/poop?link=clipboard_share", - ], - ) - def test_soundcloud_artist_url_validator_success(self, url): - username_url = SoundcloudUsernameUrlValidator(name="unit test", value=url).username_url - assert username_url == "https://soundcloud.com/poop" - - @pytest.mark.parametrize("bad_url", ["soundcloud.com", "soundnope.lol", "soundcloud.comm/"]) - def test_youtube_playlist_url_validator_fail(self, bad_url): - expected_error_msg = f"'{bad_url}' is not a valid Soundcloud username url." - with pytest.raises(ValidationException, match=re.escape(expected_error_msg)): - SoundcloudUsernameUrlValidator(name="unit test", value=bad_url)