ytdl-sub/src/ytdl_sub/entries/variables/override_variables.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

177 lines
5.4 KiB
Python

from typing import Dict
from typing import Set
from ytdl_sub.entries.script.function_scripts import CUSTOM_FUNCTION_SCRIPTS
from ytdl_sub.entries.script.variable_definitions import VARIABLE_SCRIPTS
from ytdl_sub.entries.script.variable_types import ArrayVariable
from ytdl_sub.entries.script.variable_types import BooleanVariable
from ytdl_sub.entries.script.variable_types import MapVariable
from ytdl_sub.entries.script.variable_types import StringVariable
from ytdl_sub.entries.script.variable_types import Variable
from ytdl_sub.script.functions import Functions
from ytdl_sub.script.utils.name_validation import is_valid_name
# TODO: use this
SUBSCRIPTION_ARRAY = "subscription_array"
class SubscriptionVariables:
@staticmethod
def subscription_name() -> StringVariable:
"""
Name of the subscription. For subscriptions types that use a prefix (``~``, ``+``),
the prefix and all whitespace afterwards is stripped from the subscription name.
"""
return StringVariable(variable_name="subscription_name", definition="{ %string('') }")
@staticmethod
def subscription_value() -> StringVariable:
"""
For subscriptions in the form of
.. code-block:: yaml
"Subscription Name": "https://..."
``subscription_value`` gets set to ``https://...``.
"""
return StringVariable(variable_name="subscription_value", definition="{ %string('') }")
@staticmethod
def subscription_array() -> ArrayVariable:
"""
For subscriptions in the form of
.. code-block:: yaml
"Subscription Name":
- "https://url1.com/..."
- "https://url2.com/..."
Store all values into an array named ``subscription_array``.
"""
return ArrayVariable(variable_name="subscription_array", definition="{ [] }")
@staticmethod
def subscription_indent_i(index: int) -> StringVariable:
"""
For subscriptions in the form of
.. code-block:: yaml
Preset | = Indent Value 1:
= Indent Value 2:
"Subscription Name": "https://..."
``subscription_indent_1`` and ``subscription_indent_2`` get set to
``Indent Value 1`` and ``Indent Value 2``.
"""
return StringVariable(
variable_name=f"subscription_indent_{index + 1}", definition="{ %string('') }"
)
@staticmethod
def subscription_value_i(index: int) -> StringVariable:
"""
For subscriptions in the form of
.. code-block:: yaml
"Subscription Name":
- "https://url1.com/..."
- "https://url2.com/..."
``subscription_value_1`` and ``subscription_value_2`` get set to ``https://url1.com/...``
and ``https://url2.com/...``. Note that ``subscription_value_1`` also gets set to
``subscription_value``.
"""
return StringVariable(
variable_name=f"subscription_value_{index + 1}", definition="{ %string('') }"
)
@staticmethod
def subscription_map() -> MapVariable:
"""
For subscriptions in the form of
.. code-block:: yaml
+ Subscription Name:
Music Videos:
- "https://url1.com/..."
Concerts:
- "https://url2.com/..."
Stores all the contents under the subscription name into the override variable
``subscription_map`` as a Map value. The above example is stored as:
.. code-block:: python
{
"Music Videos": [
"https://url1.com/..."
],
"Concerts: [
"https://url2.com/..."
]
}
"""
return MapVariable(variable_name="subscription_map", definition="{ {} }")
@staticmethod
def subscription_has_download_archive() -> BooleanVariable:
"""
Returns True if the subscription has any entries recorded in a download archive. False
otherwise.
"""
return BooleanVariable(
variable_name="subscription_has_download_archive", definition="{ %bool(True) }"
)
class OverrideHelpers:
@classmethod
def is_entry_variable_name(cls, name: str) -> bool:
"""
Returns
-------
True if the name is an entry variable name. False otherwise.
"""
return name in VARIABLE_SCRIPTS
@classmethod
def is_function_name(cls, name: str) -> bool:
"""
Returns
-------
True if the name is a function name (either built-in or script). False otherwise.
"""
if name.startswith("%"):
return name in CUSTOM_FUNCTION_SCRIPTS or Functions.is_built_in(name[1:])
return False
@classmethod
def is_valid_name(cls, name: str) -> bool:
"""
Returns
-------
True if the override name itself is valid. False otherwise.
"""
if name.startswith("%"):
return is_valid_name(name=name[1:])
return is_valid_name(name=name)
REQUIRED_OVERRIDE_VARIABLES: Set[Variable] = {
SubscriptionVariables.subscription_name(),
SubscriptionVariables.subscription_has_download_archive(),
}
REQUIRED_OVERRIDE_VARIABLE_DEFINITIONS: Dict[str, str] = {
var.variable_name: var.definition for var in REQUIRED_OVERRIDE_VARIABLES
}
REQUIRED_OVERRIDE_VARIABLE_NAMES: Set[str] = {
var.variable_name for var in REQUIRED_OVERRIDE_VARIABLES
}