def delete(self, id = None, **kwargs): try: db = get_db() success = False message = '' try: c = db.get('id', id) db.delete(c) # Force defaults on all empty category movies self.removeFromMovie(id) success = True except: message = log.error('Failed deleting category: %s', traceback.format_exc()) return { 'success': success, 'message': message } except: log.error('Failed: %s', traceback.format_exc()) return { 'success': False }
def forceDefaults(self): db = get_db() # Fill qualities and profiles if they are empty somehow.. if db.count(db.all, 'profile') == 0: if db.count(db.all, 'quality') == 0: fireEvent('quality.fill', single = True) self.fill() # Get all active movies without profile try: medias = fireEvent('media.with_status', 'active', single = True) profile_ids = [x.get('_id') for x in self.all()] default_id = profile_ids[0] for media in medias: if media.get('profile_id') not in profile_ids: media['profile_id'] = default_id db.update(media) except: log.error('Failed: %s', traceback.format_exc())
def treeView(self, media_id, **kwargs): db = get_db() media = db.get('id', media_id) return { 'result': fireEvent('library.tree', media, single = True) }
def notify(self, message = '', data = None, listener = None): if not data: data = {} n = { '_t': 'notification', 'time': int(time.time()), } try: db = get_db() n['message'] = toUnicode(message) if data.get('sticky'): n['sticky'] = True if data.get('important'): n['important'] = True db.insert(n) self.frontend(type = listener, data = n) return True except: log.error('Failed notify "%s": %s', (n, traceback.format_exc()))
def get(self, media_id): try: db = get_db() imdb_id = getImdb(str(media_id)) if imdb_id: media = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc'] else: media = db.get('id', media_id) if media: # Attach category try: media['category'] = db.get('id', media.get('category_id')) except: pass media['releases'] = fireEvent('release.for_media', media['_id'], single = True) return media except (RecordNotFound, RecordDeleted): log.error('Media with id "%s" not found', media_id) except: raise
def delete(self, id = None, **kwargs): try: db = get_db() success = False message = '' try: p = db.get('id', id) db.delete(p) # Force defaults on all empty profile movies self.forceDefaults() success = True except Exception as e: message = log.error('Failed deleting Profile: %s', e) return { 'success': success, 'message': message } except: log.error('Failed: %s', traceback.format_exc()) return { 'success': False }
def updateStatus(self, release_id, status = None): if not status: return False try: db = get_db() rel = db.get('id', release_id) if rel and rel.get('status') != status: release_name = None if rel.get('files'): for file_type in rel.get('files', {}): if file_type == 'movie': for release_file in rel['files'][file_type]: release_name = os.path.basename(release_file) break if not release_name and rel.get('info'): release_name = rel['info'].get('name') #update status in Db log.debug('Marking release %s as %s', (release_name, status)) rel['status'] = status rel['last_edit'] = int(time.time()) db.update(rel) #Update all movie info as there is no release update function fireEvent('notify.frontend', type = 'release.update_status', data = rel) return True except: log.error('Failed: %s', traceback.format_exc()) return False
def withStatus(self, status, types = None, with_doc = True): db = get_db() if types and not isinstance(types, (list, tuple)): types = [types] status = list(status if isinstance(status, (list, tuple)) else [status]) for s in status: for ms in db.get_many('media_status', s): if with_doc: try: doc = db.get('id', ms['_id']) if types and doc.get('type') not in types: continue yield doc except (RecordDeleted, RecordNotFound): log.debug('Record not found, skipping: %s', ms['_id']) except (ValueError, EOFError): fireEvent('database.delete_corrupted', ms.get('_id'), traceback_error = traceback.format_exc(0)) else: yield ms
def manualDownload(self, id = None, **kwargs): db = get_db() try: release = db.get('id', id) item = release['info'] movie = db.get('id', release['media_id']) fireEvent('notify.frontend', type = 'release.manual_download', data = True, message = 'Snatching "%s"' % item['name']) # Get matching provider provider = fireEvent('provider.belongs_to', item['url'], provider = item.get('provider'), single = True) if item.get('protocol') != 'torrent_magnet': item['download'] = provider.loginDownload if provider.urls.get('login') else provider.download success = self.download(data = item, media = movie, manual = True) if success: fireEvent('notify.frontend', type = 'release.manual_download', data = True, message = 'Successfully snatched "%s"' % item['name']) return { 'success': success == True } except: log.error('Couldn\'t find release with id: %s: %s', (id, traceback.format_exc())) return { 'success': False }
def clean(self, release_id): try: db = get_db() rel = db.get('id', release_id) raw_files = rel.get('files') if len(raw_files) == 0: self.delete(rel['_id']) else: files = {} for file_type in raw_files: for release_file in raw_files.get(file_type, []): if os.path.isfile(sp(release_file)): if file_type not in files: files[file_type] = [] files[file_type].append(release_file) rel['files'] = files db.update(rel) return True except: log.error('Failed: %s', traceback.format_exc()) return False
def updateReleaseDate(self, media_id): """ Update release_date (eta) info only @param media_id: document id @return: dict, with dates dvd, theater, bluray, expires """ try: db = get_db() media = db.get('id', media_id) if not media.get('info'): media = self.update(media_id) dates = media.get('info', {}).get('release_date') else: dates = media.get('info').get('release_date') if dates and (dates.get('expires', 0) < time.time() or dates.get('expires', 0) > time.time() + (604800 * 4)) or not dates: dates = fireEvent('movie.info.release_date', identifier = getIdentifier(media), merge = True) media['info'].update({'release_date': dates}) db.update(media) return dates except: log.error('Failed updating release dates: %s', traceback.format_exc()) return {}
def clean(self): try: db = get_db() for n in db.all('notification', with_doc = True): if n['doc'].get('time', 0) <= (int(time.time()) - 2419200): db.delete(n['doc']) except: log.error('Failed cleaning notification: %s', traceback.format_exc())
def root(self, media): db = get_db() cur = media while cur and cur.get('parent_id'): cur = db.get('id', cur['parent_id']) return cur
def update(self, media_id = None, identifier = None, default_title = None, extended = False): """ Update movie information inside media['doc']['info'] @param media_id: document id @param default_title: default title, if empty, use first one or existing one @param extended: update with extended info (parses more info, actors, images from some info providers) @return: dict, with media """ if self.shuttingDown(): return lock_key = 'media.get.%s' % media_id if media_id else identifier self.acquireLock(lock_key) media = {} try: db = get_db() if media_id: media = db.get('id', media_id) else: media = db.get('media', 'imdb-%s' % identifier, with_doc = True)['doc'] info = fireEvent('movie.info', merge = True, extended = extended, identifier = getIdentifier(media)) # Don't need those here try: del info['in_wanted'] except: pass try: del info['in_library'] except: pass if not info or len(info) == 0: log.error('Could not update, no movie info to work with: %s', identifier) return False # Update basic info media['info'] = info titles = info.get('titles', []) log.debug('Adding titles: %s', titles) # Define default title if default_title or media.get('title') == 'UNKNOWN' or len(media.get('title', '')) == 0: media['title'] = self.getDefaultTitle(info, default_title) # Files image_urls = info.get('images', []) self.getPoster(media, image_urls) db.update(media) except: log.error('Failed update media: %s', traceback.format_exc()) self.releaseLock(lock_key) return media
def delete(self, media_id, delete_from = None): try: db = get_db() media = db.get('id', media_id) if media: deleted = False media_releases = fireEvent('release.for_media', media['_id'], single = True) if delete_from == 'all': # Delete connected releases for release in media_releases: db.delete(release) db.delete(media) deleted = True else: total_releases = len(media_releases) total_deleted = 0 new_media_status = None for release in media_releases: if delete_from in ['wanted', 'snatched', 'late']: if release.get('status') != 'done': db.delete(release) total_deleted += 1 new_media_status = 'done' elif delete_from == 'manage': if release.get('status') == 'done' or media.get('status') == 'done': db.delete(release) total_deleted += 1 if (total_releases == total_deleted) or (total_releases == 0 and not new_media_status) or (not new_media_status and delete_from == 'late'): db.delete(media) deleted = True elif new_media_status: media['status'] = new_media_status # Remove profile (no use for in manage) if new_media_status == 'done': media['profile_id'] = None db.update(media) fireEvent('media.untag', media['_id'], 'recent', single = True) else: fireEvent('media.restatus', media.get('_id'), single = True) if deleted: fireEvent('notify.frontend', type = 'media.deleted', data = media) except: log.error('Failed deleting media: %s', traceback.format_exc()) return True
def fill(self): try: db = get_db() profiles = [{ 'label': 'Best', 'qualities': ['720p', '1080p', 'brrip', 'dvdrip'] }, { 'label': 'HD', 'qualities': ['720p', '1080p'] }, { 'label': 'SD', 'qualities': ['dvdrip', 'dvdr'] }, { 'label': 'Prefer 3D HD', 'qualities': ['1080p', '720p', '720p', '1080p'], '3d': [True, True] }, { 'label': '3D HD', 'qualities': ['1080p', '720p'], '3d': [True, True] }] # Create default quality profile order = 0 for profile in profiles: log.info('Creating default profile: %s', profile.get('label')) pro = { '_t': 'profile', 'label': toUnicode(profile.get('label')), 'order': order, 'qualities': profile.get('qualities'), 'minimum_score': 1, 'finish': [], 'wait_for': [], 'stop_after': [], '3d': [] } threed = profile.get('3d', []) for q in profile.get('qualities'): pro['finish'].append(True) pro['wait_for'].append(0) pro['stop_after'].append(0) pro['3d'].append(threed.pop() if threed else False) db.insert(pro) order += 1 return True except: log.error('Failed: %s', traceback.format_exc()) return False
def single(self, identifier = ''): db = get_db() quality_dict = {} quality = db.get('quality', identifier, with_doc = True)['doc'] if quality: quality_dict = mergeDicts(self.getQuality(quality['identifier']), quality) return quality_dict
def cleanupFaults(self): medias = fireEvent('media.with_status', 'ignored', single = True) or [] db = get_db() for media in medias: try: media['status'] = 'done' db.update(media) except: pass
def withIdentifiers(self, identifiers, with_doc = False): db = get_db() for x in identifiers: try: return db.get('media', '%s-%s' % (x, identifiers[x]), with_doc = with_doc) except: pass log.debug('No media found with identifiers: %s', identifiers) return False
def automationView(self, force_update = False, **kwargs): db = get_db() charts = fireEvent('automation.get_chart_list', merge = True) ignored = splitString(Env.prop('charts_ignore', default = '')) # Create a list the movie/list.js can use for chart in charts: medias = [] for media in chart.get('list', []): identifier = media.get('imdb') if identifier in ignored: continue try: try: in_library = db.get('media', 'imdb-%s' % identifier) if in_library: continue except RecordNotFound: pass except: pass # Cache poster posters = media.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': 'chart', 'title': getTitle(media), 'type': 'movie', 'info': media, 'files': files, 'identifiers': { 'imdb': identifier } }) chart['list'] = medias return { 'success': True, 'count': len(charts), 'charts': charts, 'ignored': ignored, }
def getProperty(self, identifier): from whatpotato import get_db db = get_db() prop = None try: propert = db.get('property', identifier, with_doc = True) prop = propert['doc']['value'] except: pass # self.log.debug('Property "%s" doesn\'t exist: %s', (identifier, traceback.format_exc(0))) return prop
def removeFromMovie(self, category_id): try: db = get_db() movies = [x['doc'] for x in db.get_many('category_media', category_id, with_doc = True)] if len(movies) > 0: for movie in movies: movie['category_id'] = None db.update(movie) except: log.error('Failed: %s', traceback.format_exc())
def createRefreshHandler(self, media_id): try: media = get_db().get('id', media_id) event = '%s.update' % media.get('type') def handler(): fireEvent(event, media_id = media_id, on_complete = self.createOnComplete(media_id)) return handler except: log.error('Refresh handler for non existing media: %s', traceback.format_exc())
def delete(self, release_id): try: db = get_db() rel = db.get('id', release_id) db.delete(rel) return True except RecordDeleted: log.debug('Already deleted: %s', release_id) return True except: log.error('Failed: %s', traceback.format_exc()) return False
def availableChars(self, types = None, status = None, release_status = None): db = get_db() # Make a list from string if status and not isinstance(status, (list, tuple)): status = [status] if release_status and not isinstance(release_status, (list, tuple)): release_status = [release_status] if types and not isinstance(types, (list, tuple)): types = [types] # query media ids if types: all_media_ids = set() for media_type in types: all_media_ids = all_media_ids.union(set([x['_id'] for x in db.get_many('media_by_type', media_type)])) else: all_media_ids = set([x['_id'] for x in db.all('media')]) media_ids = all_media_ids filter_by = {} # Filter on movie status if status and len(status) > 0: filter_by['media_status'] = set() for media_status in fireEvent('media.with_status', status, with_doc = False, single = True): filter_by['media_status'].add(media_status.get('_id')) # Filter on release status if release_status and len(release_status) > 0: filter_by['release_status'] = set() for release_status in fireEvent('release.with_status', release_status, with_doc = False, single = True): filter_by['release_status'].add(release_status.get('media_id')) # Filter by combining ids for x in filter_by: media_ids = [n for n in media_ids if n in filter_by[x]] chars = set() for x in db.all('media_startswith'): if x['_id'] in media_ids: chars.add(x['key']) if len(chars) == 27: break return list(chars)
def save(self, **kwargs): try: db = get_db() profile = { '_t': 'profile', 'label': toUnicode(kwargs.get('label')), 'order': tryInt(kwargs.get('order', 999)), 'core': kwargs.get('core', False), 'minimum_score': tryInt(kwargs.get('minimum_score', 1)), 'qualities': [], 'wait_for': [], 'stop_after': [], 'finish': [], '3d': [] } # Update types order = 0 for type in kwargs.get('types', []): profile['qualities'].append(type.get('quality')) profile['wait_for'].append(tryInt(kwargs.get('wait_for', 0))) profile['stop_after'].append(tryInt(kwargs.get('stop_after', 0))) profile['finish'].append((tryInt(type.get('finish')) == 1) if order > 0 else True) profile['3d'].append(tryInt(type.get('3d'))) order += 1 id = kwargs.get('id') try: p = db.get('id', id) profile['order'] = tryInt(kwargs.get('order', p.get('order', 999))) except: p = db.insert(profile) p.update(profile) db.update(p) return { 'success': True, 'profile': p } except: log.error('Failed: %s', traceback.format_exc()) return { 'success': False }
def withStatus(self, status, with_doc = True): db = get_db() status = list(status if isinstance(status, (list, tuple)) else [status]) for s in status: for ms in db.get_many('release_status', s): if with_doc: try: doc = db.get('id', ms['_id']) yield doc except RecordNotFound: log.debug('Record not found, skipping: %s', ms['_id']) else: yield ms
def edit(self, id = '', **kwargs): try: db = get_db() ids = splitString(id) for media_id in ids: try: m = db.get('id', media_id) m['profile_id'] = kwargs.get('profile_id') or m['profile_id'] cat_id = kwargs.get('category_id') if cat_id is not None: m['category_id'] = cat_id if len(cat_id) > 0 else m['category_id'] # Remove releases for rel in fireEvent('release.for_media', m['_id'], single = True): if rel['status'] is 'available': db.delete(rel) # Default title if kwargs.get('default_title'): m['title'] = kwargs.get('default_title') db.update(m) fireEvent('media.restatus', m['_id'], single = True) m = db.get('id', media_id) movie_dict = fireEvent('media.get', m['_id'], single = True) fireEventAsync('movie.searcher.single', movie_dict, on_complete = self.createNotifyFront(media_id)) except: print traceback.format_exc() log.error('Can\'t edit non-existing media') return { 'success': True, } except: log.error('Failed editing media: %s', traceback.format_exc()) return { 'success': False, }
def all(self): if self.cached_qualities: return self.cached_qualities db = get_db() temp = [] for quality in self.qualities: quality_doc = db.get('quality', quality.get('identifier'), with_doc = True)['doc'] q = mergeDicts(quality, quality_doc) temp.append(q) if len(temp) == len(self.qualities): self.cached_qualities = temp return temp
def restatus(self, media_id, tag_recent = True, allowed_restatus = None): try: db = get_db() m = db.get('id', media_id) previous_status = m['status'] log.debug('Changing status for %s', getTitle(m)) if not m['profile_id']: m['status'] = 'done' else: m['status'] = 'active' try: profile = db.get('id', m['profile_id']) media_releases = fireEvent('release.for_media', m['_id'], single = True) done_releases = [release for release in media_releases if release.get('status') == 'done'] if done_releases: # Check if we are finished with the media for release in done_releases: if fireEvent('quality.isfinish', {'identifier': release['quality'], 'is_3d': release.get('is_3d', False)}, profile, timedelta(seconds = time.time() - release['last_edit']).days, single = True): m['status'] = 'done' break elif previous_status == 'done': m['status'] = 'done' except RecordNotFound: log.debug('Failed restatus, keeping previous: %s', traceback.format_exc()) m['status'] = previous_status # Only update when status has changed if previous_status != m['status'] and (not allowed_restatus or m['status'] in allowed_restatus): db.update(m) # Tag media as recent if tag_recent: self.tag(media_id, 'recent', update_edited = True) return m['status'] except: log.error('Failed restatus: %s', traceback.format_exc())