def test_sanitize_imdb_title(): # See result of http://www.imdb.com/find?q=Aftermath&s=tt&mx=20 assert MovieIndexer.sanitize_imdb_title(u'Aftermath (I)') == 'aftermath' assert MovieIndexer.sanitize_imdb_title(u'Aftermath') == 'aftermath' assert MovieIndexer.sanitize_imdb_title(u'(Aftermath)') == 'aftermath' assert MovieIndexer.sanitize_imdb_title( u'The King is Dead!') == 'the king is dead'
def test_get_imdb_id_exception(mocker): mocker.patch('autosubliminal.indexer.MovieIndexer._search', side_effect=Exception) db_mock_delete = mocker.patch('autosubliminal.db.ImdbIdCacheDb.delete_imdb_id') db_mock_set = mocker.patch('autosubliminal.db.ImdbIdCacheDb.set_imdb_id') indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=True, store_id=True) == (None, 2015) assert db_mock_delete.called assert db_mock_set.called
def test_get_imdb_id_from_movie_name_mapping(monkeypatch): monkeypatch.setattr('autosubliminal.MOVIENAMEMAPPING', {'southpaw (2015)': 'tt1798684'}) indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=False, store_id=False) == ('tt1798684', 2015)
def test_get_imdb_id_and_store_in_cache(mocker): search_result = TitleSearchResult(imdb_id='tt1798684', title='Southpaw', type='feature', year=2015) mocker.patch('autosubliminal.indexer.MovieIndexer._search', return_value=search_result) db_mock_delete = mocker.patch('autosubliminal.db.ImdbIdCacheDb.delete_imdb_id') db_mock_set = mocker.patch('autosubliminal.db.ImdbIdCacheDb.set_imdb_id') indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=True, store_id=True) == ('tt1798684', 2015) assert db_mock_delete.called assert db_mock_set.called
def test_get_imdb_id_from_cache_not_found(monkeypatch, mocker): monkeypatch.setattr('autosubliminal.MOVIENAMEMAPPING', {}) mocker.patch('autosubliminal.db.ImdbIdCacheDb.get_imdb_id', return_value=IMDB_ID_UNKNOWN) indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=False, store_id=False) == (None, 2015)
def test_get_movie_details(): indexer = MovieIndexer() movie_details = indexer.get_movie_details('tt1798684') assert movie_details is not None assert isinstance(movie_details, MovieDetails) assert movie_details.imdb_id == 'tt1798684' assert movie_details.title == 'Southpaw' assert movie_details.year == 2015 assert movie_details.overview is not None assert movie_details.poster is not None
def __init__(self): self.show_db = ShowDetailsDb() self.failed_shows_db = FailedShowsDb() self.show_episodes_db = ShowEpisodeDetailsDb() self.show_settings_db = ShowSettingsDb() self.failed_movies_db = FailedMoviesDb() self.movie_db = MovieDetailsDb() self.movie_settings_db = MovieSettingsDb() self.show_indexer = ShowIndexer() self.movie_indexer = MovieIndexer()
def test_get_imdb_id_exception(mocker): mocker.patch('autosubliminal.indexer.MovieIndexer._search', side_effect=Exception) db_mock_delete = mocker.patch( 'autosubliminal.db.ImdbIdCacheDb.delete_imdb_id') db_mock_set = mocker.patch('autosubliminal.db.ImdbIdCacheDb.set_imdb_id') indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=True, store_id=True) == (None, 2015) assert db_mock_delete.called assert db_mock_set.called
def test_get_imdb_id(): indexer = MovieIndexer() # By title assert indexer.get_imdb_id_and_year(u'Southpaw', force_search=True, store_id=False) == ('tt1798684', 2015) # By title and year assert indexer.get_imdb_id_and_year(u'Southpaw', year=2015, force_search=True, store_id=False) == ('tt1798684', 2015) assert indexer.get_imdb_id_and_year(u'Joyeux Noël', year=2005, force_search=True, store_id=False) == ('tt0424205', 2005) # By alternative title assert indexer.get_imdb_id_and_year(u'Kyatapirâ', force_search=True, store_id=False) == ('tt1508290', 2010) # By alternative title and year assert indexer.get_imdb_id_and_year(u'Kyatapirâ', year=2010, force_search=True, store_id=False) == ('tt1508290', 2010) # By fallback search assert indexer.get_imdb_id_and_year(u'Bullet', year=2014, force_search=True, store_id=False) == ('tt2544734', 2014)
def test_get_imdb_id_and_store_in_cache(mocker): search_result = TitleSearchResult(imdb_id='tt1798684', title='Southpaw', type='feature', year=2015) mocker.patch('autosubliminal.indexer.MovieIndexer._search', return_value=search_result) db_mock_delete = mocker.patch( 'autosubliminal.db.ImdbIdCacheDb.delete_imdb_id') db_mock_set = mocker.patch('autosubliminal.db.ImdbIdCacheDb.set_imdb_id') indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=True, store_id=True) == ('tt1798684', 2015) assert db_mock_delete.called assert db_mock_set.called
def test_get_imdb_id(): indexer = MovieIndexer() # By title assert indexer.get_imdb_id_and_year(u'Southpaw', force_search=True, store_id=False) == ('tt1798684', 2015) # By title and year assert indexer.get_imdb_id_and_year(u'Southpaw', year=2015, force_search=True, store_id=False) == ( 'tt1798684', 2015) assert indexer.get_imdb_id_and_year(u'Joyeux Noël', year=2005, force_search=True, store_id=False) == ( 'tt0424205', 2005) # By alternative title assert indexer.get_imdb_id_and_year(u'Kyatapirâ', force_search=True, store_id=False) == ('tt1508290', 2010) # By alternative title and year assert indexer.get_imdb_id_and_year(u'Kyatapirâ', year=2010, force_search=True, store_id=False) == ( 'tt1508290', 2010) # By fallback search assert indexer.get_imdb_id_and_year(u'Bullet', year=2014, force_search=True, store_id=False) == ('tt2544734', 2014)
def test_get_imdb_id(): indexer = MovieIndexer() assert indexer.get_imdb_id_and_year(u'Southpaw', 2015, force_search=True, store_id=False) == ('tt1798684', 2015) assert indexer.get_imdb_id_and_year(u'Southpaw', force_search=True, store_id=False) == ('tt1798684', 2015) assert indexer.get_imdb_id_and_year(u'Joyeux Noël', 2005, force_search=True, store_id=False) == ('tt0424205', 2005) assert indexer.get_imdb_id_and_year(u'Kyatapirâ', 2010, force_search=True, store_id=False) == ('tt1508290', 2010)
class LibraryPathScanner(object): def __init__(self): self.show_db = ShowDetailsDb() self.failed_shows_db = FailedShowsDb() self.show_episodes_db = ShowEpisodeDetailsDb() self.show_settings_db = ShowSettingsDb() self.failed_movies_db = FailedMoviesDb() self.movie_db = MovieDetailsDb() self.movie_settings_db = MovieSettingsDb() self.show_indexer = ShowIndexer() self.movie_indexer = MovieIndexer() def scan_path(self, path): log.info('Scanning path: %s', path) # Check all folders and files for dirname, dirnames, filenames in os.walk(os.path.join(path)): log.debug('Directory: %s', dirname) # Ignore skipped dirs if is_skipped_dir(dirname): continue # Check files for filename in filenames: # Only scan valid video files if is_valid_video_file(filename, video_extensions=EXTENDED_VIDEO_EXTENSIONS): log.debug('Video file found: %s', filename) try: self._scan_file(dirname, filename) except Exception: log.exception('Error while scanning video file: %s', os.path.join(dirname, filename)) def _scan_file(self, dirname, filename): wanted_item = process_file(dirname, filename) if wanted_item: if wanted_item.is_episode: # Do a force search if no tvdb id found if not wanted_item.tvdbid: wanted_item.tvdbid = self.show_indexer.get_tvdb_id(wanted_item.title, year=wanted_item.year, force_search=True) # Skip if no tvdb id is found if not wanted_item.tvdbid: log.warning('Skipping show episode file with unknown tvdb id: %s', os.path.join(dirname, filename)) show_path = self._get_show_path(dirname) if not self.failed_shows_db.get_failed_show(show_path): self.failed_shows_db.set_failed_show(show_path) return # Store default show settings if not yet available if not self.show_settings_db.get_show_settings(wanted_item.tvdbid): self.show_settings_db.set_show_settings(ShowSettings.default_settings(wanted_item.tvdbid)) show_settings = self.show_settings_db.get_show_settings(wanted_item.tvdbid) # Get show details show_details = self.show_db.get_show(wanted_item.tvdbid) # Add show and episodes to db if not yet in db if not show_details: show_details = self.show_indexer.get_show_details(wanted_item.tvdbid) if show_details: show_details.path = self._get_show_path(dirname) self.show_db.set_show(show_details) episodes = self.show_indexer.get_show_episodes(wanted_item.tvdbid) if episodes: for episode in episodes: self.show_episodes_db.set_show_episode(episode) # Cache artwork (fullsize and thumbnail) if not yet cached if show_details: # Poster if show_details.poster: if not is_artwork_cached(self.show_indexer.name, show_details.tvdb_id, 'poster'): cache_artwork(self.show_indexer.name, show_details.tvdb_id, 'poster', get_artwork_url(show_details.poster)) if not is_artwork_cached(self.show_indexer.name, show_details.tvdb_id, 'poster', thumbnail=True): cache_artwork(self.show_indexer.name, show_details.tvdb_id, 'poster', get_artwork_url(show_details.poster, thumbnail=True), thumbnail=True) # Banner if show_details.banner: if not is_artwork_cached(self.show_indexer.name, show_details.tvdb_id, 'banner'): cache_artwork(self.show_indexer.name, show_details.tvdb_id, 'banner', get_artwork_url(show_details.banner)) if not is_artwork_cached(self.show_indexer.name, show_details.tvdb_id, 'banner', thumbnail=True): cache_artwork(self.show_indexer.name, show_details.tvdb_id, 'banner', get_artwork_url(show_details.banner, thumbnail=True), thumbnail=True) # Check episode details if isinstance(wanted_item.episode, list): for episode in wanted_item.episode: self._update_episode_details(show_settings, dirname, filename, wanted_item.tvdbid, wanted_item.season, episode) else: self._update_episode_details(show_settings, dirname, filename, wanted_item.tvdbid, wanted_item.season, wanted_item.episode) if wanted_item.is_movie: # Do a force search if no imdb id found if not wanted_item.imdbid: wanted_item.imdbid, _ = self.movie_indexer.get_imdb_id_and_year(wanted_item.title, year=wanted_item.year, force_search=True) # Skip if no imdb id is found if not wanted_item.imdbid: log.warning('Skipping movie file with unknown imdb id: %s', os.path.join(dirname, filename)) if not self.failed_movies_db.get_failed_movie(dirname): self.failed_movies_db.set_failed_movie(dirname) return # Store default movie settings if not yet available if not self.movie_settings_db.get_movie_settings(wanted_item.imdbid): self.movie_settings_db.set_movie_settings(MovieSettings.default_settings(wanted_item.imdbid)) movie_settings = self.movie_settings_db.get_movie_settings(wanted_item.imdbid) # Get movie details movie_details = self.movie_db.get_movie(wanted_item.imdbid) # Add movie to db if not yet in db if not movie_details: movie_details = self.movie_indexer.get_movie_details(wanted_item.imdbid) if movie_details: movie_details.path = dirname self.movie_db.set_movie(movie_details) # Cache artwork (fullsize and thumbnail) if not yet cached if movie_details: # Poster if movie_details.poster: if not is_artwork_cached(self.movie_indexer.name, movie_details.imdb_id, 'poster'): cache_artwork(self.movie_indexer.name, movie_details.imdb_id, 'poster', movie_details.poster) if not is_artwork_cached(self.movie_indexer.name, movie_details.imdb_id, 'poster', thumbnail=True): cache_artwork(self.movie_indexer.name, movie_details.imdb_id, 'poster', self.movie_indexer.get_artwork_thumbnail_url(movie_details.poster), thumbnail=True) # Check movie details self._update_movie_details(movie_settings, dirname, filename, wanted_item.imdbid) def _get_show_path(self, dirname): path = dirname # Get root show path (ignore season folders) while 'season' in safe_lowercase(os.path.normpath(os.path.normcase(path))): path, _ = os.path.split(path) return path def _update_episode_details(self, show_settings, dirname, filename, show_tvdb_id, season, episode): episode_details = self.show_episodes_db.get_show_episode(show_tvdb_id, season, episode) # If no episode is found, we need to fetch the episode details of the show # This is because the show is still on-going and we didn't got all episodes when the show was loaded if not episode_details: episode_details = self.show_indexer.get_show_episode(show_tvdb_id, season, episode) if episode_details: self.show_episodes_db.set_show_episode(episode_details) if episode_details: # Set details available_subtitles = get_available_subtitles(dirname, filename, autosubliminal.SCANEMBEDDEDSUBS, autosubliminal.SCANHARDCODEDSUBS) missing_languages = self._get_missing_subtitle_languages(available_subtitles, wanted_languages=show_settings.wanted_languages) episode_details.subtitles = available_subtitles episode_details.missing_languages = missing_languages episode_details.path = os.path.abspath(os.path.join(dirname, filename)) # Update details in db self.show_episodes_db.update_show_episode(episode_details, subtitles=True) def _update_movie_details(self, movie_settings, dirname, filename, imdb_id): movie_details = self.movie_db.get_movie(imdb_id) if movie_details: # Set details available_subtitles = get_available_subtitles(dirname, filename, autosubliminal.SCANEMBEDDEDSUBS, autosubliminal.SCANHARDCODEDSUBS) missing_languages = self._get_missing_subtitle_languages(available_subtitles, wanted_languages=movie_settings.wanted_languages) movie_details.subtitles = available_subtitles movie_details.missing_languages = missing_languages movie_details.path = os.path.abspath(os.path.join(dirname, filename)) # Update details in db self.movie_db.update_movie(movie_details, subtitles=True) def _get_missing_subtitle_languages(self, available_subtitles, wanted_languages=None): # Use custom wanted languages or globally configured wanted languages if not provided if wanted_languages is None: wanted_languages = get_wanted_languages() available_languages = [] for subtitle in available_subtitles: available_languages.append(subtitle.language) # Return the missing languages (= not in available languages) return [language for language in wanted_languages if language not in available_languages]
# coding=utf-8 import os import autosubliminal from autosubliminal.fileprocessor import process_file from autosubliminal.indexer import ShowIndexer, MovieIndexer autosubliminal.SHOWINDEXER = ShowIndexer() autosubliminal.MOVIEINDEXER = MovieIndexer() def test_process_file_size_too_low(monkeypatch): # Monkey patch autosubliminal.MINVIDEOFILESIZE monkeypatch.setattr('autosubliminal.MINVIDEOFILESIZE', 10) # Test process_file dir_name = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'resources') result_dict = process_file(dir_name, 'Southpaw.2015.1080p.BluRay.x264.mkv') assert result_dict is None def test_process_episode_file_insufficient_guess(): # Test process_file dir_name = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'resources') result_dict = process_file(dir_name, 'Insufficient.guess.series.S1.mkv') assert result_dict is None def test_process_episode_file(mocker): # Mock get_tvdb_id call mocker.patch.object(autosubliminal.SHOWINDEXER, 'get_tvdb_id')
def test_get_artwork_thumbnail_url(): url = 'https://m.media-amazon.com/images/M/MV5BMjI1MTcwODk0MV5BMl5BanBnXkFtZTgwMTgwMDM5NTE@._V1.jpg' url_ = 'https://m.media-amazon.com/images/M/MV5BMjI1MTcwODk0MV5BMl5BanBnXkFtZTgwMTgwMDM5NTE@._V1_.jpg' url_thumb = 'https://m.media-amazon.com/images/M/MV5BMjI1MTcwODk0MV5BMl5BanBnXkFtZTgwMTgwMDM5NTE@._V1_SX300.jpg' assert MovieIndexer.get_artwork_thumbnail_url(url) == url_thumb assert MovieIndexer.get_artwork_thumbnail_url(url_) == url_thumb
def test_sanitize_imdb_title(): # See result of http://www.imdb.com/find?q=Aftermath&s=tt&mx=20 assert MovieIndexer.sanitize_imdb_title(u'Aftermath (I)') == 'aftermath' assert MovieIndexer.sanitize_imdb_title(u'Aftermath') == 'aftermath' assert MovieIndexer.sanitize_imdb_title(u'(Aftermath)') == 'aftermath' assert MovieIndexer.sanitize_imdb_title(u'The King is Dead!') == 'the king is dead'
def initialize(): global CONFIGFILE, CONFIGVERSION, CONFIGUPGRADED, \ CACHEDIR, DEREFERURL, GITHUBURL, VERSIONURL, USERAGENT, SYSENCODING, TIMEOUT, WANTEDQUEUE, WANTEDQUEUELOCK, \ WEBSOCKETMESSAGEQUEUE, WEBSOCKETBROADCASTER, SCHEDULERS, SCANDISK, SCANLIBRARY, CHECKSUB, CHECKVERSION, \ DEVELOPER, \ TVDBAPIKEY, TVDBURL, IMDBURL, SHOWINDEXER, MOVIEINDEXER, \ DBFILE, DBVERSION, DBTIMESTAMPFORMAT, \ PYTHONVERSION, DAEMON, STARTED, PID, UUID, \ PATH, VIDEOPATHS, DEFAULTLANGUAGE, DEFAULTLANGUAGESUFFIX, ADDITIONALLANGUAGES, MANUALSEARCHWITHSCORING, \ SCANDISKINTERVAL, CHECKSUBINTERVAL, CHECKSUBDEADLINE, CHECKSUBDELTA, CHECKVERSIONINTERVAL, \ CHECKVERSIONAUTOUPDATE, SCANEMBEDDEDSUBS, SCANHARDCODEDSUBS, SKIPHIDDENDIRS, DETECTINVALIDSUBLANGUAGE, \ DETECTEDLANGUAGEPROBABILITY, MINVIDEOFILESIZE, MAXDBRESULTS, TIMESTAMPFORMAT, \ LIBRARYMODE, LIBRARYPATHS, SCANLIBRARYINTERVAL, \ LOGFILE, LOGLEVEL, LOGSIZE, LOGNUM, LOGHTTPACCESS, LOGEXTERNALLIBS, LOGDETAILEDFORMAT, LOGREVERSED, \ LOGLEVELCONSOLE, \ WEBSERVERIP, WEBSERVERPORT, WEBROOT, USERNAME, PASSWORD, LAUNCHBROWSER, \ SHOWMINMATCHSCORE, SHOWMINMATCHSCOREDEFAULT, SHOWMATCHSOURCE, SHOWMATCHQUALITY, SHOWMATCHCODEC, \ SHOWMATCHRELEASEGROUP, \ MOVIEMINMATCHSCORE, MOVIEMINMATCHSCOREDEFAULT, MOVIEMATCHSOURCE, MOVIEMATCHQUALITY, MOVIEMATCHCODEC, \ MOVIEMATCHRELEASEGROUP, \ SUBLIMINALPROVIDERMANAGER, SUBLIMINALPROVIDERS, SUBLIMINALPROVIDERCONFIGS, \ SUBTITLEUTF8ENCODING, MANUALREFINEVIDEO, REFINEVIDEO, PREFERHEARINGIMPAIRED, \ ADDIC7EDUSERNAME, ADDIC7EDPASSWORD, OPENSUBTITLESUSERNAME, OPENSUBTITLESPASSWORD, \ SHOWNAMEMAPPING, ADDIC7EDSHOWNAMEMAPPING, ALTERNATIVESHOWNAMEMAPPING, \ MOVIENAMEMAPPING, ALTERNATIVEMOVIENAMEMAPPING, \ SKIPSHOW, SKIPMOVIE, \ NOTIFY, NOTIFYMAIL, MAILSRV, MAILFROMADDR, MAILTOADDR, MAILUSERNAME, MAILPASSWORD, MAILSUBJECT, MAILAUTH, \ MAILENCRYPTION, NOTIFYTWITTER, TWITTERKEY, TWITTERSECRET, NOTIFYPUSHALOT, PUSHALOTAPI, \ NOTIFYPUSHOVER, PUSHOVERKEY, PUSHOVERAPI, PUSHOVERDEVICES, \ NOTIFYGROWL, GROWLHOST, GROWLPORT, GROWLPASS, GROWLPRIORITY, NOTIFYPROWL, PROWLAPI, PROWLPRIORITY, \ NOTIFYPUSHBULLET, PUSHBULLETAPI, NOTIFYTELEGRAM, TELEGRAMBOTAPI, TELEGRAMCHATID, \ POSTPROCESS, POSTPROCESSINDIVIDUAL, POSTPROCESSUTF8ENCODING, SHOWPOSTPROCESSCMD, SHOWPOSTPROCESSCMDARGS, \ MOVIEPOSTPROCESSCMD, MOVIEPOSTPROCESSCMDARGS # Fake some entry points to get libraries working without installation _fake_entry_points() # Check python version PYTHONVERSION = get_python_version_strict() python_version_changed = _check_python_version_change() # System settings PATH = os.path.abspath(getcwd()) CACHEDIR = os.path.abspath(os.path.join(PATH, 'cache')) DEREFERURL = 'http://www.dereferer.org/?' GITHUBURL = 'https://github.com/h3llrais3r/Auto-Subliminal' VERSIONURL = 'https://raw.github.com/h3llrais3r/Auto-Subliminal/master/autosubliminal/version.py' USERAGENT = 'Auto-Subliminal/' + version.RELEASE_VERSION TIMEOUT = 300 # Wanted queue settings WANTEDQUEUE = [] WANTEDQUEUELOCK = False # Websocket settings WEBSOCKETMESSAGEQUEUE = [] # Scheduler settings SCHEDULERS = {} # Developer settings DEVELOPER = False # Indexer settings TVDBAPIKEY = '76F2D5362F45C5EC' TVDBURL = 'http://thetvdb.com/?tab=series&id=' IMDBURL = 'http://www.imdb.com/title/' SHOWINDEXER = ShowIndexer() MOVIEINDEXER = MovieIndexer() # Startup settings STARTED = False UUID = uuid.uuid4( ) # Generate random uuid each time we initialize the application # Webserver settings LAUNCHBROWSER = True # Score settings SHOWMINMATCHSCOREDEFAULT = 330 MOVIEMINMATCHSCOREDEFAULT = 90 # Cache settings _init_cache(python_version_changed) # Guessit settings _init_guessit() # Subliminal settings SUBLIMINALPROVIDERMANAGER = _init_subliminal(python_version_changed) SUBLIMINALPROVIDERCONFIGS = {} # Langdetect settings _init_langdetect() # Config settings CONFIGUPGRADED = False if CONFIGFILE is None: CONFIGFILE = 'config.properties' config.read_config(True) if CONFIGUPGRADED: print('INFO: Config seems to be upgraded. Writing config.') config.write_config() print('INFO: Writing config done.') # Change to the new work directory if os.path.exists(PATH): os.chdir(PATH) else: print('ERROR: PATH does not exist, check config.') os._exit(1) # Database settings DBFILE = 'database.db' DBTIMESTAMPFORMAT = '%Y-%m-%d %H:%M:%S' db.initialize() # Logging settings logger.initialize()