[FEATURE] Match filters plugin (#365)

This commit is contained in:
Jesse Bannon 2022-11-26 22:32:19 -08:00 committed by GitHub
parent 2f792f774c
commit a77885f4de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 0 deletions

View file

@ -152,6 +152,14 @@ file_convert
-------------------------------------------------------------------------------
match_filters
'''''''''''''
.. autoclass:: ytdl_sub.plugins.match_filters.MatchFiltersOptions()
:members:
:exclude-members: partial_validate
-------------------------------------------------------------------------------
music_tags
''''''''''
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()

View file

@ -15,6 +15,7 @@ from ytdl_sub.plugins.chapters import ChaptersPlugin
from ytdl_sub.plugins.date_range import DateRangePlugin
from ytdl_sub.plugins.file_convert import FileConvertPlugin
from ytdl_sub.plugins.internal.view import ViewPlugin
from ytdl_sub.plugins.match_filters import MatchFiltersPlugin
from ytdl_sub.plugins.music_tags import MusicTagsPlugin
from ytdl_sub.plugins.nfo_tags import NfoTagsPlugin
from ytdl_sub.plugins.output_directory_nfo_tags import OutputDirectoryNfoTagsPlugin
@ -121,6 +122,7 @@ class PluginMapping:
"audio_extract": AudioExtractPlugin,
"date_range": DateRangePlugin,
"file_convert": FileConvertPlugin,
"match_filters": MatchFiltersPlugin,
"music_tags": MusicTagsPlugin,
"video_tags": VideoTagsPlugin,
"nfo_tags": NfoTagsPlugin,

View file

@ -0,0 +1,78 @@
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from yt_dlp import match_filter_func
from ytdl_sub.plugins.plugin import Plugin
from ytdl_sub.plugins.plugin import PluginOptions
from ytdl_sub.utils.logger import Logger
from ytdl_sub.validators.validators import StringListValidator
logger = Logger.get("match_filters")
class MatchFiltersOptions(PluginOptions):
"""
Set ``--match-filters``` to pass into yt-dlp to filter entries from being downloaded.
Uses the same syntax as yt-dlp.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
match_filters:
filters: "original_url!*=/shorts/"
Supports one or multiple filters:
.. code-block:: yaml
presets:
my_example_preset:
match_filters:
filters:
- "original_url!*=/shorts/"
- "!is_live"
"""
_required_keys = {"filters"}
@classmethod
def partial_validate(cls, name: str, value: Any) -> None:
"""Ensure filters looks right"""
if isinstance(value, dict):
value["filters"] = value.get("filters", [""])
_ = cls(name, value)
def __init__(self, name, value):
super().__init__(name, value)
self._filters = self._validate_key(key="filters", validator=StringListValidator).list
@property
def filters(self) -> List[str]:
"""
The filters themselves. If used multiple times, the filter matches if at least one of the
conditions are met.
"""
return [validator.value for validator in self._filters]
class MatchFiltersPlugin(Plugin[MatchFiltersOptions]):
plugin_options_type = MatchFiltersOptions
def ytdl_options(self) -> Optional[Dict]:
"""
Returns
-------
match_filter after calling the utility function for it
"""
match_filters: List[str] = []
for filter_str in self.plugin_options.filters:
logger.debug("Adding match-filter %s", filter_str)
match_filters.append(filter_str)
return {"match_filter": match_filter_func(match_filters)}

View file

@ -12,6 +12,7 @@ from ytdl_sub.plugins.audio_extract import AudioExtractPlugin
from ytdl_sub.plugins.chapters import ChaptersPlugin
from ytdl_sub.plugins.date_range import DateRangePlugin
from ytdl_sub.plugins.file_convert import FileConvertPlugin
from ytdl_sub.plugins.match_filters import MatchFiltersPlugin
from ytdl_sub.plugins.plugin import Plugin
from ytdl_sub.plugins.subtitles import SubtitlesPlugin
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
@ -110,6 +111,7 @@ class SubscriptionYTDLOptions:
self._global_options,
self._output_options,
self._plugin_ytdl_options(DateRangePlugin),
self._plugin_ytdl_options(MatchFiltersPlugin),
self._user_ytdl_options, # user ytdl options...
self._info_json_only_options, # then info_json_only options
)

View file

@ -0,0 +1,36 @@
import pytest
from ytdl_sub.subscriptions.subscription import Subscription
@pytest.fixture
def preset_dict(output_directory):
return {
"preset": "music_video",
"download": {"url": "https://www.youtube.com/watch?v=2zYF9JLHDmA"},
"output_options": {"output_directory": output_directory},
# download the worst format so it is fast
"ytdl_options": {
"postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility
},
"match_filters": {"filters": ["is_live", "view_count <=? 10"]},
}
class TestFileConvert:
@pytest.mark.parametrize("dry_run", [True, False])
def test_match_filters(
self,
music_video_config,
preset_dict,
output_directory,
dry_run,
):
subscription = Subscription.from_dict(
config=music_video_config,
preset_name="match_filter_test",
preset_dict=preset_dict,
)
transaction_log = subscription.download(dry_run=dry_run)
assert transaction_log.is_empty