Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add ignored required exclude option #6033

Merged
merged 11 commits into from
Jan 24, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### New Features
- Added support for Python 3 (>= 3.5.0) ([#4982](https://github.com/pymedusa/Medusa/pull/4982))
- Added feature to search episodes early or late compared to their scheduled airdate ([#5874](https://github.com/pymedusa/Medusa/pull/5874))
- Added per show required/preferred words exclude option ([#4982](https://github.com/pymedusa/Medusa/pull/6033))

#### Improvements

Expand Down
6 changes: 6 additions & 0 deletions dredd/api-description.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,12 @@ definitions:
description: Required release words
items:
type: string
requiredWordsExclude:
type: boolean
description: Exclude required words from global required words list
ignoredWordsExclude:
type: boolean
description: Exclude ignored words from global ignored words list
airdateOffset:
type: integer
description: Amount of hours we want to start searching early (-1) or late (1) for new episodes
Expand Down
19 changes: 19 additions & 0 deletions medusa/databases/main_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,3 +882,22 @@ def execute(self):
self.addColumn('tv_shows', 'airdate_offset', 'NUMERIC', 0)

self.inc_minor_version()


class AddReleaseIgnoreRequireExludeOptions(AddTvshowStartSearchOffset):
"""Add release ignore and require exclude option flags."""

def test(self):
"""Test if the version is at least 44.14"""
return self.connection.version >= (44, 14)

def execute(self):
utils.backup_database(self.connection.path, self.connection.version)

log.info(u'Adding release ignore and require exclude option flags to the tv_shows table')
if not self.hasColumn('tv_shows', 'rls_require_exclude'):
self.addColumn('tv_shows', 'rls_require_exclude', 'NUMERIC', 0)
if not self.hasColumn('tv_shows', 'rls_ignore_exclude'):
self.addColumn('tv_shows', 'rls_ignore_exclude', 'NUMERIC', 0)

self.inc_minor_version()
2 changes: 2 additions & 0 deletions medusa/server/api/v2/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ def http_patch(self, series_slug, path_param=None):
'config.release.ignoredWords': ListField(series, 'release_ignore_words'),
'config.release.blacklist': ListField(series, 'blacklist'),
'config.release.whitelist': ListField(series, 'whitelist'),
'config.release.requiredWordsExclude': BooleanField(series, 'rls_require_exclude'),
'config.release.ignoredWordsExclude': BooleanField(series, 'rls_ignore_exclude'),
'language': StringField(series, 'lang'),
'config.qualities.allowed': ListField(series, 'qualities_allowed'),
'config.qualities.preferred': ListField(series, 'qualities_preferred'),
Expand Down
26 changes: 20 additions & 6 deletions medusa/tv/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from builtins import map
from builtins import str
from collections import (
namedtuple,
OrderedDict, namedtuple
)
from itertools import groupby

Expand Down Expand Up @@ -226,6 +226,8 @@ def __init__(self, indexer, indexerid, lang='', quality=None,
self.scene = 0
self.rls_ignore_words = ''
self.rls_require_words = ''
self.rls_ignore_exclude = 0
self.rls_require_exclude = 0
self.default_ep_status = SKIPPED
self._location = ''
self.episodes = {}
Expand Down Expand Up @@ -931,13 +933,21 @@ def show_words(self):

# If word is in global ignore and also in show require, then remove it from global ignore
# Join new global ignore with show ignore
final_ignore = show_ignore + [i for i in global_ignore if i.lower() not in [r.lower() for r in show_require]]
# If word is in global require and also in show ignore, then remove it from global require
if not self.rls_ignore_exclude:
final_ignore = show_ignore + [i for i in global_ignore if i.lower() not in [r.lower() for r in show_require]]
else:
final_ignore = [i for i in global_ignore if i.lower() not in [r.lower() for r in show_require] and
i.lower() not in [sh_i.lower() for sh_i in show_ignore]]
# If word is in global require and also in show ignore, then remove it from global requires
# Join new global required with show require
final_require = show_require + [i for i in global_require if i.lower() not in [r.lower() for r in show_ignore]]
if not self.rls_require_exclude:
final_require = show_require + [i for i in global_require if i.lower() not in [r.lower() for r in show_ignore]]
else:
final_require = [gl_r for gl_r in global_require if gl_r.lower() not in [r.lower() for r in show_ignore] and
gl_r.lower() not in [sh_r.lower() for sh_r in show_require]]

ignored_words = final_ignore
required_words = final_require
ignored_words = list(OrderedDict.fromkeys(final_ignore))
required_words = list(OrderedDict.fromkeys(final_require))

return words(preferred_words, undesired_words, ignored_words, required_words)

Expand Down Expand Up @@ -1471,6 +1481,8 @@ def _load_from_db(self):

self.rls_ignore_words = sql_results[0]['rls_ignore_words']
self.rls_require_words = sql_results[0]['rls_require_words']
self.rls_ignore_exclude = sql_results[0]['rls_ignore_exclude']
self.rls_require_exclude = sql_results[0]['rls_require_exclude']

self.default_ep_status = int(sql_results[0]['default_ep_status'] or SKIPPED)

Expand Down Expand Up @@ -2082,6 +2094,8 @@ def to_json(self, detailed=True, fetch=False):
data['config']['release'] = {}
data['config']['release']['ignoredWords'] = self.release_ignore_words
data['config']['release']['requiredWords'] = self.release_required_words
data['config']['release']['ignoredWordsExclude'] = bool(self.rls_ignore_exclude)
data['config']['release']['requiredWordsExclude'] = bool(self.rls_require_exclude)
data['config']['airdateOffset'] = self.airdate_offset

# These are for now considered anime-only options
Expand Down
94 changes: 94 additions & 0 deletions tests/test_words.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,97 @@ def has_words_lazy(item, words):
"""Test if item contains words lazily."""
found_words = any(contains_words(item, words))
assert found_words, (item, words)


@pytest.mark.parametrize('p', [
# The regular Show uses xem data. To map scene S06E29 to indexer S06E28
{
'series_info': {
'name': u'Regular Show',
'is_scene': False
},
'global': {
'ignored': ['pref1', 'pref2', 'pref3'],
},
'series': {
'ignored': 'pref1,pref5,pref6',
'exclude_ignored': False,
},
'expected_ignored': [u'pref1', u'pref5', u'pref6', u'pref2', u'pref3'],
},
{
'series_info': {
'name': u'Regular Show',
'is_scene': False
},
'global': {
'ignored': ['pref1', 'pref2', 'pref3'],
},
'series': {
'ignored': 'pref1,pref2',
'exclude_ignored': True,
},
'expected_ignored': [u'pref3'],
},

])
def test_combine_ignored_words(p, create_tvshow, app_config):
app_config('IGNORE_WORDS', p['global']['ignored'])

# confirm passed in show object indexer id matches result show object indexer id
series = create_tvshow(name=p['series_info']['name'])
series.rls_ignore_words = p['series']['ignored']
series.rls_ignore_exclude = p['series']['exclude_ignored']

actual = series.show_words()

expected = p['expected_ignored']

assert expected == actual.ignored_words


@pytest.mark.parametrize('p', [
# The regular Show uses xem data. To map scene S06E29 to indexer S06E28
{
'series_info': {
'name': u'Regular Show',
'is_scene': False
},
'global': {
'required': ['req1', 'req2', 'req3']
},
'series': {
'required': 'req1,req2,req4',
'exclude_required': False,
},
'expected_required': [u'req1', u'req2', u'req4', u'req3'],
},
{
'series_info': {
'name': u'Regular Show',
'is_scene': False
},
'global': {
'required': ['req1', 'req2', 'req3']
},
'series': {
'required': 'req2',
'exclude_required': True,
},
'expected_required': [u'req1', u'req3'],
},

])
def test_combine_required_words(p, create_tvshow, app_config):
app_config('REQUIRE_WORDS', p['global']['required'])

# confirm passed in show object indexer id matches result show object indexer id
series = create_tvshow(name=p['series_info']['name'])
series.rls_require_words = p['series']['required']
series.rls_require_exclude = p['series']['exclude_required']

actual = series.show_words()

expected = p['expected_required']

assert expected == actual.required_words
10 changes: 9 additions & 1 deletion themes-default/slim/src/store/modules/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ const state = {
location: null,
paused: null,
qualities: null,
release: null,
release: {
requiredWords: [],
ignoredWords: [],
blacklist: [],
whitelist: [],
allgroups: [],
requiredWordsExclude: null,
ignoredWordsExclude: null
},
scene: null,
seasonFolders: null,
sports: null,
Expand Down
Loading