def post_process(wanted_item_index, subtitle_index): log.info('Post processing an individual subtitle') # Get wanted queue lock if not get_wanted_queue_lock(): return False # Get wanted item wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] found_subtitles = wanted_item.found_subtitles subtitles = found_subtitles['subtitles'] language = found_subtitles['language'] single = found_subtitles['single'] # Only execute post processing when a subtitle is present processed = False subtitle_path = _get_subtitle_path(wanted_item) if os.path.exists(subtitle_path): # Get wanted subtitle wanted_subtitle = _get_wanted_subtitle(subtitles, subtitle_index) # Handle download download_item = _construct_download_item(wanted_item, [wanted_subtitle], language, single) downloader = SubDownloader(download_item) downloader.mark_downloaded() processed = downloader.post_process() if processed: # Remove downloaded language from wanted languages wanted_item.languages.remove(language) # Update wanted item if there are still wanted languages if len(wanted_item.languages) > 0: WantedItemsDb().update_wanted_item(wanted_item) # Remove wanted item if there are no more wanted languages else: # Remove wanted item autosubliminal.WANTEDQUEUE.pop(int(wanted_item_index)) log.debug('Removed item from the wanted queue at index %s', int(wanted_item_index)) WantedItemsDb().delete_wanted_item(wanted_item) log.debug('Removed %s from wanted_items database', wanted_item.videopath) else: log.warning('No subtitle downloaded, skipping post processing') # Release wanted queue lock release_wanted_queue_lock() return processed
def put(self, imdb_id): """Save the settings for a movie.""" input_json = cherrypy.request.json if all(k in input_json for k in ('wanted_languages', 'refine', 'hearing_impaired', 'utf8_encoding')): wanted_languages = input_json['wanted_languages'] refine = get_boolean(input_json['refine']) hearing_impaired = get_boolean(input_json['hearing_impaired']) utf8_encoding = get_boolean(input_json['utf8_encoding']) # Update settings db = MovieSettingsDb() movie_settings = db.get_movie_settings(imdb_id) movie_settings.wanted_languages = wanted_languages movie_settings.refine = refine movie_settings.hearing_impaired = hearing_impaired movie_settings.utf8_encoding = utf8_encoding db.update_movie_settings(movie_settings) # Delete wanted items for the movie so the new settings will be used in the next disk scan WantedItemsDb().delete_wanted_items_for_movie(imdb_id) # Send notification send_websocket_notification( 'Settings will be applied on next disk scan.') return self._no_content() return self._bad_request('Missing data')
def post_process_no_subtitle(wanted_item_index): log.info('Post processing without subtitle') # Get wanted queue lock if not get_wanted_queue_lock(): return False # Get wanted item wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] # Post process only (no need to check for individual or not because we are forcing post processing) processed = PostProcessor(wanted_item).run() # Remove wanted item if processed if processed: autosubliminal.WANTEDQUEUE.pop(int(wanted_item_index)) log.debug('Removed item from the wanted queue at index %s', int(wanted_item_index)) WantedItemsDb().delete_wanted_item(wanted_item) log.debug('Removed %s from wanted_items database', wanted_item.videopath) else: send_websocket_notification('Unable to handle post processing! Please check the log file!', type='error') # Release wanted queue lock release_wanted_queue_lock() return processed
def skip_show(wanted_item_index, season): log.info('Skipping a show') # Get wanted queue lock if not get_wanted_queue_lock(): return False # Get wanted item wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] show = wanted_item.title # Remove all wanted items for the same show and season to_delete_wanted_queue = [] for index, item in enumerate(autosubliminal.WANTEDQUEUE): if item.title == show: # Skip show all seasons if season == '00': to_delete_wanted_queue.append(index) # Skip season (and specials = season 0) elif season == item.season: to_delete_wanted_queue.append(index) # Start at the end to delete to prevent index out of range error i = len(to_delete_wanted_queue) - 1 while i >= 0: wanted_item_to_delete = autosubliminal.WANTEDQUEUE.pop(to_delete_wanted_queue[i]) WantedItemsDb().delete_wanted_item(wanted_item_to_delete) i -= 1 log.info('Skipped show %s season %s', show, season) # Release wanted queue lock release_wanted_queue_lock() return True
def force_id_search(wanted_item_index): log.info('Force id search') # Get wanted queue lock if not get_wanted_queue_lock(): return # Force id search wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] title = wanted_item.title year = wanted_item.year if wanted_item.is_episode: wanted_item.tvdbid = autosubliminal.SHOWINDEXER.get_tvdb_id(title, year, force_search=True) WantedItemsDb().update_wanted_item(wanted_item) elif wanted_item.is_movie: wanted_item.imdbid, wanted_item.year = autosubliminal.MOVIEINDEXER.get_imdb_id_and_year(title, year, force_search=True) WantedItemsDb().update_wanted_item(wanted_item) # Release wanted queue lock release_wanted_queue_lock()
def delete_video(wanted_item_index, cleanup): log.info('Deleting an individual video file') # Get wanted queue lock if not get_wanted_queue_lock(): return False # Remove wanted item wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] autosubliminal.WANTEDQUEUE.pop(int(wanted_item_index)) log.debug('Removed item from the wanted queue at index %s', int(wanted_item_index)) WantedItemsDb().delete_wanted_item(wanted_item) log.debug('Removed %s from wanted_items database', wanted_item.videopath) # Physically delete the video file (and optionally leftovers) deleted = False video_path = wanted_item.videopath # Delete with cleanup if cleanup: norm_video_path = os.path.normcase(os.path.normpath(video_path)) norm_video_folder = os.path.dirname(norm_video_path).rstrip(os.sep) for root_folder in autosubliminal.VIDEOPATHS: # Get root folder norm_root_folder = os.path.normcase(os.path.normpath(root_folder)).rstrip(os.sep) # Check if video is located in subfolder underneath root folder if os.path.commonprefix([norm_root_folder, norm_video_path]) == norm_root_folder: if norm_video_folder != norm_root_folder: # Move up until the first subfolder underneath the root folder to cleanup while norm_video_folder != norm_root_folder: folder_to_clean = norm_video_folder norm_video_folder = os.path.dirname(norm_video_folder).rstrip(os.sep) try: # Remove the folder of the video underneath the root folder shutil.rmtree(folder_to_clean, onerror=set_rw_and_remove) log.info('Deleted video folder: %s', folder_to_clean) deleted = True # Break for loop break except Exception: log.exception('Unable to delete video folder: %s', folder_to_clean) # Delete video file only if not deleted: try: os.remove(video_path) log.info('Deleted file: %s', video_path) deleted = True except Exception: log.exception('Unable to delete file: %s', video_path) # Release wanted queue lock release_wanted_queue_lock() return deleted
def reset_wanted_item(self, wanted_item_index, **kwargs): if get_wanted_queue_lock(): # Get wanted item and reset values wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] wanted_item_db = WantedItemsDb().get_wanted_item( wanted_item.videopath) wanted_item_db.copy_to(wanted_item) # Release wanted queue lock release_wanted_queue_lock() # Only return updatable fields # These values represent the original values, so apply default display_value() on it! return { 'displaytitle': display_item_title(wanted_item), 'title': display_value(wanted_item.title), 'year': display_value(wanted_item.year), 'season': display_value(wanted_item.season), 'episode': display_value(wanted_item.episode), 'source': display_value(wanted_item.source, default_value='N/A'), 'quality': display_value(wanted_item.quality, default_value='N/A'), 'codec': display_value(wanted_item.codec, default_value='N/A'), 'releasegrp': display_value(wanted_item.releasegrp, default_value='N/A') } # Show notification if wanted queue is in use send_websocket_notification( 'Cannot reset wanted item when wanted queue is in use!', type='notice')
def reset_wanted_item(self, wanted_item_index, **kwargs): if get_wanted_queue_lock(): # Get wanted item and reset values wanted_item = autosubliminal.WANTEDQUEUE[int(wanted_item_index)] wanted_item_db = WantedItemsDb().get_wanted_item(wanted_item.videopath) wanted_item_db.copy_to(wanted_item) # Release wanted queue lock release_wanted_queue_lock() # Only return updatable fields # These values represent the original values, so apply default display_value() on it! return {'displaytitle': display_item_title(wanted_item), 'title': display_value(wanted_item.title), 'year': display_value(wanted_item.year), 'season': display_value(wanted_item.season), 'episode': display_value(wanted_item.episode), 'source': display_value(wanted_item.source, default_value='N/A'), 'quality': display_value(wanted_item.quality, default_value='N/A'), 'codec': display_value(wanted_item.codec, default_value='N/A'), 'releasegrp': display_value(wanted_item.releasegrp, default_value='N/A')} # Show notification if wanted queue is in use send_websocket_notification('Cannot reset wanted item when wanted queue is in use!', type='notice')
def skip_movie(wanted_item_index): log.info('Skipping a movie') # Get wanted queue lock if not get_wanted_queue_lock(): return False # Remove wanted item from queue and db wanted_item = autosubliminal.WANTEDQUEUE.pop(int(wanted_item_index)) WantedItemsDb().delete_wanted_item(wanted_item) movie = wanted_item.title if wanted_item.year: movie += ' (' + wanted_item.year + ')' log.info('Skipped movie %s', movie) # Release wanted queue lock release_wanted_queue_lock() return True
def run(self, force_run): log.info('Starting round of subtitle checking') # Wait for internet connection wait_for_internet_connection() # Show info message (only when run was forced manually) if force_run: send_websocket_notification('Checking subtitles...') to_delete_wanted_queue = [] # Setup provider pool provider_pool = _get_provider_pool() if provider_pool: log.info('Searching subtitles with providers: %s', ', '.join(provider_pool.providers)) # Process all items in wanted queue db = WantedItemsDb() for index, wanted_item in enumerate(autosubliminal.WANTEDQUEUE): log.info('Searching subtitles for video: %s', wanted_item.videopath) # Check if the search is currently active for the wanted_item if not wanted_item.is_search_active: log.info('Search not active in this run for video: %s', wanted_item.videopath) continue # Scan wanted_item for video, skip when no video could be determined video = _scan_wanted_item_for_video(wanted_item) if not video: continue # Clear discarded providers for each new wanted_item provider_pool.discarded_providers.clear() # Check subtitles for each language languages = wanted_item.languages for lang in languages[:]: # Search the best subtitle with the minimal score try: subtitles, language, single = _search_subtitles(video, lang, True, provider_pool) except Exception: log.exception('Error while searching subtitles for video %r', wanted_item.videopath) continue # Subtitle is found for the video if subtitles: # Handle download download_item = _construct_download_item(wanted_item, subtitles, language, single) SubDownloader(download_item).run() # Remove downloaded language from wanted languages languages.remove(lang) # Update wanted item if there are still wanted languages if len(languages) > 0: db.update_wanted_item(wanted_item) # Mark wanted item as deleted if there are no more wanted languages else: to_delete_wanted_queue.append(index) # Cleanup wanted item(s) i = len(to_delete_wanted_queue) - 1 while i >= 0: wanted_item_to_delete = autosubliminal.WANTEDQUEUE.pop(to_delete_wanted_queue[i]) log.debug('Removed item from the wanted queue at index %s', to_delete_wanted_queue[i]) db.delete_wanted_item(wanted_item_to_delete) log.debug('Removed %s from wanted_items database', wanted_item_to_delete.videopath) i -= 1 else: log.info('No subliminal providers configured, skipping') # Send home page reload event send_websocket_event(PAGE_RELOAD, data={'name': 'home'}) log.info('Finished round of subtitle checking')
def __init__(self): super(DiskScanner, self).__init__() self.wanted_db = WantedItemsDb() self.show_settings_db = ShowSettingsDb() self.movie_settings_db = MovieSettingsDb()
class DiskScanner(ScheduledProcess): """Disk scanner. Scan the specified path for episodes and movies with missing subtitles. If found, episodes and movies are added to the WANTEDQUEUE. """ def __init__(self): super(DiskScanner, self).__init__() self.wanted_db = WantedItemsDb() self.show_settings_db = ShowSettingsDb() self.movie_settings_db = MovieSettingsDb() @release_wanted_queue_lock_on_exception def run(self, force_run): paths = autosubliminal.VIDEOPATHS log.info('Starting round of local disk checking at %r', paths) # Show info message (only when run was forced manually) if force_run: send_websocket_notification('Scanning disk...') # Check if a path exists to scan if not one_path_exists(paths): log.error('None of the configured video paths (%r) exists, aborting...', paths) return # Walk through paths to search for wanted items new_wanted_items = [] old_wanted_items = self.wanted_db.get_wanted_items() for path in paths: try: new_wanted_items.extend(self._scan_path(path)) except Exception: log.exception('Could not scan the video path (%s), skipping it', path) # Cleanup wanted items that have been removed from disk manually but are still stored in the db log.debug('Checking for non existing wanted items in wanted_items database') for item in old_wanted_items: if item not in new_wanted_items: self.wanted_db.delete_wanted_item(item) log.debug('Deleted non existing wanted item: %s', item.videopath) # Populate WANTEDQUEUE with all items from wanted_items database log.info('Listing videos with missing subtitles:') autosubliminal.WANTEDQUEUE = [] for item in self.wanted_db.get_wanted_items(): log.info('%s %s', item.videopath, item.languages) autosubliminal.WANTEDQUEUE.append(item) # Send home page reload event send_websocket_event(PAGE_RELOAD, data={'name': 'home'}) log.info('Finished round of local disk checking') def _scan_path(self, path): log.info('Scanning video path: %s', path) wanted_items = [] # 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): log.debug('Video file found: %s', filename) wanted_item = None try: wanted_item = self._scan_file(dirname, filename) except Exception: log.exception('Error while scanning video file: %s', os.path.join(dirname, filename)) # Add it to list of wanted items if wanted_item: log.debug('Video added to list of wanted items') wanted_items.append(wanted_item) return wanted_items def _scan_file(self, dirname, filename): # Check if video file has already been processed before, so we don't need to process it again wanted_item = self.wanted_db.get_wanted_item(os.path.join(dirname, filename)) if wanted_item: log.debug('Video found in wanted_items database, no need to scan it again') else: log.debug('Video not found in wanted_items database, start scanning it') # Process file wanted_item = fileprocessor.process_file(dirname, filename) if wanted_item: # Determine wanted languages wanted_languages = [] if wanted_item.is_episode and wanted_item.tvdbid: settings = self.show_settings_db.get_show_settings(wanted_item.tvdbid) if settings and settings.wanted_languages: wanted_languages = settings.wanted_languages elif wanted_item.is_movie and wanted_item.imdbid: settings = self.movie_settings_db.get_movie_settings(wanted_item.imdbid) if settings and settings.wanted_languages: wanted_languages = settings.wanted_languages # Check for missing subtitles (scan embedded and detect invalid if configured to do so) languages = check_missing_subtitle_languages(dirname, filename, scan_embedded=autosubliminal.SCANEMBEDDEDSUBS, scan_hardcoded=autosubliminal.SCANHARDCODEDSUBS, detect_invalid=autosubliminal.DETECTINVALIDSUBLANGUAGE, wanted_languages=wanted_languages) # Process the video file if there are missing subtitles if len(languages) > 0: # Add missing languages and store in wanted_items database wanted_item.languages = languages self.wanted_db.set_wanted_item(wanted_item) else: log.debug('Video has no missing subtitles') return None else: return None # Check if we need to skip it and delete it from the database log.debug('Checking if the video needs to be skipped') if wanted_item: # Skip episode check if wanted_item.is_episode: title = wanted_item.title season = wanted_item.season if skip_show(title, season): self.wanted_db.delete_wanted_item(wanted_item) log.info('Skipping %s - Season %s', title, season) return None # Skip movie check if wanted_item.is_movie: title = wanted_item.title year = wanted_item.year if skip_movie(title, year): self.wanted_db.delete_wanted_item(wanted_item) log.info('Skipping %s (%s)', title, year) return None return wanted_item
class DiskScanner(ScheduledProcess): """Disk scanner. Scan the specified path for episodes and movies with missing subtitles. If found, episodes and movies are added to the WANTEDQUEUE. """ def __init__(self): super(DiskScanner, self).__init__() self.wanted_db = WantedItemsDb() self.show_settings_db = ShowSettingsDb() self.movie_settings_db = MovieSettingsDb() @release_wanted_queue_lock_on_exception def run(self, force_run): paths = autosubliminal.VIDEOPATHS log.info('Starting round of local disk checking at %r', paths) # Show info message (only when run was forced manually) if force_run: send_websocket_notification('Scanning disk...') # Check if a path exists to scan if not one_path_exists(paths): log.error( 'None of the configured video paths (%r) exists, aborting...', paths) return # Walk through paths to search for wanted items new_wanted_items = [] old_wanted_items = self.wanted_db.get_wanted_items() for path in paths: try: new_wanted_items.extend(self._scan_path(path)) except Exception: log.exception( 'Could not scan the video path (%s), skipping it', path) # Cleanup wanted items that have been removed from disk manually but are still stored in the db log.debug( 'Checking for non existing wanted items in wanted_items database') for item in old_wanted_items: if item not in new_wanted_items: self.wanted_db.delete_wanted_item(item) log.debug('Deleted non existing wanted item: %s', item.videopath) # Populate WANTEDQUEUE with all items from wanted_items database log.info('Listing videos with missing subtitles:') autosubliminal.WANTEDQUEUE = [] for item in self.wanted_db.get_wanted_items(): log.info('%s %s', item.videopath, item.languages) autosubliminal.WANTEDQUEUE.append(item) # Send home page reload event send_websocket_event(PAGE_RELOAD, data={'name': 'home'}) log.info('Finished round of local disk checking') def _scan_path(self, path): log.info('Scanning video path: %s', path) wanted_items = [] # 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): log.debug('Video file found: %s', filename) wanted_item = None try: wanted_item = self._scan_file(dirname, filename) except Exception: log.exception('Error while scanning video file: %s', os.path.join(dirname, filename)) # Add it to list of wanted items if wanted_item: log.debug('Video added to list of wanted items') wanted_items.append(wanted_item) return wanted_items def _scan_file(self, dirname, filename): # Check if video file has already been processed before, so we don't need to process it again wanted_item = self.wanted_db.get_wanted_item( os.path.join(dirname, filename)) if wanted_item: log.debug( 'Video found in wanted_items database, no need to scan it again' ) else: log.debug( 'Video not found in wanted_items database, start scanning it') # Process file wanted_item = fileprocessor.process_file(dirname, filename) if wanted_item: # Determine wanted languages wanted_languages = [] if wanted_item.is_episode and wanted_item.tvdbid: settings = self.show_settings_db.get_show_settings( wanted_item.tvdbid) if settings and settings.wanted_languages: wanted_languages = settings.wanted_languages elif wanted_item.is_movie and wanted_item.imdbid: settings = self.movie_settings_db.get_movie_settings( wanted_item.imdbid) if settings and settings.wanted_languages: wanted_languages = settings.wanted_languages # Check for missing subtitles (scan embedded and detect invalid if configured to do so) languages = check_missing_subtitle_languages( dirname, filename, scan_embedded=autosubliminal.SCANEMBEDDEDSUBS, scan_hardcoded=autosubliminal.SCANHARDCODEDSUBS, detect_invalid=autosubliminal.DETECTINVALIDSUBLANGUAGE, wanted_languages=wanted_languages) # Process the video file if there are missing subtitles if len(languages) > 0: # Add missing languages and store in wanted_items database wanted_item.languages = languages self.wanted_db.set_wanted_item(wanted_item) else: log.debug('Video has no missing subtitles') return None else: return None # Check if we need to skip it and delete it from the database log.debug('Checking if the video needs to be skipped') if wanted_item: # Skip episode check if wanted_item.is_episode: title = wanted_item.title season = wanted_item.season if skip_show(title, season): self.wanted_db.delete_wanted_item(wanted_item) log.info('Skipping %s - Season %s', title, season) return None # Skip movie check if wanted_item.is_movie: title = wanted_item.title year = wanted_item.year if skip_movie(title, year): self.wanted_db.delete_wanted_item(wanted_item) log.info('Skipping %s (%s)', title, year) return None return wanted_item