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

Add scene naming support for non anime shows #8155

Merged
merged 10 commits into from
May 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions medusa/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1020,10 +1020,9 @@ def get_show(name, try_indexers=False):

# try scene exceptions
if not series:
found_exceptions = list(scene_exceptions.get_scene_exceptions_by_name(series_name))
if found_exceptions:
# Only use the first exception.
series = Show.find_by_id(app.showList, found_exceptions[0].indexer, found_exceptions[0].series_id)
found_exception = scene_exceptions.get_scene_exception_by_name(series_name)
if found_exception:
series = Show.find_by_id(app.showList, found_exception.indexer, found_exception.series_id)

if not series:
match_name_only = (s.name for s in app.showList if text_type(s.imdb_year) in s.name and
Expand Down
11 changes: 5 additions & 6 deletions medusa/name_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,7 @@ def _parse_anime(result):
# For example Diamond is unbreakable - 26 -> Season 4 -> Absolute number 100 -> tvdb S03E26
season_exception = None
if result.season_number is None:
season_exception = list(scene_exceptions.get_scene_exceptions_by_name(result.series_name))
if season_exception:
season_exception = season_exception[0].season
season_exception = scene_exceptions.get_season_from_name(result.series, result.series_name)

if result.ab_episode_numbers:
for absolute_episode in result.ab_episode_numbers:
Expand All @@ -200,7 +198,7 @@ def _parse_anime(result):
if season_exception is not None or result.series.is_scene:
# Get absolute number from custom numbering (1), XEM (2) or indexer (3)
a = scene_numbering.get_indexer_absolute_numbering(
result.series, a, True, season_exception
result.series, a, fallback_to_xem=True, scene_season=season_exception
)

new_absolute_numbers.append(a)
Expand Down Expand Up @@ -270,14 +268,15 @@ def _parse_series(result):
new_season_numbers = []
new_absolute_numbers = []

season = scene_exceptions.get_season_from_name(result.series, result.series_name) or result.season_number

for episode_number in result.episode_numbers:
season = result.season_number
episode = episode_number

if result.series.is_scene:
(season, episode) = scene_numbering.get_indexer_numbering(
result.series,
result.season_number,
season,
episode_number
)
log.debug(
Expand Down
42 changes: 28 additions & 14 deletions medusa/scene_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ def get_season_scene_exceptions(series_obj, season=-1):
return set(exceptions_list)


def get_season_from_name(series_obj, exception_name):
"""
Get season number from exceptions_cache for a series scene exception name.

Use this method if you expect to get back a season number from a scene exception.
:param series_obj: A Series object.
:param series_name: The scene exception name.

:return: The season number or None.
"""
exceptions_list = exceptions_cache[(series_obj.indexer, series_obj.series_id)]
for season, exceptions in exceptions_list.items():
# Skip whole series exceptions
if season == -1:
continue
for exception in exceptions:
if exception.title == exception_name:
return exception.season


def get_all_scene_exceptions(series_obj):
"""
Get all scene exceptions for a show ID.
Expand All @@ -157,22 +177,18 @@ def get_all_scene_exceptions(series_obj):
return exceptions_cache.get((series_obj.indexer, series_obj.series_id), defaultdict(set))


def get_scene_exceptions_by_name(show_name):
"""Get the series_id, season and indexer of the scene exception."""
def get_scene_exception_by_name(series_name):
"""Get the season of a scene exception."""
# Flatten the exceptions_cache.
scene_exceptions = set()
scene_exceptions = []
for exception_set in list(exceptions_cache.values()):
for title_exception in list(exception_set.values()):
scene_exceptions.update(title_exception)
scene_exceptions += title_exception

matches = set()
# First attempt exact match.
for title_exception in scene_exceptions:
if show_name == title_exception.title:
matches.add(title_exception)

if matches:
return matches
if series_name == title_exception.title:
return title_exception

# Let's try out some sanitized names.
for title_exception in scene_exceptions:
Expand All @@ -182,15 +198,13 @@ def get_scene_exceptions_by_name(show_name):
sanitized_name.lower().replace('.', ' '),
)

if show_name.lower() in titles:
if series_name.lower() in titles:
logger.debug(
'Scene exception lookup got series id {title_exception.series_id} '
'from indexer {title_exception.indexer},'
' using that', title_exception=title_exception
)
matches.add(title_exception)

return matches
return title_exception


def update_scene_exceptions(series_obj, scene_exceptions):
Expand Down
111 changes: 0 additions & 111 deletions medusa/scene_numbering.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,117 +518,6 @@ def xem_refresh(series_obj, force=False):
logger.log(traceback.format_exc(), logger.DEBUG)


def fix_xem_numbering(series_obj): # pylint:disable=too-many-locals, too-many-branches, too-many-statements
"""
Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings
for an entire show. Both the keys and values of the dict are tuples.
Will be empty if there are no scene numbers set in xem
"""
if series_obj is None:
return {}

main_db_con = db.DBConnection()
rows = main_db_con.select(
'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number '
'FROM tv_episodes '
'WHERE indexer = ? AND showid = ?',
[series_obj.indexer, series_obj.series_id])

last_absolute_number = None
last_scene_season = None
last_scene_episode = None
last_scene_absolute_number = None

update_absolute_number = False
update_scene_season = False
update_scene_episode = False
update_scene_absolute_number = False

logger.log(
u'Fixing any XEM scene mapping issues for show ID {0} on {1}'.format(series_obj.series_id, series_obj.indexer_name),
logger.DEBUG)

cl = []
for row in rows:
season = int(row['season'])
episode = int(row['episode'])

if not int(row['scene_season']) and last_scene_season:
scene_season = last_scene_season + 1
update_scene_season = True
else:
scene_season = int(row['scene_season'])
if last_scene_season and scene_season < last_scene_season:
scene_season = last_scene_season + 1
update_scene_season = True

if not int(row['scene_episode']) and last_scene_episode:
scene_episode = last_scene_episode + 1
update_scene_episode = True
else:
scene_episode = int(row['scene_episode'])
if last_scene_episode and scene_episode < last_scene_episode:
scene_episode = last_scene_episode + 1
update_scene_episode = True

# check for unset values and correct them
if not int(row['absolute_number']) and last_absolute_number:
absolute_number = last_absolute_number + 1
update_absolute_number = True
else:
absolute_number = int(row['absolute_number'])
if last_absolute_number and absolute_number < last_absolute_number:
absolute_number = last_absolute_number + 1
update_absolute_number = True

if not int(row['scene_absolute_number']) and last_scene_absolute_number:
scene_absolute_number = last_scene_absolute_number + 1
update_scene_absolute_number = True
else:
scene_absolute_number = int(row['scene_absolute_number'])
if last_scene_absolute_number and scene_absolute_number < last_scene_absolute_number:
scene_absolute_number = last_scene_absolute_number + 1
update_scene_absolute_number = True

# store values for lookup on next iteration
last_absolute_number = absolute_number
last_scene_season = scene_season
last_scene_episode = scene_episode
last_scene_absolute_number = scene_absolute_number

if update_absolute_number:
cl.append([
'UPDATE tv_episodes SET absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?',
[absolute_number, series_obj.indexer, series_obj.series_id, season, episode]
])
update_absolute_number = False

if update_scene_season:
cl.append([
'UPDATE tv_episodes SET scene_season = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?',
[scene_season, series_obj.indexer, series_obj.series_id, season, episode]
])
update_scene_season = False

if update_scene_episode:
cl.append([
'UPDATE tv_episodes SET scene_episode = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?',
[scene_episode, series_obj.indexer, series_obj.series_id, season, episode]
])
update_scene_episode = False

if update_scene_absolute_number:
cl.append([
'UPDATE tv_episodes SET scene_absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?',
[scene_absolute_number, series_obj.indexer, series_obj.series_id, season, episode]
])
update_scene_absolute_number = False

if cl:
main_db_con = db.DBConnection()
main_db_con.mass_action(cl)


def numbering_tuple_to_dict(values, left_desc='source', right_desc='destination', level_2_left='season', level_2_right='episode'):
"""
Convert a dictionary with tuple to tuple (key/value) mapping to a json structure.
Expand Down
5 changes: 2 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,13 @@ def mock_function(mocks):
Example: The following structure will mock two functions with their expected return values.
[
('medusa.scene_numbering.get_indexer_numbering', (None, None)),
('get_scene_exceptions_by_name': [(70668, 2, 1)]),
('medusa.scene_exceptions.get_season_from_name', 2),
]
:mocks: A list of two value tuples.
"""

for function_to_mock, return_value in mocks:
def create_function(return_value):
def create_return(*args):
def create_return(*args, **kwargs):
return return_value
return create_return

Expand Down
8 changes: 4 additions & 4 deletions tests/legacy/scene_helpers_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ def test_scene_ex_babylon_5(self):

@unittest.expectedFailure
def test_scene_ex_by_name(self):
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('Babylon5'), (70726, -1))
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('babylon 5'), (70726, -1))
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('Carlos 2010'), (164451, -1))
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('Babylon5'), None)
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('babylon 5'), None)
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('Carlos 2010'), None)

def test_scene_ex_by_name_empty(self):
self.assertEqual(scene_exceptions.get_scene_exceptions_by_name('nothing useful'), [(None, None, None)])
self.assertEqual(scene_exceptions.get_scene_exception_by_name('nothing useful'), None)

def test_scene_ex_reset_name_cache(self):
# clear the exceptions
Expand Down
Loading