[FEATURE] Match filters plugin (#365)
This commit is contained in:
parent
2f792f774c
commit
a77885f4de
5 changed files with 126 additions and 0 deletions
|
|
@ -152,6 +152,14 @@ file_convert
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
match_filters
|
||||||
|
'''''''''''''
|
||||||
|
.. autoclass:: ytdl_sub.plugins.match_filters.MatchFiltersOptions()
|
||||||
|
:members:
|
||||||
|
:exclude-members: partial_validate
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
music_tags
|
music_tags
|
||||||
''''''''''
|
''''''''''
|
||||||
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()
|
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from ytdl_sub.plugins.chapters import ChaptersPlugin
|
||||||
from ytdl_sub.plugins.date_range import DateRangePlugin
|
from ytdl_sub.plugins.date_range import DateRangePlugin
|
||||||
from ytdl_sub.plugins.file_convert import FileConvertPlugin
|
from ytdl_sub.plugins.file_convert import FileConvertPlugin
|
||||||
from ytdl_sub.plugins.internal.view import ViewPlugin
|
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.music_tags import MusicTagsPlugin
|
||||||
from ytdl_sub.plugins.nfo_tags import NfoTagsPlugin
|
from ytdl_sub.plugins.nfo_tags import NfoTagsPlugin
|
||||||
from ytdl_sub.plugins.output_directory_nfo_tags import OutputDirectoryNfoTagsPlugin
|
from ytdl_sub.plugins.output_directory_nfo_tags import OutputDirectoryNfoTagsPlugin
|
||||||
|
|
@ -121,6 +122,7 @@ class PluginMapping:
|
||||||
"audio_extract": AudioExtractPlugin,
|
"audio_extract": AudioExtractPlugin,
|
||||||
"date_range": DateRangePlugin,
|
"date_range": DateRangePlugin,
|
||||||
"file_convert": FileConvertPlugin,
|
"file_convert": FileConvertPlugin,
|
||||||
|
"match_filters": MatchFiltersPlugin,
|
||||||
"music_tags": MusicTagsPlugin,
|
"music_tags": MusicTagsPlugin,
|
||||||
"video_tags": VideoTagsPlugin,
|
"video_tags": VideoTagsPlugin,
|
||||||
"nfo_tags": NfoTagsPlugin,
|
"nfo_tags": NfoTagsPlugin,
|
||||||
|
|
|
||||||
78
src/ytdl_sub/plugins/match_filters.py
Normal file
78
src/ytdl_sub/plugins/match_filters.py
Normal 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)}
|
||||||
|
|
@ -12,6 +12,7 @@ from ytdl_sub.plugins.audio_extract import AudioExtractPlugin
|
||||||
from ytdl_sub.plugins.chapters import ChaptersPlugin
|
from ytdl_sub.plugins.chapters import ChaptersPlugin
|
||||||
from ytdl_sub.plugins.date_range import DateRangePlugin
|
from ytdl_sub.plugins.date_range import DateRangePlugin
|
||||||
from ytdl_sub.plugins.file_convert import FileConvertPlugin
|
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.plugin import Plugin
|
||||||
from ytdl_sub.plugins.subtitles import SubtitlesPlugin
|
from ytdl_sub.plugins.subtitles import SubtitlesPlugin
|
||||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||||
|
|
@ -110,6 +111,7 @@ class SubscriptionYTDLOptions:
|
||||||
self._global_options,
|
self._global_options,
|
||||||
self._output_options,
|
self._output_options,
|
||||||
self._plugin_ytdl_options(DateRangePlugin),
|
self._plugin_ytdl_options(DateRangePlugin),
|
||||||
|
self._plugin_ytdl_options(MatchFiltersPlugin),
|
||||||
self._user_ytdl_options, # user ytdl options...
|
self._user_ytdl_options, # user ytdl options...
|
||||||
self._info_json_only_options, # then info_json_only options
|
self._info_json_only_options, # then info_json_only options
|
||||||
)
|
)
|
||||||
|
|
|
||||||
36
tests/e2e/plugins/test_match_filters.py
Normal file
36
tests/e2e/plugins/test_match_filters.py
Normal 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
|
||||||
Loading…
Reference in a new issue