def internalapi_addnzb(args): logger.debug("Add NZB request with args %s" % args) searchResultIds = json.loads(args["searchresultids"]) try: downloader = getDownloaderInstanceByName(args["downloader"]) except DownloaderNotFoundException as e: logger.error(e.message) return jsonify({"success": False}) added = 0 for searchResultId in searchResultIds: try: searchResult = SearchResult.get(SearchResult.id == searchResultId) except SearchResult.DoesNotExist: logger.error("Unable to find search result with ID %d in database" % searchResultId) continue link = get_nzb_link_and_guid(searchResultId, True, downloader=downloader.setting.name) if downloader.setting.nzbAddingType == config.NzbAddingTypeSelection.link: # We send a link to the downloader. The link is either to us (where it gets answered or redirected, thet later getnzb will be called) or directly to the indexer add_success = downloader.add_link(link, searchResult.title, args["category"]) else: # We download an NZB send it to the downloader nzbdownloadresult = download_nzb_and_log(searchResultId) if nzbdownloadresult is not None: add_success = downloader.add_nzb(nzbdownloadresult.content, SearchResult.get(SearchResult.id == searchResultId).title, args["category"]) else: add_success = False if add_success: added += 1 if added: return jsonify({"success": True, "added": added, "of": len(searchResultIds)}) else: return jsonify({"success": False})
def api(args): logger.debug(request.url) logger.debug("API request: %s" % args) # Map newznab api parameters to internal args["category"] = args["cat"] args["episode"] = args["ep"] if args["q"] is not None and args["q"] != "": args["query"] = args["q"] # Because internally we work with "query" instead of "q" if config.settings.main.apikey and ("apikey" not in args or args["apikey"] != config.settings.main.apikey): logger.error("Tried API access with invalid or missing API key") raise Unauthorized("API key not provided or invalid") elif args["t"] in ("search", "tvsearch", "movie", "book"): return api_search(args) elif args["t"] == "get": searchResultId = int(args["id"][len("nzbhydrasearchresult"):]) searchResult = SearchResult.get(SearchResult.id == searchResultId) if config.settings.main.logging.logIpAddresses: logger.info("API request from %s to download %s from %s" % (getIp(), searchResult.title, searchResult.indexer.name)) else: logger.info("API request to download %s from %s" % (searchResult.title, searchResult.indexer.name)) return extract_nzb_infos_and_return_response(searchResultId) elif args["t"] == "caps": xml = render_template("caps.html") return Response(xml, mimetype="text/xml") elif args["t"] == "details": searchResultId = int(args["id"][len("nzbhydrasearchresult"):]) searchResult = SearchResult.get(SearchResult.id == searchResultId) logger.info("API request from to get detils for %s from %s" % (searchResult.title, searchResult.indexer.name)) item = get_entry_by_id(searchResult.indexer.name, searchResult.guid, searchResult.title) if item is None: logger.error("Unable to find or parse details for %s" % searchResult.title) return "Unable to get details", 500 item.link = get_nzb_link_and_guid(searchResultId, False)[0] # We need to make sure the link in the details refers to us return render_search_results_for_api([item], None, None, output=args["o"]) elif args["t"] == "getnfo": searchResultId = int(args["id"][len("nzbhydrasearchresult"):]) result = get_nfo(searchResultId) if result["has_nfo"]: if args["raw"] == 1: return result["nfo"] else: # TODO Return as json if requested return render_template("nfo.html", nfo=result["nfo"]) else: return Response('<error code="300" description="No such item"/>', mimetype="text/xml") else: logger.error("Unknown API request. Supported functions: search, tvsearch, movie, get, caps, details, getnfo") return "Unknown API request. Supported functions: search, tvsearch, movie, get, caps, details, getnfo", 500
def download_nzb_and_log(searchResultId): link, papiaccess, _ = get_indexer_nzb_link(searchResultId, "serve", True) indexerName = None try: indexerName = SearchResult.get(SearchResult.id == searchResultId).indexer.name indexer = indexers.getIndexerByName(indexerName) r = indexer.get(link, timeout=10) r.raise_for_status() papiaccess.response_successful = True papiaccess.response_time = r.elapsed.microseconds / 1000 return IndexerNzbDownloadResult(content=r.content, headers=r.headers) except IndexerNotFoundException: if indexerName: logger.error("Unable to find indexer with name %s" % indexerName) else: logger.error("Unable to find indexer for search result id %s" % searchResultId) return None except SearchResult.DoesNotExist: logger.error("Unable to find search result with ID %s" % searchResultId) return None except RequestException as e: logger.error("Error while connecting to URL %s: %s" % (link, str(e))) papiaccess.error = str(e) return None finally: papiaccess.save()
def download_nzb_and_log(searchResultId): link, papiaccess, _ = get_indexer_nzb_link(searchResultId, "serve", True) indexerName = None try: indexerName = SearchResult.get( SearchResult.id == searchResultId).indexer.name indexer = indexers.getIndexerByName(indexerName) r = indexer.get(link, timeout=10) r.raise_for_status() papiaccess.response_successful = True papiaccess.response_time = r.elapsed.microseconds / 1000 return IndexerNzbDownloadResult(content=r.content, headers=r.headers) except IndexerNotFoundException: if indexerName: logger.error("Unable to find indexer with name %s" % indexerName) else: logger.error("Unable to find indexer for search result id %s" % searchResultId) return None except SearchResult.DoesNotExist: logger.error("Unable to find search result with ID %s" % searchResultId) return None except RequestException as e: logger.error("Error while connecting to URL %s: %s" % (link, str(e))) papiaccess.error = str(e) return None finally: papiaccess.save()
def get_indexer_nzb_link(searchResultId, mode, log_api_access, internal=False): """ Build a link that leads to the actual NZB of the indexer using the given informations. We log this as indexer API access and NZB download because this is only called when the NZB will be actually downloaded later (by us or a downloader) :return: str """ searchResult = SearchResult.get(SearchResult.id == searchResultId) indexerName = searchResult.indexer.name indexer = indexers.getIndexerByName(indexerName) link = searchResult.link # Log to database papiaccess = IndexerApiAccess( indexer=indexer.indexer, type="nzb", url=link, response_successful=None) if log_api_access else None try: papiaccess.username = request.authorization.username if request.authorization is not None else None except RuntimeError: pass papiaccess.save() pnzbdl = IndexerNzbDownload(searchResult=searchResult, apiAccess=papiaccess, mode=mode, title=searchResult.title, internal=internal) pnzbdl.save() return link, papiaccess, pnzbdl
def get_details(guid): searchResultId = int(guid[len("nzbhydrasearchresult"):]) searchResult = SearchResult.get(SearchResult.id == searchResultId) details_link = get_details_link(searchResult.indexer.name, searchResult.guid) if details_link: return redirect(details_link) return "Unable to find details", 500
def get_nzb_response(searchResultId): try: searchResult = SearchResult.get(SearchResult.id == searchResultId) except SearchResult.DoesNotExist: logger.error("Unable to find search result with ID %s" % searchResultId) return "Unable to find search result with ID %s" % searchResultId, 500 nzbdownloadresult = download_nzb_and_log(searchResultId) if nzbdownloadresult is not None: bio = BytesIO(nzbdownloadresult.content) filename = searchResult.title + ".nzb" if searchResult.title is not None else "nzbhydra.nzb" response = send_file(bio, mimetype='application/x-nzb;', as_attachment=True, attachment_filename=filename, add_etags=False) response.headers["content-length"] = len(nzbdownloadresult.content) for header in nzbdownloadresult.headers.keys(): if header.lower().startswith("x-dnzb") or header.lower() in ( "content-disposition", "content-type"): response.headers[header] = nzbdownloadresult.headers[header] logger.info("Returning downloaded NZB %s from %s" % (searchResult.title, searchResult.indexer.name)) return response else: logger.error("Error while trying to download NZB %s from %s" % (searchResult.title, searchResult.indexer.name)) return "Unable to download NZB", 500
def getnzb(args): logger.debug("Get NZB request with args %s" % args) searchResult = SearchResult.get(SearchResult.id == args["searchresultid"]) if config.settings.main.logging.logIpAddresses: logger.info("API request from %s to download %s from %s" % (getIp(), searchResult.title, searchResult.indexer.name)) else: logger.info("API request to download %s from %s" % (searchResult.title, searchResult.indexer.name)) return extract_nzb_infos_and_return_response(args["searchresultid"], args["downloader"])
def get_nfo(searchresultid): try: searchResult = SearchResult.get(SearchResult.id == searchresultid) indexer = indexers.getIndexerByName(searchResult.indexer.name) has_nfo, nfo, message = indexer.get_nfo(searchResult.guid) return {"has_nfo": has_nfo, "nfo": nfo, "message": message} except IndexerNotFoundException as e: logger.error(e.message) return {"has_nfo": False, "error": "Unable to find indexer"}
def getNzbById(searchResultId): # type: (int) -> (IndexerNzbDownloadResult, SearchResult) """ :rtype: (IndexerNzbDownloadResult, SearchResult) """ try: searchResult = SearchResult.get(SearchResult.id == searchResultId) except SearchResult.DoesNotExist: logger.error("Unable to find search result with ID %s" % searchResultId) raise NzbDownloadException("Unable to find search result with ID %s" % searchResultId) nzbdownloadresult = download_nzb_and_log(searchResultId) if nzbdownloadresult is None: logger.error("Error while trying to download NZB %s from %s" % (searchResult.title, searchResult.indexer.name)) raise NzbDownloadException("Unable to download NZB") return nzbdownloadresult, searchResult
def get_nzb_response(searchResultId): searchResult = SearchResult.get(SearchResult.id == searchResultId) nzbdownloadresult = download_nzb_and_log(searchResultId) if nzbdownloadresult is not None: bio = BytesIO(nzbdownloadresult.content) filename = searchResult.title + ".nzb" if searchResult.title is not None else "nzbhydra.nzb" response = send_file(bio, mimetype='application/x-nzb;', as_attachment=True, attachment_filename=filename, add_etags=False) response.headers["content-length"] = len(nzbdownloadresult.content) for header in nzbdownloadresult.headers.keys(): if header.lower().startswith("x-dnzb") or header.lower() in ("content-disposition", "content-type"): response.headers[header] = nzbdownloadresult.headers[header] logger.info("Returning downloaded NZB %s from %s" % (searchResult.title, searchResult.indexer.name)) return response else: logger.error("Error while trying to download NZB %s from %s" % (searchResult.title, searchResult.indexer.name)) return "Unable to download NZB", 500
def get_indexer_nzb_link(searchResultId, mode, log_api_access, internal=False): """ Build a link that leads to the actual NZB of the indexer using the given informations. We log this as indexer API access and NZB download because this is only called when the NZB will be actually downloaded later (by us or a downloader) :return: str """ searchResult = SearchResult.get(SearchResult.id == searchResultId) indexerName = searchResult.indexer.name indexer = indexers.getIndexerByName(indexerName) link = searchResult.link # Log to database papiaccess = IndexerApiAccess(indexer=indexer.indexer, type="nzb", url=link, response_successful=None) if log_api_access else None try: papiaccess.username = request.authorization.username if request.authorization is not None else None except RuntimeError: pass papiaccess.save() pnzbdl = IndexerNzbDownload(searchResult=searchResult, apiAccess=papiaccess, mode=mode, title=searchResult.title, internal=internal) pnzbdl.save() return link, papiaccess, pnzbdl