Merge branch 'develop' into AddMetadataURL

This commit is contained in:
FuNK3Y 2019-04-12 22:11:33 +02:00 committed by GitHub
commit b48d9a6bac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
139 changed files with 3505 additions and 2609 deletions

View file

@ -1,10 +1,10 @@
FROM mono:5.8
FROM mono:5.18
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y git ssh tar gzip ca-certificates wget zip wine wine32 wine64 libwine libwine:i386
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -E -
RUN apt-get install -y nodejs
RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-5_all.deb && dpkg -i repo-mediaarea_1.0-5_all.deb && apt-get update
RUN apt-get install -y libmediainfo-dev libmediainfo0 mediainfo
RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-7_all.deb && dpkg -i repo-mediaarea_1.0-7_all.deb && apt-get update
RUN apt-get install -y libmediainfo-dev libmediainfo0v5 mediainfo
RUN npm i -g npm
RUN apt-get install -y python3-pip && pip3 install gitchangelog pystache
RUN curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz && tar xvf go*.tar.gz && chown -R root:root ./go && mv go /usr/local

View file

@ -2,7 +2,7 @@ version: 2
defaults: &defaults
docker:
- image: gallileo/radarr-cci-primary:5.8.8
- image: gallileo/radarr-cci-primary:5.8.9
environment:
BUILD_VERSION: 0.2.0
@ -119,9 +119,9 @@ jobs:
- store_artifacts:
path: _packages
destination: artifacts
- run:
name: "Deploying"
command: chmod +x deploy.sh && ./deploy.sh
#- run:
# name: "Deploying"
# command: chmod +x deploy.sh && ./deploy.sh
- persist_to_workspace:
root: .
# Must be relative path from root

2
.gitignore vendored
View file

@ -104,7 +104,7 @@ _TeamCity*
# Radarr
Backups/
logs/
MediaCover/
#MediaCover/
UpdateLogs/
xdg/
config.xml

View file

@ -2,6 +2,37 @@
## (unreleased)
### **New features**
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Use APIKey & APIUser for authenticating to PassThePopcorn. ([#3264](https://github.com/Radarr/Radarr/issues/3264)) [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Support for forcedUP status ([#3277](https://github.com/Radarr/Radarr/issues/3277)) [<a href="https://github.com/Swizzy">Swizzy</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) rTorrent - Don't start download automatically ([#3222](https://github.com/Radarr/Radarr/issues/3222)) [<a href="https://github.com/lps-rocks">lps-rocks</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Remove Pre, postbot, xpost suffixes from release groups ([#3220](https://github.com/Radarr/Radarr/issues/3220)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Secure URLs for Links and Services ([#3219](https://github.com/Radarr/Radarr/issues/3219)) [<a href="https://github.com/Qstick">Qstick</a>]
### **Fixes**
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Another IDisposable leak when lazy loading properties. [<a href="https://github.com/Taloth Saldono">Taloth Saldono</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaCoverService tests and stupidly forgetting to open the database connection for logging. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaCovers resizing potentially leaking memory when concurrently executing. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Stream leakage inside CurlHttpDispatcher. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Apparently Microsoft thinks that you should cast to IDisposable first. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Invalid SQLite cache size. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Resource leakage inside HttpClient. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Typo that could probably lead to an infinite loop. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Resource leakage inside ManagedHttpDispatcher. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Leaking of objects when logging something to the database. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) DataMapper potentially leaking stuff when being disposed. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) DataMapper not being disposed, leading to resource leakage. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Memory leak due to unmanaged Bitmaps leaking. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Unable to parse movies from 1800s. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) OSX Packages not getting correct version info. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) npm start not working with node 10. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Hopefully fixed issue where a null downloadId or title would cause no downloads to be tracked. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Bump default version to prevent update running from source ([#3199](https://github.com/Radarr/Radarr/issues/3199)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaInfo Unit Test Failing due to AudioAdditionalFeatures ([#3221](https://github.com/Radarr/Radarr/issues/3221)) [<a href="https://github.com/Qstick">Qstick</a>]
## v0.2.0.1216 (2018-11-07)
### **New features**
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) 64bit mediainfo.dll to 32bit to resolve issue: https://github.com/Radarr/Radarr/issues/3138. [<a href="https://github.com/geogolem">geogolem</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Refactor MediaInfo tokens (fixes old tokens adds new stuff) ([#3058](https://github.com/Radarr/Radarr/issues/3058)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]

View file

@ -6,6 +6,8 @@ Radarr is an __independent__ fork of [Sonarr](https://github.com/Sonarr/Sonarr)
The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
## Getting Started
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/wiki/Installation)
@ -20,13 +22,12 @@ The project was inspired by other Usenet/BitTorrent movie downloaders such as Co
## Downloads
[![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases)
[![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts)
Branch | develop (stable) | nightly (semi-unstable) |
---|---|---
Binary Releases | [![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) | [![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts)
Docker (linuxserver.io, x86_64, arm64, armhf) | [![Docker release](https://img.shields.io/badge/linuxserver-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr) | [![Docker nightly](https://img.shields.io/badge/linuxserver-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
Docker (hotio, see [here](https://github.com/hotio/docker-radarr) for more information) | [![Docker release / nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker release / nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr)
[![Docker release](https://img.shields.io/badge/docker-release-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
[![Docker nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/hotio/suitarr)
[![Docker armhf](https://img.shields.io/badge/docker-armhf-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr)
[![Docker aarch64](https://img.shields.io/badge/docker-aarch64-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr-aarch64)
## Support
@ -93,12 +94,7 @@ Radarr is currently undergoing rapid development and pull requests are actively
### Planned Features
* Dynamically renaming folders with quality info, etc. (Currently working partially.) (\*)
* Supporting custom folder structures, such as all movie files in one folder (\*)
* Supporting multiple editions per movies (\*)
* Supporting collections of movies, such as James Bond (\*)
**Note:** All features marked with (\*) are set to be in the first release of Radarr.
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
#### [Feature Requests](http://feathub.com/Radarr/Radarr)

View file

@ -1,6 +1,7 @@
#addin nuget:?package=Cake.Npm
#addin nuget:?package=SharpZipLib
#addin nuget:?package=Cake.Compression
#addin "Cake.FileHelpers"
// Build variables
var outputFolder = "./_output";
@ -178,7 +179,7 @@ Task("PackageOsx").Does(() => {
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
});
Task("PackageOsxApp").Does(() => {
Task("PackageOsxApp").Does((ctx) => {
// Start osx app package
if (DirectoryExists(outputFolderOsxApp)) {
DeleteDirectory(outputFolderOsxApp, true);
@ -189,6 +190,9 @@ Task("PackageOsxApp").Does(() => {
// Copy osx package files
CopyDirectory("./osx/Radarr.app", outputFolderOsxApp + "/Radarr.app");
CopyDirectory(outputFolderOsx, outputFolderOsxApp + "/Radarr.app/Contents/MacOS");
// Edit version of osx app
ctx.ReplaceTextInFiles(outputFolderOsxApp + "/Radarr.app/Contents/Info.plist", "2.0", ctx.EnvironmentVariable("APPVEYOR_BUILD_VERSION") ?? "unknown");
});
Task("PackageTests").Does(() => {

View file

@ -221,9 +221,9 @@ PackageTests()
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
if [ $runtime = "dotnet" ] ; then
$nuget install NUnit.Runners -Version 3.2.1 -Output $testPackageFolder
$nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
else
mono $nuget install NUnit.Runners -Version 3.2.1 -Output $testPackageFolder
mono $nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
fi
cp $outputFolder/*.dll $testPackageFolder

0
debian/rules vendored Executable file → Normal file
View file

3848
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -44,6 +44,7 @@
"tar.gz": "0.1.1",
"url-search-params": "^0.6.1",
"webpack": "1.12.0",
"webpack-stream": "2.1.0"
"webpack-stream": "2.1.0",
"natives": "^1.1.3"
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
</component>
</project>

View file

@ -2,4 +2,4 @@
using System.Reflection;
[assembly: AssemblyVersion("0.1.0.*")]
[assembly: AssemblyVersion("10.0.0.*")]

View file

@ -941,21 +941,21 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (Command.Transaction != null)
if (_command != null)
{
Command.Transaction.Dispose();
Command.Transaction = null;
}
if (_command.Transaction != null)
{
_command.Transaction.Dispose();
_command.Transaction = null;
}
if (Command.Connection != null)
{
Command.Connection.Dispose();
Command.Connection = null;
}
if (_command.Connection != null)
{
_command.Connection.Dispose();
_command.Connection = null;
}
if (Command != null)
{
Command.Dispose();
_command.Dispose();
_command = null;
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Common;
@ -91,9 +91,11 @@ private void PrepareLazyLoadedProperties(object ent)
Type entType = ent.GetType();
if (_repos.Relationships.ContainsKey(entType))
{
var provider = _db.ProviderFactory;
var connectionString = _db.ConnectionString;
Func<IDataMapper> dbCreate = () =>
{
var db = new DataMapper(_db.ProviderFactory, _db.ConnectionString);
var db = new DataMapper(provider, connectionString);
db.SqlMode = SqlModes.Text;
return db;
};

View file

@ -5,6 +5,7 @@
using Nancy;
using Nancy.Bootstrapper;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Api.Extensions.Pipelines
@ -15,9 +16,14 @@ public class GzipCompressionPipeline : IRegisterNancyPipeline
public int Order => 0;
private readonly Action<Action<Stream>, Stream> _writeGZipStream;
public GzipCompressionPipeline(Logger logger)
{
_logger = logger;
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
_writeGZipStream = OsInfo.IsMonoRuntime ? WriteGZipStreamMono : (Action<Action<Stream>, Stream>)WriteGZipStream;
}
public void Register(IPipelines pipelines)
@ -43,14 +49,7 @@ private void CompressResponse(NancyContext context)
var contents = response.Contents;
response.Headers["Content-Encoding"] = "gzip";
response.Contents = responseStream =>
{
using (var gzip = new GZipStream(responseStream, CompressionMode.Compress, true))
using (var buffered = new BufferedStream(gzip, 8192))
{
contents.Invoke(buffered);
}
};
response.Contents = responseStream => _writeGZipStream(contents, responseStream);
}
}
@ -61,6 +60,25 @@ private void CompressResponse(NancyContext context)
}
}
private static void WriteGZipStreamMono(Action<Stream> innerContent, Stream targetStream)
{
using (var membuffer = new MemoryStream())
{
WriteGZipStream(innerContent, membuffer);
membuffer.Position = 0;
membuffer.CopyTo(targetStream);
}
}
private static void WriteGZipStream(Action<Stream> innerContent, Stream targetStream)
{
using (var gzip = new GZipStream(targetStream, CompressionMode.Compress, true))
using (var buffered = new BufferedStream(gzip, 8192))
{
innerContent.Invoke(buffered);
}
}
private static bool ContentLengthIsTooSmall(Response response)
{
var contentLength = response.Headers.GetValueOrDefault("Content-Length");

View file

@ -2,23 +2,18 @@
namespace NzbDrone.Common.Cloud
{
public interface ISonarrCloudRequestBuilder
public interface IRadarrCloudRequestBuilder
{
IHttpRequestBuilderFactory Services { get; }
IHttpRequestBuilderFactory SkyHookTvdb { get; }
IHttpRequestBuilderFactory TMDB { get; }
IHttpRequestBuilderFactory TMDBSingle { get; }
}
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder
public class RadarrCloudRequestBuilder : IRadarrCloudRequestBuilder
{
public SonarrCloudRequestBuilder()
public RadarrCloudRequestBuilder()
{
Services = new HttpRequestBuilder("http://radarr.aeonlucid.com/v1/")
.CreateFactory();
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
.SetSegment("language", "en")
Services = new HttpRequestBuilder("https://radarr.aeonlucid.com/v1/")
.CreateFactory();
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}")
@ -31,7 +26,6 @@ public SonarrCloudRequestBuilder()
}
public IHttpRequestBuilderFactory Services { get; private set; }
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
public IHttpRequestBuilderFactory TMDB { get; private set; }
public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
}

View file

@ -123,27 +123,6 @@ public bool CanUseGDIPlus()
}
}
public bool IsValidGDIPlusImage(string filename)
{
if (!CanUseGDIPlus())
{
return true;
}
try
{
using (var bmp = new Bitmap(filename))
{
}
return true;
}
catch (Exception ex)
{
Logger.Debug(ex, "Corrupted image found at: {0}.", filename);
return false;
}
}
public bool FolderWritable(string path)
{
Ensure.That(path, () => path).IsValidPath();

View file

@ -20,7 +20,6 @@ public interface IDiskProvider
bool FileExists(string path);
bool FileExists(string path, StringComparison stringComparison);
bool CanUseGDIPlus();
bool IsValidGDIPlusImage(string path);
bool FolderWritable(string path);
string[] GetDirectories(string path);
string[] GetFiles(string path, SearchOption searchOption);

View file

@ -66,95 +66,96 @@ public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
lock (CurlGlobalHandle.Instance)
{
Stream responseStream = new MemoryStream();
Stream headerStream = new MemoryStream();
using (var curlEasy = new CurlEasy())
using (Stream responseStream = new MemoryStream())
using (Stream headerStream = new MemoryStream())
{
curlEasy.AutoReferer = false;
curlEasy.WriteFunction = (b, s, n, o) =>
using (var curlEasy = new CurlEasy())
{
responseStream.Write(b, 0, s * n);
return s * n;
};
curlEasy.HeaderFunction = (b, s, n, o) =>
{
headerStream.Write(b, 0, s * n);
return s * n;
};
AddProxy(curlEasy, request);
curlEasy.Url = request.Url.FullUri;
switch (request.Method)
{
case HttpMethod.GET:
curlEasy.HttpGet = true;
break;
case HttpMethod.POST:
curlEasy.Post = true;
break;
case HttpMethod.PUT:
curlEasy.Put = true;
break;
default:
throw new NotSupportedException(string.Format("HttpCurl method {0} not supported", request.Method));
}
curlEasy.FollowLocation = false;
curlEasy.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent; ;
if (request.RequestTimeout != TimeSpan.Zero)
{
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
}
if (OsInfo.IsWindows)
{
curlEasy.CaInfo = _caBundleFilePath;
}
if (cookies != null)
{
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
}
if (request.ContentData != null)
{
curlEasy.PostFieldSize = request.ContentData.Length;
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
}
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
using (var httpRequestHeaders = SerializeHeaders(request))
{
curlEasy.HttpHeader = httpRequestHeaders;
var result = curlEasy.Perform();
if (result != CurlCode.Ok)
curlEasy.AutoReferer = false;
curlEasy.WriteFunction = (b, s, n, o) =>
{
switch (result)
responseStream.Write(b, 0, s * n);
return s * n;
};
curlEasy.HeaderFunction = (b, s, n, o) =>
{
headerStream.Write(b, 0, s * n);
return s * n;
};
AddProxy(curlEasy, request);
curlEasy.Url = request.Url.FullUri;
switch (request.Method)
{
case HttpMethod.GET:
curlEasy.HttpGet = true;
break;
case HttpMethod.POST:
curlEasy.Post = true;
break;
case HttpMethod.PUT:
curlEasy.Put = true;
break;
default:
throw new NotSupportedException(string.Format("HttpCurl method {0} not supported", request.Method));
}
curlEasy.FollowLocation = false;
curlEasy.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent; ;
if (request.RequestTimeout != TimeSpan.Zero)
{
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
}
if (OsInfo.IsWindows)
{
curlEasy.CaInfo = _caBundleFilePath;
}
if (cookies != null)
{
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
}
if (request.ContentData != null)
{
curlEasy.PostFieldSize = request.ContentData.Length;
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
}
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
using (var httpRequestHeaders = SerializeHeaders(request))
{
curlEasy.HttpHeader = httpRequestHeaders;
var result = curlEasy.Perform();
if (result != CurlCode.Ok)
{
case CurlCode.SslCaCert:
case (CurlCode)77:
throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url));
default:
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
switch (result)
{
case CurlCode.SslCaCert:
case (CurlCode)77:
throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url));
default:
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
}
}
}
var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream);
var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection);
var httpHeader = new HttpHeader(webHeaderCollection);
return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode);
}
var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream);
var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection);
var httpHeader = new HttpHeader(webHeaderCollection);
return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode);
}
}
}
@ -259,6 +260,7 @@ private string FixSetCookieHeader(string setCookie)
private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream, WebHeaderCollection webHeaderCollection)
{
byte[] bytes = null;
responseStream.Position = 0;
if (responseStream.Length != 0)
@ -268,21 +270,27 @@ private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream,
{
if (encoding.IndexOf("gzip") != -1)
{
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
using (var zipStream = new GZipStream(responseStream, CompressionMode.Decompress))
{
bytes = zipStream.ToBytes();
}
webHeaderCollection.Remove("Content-Encoding");
}
else if (encoding.IndexOf("deflate") != -1)
{
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
using (var deflateStream = new DeflateStream(responseStream, CompressionMode.Decompress))
{
bytes = deflateStream.ToBytes();
}
webHeaderCollection.Remove("Content-Encoding");
}
}
}
return responseStream.ToBytes();
if (bytes == null) bytes = responseStream.ToBytes();
return bytes;
}
}

View file

@ -1,4 +1,6 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
@ -20,72 +22,106 @@ public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, I
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
{
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
// Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
webRequest.Method = request.Method.ToString();
webRequest.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent;
webRequest.KeepAlive = request.ConnectionKeepAlive;
webRequest.AllowAutoRedirect = false;
webRequest.CookieContainer = cookies;
if (request.RequestTimeout != TimeSpan.Zero)
{
webRequest.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalMilliseconds);
}
AddProxy(webRequest, request);
if (request.Headers != null)
{
AddRequestHeaders(webRequest, request.Headers);
}
if (request.ContentData != null)
{
webRequest.ContentLength = request.ContentData.Length;
using (var writeStream = webRequest.GetRequestStream())
{
writeStream.Write(request.ContentData, 0, request.ContentData.Length);
}
}
HttpWebResponse httpWebResponse;
HttpWebResponse httpWebResponse = null;
HttpWebRequest webRequest = null;
try
{
httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
webRequest = (HttpWebRequest) WebRequest.Create((Uri) request.Url);
if (OsInfo.IsMonoRuntime)
{
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
webRequest.AutomaticDecompression = DecompressionMethods.None;
webRequest.Headers.Add("Accept-Encoding", "gzip");
}
else
{
// Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
}
webRequest.Method = request.Method.ToString();
webRequest.UserAgent = request.UseSimplifiedUserAgent
? UserAgentBuilder.UserAgentSimplified
: UserAgentBuilder.UserAgent;
webRequest.KeepAlive = request.ConnectionKeepAlive;
webRequest.AllowAutoRedirect = false;
webRequest.CookieContainer = cookies;
if (request.RequestTimeout != TimeSpan.Zero)
{
webRequest.Timeout = (int) Math.Ceiling(request.RequestTimeout.TotalMilliseconds);
}
AddProxy(webRequest, request);
if (request.Headers != null)
{
AddRequestHeaders(webRequest, request.Headers);
}
if (request.ContentData != null)
{
webRequest.ContentLength = request.ContentData.Length;
using (var writeStream = webRequest.GetRequestStream())
{
writeStream.Write(request.ContentData, 0, request.ContentData.Length);
}
}
try
{
httpWebResponse = (HttpWebResponse) webRequest.GetResponse();
}
catch (WebException e)
{
if (e.Status == WebExceptionStatus.SecureChannelFailure && OsInfo.IsWindows)
{
SecurityProtocolPolicy.DisableTls12();
}
httpWebResponse = (HttpWebResponse) e.Response;
if (httpWebResponse == null)
{
throw;
}
}
byte[] data = null;
using (var responseStream = httpWebResponse.GetResponseStream())
{
if (responseStream != null)
{
data = responseStream.ToBytes();
if (OsInfo.IsMonoRuntime && httpWebResponse.ContentEncoding == "gzip")
{
using (var compressedStream = new MemoryStream(data))
using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var decompressedStream = new MemoryStream())
{
gzip.CopyTo(decompressedStream);
data = decompressedStream.ToArray();
}
httpWebResponse.Headers.Remove("Content-Encoding");
}
}
}
return new HttpResponse(request, new HttpHeader(httpWebResponse.Headers), data,
httpWebResponse.StatusCode);
}
catch (WebException e)
finally
{
if (e.Status == WebExceptionStatus.SecureChannelFailure && OsInfo.IsWindows)
{
SecurityProtocolPolicy.DisableTls12();
}
httpWebResponse = (HttpWebResponse)e.Response;
if (httpWebResponse == null)
{
throw;
}
webRequest = null;
(httpWebResponse as IDisposable)?.Dispose();
httpWebResponse = null;
}
byte[] data = null;
using (var responseStream = httpWebResponse.GetResponseStream())
{
if (responseStream != null)
{
data = responseStream.ToBytes();
}
}
return new HttpResponse(request, new HttpHeader(httpWebResponse.Headers), data, httpWebResponse.StatusCode);
}
protected virtual void AddProxy(HttpWebRequest webRequest, HttpRequest request)

View file

@ -226,11 +226,13 @@ public void DownloadFile(string url, string fileName)
_logger.Debug("Downloading [{0}] to [{1}]", url, fileName);
var stopWatch = Stopwatch.StartNew();
var webClient = new GZipWebClient();
webClient.Headers.Add(HttpRequestHeader.UserAgent, UserAgentBuilder.UserAgent);
webClient.DownloadFile(url, fileName);
stopWatch.Stop();
_logger.Debug("Downloading Completed. took {0:0}s", stopWatch.Elapsed.Seconds);
using (var webClient = new GZipWebClient())
{
webClient.Headers.Add(HttpRequestHeader.UserAgent, UserAgentBuilder.UserAgent);
webClient.DownloadFile(url, fileName);
stopWatch.Stop();
_logger.Debug("Downloading Completed. took {0:0}s", stopWatch.Elapsed.Seconds);
}
}
catch (WebException e)
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
@ -135,7 +135,7 @@ public HttpUri CombinePath(string path)
return new HttpUri(Scheme, Host, Port, CombinePath(Path, path), Query, Fragment);
}
private static string CombinePath(string basePath, string relativePath)
public static string CombinePath(string basePath, string relativePath)
{
if (relativePath.IsNullOrWhiteSpace())
{

View file

@ -44,6 +44,8 @@ private static void HandleAppDomainException(object sender, UnhandledExceptionEv
return;
}
}
Console.WriteLine(exception.StackTrace);
Console.WriteLine("EPIC FAIL: {0}", exception);
Logger.Fatal(exception, "EPIC FAIL: " + exception.Message);

View file

@ -81,7 +81,7 @@
<Compile Include="Cache\CachedDictionary.cs" />
<Compile Include="Cache\ICached.cs" />
<Compile Include="Cache\ICachedDictionary.cs" />
<Compile Include="Cloud\SonarrCloudRequestBuilder.cs" />
<Compile Include="Cloud\RadarrCloudRequestBuilder.cs" />
<Compile Include="Composition\Container.cs" />
<Compile Include="Composition\ContainerBuilderBase.cs" />
<Compile Include="Composition\IContainer.cs" />

View file

@ -1,10 +1,10 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("radarr.tv")]
[assembly: AssemblyProduct("NzbDrone")]
[assembly: AssemblyVersion("0.1.0.*")]
[assembly: AssemblyCompany("radarr.video")]
[assembly: AssemblyProduct("Radarr")]
[assembly: AssemblyVersion("10.0.0.*")]
[assembly: AssemblyCopyright("GNU General Public v3")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Text.RegularExpressions;
using FluentAssertions;
using NUnit.Framework;
@ -23,6 +23,8 @@ public class QualityTagFixture : CoreTest
[TestCase("S_WEBdL", TagType.Source, Source.WEBDL)]
[TestCase("S_CAM", TagType.Source, Source.CAM)]
[TestCase("L_English", TagType.Language, Language.English)]
[TestCase("L_Italian", TagType.Language, Language.Italian)]
[TestCase("L_iTa", TagType.Language, Language.Italian)]
[TestCase("L_germaN", TagType.Language, Language.German)]
[TestCase("E_Director", TagType.Edition, "director")]
[TestCase("E_RX_Director('?s)?", TagType.Edition, "director('?s)?", TagModifier.Regex)]

View file

@ -19,6 +19,7 @@ public class NzbgetFixture : DownloadClientFixtureBase<Nzbget>
private NzbgetQueueItem _queued;
private NzbgetHistoryItem _failed;
private NzbgetHistoryItem _completed;
private Dictionary<string, string> _configItems;
[SetUp]
public void Setup()
@ -80,13 +81,17 @@ public void Setup()
DownloadRate = 7000000
});
var configItems = new Dictionary<string, string>();
configItems.Add("Category1.Name", "movie");
configItems.Add("Category1.DestDir", @"/remote/mount/movie");
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetVersion(It.IsAny<NzbgetSettings>()))
.Returns("14.0");
_configItems = new Dictionary<string, string>();
_configItems.Add("Category1.Name", "movie");
_configItems.Add("Category1.DestDir", @"/remote/mount/movie");
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>()))
.Returns(configItems);
.Returns(_configItems);
}
protected void GivenFailedDownload()
@ -386,5 +391,18 @@ public void should_test_version(string version, bool expected)
error.IsValid.Should().Be(expected);
}
[TestCase("0", false)]
[TestCase("1", true)]
[TestCase(" 7", false)]
[TestCase("5000000", false)]
public void should_test_keephistory(string keephistory, bool expected)
{
_configItems["KeepHistory"] = keephistory;
var error = Subject.Test();
error.IsValid.Should().Be(expected);
}
}
}

View file

@ -54,11 +54,11 @@ public void Setup()
protected void GivenSuccessfulDownload()
{
Mocker.GetMock<IRTorrentProxy>()
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<RTorrentSettings>()))
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<RTorrentSettings>()))
.Callback(PrepareClientToReturnCompletedItem);
Mocker.GetMock<IRTorrentProxy>()
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<RTorrentSettings>()))
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<RTorrentSettings>()))
.Callback(PrepareClientToReturnCompletedItem);
@ -123,4 +123,4 @@ public void Download_should_return_unique_id()
id.Should().NotBeNullOrEmpty();
}
}
}
}

View file

@ -0,0 +1,43 @@
using System.IO;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class NzbValidationServiceFixture : CoreTest<NzbValidationService>
{
private byte[] GivenNzbFile(string name)
{
return File.ReadAllBytes(GetTestPath("Files/Nzbs/" + name + ".nzb"));
}
[Test]
public void should_throw_on_invalid_nzb()
{
var filename = "NotNzb";
var fileContent = GivenNzbFile(filename);
Assert.Throws<InvalidNzbException>(() => Subject.Validate(filename, fileContent));
}
[Test]
public void should_throw_when_no_files()
{
var filename = "NoFiles";
var fileContent = GivenNzbFile(filename);
Assert.Throws<InvalidNzbException>(() => Subject.Validate(filename, fileContent));
}
[Test]
public void should_validate_nzb()
{
var filename = "ValidNzb";
var fileContent = GivenNzbFile(filename);
Subject.Validate(filename, fileContent);
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nzb
PUBLIC '-//newzBin//DTD NZB 1.1//EN'
'http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd'>
<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
</nzb>

View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<fail>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[01/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.nfo&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="12053" number="1">ZQ9h749E781168561i4J0Q6-01m6Q3185@2894t-767038L.Pg7769</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[02/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.par2&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="26932" number="1">405Z5Y4066010l377VP1k6$U4873W933@f32Bs90575538201.pj54</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[03/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.r00&quot; yEnc (1/66)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793068" number="1">1x9894417$M.1s25279485O1s1Fi95Z1_18Z554u440@D1k0854_134551.0794144</segment>
<segment bytes="793234" number="2">48JYp$W18B2R1s2rI24EG7$907$r89875n60@8xK3374080716.115545M</segment>
<segment bytes="793346" number="3">0U93471uI59Y781x77Q8-4286308-4aU35$07-179z@u90567568251.4zgUW968</segment>
<segment bytes="793302" number="4">5119x6417a.s06F$1k46$2q89298-C0@G7C-7811268.bK9x00B</segment>
<segment bytes="793289" number="5">B8$1_h0b64Z14-16_O6$ESw481L421n9agj7731k@414.473581-K$4.0Zd5A</segment>
<segment bytes="793380" number="6">O-4731$tn71v05623J9GT.yc22O975111dR01r58065p@Da1G9L33q74h3095.5X240</segment>
<segment bytes="793253" number="7">d9R03J$07w75945Z556197z50F0w.0-5.x9$58311S@J0-v50033110.4a440EYJ</segment>
<segment bytes="793317" number="8">05e650149.5r1Hk$E0Bko7G5B.1107mz8l17PS8F@vr816$S6T19245w.042B9</segment>
<segment bytes="793060" number="9">245Xy0w4o$tN6428321b.n1816Q1n95bE0816Y@q-qv7E12k.F3672H.16E19</segment>
<segment bytes="793266" number="10">H681i185g64H23101kP125z41101O91P384l@E9n597k05j798D94X.2ezz1K</segment>
<segment bytes="793223" number="11">T18.6136787.HLJ806.8$Si49m0459445101Z15-5@b80M7.788598D.gXu201cR</segment>
<segment bytes="793346" number="12">Vdl8H243Go28j1o865772039416v2@090a20-v365N5S7qf.G225s6</segment>
<segment bytes="793305" number="13">S9769892v956069345.0TN.i05R@I04825Gt2706N.BAj1DT1T</segment>
<segment bytes="793187" number="14">041800q6F28q44365799m5CQ4D43895@1Bf6268z_Q20F.045JXl</segment>
<segment bytes="793119" number="15">1c-e034z4l$9K45i44218ss25$X5_5R-1i76$40-71P@Xt691t8B686Fgv.VBSl</segment>
<segment bytes="793262" number="16">76l441W.R146a5368ed02cp_44171410hT.l@Z98.k.70X9c.5mZ1w49</segment>
<segment bytes="793221" number="17">12D035G5745-KO43wZ9920ttr1338@V7d871S2-t04t8520.uQ18</segment>
<segment bytes="793221" number="18">59V4O77211HA1f5T8h1-53952zV-55294K4M04v@kS878H3g4z.B5561.L330519</segment>
<segment bytes="793087" number="19">44-yi1-79$751944J7094$y7-y49994440d86cSn@5C82v-1O9N.wk8wMb6</segment>
<segment bytes="793375" number="20">oF7Wj3$Ydh7e030oD4.e81JM464O791495lJ@Pm058Qt4-G8Wv.T1i1a6O1</segment>
<segment bytes="793293" number="21">1T7_71M9d10F2.5953VP.11.4h75L@5049bBn384.14Ms</segment>
<segment bytes="793266" number="22">u8601765028G662749SD41j0m57651Zq70u1@J5281423406375.z.6PDSx57</segment>
<segment bytes="793376" number="23">XY0476$R87Y16g2n45OO335541589V140R026j@y2q9296x7f23C.sqK71b9</segment>
<segment bytes="793363" number="24">X7N3440l08B9T5940na4Ls397-T2.P5M12241525J57@r44O419p594M6G4I.d66RQ1</segment>
<segment bytes="793203" number="25">p4148978k45.t88w2K9886H4223y5553T7$7p287TN@N8e1T98b_0.mo55a14G</segment>
<segment bytes="793039" number="26">50U0a9iP07$A66010-51h55w386f@c$42$S96V57F5u0Y.6UDV35D</segment>
<segment bytes="792999" number="27">FnKN4n2749v958xa36J2570506414D293S@8H1A1X490$z3bv.ut6KQ4N</segment>
<segment bytes="793316" number="28">q4$d0$X8x6rm85m0Ewh307m255N@t2C7484zq870u.1RLndQ</segment>
<segment bytes="793238" number="29">364U4342$5I242404oH90-1W3c0t16705057m650Cq9f@K32rE5297347130W.UNs8evbH</segment>
<segment bytes="793461" number="30">M3081U097-r06Y.yy9-1A538001B27f@L2834Y80c7b1075.Dy150</segment>
<segment bytes="793326" number="31">189585554.NS66E5D840N4Yq5m07NC1n@51L0393057L528n.k1Mc3j0S</segment>
<segment bytes="793380" number="32">189048V505q89216C149I5f$53x-T@0V9i8n7o95.I.Z1lBJ5</segment>
<segment bytes="793205" number="33">5-L.555$139r45100-S23-59859@54844694q2.3EY9b</segment>
<segment bytes="793258" number="34">641655313y0.Z002L0g39AZ11716U-uX015PI5.v6y@veS44H89Js91903K8.P3MAvk4k</segment>
<segment bytes="793229" number="35">1C8f-yz-U-b20.610.0P1M-6Z5418i229160865010s1@M7l210D48Nc.nB0sPmi</segment>
<segment bytes="793169" number="36">0653$L0.58749-1U_1PS95-1h9gQ145@0117y0-1x1p-h94.za18yc5</segment>
<segment bytes="793109" number="37">77-Mo3-a6514904987865.K0W710G4HB9237@501F7910J6j50-Bh.6cHx1</segment>
<segment bytes="793350" number="38">m4I47082655rz$b7P751u9W679475F.89p@f.o.XZv5O7y.855rgXX</segment>
<segment bytes="793193" number="39">f075$y56E57d.t11787.0$6D155735M_w89-Y57q2@x0t5H91021wZ52Vh.1h7vabU</segment>
<segment bytes="793251" number="40">H7U1331Ad7718$Y69T-q3w4$l247HV49s985J@vi800i0004p.YD5oK</segment>
<segment bytes="793106" number="41">9nr786955Ker.M583315CoJ1-W65a817-704@IN-wU12$M1E0g466.5sMJ3</segment>
<segment bytes="793006" number="42">0.3R9mN.n2_V086N0-4.Z5gAgZo@ey3G316U382o537.f51Ed5B</segment>
<segment bytes="793198" number="43">l106Z1-N411r7j44197l628r.b5Uwc55@k4-Cl_n5xc.1B.xZbNm</segment>
<segment bytes="793070" number="44">A91LT1X591x81.TI4130N$555A57q0@L70-p5qa50.40GB</segment>
<segment bytes="793457" number="45">V5$765JR6503w0-K63099R615736843G$Qj0ev@mz776wM86445N0.4I56ne</segment>
<segment bytes="793109" number="46">A86H2P415S689$568152-025O45V@s079644915.Dd57p0</segment>
<segment bytes="793169" number="47">31x5o36q14y9554L42882X0Q10e360Z64W4K9Onx38D@5g1509788414q.Y8wib</segment>
<segment bytes="793219" number="48">b$6795157EX1044V964e14-Y9E68614O94C@4061937876$f5.6.19tV</segment>
<segment bytes="793349" number="49">D00v8X$b80m93181273J-g076Qj2p79867v5d9689Rb2@r0592.v900.j43E050E</segment>
<segment bytes="793228" number="50">Tf78L4e535.o86PK0S.M2R3-66012814z@q-5j89Y29J214Y902.53Ra0f</segment>
<segment bytes="793353" number="51">7i01.23411-lQW0212-Er260e9.N5e256jx243EX@91-T.15v40K5Hj.Fo1f</segment>
<segment bytes="793290" number="52">3A$H7m63$i595.4713vv0A4$A7Lk7Jsq@0cM0Tw4107f.B520.q5Z91</segment>
<segment bytes="793364" number="53">j572m$3h87LS$37167Wp10k41541.T779-Fn@V53C11045619xJ.52.0PnnX4v5</segment>
<segment bytes="793506" number="54">A.2d4599a720rk2IB32h0X523MjTL415v89706-7Z45y@R4746-B106358.t3g62r4</segment>
<segment bytes="793118" number="55">5q6100961jM-G9F7t755x366zxc102M1SdMF@7394521p651X1I.AL05545a</segment>
<segment bytes="793137" number="56">04e851111$12u2213-80VR133125B@7x8865M4hQ9$5.1N345x</segment>
<segment bytes="793180" number="57">K2476D3600-73B4W363$008s888980421f27125V$q0@0Zc0a56-m7550.1637vAr1</segment>
<segment bytes="793214" number="58">0306u425024v448ZeCE3Q9825m9th1858@5648018-H0.2k7J4.12k0B</segment>
<segment bytes="793274" number="59">220u4SK433564Cr2l004t0wP888545779g@19j360863S$55559m.70V7Ndr</segment>
<segment bytes="793339" number="60">5u1q051C5Qq8Z9Iy$Z.5.1510NY.S2565n@7m.5-09$z235p74.8kW5</segment>
<segment bytes="793297" number="61">6F472C8nh2621_X0C1093P7n39643b5p2f76s60r@1T55203qQY6.wZml1Vb</segment>
<segment bytes="793351" number="62">5qC4568844767324-o8i05983-0f.n4.y.OBZ41f@q36B50684KU66.0R1784</segment>
<segment bytes="793257" number="63">4P0g470-F59307aDf.JF070Xx959648dO3y00463J6s@71P$D961$C0.11.I096sQ</segment>
<segment bytes="793277" number="64">z5kod75077z01w11-A5h.wiG550.J5-p756$81.Db@5l01K49h3K.Ok4R5512</segment>
<segment bytes="793292" number="65">F3JX28.B8h90T0075-08001X5w611V071@D75X9263$6$9f.OT050p5Z</segment>
<segment bytes="83545" number="66">2B8sT.A650z101514671183y47977219.M4211xYp@0b0021p736BX92.B0lSm4J3</segment>
</segments>
</file>
<file date="1504571157" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[46/46] - &quot;sample-Series.Title.s04e06.720p.hdtv.x264-killers.vol31+04.par2&quot; yEnc (1/3)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793316" number="1">16ND-8I545Pq-s107t0h07g8908870711@K401476783.5.0mFs1</segment>
<segment bytes="793409" number="2">iYdZ2D11089F310711.ci-O7O4KG03@260c03388O84Kd.GCEgv</segment>
<segment bytes="6784" number="3">r63cDD59Mg1c95738Sn75085O4X7823V1@16V6-b87O21S1937O.lw17o1VS</segment>
</segments>
</file>
</fail>

View file

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nzb
PUBLIC '-//newzBin//DTD NZB 1.1//EN'
'http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd'>
<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[01/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.nfo&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="12053" number="1">ZQ9h749E781168561i4J0Q6-01m6Q3185@2894t-767038L.Pg7769</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[02/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.par2&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="26932" number="1">405Z5Y4066010l377VP1k6$U4873W933@f32Bs90575538201.pj54</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[03/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.r00&quot; yEnc (1/66)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793068" number="1">1x9894417$M.1s25279485O1s1Fi95Z1_18Z554u440@D1k0854_134551.0794144</segment>
<segment bytes="793234" number="2">48JYp$W18B2R1s2rI24EG7$907$r89875n60@8xK3374080716.115545M</segment>
<segment bytes="793346" number="3">0U93471uI59Y781x77Q8-4286308-4aU35$07-179z@u90567568251.4zgUW968</segment>
<segment bytes="793302" number="4">5119x6417a.s06F$1k46$2q89298-C0@G7C-7811268.bK9x00B</segment>
<segment bytes="793289" number="5">B8$1_h0b64Z14-16_O6$ESw481L421n9agj7731k@414.473581-K$4.0Zd5A</segment>
<segment bytes="793380" number="6">O-4731$tn71v05623J9GT.yc22O975111dR01r58065p@Da1G9L33q74h3095.5X240</segment>
<segment bytes="793253" number="7">d9R03J$07w75945Z556197z50F0w.0-5.x9$58311S@J0-v50033110.4a440EYJ</segment>
<segment bytes="793317" number="8">05e650149.5r1Hk$E0Bko7G5B.1107mz8l17PS8F@vr816$S6T19245w.042B9</segment>
<segment bytes="793060" number="9">245Xy0w4o$tN6428321b.n1816Q1n95bE0816Y@q-qv7E12k.F3672H.16E19</segment>
<segment bytes="793266" number="10">H681i185g64H23101kP125z41101O91P384l@E9n597k05j798D94X.2ezz1K</segment>
<segment bytes="793223" number="11">T18.6136787.HLJ806.8$Si49m0459445101Z15-5@b80M7.788598D.gXu201cR</segment>
<segment bytes="793346" number="12">Vdl8H243Go28j1o865772039416v2@090a20-v365N5S7qf.G225s6</segment>
<segment bytes="793305" number="13">S9769892v956069345.0TN.i05R@I04825Gt2706N.BAj1DT1T</segment>
<segment bytes="793187" number="14">041800q6F28q44365799m5CQ4D43895@1Bf6268z_Q20F.045JXl</segment>
<segment bytes="793119" number="15">1c-e034z4l$9K45i44218ss25$X5_5R-1i76$40-71P@Xt691t8B686Fgv.VBSl</segment>
<segment bytes="793262" number="16">76l441W.R146a5368ed02cp_44171410hT.l@Z98.k.70X9c.5mZ1w49</segment>
<segment bytes="793221" number="17">12D035G5745-KO43wZ9920ttr1338@V7d871S2-t04t8520.uQ18</segment>
<segment bytes="793221" number="18">59V4O77211HA1f5T8h1-53952zV-55294K4M04v@kS878H3g4z.B5561.L330519</segment>
<segment bytes="793087" number="19">44-yi1-79$751944J7094$y7-y49994440d86cSn@5C82v-1O9N.wk8wMb6</segment>
<segment bytes="793375" number="20">oF7Wj3$Ydh7e030oD4.e81JM464O791495lJ@Pm058Qt4-G8Wv.T1i1a6O1</segment>
<segment bytes="793293" number="21">1T7_71M9d10F2.5953VP.11.4h75L@5049bBn384.14Ms</segment>
<segment bytes="793266" number="22">u8601765028G662749SD41j0m57651Zq70u1@J5281423406375.z.6PDSx57</segment>
<segment bytes="793376" number="23">XY0476$R87Y16g2n45OO335541589V140R026j@y2q9296x7f23C.sqK71b9</segment>
<segment bytes="793363" number="24">X7N3440l08B9T5940na4Ls397-T2.P5M12241525J57@r44O419p594M6G4I.d66RQ1</segment>
<segment bytes="793203" number="25">p4148978k45.t88w2K9886H4223y5553T7$7p287TN@N8e1T98b_0.mo55a14G</segment>
<segment bytes="793039" number="26">50U0a9iP07$A66010-51h55w386f@c$42$S96V57F5u0Y.6UDV35D</segment>
<segment bytes="792999" number="27">FnKN4n2749v958xa36J2570506414D293S@8H1A1X490$z3bv.ut6KQ4N</segment>
<segment bytes="793316" number="28">q4$d0$X8x6rm85m0Ewh307m255N@t2C7484zq870u.1RLndQ</segment>
<segment bytes="793238" number="29">364U4342$5I242404oH90-1W3c0t16705057m650Cq9f@K32rE5297347130W.UNs8evbH</segment>
<segment bytes="793461" number="30">M3081U097-r06Y.yy9-1A538001B27f@L2834Y80c7b1075.Dy150</segment>
<segment bytes="793326" number="31">189585554.NS66E5D840N4Yq5m07NC1n@51L0393057L528n.k1Mc3j0S</segment>
<segment bytes="793380" number="32">189048V505q89216C149I5f$53x-T@0V9i8n7o95.I.Z1lBJ5</segment>
<segment bytes="793205" number="33">5-L.555$139r45100-S23-59859@54844694q2.3EY9b</segment>
<segment bytes="793258" number="34">641655313y0.Z002L0g39AZ11716U-uX015PI5.v6y@veS44H89Js91903K8.P3MAvk4k</segment>
<segment bytes="793229" number="35">1C8f-yz-U-b20.610.0P1M-6Z5418i229160865010s1@M7l210D48Nc.nB0sPmi</segment>
<segment bytes="793169" number="36">0653$L0.58749-1U_1PS95-1h9gQ145@0117y0-1x1p-h94.za18yc5</segment>
<segment bytes="793109" number="37">77-Mo3-a6514904987865.K0W710G4HB9237@501F7910J6j50-Bh.6cHx1</segment>
<segment bytes="793350" number="38">m4I47082655rz$b7P751u9W679475F.89p@f.o.XZv5O7y.855rgXX</segment>
<segment bytes="793193" number="39">f075$y56E57d.t11787.0$6D155735M_w89-Y57q2@x0t5H91021wZ52Vh.1h7vabU</segment>
<segment bytes="793251" number="40">H7U1331Ad7718$Y69T-q3w4$l247HV49s985J@vi800i0004p.YD5oK</segment>
<segment bytes="793106" number="41">9nr786955Ker.M583315CoJ1-W65a817-704@IN-wU12$M1E0g466.5sMJ3</segment>
<segment bytes="793006" number="42">0.3R9mN.n2_V086N0-4.Z5gAgZo@ey3G316U382o537.f51Ed5B</segment>
<segment bytes="793198" number="43">l106Z1-N411r7j44197l628r.b5Uwc55@k4-Cl_n5xc.1B.xZbNm</segment>
<segment bytes="793070" number="44">A91LT1X591x81.TI4130N$555A57q0@L70-p5qa50.40GB</segment>
<segment bytes="793457" number="45">V5$765JR6503w0-K63099R615736843G$Qj0ev@mz776wM86445N0.4I56ne</segment>
<segment bytes="793109" number="46">A86H2P415S689$568152-025O45V@s079644915.Dd57p0</segment>
<segment bytes="793169" number="47">31x5o36q14y9554L42882X0Q10e360Z64W4K9Onx38D@5g1509788414q.Y8wib</segment>
<segment bytes="793219" number="48">b$6795157EX1044V964e14-Y9E68614O94C@4061937876$f5.6.19tV</segment>
<segment bytes="793349" number="49">D00v8X$b80m93181273J-g076Qj2p79867v5d9689Rb2@r0592.v900.j43E050E</segment>
<segment bytes="793228" number="50">Tf78L4e535.o86PK0S.M2R3-66012814z@q-5j89Y29J214Y902.53Ra0f</segment>
<segment bytes="793353" number="51">7i01.23411-lQW0212-Er260e9.N5e256jx243EX@91-T.15v40K5Hj.Fo1f</segment>
<segment bytes="793290" number="52">3A$H7m63$i595.4713vv0A4$A7Lk7Jsq@0cM0Tw4107f.B520.q5Z91</segment>
<segment bytes="793364" number="53">j572m$3h87LS$37167Wp10k41541.T779-Fn@V53C11045619xJ.52.0PnnX4v5</segment>
<segment bytes="793506" number="54">A.2d4599a720rk2IB32h0X523MjTL415v89706-7Z45y@R4746-B106358.t3g62r4</segment>
<segment bytes="793118" number="55">5q6100961jM-G9F7t755x366zxc102M1SdMF@7394521p651X1I.AL05545a</segment>
<segment bytes="793137" number="56">04e851111$12u2213-80VR133125B@7x8865M4hQ9$5.1N345x</segment>
<segment bytes="793180" number="57">K2476D3600-73B4W363$008s888980421f27125V$q0@0Zc0a56-m7550.1637vAr1</segment>
<segment bytes="793214" number="58">0306u425024v448ZeCE3Q9825m9th1858@5648018-H0.2k7J4.12k0B</segment>
<segment bytes="793274" number="59">220u4SK433564Cr2l004t0wP888545779g@19j360863S$55559m.70V7Ndr</segment>
<segment bytes="793339" number="60">5u1q051C5Qq8Z9Iy$Z.5.1510NY.S2565n@7m.5-09$z235p74.8kW5</segment>
<segment bytes="793297" number="61">6F472C8nh2621_X0C1093P7n39643b5p2f76s60r@1T55203qQY6.wZml1Vb</segment>
<segment bytes="793351" number="62">5qC4568844767324-o8i05983-0f.n4.y.OBZ41f@q36B50684KU66.0R1784</segment>
<segment bytes="793257" number="63">4P0g470-F59307aDf.JF070Xx959648dO3y00463J6s@71P$D961$C0.11.I096sQ</segment>
<segment bytes="793277" number="64">z5kod75077z01w11-A5h.wiG550.J5-p756$81.Db@5l01K49h3K.Ok4R5512</segment>
<segment bytes="793292" number="65">F3JX28.B8h90T0075-08001X5w611V071@D75X9263$6$9f.OT050p5Z</segment>
<segment bytes="83545" number="66">2B8sT.A650z101514671183y47977219.M4211xYp@0b0021p736BX92.B0lSm4J3</segment>
</segments>
</file>
<file date="1504571157" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[46/46] - &quot;sample-Series.Title.s04e06.720p.hdtv.x264-killers.vol31+04.par2&quot; yEnc (1/3)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793316" number="1">16ND-8I545Pq-s107t0h07g8908870711@K401476783.5.0mFs1</segment>
<segment bytes="793409" number="2">iYdZ2D11089F310711.ci-O7O4KG03@260c03388O84Kd.GCEgv</segment>
<segment bytes="6784" number="3">r63cDD59Mg1c95738Sn75085O4X7823V1@16V6-b87O21S1937O.lw17o1VS</segment>
</segments>
</file>
</nzb>

View file

@ -30,7 +30,7 @@ protected void UseRealHttp()
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<NLog.Logger>()));
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), TestLogger));
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder());
Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
}
//Used for tests that rely on parsing working correctly.

View file

@ -1,4 +1,4 @@
using Moq;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
@ -24,6 +24,64 @@ public void Setup()
};
}
private void GivenOldFeedFormat()
{
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/torrents/rss?u=snip;tp=snip;3;80;93;37;download" }
};
}
private void GivenNewFeedFormat()
{
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/t.rss?u=USERID;tp=APIKEY;3;80;93;37;download" }
};
}
private void GivenFeedNoDownloadFormat()
{
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/t.rss?u=USERID;tp=APIKEY;3;80;93;37" }
};
}
[Test]
public void should_validate_old_feed_format()
{
GivenOldFeedFormat();
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeTrue();
}
[Test]
public void should_validate_new_feed_format()
{
GivenNewFeedFormat();
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeTrue();
}
[Test]
public void should_not_validate_bad_format()
{
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeFalse();
}
[Test]
public void should_not_validate_no_download_format()
{
GivenFeedNoDownloadFormat();
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeFalse();
}
[Test]
public void should_parse_recent_feed_from_IPTorrents()
{

View file

@ -37,15 +37,6 @@ private void GivenExistingFileSize(long bytes)
}
private void GivenImageFileCorrupt(bool corrupt)
{
GivenFileExistsOnDisk();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.IsValidGDIPlusImage(It.IsAny<string>()))
.Returns(!corrupt);
}
[Test]
public void should_return_false_if_file_not_exists()
{
@ -61,21 +52,11 @@ public void should_return_false_if_file_exists_but_diffrent_size()
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse();
}
[Test]
public void should_return_false_if_file_exists_and_same_size_and_corrupt()
{
GivenExistingFileSize(100);
GivenImageFileCorrupt(true);
_httpResponse.Headers.ContentLength = 100;
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse();
}
[Test]
public void should_return_true_if_file_exists_and_same_size_and_not_corrupt()
{
GivenExistingFileSize(100);
GivenImageFileCorrupt(false);
_httpResponse.Headers.ContentLength = 100;
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeTrue();
}

View file

@ -8,6 +8,7 @@
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Events;
@ -28,6 +29,18 @@ public void Setup()
.With(v => v.Id = 2)
.With(v => v.Images = new List<MediaCover.MediaCover> { new MediaCover.MediaCover(MediaCoverTypes.Poster, "") })
.Build();
Mocker.GetMock<IMovieService>().Setup(m => m.GetMovie(It.Is<int>(id => id == _movie.Id))).Returns(_movie);
}
private void ExecuteAndVerifyCommand(Movie movie)
{
Subject.HandleAsync(new MovieUpdatedEvent(movie));
Mocker.GetMock<IManageCommandQueue>()
.Verify(v => v.Push(It.Is<EnsureMediaCoversCommand>(c => c.MovieId == movie.Id), It.IsAny<CommandPriority>(), It.IsAny<CommandTrigger>()), Times.Once());
Subject.Execute(new EnsureMediaCoversCommand(movie.Id));
}
[Test]
@ -76,7 +89,7 @@ public void should_resize_covers_if_main_downloaded()
.Setup(v => v.FileExists(It.IsAny<string>()))
.Returns(true);
Subject.HandleAsync(new MovieUpdatedEvent(_movie));
ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@ -93,7 +106,7 @@ public void should_resize_covers_if_missing()
.Setup(v => v.FileExists(It.IsAny<string>()))
.Returns(false);
Subject.HandleAsync(new MovieUpdatedEvent(_movie));
ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@ -114,7 +127,7 @@ public void should_not_resize_covers_if_exists()
.Setup(v => v.GetFileSize(It.IsAny<string>()))
.Returns(1000);
Subject.HandleAsync(new MovieUpdatedEvent(_movie));
ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Never());
@ -135,7 +148,7 @@ public void should_resize_covers_if_existing_is_empty()
.Setup(v => v.GetFileSize(It.IsAny<string>()))
.Returns(0);
Subject.HandleAsync(new MovieUpdatedEvent(_movie));
ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@ -156,7 +169,7 @@ public void should_log_error_if_resize_failed()
.Setup(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()))
.Throws<ApplicationException>();
Subject.HandleAsync(new MovieUpdatedEvent(_movie));
ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));

View file

@ -53,7 +53,7 @@ public void get_info()
info.AudioBitrate.Should().Be(128000);
info.AudioChannels.Should().Be(2);
info.AudioLanguages.Should().Be("English");
info.AudioAdditionalFeatures.Should().Be("");
info.AudioAdditionalFeatures.Should().Be("LC");
info.Height.Should().Be(320);
info.RunTime.Seconds.Should().Be(10);
info.ScanType.Should().Be("Progressive");
@ -90,7 +90,7 @@ public void get_info_unicode()
info.AudioBitrate.Should().Be(128000);
info.AudioChannels.Should().Be(2);
info.AudioLanguages.Should().Be("English");
info.AudioAdditionalFeatures.Should().Be("");
info.AudioAdditionalFeatures.Should().Be("LC");
info.Height.Should().Be(320);
info.RunTime.Seconds.Should().Be(10);
info.ScanType.Should().Be("Progressive");

View file

@ -195,6 +195,7 @@
<Compile Include="Download\DownloadClientTests\DownloadStationTests\UsenetDownloadStationFixture.cs" />
<Compile Include="Download\DownloadServiceFixture.cs" />
<Compile Include="Download\FailedDownloadServiceFixture.cs" />
<Compile Include="Download\NzbValidationServiceFixture.cs" />
<Compile Include="Download\Pending\PendingReleaseServiceTests\PendingReleaseServiceFixture.cs" />
<Compile Include="Download\Pending\PendingReleaseServiceTests\RemovePendingFixture.cs" />
<Compile Include="Download\Pending\PendingReleaseServiceTests\RemoveRejectedFixture.cs" />
@ -553,6 +554,15 @@
</Content>
</ItemGroup>
<ItemGroup>
<None Include="Files\Nzbs\NoFiles.nzb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\Nzbs\NotNzb.nzb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\Nzbs\ValidNzb.nzb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\TestArchive.tar.gz">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@ -584,4 +594,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View file

@ -49,6 +49,7 @@ public class LanguageParserFixture : CoreTest
[TestCase("The Danish Girl 2015", Language.English)]
[TestCase("Nocturnal Animals (2016) MULTi VFQ English [1080p] BluRay x264-PopHD", Language.English, Language.French)]
[TestCase("Wonder.Woman.2017.720p.BluRay.DD5.1.x264-TayTO.CZ-FTU", Language.Czech)]
[TestCase("Fantastic.Beasts.The.Crimes.Of.Grindelwald.2018.2160p.WEBRip.x265.10bit.HDR.DD5.1-GASMASK", Language.English)]
public void should_parse_language(string postTitle, params Language[] languages)
{
var movieInfo = Parser.Parser.ParseMovieTitle(postTitle, true);
@ -65,6 +66,8 @@ public void should_parse_language(string postTitle, params Language[] languages)
[TestCase("2 Broke Girls - S01E01 - Pilot.en.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot.eng.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot.sub", Language.Unknown)]
[TestCase("2 Broke Girls - S01E01 - Pilot.eng.forced.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot-eng-forced.sub", Language.English)]
public void should_parse_subtitle_language(string fileName, Language language)
{
var result = LanguageParser.ParseSubtitleLanguage(fileName);

View file

@ -43,7 +43,8 @@ public void should_remove_accents_from_title()
[TestCase("To.Live.and.Die.in.L.A.1985.1080p.BluRay", "To Live and Die in L.A.")]
[TestCase("A.I.Artificial.Intelligence.(2001)", "A.I. Artificial Intelligence")]
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
[TestCase("www.Torrenting.com - Revenge.2008.720p.X264-DIMENSION", "Revenge")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
[TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Soldat James")]
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", "Passengers")]
@ -52,7 +53,10 @@ public void should_remove_accents_from_title()
[TestCase("Mission Impossible: Rogue Nation (2015)<29>[XviD - Ita Ac3 - SoftSub Ita]azione, spionaggio, thriller *Prima Visione* Team mulnic Tom Cruise", "Mission Impossible Rogue Nation")]
[TestCase("Scary.Movie.2000.FRENCH..BluRay.-AiRLiNE", "Scary Movie")]
[TestCase("My Movie 1999 German Bluray", "My Movie")]
public void should_parse_movie_title(string postTitle, string title)
[TestCase("Leaving Jeruselem by Railway (1897) [DVD].mp4", "Leaving Jeruselem by Railway")]
[TestCase("Climax.2018.1080p.AMZN.WEB-DL.DD5.1.H.264-NTG", "Climax")]
[TestCase("Movie.Title.Imax.2018.1080p.AMZN.WEB-DL.DD5.1.H.264-NTG", "Movie Title")]
public void should_parse_movie_title(string postTitle, string title)
{
Parser.Parser.ParseMovieTitle(postTitle, true).MovieTitle.Should().Be(title);
}
@ -66,6 +70,7 @@ public void should_parse_movie_folder_name(string postTitle, string title)
[TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", 2016)]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)]
[TestCase("Leaving Jeruselem by Railway (1897) [DVD].mp4", 1897)]
public void should_parse_movie_year(string postTitle, int year)
{
Parser.Parser.ParseMovieTitle(postTitle, false).Year.Should().Be(year);
@ -108,6 +113,7 @@ public void should_parse_movie_year(string postTitle, int year)
[TestCase("My.Movie.GERMAN.Extended.Cut.2016", "Extended Cut")]
[TestCase("My.Movie.GERMAN.Extended.Cut", "Extended Cut")]
[TestCase("Mission Impossible: Rogue Nation 2012 Bluray", "")]
[TestCase("Loving.Pablo.2018.TS.FRENCH.MD.x264-DROGUERiE","")]
public void should_parse_edition(string postTitle, string edition)
{
var parsed = Parser.Parser.ParseMovieTitle(postTitle, true);

View file

@ -207,6 +207,9 @@ public void should_parse_webdl2160p_quality(string title, bool proper)
[TestCase("[Elysium]Lucky.Star.01(BD.720p.AAC.DA)[0BB96AD8].mkv", false)]
[TestCase("Battlestar.Galactica.S01E01.33.720p.HDDVD.x264-SiNNERS.mkv", false)]
[TestCase("The.Expanse.S01E07.RERIP.720p.BluRay.x264-DEMAND", true)]
[TestCase("John.Carpenter.Live.Retrospective.2016.2018.720p.MBluRay.x264-CRUELTY.mkv", false)]
[TestCase("Heart.Live.In.Atlantic.City.2019.720p.MBLURAY.x264-MBLURAYFANS.mkv", false)]
[TestCase("Opeth.Garden.Of.The.Titans.Live.At.Red.Rocks.Amphitheatre.2017.720p.MBluRay.x264-TREBLE.mkv", false)]
public void should_parse_bluray720p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R720P);
@ -221,6 +224,9 @@ public void should_parse_bluray720p_quality(string title, bool proper)
[TestCase("[Zurako] Log Horizon - 01 - The Apocalypse (BD 1080p AAC) [7AE12174].mkv", false)]
[TestCase("WEEDS.S03E01-06.DUAL.1080p.Blu-ray.AC3.-HELLYWOOD.avi", false)]
[TestCase("[Coalgirls]_Durarara!!_01_(1920x1080_Blu-ray_FLAC)_[8370CB8F].mkv", false)]
[TestCase("John.Carpenter.Live.Retrospective.2016.2018.1080p.MBluRay.x264-CRUELTY.mkv", false)]
[TestCase("Heart.Live.In.Atlantic.City.2019.1080p.MBLURAY.x264-MBLURAYFANS.mkv", false)]
[TestCase("Opeth.Garden.Of.The.Titans.Live.At.Red.Rocks.Amphitheatre.2017.1080p.MBluRay.x264-TREBLE.mkv", false)]
public void should_parse_bluray1080p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R1080P);

View file

@ -26,6 +26,9 @@ public class ReleaseGroupParserFixture : CoreTest
[TestCase("[ www.Torrenting.com ] - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "DIMENSION")]
[TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "2HD")]
[TestCase("7s-atlantis-s02e01-720p.mkv", null)]
[TestCase("The.Middle.720p.HEVC.x265-MeGusta-Pre", "MeGusta")]
[TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-postbot", "NTb")]
[TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-xpost", "NTb")]
//[TestCase("", "")]
public void should_parse_release_group(string title, string expected)
{

View file

@ -20,12 +20,12 @@ public UserRepository(IMainDatabase database, IEventAggregator eventAggregator)
public User FindUser(string username)
{
return Query.Where(u => u.Username == username).SingleOrDefault();
return Query(q => q.Where(u => u.Username == username).SingleOrDefault());
}
public User FindUser(Guid identifier)
{
return Query.Where(u => u.Identifier == identifier).SingleOrDefault();
return Query(q => q.Where(u => u.Identifier == identifier).SingleOrDefault());
}
}
}

View file

@ -26,7 +26,11 @@ public MakeDatabaseBackup(Logger logger)
public void BackupDatabase(IDatabase database, string targetDirectory)
{
var sourceConnectionString = database.GetDataMapper().ConnectionString;
var sourceConnectionString = "";
using (var db = database.GetDataMapper())
{
sourceConnectionString = db.ConnectionString;
}
var backupConnectionStringBuilder = new SQLiteConnectionStringBuilder(sourceConnectionString);
backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource));

View file

@ -22,19 +22,19 @@ public BlacklistRepository(IMainDatabase database, IEventAggregator eventAggrega
public List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle)
{
return Query.Where(e => e.MovieId == movieId)
.AndWhere(e => e.SourceTitle.Contains(sourceTitle));
return Query(q => q.Where(e => e.MovieId == movieId)
.AndWhere(e => e.SourceTitle.Contains(sourceTitle)).ToList());
}
public List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash)
{
return Query.Where(e => e.MovieId == movieId)
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash));
return Query(q => q.Where(e => e.MovieId == movieId)
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash)).ToList());
}
public List<Blacklist> BlacklistedByMovie(int movieId)
{
return Query.Where(b => b.MovieId == movieId);
return Query(q => q.Where(b => b.MovieId == movieId).ToList());
}
protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec)

View file

@ -21,7 +21,7 @@ public ConfigRepository(IMainDatabase database, IEventAggregator eventAggregator
public Config Get(string key)
{
return Query.Where(c => c.Key == key).SingleOrDefault();
return Query(q => q.Where(c => c.Key == key).SingleOrDefault());
}
public Config Upsert(string key, string value)

View file

@ -83,7 +83,9 @@ private bool DoesItMatchWithoutMods(ParsedMovieInfo movieInfo)
var tuple = Value as (long, long)? ?? (0, 0);
return size > tuple.Item1 && size < tuple.Item2;
case TagType.Indexer:
#if !LIBRARY
return (movieInfo.ExtraInfo.GetValueOrDefault("IndexerFlags") as IndexerFlags?)?.HasFlag((IndexerFlags) Value) == true;
#endif
default:
return false;
}
@ -188,6 +190,7 @@ private void ParseRawMatch(Match match)
Value = Parser.LanguageParser.ParseLanguages(value).First();
break;
case "i":
#if !LIBRARY
TagType = TagType.Indexer;
var flagValues = Enum.GetValues(typeof(IndexerFlags));
@ -198,7 +201,7 @@ private void ParseRawMatch(Match match)
Value = flagValue;
break;
}
#endif
break;
case "g":
TagType = TagType.Size;

View file

@ -42,7 +42,10 @@ namespace NzbDrone.Core.Datastore
private readonly IDatabase _database;
private readonly IEventAggregator _eventAggregator;
protected IDataMapper DataMapper => _database.GetDataMapper();
protected IDataMapper DataMapper()
{
return _database.GetDataMapper();
}
public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
{
@ -50,26 +53,40 @@ public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
_eventAggregator = eventAggregator;
}
protected QueryBuilder<TModel> Query => AddJoinQueries(DataMapper.Query<TModel>());
protected T Query<T>(Func<QueryBuilder<TModel>, T> finalizeQuery)
{
using (var mapper = DataMapper())
{
var query = AddJoinQueries(mapper.Query<TModel>());
return finalizeQuery(query);
}
}
protected void Delete(Expression<Func<TModel, bool>> filter)
{
DataMapper.Delete(filter);
using (var db = DataMapper())
{
db.Delete(filter);
}
}
public IEnumerable<TModel> All()
{
return Query.ToList();
return Query((q => q.ToList()));
}
public int Count()
{
return DataMapper.Query<TModel>().GetRowCount();
using (var db = DataMapper())
{
return db.Query<TModel>().GetRowCount();
}
}
public TModel Get(int id)
{
var model = Query.Where(c => c.Id == id).SingleOrDefault();
TModel model = Query(q => q.Where(c => c.Id == id).SingleOrDefault());
if (model == null)
{
@ -83,7 +100,7 @@ public IEnumerable<TModel> Get(IEnumerable<int> ids)
{
var idList = ids.ToList();
var query = string.Format("Id IN ({0})", string.Join(",", idList));
var result = Query.Where(m => m.Id.In(idList)).ToList();
var result = Query(q => q.Where(m => m.Id.In(idList)).ToList());
//var result = Query.Where(query).ToList();
if (result.Count != idList.Count())
@ -111,7 +128,10 @@ public TModel Insert(TModel model)
throw new InvalidOperationException("Can't insert model with existing ID " + model.Id);
}
DataMapper.Insert(model);
using (var db = DataMapper())
{
db.Insert(model);
}
ModelCreated(model);
@ -125,7 +145,10 @@ public TModel Update(TModel model)
throw new InvalidOperationException("Can't update model with ID 0");
}
DataMapper.Update(model, c => c.Id == model.Id);
using (var db = DataMapper())
{
db.Update(model, c => c.Id == model.Id);
}
ModelUpdated(model);
@ -139,7 +162,7 @@ public void Delete(TModel model)
public void InsertMany(IList<TModel> models)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
using (var unitOfWork = new UnitOfWork(() => DataMapper()))
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
@ -154,7 +177,7 @@ public void InsertMany(IList<TModel> models)
public void UpdateMany(IList<TModel> models)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
using (var unitOfWork = new UnitOfWork(() => DataMapper()))
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
@ -192,12 +215,15 @@ public TModel Upsert(TModel model)
public void Delete(int id)
{
DataMapper.Delete<TModel>(c => c.Id == id);
using (var db = DataMapper())
{
db.Delete<TModel>(c => c.Id == id);
}
}
public void DeleteMany(IEnumerable<int> ids)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
using (var unitOfWork = new UnitOfWork(() => DataMapper()))
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
@ -214,7 +240,10 @@ public void DeleteMany(IEnumerable<int> ids)
public void Purge(bool vacuum = false)
{
DataMapper.Delete<TModel>(c => c.Id > -1);
using (var db = DataMapper())
{
db.Delete<TModel>(c => c.Id > -1);
}
if (vacuum)
{
Vacuum();
@ -238,20 +267,23 @@ public void SetFields(TModel model, params Expression<Func<TModel, object>>[] pr
throw new InvalidOperationException("Attempted to updated model without ID");
}
DataMapper.Update<TModel>()
.Where(c => c.Id == model.Id)
.ColumnsIncluding(properties)
.Entity(model)
.Execute();
using (var db = DataMapper())
{
db.Update<TModel>()
.Where(c => c.Id == model.Id)
.ColumnsIncluding(properties)
.Entity(model)
.Execute();
}
ModelUpdated(model);
}
public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec)
{
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize).ToList();
pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount();
pagingSpec.Records = Query(q => GetPagedQuery(q, pagingSpec).Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize).ToList());
pagingSpec.TotalRecords = Query(q => GetPagedQuery(q, pagingSpec).GetRowCount());
return pagingSpec;
}
@ -285,7 +317,7 @@ private void PublishModelEvent(TModel model, ModelAction action)
}
}
protected virtual QueryBuilder<TModel> AddJoinQueries(QueryBuilder<TModel> baseQuery)
protected virtual QueryBuilder<TActual> AddJoinQueries<TActual>(QueryBuilder<TActual> baseQuery)
{
return baseQuery;
}

View file

@ -35,7 +35,7 @@ private static string GetConnectionString(string dbPath)
var connectionBuilder = new SQLiteConnectionStringBuilder();
connectionBuilder.DataSource = dbPath;
connectionBuilder.CacheSize = (int)-10.Megabytes();
connectionBuilder.CacheSize = (int)-20000;
connectionBuilder.DateTimeKind = DateTimeKind.Utc;
connectionBuilder.JournalMode = OsInfo.IsOsx ? SQLiteJournalModeEnum.Truncate : SQLiteJournalModeEnum.Wal;
connectionBuilder.Pooling = true;

View file

@ -34,8 +34,11 @@ public Version Version
{
get
{
var version = _datamapperFactory().ExecuteScalar("SELECT sqlite_version()").ToString();
return new Version(version);
using (var db = _datamapperFactory())
{
var version = db.ExecuteScalar("SELECT sqlite_version()").ToString();
return new Version(version);
}
}
}
@ -44,7 +47,10 @@ public void Vacuum()
try
{
_logger.Info("Vacuuming {0} database", _databaseName);
_datamapperFactory().ExecuteNonQuery("Vacuum;");
using (var db = _datamapperFactory())
{
db.ExecuteNonQuery("Vacuum;");
}
_logger.Info("{0} database compressed", _databaseName);
}
catch (Exception e)

View file

@ -25,8 +25,9 @@ public UsenetBlackhole(IScanWatchFolder scanWatchFolder,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{
_scanWatchFolder = scanWatchFolder;

View file

@ -34,9 +34,10 @@ public UsenetDownloadStation(ISharedFolderResolver sharedFolderResolver,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger
)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{
_dsInfoProxy = dsInfoProxy;
_dsTaskProxy = dsTaskProxy;

View file

@ -25,8 +25,9 @@ public NzbVortex(INzbVortexProxy proxy,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{
_proxy = proxy;
}

View file

@ -12,6 +12,7 @@
using NzbDrone.Core.Validation;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Organizer;
using System.Globalization;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
@ -27,8 +28,9 @@ public Nzbget(INzbgetProxy proxy,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{
_proxy = proxy;
}
@ -53,19 +55,8 @@ protected override string AddFromNzbFile(RemoteMovie remoteMovie, string filenam
private IEnumerable<DownloadClientItem> GetQueue()
{
NzbgetGlobalStatus globalStatus;
List<NzbgetQueueItem> queue;
try
{
globalStatus = _proxy.GetGlobalStatus(Settings);
queue = _proxy.GetQueue(Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex, ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var globalStatus = _proxy.GetGlobalStatus(Settings);
var queue = _proxy.GetQueue(Settings);
var queueItems = new List<DownloadClientItem>();
@ -121,17 +112,7 @@ private IEnumerable<DownloadClientItem> GetQueue()
private IEnumerable<DownloadClientItem> GetHistory()
{
List<NzbgetHistoryItem> history;
try
{
history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
}
catch (DownloadClientException ex)
{
_logger.Error(ex, ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
var historyItems = new List<DownloadClientItem>();
@ -307,7 +288,7 @@ private ValidationFailure TestCategory()
{
return new NzbDroneValidationFailure("MovieCategory", "Category does not exist")
{
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "The category you entered doesn't exist in NZBGet. Go to NZBGet to create it."
};
}
@ -319,13 +300,22 @@ private ValidationFailure TestSettings()
{
var config = _proxy.GetConfig(Settings);
var keepHistory = config.GetValueOrDefault("KeepHistory");
if (keepHistory == "0")
var keepHistory = config.GetValueOrDefault("KeepHistory", "7");
int value;
if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out value) || value == 0)
{
return new NzbDroneValidationFailure(string.Empty, "NZBGet setting KeepHistory should be greater than 0")
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
{
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port),
DetailedDescription = "NZBGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
};
}
else if (value > 25000)
{
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be less than 25000")
{
InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "NzbGet setting KeepHistory is set too high."
};
}

View file

@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
public interface INzbgetProxy
{
string GetBaseUrl(NzbgetSettings settings, string relativePath = null);
string DownloadNzb(byte[] nzbData, string title, string category, int priority, bool addpaused, NzbgetSettings settings);
NzbgetGlobalStatus GetGlobalStatus(NzbgetSettings settings);
List<NzbgetQueueItem> GetQueue(NzbgetSettings settings);
@ -36,9 +37,17 @@ public NzbgetProxy(IHttpClient httpClient, ICacheManager cacheManager, Logger lo
_versionCache = cacheManager.GetCache<string>(GetType(), "versions");
}
public string GetBaseUrl(NzbgetSettings settings, string relativePath = null)
{
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase);
baseUrl = HttpUri.CombinePath(baseUrl, relativePath);
return baseUrl;
}
private bool HasVersion(int minimumVersion, NzbgetSettings settings)
{
var versionString = _versionCache.Find(settings.Host + ":" + settings.Port) ?? GetVersion(settings);
var versionString = _versionCache.Find(GetBaseUrl(settings)) ?? GetVersion(settings);
var version = int.Parse(versionString.Split(new[] { '.', '-' })[0]);
@ -139,7 +148,7 @@ public string GetVersion(NzbgetSettings settings)
{
var response = ProcessRequest<string>(settings, "version");
_versionCache.Set(settings.Host + ":" + settings.Port, response, TimeSpan.FromDays(1));
_versionCache.Set(GetBaseUrl(settings), response, TimeSpan.FromDays(1));
return response;
}
@ -170,7 +179,7 @@ public void RemoveItem(string id, NzbgetSettings settings)
queueItem = queue.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string)));
historyItem = history.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string)));
}
if (queueItem != null)
{
if (!EditQueue("GroupFinalDelete", 0, "", queueItem.NzbId, settings))
@ -218,7 +227,7 @@ private bool EditQueue(string command, int offset, string editText, int id, Nzbg
private T ProcessRequest<T>(NzbgetSettings settings, string method, params object[] parameters)
{
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, "jsonrpc");
var baseUrl = GetBaseUrl(settings, "jsonrpc");
var requestBuilder = new JsonRpcRequestBuilder(baseUrl, method, parameters);
requestBuilder.LogResponseContent = true;

View file

@ -1,4 +1,5 @@
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@ -11,6 +12,8 @@ public NzbgetSettingsValidator()
{
RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace());
RuleFor(c => c.Username).NotEmpty().When(c => !string.IsNullOrWhiteSpace(c.Password));
RuleFor(c => c.Password).NotEmpty().When(c => !string.IsNullOrWhiteSpace(c.Username));
@ -39,25 +42,28 @@ public NzbgetSettings()
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; }
[FieldDefinition(2, Label = "Username", Type = FieldType.Textbox)]
[FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the nzbget url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")]
public string UrlBase { get; set; }
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)]
public string Username { get; set; }
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string MovieCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")]
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")]
public int RecentMoviePriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
[FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
public int OlderMoviePriority { get; set; }
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; }
[FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NZBGet version 16.0")]
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NZBGet version 16.0")]
public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -154,6 +154,7 @@ public override IEnumerable<DownloadClientItem> GetItems()
case "stalledUP": // torrent is being seeded, but no connection were made
case "queuedUP": // queuing is enabled and torrent is queued for upload
case "checkingUP": // torrent has finished downloading and is being checked
case "forcedUP": // torrent is beeing seeded by force
item.Status = DownloadItemStatus.Completed;
item.RemainingTime = TimeSpan.Zero; // qBittorrent sends eta=8640000 for completed torrents
break;
@ -164,7 +165,12 @@ public override IEnumerable<DownloadClientItem> GetItems()
break;
case "downloading": // torrent is being downloaded and data is being transfered
item.Status = DownloadItemStatus.Downloading;
break;
default: // new status in API? default to downloading
item.Message = "Unknown download state: " + torrent.State;
_logger.Warn(item.Message);
item.Status = DownloadItemStatus.Downloading;
break;
}

View file

@ -25,8 +25,9 @@ public Sabnzbd(ISabnzbdProxy proxy,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{
_proxy = proxy;
}
@ -115,17 +116,7 @@ private IEnumerable<DownloadClientItem> GetQueue()
private IEnumerable<DownloadClientItem> GetHistory()
{
SabnzbdHistory sabHistory;
try
{
sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.MovieCategory, Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex, ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.MovieCategory, Settings);
var historyItems = new List<DownloadClientItem>();
@ -191,6 +182,7 @@ private IEnumerable<DownloadClientItem> GetHistory()
}
}
historyItems.Add(historyItem);
}
@ -327,6 +319,11 @@ private bool HasVersion(int major, int minor, int patch = 0)
private Version ParseVersion(string version)
{
if (version.IsNullOrWhiteSpace())
{
return null;
}
var parsed = VersionRegex.Match(version);
int major;
@ -364,7 +361,7 @@ private ValidationFailure TestConnectionAndVersion()
if (version == null)
{
return new ValidationFailure("Version", "Unknown Version: " + version);
return new ValidationFailure("Version", "Unknown Version: " + rawVersion);
}
if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
@ -424,7 +421,7 @@ private ValidationFailure TestGlobalConfig()
{
return new NzbDroneValidationFailure("", "Disable 'Check before download' option in SABnzbd")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/switches/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings, "config/switches/"),
DetailedDescription = "Using Check before download affects Radarr ability to track new downloads. Also SABnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective."
};
}
@ -443,7 +440,7 @@ private ValidationFailure TestCategory()
{
return new NzbDroneValidationFailure("MovieCategory", "Enable Job folders")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"),
DetailedDescription = "Radarr prefers each download to have a separate folder. With * appended to the Folder/Path SABnzbd will not create these job folders. Go to SABnzbd to fix it."
};
}
@ -454,7 +451,7 @@ private ValidationFailure TestCategory()
{
return new NzbDroneValidationFailure("MovieCategory", "Category does not exist")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"),
DetailedDescription = "The category you entered doesn't exist in SABnzbd. Go to SABnzbd to create it."
};
}
@ -463,7 +460,7 @@ private ValidationFailure TestCategory()
{
return new NzbDroneValidationFailure("MovieCategory", "Disable TV Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it."
};
}
@ -471,7 +468,7 @@ private ValidationFailure TestCategory()
{
return new NzbDroneValidationFailure("MovieCategory", "Disable Movie Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it."
};
}
@ -479,7 +476,7 @@ private ValidationFailure TestCategory()
{
return new NzbDroneValidationFailure("MovieCategory", "Disable Date Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it."
};
}

View file

@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
public interface ISabnzbdProxy
{
string GetBaseUrl(SabnzbdSettings settings, string relativePath = null);
SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings);
void RemoveFrom(string source, string id,bool deleteData, SabnzbdSettings settings);
string GetVersion(SabnzbdSettings settings);
@ -32,6 +33,14 @@ public SabnzbdProxy(IHttpClient httpClient, Logger logger)
_logger = logger;
}
public string GetBaseUrl(SabnzbdSettings settings, string relativePath = null)
{
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase);
baseUrl = HttpUri.CombinePath(baseUrl, relativePath);
return baseUrl;
}
public SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings)
{
var request = BuildRequest("addfile", settings).Post();
@ -140,10 +149,7 @@ public string RetryDownload(string id, SabnzbdSettings settings)
private HttpRequestBuilder BuildRequest(string mode, SabnzbdSettings settings)
{
var baseUrl = string.Format(@"{0}://{1}:{2}/api",
settings.UseSsl ? "https" : "http",
settings.Host,
settings.Port);
var baseUrl = GetBaseUrl(settings, "api");
var requestBuilder = new HttpRequestBuilder(baseUrl)
.Accept(HttpAccept.Json)

View file

@ -1,4 +1,5 @@
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@ -11,6 +12,7 @@ public SabnzbdSettingsValidator()
{
RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace());
RuleFor(c => c.ApiKey).NotEmpty()
.WithMessage("API Key is required when username/password are not configured")
@ -49,25 +51,28 @@ public SabnzbdSettings()
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; }
[FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)]
[FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Sabnzbd url, e.g. http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; }
[FieldDefinition(3, Label = "API Key", Type = FieldType.Textbox)]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)]
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox)]
public string Username { get; set; }
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
[FieldDefinition(5, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string MovieCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")]
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")]
public int RecentMoviePriority { get; set; }
[FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
public int OlderMoviePriority { get; set; }
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
[FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -43,7 +43,7 @@ protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash
{
var priority = (RTorrentPriority)(remoteMovie.Movie.IsRecentMovie ? Settings.RecentMoviePriority : Settings.OlderMoviePriority);
_proxy.AddTorrentFromUrl(magnetLink, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings);
_proxy.AddTorrentFromUrl(magnetLink, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings.DontStartAutomatically, Settings);
var tries = 10;
var retryDelay = 500;
@ -63,7 +63,7 @@ protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string has
{
var priority = (RTorrentPriority)(remoteMovie.Movie.IsRecentMovie ? Settings.RecentMoviePriority : Settings.OlderMoviePriority);
_proxy.AddTorrentFromFile(filename, fileContent, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings);
_proxy.AddTorrentFromFile(filename, fileContent, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings.DontStartAutomatically, Settings);
var tries = 10;
var retryDelay = 500;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@ -13,8 +13,8 @@ public interface IRTorrentProxy
string GetVersion(RTorrentSettings settings);
List<RTorrentTorrent> GetTorrents(RTorrentSettings settings);
void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings);
void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings);
void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings);
void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings);
void RemoveTorrent(string hash, RTorrentSettings settings);
bool HasHashTorrent(string hash, RTorrentSettings settings);
}
@ -24,9 +24,15 @@ public interface IRTorrent : IXmlRpcProxy
[XmlRpcMethod("d.multicall2")]
object[] TorrentMulticall(params string[] parameters);
[XmlRpcMethod("load.normal")]
int Load(string target, string data, params string[] commands);
[XmlRpcMethod("load.start")]
int LoadStart(string target, string data, params string[] commands);
[XmlRpcMethod("load.raw")]
int LoadRaw(string target, byte[] data, params string[] commands);
[XmlRpcMethod("load.raw_start")]
int LoadRawStart(string target, byte[] data, params string[] commands);
@ -102,26 +108,50 @@ public List<RTorrentTorrent> GetTorrents(RTorrentSettings settings)
return items;
}
public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: load.normal");
_logger.Debug("Adding Torrent From URL");
var client = BuildClient(settings);
var response = client.LoadStart("", torrentUrl, GetCommands(label, priority, directory));
var response = -1;
if (doNotStart)
{
_logger.Debug("Executing remote method load.normal");
response = client.Load("", torrentUrl, GetCommands(label, priority, directory));
}
else
{
_logger.Debug("Executing remote method load.start");
response = client.LoadStart("", torrentUrl, GetCommands(label, priority, directory));
}
if (response != 0)
{
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
}
}
public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: load.raw");
_logger.Debug("Loading Torrent from File");
var client = BuildClient(settings);
var response = client.LoadRawStart("", fileContent, GetCommands(label, priority, directory));
var response = -1;
if (doNotStart)
{
_logger.Debug("Executing remote method load.raw");
response = client.LoadRaw("", fileContent, GetCommands(label, priority, directory));
}
else
{
_logger.Debug("Executing remote method load.raw_start");
response = client.LoadRawStart("", fileContent, GetCommands(label, priority, directory));
}
if (response != 0)
{
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
@ -202,4 +232,4 @@ private IRTorrent BuildClient(RTorrentSettings settings)
return client;
}
}
}
}

View file

@ -59,6 +59,9 @@ public RTorrentSettings()
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
public int OlderMoviePriority { get; set; }
[FieldDefinition(10, Label = "Don't start download automatically", Type = FieldType.Checkbox, Advanced = true, HelpText = "Add Download in a stopped state. This is useful for letting a Queue manager like pyrotorque automatically start the download.")]
public bool DontStartAutomatically { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View file

@ -0,0 +1,24 @@
using System;
using NzbDrone.Common.Exceptions;
namespace NzbDrone.Core.Download
{
public class InvalidNzbException : NzbDroneException
{
public InvalidNzbException(string message, params object[] args) : base(message, args)
{
}
public InvalidNzbException(string message) : base(message)
{
}
public InvalidNzbException(string message, Exception innerException, params object[] args) : base(message, innerException, args)
{
}
public InvalidNzbException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View file

@ -0,0 +1,45 @@
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Download
{
public interface IValidateNzbs
{
void Validate(string filename, byte[] fileContent);
}
public class NzbValidationService : IValidateNzbs
{
public void Validate(string filename, byte[] fileContent)
{
var reader = new StreamReader(new MemoryStream(fileContent));
using (var xmlTextReader = XmlReader.Create(reader, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, IgnoreComments = true }))
{
var xDoc = XDocument.Load(xmlTextReader);
var nzb = xDoc.Root;
if (nzb == null)
{
throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
}
if (!nzb.Name.LocalName.Equals("nzb"))
{
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
}
var ns = nzb.Name.Namespace;
var files = nzb.Elements(ns + "file").ToList();
if (files.Empty())
{
throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
}
}
}
}
}

View file

@ -24,7 +24,7 @@ public void DeleteByMovieId(int movieId)
public List<PendingRelease> AllByMovieId(int movieId)
{
return Query.Where(p => p.MovieId == movieId);
return Query(q => q.Where(p => p.MovieId == movieId).ToList());
}
}
}

View file

@ -44,6 +44,20 @@ public TrackedDownload Find(string downloadId)
public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, DownloadClientItem downloadItem)
{
if (downloadItem.DownloadId.IsNullOrWhiteSpace())
{
_logger.Warn("The following download client item ({0}) has no download hash (id), so it cannot be tracked: {1}",
downloadClient.Name, downloadItem.Title);
return null;
}
if (downloadItem.Title.IsNullOrWhiteSpace())
{
_logger.Warn("The following download client item ({0}) has no title so it cannot be tracked: {1}",
downloadClient.Name, downloadItem.Title);
return null;
}
var existingItem = Find(downloadItem.DownloadId);
if (existingItem != null && existingItem.State != TrackedDownloadStage.Downloading)

View file

@ -17,16 +17,19 @@ public abstract class UsenetClientBase<TSettings> : DownloadClientBase<TSettings
where TSettings : IProviderConfig, new()
{
protected readonly IHttpClient _httpClient;
private readonly IValidateNzbs _nzbValidationService;
protected UsenetClientBase(IHttpClient httpClient,
IConfigService configService,
INamingConfigService namingConfigService,
IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger)
: base(configService, namingConfigService, diskProvider, remotePathMappingService, logger)
{
_httpClient = httpClient;
_nzbValidationService = nzbValidationService;
}
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
@ -66,6 +69,8 @@ public override string Download(RemoteMovie remoteMovie)
throw new ReleaseDownloadException(remoteMovie.Release, "Downloading nzb failed", ex);
}
_nzbValidationService.Validate(filename, nzbData);
_logger.Info("Adding report [{0}] to the queue.", remoteMovie.Release.Title);
return AddFromNzbFile(remoteMovie, filename, nzbData);
}

View file

@ -34,17 +34,17 @@ public void DeleteForMovieFile(int movieFileId)
public List<TExtraFile> GetFilesByMovie(int movieId)
{
return Query.Where(c => c.MovieId == movieId);
return Query(q => q.Where(c => c.MovieId == movieId).ToList());
}
public List<TExtraFile> GetFilesByMovieFile(int movieFileId)
{
return Query.Where(c => c.MovieFileId == movieFileId);
return Query(q => q.Where(c => c.MovieFileId == movieFileId).ToList());
}
public TExtraFile FindByPath(string path)
{
return Query.Where(c => c.RelativePath == path).SingleOrDefault();
return Query(q => q.Where(c => c.RelativePath == path).SingleOrDefault());
}
}
}

View file

@ -127,11 +127,24 @@ public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFil
{
details.Add(new XElement("rating", movie.Ratings.Value));
}
details.Add(new XElement("plot", movie.Overview));
details.Add(new XElement("id", movie.ImdbId));
details.Add(new XElement("year", movie.Year));
if (movie.ImdbId.IsNotNullOrWhiteSpace())
{
var imdbId = new XElement("uniqueid", movie.ImdbId);
imdbId.SetAttributeValue("type", "imdb");
imdbId.SetAttributeValue("default", true);
details.Add(imdbId);
}
var uniqueId = new XElement("uniqueid", movie.TmdbId);
uniqueId.SetAttributeValue("type", "tmdb");
details.Add(uniqueId);
details.Add(new XElement("year", movie.Year));
if (movie.InCinemas.HasValue)
{
details.Add(new XElement("premiered", movie.InCinemas.Value.ToString("yyyy-MM-dd")));

View file

@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if !LIBRARY
using NzbDrone.Common.EnsureThat;
#endif
namespace NzbDrone.Core
{
@ -10,8 +12,9 @@ public static class Fluent
{
public static string WithDefault(this string actual, object defaultValue)
{
#if !LIBRARY
Ensure.That(defaultValue, () => defaultValue).IsNotNull();
#endif
if (string.IsNullOrWhiteSpace(actual))
{
return defaultValue.ToString();

View file

@ -0,0 +1,32 @@
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.PassThePopcorn;
namespace NzbDrone.Core.HealthCheck.Checks
{
public class PTPOldSettingsCheck : HealthCheckBase
{
private readonly IIndexerFactory _indexerFactory;
public PTPOldSettingsCheck(IIndexerFactory indexerFactory)
{
_indexerFactory = indexerFactory;
}
public override HealthCheck Check()
{
var ptpIndexers = _indexerFactory.All().Where(i => i.Settings.GetType() == typeof(PassThePopcornSettings));
var ptpIndexerOldSettings = ptpIndexers
.Where(i => (i.Settings as PassThePopcornSettings).APIUser.IsNullOrWhiteSpace()).Select(i => i.Name);
if (ptpIndexerOldSettings.Count() > 0)
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, $"The following PassThePopcorn indexers have deprecated settings and should be updated: {string.Join(",", ptpIndexerOldSettings)}");
}
return new HealthCheck(GetType());
}
}
}

View file

@ -16,7 +16,7 @@ public class ProxyCheck : HealthCheckBase
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
public ProxyCheck(ISonarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger)
public ProxyCheck(IRadarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger)
{
_configService = configService;
_client = client;

View file

@ -30,37 +30,37 @@ public HistoryRepository(IMainDatabase database, IEventAggregator eventAggregato
public List<QualityModel> GetBestQualityInHistory(int movieId)
{
var history = Query.Where(c => c.MovieId == movieId);
var history = Query(q => q.Where(c => c.MovieId == movieId).ToList());
return history.Select(h => h.Quality).ToList();
}
public History MostRecentForDownloadId(string downloadId)
{
return Query.Where(h => h.DownloadId == downloadId)
return Query(q => q.Where(h => h.DownloadId == downloadId)
.OrderByDescending(h => h.Date)
.FirstOrDefault();
.FirstOrDefault());
}
public List<History> FindByDownloadId(string downloadId)
{
return Query.Where(h => h.DownloadId == downloadId);
return Query(q => q.Where(h => h.DownloadId == downloadId).ToList());
}
public List<History> FindDownloadHistory(int idMovieId, QualityModel quality)
{
return Query.Where(h =>
return Query(q => q.Where(h =>
h.MovieId == idMovieId &&
h.Quality == quality &&
(h.EventType == HistoryEventType.Grabbed ||
h.EventType == HistoryEventType.DownloadFailed ||
h.EventType == HistoryEventType.DownloadFolderImported)
).ToList();
).ToList());
}
public List<History> FindByMovieId(int movieId)
{
return Query.Where(h => h.MovieId == movieId);
return Query(q => q.Where(h => h.MovieId == movieId).ToList());
}
public void DeleteForMovie(int movieId)
@ -77,9 +77,9 @@ protected override SortBuilder<History> GetPagedQuery(QueryBuilder<History> quer
public History MostRecentForMovie(int movieId)
{
return Query.Where(h => h.MovieId == movieId)
return Query(q => q.Where(h => h.MovieId == movieId)
.OrderByDescending(h => h.Date)
.FirstOrDefault();
.FirstOrDefault());
}
}
}

View file

@ -13,9 +13,9 @@ public CleanupAbsolutePathMetadataFiles(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT Id FROM MetadataFiles
WHERE RelativePath
@ -25,6 +25,7 @@ OR RelativePath
OR RelativePath
LIKE '/%'
)");
}
}
}
}

View file

@ -13,12 +13,14 @@ public CleanupAdditionalNamingSpecs(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig
WHERE ID NOT IN (
SELECT ID FROM NamingConfig
LIMIT 1)");
}
}
}
}

View file

@ -13,13 +13,15 @@ public CleanupAdditionalUsers(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM Users
mapper.ExecuteNonQuery(@"DELETE FROM Users
WHERE ID NOT IN (
SELECT ID FROM Users
LIMIT 1)");
}
}
}
}
}

View file

@ -19,28 +19,32 @@ public void Clean()
private void DeleteDuplicateMovieMetadata()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT Id FROM MetadataFiles
WHERE Type = 1
GROUP BY MovieId, Consumer
HAVING COUNT(MovieId) > 1
)");
}
}
private void DeleteDuplicateMovieFileMetadata()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT Id FROM MetadataFiles
WHERE Type = 1
GROUP BY MovieFileId, Consumer
HAVING COUNT(MovieFileId) > 1
)");
}
}
}
}

View file

@ -13,14 +13,16 @@ public CleanupOrphanedAlternativeTitles(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM AlternativeTitles
mapper.ExecuteNonQuery(@"DELETE FROM AlternativeTitles
WHERE Id IN (
SELECT AlternativeTitles.Id FROM AlternativeTitles
LEFT OUTER JOIN Movies
ON AlternativeTitles.MovieId = Movies.Id
WHERE Movies.Id IS NULL)");
}
}
}
}

View file

@ -13,14 +13,16 @@ public CleanupOrphanedBlacklist(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM Blacklist
mapper.ExecuteNonQuery(@"DELETE FROM Blacklist
WHERE Id IN (
SELECT Blacklist.Id FROM Blacklist
LEFT OUTER JOIN Movies
ON Blacklist.MovieId = Movies.Id
WHERE Movies.Id IS NULL)");
}
}
}
}

View file

@ -18,14 +18,16 @@ public void Clean()
private void CleanupOrphanedByMovie()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM History
mapper.ExecuteNonQuery(@"DELETE FROM History
WHERE Id IN (
SELECT History.Id FROM History
LEFT OUTER JOIN Movies
ON History.MovieId = Movies.Id
WHERE Movies.Id IS NULL)");
}
}
}
}

View file

@ -13,14 +13,16 @@ public CleanupOrphanedIndexerStatus(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus
mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus
WHERE Id IN (
SELECT IndexerStatus.Id FROM IndexerStatus
LEFT OUTER JOIN Indexers
ON IndexerStatus.IndexerId = Indexers.Id
WHERE Indexers.Id IS NULL)");
}
}
}
}

View file

@ -20,38 +20,44 @@ public void Clean()
private void DeleteOrphanedByMovie()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT MetadataFiles.Id FROM MetadataFiles
LEFT OUTER JOIN Movies
ON MetadataFiles.MovieId = Movies.Id
WHERE Movies.Id IS NULL)");
}
}
private void DeleteOrphanedByMovieFile()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT MetadataFiles.Id FROM MetadataFiles
LEFT OUTER JOIN MovieFiles
ON MetadataFiles.MovieFileId = MovieFiles.Id
WHERE MetadataFiles.MovieFileId > 0
AND MovieFiles.Id IS NULL)");
}
}
private void DeleteWhereMovieFileIsZero()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT Id FROM MetadataFiles
WHERE Type IN (1, 2)
AND MovieFileId = 0)");
}
}
}
}

View file

@ -13,14 +13,15 @@ public CleanupOrphanedMovieFiles(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM MovieFiles
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MovieFiles
WHERE Id IN (
SELECT MovieFiles.Id FROM MovieFiles
LEFT OUTER JOIN Movies
ON MovieFiles.Id = Movies.MovieFileId
WHERE Movies.Id IS NULL)");
}
}
}
}

View file

@ -13,14 +13,16 @@ public CleanupOrphanedPendingReleases(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
WHERE Id IN (
SELECT PendingReleases.Id FROM PendingReleases
LEFT OUTER JOIN Movies
ON PendingReleases.MovieId = Movies.Id
WHERE Movies.Id IS NULL)");
}
}
}
}

View file

@ -17,16 +17,18 @@ public CleanupUnusedTags(IMainDatabase database)
public void Clean()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
var usedTags = new[] { "Movies", "Notifications", "DelayProfiles", "Restrictions" }
.SelectMany(v => GetUsedTags(v, mapper))
.Distinct()
.ToArray();
var usedTags = new[] {"Movies", "Notifications", "DelayProfiles", "Restrictions"}
.SelectMany(v => GetUsedTags(v, mapper))
.Distinct()
.ToArray();
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})");
mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})");
}
}
private int[] GetUsedTags(string table, IDataMapper mapper)

View file

@ -23,12 +23,14 @@ public void Clean()
_logger.Debug("Not running scheduled task last execution cleanup during debug");
}
var mapper = _database.GetDataMapper();
mapper.AddParameter("time", DateTime.UtcNow);
using (var mapper = _database.GetDataMapper())
{
mapper.AddParameter("time", DateTime.UtcNow);
mapper.ExecuteNonQuery(@"UPDATE ScheduledTasks
mapper.ExecuteNonQuery(@"UPDATE ScheduledTasks
SET LastExecution = @time
WHERE LastExecution > @time");
}
}
}
}

View file

@ -16,11 +16,11 @@ public IPTorrentsSettingsValidator()
{
RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.BaseUrl).Matches(@"/rss\?.+$");
RuleFor(c => c.BaseUrl).Matches(@"(?:/|t\.)rss\?.+$");
RuleFor(c => c.BaseUrl).Matches(@"/rss\?.+;download(?:;|$)")
RuleFor(c => c.BaseUrl).Matches(@"(?:/|t\.)rss\?.+;download(?:;|$)")
.WithMessage("Use Direct Download Url (;download)")
.When(v => v.BaseUrl.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.BaseUrl, @"/rss\?.+$"));
.When(v => v.BaseUrl.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.BaseUrl, @"(?:/|t\.)rss\?.+$"));
}
}

View file

@ -20,7 +20,7 @@ public IndexerStatusRepository(IMainDatabase database, IEventAggregator eventAgg
public IndexerStatus FindByIndexerId(int indexerId)
{
return Query.Where(c => c.IndexerId == indexerId).SingleOrDefault();
return Query(q => q.Where(c => c.IndexerId == indexerId).SingleOrDefault());
}
}
}

View file

@ -4,6 +4,7 @@
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Common.Cache;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Indexers.PassThePopcorn
@ -39,21 +40,34 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{
Cookies = GetCookies();
Authenticate();
var request =
new IndexerRequest(
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?action=advanced&json=noredirect&searchstr={searchParameters}",
HttpAccept.Json);
foreach (var cookie in Cookies)
if (Settings.APIKey.IsNullOrWhiteSpace())
{
request.HttpRequest.Cookies[cookie.Key] = cookie.Value;
Cookies = GetCookies();
Authenticate();
Logger.Warn("You are using the old method of logging into PassThePopcorn. Please switch to the new method using APIUser & APIKey.");
}
else
{
request.HttpRequest.Headers["ApiUser"] = Settings.APIUser;
request.HttpRequest.Headers["ApiKey"] = Settings.APIKey;
}
CookiesUpdater(Cookies, DateTime.Now + TimeSpan.FromDays(30));
if (Settings.APIKey.IsNullOrWhiteSpace())
{
foreach (var cookie in Cookies)
{
request.HttpRequest.Cookies[cookie.Key] = cookie.Value;
}
CookiesUpdater(Cookies, DateTime.Now + TimeSpan.FromDays(30));
}
yield return request;
}

View file

@ -14,9 +14,11 @@ public class PassThePopcornSettingsValidator : AbstractValidator<PassThePopcornS
public PassThePopcornSettingsValidator()
{
RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.Username).NotEmpty();
RuleFor(c => c.Password).NotEmpty();
RuleFor(c => c.Passkey).NotEmpty();
RuleFor(c => c.Username).Empty();
RuleFor(c => c.Password).Empty();
RuleFor(c => c.Passkey).Empty();
RuleFor(c => c.APIUser).NotEmpty();
RuleFor(c => c.APIKey).NotEmpty();
}
}
@ -33,22 +35,28 @@ public PassThePopcornSettings()
[FieldDefinition(0, Label = "URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your cookie will be sent to that host.")]
public string BaseUrl { get; set; }
[FieldDefinition(1, Label = "Username", HelpText = "PTP Username")]
[FieldDefinition(1, Label = "APIUser", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).")]
public string APIUser { get; set; }
[FieldDefinition(2, Label = "APIKey", Type = FieldType.Password)]
public string APIKey { get; set; }
[FieldDefinition(3, Label = "DEPRECATED: User", HelpText = "Please use APIKey & APIUser instead. PTP Username")]
public string Username { get; set; }
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "PTP Password")]
[FieldDefinition(4, Label = "DEPRECATED: Pass", Type = FieldType.Password, HelpText = "Please use APIKey & APIUser instead. PTP Password")]
public string Password { get; set; }
[FieldDefinition(3, Label = "Passkey", HelpText = "PTP Passkey")]
[FieldDefinition(5, Label = "DEPRECATED: Passkey", HelpText = "Please use APIKey & APIUser instead. PTP Passkey")]
public string Passkey { get; set; }
[FieldDefinition(4, Type = FieldType.Tag, SelectOptions = typeof(Language), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)]
[FieldDefinition(6, Type = FieldType.Tag, SelectOptions = typeof(Language), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(5, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
[FieldDefinition(7, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(6, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", HelpLink = "https://github.com/Radarr/Radarr/wiki/Indexer-Flags#1-required-flags", Advanced = true)]
[FieldDefinition(8, Type = FieldType.Tag, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", HelpLink = "https://github.com/Radarr/Radarr/wiki/Indexer-Flags#1-required-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()

View file

@ -8,7 +8,7 @@ public static class XmlCleaner
{
private static readonly Regex ReplaceEntitiesRegex = new Regex("&[a-z]+;", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReplaceUnicodeRegex = new Regex(@"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReplaceUnicodeRegex = new Regex(@"[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static string ReplaceEntities(string content)
{

View file

@ -15,13 +15,14 @@ public class DatabaseTarget : TargetWithLayout, IHandle<ApplicationShutdownReque
{
private readonly SQLiteConnection _connection;
private readonly IConnectionStringFactory _connectionStringFactory;
const string INSERT_COMMAND = "INSERT INTO [Logs]([Message],[Time],[Logger],[Exception],[ExceptionType],[Level]) " +
"VALUES(@Message,@Time,@Logger,@Exception,@ExceptionType,@Level)";
public DatabaseTarget(IConnectionStringFactory connectionStringFactory)
{
_connection = new SQLiteConnection(connectionStringFactory.LogDbConnectionString);
_connection.Open();
_connectionStringFactory = connectionStringFactory;
}
public void Register()
@ -84,16 +85,25 @@ protected override void Write(LogEventInfo logEvent)
log.Level = logEvent.Level.Name;
var sqlCommand = new SQLiteCommand(INSERT_COMMAND, _connection);
using (var connection =
SQLiteFactory.Instance.CreateConnection())
{
connection.ConnectionString = _connectionStringFactory.LogDbConnectionString;
connection.Open();
using (var sqlCommand = connection.CreateCommand())
{
sqlCommand.CommandText = INSERT_COMMAND;
sqlCommand.Parameters.Add(new SQLiteParameter("Message", DbType.String) { Value = log.Message });
sqlCommand.Parameters.Add(new SQLiteParameter("Time", DbType.DateTime) { Value = log.Time.ToUniversalTime() });
sqlCommand.Parameters.Add(new SQLiteParameter("Logger", DbType.String) { Value = log.Logger });
sqlCommand.Parameters.Add(new SQLiteParameter("Exception", DbType.String) { Value = log.Exception });
sqlCommand.Parameters.Add(new SQLiteParameter("ExceptionType", DbType.String) { Value = log.ExceptionType });
sqlCommand.Parameters.Add(new SQLiteParameter("Level", DbType.String) { Value = log.Level });
sqlCommand.Parameters.Add(new SQLiteParameter("Message", DbType.String) { Value = log.Message });
sqlCommand.Parameters.Add(new SQLiteParameter("Time", DbType.DateTime) { Value = log.Time.ToUniversalTime() });
sqlCommand.Parameters.Add(new SQLiteParameter("Logger", DbType.String) { Value = log.Logger });
sqlCommand.Parameters.Add(new SQLiteParameter("Exception", DbType.String) { Value = log.Exception });
sqlCommand.Parameters.Add(new SQLiteParameter("ExceptionType", DbType.String) { Value = log.ExceptionType });
sqlCommand.Parameters.Add(new SQLiteParameter("Level", DbType.String) { Value = log.Level });
sqlCommand.ExecuteNonQuery();
}
}
sqlCommand.ExecuteNonQuery();
}
catch (SQLiteException ex)
{
@ -104,7 +114,7 @@ protected override void Write(LogEventInfo logEvent)
public void Handle(ApplicationShutdownRequested message)
{
if (LogManager.Configuration.LoggingRules.Contains(Rule))
if (LogManager.Configuration?.LoggingRules?.Contains(Rule) == true)
{
UnRegister();
}

View file

@ -22,7 +22,7 @@ public ScheduledTaskRepository(IMainDatabase database, IEventAggregator eventAgg
public ScheduledTask GetDefinition(Type type)
{
return Query.Where(c => c.TypeName == type.FullName).Single();
return Query(q => q.Where(c => c.TypeName == type.FullName).Single());
}
public void SetLastExecutionTime(int id, DateTime executionTime)

View file

@ -31,12 +31,6 @@ public bool AlreadyExists(string url, string path)
return false;
}
if (!_diskProvider.IsValidGDIPlusImage(path))
{
_diskProvider.DeleteFile(path);
return false;
}
var headers = _httpClient.Head(new HttpRequest(url)).Headers;
var fileSize = _diskProvider.GetFileSize(path);
return fileSize == headers.ContentLength;

View file

@ -0,0 +1,19 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.MediaCover
{
public class EnsureMediaCoversCommand : Command
{
public int MovieId { get; set; }
public EnsureMediaCoversCommand()
{
}
public EnsureMediaCoversCommand(int movieId)
{
MovieId = movieId;
}
}
}

View file

@ -8,6 +8,7 @@
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Events;
@ -22,8 +23,9 @@ public interface IMapCoversToLocal
public class MediaCoverService :
IHandleAsync<MovieUpdatedEvent>,
IHandleAsync<MovieAddedEvent>,
//IHandleAsync<MovieAddedEvent>,
IHandleAsync<MovieDeletedEvent>,
IExecute<EnsureMediaCoversCommand>,
IMapCoversToLocal
{
private readonly IImageResizer _resizer;
@ -32,6 +34,8 @@ public class MediaCoverService :
private readonly ICoverExistsSpecification _coverExistsSpecification;
private readonly IConfigFileProvider _configFileProvider;
private readonly IEventAggregator _eventAggregator;
private readonly IManageCommandQueue _commandQueue;
private readonly IMovieService _movieService;
private readonly Logger _logger;
private readonly string _coverRootFolder;
@ -43,6 +47,8 @@ public MediaCoverService(IImageResizer resizer,
ICoverExistsSpecification coverExistsSpecification,
IConfigFileProvider configFileProvider,
IEventAggregator eventAggregator,
IManageCommandQueue commandQueue,
IMovieService movieService,
Logger logger)
{
_resizer = resizer;
@ -51,6 +57,8 @@ public MediaCoverService(IImageResizer resizer,
_coverExistsSpecification = coverExistsSpecification;
_configFileProvider = configFileProvider;
_eventAggregator = eventAggregator;
_commandQueue = commandQueue;
_movieService = movieService;
_logger = logger;
_coverRootFolder = appFolderInfo.GetMediaCoverPath();
@ -109,7 +117,7 @@ private void EnsureCovers(Movie movie, int retried = 0)
_logger.Warn(e, string.Format("Couldn't download media cover for {0}. {1}", movie, e.Message));
if (retried < 3)
{
retried = +1;
retried += 1;
_logger.Warn("Retrying for the {0}. time in ten seconds.", retried);
System.Threading.Thread.Sleep(10 * 1000);
EnsureCovers(movie, retried);
@ -182,16 +190,26 @@ private void EnsureResizedCovers(Movie movie, MediaCover cover, bool forceResize
}
}
public void Execute(EnsureMediaCoversCommand command)
{
var movie = _movieService.GetMovie(command.MovieId);
EnsureCovers(movie);
_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(movie));
}
public void HandleAsync(MovieUpdatedEvent message)
{
EnsureCovers(message.Movie);
_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie));
//EnsureCovers(message.Movie);
_logger.Info("Testing: {0}, {1}", _commandQueue, message.Movie.Id);
_commandQueue.Push(new EnsureMediaCoversCommand(message.Movie.Id));
//_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie));
}
public void HandleAsync(MovieAddedEvent message)
{
EnsureCovers(message.Movie);
_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie));
//EnsureCovers(message.Movie);
//_commandQueue.Push(new EnsureMediaCoversCommand(message.Movie.Id));
//_eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie));
}
public void HandleAsync(MovieDeletedEvent message)

View file

@ -21,12 +21,12 @@ public MediaFileRepository(IMainDatabase database, IEventAggregator eventAggrega
public List<MovieFile> GetFilesByMovie(int movieId)
{
return Query.Where(c => c.MovieId == movieId).ToList();
return Query(q => q.Where(c => c.MovieId == movieId).ToList());
}
public List<MovieFile> GetFilesWithoutMediaInfo()
{
return Query.Where(c => c.MediaInfo == null).ToList();
return Query(q => q.Where(c => c.MediaInfo == null).ToList());
}
}
}

View file

@ -37,37 +37,39 @@ public void Trim()
public void OrphanStarted()
{
var mapper = _database.GetDataMapper();
using (var mapper = _database.GetDataMapper())
{
mapper.Parameters.Add(new SQLiteParameter("@orphaned", (int)CommandStatus.Orphaned));
mapper.Parameters.Add(new SQLiteParameter("@started", (int)CommandStatus.Started));
mapper.Parameters.Add(new SQLiteParameter("@ended", DateTime.UtcNow));
mapper.Parameters.Add(new SQLiteParameter("@orphaned", (int) CommandStatus.Orphaned));
mapper.Parameters.Add(new SQLiteParameter("@started", (int) CommandStatus.Started));
mapper.Parameters.Add(new SQLiteParameter("@ended", DateTime.UtcNow));
mapper.ExecuteNonQuery(@"UPDATE Commands
mapper.ExecuteNonQuery(@"UPDATE Commands
SET Status = @orphaned, EndedAt = @ended
WHERE Status = @started");
}
}
public List<CommandModel> FindCommands(string name)
{
return Query.Where(c => c.Name == name).ToList();
return Query(q => q.Where(c => c.Name == name).ToList());
}
public List<CommandModel> FindQueuedOrStarted(string name)
{
return Query.Where(c => c.Name == name)
return Query(q => q.Where(c => c.Name == name)
.AndWhere("[Status] IN (0,1)")
.ToList();
.ToList());
}
public List<CommandModel> Queued()
{
return Query.Where(c => c.Status == CommandStatus.Queued);
return Query(q => q.Where(c => c.Status == CommandStatus.Queued).ToList());
}
public List<CommandModel> Started()
{
return Query.Where(c => c.Status == CommandStatus.Started);
return Query(q => q.Where(c => c.Status == CommandStatus.Started).ToList());
}
public void Start(CommandModel command)

Some files were not shown because too many files have changed in this diff Show more