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

Added initial IMDB indexer. #3603

Merged
merged 90 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
c8e276e
Added initial IMDB indexer.
p0psicles Jan 12, 2018
b76d78e
Don't add video games and tv short.
p0psicles Jan 12, 2018
6912013
Fixed setting the imdb_id external.
p0psicles Jan 13, 2018
e94dd24
Added images (only poster for now).
p0psicles Jan 13, 2018
81f06ef
Added show status and firstaired using releases route.
p0psicles Jan 14, 2018
e325619
Added better support for poster and poster_thumbs.
p0psicles Jan 14, 2018
dc25c36
Added routes that still need to be added to imdbpie.py
p0psicles Jan 14, 2018
6222031
Fix show_url
duramato Jan 14, 2018
8b4e251
Merge remote-tracking branch 'remotes/origin/develop' into feature/ad…
p0psicles Feb 8, 2018
2223f45
Fix network and images.
p0psicles Feb 8, 2018
80a607b
Added episode rating, overview and airdate.
p0psicles Feb 9, 2018
cad5dea
Fixed bug where adding show without a summary, throwed an error.
p0psicles Feb 9, 2018
d3a4e7d
Added actors.
p0psicles Feb 9, 2018
06a99f4
Added fanart and production_art (as fanart).
p0psicles Feb 10, 2018
730620a
Use the poster initially provided with the show, as the images api, d…
p0psicles Feb 10, 2018
eace45e
Refactor indexers.
p0psicles Feb 10, 2018
4d9986b
Use get_nested_value() function for tmdb.
p0psicles Feb 11, 2018
817e695
tvmaze exceptions got removed.
p0psicles Feb 11, 2018
f3e87f3
Get airs day of week, from last 10 airdates.
p0psicles Feb 11, 2018
20b0d48
Added episode thumbnails with kodi_12plus.
p0psicles Feb 11, 2018
203ffbd
Merge remote-tracking branch 'remotes/origin/develop' into feature/ad…
p0psicles Feb 18, 2018
f1bfbc1
Fix issues with shows missing info.
p0psicles Feb 18, 2018
f956eec
Fix exception for when search doesn't return any results.
p0psicles Feb 19, 2018
65fb5d6
Use templating for the show_url.
p0psicles Feb 19, 2018
1800961
Change the addShows addShows/searchIndexersForShowName function to re…
p0psicles Feb 19, 2018
05d08c6
Fixed searching for The Tick.
p0psicles Feb 19, 2018
8d601eb
Disable the delay on ShowUpdater.
p0psicles Mar 15, 2018
81304ae
Update imdbpie to latest develop.
p0psicles Mar 15, 2018
4521d70
Modify get_last_updated_series parameters.
p0psicles Mar 15, 2018
6d1e970
Start changing show_updater.py to add the ability of adding using per…
p0psicles Mar 15, 2018
9a907b2
Merge remote-tracking branch 'remotes/origin/develop' into feature/ad…
p0psicles Mar 16, 2018
1ccbbea
Added per season calculated intervals for show updates.
p0psicles Mar 16, 2018
1b90fe9
restored the show update delay.
p0psicles Mar 16, 2018
920156f
Changed per season update to show updates for imdb.
p0psicles Mar 16, 2018
4ec0fde
Moved the show search in the exception handling block.
p0psicles Mar 17, 2018
36dfa7b
Merge remote-tracking branch 'origin/develop' into feature/add-imdb-i…
p0psicles Feb 18, 2022
6ed906a
Fix conflicts
p0psicles Feb 18, 2022
e48c213
Fix adding shows using imdb
p0psicles Feb 18, 2022
8a4a62a
And it loads the displayShow
p0psicles Feb 18, 2022
2c57175
Fixed a number of bugs
p0psicles Feb 19, 2022
4a0f630
Fix status tmdb
p0psicles Feb 20, 2022
ea901f3
Fix method call get_last_seasons for tvdb
p0psicles Feb 20, 2022
7858ba5
Fix show_updater
p0psicles Feb 20, 2022
8a6e227
Fix get external lookups for imdb.
p0psicles Feb 20, 2022
ad540a2
Fix getting rating/contentrating from imdb.
p0psicles Feb 21, 2022
2f6123e
Fix show imdb icon for non-imdb shows
p0psicles Feb 21, 2022
34d9b68
Fix regressions on providers with absolute numbering
p0psicles Feb 21, 2022
bdf374e
fixing old merge conflicts
p0psicles Feb 21, 2022
76c644e
Not using this
p0psicles Feb 21, 2022
7992b6d
Fix more indexer merge conflicts
p0psicles Feb 21, 2022
7fc2c80
Fix more merge conflicts
p0psicles Feb 21, 2022
5ff8c06
More merge conflicts
p0psicles Feb 21, 2022
e678649
revert the show_updater scheduler
p0psicles Feb 21, 2022
4025d71
Fix change-indexer
p0psicles Feb 21, 2022
8f6a1ab
Fix season updating for imdb
p0psicles Feb 21, 2022
6d3c8be
comment show_updater
p0psicles Feb 21, 2022
a572ca2
fix kodi metadata actor error
p0psicles Feb 21, 2022
4bd36a0
Fix get origin_country
p0psicles Feb 21, 2022
7f13fe2
Fix change-indexer
p0psicles Feb 22, 2022
57c1271
Lots of fixes:
p0psicles Feb 22, 2022
52a77e5
Fixed bug with actor without role?
p0psicles Feb 22, 2022
667203a
Add search imdb by id.
p0psicles Feb 22, 2022
ee0ba44
fix: Add existing shows
p0psicles Feb 22, 2022
ff23600
Make searching more flexible
p0psicles Feb 22, 2022
bf3ad02
added externals component
p0psicles Feb 22, 2022
8ec6e04
Get the thumb version for poster.
p0psicles Feb 22, 2022
fb6874f
Fix imdb status
p0psicles Feb 23, 2022
1303532
Extended tmdb exception handling.
p0psicles Feb 23, 2022
fd5f013
Fix error with lastaired
p0psicles Feb 23, 2022
76b8246
Adding log for when tt id's are cleaned from indexer_mapping.
p0psicles Feb 23, 2022
b8c133b
Fix get_episode error
p0psicles Feb 23, 2022
baaf043
Clean recommended_db
p0psicles Feb 23, 2022
e14be33
Improve searching
p0psicles Feb 23, 2022
d783c2a
add logs
p0psicles Feb 23, 2022
63b7444
simplify clean imdb tt
p0psicles Feb 23, 2022
77e7632
Fix parsing images tmdb
p0psicles Feb 24, 2022
41a688b
Fix exception handling for show_updater when updating through indexer…
p0psicles Feb 24, 2022
a6a8226
Get posters and banners by aspect_ratio
p0psicles Feb 25, 2022
b8e6fff
New imdb sorting logic
p0psicles Feb 25, 2022
19e3363
Improve the show season updates.
p0psicles Feb 25, 2022
6db36ff
Also applied to the other indexers
p0psicles Feb 25, 2022
aaefba0
Refactored the get_last_updated_seasons method.
p0psicles Feb 26, 2022
f68957b
comment
p0psicles Feb 26, 2022
5c273af
Fix imdb id parse error
p0psicles Feb 28, 2022
1cc83c8
Fix diverse imdb id mapping
p0psicles Feb 28, 2022
e23ebf6
Add login response check
p0psicles Feb 28, 2022
b3d7a8d
Added imdb exception handling
p0psicles Feb 28, 2022
5e8096d
Fix flake warnings
p0psicles Mar 1, 2022
763289e
Refactor and fix flake
p0psicles Mar 1, 2022
983f51b
Fix jest tests
p0psicles Mar 1, 2022
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
1 change: 1 addition & 0 deletions medusa/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ def initialize(self, console_logging=True):
# initialize the recommended shows database
recommended_db_con = db.DBConnection('recommended.db')
db.upgradeDatabase(recommended_db_con, recommended_db.InitialSchema)
db.sanityCheckDatabase(recommended_db_con, recommended_db.RecommendedSanityCheck)

# Performs a vacuum on cache.db
logger.debug(u'Performing a vacuum on the CACHE database')
Expand Down
25 changes: 24 additions & 1 deletion medusa/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@

from dateutil import parser


from medusa import app, ws
from medusa.common import (
MULTI_EP_RESULT,
Quality,
SEASON_RESULT,
)
from medusa.helper.common import sanitize_filename
from medusa.logger.adapters.style import BraceAdapter
from medusa.search import SearchType

from six import itervalues

from trans import trans

log = BraceAdapter(logging.getLogger(__name__))
log.logger.addHandler(logging.NullHandler())

Expand Down Expand Up @@ -365,6 +369,22 @@ def select_series(self, all_series):
search_results = []
series_names = []

def searchterm_in_result(search_term, search_result):
norm_search_term = sanitize_filename(search_term.lower())
norm_result = sanitize_filename(search_result.lower())

if norm_search_term in norm_result:
return True

# translates national characters into similar sounding latin characters
# For ex. Физрук -> Fizruk
search_term_alpha = trans(self.config['searchterm'])

if search_term_alpha != search_term and search_term_alpha in norm_result:
return True

return False

# get all available shows
if all_series:
if 'searchterm' in self.config:
Expand All @@ -382,8 +402,11 @@ def select_series(self, all_series):
if search_term.isdigit():
series_names.append(search_term)

if search_term.startswith('tt'):
series_names.append(search_term)

for name in series_names:
if search_term.lower() in name.lower():
if searchterm_in_result(search_term, name):
if 'firstaired' not in cur_show:
default_date = parser.parse('1900-01-01').date()
cur_show['firstaired'] = default_date.strftime(dateFormat)
Expand Down
20 changes: 20 additions & 0 deletions medusa/databases/cache_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
# Add new migrations at the bottom of the list
# and subclass the previous migration.
class InitialSchema(db.SchemaUpgrade):
"""Cache.db initial schema class."""

def test(self):
"""Test db version."""
return self.hasTable('db_version')

def execute(self):
"""Execute."""
queries = [
('CREATE TABLE lastUpdate (provider TEXT, time NUMERIC);',),
('CREATE TABLE lastSearch (provider TEXT, time NUMERIC);',),
Expand Down Expand Up @@ -229,3 +233,19 @@ def test(self):
def execute(self):
self.connection.action('DROP TABLE IF EXISTS scene_exceptions;')
self.inc_major_version()


class AddSeasonUpdatesTable(RemoveSceneExceptionsTable): # pylint:disable=too-many-ancestors
def test(self):
return self.hasTable('season_updates')

def execute(self):
self.connection.action(
"""CREATE TABLE "season_updates" (
`season_updates_id` INTEGER,
`indexer` INTEGER NOT NULL,
`series_id` INTEGER NOT NULL,
`season` INTEGER,
`time` INTEGER,
PRIMARY KEY(season_updates_id))"""
)
8 changes: 8 additions & 0 deletions medusa/databases/main_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def check(self):
self.fix_show_nfo_lang()
self.fix_subtitle_reference()
self.clean_null_indexer_mappings()
self.clean_imdb_tt_ids()

def clean_null_indexer_mappings(self):
log.debug(u'Checking for null indexer mappings')
Expand Down Expand Up @@ -219,6 +220,13 @@ def fix_subtitles_codes(self):
def fix_show_nfo_lang(self):
self.connection.action("UPDATE tv_shows SET lang = '' WHERE lang = 0 OR lang = '0';")

def clean_imdb_tt_ids(self):
# Get all records with 'tt'
log.debug(u'Cleaning indexer_mapping table, removing references to same indexer')
self.connection.action('DELETE from indexer_mapping WHERE indexer = mindexer')
log.debug(u'Cleaning indexer_mapping table from tt indexer ids')
self.connection.action("DELETE FROM indexer_mapping where indexer_id like '%tt%' or mindexer_id like '%tt%'")


# ======================
# = Main DB Migrations =
Expand Down
13 changes: 13 additions & 0 deletions medusa/databases/recommended_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
log.logger.addHandler(logging.NullHandler())


class RecommendedSanityCheck(db.DBSanityCheck):
"""Sanity check class."""

def check(self):
"""Check functions."""
self.remove_imdb_tt()

def remove_imdb_tt(self):
"""Remove tt from imdb id's."""
log.debug(u'Remove shows added with an incorrect imdb id.')
self.connection.action("DELETE FROM shows WHERE source = 10 AND series_id like '%tt%'")


# Add new migrations at the bottom of the list
# and subclass the previous migration.
class InitialSchema(db.SchemaUpgrade):
Expand Down
8 changes: 6 additions & 2 deletions medusa/helpers/trakt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging

from medusa.helpers import get_title_without_year
from medusa.indexers.imdb.api import ImdbIdentifier
from medusa.logger.adapters.style import BraceAdapter

from requests.exceptions import RequestException
Expand Down Expand Up @@ -70,8 +71,11 @@ def create_show_structure(show_obj):
'ids': {}
}
for valid_trakt_id in ['tvdb_id', 'trakt_id', 'tmdb_id', 'imdb_id']:
if show_obj.externals.get(valid_trakt_id):
show['ids'][valid_trakt_id[:-3]] = show_obj.externals.get(valid_trakt_id)
external = show_obj.externals.get(valid_trakt_id)
if external:
if valid_trakt_id == 'imdb_id':
external = ImdbIdentifier(external).imdb_id
show['ids'][valid_trakt_id[:-3]] = external
return show


Expand Down
9 changes: 6 additions & 3 deletions medusa/indexers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ def indexer(self, *args, **kwargs):
def config(self):
if self.indexer_id:
return indexerConfig[self.indexer_id]
# Sort and put the default language first
init_config['valid_languages'].sort(key=lambda i: '\0' if i == app.INDEXER_DEFAULT_LANGUAGE else i)
return init_config
_ = init_config
if app.INDEXER_DEFAULT_LANGUAGE in _:
del _[_['valid_languages'].index(app.INDEXER_DEFAULT_LANGUAGE)]
_['valid_languages'].sort()
_['valid_languages'].insert(0, app.INDEXER_DEFAULT_LANGUAGE)
return _

@property
def name(self):
Expand Down
112 changes: 72 additions & 40 deletions medusa/indexers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
IndexerSeasonNotFound,
IndexerSeasonUpdatesNotSupported,
IndexerShowNotFound,
IndexerShowUpdatesNotSupported,
)
from medusa.indexers.ui import BaseUI, ConsoleUI
from medusa.logger.adapters.style import BraceAdapter
from medusa.session.core import IndexerSession
from medusa.statistics import weights

from six import integer_types, itervalues, string_types, text_type, viewitems
from six import integer_types, itervalues, string_types, viewitems


log = BraceAdapter(logging.getLogger(__name__))
Expand Down Expand Up @@ -57,24 +58,18 @@ def __init__(self,
"""Pass these arguments on as args from the subclass."""
self.shows = ShowContainer() # Holds all Show classes
self.corrections = {} # Holds show-name to show_id mapping

self.config = {}

self.config['debug_enabled'] = debug # show debugging messages

self.config['custom_ui'] = custom_ui

self.config['interactive'] = interactive # prompt for correct series?

self.config['select_first'] = select_first

self.config['search_all_languages'] = search_all_languages

self.config['use_zip'] = use_zip

self.config['dvdorder'] = dvdorder

self.config['proxy'] = proxy
self.name = None

self.config = {
'debug_enabled': debug,
'custom_ui': custom_ui,
'interactive': interactive,
'select_first': select_first,
'search_all_languages': search_all_languages,
'use_zip': use_zip,
'dvdorder': dvdorder,
'proxy': proxy
}

if cache is True:
self.config['cache_enabled'] = True
Expand All @@ -93,6 +88,7 @@ def __init__(self,
self.config['banners_enabled'] = banners
self.config['image_type'] = image_type
self.config['actors_enabled'] = actors
self.config['limit_seasons'] = []

if self.config['debug_enabled']:
warnings.warn('The debug argument to tvdbv2_api.__init__ will be removed in the next version. '
Expand Down Expand Up @@ -127,7 +123,46 @@ def __init__(self,
else:
self.config['language'] = language

def _get_temp_dir(self): # pylint: disable=no-self-use
def get_nested_value(self, value, config):
"""
Get a nested value from a dictionary using a dot separated string.

For example the config 'plot.summaries[0].text' will return the value for dict['plot']['summaries'][0].
:param value: Dictionary you want to get a value from.
:param config: Dot separated string.
:return: The value matching the config.
"""
# Remove a level
split_config = config.split('.')
check_key = split_config[0]

if check_key.endswith(']'):
list_index = int(check_key.split('[')[-1].rstrip(']'))
check_key = check_key.split('[')[0]
check_value = value.get(check_key)
if check_value and list_index < len(check_value):
check_value = check_value[list_index]
else:
check_value = value.get(check_key)
next_keys = '.'.join(split_config[1:])

if check_value is None:
return None

if isinstance(check_value, dict) and next_keys:
return self.get_nested_value(check_value, next_keys)
else:
try:
# Some object have a __dict__ attr. Let's try that.
# It shouldn't match basic types like strings, integers or floats.
parse_dict = check_value.__dict__
except AttributeError:
return check_value
else:
return self.get_nested_value(parse_dict, next_keys)

@staticmethod
def _get_temp_dir(): # pylint: disable=no-self-use
"""Return the [system temp dir]/tvdb_api-u501 (or tvdb_api-myuser)."""
if hasattr(os, 'getuid'):
uid = 'u{0}'.format(os.getuid()) # pylint: disable=no-member
Expand All @@ -145,19 +180,21 @@ def _get_show_data(self, sid, language):
return None

def _get_series(self, series):
"""Search for the series name.
"""Search indexer for the series name.

If a custom_ui UI is configured, it uses this to select the correct
series. If not, and interactive == True, ConsoleUI is used, if not
BaseUI is used to select the first result.

:param series: the query for the series name
:return: A list of series mapped to a UI (for example: a BaseUI or custom_ui).
:return: A list of series mapped to a UI (for example: a BaseUi or custom_ui).
"""
all_series = self.search(series)
if not all_series:
log.debug('Series result returned zero')
raise IndexerShowNotFound('Show search returned zero results (cannot find show on Indexer)')
raise IndexerShowNotFound(
'Show search for {series} returned zero results (cannot find show on Indexer)'.format(series=series)
)

if not isinstance(all_series, list):
all_series = [all_series]
Expand All @@ -184,7 +221,7 @@ def _set_show_data(self, sid, key, value):

def __repr__(self):
"""Indexer representation, returning representation of all shows indexed."""
return text_type(self.shows)
return str(self.shows)

def _set_item(self, sid, seas, ep, attrib, value): # pylint: disable=too-many-arguments
"""Create a new episode, creating Show(), Season() and Episode()s as required.
Expand Down Expand Up @@ -391,14 +428,14 @@ def _save_images(self, series_id, images):
self._save_images_by_type(img_type, series_id, images_by_type)

def __getitem__(self, key):
"""Handle tvdbv2_instance['seriesname'] calls. The dict index should be the show id."""
"""Handle indexer['seriesname'] calls. The dict index should be the show id."""
if isinstance(key, (integer_types, int)):
# Item is integer, treat as show id
if key not in self.shows:
self._get_show_data(key, self.config['language'])
return self.shows[key]

key = text_type(key).lower()
key = str(key).lower()
self.config['searchterm'] = key
selected_series = self._get_series(key)
if isinstance(selected_series, dict):
Expand All @@ -409,19 +446,14 @@ def __getitem__(self, key):
self._set_show_data(show['id'], k, v)
return selected_series

def get_last_updated_series(self, from_time, weeks=1, filter_show_list=None):
"""Retrieve a list with updated shows.
def get_last_updated_series(self, *args, **kwargs):
"""Retrieve a list with updated shows."""
raise IndexerShowUpdatesNotSupported('Method get_last_updated_series not implemented by this indexer')

:param from_time: epoch timestamp, with the start date/time
:param weeks: number of weeks to get updates for.
:param filter_show_list: Optional list of show objects, to use for filtering the returned list.
"""
def get_last_updated_seasons(self, *args, **kwargs):
"""Retrieve a list with updated show seasons."""
raise IndexerSeasonUpdatesNotSupported('Method get_last_updated_series not implemented by this indexer')

def get_episodes_for_season(self, show_id, *args, **kwargs):
self._get_episodes(show_id, *args, **kwargs)
return self.shows[show_id]


class ShowContainer(dict):
"""Simple dict that holds a series of Show instances."""
Expand Down Expand Up @@ -502,7 +534,7 @@ def __bool__(self):

def aired_on(self, date):
"""Search and return a list of episodes with the airdates."""
ret = self.search(text_type(date), 'firstaired')
ret = self.search(str(date), 'firstaired')
if len(ret) == 0:
raise IndexerEpisodeNotFound('Could not find any episodes that aired on {0}'.format(date))
return ret
Expand Down Expand Up @@ -631,13 +663,13 @@ def search(self, term=None, key=None):
if term is None:
raise TypeError('must supply string to search for (contents)')

term = text_type(term).lower()
term = str(term).lower()
for cur_key, cur_value in viewitems(self):
cur_key, cur_value = text_type(cur_key).lower(), text_type(cur_value).lower()
cur_key, cur_value = str(cur_key).lower(), str(cur_value).lower()
if key is not None and cur_key != key:
# Do not search this key
continue
if cur_value.find(text_type(term).lower()) > -1:
if cur_value.find(str(term).lower()) > -1:
return self


Expand Down
Loading