[BACKEND] %contains_any function, list + dict support for tilda overrides mode (#1054)

Adds the function `%contains_any`, and list support in a tilda override subscription. The end-goal of these features are to more easily add a title exclude list, like so:

```
__preset__:
  filter_exclude:
    - "{%contains_any( %lower(title), exclude_title_strings )}"

...
Jellyfin TV Show by Date:
  ~History Documentaries:
    url: "https://..."
    exclude_title_strings:
      - "trailer"
      - "preview"
```

A proper prebuilt preset or built-in functionality will follow this change.
This commit is contained in:
Jesse Bannon 2024-09-20 09:23:22 -07:00 committed by GitHub
parent 759948361f
commit 10cb82a69a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 42 additions and 2 deletions

View file

@ -595,6 +595,13 @@ contains
:description:
Returns True if ``contains`` is in ``string``. False otherwise.
contains_any
~~~~~~~~~~~~
:spec: ``contains_any(string: String, contains_array: Array) -> Boolean``
:description:
Returns true if any element in ``contains_array`` is in ``string``. False otherwise.
lower
~~~~~
:spec: ``lower(string: String) -> String``

View file

@ -3,6 +3,7 @@ from typing import Optional
from ytdl_sub.script.types.array import Array
from ytdl_sub.script.types.resolvable import AnyArgument
from ytdl_sub.script.types.resolvable import Boolean
from ytdl_sub.script.types.resolvable import Float
from ytdl_sub.script.types.resolvable import Integer
from ytdl_sub.script.types.resolvable import Numeric
from ytdl_sub.script.types.resolvable import String
@ -25,6 +26,20 @@ class StringFunctions:
"""
return Boolean(contains.value in string.value)
@staticmethod
def contains_any(string: String, contains_array: Array) -> Boolean:
"""
:description:
Returns true if any element in ``contains_array`` is in ``string``. False otherwise.
"""
return Boolean(
any(
str(val) in string.value
for val in contains_array.value
if isinstance(val, (String, Integer, Boolean, Float))
)
)
@staticmethod
def slice(string: String, start: Integer, end: Optional[Integer] = None) -> String:
"""

View file

@ -11,7 +11,7 @@ from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.config.overrides import Overrides
from ytdl_sub.entries.variables.override_variables import SubscriptionVariables
from ytdl_sub.utils.script import ScriptUtils
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import UnstructuredDictFormatterValidator
from ytdl_sub.validators.validators import DictValidator
from ytdl_sub.validators.validators import LiteralDictValidator
from ytdl_sub.validators.validators import StringListValidator
@ -196,7 +196,9 @@ class SubscriptionListValuesValidator(SubscriptionLeafValidator, StringListValid
)
class SubscriptionWithOverridesValidator(SubscriptionLeafValidator, DictFormatterValidator):
class SubscriptionWithOverridesValidator(
SubscriptionLeafValidator, UnstructuredDictFormatterValidator
):
def __init__(
self,
name,

View file

@ -159,6 +159,12 @@ def preset_with_subscription_overrides_tilda(
"parent_preset_2 | parent_preset_1": {
"~ test_2_1": {
"current_override": "test_2_1",
"custom_list": [
"elem1",
"elem2",
"elem3",
],
"passed_list_elem": "{%contains_any('elem2', custom_list)}",
}
},
},
@ -262,6 +268,8 @@ def test_subscription_overrides_tilda(
assert sub_2_1.get("subscription_name").native == "test_2_1"
assert sub_2_1.get("current_override").native == "test_2_1" # tilda sub takes precedence
assert sub_2_1.get("passed_list_elem").native is True
assert sub_2_1.get("custom_list").native == ["elem1", "elem2", "elem3"]
def test_subscription_overrides_map(

View file

@ -118,6 +118,14 @@ class TestNumericFunctions:
output = single_variable_output(f"{{%contains('a brown dog', '{value}')}}")
assert output == expected_output
@pytest.mark.parametrize(
"value, expected_output",
[("['a', 'b', 'c']", True), ("['nope', [], {}]", False), ("['at', 'dog']", True)],
)
def test_contains_any(self, value, expected_output):
output = single_variable_output(f"{{%contains_any('a brown dog', {value})}}")
assert output == expected_output
@pytest.mark.parametrize(
"input_string, split, max_split, expected_output",
[