def addMovies(self): movies = fireEvent('automation.get_movies', merge=True) movie_ids = [] for imdb_id in movies: if self.shuttingDown(): break prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default=False) if not added: added_movie = fireEvent('movie.add', params={'identifier': imdb_id}, force_readd=False, search_after=False, update_after=True, single=True) if added_movie: movie_ids.append(added_movie['_id']) Env.prop(prop_name, True) for movie_id in movie_ids: if self.shuttingDown(): break movie_dict = fireEvent('media.get', movie_id, single=True) if movie_dict: fireEvent('movie.searcher.single', movie_dict) return True
def getDomain(self, url=''): if not self.domain: for proxy in self.proxy_list: prop_name = 'tpb_proxy.%s' % proxy last_check = float(Env.prop(prop_name, default=0)) if last_check > time.time() - 1209600: continue data = '' try: data = self.urlopen(proxy, timeout=3) except: log.debug('Failed tpb proxy %s', proxy) if 'title="Pirate Search"' in data: log.debug('Using proxy: %s', proxy) self.domain = proxy break Env.prop(prop_name, time.time()) if not self.domain: log.error( 'No TPB proxies left, please add one in settings, or let us know which one to add on the forum.' ) return None return cleanHost(self.domain).rstrip('/') + url
def updateLibrary(self, full = True): last_update = float(Env.prop('manage.last_update', default = 0)) if self.isDisabled() or (last_update > time.time() - 20): return directories = self.directories() added_identifiers = [] for directory in directories: if not os.path.isdir(directory): if len(directory) > 0: log.error('Directory doesn\'t exist: %s' % directory) continue log.info('Updating manage library: %s' % directory) identifiers = fireEvent('scanner.folder', folder = directory, newer_than = last_update, single = True) added_identifiers.extend(identifiers) # Break if CP wants to shut down if self.shuttingDown(): break # If cleanup option is enabled, remove offline files from database if self.conf('cleanup') and full and not self.shuttingDown(): # Get movies with done status done_movies = fireEvent('movie.list', status = 'done', single = True) for done_movie in done_movies: if done_movie['library']['identifier'] not in added_identifiers: fireEvent('movie.delete', movie_id = done_movie['id']) Env.prop('manage.last_update', time.time())
def suggestView(self, limit = 6, **kwargs): movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: db = get_session() active_movies = db.query(Movie) \ .options(joinedload_all('library')) \ .filter(or_(*[Movie.status.has(identifier = s) for s in ['active', 'done']])).all() movies = [x.library.identifier for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default = '')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default = ''))) suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True) self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks return { 'success': True, 'count': len(suggestions), 'suggestions': suggestions[:int(limit)] }
def addMovies(self): movies = fireEvent('automation.get_movies', merge = True) movie_ids = [] for imdb_id in movies: if self.shuttingDown(): break prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default = False) if not added: added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_library = True, single = True) if added_movie: movie_ids.append(added_movie['id']) Env.prop(prop_name, True) for movie_id in movie_ids: if self.shuttingDown(): break movie_dict = fireEvent('media.get', movie_id, single = True) fireEvent('movie.searcher.single', movie_dict) return True
def getDomain(self, url=''): forced_domain = self.conf('domain') if forced_domain: return cleanHost(forced_domain).rstrip('/') + url if not self.proxy_domain: for proxy in self.proxy_list: prop_name = 'proxy.%s' % proxy last_check = float(Env.prop(prop_name, default=0)) if last_check > time.time() - 1209600: continue data = '' try: data = self.urlopen(proxy, timeout=3, show_error=False) except: log.debug('Failed %s proxy %s', (self.getName(), proxy)) if self.correctProxy(data): log.debug('Using proxy for %s: %s', (self.getName(), proxy)) self.proxy_domain = proxy break Env.prop(prop_name, time.time()) if not self.proxy_domain: log.error( 'No %s proxies left, please add one in settings, or let us know which one to add on the forum.', self.getName()) return None return cleanHost(self.proxy_domain).rstrip('/') + url
def search(self, name, year=None, imdb_only=False): try: cache_name = name.decode('utf-8').encode('ascii', 'ignore') except UnicodeEncodeError: cache_name = unicodedata.normalize('NFKD', name).encode('ascii', 'ignore') prop_name = 'automation.cached.%s.%s' % (cache_name, year) cached_imdb = Env.prop(prop_name, default=False) if cached_imdb and imdb_only: return cached_imdb result = fireEvent('movie.search', q='%s %s' % (name, year if year else ''), limit=1, merge=True) if len(result) > 0: if imdb_only and result[0].get('imdb'): Env.prop(prop_name, result[0].get('imdb')) return result[0].get('imdb') if imdb_only else result[0] else: return None
def suggestView(self, limit = 6, **kwargs): movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: db = get_session() active_movies = db.query(Media) \ .options(joinedload_all('library')) \ .filter(or_(*[Media.status.has(identifier = s) for s in ['active', 'done']])).all() movies = [x.library.identifier for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default = '')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default = ''))) suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True) self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks return { 'success': True, 'count': len(suggestions), 'suggestions': suggestions[:int(limit)] }
def suggestView(self, limit = 6, **kwargs): movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: active_movies = fireEvent('media.with_status', ['active', 'done'], single = True) movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default = '')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default = ''))) suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True) self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks return { 'success': True, 'count': len(suggestions), 'suggestions': suggestions[:int(limit)] }
def suggestView(self, limit=6, **kwargs): movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: active_movies = fireEvent('media.with_status', ['active', 'done'], single=True) movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default='')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default=''))) suggestions = fireEvent('movie.suggest', movies=movies, ignore=ignored, single=True) self.setCache('suggestion_cached', suggestions, timeout=6048000) # Cache for 10 weeks return { 'success': True, 'count': len(suggestions), 'suggestions': suggestions[:int(limit)] }
def autoUpdate(self): do_check = True try: last_check = tryInt(Env.prop(self.last_check, default = 0)) now = tryInt(time.time()) do_check = last_check < now - 43200 if do_check: Env.prop(self.last_check, value = now) except: log.error('Failed checking last time to update: %s', traceback.format_exc()) if do_check and self.isEnabled() and self.check() and self.conf('automatic') and not self.updater.update_failed: if self.updater.doUpdate(): # Notify before restarting try: if self.conf('notification'): info = self.updater.info() version_date = datetime.fromtimestamp(info['update_version']['date']) fireEvent('updater.updated', 'CouchPotato: Updated to a new version with hash "%s", this version is from %s' % (info['update_version']['hash'], version_date), data = info) except: log.error('Failed notifying for update: %s', traceback.format_exc()) fireEventAsync('app.restart') return True return False
def getDomain(self, url = ''): forced_domain = self.conf('domain') if forced_domain: return cleanHost(forced_domain).rstrip('/') + url if not self.proxy_domain: for proxy in self.proxy_list: prop_name = 'proxy.%s' % proxy last_check = float(Env.prop(prop_name, default = 0)) if last_check > time.time() - 1209600: continue data = '' try: data = self.urlopen(proxy, timeout = 3, show_error = False) except: log.debug('Failed %s proxy %s', (self.getName(), proxy)) if self.correctProxy(data): log.debug('Using proxy for %s: %s', (self.getName(), proxy)) self.proxy_domain = proxy break Env.prop(prop_name, time.time()) if not self.proxy_domain: log.error('No %s proxies left, please add one in settings, or let us know which one to add on the forum.', self.getName()) return None return cleanHost(self.proxy_domain).rstrip('/') + url
def getDomain(self, url = ''): if not self.domain: for proxy in self.proxy_list: prop_name = 'tpb_proxy.%s' % proxy last_check = float(Env.prop(prop_name, default = 0)) if last_check > time.time() - 1209600: continue data = '' try: data = self.urlopen(proxy, timeout = 3, show_error = False) except: log.debug('Failed tpb proxy %s', proxy) if 'title="Pirate Search"' in data: log.debug('Using proxy: %s', proxy) self.domain = proxy break Env.prop(prop_name, time.time()) if not self.domain: log.error('No TPB proxies left, please add one in settings, or let us know which one to add on the forum.') return None return cleanHost(self.domain).rstrip('/') + url
def cleanReleases(self): prop_name = 'cleaned_releases' already_cleaned = Env.prop(prop_name, default=False) if already_cleaned: return True log.info('Removing releases from library movies') db = get_session() movies = db.query(Movie).all() done_status = fireEvent('status.get', 'done', single=True) available_status = fireEvent('status.get', 'available', single=True) snatched_status = fireEvent('status.get', 'snatched', single=True) for movie in movies: if movie.status_id == done_status.get('id'): for rel in movie.releases: if rel.status_id in [ available_status.get('id'), snatched_status.get('id') ]: fireEvent('release.delete', id=rel.id, single=True) Env.prop(prop_name, True)
def suggestView(self, limit=6, **kwargs): if self.isDisabled(): return {'success': True, 'movies': []} movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: active_movies = fireEvent('media.with_status', ['active', 'done'], types='movie', single=True) movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default='')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default=''))) suggestions = fireEvent('movie.suggest', movies=movies, ignore=ignored, single=True) self.setCache('suggestion_cached', suggestions, timeout=6048000) # Cache for 10 weeks medias = [] for suggestion in suggestions[:int(limit)]: # Cache poster posters = suggestion.get('images', {}).get('poster', []) poster = [x for x in posters if 'tmdb' in x] posters = poster if len(poster) > 0 else posters cached_poster = fireEvent( 'file.download', url=posters[0], single=True) if len(posters) > 0 else False files = {'image_poster': [cached_poster]} if cached_poster else {} medias.append({ 'status': 'suggested', 'title': getTitle(suggestion), 'type': 'movie', 'info': suggestion, 'files': files, 'identifiers': { 'imdb': suggestion.get('imdb') } }) return {'success': True, 'movies': medias}
def addMovies(self): movies = fireEvent('automation.get_movies', merge = True) for imdb_id in movies: prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default = False) if not added: fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False) Env.prop(prop_name, True)
def suggestView(self, limit = 6, **kwargs): if self.isDisabled(): return { 'success': True, 'movies': [] } movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: active_movies = fireEvent('media.with_status', ['active', 'done'], types = 'movie', single = True) movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default = '')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default = ''))) suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True) self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks medias = [] for suggestion in suggestions[:int(limit)]: # Cache poster posters = suggestion.get('images', {}).get('poster', []) poster = [x for x in posters if 'tmdb' in x] posters = poster if len(poster) > 0 else posters cached_poster = fireEvent('file.download', url = posters[0], single = True) if len(posters) > 0 else False files = {'image_poster': [cached_poster] } if cached_poster else {} medias.append({ 'status': 'suggested', 'title': getTitle(suggestion), 'type': 'movie', 'info': suggestion, 'files': files, 'identifiers': { 'imdb': suggestion.get('imdb') } }) return { 'success': True, 'movies': medias }
def addMovies(self): movies = fireEvent('automation.get_movies', merge=True) for imdb_id in movies: prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default=False) if not added: fireEvent('movie.add', params={'identifier': imdb_id}, force_readd=False) Env.prop(prop_name, True)
def getIMDBids(self): if self.isDisabled(): return movies = [] enablers = self.conf('automation_urls_use').split(',') index = -1 for rss_url in self.conf('automation_urls').split(','): index += 1 if not enablers[index]: continue elif 'rss.imdb' not in rss_url: log.error('This isn\'t the correct url.: %s', rss_url) continue prop_name = 'automation.imdb.last_update.%s' % md5(rss_url) last_update = float(Env.prop(prop_name, default=0)) last_movie_added = 0 try: cache_key = 'imdb.rss.%s' % md5(rss_url) rss_data = self.getCache(cache_key, rss_url) data = XMLTree.fromstring(rss_data) rss_movies = self.getElements(data, 'channel/item') for movie in rss_movies: created = int( time.mktime( parse(self.getTextElement(movie, "pubDate")).timetuple())) imdb = getImdb(self.getTextElement(movie, "link")) if created > last_movie_added: last_movie_added = created if not imdb or created <= last_update: continue movies.append(imdb) except: log.error('Failed loading IMDB watchlist: %s %s', (rss_url, traceback.format_exc())) Env.prop(prop_name, last_movie_added) return movies
def getIMDBids(self): if self.isDisabled(): return movies = [] enablers = self.conf('automation_urls_use').split(',') index = -1 for rss_url in self.conf('automation_urls').split(','): index += 1 if not enablers[index]: continue prop_name = 'automation.moviesio.last_update.%s' % md5(rss_url) last_update = float(Env.prop(prop_name, default = 0)) last_movie_added = 0 try: cache_key = 'imdb.rss.%s' % md5(rss_url) rss_data = self.getCache(cache_key, rss_url, headers = {'Referer': ''}) data = XMLTree.fromstring(rss_data) rss_movies = self.getElements(data, 'channel/item') for movie in rss_movies: created = int(time.mktime(parse(self.getTextElement(movie, "pubDate")).timetuple())) if created > last_movie_added: last_movie_added = created if created <= last_update: continue nameyear = fireEvent('scanner.name_year', self.getTextElement(movie, "title"), single = True) imdb = self.search(nameyear.get('name'), nameyear.get('year'), imdb_only = True) if not imdb: continue movies.append(imdb) except ParseError: log.debug('Failed loading Movies.io watchlist, probably empty: %s', (rss_url)) except: log.error('Failed loading Movies.io watchlist: %s %s', (rss_url, traceback.format_exc())) Env.prop(prop_name, last_movie_added) return movies
def checkMessages(self): prop_name = 'messages.last_check' last_check = tryInt(Env.prop(prop_name, default = 0)) messages = fireEvent('cp.messages', last_check = last_check, single = True) for message in messages: if message.get('time') > last_check: fireEvent('core.message', message = message.get('message'), data = message) if last_check < message.get('time'): last_check = message.get('time') Env.prop(prop_name, value = last_check)
def search(self, name, year = None, imdb_only = False): prop_name = 'automation.cached.%s.%s' % (name, year) cached_imdb = Env.prop(prop_name, default = False) if cached_imdb and imdb_only: return cached_imdb result = fireEvent('movie.search', q = '%s %s' % (name, year if year else ''), limit = 1, merge = True) if len(result) > 0: if imdb_only and result[0].get('imdb'): Env.prop(prop_name, result[0].get('imdb')) return result[0].get('imdb') if imdb_only else result[0] else: return None
def ignoreView(self, imdb = None, limit = 6, remove_only = False, **kwargs): ignored = splitString(Env.prop('suggest_ignore', default = '')) if imdb: if not remove_only: ignored.append(imdb) Env.prop('suggest_ignore', ','.join(set(ignored))) new_suggestions = self.updateSuggestionCache(ignore_imdb = imdb, limit = limit, ignored = ignored) return { 'result': True, 'ignore_count': len(ignored), 'suggestions': new_suggestions[limit - 1:limit] }
def search(self, name, year=None, imdb_only=False): cache_name = name.decode("utf-8").encode("ascii", "ignore") prop_name = "automation.cached.%s.%s" % (cache_name, year) cached_imdb = Env.prop(prop_name, default=False) if cached_imdb and imdb_only: return cached_imdb result = fireEvent("movie.search", q="%s %s" % (name, year if year else ""), limit=1, merge=True) if len(result) > 0: if imdb_only and result[0].get("imdb"): Env.prop(prop_name, result[0].get("imdb")) return result[0].get("imdb") if imdb_only else result[0] else: return None
def checkMessages(self): prop_name = 'messages.last_check' last_check = tryInt(Env.prop(prop_name, default=0)) messages = fireEvent('cp.messages', last_check=last_check, single=True) for message in messages: if message.get('time') > last_check: fireEvent('core.message', message=message.get('message'), data=message) if last_check < message.get('time'): last_check = message.get('time') Env.prop(prop_name, value=last_check)
def addMovies(self): movies = fireEvent('automation.get_movies', merge = True) movie_ids = [] for imdb_id in movies: prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default = False) if not added: added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_library = True, single = True) if added_movie: movie_ids.append(added_movie['id']) Env.prop(prop_name, True) for movie_id in movie_ids: movie_dict = fireEvent('movie.get', movie_id, single = True) fireEvent('searcher.single', movie_dict)
def ignoreView(self, imdb = None, limit = 6, remove_only = False, **kwargs): ignored = splitString(Env.prop('suggest_ignore', default = '')) new_suggestions = [] if imdb: if not remove_only: ignored.append(imdb) Env.prop('suggest_ignore', ','.join(set(ignored))) new_suggestions = self.updateSuggestionCache(ignore_imdb = imdb, limit = limit, ignored = ignored) return { 'result': True, 'ignore_count': len(ignored), 'suggestions': new_suggestions[limit - 1:limit] }
def checkMessages(self): prop_name = "messages.last_check" last_check = tryInt(Env.prop(prop_name, default=0)) messages = fireEvent("cp.messages", last_check=last_check, single=True) or [] for message in messages: if message.get("time") > last_check: message["sticky"] = True # Always sticky core messages message_type = "core.message.important" if message.get("important") else "core.message" fireEvent(message_type, message=message.get("message"), data=message) if last_check < message.get("time"): last_check = message.get("time") Env.prop(prop_name, value=last_check)
def checkMessages(self): prop_name = 'messages.last_check' last_check = tryInt(Env.prop(prop_name, default = 0)) messages = fireEvent('cp.messages', last_check = last_check, single = True) or [] for message in messages: if message.get('time') > last_check: message['sticky'] = True # Always sticky core messages message_type = 'core.message.important' if message.get('important') else 'core.message' fireEvent(message_type, message = message.get('message'), data = message) if last_check < message.get('time'): last_check = message.get('time') Env.prop(prop_name, value = last_check)
def getIMDBids(self): if self.isDisabled(): return movies = [] enablers = self.conf('automation_urls_use').split(',') index = -1 for rss_url in self.conf('automation_urls').split(','): index += 1 if not enablers[index]: continue elif 'rss.imdb' not in rss_url: log.error('This isn\'t the correct url.: %s' % rss_url) continue prop_name = 'automation.imdb.last_update.%s' % md5(rss_url) last_update = float(Env.prop(prop_name, default = 0)) try: cache_key = 'imdb.rss.%s' % md5(rss_url) rss_data = self.getCache(cache_key, rss_url) data = XMLTree.fromstring(rss_data) rss_movies = self.getElements(data, 'channel/item') for movie in rss_movies: created = int(time.mktime(parse(self.getTextElement(movie, "pubDate")).timetuple())) imdb = getImdb(self.getTextElement(movie, "link")) if not imdb or created < last_update: continue movies.append(imdb) except: log.error('Failed loading IMDB watchlist: %s %s' % (rss_url, traceback.format_exc())) Env.prop(prop_name, time.time()) return movies
def updateLibrary(self, full = True): last_update = float(Env.prop('manage.last_update', default = 0)) if self.isDisabled() or (last_update > time.time() - 20): return directories = self.directories() added_identifiers = [] for directory in directories: if not os.path.isdir(directory): if len(directory) > 0: log.error('Directory doesn\'t exist: %s', directory) continue log.info('Updating manage library: %s', directory) identifiers = fireEvent('scanner.folder', folder = directory, newer_than = last_update if not full else 0, single = True) if identifiers: added_identifiers.extend(identifiers) # Break if CP wants to shut down if self.shuttingDown(): break # If cleanup option is enabled, remove offline files from database if self.conf('cleanup') and full and not self.shuttingDown(): # Get movies with done status total_movies, done_movies = fireEvent('movie.list', status = 'done', single = True) for done_movie in done_movies: if done_movie['library']['identifier'] not in added_identifiers: fireEvent('movie.delete', movie_id = done_movie['id'], delete_from = 'all') else: for release in done_movie.get('releases', []): for release_file in release.get('files', []): # Remove release not available anymore if not os.path.isfile(ss(release_file['path'])): fireEvent('release.clean', release['id']) break Env.prop('manage.last_update', time.time())
def search(self, name, year = None, imdb_only = False): try: cache_name = name.decode('utf-8').encode('ascii', 'ignore') except UnicodeEncodeError: cache_name = unicodedata.normalize('NFKD', name).encode('ascii','ignore') prop_name = 'automation.cached.%s.%s' % (cache_name, year) cached_imdb = Env.prop(prop_name, default = False) if cached_imdb and imdb_only: return cached_imdb result = fireEvent('movie.search', q = '%s %s' % (name, year if year else ''), limit = 1, merge = True) if len(result) > 0: if imdb_only and result[0].get('imdb'): Env.prop(prop_name, result[0].get('imdb')) return result[0].get('imdb') if imdb_only else result[0] else: return None
def getIMDBids(self): if self.isDisabled(): return movies = [] headers = {} for csv_url in self.conf('automation_urls').split(','): prop_name = 'automation.imdb.last_update.%s' % md5(csv_url) last_update = float(Env.prop(prop_name, default = 0)) try: cache_key = 'imdb_csv.%s' % md5(csv_url) csv_data = self.getCache(cache_key, csv_url) csv_reader = csv.reader(StringIO.StringIO(csv_data)) if not headers: nr = 0 for column in csv_reader.next(): headers[column] = nr nr += 1 for row in csv_reader: created = int(time.mktime(parse(row[headers['created']]).timetuple())) if created < last_update: continue imdb = row[headers['const']] if imdb: movies.append(imdb) except: log.error('Failed loading IMDB watchlist: %s %s' % (csv_url, traceback.format_exc())) Env.prop(prop_name, time.time()) return movies
def cleanReleases(self): prop_name = 'cleaned_releases' already_cleaned = Env.prop(prop_name, default = False) if already_cleaned: return True log.info('Removing releases from library movies') db = get_session() movies = db.query(Movie).all() done_status = fireEvent('status.get', 'done', single = True) available_status = fireEvent('status.get', 'available', single = True) snatched_status = fireEvent('status.get', 'snatched', single = True) for movie in movies: if movie.status_id == done_status.get('id'): for rel in movie.releases: if rel.status_id in [available_status.get('id'), snatched_status.get('id')]: fireEvent('release.delete', id = rel.id, single = True) Env.prop(prop_name, True)
def ignoreView(self, imdb=None, limit=6, remove_only=False, mark_seen=False, **kwargs): ignored = splitString(Env.prop('suggest_ignore', default='')) seen = splitString(Env.prop('suggest_seen', default='')) new_suggestions = [] if imdb: if mark_seen: seen.append(imdb) Env.prop('suggest_seen', ','.join(set(seen))) elif not remove_only: ignored.append(imdb) Env.prop('suggest_ignore', ','.join(set(ignored))) new_suggestions = self.updateSuggestionCache(ignore_imdb=imdb, limit=limit, ignored=ignored, seen=seen) if len(new_suggestions) <= limit: return {'result': False} # Only return new (last) item media = { 'status': 'suggested', 'title': getTitle(new_suggestions[limit]), 'type': 'movie', 'info': new_suggestions[limit], 'identifiers': { 'imdb': new_suggestions[limit].get('imdb') } } return {'result': True, 'movie': media}
def ignoreView(self, imdb = None, limit = 6, remove_only = False, mark_seen = False, **kwargs): ignored = splitString(Env.prop('suggest_ignore', default = '')) seen = splitString(Env.prop('suggest_seen', default = '')) new_suggestions = [] if imdb: if mark_seen: seen.append(imdb) Env.prop('suggest_seen', ','.join(set(seen))) elif not remove_only: ignored.append(imdb) Env.prop('suggest_ignore', ','.join(set(ignored))) new_suggestions = self.updateSuggestionCache(ignore_imdb = imdb, limit = limit, ignored = ignored, seen = seen) if len(new_suggestions) <= limit: return { 'result': False } # Only return new (last) item media = { 'status': 'suggested', 'title': getTitle(new_suggestions[limit]), 'type': 'movie', 'info': new_suggestions[limit], 'identifiers': { 'imdb': new_suggestions[limit].get('imdb') } } return { 'result': True, 'movie': media }
def single(self, movie, search_protocols = None, manual = False, force_download = False): # Find out search type try: if not search_protocols: search_protocols = fireEvent('searcher.protocols', single = True) except SearchSetupError: return if not movie['profile_id'] or (movie['status'] == 'done' and not manual): log.debug('Movie doesn\'t have a profile or already done, assuming in manage tab.') fireEvent('media.restatus', movie['_id'], single = True) return default_title = getTitle(movie) if not default_title: log.error('No proper info found for movie, removing it from library to stop it from causing more issues.') fireEvent('media.delete', movie['_id'], single = True) return # Update media status and check if it is still not done (due to the stop searching after feature if fireEvent('media.restatus', movie['_id'], single = True) == 'done': log.debug('No better quality found, marking movie %s as done.', default_title) pre_releases = fireEvent('quality.pre_releases', single = True) release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge = True) found_releases = [] previous_releases = movie.get('releases', []) too_early_to_search = [] outside_eta_results = 0 alway_search = self.conf('always_search') ignore_eta = manual total_result_count = 0 fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) # Ignore eta once every 7 days if not alway_search: prop_name = 'last_ignored_eta.%s' % movie['_id'] last_ignored_eta = float(Env.prop(prop_name, default = 0)) if last_ignored_eta > time.time() - 604800: ignore_eta = True Env.prop(prop_name, value = time.time()) db = get_db() profile = db.get('id', movie['profile_id']) ret = False for index, q_identifier in enumerate(profile.get('qualities', [])): quality_custom = { 'index': index, 'quality': q_identifier, 'finish': profile['finish'][index], 'wait_for': tryInt(profile['wait_for'][index]), '3d': profile['3d'][index] if profile.get('3d') else False } could_not_be_released = not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']) if not alway_search and could_not_be_released: too_early_to_search.append(q_identifier) # Skip release, if ETA isn't ignored if not ignore_eta: continue has_better_quality = 0 # See if better quality is available for release in movie.get('releases', []): if release['status'] not in ['available', 'ignored', 'failed']: is_higher = fireEvent('quality.ishigher', \ {'identifier': q_identifier, 'is_3d': quality_custom.get('3d', 0)}, \ {'identifier': release['quality'], 'is_3d': release.get('is_3d', 0)}, \ profile, single = True) if is_higher != 'higher': has_better_quality += 1 # Don't search for quality lower then already available. if has_better_quality > 0: log.info('Better quality (%s) already available or snatched for %s', (q_identifier, default_title)) fireEvent('media.restatus', movie['_id'], single = True) break quality = fireEvent('quality.single', identifier = q_identifier, single = True) log.info('Search for %s in %s%s', (default_title, quality['label'], ' ignoring ETA' if alway_search or ignore_eta else '')) # Extend quality with profile customs quality['custom'] = quality_custom results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or [] results_count = len(results) total_result_count += results_count if results_count == 0: log.debug('Nothing found for %s in %s', (default_title, quality['label'])) # Keep track of releases found outside ETA window outside_eta_results += results_count if could_not_be_released else 0 # Check if movie isn't deleted while searching if not fireEvent('media.get', movie.get('_id'), single = True): break # Add them to this movie releases list found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True) # Don't trigger download, but notify user of available releases if could_not_be_released: if results_count > 0: log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title)) # Try find a valid result and download it if (force_download or not could_not_be_released or alway_search) and fireEvent('release.try_download_result', results, movie, quality_custom, single = True): ret = True # Remove releases that aren't found anymore temp_previous_releases = [] for release in previous_releases: if release.get('status') == 'available' and release.get('identifier') not in found_releases: fireEvent('release.delete', release.get('_id'), single = True) else: temp_previous_releases.append(release) previous_releases = temp_previous_releases del temp_previous_releases # Break if CP wants to shut down if self.shuttingDown() or ret: break if total_result_count > 0: fireEvent('media.tag', movie['_id'], 'recent', update_edited = True, single = True) if len(too_early_to_search) > 0: log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) if outside_eta_results > 0: message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % (outside_eta_results, default_title) log.info(message) if not manual: fireEvent('media.available', message = message, data = {}) fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) return ret
def updateLibrary(self, full = True): last_update = float(Env.prop('manage.last_update', default = 0)) if self.in_progress: log.info('Already updating library: %s', self.in_progress) return elif self.isDisabled() or (last_update > time.time() - 20): return self.in_progress = {} fireEvent('notify.frontend', type = 'manage.updating', data = True) try: directories = self.directories() added_identifiers = [] # Add some progress self.in_progress = {} for directory in directories: self.in_progress[os.path.normpath(directory)] = { 'total': None, 'to_go': None, } for directory in directories: folder = os.path.normpath(directory) if not os.path.isdir(folder): if len(directory) > 0: log.error('Directory doesn\'t exist: %s', folder) continue log.info('Updating manage library: %s', folder) fireEvent('notify.frontend', type = 'manage.update', data = True, message = 'Scanning for movies in "%s"' % folder) onFound = self.createAddToLibrary(folder, added_identifiers) fireEvent('scanner.scan', folder = folder, simple = True, newer_than = last_update if not full else 0, on_found = onFound, single = True) # Break if CP wants to shut down if self.shuttingDown(): break # If cleanup option is enabled, remove offline files from database if self.conf('cleanup') and full and not self.shuttingDown(): # Get movies with done status total_movies, done_movies = fireEvent('movie.list', status = 'done', single = True) for done_movie in done_movies: if done_movie['library']['identifier'] not in added_identifiers: fireEvent('movie.delete', movie_id = done_movie['id'], delete_from = 'all') else: for release in done_movie.get('releases', []): if len(release.get('files', [])) == 0: fireEvent('release.delete', release['id']) else: for release_file in release.get('files', []): # Remove release not available anymore if not os.path.isfile(ss(release_file['path'])): fireEvent('release.clean', release['id']) break # Check if there are duplicate releases (different quality) use the last one, delete the rest if len(done_movie.get('releases', [])) > 1: used_files = {} for release in done_movie.get('releases', []): for release_file in release.get('files', []): already_used = used_files.get(release_file['path']) if already_used: if already_used < release['id']: fireEvent('release.delete', release['id'], single = True) # delete this one else: fireEvent('release.delete', already_used, single = True) # delete previous one break else: used_files[release_file['path']] = release.get('id') del used_files Env.prop('manage.last_update', time.time()) except: log.error('Failed updating library: %s', (traceback.format_exc())) while True and not self.shuttingDown(): delete_me = {} for folder in self.in_progress: if self.in_progress[folder]['to_go'] <= 0: delete_me[folder] = True for delete in delete_me: del self.in_progress[delete] if len(self.in_progress) == 0: break time.sleep(1) fireEvent('notify.frontend', type = 'manage.updating', data = False) self.in_progress = False
def updateLibrary(self, full=True): last_update = float(Env.prop('manage.last_update', default=0)) if self.in_progress: log.info('Already updating library: %s', self.in_progress) return elif self.isDisabled() or (last_update > time.time() - 20): return self.in_progress = {} fireEvent('notify.frontend', type='manage.updating', data=True) try: directories = self.directories() added_identifiers = [] # Add some progress self.in_progress = {} for directory in directories: self.in_progress[os.path.normpath(directory)] = { 'total': None, 'to_go': None, } for directory in directories: folder = os.path.normpath(directory) if not os.path.isdir(folder): if len(directory) > 0: log.error('Directory doesn\'t exist: %s', folder) continue log.info('Updating manage library: %s', folder) fireEvent('notify.frontend', type='manage.update', data=True, message='Scanning for movies in "%s"' % folder) onFound = self.createAddToLibrary(folder, added_identifiers) fireEvent('scanner.scan', folder=folder, simple=True, newer_than=last_update if not full else 0, on_found=onFound, single=True) # Break if CP wants to shut down if self.shuttingDown(): break # If cleanup option is enabled, remove offline files from database if self.conf('cleanup') and full and not self.shuttingDown(): # Get movies with done status total_movies, done_movies = fireEvent('movie.list', status='done', single=True) for done_movie in done_movies: if done_movie['library'][ 'identifier'] not in added_identifiers: fireEvent('movie.delete', movie_id=done_movie['id'], delete_from='all') else: for release in done_movie.get('releases', []): for release_file in release.get('files', []): # Remove release not available anymore if not os.path.isfile(ss( release_file['path'])): fireEvent('release.clean', release['id']) break Env.prop('manage.last_update', time.time()) except: log.error('Failed updating library: %s', (traceback.format_exc())) while True and not self.shuttingDown(): delete_me = {} for folder in self.in_progress: if self.in_progress[folder]['to_go'] <= 0: delete_me[folder] = True for delete in delete_me: del self.in_progress[delete] if len(self.in_progress) == 0: break time.sleep(1) fireEvent('notify.frontend', type='manage.updating', data=False) self.in_progress = False
def getIMDBids(self): if self.isDisabled(): return movies = [] enablers = self.conf('automation_urls_use').split(',') index = -1 for rss_url in self.conf('automation_urls').split(','): index += 1 if not enablers[index]: continue prop_name = 'automation.moviesio.last_update.%s' % md5(rss_url) last_update = float(Env.prop(prop_name, default=0)) last_movie_added = 0 try: cache_key = 'imdb.rss.%s' % md5(rss_url) rss_data = self.getCache(cache_key, rss_url, headers={'Referer': ''}) data = XMLTree.fromstring(rss_data) rss_movies = self.getElements(data, 'channel/item') for movie in rss_movies: created = int( time.mktime( parse(self.getTextElement(movie, "pubDate")).timetuple())) if created > last_movie_added: last_movie_added = created if created <= last_update: continue nameyear = fireEvent('scanner.name_year', self.getTextElement(movie, "title"), single=True) imdb = self.search(nameyear.get('name'), nameyear.get('year'), imdb_only=True) if not imdb: continue movies.append(imdb) except ParseError: log.debug( 'Failed loading Movies.io watchlist, probably empty: %s', (rss_url)) except: log.error('Failed loading Movies.io watchlist: %s %s', (rss_url, traceback.format_exc())) Env.prop(prop_name, last_movie_added) return movies
def single(self, movie, search_protocols=None, manual=False, force_download=False): # Find out search type try: if not search_protocols: search_protocols = fireEvent('searcher.protocols', single=True) except SearchSetupError: return if not movie['profile_id'] or (movie['status'] == 'done' and not manual): log.debug( 'Movie doesn\'t have a profile or already done, assuming in manage tab.' ) fireEvent('media.restatus', movie['_id'], single=True) return default_title = getTitle(movie) if not default_title: log.error( 'No proper info found for movie, removing it from library to stop it from causing more issues.' ) fireEvent('media.delete', movie['_id'], single=True) return # Update media status and check if it is still not done (due to the stop searching after feature if fireEvent('media.restatus', movie['_id'], single=True) == 'done': log.debug('No better quality found, marking movie %s as done.', default_title) pre_releases = fireEvent('quality.pre_releases', single=True) release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge=True) found_releases = [] previous_releases = movie.get('releases', []) too_early_to_search = [] outside_eta_results = 0 alway_search = self.conf('always_search') ignore_eta = manual total_result_count = 0 fireEvent('notify.frontend', type='movie.searcher.started', data={'_id': movie['_id']}, message='Searching for "%s"' % default_title) # Ignore eta once every 7 days if not alway_search: prop_name = 'last_ignored_eta.%s' % movie['_id'] last_ignored_eta = float(Env.prop(prop_name, default=0)) if last_ignored_eta > time.time() - 604800: ignore_eta = True Env.prop(prop_name, value=time.time()) db = get_db() profile = db.get('id', movie['profile_id']) ret = False for index, q_identifier in enumerate(profile.get('qualities', [])): quality_custom = { 'index': index, 'quality': q_identifier, 'finish': profile['finish'][index], 'wait_for': tryInt(profile['wait_for'][index]), '3d': profile['3d'][index] if profile.get('3d') else False } could_not_be_released = not self.couldBeReleased( q_identifier in pre_releases, release_dates, movie['info']['year']) if not alway_search and could_not_be_released: too_early_to_search.append(q_identifier) # Skip release, if ETA isn't ignored if not ignore_eta: continue has_better_quality = 0 # See if better quality is available for release in movie.get('releases', []): if release['status'] not in ['available', 'ignored', 'failed']: is_higher = fireEvent('quality.ishigher', \ {'identifier': q_identifier, 'is_3d': quality_custom.get('3d', 0)}, \ {'identifier': release['quality'], 'is_3d': release.get('is_3d', 0)}, \ profile, single = True) if is_higher != 'higher': has_better_quality += 1 # Don't search for quality lower then already available. if has_better_quality > 0: log.info( 'Better quality (%s) already available or snatched for %s', (q_identifier, default_title)) fireEvent('media.restatus', movie['_id'], single=True) break quality = fireEvent('quality.single', identifier=q_identifier, single=True) log.info('Search for %s in %s%s', (default_title, quality['label'], ' ignoring ETA' if alway_search or ignore_eta else '')) # Extend quality with profile customs quality['custom'] = quality_custom results = fireEvent('searcher.search', search_protocols, movie, quality, single=True) or [] results_count = len(results) total_result_count += results_count if results_count == 0: log.debug('Nothing found for %s in %s', (default_title, quality['label'])) # Keep track of releases found outside ETA window outside_eta_results += results_count if could_not_be_released else 0 # Check if movie isn't deleted while searching if not fireEvent('media.get', movie.get('_id'), single=True): break # Add them to this movie releases list found_releases += fireEvent('release.create_from_search', results, movie, quality, single=True) # Don't trigger download, but notify user of available releases if could_not_be_released: if results_count > 0: log.debug( 'Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title)) # Try find a valid result and download it if (force_download or not could_not_be_released or alway_search) and fireEvent('release.try_download_result', results, movie, quality_custom, single=True): ret = True # Remove releases that aren't found anymore temp_previous_releases = [] for release in previous_releases: if release.get('status') == 'available' and release.get( 'identifier') not in found_releases: fireEvent('release.delete', release.get('_id'), single=True) else: temp_previous_releases.append(release) previous_releases = temp_previous_releases del temp_previous_releases # Break if CP wants to shut down if self.shuttingDown() or ret: break if total_result_count > 0: fireEvent('media.tag', movie['_id'], 'recent', update_edited=True, single=True) if len(too_early_to_search) > 0: log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) if outside_eta_results > 0: message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % ( outside_eta_results, default_title) log.info(message) if not manual: fireEvent('media.available', message=message, data={}) fireEvent('notify.frontend', type='movie.searcher.ended', data={'_id': movie['_id']}) return ret
def updateLibrary(self, full = True): last_update_key = 'manage.last_update%s' % ('_full' if full else '') last_update = float(Env.prop(last_update_key, default = 0)) if self.in_progress: log.info('Already updating library: %s', self.in_progress) return elif self.isDisabled() or (last_update > time.time() - 20): return self.in_progress = {} fireEvent('notify.frontend', type = 'manage.updating', data = True) try: directories = self.directories() directories.sort() added_identifiers = [] # Add some progress for directory in directories: self.in_progress[os.path.normpath(directory)] = { 'started': False, 'eta': -1, 'total': None, 'to_go': None, } for directory in directories: folder = os.path.normpath(directory) self.in_progress[os.path.normpath(directory)]['started'] = tryInt(time.time()) if not os.path.isdir(folder): if len(directory) > 0: log.error('Directory doesn\'t exist: %s', folder) continue log.info('Updating manage library: %s', folder) fireEvent('notify.frontend', type = 'manage.update', data = True, message = 'Scanning for movies in "%s"' % folder) onFound = self.createAddToLibrary(folder, added_identifiers) fireEvent('scanner.scan', folder = folder, simple = True, newer_than = last_update if not full else 0, on_found = onFound, single = True) # Break if CP wants to shut down if self.shuttingDown(): break # If cleanup option is enabled, remove offline files from database if self.conf('cleanup') and full and not self.shuttingDown(): # Get movies with done status total_movies, done_movies = fireEvent('media.list', types = 'movie', status = 'done', release_status = 'done', status_or = True, single = True) deleted_releases = [] for done_movie in done_movies: if getIdentifier(done_movie) not in added_identifiers: fireEvent('media.delete', media_id = done_movie['_id'], delete_from = 'all') else: releases = done_movie.get('releases', []) for release in releases: if release.get('files'): brk = False for file_type in release.get('files', {}): for release_file in release['files'][file_type]: # Remove release not available anymore if not os.path.isfile(sp(release_file)): fireEvent('release.clean', release['_id']) brk = True break if brk: break # Check if there are duplicate releases (different quality) use the last one, delete the rest if len(releases) > 1: used_files = {} for release in releases: for file_type in release.get('files', {}): for release_file in release['files'][file_type]: already_used = used_files.get(release_file) if already_used: release_id = release['_id'] if already_used.get('last_edit', 0) < release.get('last_edit', 0) else already_used['_id'] if release_id not in deleted_releases: fireEvent('release.delete', release_id, single = True) deleted_releases.append(release_id) break else: used_files[release_file] = release del used_files # Break if CP wants to shut down if self.shuttingDown(): break if not self.shuttingDown(): db = get_db() db.reindex() Env.prop(last_update_key, time.time()) except: log.error('Failed updating library: %s', (traceback.format_exc())) while self.in_progress and len(self.in_progress) > 0 and not self.shuttingDown(): delete_me = {} for folder in self.in_progress: if self.in_progress[folder]['to_go'] <= 0: delete_me[folder] = True for delete in delete_me: del self.in_progress[delete] time.sleep(1) fireEvent('notify.frontend', type = 'manage.updating', data = False) self.in_progress = False
def updateLibrary(self, full=True): last_update_key = 'manage.last_update%s' % ('_full' if full else '') last_update = float(Env.prop(last_update_key, default=0)) if self.in_progress: log.info('Already updating library: %s', self.in_progress) return elif self.isDisabled() or (last_update > time.time() - 20): return self.in_progress = {} fireEvent('notify.frontend', type='manage.updating', data=True) try: directories = self.directories() directories.sort() added_identifiers = [] # Add some progress for directory in directories: self.in_progress[os.path.normpath(directory)] = { 'started': False, 'eta': -1, 'total': None, 'to_go': None, } for directory in directories: folder = os.path.normpath(directory) self.in_progress[os.path.normpath( directory)]['started'] = tryInt(time.time()) if not os.path.isdir(folder): if len(directory) > 0: log.error('Directory doesn\'t exist: %s', folder) continue log.info('Updating manage library: %s', folder) fireEvent('notify.frontend', type='manage.update', data=True, message='Scanning for movies in "%s"' % folder) onFound = self.createAddToLibrary(folder, added_identifiers) fireEvent('scanner.scan', folder=folder, simple=True, newer_than=last_update if not full else 0, on_found=onFound, single=True) # Break if CP wants to shut down if self.shuttingDown(): break # If cleanup option is enabled, remove offline files from database if self.conf('cleanup') and full and not self.shuttingDown(): # Get movies with done status total_movies, done_movies = fireEvent('media.list', types='movie', status='done', release_status='done', status_or=True, single=True) deleted_releases = [] for done_movie in done_movies: if getIdentifier(done_movie) not in added_identifiers: fireEvent('media.delete', media_id=done_movie['_id'], delete_from='all') else: releases = done_movie.get('releases', []) for release in releases: if release.get('files'): brk = False for file_type in release.get('files', {}): for release_file in release['files'][ file_type]: # Remove release not available anymore if not os.path.isfile( sp(release_file)): fireEvent('release.clean', release['_id']) brk = True break if brk: break # Check if there are duplicate releases (different quality) use the last one, delete the rest if len(releases) > 1: used_files = {} for release in releases: for file_type in release.get('files', {}): for release_file in release['files'][ file_type]: already_used = used_files.get( release_file) if already_used: release_id = release[ '_id'] if already_used.get( 'last_edit', 0) < release.get( 'last_edit', 0 ) else already_used['_id'] if release_id not in deleted_releases: fireEvent('release.delete', release_id, single=True) deleted_releases.append( release_id) break else: used_files[release_file] = release del used_files # Break if CP wants to shut down if self.shuttingDown(): break if not self.shuttingDown(): db = get_db() db.reindex() Env.prop(last_update_key, time.time()) except: log.error('Failed updating library: %s', (traceback.format_exc())) while self.in_progress and len( self.in_progress) > 0 and not self.shuttingDown(): delete_me = {} for folder in self.in_progress: if self.in_progress[folder]['to_go'] <= 0: delete_me[folder] = True for delete in delete_me: del self.in_progress[delete] time.sleep(1) fireEvent('notify.frontend', type='manage.updating', data=False) self.in_progress = False
def single(self, movie, search_protocols=None, manual=False, force_download=False): # Find out search type try: if not search_protocols: search_protocols = fireEvent("searcher.protocols", single=True) except SearchSetupError: return if not movie["profile_id"] or (movie["status"] == "done" and not manual): log.debug("Movie doesn't have a profile or already done, assuming in manage tab.") fireEvent("media.restatus", movie["_id"], single=True) return default_title = getTitle(movie) if not default_title: log.error("No proper info found for movie, removing it from library to stop it from causing more issues.") fireEvent("media.delete", movie["_id"], single=True) return # Update media status and check if it is still not done (due to the stop searching after feature if fireEvent("media.restatus", movie["_id"], single=True) == "done": log.debug("No better quality found, marking movie %s as done.", default_title) pre_releases = fireEvent("quality.pre_releases", single=True) release_dates = fireEvent("movie.update_release_dates", movie["_id"], merge=True) found_releases = [] previous_releases = movie.get("releases", []) too_early_to_search = [] outside_eta_results = 0 alway_search = self.conf("always_search") ignore_eta = manual total_result_count = 0 fireEvent( "notify.frontend", type="movie.searcher.started", data={"_id": movie["_id"]}, message='Searching for "%s"' % default_title, ) # Ignore eta once every 7 days if not alway_search: prop_name = "last_ignored_eta.%s" % movie["_id"] last_ignored_eta = float(Env.prop(prop_name, default=0)) if last_ignored_eta > time.time() - 604800: ignore_eta = True Env.prop(prop_name, value=time.time()) db = get_db() profile = db.get("id", movie["profile_id"]) ret = False for index, q_identifier in enumerate(profile.get("qualities", [])): quality_custom = { "index": index, "quality": q_identifier, "finish": profile["finish"][index], "wait_for": tryInt(profile["wait_for"][index]), "3d": profile["3d"][index] if profile.get("3d") else False, } could_not_be_released = not self.couldBeReleased( q_identifier in pre_releases, release_dates, movie["info"]["year"] ) if not alway_search and could_not_be_released: too_early_to_search.append(q_identifier) # Skip release, if ETA isn't ignored if not ignore_eta: continue has_better_quality = 0 # See if better quality is available for release in movie.get("releases", []): if release["status"] not in ["available", "ignored", "failed"]: is_higher = fireEvent( "quality.ishigher", {"identifier": q_identifier, "is_3d": quality_custom.get("3d", 0)}, {"identifier": release["quality"], "is_3d": release.get("is_3d", 0)}, profile, single=True, ) if is_higher != "higher": has_better_quality += 1 # Don't search for quality lower then already available. if has_better_quality > 0: log.info("Better quality (%s) already available or snatched for %s", (q_identifier, default_title)) fireEvent("media.restatus", movie["_id"], single=True) break quality = fireEvent("quality.single", identifier=q_identifier, single=True) log.info( "Search for %s in %s%s", (default_title, quality["label"], " ignoring ETA" if alway_search or ignore_eta else ""), ) # Extend quality with profile customs quality["custom"] = quality_custom results = fireEvent("searcher.search", search_protocols, movie, quality, single=True) or [] results_count = len(results) total_result_count += results_count if results_count == 0: log.debug("Nothing found for %s in %s", (default_title, quality["label"])) # Keep track of releases found outside ETA window outside_eta_results += results_count if could_not_be_released else 0 # Check if movie isn't deleted while searching if not fireEvent("media.get", movie.get("_id"), single=True): break # Add them to this movie releases list found_releases += fireEvent("release.create_from_search", results, movie, quality, single=True) # Don't trigger download, but notify user of available releases if could_not_be_released: if results_count > 0: log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title)) # Try find a valid result and download it if (force_download or not could_not_be_released or alway_search) and fireEvent( "release.try_download_result", results, movie, quality_custom, single=True ): ret = True # Remove releases that aren't found anymore temp_previous_releases = [] for release in previous_releases: if release.get("status") == "available" and release.get("identifier") not in found_releases: fireEvent("release.delete", release.get("_id"), single=True) else: temp_previous_releases.append(release) previous_releases = temp_previous_releases del temp_previous_releases # Break if CP wants to shut down if self.shuttingDown() or ret: break if total_result_count > 0: fireEvent("media.tag", movie["_id"], "recent", update_edited=True, single=True) if len(too_early_to_search) > 0: log.info2("Too early to search for %s, %s", (too_early_to_search, default_title)) if outside_eta_results > 0: message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % ( outside_eta_results, default_title, ) log.info(message) if not manual: fireEvent("media.available", message=message, data={}) fireEvent("notify.frontend", type="movie.searcher.ended", data={"_id": movie["_id"]}) return ret