From 4749193edd23cb37ff12608880bff08c7f93b1c8 Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 13 Jul 2024 07:37:10 +0800 Subject: [PATCH] Allow inverting filter text. Fix #266 --- tubesync/sync/filtering.py | 31 ++++++++++++++----- .../migrations/0023_media_duration_filter.py | 9 ++++++ tubesync/sync/models.py | 5 +++ tubesync/sync/templates/sync/source.html | 5 +-- tubesync/sync/tests.py | 24 ++++++++++++++ tubesync/sync/views.py | 2 +- 6 files changed, 65 insertions(+), 11 deletions(-) diff --git a/tubesync/sync/filtering.py b/tubesync/sync/filtering.py index e936c02..2e1eb1e 100644 --- a/tubesync/sync/filtering.py +++ b/tubesync/sync/filtering.py @@ -62,20 +62,35 @@ def filter_filter_text(instance: Media): if not filter_text: return False - # We match the filter text, so don't skip downloading this + if not instance.source.filter_text_invert: + # We match the filter text, so don't skip downloading this + if instance.source.is_regex_match(instance.title): + log.info( + f"Media: {instance.source} / {instance} has a valid " + f"title filter, not marking to be skipped" + ) + return False + + log.info( + f"Media: {instance.source} / {instance} doesn't match " + f"title filter, marking to be skipped" + ) + + return True + if instance.source.is_regex_match(instance.title): log.info( - f"Media: {instance.source} / {instance} has a valid " - f"title filter, not marking to be skipped" + f"Media: {instance.source} / {instance} matches inverted " + f"title filter, marking to be skipped" ) - return False + + return True log.info( - f"Media: {instance.source} / {instance} doesn't match " - f"title filter, marking to be skipped" + f"Media: {instance.source} / {instance} does not match the inverted " + f"title filter, not marking to be skipped" ) - - return True + return False def filter_max_cap(instance: Media): diff --git a/tubesync/sync/migrations/0023_media_duration_filter.py b/tubesync/sync/migrations/0023_media_duration_filter.py index a77e345..558cc0e 100644 --- a/tubesync/sync/migrations/0023_media_duration_filter.py +++ b/tubesync/sync/migrations/0023_media_duration_filter.py @@ -50,4 +50,13 @@ class Migration(migrations.Migration): "video greater than maximum) video duration", ), ), + migrations.AddField( + model_name="source", + name="filter_text_invert", + field=models.BooleanField( + verbose_name="invert filter text matching", + default=False, + help_text="Invert filter string regex match, skip any matching titles when selected", + ), + ), ] diff --git a/tubesync/sync/models.py b/tubesync/sync/models.py index b71d527..5e1afba 100644 --- a/tubesync/sync/models.py +++ b/tubesync/sync/models.py @@ -298,6 +298,11 @@ class Source(models.Model): blank=True, help_text=_('Regex compatible filter string for video titles') ) + filter_text_invert = models.BooleanField( + _("invert filter text matching"), + default=False, + help_text="Invert filter string regex match, skip any matching titles when selected", + ) filter_seconds = models.PositiveIntegerField( _('filter seconds'), blank=True, diff --git a/tubesync/sync/templates/sync/source.html b/tubesync/sync/templates/sync/source.html index 838eb9b..379cef8 100644 --- a/tubesync/sync/templates/sync/source.html +++ b/tubesync/sync/templates/sync/source.html @@ -44,8 +44,9 @@ Directory
{{ source.directory }} - Filter text - Filter text
{{ source.filter_text }} + Filter text{% if source.filter_text_invert %} Inverted{% endif %} + Filter text{% if source.filter_text_invert %} Inverted{% endif %}
+ {{ source.filter_text }} {% if source.filter_seconds %} diff --git a/tubesync/sync/tests.py b/tubesync/sync/tests.py index f2679d1..7241189 100644 --- a/tubesync/sync/tests.py +++ b/tubesync/sync/tests.py @@ -777,6 +777,30 @@ class MediaFilterTestCase(TestCase): self.assertTrue(changed) self.assertFalse(self.media.skip) + def test_filter_filter_text_invert_nomatch(self): + # Check that if we don't match the filter text, we don't skip + self.media.source.filter_text = "No fancy stuff" + self.media.source.filter_text_invert = True + self.media.skip = True + self.media.published = timezone.make_aware( + datetime(year=2020, month=1, day=1, hour=1, minute=1, second=1) + ) + changed = filter_media(self.media) + self.assertTrue(changed) + self.assertFalse(self.media.skip) + + def test_filter_filter_text_invert_match(self): + # Check that if we match the filter text and do skip + self.media.source.filter_text = "(?i)No fancy stuff" + self.media.source.filter_text_invert = True + self.media.skip = False + self.media.published = timezone.make_aware( + datetime(year=2020, month=1, day=1, hour=1, minute=1, second=1) + ) + changed = filter_media(self.media) + self.assertTrue(changed) + self.assertTrue(self.media.skip) + def test_filter_max_cap_skip(self): # Check if it's older than the max_cap, we don't download it (1 second so it will always fail) self.media.source.download_cap = 1 diff --git a/tubesync/sync/views.py b/tubesync/sync/views.py index ef482ec..8b60a07 100644 --- a/tubesync/sync/views.py +++ b/tubesync/sync/views.py @@ -296,7 +296,7 @@ class ValidateSourceView(FormView): class EditSourceMixin: model = Source - fields = ('source_type', 'key', 'name', 'directory', 'filter_text', 'filter_seconds', 'filter_seconds_min', + fields = ('source_type', 'key', 'name', 'directory', 'filter_text', 'filter_text_invert', 'filter_seconds', 'filter_seconds_min', 'media_format', 'index_schedule', 'download_media', 'download_cap', 'delete_old_media', 'delete_removed_media', 'days_to_keep', 'source_resolution', 'source_vcodec', 'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback', 'copy_channel_images',