2020-03-03 16:28:26 +00:00
using System ;
2020-02-09 18:08:34 +00:00
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using System.Net ;
using System.Text ;
using System.Threading.Tasks ;
using System.Xml.Linq ;
using Jackett.Common ;
2018-05-01 12:03:16 +00:00
using Jackett.Common.Indexers ;
using Jackett.Common.Indexers.Meta ;
using Jackett.Common.Models ;
using Jackett.Common.Models.DTO ;
using Jackett.Common.Services.Interfaces ;
using Jackett.Common.Utils ;
2018-05-12 02:44:47 +00:00
using Microsoft.AspNetCore.Authorization ;
2018-05-01 12:55:09 +00:00
using Microsoft.AspNetCore.Mvc ;
using Microsoft.AspNetCore.Mvc.Filters ;
2018-06-10 11:56:45 +00:00
using Microsoft.AspNetCore.Routing ;
2018-05-01 12:03:16 +00:00
using NLog ;
2018-05-01 12:55:09 +00:00
namespace Jackett.Server.Controllers
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
public class RequiresApiKey : IActionFilter
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
public IServerService serverService ;
2020-02-25 16:08:03 +00:00
public RequiresApiKey ( IServerService ss ) = > serverService = ss ;
2018-05-01 12:55:09 +00:00
public void OnActionExecuting ( ActionExecutingContext context )
{
var validApiKey = serverService . GetApiKey ( ) ;
var queryParams = context . HttpContext . Request . Query ;
2018-05-01 12:03:16 +00:00
var queryApiKey = queryParams . Where ( x = > x . Key = = "apikey" | | x . Key = = "passkey" ) . Select ( x = > x . Value ) . FirstOrDefault ( ) ;
#if DEBUG
if ( Debugger . IsAttached )
2018-05-01 12:55:09 +00:00
{
2018-05-01 12:03:16 +00:00
return ;
2018-05-01 12:55:09 +00:00
}
2018-05-01 12:03:16 +00:00
#endif
if ( queryApiKey ! = validApiKey )
2018-05-01 12:55:09 +00:00
{
2018-06-10 11:56:45 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . Unauthorized , 100 , "Invalid API Key" ) ;
2018-05-01 12:55:09 +00:00
}
}
public void OnActionExecuted ( ActionExecutedContext context )
{
// do something after the action executes
2018-05-01 12:03:16 +00:00
}
}
2018-05-01 12:55:09 +00:00
public class RequiresConfiguredIndexer : IActionFilter
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
public void OnActionExecuting ( ActionExecutingContext context )
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
var controller = context . Controller ;
2018-05-01 12:03:16 +00:00
if ( ! ( controller is IIndexerController ) )
return ;
var indexerController = controller as IIndexerController ;
2018-05-01 12:55:09 +00:00
var parameters = context . RouteData . Values ;
2018-05-01 12:03:16 +00:00
if ( ! parameters . ContainsKey ( "indexerId" ) )
{
indexerController . CurrentIndexer = null ;
2018-06-10 11:56:45 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . NotFound , 200 , "Indexer is not specified" ) ;
2018-05-01 12:03:16 +00:00
return ;
}
var indexerId = parameters [ "indexerId" ] as string ;
2020-03-25 02:39:38 +00:00
if ( string . IsNullOrWhiteSpace ( indexerId ) )
2018-05-01 12:03:16 +00:00
{
indexerController . CurrentIndexer = null ;
2018-06-10 11:56:45 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . NotFound , 201 , "Indexer is not specified (empty value)" ) ;
2018-05-01 12:03:16 +00:00
return ;
}
var indexerService = indexerController . IndexerService ;
var indexer = indexerService . GetIndexer ( indexerId ) ;
if ( indexer = = null )
{
indexerController . CurrentIndexer = null ;
2018-06-10 11:56:45 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . NotFound , 201 , "Indexer is not supported" ) ;
2018-05-01 12:03:16 +00:00
return ;
}
if ( ! indexer . IsConfigured )
{
indexerController . CurrentIndexer = null ;
2018-06-10 11:56:45 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . NotFound , 201 , "Indexer is not configured" ) ;
2018-05-01 12:03:16 +00:00
return ;
}
indexerController . CurrentIndexer = indexer ;
}
2018-05-01 12:55:09 +00:00
public void OnActionExecuted ( ActionExecutedContext context )
{
// do something after the action executes
}
2018-05-01 12:03:16 +00:00
}
2018-05-01 12:55:09 +00:00
public class RequiresValidQuery : IActionFilter
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
public void OnActionExecuting ( ActionExecutingContext context )
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
//TODO: Not sure what this is meant to do
//if (context.HttpContext.Response != null)
// return;
2018-05-01 12:03:16 +00:00
2018-05-01 12:55:09 +00:00
var controller = context . Controller ;
2018-05-01 12:03:16 +00:00
if ( ! ( controller is IResultController ) )
2018-05-01 12:55:09 +00:00
{
2018-05-01 12:03:16 +00:00
return ;
2018-05-01 12:55:09 +00:00
}
2018-05-01 12:03:16 +00:00
var resultController = controller as IResultController ;
2018-05-01 12:55:09 +00:00
var query = context . ActionArguments . First ( ) . Value ;
2018-05-01 12:03:16 +00:00
var queryType = query . GetType ( ) ;
var converter = queryType . GetMethod ( "ToTorznabQuery" , System . Reflection . BindingFlags . Static | System . Reflection . BindingFlags . Public ) ;
if ( converter = = null )
2018-05-01 12:55:09 +00:00
{
2018-06-10 11:56:45 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . BadRequest , 900 , "ToTorznabQuery() not found" ) ;
2018-05-01 12:55:09 +00:00
}
2018-05-01 12:03:16 +00:00
var converted = converter . Invoke ( null , new object [ ] { query } ) ;
var torznabQuery = converted as TorznabQuery ;
resultController . CurrentQuery = torznabQuery ;
if ( queryType = = typeof ( ApiSearch ) ) // Skip CanHandleQuery() check for manual search (CurrentIndexer isn't used during manul search)
2018-05-01 12:55:09 +00:00
{
2018-05-01 12:03:16 +00:00
return ;
2018-05-01 12:55:09 +00:00
}
2018-05-01 12:03:16 +00:00
if ( ! resultController . CurrentIndexer . CanHandleQuery ( resultController . CurrentQuery ) )
2018-05-01 12:55:09 +00:00
{
2021-05-16 18:13:54 +00:00
context . Result = ResultsController . GetErrorActionResult ( context . RouteData , HttpStatusCode . BadRequest , 201 ,
2020-11-08 22:27:54 +00:00
$"{resultController.CurrentIndexer.Id} does not support the requested query. " +
"Please check the capabilities (t=caps) and make sure the search mode and parameters are supported." ) ;
2018-06-10 11:56:45 +00:00
2018-05-01 12:55:09 +00:00
}
2018-05-01 12:03:16 +00:00
}
2018-05-01 12:55:09 +00:00
public void OnActionExecuted ( ActionExecutedContext context )
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
// do something after the action executes
2018-05-01 12:03:16 +00:00
}
}
public interface IResultController : IIndexerController
{
TorznabQuery CurrentQuery { get ; set ; }
}
2018-05-12 02:44:47 +00:00
[AllowAnonymous]
2018-05-01 12:55:09 +00:00
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
[Route("api/v2.0/indexers/{indexerId}/results")]
[TypeFilter(typeof(RequiresApiKey))]
[TypeFilter(typeof(RequiresConfiguredIndexer))]
[TypeFilter(typeof(RequiresValidQuery))]
public class ResultsController : Controller , IResultController
2018-05-01 12:03:16 +00:00
{
public IIndexerManagerService IndexerService { get ; private set ; }
public IIndexer CurrentIndexer { get ; set ; }
public TorznabQuery CurrentQuery { get ; set ; }
2020-02-10 22:16:19 +00:00
private readonly Logger logger ;
private readonly IServerService serverService ;
private readonly ICacheService cacheService ;
private readonly Common . Models . Config . ServerConfig serverConfig ;
2018-05-01 12:03:16 +00:00
2018-06-18 11:54:39 +00:00
public ResultsController ( IIndexerManagerService indexerManagerService , IServerService ss , ICacheService c , Logger logger , Common . Models . Config . ServerConfig sConfig )
2018-05-01 12:03:16 +00:00
{
IndexerService = indexerManagerService ;
serverService = ss ;
cacheService = c ;
this . logger = logger ;
2018-06-18 11:54:39 +00:00
serverConfig = sConfig ;
2018-05-01 12:03:16 +00:00
}
2018-05-01 12:55:09 +00:00
[Route("")]
2018-05-01 12:03:16 +00:00
[HttpGet]
2018-05-01 12:55:09 +00:00
public async Task < IActionResult > Results ( [ FromQuery ] ApiSearch requestt )
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
//TODO: Better way to parse querystring
2020-02-10 22:16:19 +00:00
var request = new ApiSearch ( ) ;
2018-05-01 12:55:09 +00:00
foreach ( var t in Request . Query )
{
if ( t . Key = = "Tracker[]" )
{
2018-05-20 11:51:52 +00:00
request . Tracker = t . Value . ToString ( ) . Split ( ',' ) ;
2018-05-01 12:55:09 +00:00
}
if ( t . Key = = "Category[]" )
{
2020-02-10 22:16:19 +00:00
request . Category = t . Value . ToString ( ) . Split ( ',' ) . Select ( int . Parse ) . ToArray ( ) ;
2018-07-30 15:39:28 +00:00
CurrentQuery . Categories = request . Category ;
2018-05-01 12:55:09 +00:00
}
if ( t . Key = = "query" )
{
request . Query = t . Value . ToString ( ) ;
}
}
2018-05-01 12:03:16 +00:00
var manualResult = new ManualSearchResult ( ) ;
2021-03-15 06:07:47 +00:00
var trackers = CurrentIndexer is BaseMetaIndexer
2021-05-08 20:24:18 +00:00
? ( CurrentIndexer as BaseMetaIndexer ) . ValidIndexers
2021-03-15 06:07:47 +00:00
: ( new [ ] { CurrentIndexer } ) ;
// Filter current trackers list on Tracker query parameter if available
2018-05-01 12:03:16 +00:00
if ( request . Tracker ! = null )
2020-05-11 19:59:28 +00:00
trackers = trackers . Where ( t = > request . Tracker . Contains ( t . Id ) ) ;
2018-05-01 12:03:16 +00:00
trackers = trackers . Where ( t = > t . CanHandleQuery ( CurrentQuery ) ) ;
2020-11-08 22:27:54 +00:00
var isMetaIndexer = request . Tracker = = null | | request . Tracker . Length > 1 ;
var tasks = trackers . ToList ( ) . Select ( t = > t . ResultsForQuery ( CurrentQuery , isMetaIndexer ) ) . ToList ( ) ;
2018-05-01 12:03:16 +00:00
try
{
var aggregateTask = Task . WhenAll ( tasks ) ;
await aggregateTask ;
}
2020-09-24 20:02:45 +00:00
catch ( AggregateException e )
2018-05-01 12:03:16 +00:00
{
2020-09-24 20:02:45 +00:00
foreach ( var ex in e . InnerExceptions )
2018-05-01 12:03:16 +00:00
{
logger . Error ( ex ) ;
}
}
2020-09-24 20:02:45 +00:00
catch ( Exception e )
2018-05-01 12:03:16 +00:00
{
2020-09-24 20:02:45 +00:00
logger . Error ( e ) ;
2018-05-01 12:03:16 +00:00
}
manualResult . Indexers = tasks . Select ( t = >
{
var resultIndexer = new ManualSearchResultIndexer ( ) ;
IIndexer indexer = null ;
if ( t . Status = = TaskStatus . RanToCompletion )
{
resultIndexer . Status = ManualSearchResultIndexerStatus . OK ;
resultIndexer . Results = t . Result . Releases . Count ( ) ;
resultIndexer . Error = null ;
indexer = t . Result . Indexer ;
}
else if ( t . Exception . InnerException is IndexerException )
{
resultIndexer . Status = ManualSearchResultIndexerStatus . Error ;
resultIndexer . Results = 0 ;
resultIndexer . Error = ( ( IndexerException ) t . Exception . InnerException ) . ToString ( ) ;
indexer = ( ( IndexerException ) t . Exception . InnerException ) . Indexer ;
}
else
{
resultIndexer . Status = ManualSearchResultIndexerStatus . Unknown ;
resultIndexer . Results = 0 ;
resultIndexer . Error = null ;
}
if ( indexer ! = null )
{
2020-05-11 19:59:28 +00:00
resultIndexer . ID = indexer . Id ;
2018-05-01 12:03:16 +00:00
resultIndexer . Name = indexer . DisplayName ;
}
return resultIndexer ;
} ) . ToList ( ) ;
2018-06-10 11:56:45 +00:00
manualResult . Results = tasks . Where ( t = > t . Status = = TaskStatus . RanToCompletion ) . Where ( t = > t . Result . Releases . Any ( ) ) . SelectMany ( t = >
2018-05-01 12:03:16 +00:00
{
var searchResults = t . Result . Releases ;
var indexer = t . Result . Indexer ;
return searchResults . Select ( result = >
{
var item = AutoMapper . Mapper . Map < TrackerCacheResult > ( result ) ;
item . Tracker = indexer . DisplayName ;
2020-05-11 19:59:28 +00:00
item . TrackerId = indexer . Id ;
2018-05-01 12:03:16 +00:00
item . Peers = item . Peers - item . Seeders ; // Use peers as leechers
return item ;
} ) ;
} ) . OrderByDescending ( d = > d . PublishDate ) . ToList ( ) ;
ConfigureCacheResults ( manualResult . Results ) ;
2020-12-11 22:14:21 +00:00
// Log info
var indexersName = string . Join ( ", " , manualResult . Indexers . Select ( i = > i . ID ) ) ;
var cacheStr = tasks . Where ( t = > t . Status = = TaskStatus . RanToCompletion ) . Any ( t = > t . Result . IsFromCache ) ? " (from cache)" : "" ;
if ( string . IsNullOrWhiteSpace ( CurrentQuery . SanitizedSearchTerm ) )
logger . Info ( $"Manual search in {indexersName} => Found {manualResult.Results.Count()} releases{cacheStr}" ) ;
else
logger . Info ( $"Manual search in {indexersName} for {CurrentQuery.GetQueryString()} => Found {manualResult.Results.Count()} releases{cacheStr}" ) ;
2018-05-01 12:55:09 +00:00
return Json ( manualResult ) ;
2018-05-01 12:03:16 +00:00
}
2018-05-01 12:55:09 +00:00
[Route("[action] / { ignored ? } ")]
2018-05-01 12:03:16 +00:00
[HttpGet]
2021-05-16 18:13:54 +00:00
public async Task < IActionResult > Torznab ( [ FromQuery ] TorznabRequest request )
2018-05-01 12:03:16 +00:00
{
if ( string . Equals ( CurrentQuery . QueryType , "caps" , StringComparison . InvariantCultureIgnoreCase ) )
{
2018-05-01 12:55:09 +00:00
return Content ( CurrentIndexer . TorznabCaps . ToXml ( ) , "application/rss+xml" , Encoding . UTF8 ) ;
2018-05-01 12:03:16 +00:00
}
// indexers - returns a list of all included indexers (meta indexers only)
if ( string . Equals ( CurrentQuery . QueryType , "indexers" , StringComparison . InvariantCultureIgnoreCase ) )
{
if ( ! ( CurrentIndexer is BaseMetaIndexer ) ) // shouldn't be needed because CanHandleQuery should return false
{
2018-05-01 12:55:09 +00:00
logger . Warn ( $"A search request with t=indexers from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} isn't a meta indexer." ) ;
2018-05-01 12:03:16 +00:00
return GetErrorXML ( 203 , "Function Not Available: this isn't a meta indexer" ) ;
}
var CurrentBaseMetaIndexer = ( BaseMetaIndexer ) CurrentIndexer ;
var indexers = CurrentBaseMetaIndexer . Indexers ;
if ( string . Equals ( request . configured , "true" , StringComparison . InvariantCultureIgnoreCase ) )
indexers = indexers . Where ( i = > i . IsConfigured ) ;
else if ( string . Equals ( request . configured , "false" , StringComparison . InvariantCultureIgnoreCase ) )
indexers = indexers . Where ( i = > ! i . IsConfigured ) ;
var xdoc = new XDocument (
new XDeclaration ( "1.0" , "UTF-8" , null ) ,
new XElement ( "indexers" ,
from i in indexers
select new XElement ( "indexer" ,
2020-05-11 19:59:28 +00:00
new XAttribute ( "id" , i . Id ) ,
2018-05-01 12:03:16 +00:00
new XAttribute ( "configured" , i . IsConfigured ) ,
new XElement ( "title" , i . DisplayName ) ,
new XElement ( "description" , i . DisplayDescription ) ,
new XElement ( "link" , i . SiteLink ) ,
new XElement ( "language" , i . Language ) ,
new XElement ( "type" , i . Type ) ,
i . TorznabCaps . GetXDocument ( ) . FirstNode
)
)
) ;
2018-05-01 12:55:09 +00:00
return Content ( xdoc . Declaration . ToString ( ) + Environment . NewLine + xdoc . ToString ( ) , "application/xml" , Encoding . UTF8 ) ;
2018-05-01 12:03:16 +00:00
}
if ( CurrentQuery . ImdbID ! = null )
{
2018-07-31 10:14:02 +00:00
/ * We should allow this ( helpful in case of aggregate indexers )
2018-05-01 12:03:16 +00:00
if ( ! string . IsNullOrEmpty ( CurrentQuery . SearchTerm ) )
{
2018-05-01 12:55:09 +00:00
logger . Warn ( $"A search request from {Request.HttpContext.Connection.RemoteIpAddress} was made containing q and imdbid." ) ;
2018-05-01 12:03:16 +00:00
return GetErrorXML ( 201 , "Incorrect parameter: please specify either imdbid or q" ) ;
}
2018-07-31 10:14:02 +00:00
* /
2018-05-01 12:03:16 +00:00
CurrentQuery . ImdbID = ParseUtil . GetFullImdbID ( CurrentQuery . ImdbID ) ; // normalize ImdbID
if ( CurrentQuery . ImdbID = = null )
{
2018-05-01 12:55:09 +00:00
logger . Warn ( $"A search request from {Request.HttpContext.Connection.RemoteIpAddress} was made with an invalid imdbid." ) ;
2018-05-01 12:03:16 +00:00
return GetErrorXML ( 201 , "Incorrect parameter: invalid imdbid format" ) ;
}
2020-10-18 17:26:22 +00:00
if ( CurrentQuery . IsMovieSearch & & ! CurrentIndexer . TorznabCaps . MovieSearchImdbAvailable )
2018-05-01 12:03:16 +00:00
{
2018-05-01 12:55:09 +00:00
logger . Warn ( $"A search request with imdbid from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it." ) ;
2020-01-09 03:32:02 +00:00
return GetErrorXML ( 203 , "Function Not Available: imdbid is not supported for movie search by this indexer" ) ;
}
2020-10-18 20:47:36 +00:00
if ( CurrentQuery . IsTVSearch & & ! CurrentIndexer . TorznabCaps . TvSearchImdbAvailable )
2020-01-09 03:32:02 +00:00
{
logger . Warn ( $"A search request with imdbid from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it." ) ;
return GetErrorXML ( 203 , "Function Not Available: imdbid is not supported for TV search by this indexer" ) ;
2018-05-01 12:03:16 +00:00
}
}
2020-08-16 22:07:04 +00:00
if ( CurrentQuery . TmdbID ! = null )
{
2020-10-18 17:26:22 +00:00
if ( CurrentQuery . IsMovieSearch & & ! CurrentIndexer . TorznabCaps . MovieSearchTmdbAvailable )
2020-08-16 22:07:04 +00:00
{
logger . Warn ( $"A search request with tmdbid from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it." ) ;
return GetErrorXML ( 203 , "Function Not Available: tmdbid is not supported for movie search by this indexer" ) ;
}
}
if ( CurrentQuery . TvdbID ! = null )
{
2020-10-18 20:47:36 +00:00
if ( CurrentQuery . IsTVSearch & & ! CurrentIndexer . TorznabCaps . TvSearchAvailable )
2020-08-16 22:07:04 +00:00
{
logger . Warn ( $"A search request with tvdbid from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it." ) ;
return GetErrorXML ( 203 , "Function Not Available: tvdbid is not supported for movie search by this indexer" ) ;
}
}
2018-06-10 11:56:45 +00:00
try
{
var result = await CurrentIndexer . ResultsForQuery ( CurrentQuery ) ;
2018-05-01 12:03:16 +00:00
2018-06-10 11:56:45 +00:00
// Log info
2020-12-11 22:14:21 +00:00
var cacheStr = result . IsFromCache ? " (from cache)" : "" ;
if ( string . IsNullOrWhiteSpace ( CurrentQuery . SanitizedSearchTerm ) )
logger . Info ( $"Torznab search in {CurrentIndexer.DisplayName} => Found {result.Releases.Count()} releases{cacheStr}" ) ;
2018-06-10 11:56:45 +00:00
else
2020-12-11 22:14:21 +00:00
logger . Info ( $"Torznab search in {CurrentIndexer.DisplayName} for {CurrentQuery.GetQueryString()} => Found {result.Releases.Count()} releases{cacheStr}" ) ;
2018-05-01 12:03:16 +00:00
2018-06-10 11:56:45 +00:00
var serverUrl = serverService . GetServerUrl ( Request ) ;
var resultPage = new ResultPage ( new ChannelInfo
{
Title = CurrentIndexer . DisplayName ,
Description = CurrentIndexer . DisplayDescription ,
2021-01-17 12:42:52 +00:00
Link = new Uri ( CurrentIndexer . SiteLink )
2018-06-10 11:56:45 +00:00
} ) ;
var proxiedReleases = result . Releases . Select ( r = > AutoMapper . Mapper . Map < ReleaseInfo > ( r ) ) . Select ( r = >
{
2020-05-11 19:59:28 +00:00
r . Link = serverService . ConvertToProxyLink ( r . Link , serverUrl , r . Origin . Id , "dl" , r . Title ) ;
2018-06-10 11:56:45 +00:00
return r ;
} ) ;
2018-05-01 12:03:16 +00:00
2018-06-10 11:56:45 +00:00
resultPage . Releases = proxiedReleases . ToList ( ) ;
2018-05-01 12:03:16 +00:00
2018-06-10 11:56:45 +00:00
var xml = resultPage . ToXml ( new Uri ( serverUrl ) ) ;
// Force the return as XML
2018-05-01 12:55:09 +00:00
2018-06-10 11:56:45 +00:00
return Content ( xml , "application/rss+xml" , Encoding . UTF8 ) ;
}
2020-09-24 20:02:45 +00:00
catch ( Exception e )
2018-06-10 11:56:45 +00:00
{
2020-09-24 20:02:45 +00:00
logger . Error ( e ) ;
return GetErrorXML ( 900 , e . ToString ( ) ) ;
2018-06-10 11:56:45 +00:00
}
2018-05-01 12:03:16 +00:00
}
2018-05-01 12:55:09 +00:00
[Route("[action] / { ignored ? } ")]
2020-02-25 16:08:03 +00:00
public IActionResult GetErrorXML ( int code , string description ) = > Content ( CreateErrorXML ( code , description ) , "application/xml" , Encoding . UTF8 ) ;
2018-06-10 11:56:45 +00:00
public static string CreateErrorXML ( int code , string description )
2018-05-01 12:03:16 +00:00
{
var xdoc = new XDocument (
new XDeclaration ( "1.0" , "UTF-8" , null ) ,
new XElement ( "error" ,
new XAttribute ( "code" , code . ToString ( ) ) ,
new XAttribute ( "description" , description )
)
) ;
2018-06-10 11:56:45 +00:00
return xdoc . Declaration + Environment . NewLine + xdoc ;
}
2018-05-01 12:03:16 +00:00
2018-06-10 11:56:45 +00:00
public static IActionResult GetErrorActionResult ( RouteData routeData , HttpStatusCode status , int torznabCode , string description )
{
2020-02-10 22:16:19 +00:00
var isTorznab = routeData . Values [ "action" ] . ToString ( ) . Equals ( "torznab" , StringComparison . OrdinalIgnoreCase ) ;
2018-06-10 11:56:45 +00:00
if ( isTorznab )
{
2020-02-10 22:16:19 +00:00
var contentResult = new ContentResult
2018-06-10 11:56:45 +00:00
{
Content = CreateErrorXML ( torznabCode , description ) ,
ContentType = "application/xml" ,
StatusCode = 200
} ;
return contentResult ;
}
else
{
switch ( status )
{
case HttpStatusCode . Unauthorized :
return new UnauthorizedResult ( ) ;
case HttpStatusCode . NotFound :
return new NotFoundObjectResult ( description ) ;
case HttpStatusCode . BadRequest :
return new BadRequestObjectResult ( description ) ;
default :
return new ContentResult
{
Content = description ,
StatusCode = ( int ) status
} ;
}
}
2018-05-01 12:03:16 +00:00
}
2018-05-01 12:55:09 +00:00
[Route("[action] / { ignored ? } ")]
2018-05-01 12:03:16 +00:00
[HttpGet]
2021-05-16 18:13:54 +00:00
public async Task < TorrentPotatoResponse > Potato ( [ FromQuery ] TorrentPotatoRequest request )
2018-05-01 12:03:16 +00:00
{
var result = await CurrentIndexer . ResultsForQuery ( CurrentQuery ) ;
// Log info
2020-12-11 22:14:21 +00:00
var cacheStr = result . IsFromCache ? " (from cache)" : "" ;
2018-05-01 12:03:16 +00:00
if ( string . IsNullOrWhiteSpace ( CurrentQuery . SanitizedSearchTerm ) )
2020-12-11 22:14:21 +00:00
logger . Info ( $"Potato search in {CurrentIndexer.DisplayName} => Found {result.Releases.Count()} releases{cacheStr}" ) ;
2018-05-01 12:03:16 +00:00
else
2020-12-11 22:14:21 +00:00
logger . Info ( $"Potato search in {CurrentIndexer.DisplayName} for {CurrentQuery.GetQueryString()} => Found {result.Releases.Count()} releases{cacheStr}" ) ;
2018-05-01 12:03:16 +00:00
var serverUrl = serverService . GetServerUrl ( Request ) ;
var potatoReleases = result . Releases . Where ( r = > r . Link ! = null | | r . MagnetUri ! = null ) . Select ( r = >
{
var release = AutoMapper . Mapper . Map < ReleaseInfo > ( r ) ;
2020-05-11 19:59:28 +00:00
release . Link = serverService . ConvertToProxyLink ( release . Link , serverUrl , CurrentIndexer . Id , "dl" , release . Title ) ;
2020-04-12 16:06:39 +00:00
// IMPORTANT: We can't use Uri.ToString(), because it generates URLs without URL encode (links with unicode
// characters are broken). We must use Uri.AbsoluteUri instead that handles encoding correctly
2018-05-01 12:03:16 +00:00
var item = new TorrentPotatoResponseItem ( )
{
release_name = release . Title + "[" + CurrentIndexer . DisplayName + "]" , // Suffix the indexer so we can see which tracker we are using in CPS as it just says torrentpotato >.>
2020-04-12 16:06:39 +00:00
torrent_id = release . Guid . AbsoluteUri , // GUID and (Link or Magnet) are mandatory
2020-11-08 02:11:27 +00:00
details_url = release . Details ? . AbsoluteUri ,
2020-04-12 16:06:39 +00:00
download_url = ( release . Link ! = null ? release . Link . AbsoluteUri : release . MagnetUri . AbsoluteUri ) ,
2018-07-09 10:40:37 +00:00
imdb_id = release . Imdb . HasValue ? ParseUtil . GetFullImdbID ( "tt" + release . Imdb ) : null ,
2018-05-01 12:03:16 +00:00
freeleech = ( release . DownloadVolumeFactor = = 0 ? true : false ) ,
type = "movie" ,
size = ( long ) release . Size / ( 1024 * 1024 ) , // This is in MB
leechers = ( release . Peers ? ? - 1 ) - ( release . Seeders ? ? 0 ) ,
seeders = release . Seeders ? ? - 1 ,
publish_date = r . PublishDate = = DateTime . MinValue ? null : release . PublishDate . ToUniversalTime ( ) . ToString ( "s" )
} ;
return item ;
} ) ;
var potatoResponse = new TorrentPotatoResponse ( )
{
results = potatoReleases . ToList ( )
} ;
return potatoResponse ;
}
2018-05-01 12:55:09 +00:00
[Route("[action] / { ignored ? } ")]
2018-05-01 12:03:16 +00:00
private void ConfigureCacheResults ( IEnumerable < TrackerCacheResult > results )
{
var serverUrl = serverService . GetServerUrl ( Request ) ;
foreach ( var result in results )
{
var link = result . Link ;
var file = StringUtil . MakeValidFileName ( result . Title , '_' , false ) ;
result . Link = serverService . ConvertToProxyLink ( link , serverUrl , result . TrackerId , "dl" , file ) ;
2018-06-18 11:54:39 +00:00
if ( ! string . IsNullOrWhiteSpace ( serverConfig . BlackholeDir ) )
2018-06-10 11:54:12 +00:00
{
if ( result . Link ! = null )
result . BlackholeLink = serverService . ConvertToProxyLink ( link , serverUrl , result . TrackerId , "bh" , file ) ;
else if ( result . MagnetUri ! = null )
result . BlackholeLink = serverService . ConvertToProxyLink ( result . MagnetUri , serverUrl , result . TrackerId , "bh" , file ) ;
}
2018-05-01 12:03:16 +00:00
}
}
}
}