[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
|
||||
''''''''''
|
||||
.. 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.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,
|
||||
|
|
|
|||
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.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
|
||||
)
|
||||
|
|
|
|||
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