ytdl-sub/tests/unit/cli/test_entrypoint.py
Jesse Bannon 5e9472986a
[FEATURE] Enhance music video presets using specialized map syntax (#808)
Enhances the music video presets by supporting the following syntax:

`subscriptions.yaml`
```
__preset__:
  overrides:
    music_video_directory: "/music_videos"

# Choose between Jellyfin/Kodi/Plex Music Videos preset:
#   - Plex Music Videos:
#   - Jellyfin Music Videos:
#   - Kodi Music Videos:
#
"Plex Music Videos":

  = Pop:  # Sets genre tag to "Pop"
    "Rick Astley": "https://www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc"
    "Michael Jackson": "https://www.youtube.com/playlist?list=OLAK5uy_mnY03zP6abNWH929q2XhGzWD_2uKJ_n8E"

  = Rock:
    # Prefixing with '+' puts the subscription into 'map-mode'.
    # Music video presets in map-mode support grouping videos into different
    # categories, which get set on the album field.
    #
    # URLs can either be strings, or maps that can overload title, year, date
    "+ Guns N' Roses":
      Music Videos:
        - "https://www.youtube.com/playlist?list=PLOTK54q5K4INNXaHKtmXYr6J7CajWjqeJ"
      Concerts:
        - title: "Live at The Ritz - New York City"
          year: "1988"
          url: "https://www.youtube.com/watch?v=OldpIhHPsbs"
        - title: "Live at The Hollywood Bowl"
          date: "2023-01-11"
          url: "https://www.youtube.com/watch?v=Z7hutGlvq9I"
```
The 'map-mode' (denoted by `+`) lets you specify a map of music video categories. Under each category, a URL can be specified as-is or with additional metadata (title, year/date, url).

Music videos and concerts, especially older ones, are typically uploaded by random users with inconsistencies in their titles. This syntax aims to make it easy to specify individual URLs with the ability to overwrite their title and year, and group them by category.
2024-06-06 11:50:33 -07:00

122 lines
4.7 KiB
Python

import re
import sys
from pathlib import Path
from typing import Callable
from typing import List
from unittest.mock import patch
import pytest
from ytdl_sub.cli.entrypoint import _download_subscriptions_from_yaml_files
from ytdl_sub.cli.entrypoint import main
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled
####################################################################################################
# SHARED FIXTURES
####################################################################################################
# PERSIST LOGS FIXTURES + TESTS
@pytest.mark.parametrize("dry_run", [True, False])
@pytest.mark.parametrize("mock_success_output", [True, False])
@pytest.mark.parametrize("keep_successful_logs", [True, False])
@pytest.mark.parametrize("match", [[], ["Rick", "Michael"]])
def test_subscription_logs_write_to_file(
persist_logs_directory: str,
persist_logs_config_factory: Callable,
mock_subscription_download_factory: Callable,
music_video_subscription_path: Path,
dry_run: bool,
mock_success_output: bool,
keep_successful_logs: bool,
match: List[str],
):
subscription_names = ["Rick Astley", "Michael Jackson", "Eric Clapton", "Guns N' Roses"]
if match:
subscription_names = ["Rick Astley", "Michael Jackson"]
num_runs = 2
config = persist_logs_config_factory(keep_successful_logs=keep_successful_logs)
subscription_paths = [str(music_video_subscription_path)] * num_runs
with (
patch.object(
Subscription,
"download",
new=mock_subscription_download_factory(mock_success_output=mock_success_output),
# mock datetime to be an index to be able to run instantly
),
patch("ytdl_sub.cli.entrypoint._log_time", side_effect=[str(idx) for idx in range(10)]),
):
try:
_download_subscriptions_from_yaml_files(
config=config,
subscription_paths=subscription_paths,
subscription_matches=match,
subscription_override_dict={},
update_with_info_json=False,
dry_run=dry_run,
)
except ValueError:
assert not mock_success_output
log_directory_files = sorted(list(Path(persist_logs_directory).rglob("*")))
# If dry run or success but success logging disabled, expect 0 log files
if dry_run or (mock_success_output and not keep_successful_logs):
assert len(log_directory_files) == 0
return
# If not success, expect 2 log files for both sub errors
elif not mock_success_output:
assert len(log_directory_files) == (num_runs * len(subscription_names))
for log_path, subscription_name in zip(log_directory_files, subscription_names):
subscription_log_file_name = subscription_name.lower().replace(" ", "_")
assert bool(re.match(rf"\d\.{subscription_log_file_name}\.error\.log", log_path.name))
with open(log_path, "r", encoding="utf-8") as log_file:
assert log_file.readlines()[-1] == (
f"Please upload the error log file '{str(log_path)}' and make a Github issue "
f"at https://github.com/jmbannon/ytdl-sub/issues with your config and "
f"command/subscription yaml file to reproduce. Thanks for trying ytdl-sub!\n"
)
# If success and success logging, expect 3 log files
else:
assert len(log_directory_files) == (num_runs * len(subscription_names))
for log_file_path, subscription_name in zip(
log_directory_files, subscription_names * num_runs
):
subscription_log_file_name = subscription_name.lower().replace(" ", "_")
assert bool(
re.match(rf"\d\.{subscription_log_file_name}\.success\.log", log_file_path.name)
)
with open(log_file_path, "r", encoding="utf-8") as log_file:
assert (
log_file.readlines()[-1]
== f"[ytdl-sub] name={subscription_name} success=True dry_run=False\n"
)
def test_update_with_info_json_requires_experimental_flag(
default_config_path: Path,
music_video_subscription_path: Path,
) -> None:
with (
patch.object(
sys,
"argv",
[
"ytdl-sub",
"--config",
str(default_config_path),
"sub",
str(music_video_subscription_path),
"--update-with-info-json",
],
),
pytest.raises(ExperimentalFeatureNotEnabled),
):
_ = main()