def post(self, session: Session = None) -> Response: """Perform DB operations""" msg = '' data = request.json operation = data['operation'] if operation == 'cleanup': self.manager.db_cleanup(force=True) msg = 'DB Cleanup finished' elif operation == 'vacuum': session.execute('VACUUM') session.commit() msg = 'DB VACUUM finished' elif operation == 'plugin_reset': plugin_name = data.get('plugin_name') if not plugin_name: raise BadRequest( "'plugin_name' attribute must be used when trying to reset plugin" ) try: reset_schema(plugin_name) msg = f'Plugin {plugin_name} DB reset was successful' except ValueError: raise BadRequest( f'The plugin {plugin_name} has no stored schema to reset') return success_response(msg)
def delete(self, show_id, ep_id, rel_id, session): """ Delete episode release by show ID, episode ID and release ID """ try: series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) try: release = series.release_by_id(rel_id, session) except NoResultFound: raise NotFoundError('release with ID %s not found' % rel_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) if not series.release_in_episode(ep_id, rel_id): raise BadRequest('release id %s does not belong to episode %s' % (rel_id, ep_id)) args = delete_parser.parse_args() if args.get('forget'): fire_event('forget', release.title) series.delete_release_by_id(rel_id) return success_response( 'successfully deleted release %d from episode %d' % (rel_id, ep_id))
def delete(self, show_id, season_id, rel_id, session): """ Delete episode release by show ID, season ID and release ID """ try: db.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: db.season_by_id(season_id, session) except NoResultFound: raise NotFoundError('season with ID %s not found' % season_id) try: release = db.season_release_by_id(rel_id, session) except NoResultFound: raise NotFoundError('release with ID %s not found' % rel_id) if not db.season_in_show(show_id, season_id): raise BadRequest('season with id %s does not belong to show %s' % (season_id, show_id)) if not db.release_in_season(season_id, rel_id): raise BadRequest('release id %s does not belong to season %s' % (rel_id, season_id)) args = delete_parser.parse_args() if args.get('forget'): fire_event('forget', release.title) db.delete_season_release_by_id(rel_id) return success_response( 'successfully deleted release %d from season %d' % (rel_id, season_id))
def put(self, show_id, ep_id, rel_id, session): """ Resets a downloaded release status """ try: series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) try: release = series.release_by_id(rel_id, session) except NoResultFound: raise NotFoundError('release with ID %s not found' % rel_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) if not series.release_in_episode(ep_id, rel_id): raise BadRequest('release id %s does not belong to episode %s' % (rel_id, ep_id)) if not release.downloaded: raise BadRequest('release with id %s is not set as downloaded' % rel_id) release.downloaded = False rsp = jsonify(release.to_dict()) rsp.headers.extend({'Series-ID': show_id, 'Episode-ID': ep_id}) return rsp
def get(self, show_id, ep_id, rel_id, session): """ Get episode release by show ID, episode ID and release ID """ try: series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) try: release = series.release_by_id(rel_id, session) except NoResultFound: raise NotFoundError('release with ID %s not found' % rel_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) if not series.release_in_episode(ep_id, rel_id): raise BadRequest('release id %s does not belong to episode %s' % (rel_id, ep_id)) rsp = jsonify(release.to_dict()) rsp.headers.extend({ 'Series-ID': show_id, 'Episode-ID': ep_id }) return rsp
def post(self, session: Session = None) -> Response: """ Update config """ config = {} data = request.json try: raw_config = base64.b64decode(data['raw_config']) except (TypeError, binascii.Error): raise BadRequest(message='payload was not a valid base64 encoded string') try: config = yaml.safe_load(raw_config) except YAMLError as e: if isinstance(e, MarkedYAMLError): error: Dict[str, int] = {} if e.problem is not None: error.update({'reason': e.problem}) if e.context_mark is not None: error.update({'line': e.context_mark.line, 'column': e.context_mark.column}) if e.problem_mark is not None: error.update({'line': e.problem_mark.line, 'column': e.problem_mark.column}) raise BadRequest(message='Invalid YAML syntax', payload=error) try: backup_path = self.manager.update_config(config) except ConfigError as e: errors = [] for er in e.errors: errors.append({'error': er.message, 'config_path': er.json_pointer}) raise BadRequest( message=f'Error loading config: {e.args[0]}', payload={'errors': errors} ) try: self.manager.backup_config() except Exception as e: raise APIError( message='Failed to create config backup, config updated but NOT written to file', payload={'reason': str(e)}, ) try: with open(self.manager.config_path, 'w', encoding='utf-8') as f: f.write(raw_config.decode('utf-8').replace('\r\n', '\n')) except Exception as e: raise APIError( message='Failed to write new config to file, please load from backup', payload={'reason': str(e), 'backup_path': backup_path}, ) return success_response('Config was loaded and successfully updated to file')
def delete(self, show_id, ep_id, session): """ Deletes all episodes releases by show ID and episode ID """ try: series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: episode = series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) args = release_delete_parser.parse_args() downloaded = args.get('downloaded') is True if args.get( 'downloaded') is not None else None release_items = [] for release in episode.releases: if downloaded and release.downloaded or downloaded is False and not release.downloaded or not downloaded: release_items.append(release) for release in release_items: if args.get('forget'): fire_event('forget', release.title) series.delete_release_by_id(release.id) return success_response( 'successfully deleted all releases for episode %s from show %s' % (ep_id, show_id))
def get(self, session=None): """TheTVDB series search""" args = search_parser.parse_args() language = args['language'] search_name = args.get('search_name') imdb_id = args.get('imdb_id') zap2it_id = args.get('zap2it_id') force_search = args.get('force_search') if not any(arg for arg in [search_name, imdb_id, zap2it_id]): raise BadRequest('Not enough lookup arguments') kwargs = { 'search_name': search_name, 'imdb_id': imdb_id, 'zap2it_id': zap2it_id, 'force_search': force_search, 'session': session, 'language': language, } try: search_results = search_for_series(**kwargs) except LookupError as e: raise NotFoundError(e.args[0]) return jsonify([a.to_dict() for a in search_results])
def delete(self, show_id, season_id, session): """ Deletes all season releases by show ID and season ID """ try: db.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: season = db.season_by_id(season_id, session) except NoResultFound: raise NotFoundError('seasons with ID %s not found' % season_id) if not db.season_in_show(show_id, season_id): raise BadRequest('season with id %s does not belong to show %s' % (season_id, show_id)) args = release_delete_parser.parse_args() downloaded = args.get('downloaded') is True if args.get( 'downloaded') is not None else None release_items = [] for release in season.releases: if (downloaded and release.downloaded or downloaded is False and not release.downloaded or not downloaded): release_items.append(release) for release in release_items: if args.get('forget'): fire_event('forget', release.title) db.delete_season_release_by_id(release.id) return success_response( 'successfully deleted all releases for season %s from show %s' % (season_id, show_id))
def get(self, session=None): """Get channel status enumeration meaning""" try: from irc_bot import simple_irc_bot except ImportError: raise BadRequest('irc_bot dep is not installed') return jsonify(simple_irc_bot.IRCChannelStatus().enum_dict)
def post(self, list_id, session=None): """ Add movies to list by ID """ try: ml.get_list_by_id(list_id=list_id, session=session) except NoResultFound: raise NotFoundError('list_id %d does not exist' % list_id) data = request.json movie_identifiers = data.get('movie_identifiers', []) # Validates ID type based on allowed ID for id_name in movie_identifiers: if list(id_name)[0] not in MovieListBase().supported_ids: raise BadRequest('movie identifier %s is not allowed' % id_name) title, year = data['movie_name'], data.get('movie_year') movie = ml.get_movie_by_title_and_year(list_id=list_id, title=title, year=year, session=session) if movie: raise Conflict('movie with name "%s" already exist in list %d' % (title, list_id)) movie = ml.MovieListMovie() movie.title = title movie.year = year movie.ids = ml.get_db_movie_identifiers( identifier_list=movie_identifiers, session=session) movie.list_id = list_id session.add(movie) session.commit() response = jsonify(movie.to_dict()) response.status_code = 201 return response
def get(self, session=None): """ Get TMDB movie data """ args = tmdb_parser.parse_args() title = args.get('title') tmdb_id = args.get('tmdb_id') imdb_id = args.get('imdb_id') posters = args.pop('include_posters', False) backdrops = args.pop('include_backdrops', False) if not (title or tmdb_id or imdb_id): raise BadRequest(description) lookup = plugin.get('api_tmdb', 'tmdb.api').lookup try: movie = lookup(session=session, **args) except LookupError as e: raise NotFoundError(e.args[0]) return_movie = movie.to_dict() if posters: return_movie['posters'] = [p.to_dict() for p in movie.posters] if backdrops: return_movie['backdrops'] = [p.to_dict() for p in movie.backdrops] return jsonify(return_movie)
def get(self, tvdb_id, session=None): args = episode_parser.parse_args() language = args['language'] absolute_number = args.get('absolute_number') season_number = args.get('season_number') ep_number = args.get('ep_number') air_date = args.get('air_date') if not ((season_number and ep_number) or absolute_number or air_date): raise BadRequest('not enough parameters for lookup. Either season and episode number or absolute number ' 'are required.') kwargs = {'tvdb_id': tvdb_id, 'session': session, 'language': language} if absolute_number: kwargs['absolute_number'] = absolute_number if season_number and ep_number: kwargs['season_number'] = season_number kwargs['episode_number'] = ep_number if air_date: kwargs['first_aired'] = air_date try: episode = lookup_episode(**kwargs) except LookupError as e: raise NotFoundError(e.args[0]) return jsonify(episode.to_dict())
def get(self, session=None): """ List of previously accepted entries """ args = history_parser.parse_args() # Pagination and sorting params page = args['page'] per_page = args['per_page'] sort_by = args['sort_by'] sort_order = args['order'] # Hard limit results per page to 100 if per_page > 100: per_page = 100 # Filter param task = args['task'] # Build query query = session.query(db.History) if task: query = query.filter(db.History.task == task) total_items = query.count() if not total_items: return jsonify([]) total_pages = int(ceil(total_items / float(per_page))) if page > total_pages: raise NotFoundError('page %s does not exist' % page) start = (page - 1) * per_page finish = start + per_page # Choose sorting order order = desc if sort_order == 'desc' else asc # Get items try: items = query.order_by(order(getattr(db.History, sort_by))).slice( start, finish) except AttributeError as e: raise BadRequest(str(e)) # Actual results in page actual_size = min(items.count(), per_page) # Get pagination headers pagination = pagination_headers(total_pages, total_items, actual_size, request) # Create response rsp = jsonify([item.to_dict() for item in items]) # Add link header to response rsp.headers.extend(pagination) return rsp
def get(self, session=None): """ Reset the DB of a specific plugin """ args = plugin_parser.parse_args() plugin = args['plugin_name'] try: reset_schema(plugin) except ValueError: raise BadRequest('The plugin {} has no stored schema to reset'.format(plugin)) return success_response('Plugin {} DB reset was successful'.format(plugin))
def put(self, session=None): """ Change user password """ user = current_user data = request.json try: change_password(username=user.name, password=data.get('password'), session=session) except WeakPassword as e: raise BadRequest(e.value) return success_response('Successfully changed user password')
def get(self, plugin_name, session=None): """ Return plugin data by name""" args = plugin_parser.parse_args() try: plugin = get_plugin_by_name(plugin_name, issued_by='plugins API') except DependencyError as e: raise BadRequest(e.message) p = plugin_to_dict(plugin) if args['include_schema']: p['schema'] = plugin.schema return jsonify(p)
def get(self, session=None): """ Cache remote resources """ args = cached_parser.parse_args() url = args.get('url') force = args.get('force') try: file_path, mime_type = cached_resource(url, self.manager.config_base, force=force) except RequestException as e: raise BadRequest('Request Error: {}'.format(e.args[0])) except OSError as e: raise APIError('Error: {}'.format(str(e))) return send_file(file_path, mimetype=mime_type)
def get(self, session=None): """Returns status of IRC connections""" from flexget.plugins.daemon.irc import irc_manager if irc_manager is None: raise BadRequest('IRC daemon does not appear to be running') args = irc_parser.parse_args() name = args.get('name') try: status = irc_manager.status(name) except ValueError as e: raise NotFoundError(e.args[0]) return jsonify(status)
def get(self, session=None): """Restarts IRC connections""" from .irc import irc_manager if irc_manager is None: raise BadRequest('IRC daemon does not appear to be running') args = irc_parser.parse_args() connection = args.get('name') try: irc_manager.restart_connections(connection) except KeyError: raise NotFoundError('Connection {} is not a valid IRC connection'.format(connection)) return success_response('Successfully restarted connection(s)')
def get(self, show_id, season_id, rel_id, session): """ Get season release by show ID, season ID and release ID """ try: db.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: db.season_by_id(season_id, session) except NoResultFound: raise NotFoundError('season with ID %s not found' % season_id) try: release = db.season_release_by_id(rel_id, session) except NoResultFound: raise NotFoundError('release with ID %s not found' % rel_id) if not db.season_in_show(show_id, season_id): raise BadRequest('season with id %s does not belong to show %s' % (season_id, show_id)) if not db.release_in_season(season_id, rel_id): raise BadRequest('release id %s does not belong to season %s' % (rel_id, season_id)) rsp = jsonify(release.to_dict()) rsp.headers.extend({'Series-ID': show_id, 'Season-ID': season_id}) return rsp
def get(self, session=None): """Stops IRC connections""" from flexget.plugins.daemon.irc import irc_manager if irc_manager is None: raise BadRequest('IRC daemon does not appear to be running') args = irc_stop_parser.parse_args() name = args.get('name') wait = args.get('wait') try: irc_manager.stop_connections(wait=wait, name=name) except KeyError: raise NotFoundError( 'Connection {} is not a valid IRC connection'.format(name)) return success_response('Successfully stopped connection(s)')
def put(self, task, session: Session = None) -> Response: """ Update tasks config """ data = request.json new_task_name = data['name'] if task not in self.manager.user_config.get('tasks', {}): raise NotFoundError(f'task `{task}` not found') if 'tasks' not in self.manager.user_config: self.manager.user_config['tasks'] = {} if 'tasks' not in self.manager.config: self.manager.config['tasks'] = {} if task != new_task_name: # Rename task if new_task_name in self.manager.user_config['tasks']: raise BadRequest('cannot rename task as it already exist') del self.manager.user_config['tasks'][task] del self.manager.config['tasks'][task] # Process the task config task_schema_processed = copy.deepcopy(data) errors = process_config(task_schema_processed, schema=task_return_schema.__schema__, set_defaults=True) if errors: raise APIError( 'problem loading config, raise a BUG as this should not happen!' ) self.manager.user_config['tasks'][new_task_name] = data['config'] self.manager.config['tasks'][new_task_name] = task_schema_processed[ 'config'] self.manager.save_config() self.manager.config_changed() rsp = jsonify({ 'name': new_task_name, 'config': self.manager.user_config['tasks'][new_task_name] }) rsp.status_code = 200 return rsp
def get(self, session=None): """ Get list of registered plugins """ args = plugins_parser.parse_args() # Pagination and sorting params page = args['page'] per_page = args['per_page'] # Handle max size limit if per_page > 100: per_page = 100 start = per_page * (page - 1) stop = start + per_page plugin_list = [] try: for plugin in get_plugins(phase=args['phase'], interface=args['interface']): p = plugin_to_dict(plugin) if args['include_schema']: p['schema'] = plugin.schema plugin_list.append(p) except ValueError as e: raise BadRequest(str(e)) total_items = len(plugin_list) sliced_list = plugin_list[start:stop] # Total number of pages total_pages = int(ceil(total_items / float(per_page))) if page > total_pages and total_pages != 0: raise NotFoundError('page %s does not exist' % page) # Actual results in page actual_size = min(per_page, len(sliced_list)) # Get pagination headers pagination = pagination_headers(total_pages, total_items, actual_size, request) rsp = jsonify(sliced_list) # Add link header to response rsp.headers.extend(pagination) return rsp
def delete(self, show_id, ep_id, session): """ Forgets episode by show ID and episode ID """ try: show = series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: episode = series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) args = delete_parser.parse_args() series.remove_series_entity(show.name, episode.identifier, args.get('forget')) return success_response('successfully removed episode %s from show %s' % (ep_id, show_id))
def put(self, entry_id, session=None): """Approve/Reject the status of a pending entry""" try: entry = db.get_entry_by_id(session, entry_id) except NoResultFound: raise NotFoundError('No pending entry with ID %s' % entry_id) data = request.json approved = data['operation'] == 'approve' operation_text = 'approved' if approved else 'pending' if entry.approved is approved: raise BadRequest('Entry with id {} is already {}'.format(entry_id, operation_text)) entry.approved = approved session.commit() rsp = jsonify(entry.to_dict()) rsp.status_code = 201 return rsp
def get(self, show_id, ep_id, session): """ Get episode by show ID and episode ID""" try: series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: episode = series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) rsp = jsonify(episode.to_dict()) # Add Series-ID header rsp.headers.extend({'Series-ID': show_id}) return rsp
def get(self, show_id, season_id, session): """ Get season by show ID and season ID""" try: db.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: season = db.season_by_id(season_id, session) except NoResultFound: raise NotFoundError('season with ID %s not found' % season_id) if not db.season_in_show(show_id, season_id): raise BadRequest('season with id %s does not belong to show %s' % (season_id, show_id)) rsp = jsonify(season.to_dict()) # Add Series-ID header rsp.headers.extend({'Series-ID': show_id}) return rsp
def put(self, show_id, ep_id, session): """ Marks all downloaded releases as not downloaded """ try: series.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: episode = series.episode_by_id(ep_id, session) except NoResultFound: raise NotFoundError('episode with ID %s not found' % ep_id) if not series.episode_in_show(show_id, ep_id): raise BadRequest('episode with id %s does not belong to show %s' % (ep_id, show_id)) for release in episode.releases: if release.downloaded: release.downloaded = False return success_response( 'successfully reset download status for all releases for episode %s from show %s' % (ep_id, show_id))
def delete(self, show_id, season_id, session): """ Forgets season by show ID and season ID """ try: show = db.show_by_id(show_id, session=session) except NoResultFound: raise NotFoundError('show with ID %s not found' % show_id) try: season = db.season_by_id(season_id, session) except NoResultFound: raise NotFoundError('season with ID %s not found' % season_id) if not db.season_in_show(show_id, season_id): raise BadRequest('season with id %s does not belong to show %s' % (season_id, show_id)) args = delete_parser.parse_args() db.remove_series_entity(show.name, season.identifier, args.get('forget')) return success_response('successfully removed season %s from show %s' % (season_id, show_id))