|
|
|
@ -40,21 +40,109 @@ namespace NzbDrone.Core.Download
|
|
|
|
|
|
|
|
|
|
protected abstract string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink);
|
|
|
|
|
protected abstract string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent);
|
|
|
|
|
protected virtual string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
protected virtual string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Download(RemoteMovie remoteMovie)
|
|
|
|
|
{
|
|
|
|
|
var torrentInfo = remoteMovie.Release as TorrentInfo;
|
|
|
|
|
|
|
|
|
|
string magnetUrl = null;
|
|
|
|
|
string torrentUrl = null;
|
|
|
|
|
|
|
|
|
|
if (remoteMovie.Release.DownloadUrl.IsNotNullOrWhiteSpace() && remoteMovie.Release.DownloadUrl.StartsWith("magnet:"))
|
|
|
|
|
{
|
|
|
|
|
magnetUrl = remoteMovie.Release.DownloadUrl;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
torrentUrl = remoteMovie.Release.DownloadUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (torrentInfo != null && !torrentInfo.MagnetUrl.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
magnetUrl = torrentInfo.MagnetUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PreferTorrentFile)
|
|
|
|
|
{
|
|
|
|
|
if (torrentUrl.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromWebUrl(remoteMovie, torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (!magnetUrl.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.Debug("Torrent download failed, trying magnet. ({0})", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (magnetUrl.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromMagnetUrl(remoteMovie, magnetUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (NotSupportedException ex)
|
|
|
|
|
{
|
|
|
|
|
throw new ReleaseDownloadException(remoteMovie.Release, "Magnet not supported by download client. ({0})", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (magnetUrl.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromMagnetUrl(remoteMovie, magnetUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (NotSupportedException ex)
|
|
|
|
|
{
|
|
|
|
|
if (torrentUrl.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
throw new ReleaseDownloadException(remoteMovie.Release, "Magnet not supported by download client. ({0})", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Download(RemoteEpisode remoteEpisode)
|
|
|
|
|
if (torrentUrl.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromWebUrl(remoteMovie, torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Download(RemoteEpisode remoteMovie)
|
|
|
|
|
{
|
|
|
|
|
var torrentInfo = remoteEpisode.Release as TorrentInfo;
|
|
|
|
|
var torrentInfo = remoteMovie.Release as TorrentInfo;
|
|
|
|
|
|
|
|
|
|
string magnetUrl = null;
|
|
|
|
|
string torrentUrl = null;
|
|
|
|
|
|
|
|
|
|
if (remoteEpisode.Release.DownloadUrl.IsNotNullOrWhiteSpace() && remoteEpisode.Release.DownloadUrl.StartsWith("magnet:"))
|
|
|
|
|
if (remoteMovie.Release.DownloadUrl.IsNotNullOrWhiteSpace() && remoteMovie.Release.DownloadUrl.StartsWith("magnet:"))
|
|
|
|
|
{
|
|
|
|
|
magnetUrl = remoteEpisode.Release.DownloadUrl;
|
|
|
|
|
magnetUrl = remoteMovie.Release.DownloadUrl;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
torrentUrl = remoteEpisode.Release.DownloadUrl;
|
|
|
|
|
torrentUrl = remoteMovie.Release.DownloadUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (torrentInfo != null && !torrentInfo.MagnetUrl.IsNullOrWhiteSpace())
|
|
|
|
@ -68,7 +156,7 @@ namespace NzbDrone.Core.Download
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromWebUrl(remoteEpisode, torrentUrl);
|
|
|
|
|
return DownloadFromWebUrl(remoteMovie, torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
@ -85,11 +173,11 @@ namespace NzbDrone.Core.Download
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromMagnetUrl(remoteEpisode, magnetUrl);
|
|
|
|
|
return DownloadFromMagnetUrl(remoteMovie, magnetUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (NotSupportedException ex)
|
|
|
|
|
{
|
|
|
|
|
throw new ReleaseDownloadException(remoteEpisode.Release, "Magnet not supported by download client. ({0})", ex.Message);
|
|
|
|
|
throw new ReleaseDownloadException(remoteMovie.Release, "Magnet not supported by download client. ({0})", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -99,13 +187,13 @@ namespace NzbDrone.Core.Download
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromMagnetUrl(remoteEpisode, magnetUrl);
|
|
|
|
|
return DownloadFromMagnetUrl(remoteMovie, magnetUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (NotSupportedException ex)
|
|
|
|
|
{
|
|
|
|
|
if (torrentUrl.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
throw new ReleaseDownloadException(remoteEpisode.Release, "Magnet not supported by download client. ({0})", ex.Message);
|
|
|
|
|
throw new ReleaseDownloadException(remoteMovie.Release, "Magnet not supported by download client. ({0})", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message);
|
|
|
|
@ -114,13 +202,113 @@ namespace NzbDrone.Core.Download
|
|
|
|
|
|
|
|
|
|
if (torrentUrl.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromWebUrl(remoteEpisode, torrentUrl);
|
|
|
|
|
return DownloadFromWebUrl(remoteMovie, torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string DownloadFromWebUrl(RemoteMovie remoteEpisode, string torrentUrl)
|
|
|
|
|
{
|
|
|
|
|
byte[] torrentFile = null;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequest(torrentUrl);
|
|
|
|
|
request.Headers.Accept = "application/x-bittorrent";
|
|
|
|
|
request.AllowAutoRedirect = false;
|
|
|
|
|
|
|
|
|
|
var response = _httpClient.Get(request);
|
|
|
|
|
|
|
|
|
|
if (response.StatusCode == HttpStatusCode.SeeOther || response.StatusCode == HttpStatusCode.Found)
|
|
|
|
|
{
|
|
|
|
|
var locationHeader = response.Headers.GetSingleValue("Location");
|
|
|
|
|
|
|
|
|
|
_logger.Trace("Torrent request is being redirected to: {0}", locationHeader);
|
|
|
|
|
|
|
|
|
|
if (locationHeader != null)
|
|
|
|
|
{
|
|
|
|
|
if (locationHeader.StartsWith("magnet:"))
|
|
|
|
|
{
|
|
|
|
|
return DownloadFromMagnetUrl(remoteEpisode, locationHeader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DownloadFromWebUrl(remoteEpisode, locationHeader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new WebException("Remote website tried to redirect without providing a location.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
torrentFile = response.ResponseData;
|
|
|
|
|
|
|
|
|
|
_logger.Debug("Downloading torrent for episode '{0}' finished ({1} bytes from {2})", remoteEpisode.Release.Title, torrentFile.Length, torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
catch (HttpException ex)
|
|
|
|
|
{
|
|
|
|
|
if ((int)ex.Response.StatusCode == 429)
|
|
|
|
|
{
|
|
|
|
|
_logger.Error("API Grab Limit reached for {0}", torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.Error(ex, "Downloading torrent file for episode '{0}' failed ({1})", remoteEpisode.Release.Title, torrentUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new ReleaseDownloadException(remoteEpisode.Release, "Downloading torrent failed", ex);
|
|
|
|
|
}
|
|
|
|
|
catch (WebException ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.Error(ex, "Downloading torrent file for episode '{0}' failed ({1})", remoteEpisode.Release.Title, torrentUrl);
|
|
|
|
|
|
|
|
|
|
throw new ReleaseDownloadException(remoteEpisode.Release, "Downloading torrent failed", ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var filename = string.Format("{0}.torrent", FileNameBuilder.CleanFileName(remoteEpisode.Release.Title));
|
|
|
|
|
var hash = _torrentFileInfoReader.GetHashFromTorrentFile(torrentFile);
|
|
|
|
|
var actualHash = AddFromTorrentFile(remoteEpisode, hash, filename, torrentFile);
|
|
|
|
|
|
|
|
|
|
if (actualHash.IsNotNullOrWhiteSpace() && hash != actualHash)
|
|
|
|
|
{
|
|
|
|
|
_logger.Debug(
|
|
|
|
|
"{0} did not return the expected InfoHash for '{1}', Sonarr could potentially lose track of the download in progress.",
|
|
|
|
|
Definition.Implementation, remoteEpisode.Release.DownloadUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return actualHash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string DownloadFromMagnetUrl(RemoteMovie remoteEpisode, string magnetUrl)
|
|
|
|
|
{
|
|
|
|
|
string hash = null;
|
|
|
|
|
string actualHash = null;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
hash = new MagnetLink(magnetUrl).InfoHash.ToHex();
|
|
|
|
|
}
|
|
|
|
|
catch (FormatException ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.Error(ex, "Failed to parse magnetlink for episode '{0}': '{1}'", remoteEpisode.Release.Title, magnetUrl);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hash != null)
|
|
|
|
|
{
|
|
|
|
|
actualHash = AddFromMagnetLink(remoteEpisode, hash, magnetUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (actualHash.IsNotNullOrWhiteSpace() && hash != actualHash)
|
|
|
|
|
{
|
|
|
|
|
_logger.Debug(
|
|
|
|
|
"{0} did not return the expected InfoHash for '{1}', Sonarr could potentially lose track of the download in progress.",
|
|
|
|
|
Definition.Implementation, remoteEpisode.Release.DownloadUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return actualHash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string DownloadFromWebUrl(RemoteEpisode remoteEpisode, string torrentUrl)
|
|
|
|
|
{
|
|
|
|
|
byte[] torrentFile = null;
|
|
|
|
|