1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-13 15:34:56 -04:00

Compare commits

...

60 Commits

Author SHA1 Message Date
Leonardo Galli
5a8d944397 Fixed sorting in movie list view. Also added new downloaded quality column.
Fixes #128
2017-01-12 23:07:09 +01:00
Leonardo Galli
0b70a5c315 Should fix ordering of releases. Fixes #147 (hopefully) 2017-01-12 22:12:32 +01:00
Leonardo Galli
91cded3b71 Merge pull request #202 from Radarr/onedr0p-patch-1
Update UserAgentBuilder.cs
2017-01-12 21:48:16 +01:00
Leonardo Galli
84112dc85b Should fix queueService failed while processing
(#178)
2017-01-12 21:48:02 +01:00
Devin Buhl
b1b947ae7f Update UserAgentBuilder.cs
Make user-agent Radarr
2017-01-12 15:41:56 -05:00
Leonardo Galli
1b035f8657 Update Parser to support large array of Extended, Director, Collectors, ... Cut, Edition, etc.
Fixes #192
2017-01-12 19:21:09 +01:00
Devin Buhl
9d50f4d651 Merge pull request #199 from Radarr/patch/add-uhd-todefault-indexers
Add UHD to default movie categories for newsnab providers
2017-01-12 12:58:23 -05:00
Devin Buhl
c06a6dc988 Add UHD to default movie categories for newsnab providers 2017-01-12 12:53:09 -05:00
Devin Buhl
f198ca2b77 Merge pull request #177 from Radarr/patch/sort-title
Movies in list don't sort correctly #174
2017-01-12 01:08:32 -05:00
Devin Buhl
9cfc766889 Merge pull request #180 from wcomartin/Issue115
Changed Sonarr Branding to Radarr
2017-01-11 23:27:31 -05:00
William Comartin
5ef1e40403 Change Sonarr to Radarr in CLA.md and CONTRIBUTING.md 2017-01-11 22:02:54 -05:00
William Comartin
c9e6835d7b Change Sonarr to Radarr in Help Text, and in Notification Text
Change sonarr log files to radarr log files
2017-01-11 21:59:13 -05:00
William Comartin
604cea00f6 Replace Sonarr With Radarr in UI Directory 2017-01-11 21:42:47 -05:00
Tim Turner
b4782da1d1 Update sortValue when selecting movie for manual import 2017-01-11 19:29:22 -05:00
Devin Buhl
d5504043c5 Movies in list don't sort correctly #174 2017-01-11 19:06:08 -05:00
Leonardo Galli
81ebbcad70 Now hidden files are ignored :). Fixes #166. 2017-01-11 23:05:11 +01:00
Leonardo Galli
92e9dc6ee1 Fix sorting of unkown release date. 2017-01-11 22:39:07 +01:00
Leonardo Galli
5e8b617625 Sorting now working according to quality in release collection. Fixes #85. 2017-01-11 22:27:37 +01:00
Leonardo Galli
2cbe17151d Correctly check if inCinemas date is present. Creates issue with sorting, but eh. Fixes 140. 2017-01-11 22:02:24 +01:00
Devin Buhl
95bd615718 Merge pull request #169 from Radarr/patch/big-movie-revenues
Problem with Avatar (2009) #168
2017-01-11 15:53:16 -05:00
Leonardo Galli
8e8c4ff497 Update parser to recognize [] and year at the beginning. Fixes #155, fixes #137 and fixes #136. 2017-01-11 21:49:59 +01:00
Devin Buhl
5eddcc1660 Problem with Avatar (2009) #168 2017-01-11 15:48:19 -05:00
Tim Turner
d42165a93a Clean up basic movie naming
Fixes #132
2017-01-11 15:03:55 -05:00
Devin Buhl
99012d8a40 Merge pull request #163 from Radarr/patch/fix-plex-notifs
update plex movie libraries instead of series
2017-01-11 12:35:01 -05:00
Devin Buhl
8bc42c76e4 update plex movie libraries instead of series 2017-01-11 12:25:27 -05:00
Leonardo Galli
b7f72c6259 Merge pull request #160 from Radarr/patch/fix-newznab
fix some spelling mistakes and update the newznab api 'imdbid'
2017-01-11 16:59:09 +01:00
Devin Buhl
64ef8db037 fix some spelling mistakes and update the newznab api 'imdbid' 2017-01-11 10:51:06 -05:00
Leonardo Galli
3b5887bf09 Merge pull request #133 from Radarr/fix-manual-import
Fix Manual Import & Drone Factory
2017-01-11 16:39:09 +01:00
Leonardo Galli
40a75949ba Merge pull request #156 from CHBMB/develop
aarch64 docker container added to readme.
2017-01-11 16:33:36 +01:00
Neil
6747267d19 aarch64 docker container added to readme. 2017-01-11 15:12:26 +00:00
Devin Buhl
13db03f97c Merge pull request #154 from Radarr/patch/remove-fanzub
removed indexer Fanzub - site shutdown
2017-01-11 10:12:03 -05:00
Devin Buhl
cd68eea790 removed indexer Fanzub - site shutdown 2017-01-11 09:40:42 -05:00
Devin Buhl
cfe55d00ae Merge pull request #148 from Radarr/patch/newznab-imdb
search imdbid for usenet indexers that support it
2017-01-11 08:53:28 -05:00
Devin Buhl
1d88313424 #146 search imdbid for usenet indexers that support it 2017-01-11 08:42:29 -05:00
Mike
9513068467 Get rid of unnecessary AppVeyor builds. 2017-01-11 08:57:15 +01:00
Tim Turner
9206258370 Fixes Manual Import and DroneFactory
Automatic Import still doesn't work - waiting for answer from Sonarr
team.
2017-01-10 21:12:47 -05:00
Tim Turner
0fa1509ca6 Manual Import works
Automatically importing still broken
2017-01-10 19:37:17 -05:00
Devin Buhl
c5eb772f7a Merge pull request #130 from Radarr/newznab-patch
Add category 2035 to Newznab providers for WEB-DL search support. #123
2017-01-10 19:14:21 -05:00
Devin Buhl
04fec6d4d8 Add category 2035 to Newznab providers for WEB-DL search support. #123 2017-01-10 19:09:12 -05:00
Devin Buhl
d0b5e380d7 Merge pull request #122 from Radarr/transmission-patch
Fix transmission
2017-01-10 17:43:16 -05:00
Devin Buhl
df69c58d2b Fix transmission 2017-01-10 17:37:23 -05:00
Leonardo Galli
27114c9399 Merge pull request #119 from CHBMB/develop
Update readme.md
2017-01-10 23:25:05 +01:00
Neil
cfca07996b Update readme.md 2017-01-10 22:19:12 +00:00
Leonardo Galli
e97b80f630 Update readme.md 2017-01-10 23:19:00 +01:00
Devin Buhl
792679fd81 Merge pull request #118 from lxh87/patch-1
Update SystemLayout.js
2017-01-10 16:50:15 -05:00
lxh87
ecf47d4b17 Update SystemLayout.js
Change restart/shutdown message from"Sonarr" to "Radarr"
2017-01-10 22:42:44 +01:00
Leonardo Galli
3b1d49a78f Update readme.md 2017-01-10 22:30:36 +01:00
Devin Buhl
753f3eb863 Merge pull request #117 from Radarr/wombles-patch
Fix Wombles for movies
2017-01-10 16:29:02 -05:00
Devin Buhl
9fc2d22d19 Fix Wombles for movies 2017-01-10 16:28:16 -05:00
Tim Turner
3cb42f06c2 Clean up Feature Requests 2017-01-10 16:05:56 -05:00
Devin Buhl
a6c396a595 Merge pull request #111 from Radarr/addmovie-patch
fix #108 - Links to IMDB not working when searching for movies
2017-01-10 15:45:39 -05:00
Devin Buhl
bac9076b1e fix #108 - Links to IMDB not working when searching for movies 2017-01-10 15:39:25 -05:00
Tim Turner
b228273be0 Update Info page.
Updates #73
2017-01-10 15:34:43 -05:00
Leonardo Galli
e7fa4cba19 Fix download rejections being ignored. 2017-01-10 21:25:36 +01:00
Leonardo Galli
0506cc4185 Merge branch 'develop' of https://github.com/galli-leo/Radarr into develop 2017-01-10 20:07:01 +01:00
Leonardo Galli
cde8b4dd97 Added MovieFileResource. This allows the UI to interact with movie files better. Downloaded Quality is now shown in the table. 2017-01-10 20:06:53 +01:00
Leonardo Galli
0a0a44162c Merge pull request #105 from galli-leo/linux-tmp-path-patch
Fixes #104 - Backup/update fail Access to the path "/tmp/nzbdrone_backup/config.xml" is denied
2017-01-10 19:31:12 +01:00
Devin Buhl
125f46fbec Fixes #104 - Backup/update fail Access to the path "/tmp/nzbdrone_backup/config.xml" is denied 2017-01-10 13:21:54 -05:00
Leonardo Galli
f3222ca7c7 Merge pull request #102 from galli-leo/addmovies-patch
When adding a movie, monitored toggle doesn't apply and always defaults to being monitored
2017-01-10 19:21:24 +01:00
Devin Buhl
d252a8b232 Fixes #100 - When adding a movie, monitored toggle doesn't apply and always defaults to being monitored 2017-01-10 12:39:28 -05:00
114 changed files with 1353 additions and 760 deletions

4
CLA.md
View File

@@ -1,6 +1,6 @@
# Sonarr Individual Contributor License Agreement #
# Radarr Individual Contributor License Agreement #
Thank you for your interest in contributing to Sonarr ("We" or "Us").
Thank you for your interest in contributing to Radarr ("We" or "Us").
This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please complete the form below. This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us.
## 1. Definitions ##

View File

@@ -1,6 +1,6 @@
# How to Contribute #
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute.
We're always looking for people to help make Radarr even better, there are a number of ways to contribute.
## Documentation ##
Setup guides, FAQ, the more information we have on the wiki the better.
@@ -15,7 +15,7 @@ Setup guides, FAQ, the more information we have on the wiki the better.
### Getting started ###
1. Fork Sonarr
1. Fork Radarr
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
3. Run `npm install`
4. Run `npm start` - Used to compile the UI components and copy them.
@@ -24,8 +24,8 @@ Setup guides, FAQ, the more information we have on the wiki the better.
5. Compile in Visual Studio
### Contributing Code ###
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Sonarr/Sonarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
- Rebase from Sonarr's develop branch, don't merge
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Radarr/Radarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
- Rebase from Radarr's develop branch, don't merge
- Make meaningful commits, or squash them
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
- Reach out to us on the forums or on IRC if you have any questions

View File

@@ -35,4 +35,11 @@ artifacts:
cache:
- '%USERPROFILE%\.nuget\packages'
- node_modules
- node_modules
only_commits:
files:
- src/
- osx/
- gulp/
- logo/

View File

@@ -2,7 +2,7 @@
| Service | Master | Develop |
|----------|:---------------------------:|:----------------------------:|
| AppVeyor | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr) | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr/develop.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr) |
| AppVeyor | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr) | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr-usby1/develop.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
| Travis | [![Travis](https://img.shields.io/travis/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://travis-ci.org/galli-leo/Radarr) | [![Travis](https://img.shields.io/travis/galli-leo/Radarr/develop.svg?maxAge=60&style=flat-square)](https://travis-ci.org/galli-leo/Radarr) |
This fork of Sonarr aims to turn it into something like Couchpotato.
@@ -26,6 +26,11 @@ This fork of Sonarr aims to turn it into something like Couchpotato.
## Download
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
Docker containers from [linuxserver.io](https://linuxserver.io) can be found here.
* [Radarr (x64)](https://hub.docker.com/r/linuxserver/radarr/)
* [Radarr (armhf)](https://hub.docker.com/r/lsioarmhf/radarr/)
* [Radarr (aarch64)](https://hub.docker.com/r/lsioarmhf/radarr-aarch64/)
For more up to date versions (but also sometimes broken), daily builds can be found here:
* [OSX](https://leonardogalli.ch/radarr/builds/latest.php?os=osx)
* [Windows](https://leonardogalli.ch/radarr/builds/latest.php?os=windows)

View File

@@ -27,6 +27,7 @@ namespace NzbDrone.Api.Calendar
Get["/NzbDrone.ics"] = options => GetCalendarFeed();
Get["/Sonarr.ics"] = options => GetCalendarFeed();
Get["/Radarr.ics"] = options => GetCalendarFeed();
}
private Response GetCalendarFeed()

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Api.Frontend.Mappers
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/backup/") && resourceUrl.ContainsIgnoreCase("nzbdrone_backup_") && resourceUrl.EndsWith(".zip");
return resourceUrl.StartsWith("/backup/") && resourceUrl.ContainsIgnoreCase("radarr_backup_") && resourceUrl.EndsWith(".zip");
}
}
}

View File

@@ -97,7 +97,7 @@ namespace NzbDrone.Api.Indexers
{
Guid = releaseInfo.Guid,
Quality = parsedMovieInfo.Quality,
//QualityWeight
QualityWeight = parsedMovieInfo.Quality.Quality.Id, //Id kinda hacky for wheight, but what you gonna do? TODO: Fix this shit!
Age = releaseInfo.Age,
AgeHours = releaseInfo.AgeHours,
AgeMinutes = releaseInfo.AgeMinutes,

View File

@@ -232,6 +232,7 @@
<Compile Include="RootFolders\RootFolderResource.cs" />
<Compile Include="SeasonPass\SeasonPassResource.cs" />
<Compile Include="Series\AlternateTitleResource.cs" />
<Compile Include="Series\MovieFileResource.cs" />
<Compile Include="Series\SeasonResource.cs" />
<Compile Include="SeasonPass\SeasonPassModule.cs" />
<Compile Include="Series\SeriesEditorModule.cs" />

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.REST;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Series;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Api.Movie
{
public class MovieFileResource : RestResource
{
public MovieFileResource()
{
}
//Todo: Sorters should be done completely on the client
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
//Todo: We should get the entire Profile instead of ID and Name separately
public int MovieId { get; set; }
public string RelativePath { get; set; }
public string Path { get; set; }
public long Size { get; set; }
public DateTime DateAdded { get; set; }
public string SceneName { get; set; }
public string ReleaseGroup { get; set; }
public QualityModel Quality { get; set; }
public MovieResource Movie { get; set; }
//TODO: Add series statistics as a property of the series (instead of individual properties)
}
public static class MovieFileResourceMapper
{
public static MovieFileResource ToResource(this MovieFile model)
{
if (model == null) return null;
MovieResource movie = null;
if (model.Movie != null)
{
model.Movie.LazyLoad();
if (model.Movie.Value != null)
{
//movie = model.Movie.Value.ToResource();
}
}
return new MovieFileResource
{
Id = model.Id,
RelativePath = model.RelativePath,
Path = model.Path,
Size = model.Size,
DateAdded = model.DateAdded,
ReleaseGroup = model.ReleaseGroup,
Quality = model.Quality,
Movie = movie,
};
}
public static MovieFile ToModel(this MovieFileResource resource)
{
if (resource == null) return null;
return new MovieFile
{
};
}
public static List<MovieFileResource> ToResource(this IEnumerable<MovieFile> movies)
{
return movies.Select(ToResource).ToList();
}
}
}

View File

@@ -55,6 +55,7 @@ namespace NzbDrone.Api.Movie
public AddMovieOptions AddOptions { get; set; }
public Ratings Ratings { get; set; }
public List<string> AlternativeTitles { get; set; }
public MovieFileResource MovieFile { get; set; }
//TODO: Add series statistics as a property of the series (instead of individual properties)
@@ -84,6 +85,7 @@ namespace NzbDrone.Api.Movie
long size = 0;
bool downloaded = false;
MovieFileResource movieFile = null;
if(model.MovieFile != null)
@@ -95,6 +97,7 @@ namespace NzbDrone.Api.Movie
{
size = model.MovieFile.Value.Size;
downloaded = true;
movieFile = model.MovieFile.Value.ToResource();
}
return new MovieResource
@@ -140,7 +143,8 @@ namespace NzbDrone.Api.Movie
Added = model.Added,
AddOptions = model.AddOptions,
AlternativeTitles = model.AlternativeTitles,
Ratings = model.Ratings
Ratings = model.Ratings,
MovieFile = movieFile
};
}

View File

@@ -23,7 +23,7 @@ namespace NzbDrone.Common
Console.WriteLine(" Commands:");
Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupContext.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupContext.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Don't open Sonarr in a browser", StartupContext.NO_BROWSER);
Console.WriteLine(" /{0} Don't open Radarr in a browser", StartupContext.NO_BROWSER);
Console.WriteLine(" <No Arguments> Run application in console mode.");
}

View File

@@ -9,12 +9,12 @@ namespace NzbDrone.Common.Http
static UserAgentBuilder()
{
UserAgent = string.Format("Sonarr/{0} ({1} {2})",
UserAgent = string.Format("Radarr/{0} ({1} {2})",
BuildInfo.Version,
OsInfo.Os, OsInfo.Version.ToString(2));
UserAgentSimplified = string.Format("Sonarr/{0}",
UserAgentSimplified = string.Format("Radarr/{0}",
BuildInfo.Version.ToString(2));
}
}
}
}

View File

@@ -103,9 +103,9 @@ namespace NzbDrone.Common.Instrumentation
private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{
RegisterAppFile(appFolderInfo, "appFileInfo", "sonarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "sonarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "sonarr.trace.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileInfo", "radarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "radarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "radarr.trace.txt", 50, LogLevel.Off);
}
private static LoggingRule RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)

View File

@@ -1,51 +0,0 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Fanzub;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.FanzubTests
{
[TestFixture]
public class FanzubFixture : CoreTest<Fanzub>
{
[SetUp]
public void Setup()
{
Subject.Definition = new IndexerDefinition()
{
Name = "Fanzub",
Settings = new FanzubSettings()
};
}
[Test]
public void should_parse_recent_feed_from_fanzub()
{
var recentFeed = ReadAllText(@"Files/Indexers/Fanzub/fanzub.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(3);
var releaseInfo = releases.First();
releaseInfo.Title.Should().Be("[Vivid] Hanayamata - 10 [A33D6606]");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
releaseInfo.DownloadUrl.Should().Be("http://fanzub.com/nzb/296464/Vivid%20Hanayamata%20-%2010.nzb");
releaseInfo.InfoUrl.Should().BeNullOrEmpty();
releaseInfo.CommentUrl.Should().BeNullOrEmpty();
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("2014/09/13 12:56:53"));
releaseInfo.Size.Should().Be(556246858);
}
}
}

View File

@@ -249,7 +249,6 @@
<Compile Include="IndexerTests\NewznabTests\NewznabFixture.cs" />
<Compile Include="IndexerTests\NewznabTests\NewznabRequestGeneratorFixture.cs" />
<Compile Include="IndexerTests\NewznabTests\NewznabSettingFixture.cs" />
<Compile Include="IndexerTests\FanzubTests\FanzubFixture.cs" />
<Compile Include="IndexerTests\OmgwtfnzbsTests\OmgwtfnzbsFixture.cs" />
<Compile Include="IndexerTests\SeasonSearchFixture.cs" />
<Compile Include="IndexerTests\TestIndexer.cs" />

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Backup
private string _backupTempFolder;
private static readonly Regex BackupFileRegex = new Regex(@"nzbdrone_backup_[._0-9]+\.zip", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex BackupFileRegex = new Regex(@"radarr_backup_[._0-9]+\.zip", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public BackupService(IMainDatabase maindDb,
IDiskTransferService diskTransferService,
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Backup
_archiveService = archiveService;
_logger = logger;
_backupTempFolder = Path.Combine(_appFolderInfo.TempFolder, "nzbdrone_backup");
_backupTempFolder = Path.Combine(_appFolderInfo.TempFolder, "radarr_backup");
}
public void Backup(BackupType backupType)
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Backup
_diskProvider.EnsureFolder(_backupTempFolder);
_diskProvider.EnsureFolder(GetBackupFolder(backupType));
var backupFilename = string.Format("nzbdrone_backup_{0:yyyy.MM.dd_HH.mm.ss}.zip", DateTime.Now);
var backupFilename = string.Format("radarr_backup_{0:yyyy.MM.dd_HH.mm.ss}.zip", DateTime.Now);
var backupPath = Path.Combine(GetBackupFolder(backupType), backupFilename);
Cleanup();

View File

@@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(114)]
public class remove_fanzub : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Delete.FromTable("Indexers").Row(new { Implementation = "Fanzub" });
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(115)]
public class update_movie_sorttitle : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
// Create.Column("SortTitle").OnTable("Series").AsString().Nullable();
Execute.WithConnection(SetSortTitles);
}
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, Title FROM Movies";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var id = seriesReader.GetInt32(0);
var title = seriesReader.GetString(1);
var sortTitle = Parser.Parser.NormalizeTitle(title).ToLower();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE Movies SET SortTitle = ? WHERE Id = ?";
updateCmd.AddParameter(sortTitle);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

View File

@@ -24,8 +24,6 @@ namespace NzbDrone.Core.DecisionEngine
{
CompareQuality,
CompareProtocol,
CompareEpisodeCount,
CompareEpisodeNumber,
ComparePeersIfTorrent,
CompareAgeIfUsenet,
CompareSize
@@ -56,6 +54,12 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareQuality(DownloadDecision x, DownloadDecision y)
{
if (x.IsForMovie && y.IsForMovie)
{
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Movie.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedMovieInfo.Quality.Quality)),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.ParsedMovieInfo.Quality.Revision.Real),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.ParsedMovieInfo.Quality.Revision.Version));
}
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));
@@ -63,6 +67,7 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareProtocol(DownloadDecision x, DownloadDecision y)
{
var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Series.Tags);
@@ -70,13 +75,22 @@ namespace NzbDrone.Core.DecisionEngine
return downloadProtocol == delayProfile.PreferredProtocol;
});
if (x.IsForMovie)
{
result = CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Movie.Tags);
var downloadProtocol = remoteEpisode.Release.DownloadProtocol;
return downloadProtocol == delayProfile.PreferredProtocol;
});
}
return result;
}
private int CompareEpisodeCount(DownloadDecision x, DownloadDecision y)
{
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.FullSeason),
CompareByReverse(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Episodes.Count));
return 0;
}
private int CompareEpisodeNumber(DownloadDecision x, DownloadDecision y)
@@ -88,20 +102,20 @@ namespace NzbDrone.Core.DecisionEngine
{
// Different protocols should get caught when checking the preferred protocol,
// since we're dealing with the same series in our comparisions
if (x.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent ||
y.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent)
if (x.RemoteMovie.Release.DownloadProtocol != DownloadProtocol.Torrent ||
y.RemoteMovie.Release.DownloadProtocol != DownloadProtocol.Torrent)
{
return 0;
}
return CompareAll(
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var seeders = TorrentInfo.GetSeeders(remoteEpisode.Release);
return seeders.HasValue && seeders.Value > 0 ? Math.Round(Math.Log10(seeders.Value)) : 0;
}),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var peers = TorrentInfo.GetPeers(remoteEpisode.Release);
@@ -117,7 +131,7 @@ namespace NzbDrone.Core.DecisionEngine
return 0;
}
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var ageHours = remoteEpisode.Release.AgeHours;
var age = remoteEpisode.Release.Age;
@@ -145,7 +159,7 @@ namespace NzbDrone.Core.DecisionEngine
{
// TODO: Is smaller better? Smaller for usenet could mean no par2 files.
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
}
}
}

View File

@@ -83,7 +83,7 @@ namespace NzbDrone.Core.DecisionEngine
{
if (parsedEpisodeInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace())
{
remoteEpisode.DownloadAllowed = true;
remoteEpisode.DownloadAllowed = false;
decision = new DownloadDecision(remoteEpisode, new Rejection("Hardcoded subs found: " + parsedEpisodeInfo.Quality.HardcodedSubs));
}
else
@@ -257,7 +257,7 @@ namespace NzbDrone.Core.DecisionEngine
}
catch (NotImplementedException e)
{
_logger.Info("Spec " + spec.GetType().Name + " does not care about movies.");
_logger.Trace("Spec " + spec.GetType().Name + " does not care about movies.");
}
catch (Exception e)
{
@@ -265,7 +265,6 @@ namespace NzbDrone.Core.DecisionEngine
e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
_logger.Error(e, "Couldn't evaluate decision on " + remoteEpisode.Release.Title + ", with spec: " + spec.GetType().Name);
return new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));//TODO UPDATE SPECS!
return null;
}
return null;

View File

@@ -26,10 +26,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .torrent file")]
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .torrent file")]
public string TorrentFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")]
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")]
public string WatchFolder { get; set; }
[DefaultValue(false)]
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
[DefaultValue(false)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Sonarr to Copy or Hardlink (depending on settings/system configuration)")]
[FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Radarr to Copy or Hardlink (depending on settings/system configuration)")]
public bool ReadOnly { get; set; }
public NzbDroneValidationResult Validate()

View File

@@ -19,10 +19,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
{
private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .nzb file")]
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .nzb file")]
public string NzbFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")]
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")]
public string WatchFolder { get; set; }
public NzbDroneValidationResult Validate()

View File

@@ -306,7 +306,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{
return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed")
{
DetailedDescription = "Sonarr as unable to add the label to Deluge."
DetailedDescription = "Radarr as unable to add the label to Deluge."
};
}
}

View File

@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
[FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(3, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(4, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

View File

@@ -337,7 +337,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
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 Sonarr from seeing completed downloads."
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
};
}

View File

@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

View File

@@ -372,7 +372,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 1.1.0 or higher.")
{
IsWarning = true,
DetailedDescription = "Sonarr may not be able to support new features added to SABnzbd when running develop versions."
DetailedDescription = "Radarr may not be able to support new features added to SABnzbd when running develop versions."
};
}
@@ -431,7 +431,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("", "Disable 'Check before download' option in Sabnbzd")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/switches/", Settings.Host, Settings.Port),
DetailedDescription = "Using Check before download affects Sonarr ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective."
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."
};
}
@@ -450,7 +450,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Enable Job folders")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port),
DetailedDescription = "Sonarr 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."
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."
};
}
}
@@ -475,7 +475,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable TV Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
};
}
}
@@ -489,7 +489,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable Movie Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
};
}
}
@@ -503,7 +503,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable Date Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
};
}
}

View File

@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr 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 TvCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

View File

@@ -165,6 +165,31 @@ namespace NzbDrone.Core.Download.Clients.Transmission
return hash;
}
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
{
_proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings);
if (remoteMovie.Release.Age < 14 && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
remoteMovie.Release.Age > 14 && Settings.OlderTvPriority == (int)TransmissionPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}
return hash;
}
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
{
_proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings);
if (remoteMovie.Release.Age < 14 && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
remoteMovie.Release.Age > 14 && Settings.OlderTvPriority == (int)TransmissionPriority.First)
{
_proxy.MoveTorrentToTopInQueue(hash, Settings);
}
return hash;
}
protected override void Test(List<ValidationFailure> failures)
{
failures.AddIfNotNull(TestConnection());
@@ -207,7 +232,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
_logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure")
{
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name)
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Radarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name)
};
}
catch (WebException ex)

View File

@@ -50,16 +50,16 @@ namespace NzbDrone.Core.Download.Clients.Transmission
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
public string TvCategory { get; set; }
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")]
public string TvDirectory { get; set; }
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that we're released within the last 14 days")]
public int RecentTvPriority { get; set; }
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that we're released over 14 days ago")]
public int OlderTvPriority { get; set; }
[FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]

View File

@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(5, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr 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 TvCategory { get; set; }
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")]

View File

@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]

View File

@@ -109,7 +109,11 @@ namespace NzbDrone.Core.Download
if (movie == null)
{
movie = _movieService.GetMovie(historyItem.MovieId);
if (historyItem != null)
{
movie = _movieService.GetMovie(historyItem.MovieId);
}
if (movie == null)
{

View File

@@ -72,7 +72,7 @@ namespace NzbDrone.Core.Download
if (grabbedItems.Empty())
{
trackedDownload.Warn("Download wasn't grabbed by sonarr, skipping");
trackedDownload.Warn("Download wasn't grabbed by Radarr, skipping");
return;
}

View File

@@ -51,6 +51,12 @@ namespace NzbDrone.Core.Download
continue;
}
if (report.Rejections.Any())
{
_logger.Debug("Rejecting release {0} because {1}", report.ToString(), report.Rejections.First().Reason);
continue;
}
if (remoteMovie == null || remoteMovie.Movie == null)
{
continue;

View File

@@ -1,30 +0,0 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Indexers.Fanzub
{
public class Fanzub : HttpIndexerBase<FanzubSettings>
{
public override string Name => "Fanzub";
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public Fanzub(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
: base(httpClient, indexerStatusService, configService, parsingService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new FanzubRequestGenerator() { Settings = Settings };
}
public override IParseIndexerResponse GetParser()
{
return new RssParser() { UseEnclosureUrl = true, UseEnclosureLength = true };
}
}
}

View File

@@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Indexers.Fanzub
{
public class FanzubRequestGenerator : IIndexerRequestGenerator
{
private static readonly Regex RemoveCharactersRegex = new Regex(@"[!?`]", RegexOptions.Compiled);
public FanzubSettings Settings { get; set; }
public int PageSize { get; set; }
public FanzubRequestGenerator()
{
PageSize = 100;
}
public virtual IndexerPageableRequestChain GetRecentRequests()
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(null));
return pageableRequests;
}
public virtual IndexerPageableRequestChain GetSearchRequests(SingleEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(AnimeEpisodeSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
var searchTitles = searchCriteria.QueryTitles.SelectMany(v => GetTitleSearchStrings(v, searchCriteria.AbsoluteEpisodeNumber)).ToList();
pageableRequests.Add(GetPagedRequests(string.Join("|", searchTitles)));
return pageableRequests;
}
public virtual IndexerPageableRequestChain GetSearchRequests(SpecialEpisodeSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private IEnumerable<IndexerRequest> GetPagedRequests(string query)
{
var url = new StringBuilder();
url.AppendFormat("{0}?cat=anime&max={1}", Settings.BaseUrl, PageSize);
if (query.IsNotNullOrWhiteSpace())
{
url.AppendFormat("&q={0}", query);
}
yield return new IndexerRequest(url.ToString(), HttpAccept.Rss);
}
private IEnumerable<string> GetTitleSearchStrings(string title, int absoluteEpisodeNumber)
{
var formats = new[] { "{0}%20{1:00}", "{0}%20-%20{1:00}" };
return formats.Select(s => "\"" + string.Format(s, CleanTitle(title), absoluteEpisodeNumber) + "\"");
}
private string CleanTitle(string title)
{
return RemoveCharactersRegex.Replace(title, "");
}
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
}
}

View File

@@ -1,33 +0,0 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Fanzub
{
public class FanzubSettingsValidator : AbstractValidator<FanzubSettings>
{
public FanzubSettingsValidator()
{
RuleFor(c => c.BaseUrl).ValidRootUrl();
}
}
public class FanzubSettings : IProviderConfig
{
private static readonly FanzubSettingsValidator Validator = new FanzubSettingsValidator();
public FanzubSettings()
{
BaseUrl = "http://fanzub.com/rss/";
}
[FieldDefinition(0, Label = "Rss URL", HelpText = "Enter to URL to an Fanzub compatible RSS feed")]
public string BaseUrl { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -106,8 +106,8 @@ namespace NzbDrone.Core.Indexers.Newznab
}
if (capabilities.SupportedTvSearchParameters != null &&
new[] { "q", "imdb" }.Any(v => capabilities.SupportedMovieSearchParamters.Contains(v)) &&
new[] { "imdbtitle", "imdbyear" }.All(v => capabilities.SupportedMovieSearchParamters.Contains(v)))
new[] { "q", "imdbid" }.Any(v => capabilities.SupportedMovieSearchParameters.Contains(v)) &&
new[] { "imdbtitle", "imdbyear" }.All(v => capabilities.SupportedMovieSearchParameters.Contains(v)))
{
return null;
}

View File

@@ -8,7 +8,7 @@ namespace NzbDrone.Core.Indexers.Newznab
public int MaxPageSize { get; set; }
public string[] SupportedSearchParameters { get; set; }
public string[] SupportedTvSearchParameters { get; set; }
public string[] SupportedMovieSearchParamters { get; set; }
public string[] SupportedMovieSearchParameters { get; set; }
public bool SupportsAggregateIdSearch { get; set; }
public List<NewznabCategory> Categories { get; set; }
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Indexers.Newznab
DefaultPageSize = 100;
MaxPageSize = 100;
SupportedSearchParameters = new[] { "q" };
SupportedMovieSearchParamters = new[] { "q", "imdb", "imdbtitle", "imdbyear" };
SupportedMovieSearchParameters = new[] { "q", "imdbid", "imdbtitle", "imdbyear" };
SupportedTvSearchParameters = new[] { "q", "rid", "season", "ep" }; // This should remain 'rid' for older newznab installs.
SupportsAggregateIdSearch = false;
Categories = new List<NewznabCategory>();

View File

@@ -102,11 +102,11 @@ namespace NzbDrone.Core.Indexers.Newznab
var xmlMovieSearch = xmlSearching.Element("movie-search");
if (xmlMovieSearch == null || xmlMovieSearch.Attribute("available").Value != "yes")
{
capabilities.SupportedMovieSearchParamters = null;
capabilities.SupportedMovieSearchParameters = null;
}
else if (xmlMovieSearch.Attribute("supportedParams") != null)
{
capabilities.SupportedMovieSearchParamters = xmlMovieSearch.Attribute("supportedParams").Value.Split(',');
capabilities.SupportedMovieSearchParameters = xmlMovieSearch.Attribute("supportedParams").Value.Split(',');
capabilities.SupportsAggregateIdSearch = true;
}
}

View File

@@ -91,8 +91,8 @@ namespace NzbDrone.Core.Indexers.Newznab
{
var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
return capabilities.SupportedMovieSearchParamters != null &&
capabilities.SupportedMovieSearchParamters.Contains("imdb");
return capabilities.SupportedMovieSearchParameters != null &&
capabilities.SupportedMovieSearchParameters.Contains("imdbid");
}
}
@@ -112,7 +112,7 @@ namespace NzbDrone.Core.Indexers.Newznab
var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
if (capabilities.SupportedMovieSearchParamters != null)
if (capabilities.SupportedMovieSearchParameters != null)
{
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories.Concat(Settings.AnimeCategories), "movie", ""));
}
@@ -124,7 +124,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{
var pageableRequests = new IndexerPageableRequestChain();
if(SupportsMovieSearch)
if (SupportsMovieSearch)
{
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "movie",
string.Format("&imdbid={0}", searchCriteria.Movie.ImdbId.Substring(2)))); //strip off the "tt" - VERY HACKY

View File

@@ -48,9 +48,7 @@ namespace NzbDrone.Core.Indexers.Newznab
protected override ReleaseInfo ProcessItem(XElement item, ReleaseInfo releaseInfo)
{
releaseInfo = base.ProcessItem(item, releaseInfo);
releaseInfo.TvdbId = GetTvdbId(item);
releaseInfo.TvRageId = GetTvRageId(item);
releaseInfo.ImdbId = GetImdbId(item);
return releaseInfo;
}
@@ -114,27 +112,14 @@ namespace NzbDrone.Core.Indexers.Newznab
return url;
}
protected virtual int GetTvdbId(XElement item)
protected virtual int GetImdbId(XElement item)
{
var tvdbIdString = TryGetNewznabAttribute(item, "tvdbid");
int tvdbId;
var imdbIdString = TryGetNewznabAttribute(item, "imdb");
int imdbId;
if (!tvdbIdString.IsNullOrWhiteSpace() && int.TryParse(tvdbIdString, out tvdbId))
if (!imdbIdString.IsNullOrWhiteSpace() && int.TryParse(imdbIdString, out imdbId))
{
return tvdbId;
}
return 0;
}
protected virtual int GetTvRageId(XElement item)
{
var tvRageIdString = TryGetNewznabAttribute(item, "rageid");
int tvRageId;
if (!tvRageIdString.IsNullOrWhiteSpace() && int.TryParse(tvRageIdString, out tvRageId))
{
return tvRageId;
return imdbId;
}
return 0;

View File

@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers.Newznab
public NewznabSettings()
{
Categories = new[] { 2030, 2040, 2050 };
Categories = new[] { 2030, 2035, 2040, 2045, 2050 };
AnimeCategories = Enumerable.Empty<int>();
}

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Indexers.Wombles
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new RssIndexerRequestGenerator("http://newshost.co.za/rss/?sec=TV&fr=false");
return new RssIndexerRequestGenerator("http://newshost.co.za/rss/?sec=Movies&fr=false");
}
public Wombles(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)

View File

@@ -0,0 +1,17 @@
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.MediaFiles.Commands
{
public class DownloadedMovieScanCommand : Command
{
public override bool SendUpdatesToClient => SendUpdates;
public bool SendUpdates { get; set; }
// Properties used by third-party apps, do not modify.
public string Path { get; set; }
public string DownloadClientId { get; set; }
public ImportMode ImportMode { get; set; }
}
}

View File

@@ -1,265 +1,107 @@
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Messaging.Commands;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model;
using System.Text;
namespace NzbDrone.Core.MediaFiles
{
public interface IDownloadedMovieImportService
{
List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo);
List<ImportResult> ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null);
bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie);
}
public class DownloadedMovieImportService : IDownloadedMovieImportService
public class DownloadedMovieCommandService : IExecute<DownloadedMovieScanCommand>
{
private readonly IDownloadedMovieImportService _downloadedMovieImportService;
private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IDiskProvider _diskProvider;
private readonly IDiskScanService _diskScanService;
private readonly IMovieService _movieService;
private readonly IParsingService _parsingService;
private readonly IMakeImportDecision _importDecisionMaker;
private readonly IImportApprovedMovie _importApprovedMovie;
private readonly IDetectSample _detectSample;
private readonly IConfigService _configService;
private readonly Logger _logger;
public DownloadedMovieImportService(IDiskProvider diskProvider,
IDiskScanService diskScanService,
IMovieService movieService,
IParsingService parsingService,
IMakeImportDecision importDecisionMaker,
IImportApprovedMovie importApprovedMovie,
IDetectSample detectSample,
Logger logger)
public DownloadedMovieCommandService(IDownloadedMovieImportService downloadedMovieImportService,
ITrackedDownloadService trackedDownloadService,
IDiskProvider diskProvider,
IConfigService configService,
Logger logger)
{
_downloadedMovieImportService = downloadedMovieImportService;
_trackedDownloadService = trackedDownloadService;
_diskProvider = diskProvider;
_diskScanService = diskScanService;
_movieService = movieService;
_parsingService = parsingService;
_importDecisionMaker = importDecisionMaker;
_importApprovedMovie = importApprovedMovie;
_detectSample = detectSample;
_configService = configService;
_logger = logger;
}
public List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo)
private List<ImportResult> ProcessDroneFactoryFolder()
{
var results = new List<ImportResult>();
var downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder;
foreach (var subFolder in _diskProvider.GetDirectories(directoryInfo.FullName))
if (string.IsNullOrEmpty(downloadedEpisodesFolder))
{
var folderResults = ProcessFolder(new DirectoryInfo(subFolder), ImportMode.Auto, null);
results.AddRange(folderResults);
}
foreach (var videoFile in _diskScanService.GetVideoFiles(directoryInfo.FullName, false))
{
var fileResults = ProcessFile(new FileInfo(videoFile), ImportMode.Auto, null);
results.AddRange(fileResults);
}
return results;
}
public List<ImportResult> ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null)
{
if (_diskProvider.FolderExists(path))
{
var directoryInfo = new DirectoryInfo(path);
if (movie == null)
{
return ProcessFolder(directoryInfo, importMode, downloadClientItem);
}
return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem);
}
if (_diskProvider.FileExists(path))
{
var fileInfo = new FileInfo(path);
if (movie == null)
{
return ProcessFile(fileInfo, importMode, downloadClientItem);
}
return ProcessFile(fileInfo, importMode, movie, downloadClientItem);
}
_logger.Error("Import failed, path does not exist or is not accessible by Sonarr: {0}", path);
return new List<ImportResult>();
}
public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie)
{
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
var rarFiles = _diskProvider.GetFiles(directoryInfo.FullName, SearchOption.AllDirectories).Where(f => Path.GetExtension(f) == ".rar");
foreach (var videoFile in videoFiles)
{
var episodeParseResult = Parser.Parser.ParseTitle(Path.GetFileName(videoFile));
if (episodeParseResult == null)
{
_logger.Warn("Unable to parse file on import: [{0}]", videoFile);
return false;
}
var size = _diskProvider.GetFileSize(videoFile);
var quality = QualityParser.ParseQuality(videoFile);
if (!_detectSample.IsSample(movie, quality, videoFile, size, episodeParseResult.IsPossibleSpecialEpisode))
{
_logger.Warn("Non-sample file detected: [{0}]", videoFile);
return false;
}
}
if (rarFiles.Any(f => _diskProvider.GetFileSize(f) > 10.Megabytes()))
{
_logger.Warn("RAR file detected, will require manual cleanup");
return false;
}
return true;
}
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, DownloadClientItem downloadClientItem)
{
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var movie = _parsingService.GetMovie(cleanedUpName);
if (movie == null)
{
_logger.Debug("Unknown Movie {0}", cleanedUpName);
return new List<ImportResult>
{
UnknownMovieResult("Unknown Movie")
};
}
return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem);
}
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem)
{
if (_movieService.MoviePathExists(directoryInfo.FullName))
{
_logger.Warn("Unable to process folder that is mapped to an existing show");
_logger.Trace("Drone Factory folder is not configured");
return new List<ImportResult>();
}
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name);
if (folderInfo != null)
if (!_diskProvider.FolderExists(downloadedEpisodesFolder))
{
_logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality);
_logger.Warn("Drone Factory folder [{0}] doesn't exist.", downloadedEpisodesFolder);
return new List<ImportResult>();
}
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
return _downloadedMovieImportService.ProcessRootFolder(new DirectoryInfo(downloadedEpisodesFolder));
}
if (downloadClientItem == null)
private List<ImportResult> ProcessPath(DownloadedMovieScanCommand message)
{
if (!_diskProvider.FolderExists(message.Path) && !_diskProvider.FileExists(message.Path))
{
foreach (var videoFile in videoFiles)
_logger.Warn("Folder/File specified for import scan [{0}] doesn't exist.", message.Path);
return new List<ImportResult>();
}
if (message.DownloadClientId.IsNotNullOrWhiteSpace())
{
var trackedDownload = _trackedDownloadService.Find(message.DownloadClientId);
if (trackedDownload != null)
{
if (_diskProvider.IsFileLocked(videoFile))
{
return new List<ImportResult>
{
FileIsLockedResult(videoFile)
};
}
_logger.Debug("External directory scan request for known download {0}. [{1}]", message.DownloadClientId, message.Path);
return _downloadedMovieImportService.ProcessPath(message.Path, message.ImportMode, trackedDownload.RemoteMovie.Movie, trackedDownload.DownloadItem);
}
else
{
_logger.Warn("External directory scan request for unknown download {0}, attempting normal import. [{1}]", message.DownloadClientId, message.Path);
return _downloadedMovieImportService.ProcessPath(message.Path, message.ImportMode);
}
}
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true);
var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
return _downloadedMovieImportService.ProcessPath(message.Path, message.ImportMode);
}
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) &&
importResults.Any(i => i.Result == ImportResultType.Imported) &&
ShouldDeleteFolder(directoryInfo, movie))
public void Execute(DownloadedMovieScanCommand message)
{
List<ImportResult> importResults;
if (message.Path.IsNotNullOrWhiteSpace())
{
_logger.Debug("Deleting folder after importing valid files");
_diskProvider.DeleteFolder(directoryInfo.FullName, true);
importResults = ProcessPath(message);
}
else
{
importResults = ProcessDroneFactoryFolder();
}
return importResults;
}
private List<ImportResult> ProcessFile(FileInfo fileInfo, ImportMode importMode, DownloadClientItem downloadClientItem)
{
var movie = _parsingService.GetMovie(Path.GetFileNameWithoutExtension(fileInfo.Name));
if (movie == null)
if (importResults == null || importResults.All(v => v.Result != ImportResultType.Imported))
{
_logger.Debug("Unknown Movie for file: {0}", fileInfo.Name);
return new List<ImportResult>
{
UnknownMovieResult(string.Format("Unknown Movie for file: {0}", fileInfo.Name), fileInfo.FullName)
};
// Atm we don't report it as a command failure, coz that would cause the download to be failed.
// Changing the message won't do a thing either, coz it will get set to 'Completed' a msec later.
//message.SetMessage("Failed to import");
}
return ProcessFile(fileInfo, importMode, movie, downloadClientItem);
}
private List<ImportResult> ProcessFile(FileInfo fileInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem)
{
if (Path.GetFileNameWithoutExtension(fileInfo.Name).StartsWith("._"))
{
_logger.Debug("[{0}] starts with '._', skipping", fileInfo.FullName);
return new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, new Rejection("Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'")
};
}
if (downloadClientItem == null)
{
if (_diskProvider.IsFileLocked(fileInfo.FullName))
{
return new List<ImportResult>
{
FileIsLockedResult(fileInfo.FullName)
};
}
}
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, null, true);
return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
}
private string GetCleanedUpFolderName(string folder)
{
folder = folder.Replace("_UNPACK_", "")
.Replace("_FAILED_", "");
return folder;
}
private ImportResult FileIsLockedResult(string videoFile)
{
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later");
}
private ImportResult UnknownMovieResult(string message, string videoFile = null)
{
var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile };
return new ImportResult(new ImportDecision(localEpisode, new Rejection("Unknown Movie")), message);
}
}
}

View File

@@ -0,0 +1,266 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.MediaFiles.Commands;
namespace NzbDrone.Core.MediaFiles
{
public interface IDownloadedMovieImportService
{
List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo);
List<ImportResult> ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null);
bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie);
}
public class DownloadedMovieImportService : IDownloadedMovieImportService
{
private readonly IDiskProvider _diskProvider;
private readonly IDiskScanService _diskScanService;
private readonly IMovieService _movieService;
private readonly IParsingService _parsingService;
private readonly IMakeImportDecision _importDecisionMaker;
private readonly IImportApprovedMovie _importApprovedMovie;
private readonly IDetectSample _detectSample;
private readonly Logger _logger;
public DownloadedMovieImportService(IDiskProvider diskProvider,
IDiskScanService diskScanService,
IMovieService movieService,
IParsingService parsingService,
IMakeImportDecision importDecisionMaker,
IImportApprovedMovie importApprovedMovie,
IDetectSample detectSample,
Logger logger)
{
_diskProvider = diskProvider;
_diskScanService = diskScanService;
_movieService = movieService;
_parsingService = parsingService;
_importDecisionMaker = importDecisionMaker;
_importApprovedMovie = importApprovedMovie;
_detectSample = detectSample;
_logger = logger;
}
public List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo)
{
var results = new List<ImportResult>();
foreach (var subFolder in _diskProvider.GetDirectories(directoryInfo.FullName))
{
var folderResults = ProcessFolder(new DirectoryInfo(subFolder), ImportMode.Auto, null);
results.AddRange(folderResults);
}
foreach (var videoFile in _diskScanService.GetVideoFiles(directoryInfo.FullName, false))
{
var fileResults = ProcessFile(new FileInfo(videoFile), ImportMode.Auto, null);
results.AddRange(fileResults);
}
return results;
}
public List<ImportResult> ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null)
{
if (_diskProvider.FolderExists(path))
{
var directoryInfo = new DirectoryInfo(path);
if (movie == null)
{
return ProcessFolder(directoryInfo, importMode, downloadClientItem);
}
return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem);
}
if (_diskProvider.FileExists(path))
{
var fileInfo = new FileInfo(path);
if (movie == null)
{
return ProcessFile(fileInfo, importMode, downloadClientItem);
}
return ProcessFile(fileInfo, importMode, movie, downloadClientItem);
}
_logger.Error("Import failed, path does not exist or is not accessible by Radarr: {0}", path);
return new List<ImportResult>();
}
public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie)
{
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
var rarFiles = _diskProvider.GetFiles(directoryInfo.FullName, SearchOption.AllDirectories).Where(f => Path.GetExtension(f) == ".rar");
foreach (var videoFile in videoFiles)
{
var episodeParseResult = Parser.Parser.ParseTitle(Path.GetFileName(videoFile));
if (episodeParseResult == null)
{
_logger.Warn("Unable to parse file on import: [{0}]", videoFile);
return false;
}
var size = _diskProvider.GetFileSize(videoFile);
var quality = QualityParser.ParseQuality(videoFile);
if (!_detectSample.IsSample(movie, quality, videoFile, size, episodeParseResult.IsPossibleSpecialEpisode))
{
_logger.Warn("Non-sample file detected: [{0}]", videoFile);
return false;
}
}
if (rarFiles.Any(f => _diskProvider.GetFileSize(f) > 10.Megabytes()))
{
_logger.Warn("RAR file detected, will require manual cleanup");
return false;
}
return true;
}
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, DownloadClientItem downloadClientItem)
{
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var movie = _parsingService.GetMovie(cleanedUpName);
if (movie == null)
{
_logger.Debug("Unknown Movie {0}", cleanedUpName);
return new List<ImportResult>
{
UnknownMovieResult("Unknown Movie")
};
}
return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem);
}
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem)
{
if (_movieService.MoviePathExists(directoryInfo.FullName))
{
_logger.Warn("Unable to process folder that is mapped to an existing show");
return new List<ImportResult>();
}
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name);
if (folderInfo != null)
{
_logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality);
}
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
if (downloadClientItem == null)
{
foreach (var videoFile in videoFiles)
{
if (_diskProvider.IsFileLocked(videoFile))
{
return new List<ImportResult>
{
FileIsLockedResult(videoFile)
};
}
}
}
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true);
var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) &&
importResults.Any(i => i.Result == ImportResultType.Imported) &&
ShouldDeleteFolder(directoryInfo, movie))
{
_logger.Debug("Deleting folder after importing valid files");
_diskProvider.DeleteFolder(directoryInfo.FullName, true);
}
return importResults;
}
private List<ImportResult> ProcessFile(FileInfo fileInfo, ImportMode importMode, DownloadClientItem downloadClientItem)
{
var movie = _parsingService.GetMovie(Path.GetFileNameWithoutExtension(fileInfo.Name));
if (movie == null)
{
_logger.Debug("Unknown Movie for file: {0}", fileInfo.Name);
return new List<ImportResult>
{
UnknownMovieResult(string.Format("Unknown Movie for file: {0}", fileInfo.Name), fileInfo.FullName)
};
}
return ProcessFile(fileInfo, importMode, movie, downloadClientItem);
}
private List<ImportResult> ProcessFile(FileInfo fileInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem)
{
if (Path.GetFileNameWithoutExtension(fileInfo.Name).StartsWith("._"))
{
_logger.Debug("[{0}] starts with '._', skipping", fileInfo.FullName);
return new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, new Rejection("Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'")
};
}
if (downloadClientItem == null)
{
if (_diskProvider.IsFileLocked(fileInfo.FullName))
{
return new List<ImportResult>
{
FileIsLockedResult(fileInfo.FullName)
};
}
}
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, null, true);
return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
}
private string GetCleanedUpFolderName(string folder)
{
folder = folder.Replace("_UNPACK_", "")
.Replace("_FAILED_", "");
return folder;
}
private ImportResult FileIsLockedResult(string videoFile)
{
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later");
}
private ImportResult UnknownMovieResult(string message, string videoFile = null)
{
var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile };
return new ImportResult(new ImportDecision(localEpisode, new Rejection("Unknown Movie")), message);
}
}
}

View File

@@ -10,5 +10,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
public List<int> EpisodeIds { get; set; }
public QualityModel Quality { get; set; }
public string DownloadId { get; set; }
public int MovieId { get; set; }
}
}

View File

@@ -17,5 +17,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
public QualityModel Quality { get; set; }
public string DownloadId { get; set; }
public IEnumerable<Rejection> Rejections { get; set; }
public Movie Movie { get; set; }
}
}

View File

@@ -30,11 +30,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
private readonly IDiskScanService _diskScanService;
private readonly IMakeImportDecision _importDecisionMaker;
private readonly ISeriesService _seriesService;
private readonly IMovieService _movieService;
private readonly IEpisodeService _episodeService;
private readonly IVideoFileInfoReader _videoFileInfoReader;
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
private readonly IImportApprovedMovie _importApprovedMovie;
private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
private readonly IDownloadedMovieImportService _downloadedMovieImportService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
@@ -43,11 +46,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
IDiskScanService diskScanService,
IMakeImportDecision importDecisionMaker,
ISeriesService seriesService,
IMovieService movieService,
IEpisodeService episodeService,
IVideoFileInfoReader videoFileInfoReader,
IImportApprovedEpisodes importApprovedEpisodes,
IImportApprovedMovie importApprovedMovie,
ITrackedDownloadService trackedDownloadService,
IDownloadedEpisodesImportService downloadedEpisodesImportService,
IDownloadedMovieImportService downloadedMovieImportService,
IEventAggregator eventAggregator,
Logger logger)
{
@@ -56,11 +62,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
_diskScanService = diskScanService;
_importDecisionMaker = importDecisionMaker;
_seriesService = seriesService;
_movieService = movieService;
_episodeService = episodeService;
_videoFileInfoReader = videoFileInfoReader;
_importApprovedEpisodes = importApprovedEpisodes;
_importApprovedMovie = importApprovedMovie;
_trackedDownloadService = trackedDownloadService;
_downloadedEpisodesImportService = downloadedEpisodesImportService;
_downloadedMovieImportService = downloadedMovieImportService;
_eventAggregator = eventAggregator;
_logger = logger;
}
@@ -126,62 +135,128 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
var relativeFile = folder.GetRelativePath(file);
var series = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);
var movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]);
if (series == null)
if (movie == null)
{
series = _parsingService.GetSeries(relativeFile);
movie = _parsingService.GetMovie(relativeFile);
}
if (series == null && downloadId.IsNotNullOrWhiteSpace())
if (movie == null && downloadId.IsNotNullOrWhiteSpace())
{
var trackedDownload = _trackedDownloadService.Find(downloadId);
series = trackedDownload.RemoteEpisode.Series;
movie = trackedDownload.RemoteMovie.Movie;
}
if (series == null)
if (movie == null)
{
var localEpisode = new LocalEpisode();
localEpisode.Path = file;
localEpisode.Quality = QualityParser.ParseQuality(file);
localEpisode.Size = _diskProvider.GetFileSize(file);
var localMovie = new LocalMovie()
{
Path = file,
Quality = QualityParser.ParseQuality(file),
Size = _diskProvider.GetFileSize(file)
};
return MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), folder, downloadId);
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), folder, downloadId);
}
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> {file},
series, null, SceneSource(series, folder));
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
movie, null, SceneSource(movie, folder));
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
}
//private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
//{
// if (folder.IsNullOrWhiteSpace())
// {
// folder = new FileInfo(file).Directory.FullName;
// }
// var relativeFile = folder.GetRelativePath(file);
// var series = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);
// if (series == null)
// {
// series = _parsingService.GetSeries(relativeFile);
// }
// if (series == null && downloadId.IsNotNullOrWhiteSpace())
// {
// var trackedDownload = _trackedDownloadService.Find(downloadId);
// series = trackedDownload.RemoteEpisode.Series;
// }
// if (series == null)
// {
// var localEpisode = new LocalEpisode();
// localEpisode.Path = file;
// localEpisode.Quality = QualityParser.ParseQuality(file);
// localEpisode.Size = _diskProvider.GetFileSize(file);
// return MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), folder, downloadId);
// }
// var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> {file},
// series, null, SceneSource(series, folder));
// return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
//}
private bool SceneSource(Series series, string folder)
{
return !(series.Path.PathEquals(folder) || series.Path.IsParentPath(folder));
}
private bool SceneSource(Movie movie, string folder)
{
return !(movie.Path.PathEquals(folder) || movie.Path.IsParentPath(folder));
}
//private ManualImportItem MapItem(ImportDecision decision, string folder, string downloadId)
//{
// var item = new ManualImportItem();
// item.Path = decision.LocalEpisode.Path;
// item.RelativePath = folder.GetRelativePath(decision.LocalEpisode.Path);
// item.Name = Path.GetFileNameWithoutExtension(decision.LocalEpisode.Path);
// item.DownloadId = downloadId;
// if (decision.LocalEpisode.Series != null)
// {
// item.Series = decision.LocalEpisode.Series;
// }
// if (decision.LocalEpisode.Episodes.Any())
// {
// item.SeasonNumber = decision.LocalEpisode.SeasonNumber;
// item.Episodes = decision.LocalEpisode.Episodes;
// }
// item.Quality = decision.LocalEpisode.Quality;
// item.Size = _diskProvider.GetFileSize(decision.LocalEpisode.Path);
// item.Rejections = decision.Rejections;
// return item;
//}
private ManualImportItem MapItem(ImportDecision decision, string folder, string downloadId)
{
var item = new ManualImportItem();
item.Path = decision.LocalEpisode.Path;
item.RelativePath = folder.GetRelativePath(decision.LocalEpisode.Path);
item.Name = Path.GetFileNameWithoutExtension(decision.LocalEpisode.Path);
item.Path = decision.LocalMovie.Path;
item.RelativePath = folder.GetRelativePath(decision.LocalMovie.Path);
item.Name = Path.GetFileNameWithoutExtension(decision.LocalMovie.Path);
item.DownloadId = downloadId;
if (decision.LocalEpisode.Series != null)
if (decision.LocalMovie.Movie != null)
{
item.Series = decision.LocalEpisode.Series;
item.Movie = decision.LocalMovie.Movie;
}
if (decision.LocalEpisode.Episodes.Any())
{
item.SeasonNumber = decision.LocalEpisode.SeasonNumber;
item.Episodes = decision.LocalEpisode.Episodes;
}
item.Quality = decision.LocalEpisode.Quality;
item.Size = _diskProvider.GetFileSize(decision.LocalEpisode.Path);
item.Quality = decision.LocalMovie.Quality;
item.Size = _diskProvider.GetFileSize(decision.LocalMovie.Path);
item.Rejections = decision.Rejections;
return item;
@@ -199,45 +274,43 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
_logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);
var file = message.Files[i];
var series = _seriesService.GetSeries(file.SeriesId);
var episodes = _episodeService.GetEpisodes(file.EpisodeIds);
var parsedEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo();
var movie = _movieService.GetMovie(file.MovieId);
var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path) ?? new ParsedMovieInfo();
var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
var existingFile = series.Path.IsParentPath(file.Path);
var existingFile = movie.Path.IsParentPath(file.Path);
var localEpisode = new LocalEpisode
var localMovie = new LocalMovie
{
ExistingFile = false,
Episodes = episodes,
MediaInfo = mediaInfo,
ParsedEpisodeInfo = parsedEpisodeInfo,
ParsedMovieInfo = parsedMovieInfo,
Path = file.Path,
Quality = file.Quality,
Series = series,
Movie = movie,
Size = 0
};
//TODO: Cleanup non-tracked downloads
var importDecision = new ImportDecision(localEpisode);
var importDecision = new ImportDecision(localMovie);
if (file.DownloadId.IsNullOrWhiteSpace())
{
imported.AddRange(_importApprovedEpisodes.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
imported.AddRange(_importApprovedMovie.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
}
else
{
var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
var importResult = _importApprovedEpisodes.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
var importResult = _importApprovedMovie.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
imported.Add(importResult);
importedTrackedDownload.Add(new ManuallyImportedFile
{
TrackedDownload = trackedDownload,
ImportResult = importResult
});
{
TrackedDownload = trackedDownload,
ImportResult = importResult
});
}
}
@@ -249,20 +322,98 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
{
if (_downloadedEpisodesImportService.ShouldDeleteFolder(
if (_downloadedMovieImportService.ShouldDeleteFolder(
new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
trackedDownload.RemoteEpisode.Series) && !trackedDownload.DownloadItem.IsReadOnly)
trackedDownload.RemoteMovie.Movie) && !trackedDownload.DownloadItem.IsReadOnly)
{
_diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
}
}
if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count))
if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, 1)) //TODO: trackedDownload.RemoteMovie.Movie.Count is always 1?
{
trackedDownload.State = TrackedDownloadStage.Imported;
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
}
}
}
//public void Execute(ManualImportCommand message)
//{
// _logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);
// var imported = new List<ImportResult>();
// var importedTrackedDownload = new List<ManuallyImportedFile>();
// for (int i = 0; i < message.Files.Count; i++)
// {
// _logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);
// var file = message.Files[i];
// var series = _seriesService.GetSeries(file.SeriesId);
// var episodes = _episodeService.GetEpisodes(file.EpisodeIds);
// var parsedEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo();
// var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
// var existingFile = series.Path.IsParentPath(file.Path);
// var localEpisode = new LocalEpisode
// {
// ExistingFile = false,
// Episodes = episodes,
// MediaInfo = mediaInfo,
// ParsedEpisodeInfo = parsedEpisodeInfo,
// Path = file.Path,
// Quality = file.Quality,
// Series = series,
// Size = 0
// };
// //TODO: Cleanup non-tracked downloads
// var importDecision = new ImportDecision(localEpisode);
// if (file.DownloadId.IsNullOrWhiteSpace())
// {
// imported.AddRange(_importApprovedEpisodes.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
// }
// else
// {
// var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
// var importResult = _importApprovedEpisodes.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
// imported.Add(importResult);
// importedTrackedDownload.Add(new ManuallyImportedFile
// {
// TrackedDownload = trackedDownload,
// ImportResult = importResult
// });
// }
// }
// _logger.ProgressTrace("Manually imported {0} files", imported.Count);
// foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
// {
// var trackedDownload = groupedTrackedDownload.First().TrackedDownload;
// if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
// {
// if (_downloadedEpisodesImportService.ShouldDeleteFolder(
// new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
// trackedDownload.RemoteEpisode.Series) && !trackedDownload.DownloadItem.IsReadOnly)
// {
// _diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
// }
// }
// if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count))
// {
// trackedDownload.State = TrackedDownloadStage.Imported;
// _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
// }
// }
//}
}
}

View File

@@ -55,7 +55,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public Production_Companies[] production_companies { get; set; }
public Production_Countries[] production_countries { get; set; }
public string release_date { get; set; }
public int revenue { get; set; }
public long revenue { get; set; }
public int runtime { get; set; }
public Spoken_Languages[] spoken_languages { get; set; }
public string status { get; set; }

View File

@@ -189,7 +189,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
{
var lowerTitle = title.ToLower();
var parserResult = Parser.Parser.ParseMovieTitle(title);
var parserResult = Parser.Parser.ParseMovieTitle(title, true);
var yearTerm = "";
@@ -327,7 +327,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
imdbMovie.TmdbId = result.id;
try
{
imdbMovie.SortTitle = result.title;
imdbMovie.SortTitle = Parser.Parser.NormalizeTitle(result.title);
imdbMovie.Title = result.title;
string titleSlug = result.title;
imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-");

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnGrab(GrabMessage grabMessage)
{
const string subject = "Sonarr [TV] - Grabbed";
const string subject = "Radarr [TV] - Grabbed";
var body = string.Format("{0} sent to queue.", grabMessage.Message);
_emailService.SendEmail(Settings, subject, body);
@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnDownload(DownloadMessage message)
{
const string subject = "Sonarr [TV] - Downloaded";
const string subject = "Radarr [TV] - Downloaded";
var body = string.Format("{0} Downloaded and sorted.", message.Message);
_emailService.SendEmail(Settings, subject, body);

View File

@@ -64,7 +64,7 @@ namespace NzbDrone.Core.Notifications.Email
try
{
SendEmail(settings, "Sonarr - Test Notification", body);
SendEmail(settings, "Radarr - Test Notification", body);
}
catch (Exception ex)
{

View File

@@ -18,14 +18,14 @@ namespace NzbDrone.Core.Notifications.Join
public override void OnGrab(GrabMessage grabMessage)
{
const string title = "Sonarr - Episode Grabbed";
const string title = "Radarr - Episode Grabbed";
_proxy.SendNotification(title, grabMessage.Message, Settings);
}
public override void OnDownload(DownloadMessage message)
{
const string title = "Sonarr - Episode Downloaded";
const string title = "Radarr - Episode Downloaded";
_proxy.SendNotification(title, message.Message, Settings);
}

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser
public override void OnGrab(GrabMessage grabMessage)
{
const string title = "Sonarr - Grabbed";
const string title = "Radarr - Grabbed";
if (Settings.Notify)
{
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser
public override void OnDownload(DownloadMessage message)
{
const string title = "Sonarr - Downloaded";
const string title = "Radarr - Downloaded";
if (Settings.Notify)
{

View File

@@ -18,13 +18,13 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(GrabMessage grabMessage)
{
const string header = "Sonarr [TV] - Grabbed";
const string header = "Radarr [TV] - Grabbed";
_plexClientService.Notify(Settings, header, grabMessage.Message);
}
public override void OnDownload(DownloadMessage message)
{
const string header = "Sonarr [TV] - Downloaded";
const string header = "Radarr [TV] - Downloaded";
_plexClientService.Notify(Settings, header, message.Message);
}

View File

@@ -23,14 +23,14 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(GrabMessage grabMessage)
{
const string header = "Sonarr - Grabbed";
const string header = "Radarr - Grabbed";
Notify(Settings, header, grabMessage.Message);
}
public override void OnDownload(DownloadMessage message)
{
const string header = "Sonarr - Downloaded";
const string header = "Radarr - Downloaded";
Notify(Settings, header, message.Message);
}

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Notifications.Plex
{
return Json.Deserialize<PlexMediaContainerLegacy>(response.Content)
.Sections
.Where(d => d.Type == "show")
.Where(d => d.Type == "movie")
.Select(s => new PlexSection
{
Id = s.Id,
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.Notifications.Plex
return Json.Deserialize<PlexResponse<PlexSectionsContainer>>(response.Content)
.MediaContainer
.Sections
.Where(d => d.Type == "show")
.Where(d => d.Type == "movie")
.ToList();
}

View File

@@ -98,7 +98,7 @@ namespace NzbDrone.Core.Notifications.Plex
{
if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1))
{
throw new PlexVersionException("Found version {0}, upgrade to PMS 1.3.1 to fix library updating and then restart Sonarr", version);
throw new PlexVersionException("Found version {0}, upgrade to PMS 1.3.1 to fix library updating and then restart Radarr", version);
}
}

View File

@@ -18,14 +18,14 @@ namespace NzbDrone.Core.Notifications.PushBullet
public override void OnGrab(GrabMessage grabMessage)
{
const string title = "Sonarr - Episode Grabbed";
const string title = "Radarr - Episode Grabbed";
_proxy.SendNotification(title, grabMessage.Message, Settings);
}
public override void OnDownload(DownloadMessage message)
{
const string title = "Sonarr - Episode Downloaded";
const string title = "Radarr - Episode Downloaded";
_proxy.SendNotification(title, message.Message, Settings);
}

View File

@@ -92,7 +92,7 @@ namespace NzbDrone.Core.Notifications.PushBullet
{
try
{
const string title = "Sonarr - Test Notification";
const string title = "Radarr - Test Notification";
const string body = "This is a test message from Radarr";
SendNotification(title, body, settings);

View File

@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.Pushalot
[FieldDefinition(1, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(PushalotPriority))]
public int Priority { get; set; }
[FieldDefinition(2, Label = "Image", Type = FieldType.Checkbox, HelpText = "Include Sonarr logo with notifications")]
[FieldDefinition(2, Label = "Image", Type = FieldType.Checkbox, HelpText = "Include Radarr logo with notifications")]
public bool Image { get; set; }
public bool IsValid => !string.IsNullOrWhiteSpace(AuthToken);

View File

@@ -101,7 +101,7 @@ namespace NzbDrone.Core.Notifications.Slack
{
try
{
var message = $"Test message from Sonarr posted at {DateTime.Now}";
var message = $"Test message from Radarr posted at {DateTime.Now}";
var payload = new SlackPayload
{
IconEmoji = Settings.Icon,

View File

@@ -125,7 +125,7 @@ namespace NzbDrone.Core.Notifications.Twitter
{
try
{
var body = "Sonarr: Test Message @ " + DateTime.Now;
var body = "Radarr: Test Message @ " + DateTime.Now;
SendNotification(body, settings);
}

View File

@@ -23,14 +23,14 @@ namespace NzbDrone.Core.Notifications.Xbmc
public override void OnGrab(GrabMessage grabMessage)
{
const string header = "Sonarr - Grabbed";
const string header = "Radarr - Grabbed";
Notify(Settings, header, grabMessage.Message);
}
public override void OnDownload(DownloadMessage message)
{
const string header = "Sonarr - Downloaded";
const string header = "Radarr - Downloaded";
Notify(Settings, header, message.Message);
UpdateAndClean(message.Series, message.OldFiles.Any());

View File

@@ -183,8 +183,10 @@
<Compile Include="Datastore\Migration\002_remove_tvrage_imdb_unique_constraint.cs" />
<Compile Include="Datastore\Migration\003_remove_clean_title_from_scene_mapping.cs" />
<Compile Include="Datastore\Migration\004_updated_history.cs" />
<Compile Include="Datastore\Migration\115_update_movie_sorttitle.cs" />
<Compile Include="Datastore\Migration\111_remove_bitmetv.cs" />
<Compile Include="Datastore\Migration\112_remove_torrentleech.cs" />
<Compile Include="Datastore\Migration\114_remove_fanzub.cs" />
<Compile Include="Datastore\Migration\113_remove_broadcasthenet.cs" />
<Compile Include="Datastore\Migration\108_update_schedule_interval.cs" />
<Compile Include="Datastore\Migration\107_fix_movie_files.cs" />
@@ -582,9 +584,6 @@
<Compile Include="Indexers\Exceptions\RequestLimitReachedException.cs" />
<Compile Include="Indexers\Exceptions\UnsupportedFeedException.cs" />
<Compile Include="Indexers\EzrssTorrentRssParser.cs" />
<Compile Include="Indexers\Fanzub\Fanzub.cs" />
<Compile Include="Indexers\Fanzub\FanzubRequestGenerator.cs" />
<Compile Include="Indexers\Fanzub\FanzubSettings.cs" />
<Compile Include="Indexers\FetchAndParseRssService.cs" />
<Compile Include="Indexers\PassThePopcorn\PassThePopcorn.cs" />
<Compile Include="Indexers\PassThePopcorn\PassThePopcornApi.cs" />
@@ -707,10 +706,12 @@
<Compile Include="MediaFiles\Commands\BackendCommandAttribute.cs" />
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
<Compile Include="MediaFiles\Commands\DownloadedMovieScanCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameMovieCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameMovieFilesCommand.cs" />
<Compile Include="MediaFiles\Commands\RescanMovieCommand.cs" />
<Compile Include="MediaFiles\DownloadedMovieCommandService.cs" />
<Compile Include="MediaFiles\DownloadedMovieImportService.cs" />
<Compile Include="MediaFiles\MovieFileMovingService.cs" />
<Compile Include="MediaFiles\Events\MovieDownloadedEvent.cs" />
<Compile Include="MediaFiles\Events\MovieFileAddedEvent.cs" />

View File

@@ -17,6 +17,7 @@ namespace NzbDrone.Core.Parser.Model
public DownloadProtocol DownloadProtocol { get; set; }
public int TvdbId { get; set; }
public int TvRageId { get; set; }
public int ImdbId { get; set; }
public DateTime PublishDate { get; set; }
public string Origin { get; set; }
@@ -82,6 +83,7 @@ namespace NzbDrone.Core.Parser.Model
stringBuilder.AppendLine("DownloadProtocol: " + DownloadProtocol ?? "Empty");
stringBuilder.AppendLine("TvdbId: " + TvdbId ?? "Empty");
stringBuilder.AppendLine("TvRageId: " + TvRageId ?? "Empty");
stringBuilder.AppendLine("ImdbId: " + ImdbId ?? "Empty");
stringBuilder.AppendLine("PublishDate: " + PublishDate ?? "Empty");
return stringBuilder.ToString();
default:

View File

@@ -18,16 +18,10 @@ namespace NzbDrone.Core.Parser
private static readonly Regex[] ReportMovieTitleRegex = new[]
{
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\w+\.?edition))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\.?((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX)))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.2011.Special.Edition //TODO: Seems to slow down parsing heavily!
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}edition))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Cut Movies, e.g: Mission.Impossible.3.Directors.Cut.2011
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\w+\.?cut))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Cut Movies, e.g: Mission.Impossible.3.2011.Directors.Cut
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}cut))",
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Normal movie format, e.g: Mission.Impossible.3.2011
@@ -36,6 +30,15 @@ namespace NzbDrone.Core.Parser
//PassThePopcorn Torrent names: Star.Wars[PassThePopcorn]
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(\[\w *\])))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//That did not work? Maybe some tool uses [] for years. Who would do that?
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
};
private static readonly Regex[] ReportMovieTitleFolderRegex = new[]
{
//When year comes first.
new Regex(@"^(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?<title>.+?)?$")
};
private static readonly Regex[] ReportTitleRegex = new[]
@@ -327,7 +330,7 @@ namespace NzbDrone.Core.Parser
{
var fileInfo = new FileInfo(path);
var result = ParseMovieTitle(fileInfo.Name);
var result = ParseMovieTitle(fileInfo.Name, true);
if (result == null)
{
@@ -345,7 +348,7 @@ namespace NzbDrone.Core.Parser
}
public static ParsedMovieInfo ParseMovieTitle(string title)
public static ParsedMovieInfo ParseMovieTitle(string title, bool isDir = false)
{
ParsedMovieInfo realResult = null;
@@ -376,7 +379,14 @@ namespace NzbDrone.Core.Parser
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle, string.Empty);
foreach (var regex in ReportMovieTitleRegex)
var allRegexes = ReportMovieTitleRegex.ToList();
if (isDir)
{
allRegexes.AddRange(ReportMovieTitleFolderRegex);
}
foreach (var regex in allRegexes)
{
var match = regex.Matches(simpleTitle);

View File

@@ -149,7 +149,11 @@ namespace NzbDrone.Core.RootFolders
foreach (string unmappedFolder in unmappedFolders)
{
var di = new DirectoryInfo(unmappedFolder.Normalize());
results.Add(new UnmappedFolder { Name = di.Name, Path = di.FullName });
if (!di.Attributes.HasFlag(FileAttributes.System) && !di.Attributes.HasFlag(FileAttributes.Hidden))
{
results.Add(new UnmappedFolder { Name = di.Name, Path = di.FullName });
}
}
var setToRemove = SpecialFolders;

View File

@@ -24,7 +24,7 @@ namespace Radarr.Host
SecurityProtocolPolicy.Register();
X509CertificateValidationPolicy.Register();
Logger.Info("Starting Sonarr - {0} - Version {1}", Assembly.GetCallingAssembly().Location, Assembly.GetExecutingAssembly().GetName().Version);
Logger.Info("Starting Radarr - {0} - Version {1}", Assembly.GetCallingAssembly().Location, Assembly.GetExecutingAssembly().GetName().Version);
if (!PlatformValidation.IsValidate(userAlert))
{

View File

@@ -91,7 +91,7 @@
{{/if_eq}}
{{#if_eq reason compare="MissingFromDisk"}}
Sonarr was unable to find the file on disk so it was removed
Radarr was unable to find the file on disk so it was removed
{{/if_eq}}
{{#if_eq reason compare="Upgrade"}}

View File

@@ -58,7 +58,7 @@ var view = Marionette.ItemView.extend({
var defaultProfile = Config.getValue(Config.Keys.DefaultProfileId);
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
var useSeasonFolder = Config.getValueBoolean(Config.Keys.UseSeasonFolder, true);
var defaultMonitorEpisodes = Config.getValue(Config.Keys.MonitorEpisodes, 'missing');
var defaultMonitorEpisodes = Config.getValue(Config.Keys.MonitorEpisodes, 'all');
if (Profiles.get(defaultProfile)) {
this.ui.profile.val(defaultProfile);
@@ -169,6 +169,7 @@ var view = Marionette.ItemView.extend({
var profile = this.ui.profile.val();
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
var monitor = this.ui.monitor.val();
var options = this._getAddMoviesOptions();
options.searchForMovie = searchForMovie;
@@ -178,7 +179,7 @@ var view = Marionette.ItemView.extend({
profileId : profile,
rootFolderPath : rootFolderPath,
addOptions : options,
monitored : true
monitored : (monitor === 'all' ? true : false)
}, { silent : true });
var self = this;
@@ -229,44 +230,10 @@ var view = Marionette.ItemView.extend({
},
_getAddMoviesOptions : function() {
var monitor = this.ui.monitor.val();
var options = {
return {
ignoreEpisodesWithFiles : false,
ignoreEpisodesWithoutFiles : false
};
if (monitor === 'all') {
return options;
}
else if (monitor === 'future') {
options.ignoreEpisodesWithFiles = true;
options.ignoreEpisodesWithoutFiles = true;
}
// else if (monitor === 'latest') {
// this.model.setSeasonPass(lastSeason.seasonNumber);
// }
// else if (monitor === 'first') {
// this.model.setSeasonPass(lastSeason.seasonNumber + 1);
// this.model.setSeasonMonitored(firstSeason.seasonNumber);
// }
else if (monitor === 'missing') {
options.ignoreEpisodesWithFiles = true;
}
else if (monitor === 'existing') {
options.ignoreEpisodesWithoutFiles = true;
}
// else if (monitor === 'none') {
// this.model.setSeasonPass(lastSeason.seasonNumber + 1);
// }
return options;
}
});

View File

@@ -1,7 +1,7 @@
<div class="search-item {{#unless isExisting}}search-item-new{{/unless}}">
<div class="row">
<div class="col-md-2">
<a href="{{imdbUrl}}" target="_blank">
<a href="{{tmdbUrl}}" target="_blank">
{{#if remotePoster}}
{{remotePoster}}
{{else}}
@@ -41,9 +41,9 @@
<div class="form-group col-md-2">
<label>Monitor <i class="icon-sonarr-form-info monitor-tooltip x-monitor-tooltip"></i></label>
<select class="form-control col-md-2 x-monitor">
<option value="all">All</option>
<option value="missing">Missing</option>
<option value="none">None</option>
<option value="all">Yes</option>
<!-- <option value="missing">Missing</option> -->
<option value="none">No</option>
</select>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Sonarr Calendar feed</h3>
<h3>Radarr Calendar feed</h3>
</div>
<div class="modal-body edit-series-modal">
<div class="form-horizontal">

View File

@@ -0,0 +1,34 @@
var Backgrid = require('backgrid');
var ProfileCollection = require('../Profile/ProfileCollection');
var _ = require('underscore');
module.exports = Backgrid.Cell.extend({
className : 'profile-cell',
_originalInit : Backgrid.Cell.prototype.initialize,
initialize : function () {
this._originalInit.apply(this, arguments);
this.listenTo(ProfileCollection, 'sync', this.render);
},
render : function() {
this.$el.empty();
if (this.model.get("movieFile")) {
var profileId = this.model.get("movieFile").quality.quality.id;
var profile = _.findWhere(ProfileCollection.models, { id : profileId });
if (profile) {
this.$el.html(profile.get('name'));
} else {
this.$el.html(this.model.get("movieFile").quality.quality.name);
}
}
return this;
}
});

View File

@@ -7,10 +7,16 @@ module.exports = TemplatedCell.extend({
var monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
var cinemasDate = new Date(this.model.get("inCinemas"));
var year = cinemasDate.getFullYear();
var month = monthNames[cinemasDate.getMonth()];
this.$el.html(month + " " + year); //Hack, but somehow handlebar helper does not work.
return this;
this.$el.html("");
if (this.model.get("inCinemas")) {
var cinemasDate = new Date(this.model.get("inCinemas"));
var year = cinemasDate.getFullYear();
var month = monthNames[cinemasDate.getMonth()];
this.$el.html(month + " " + year); //Hack, but somehow handlebar helper does not work.
}
return this;
}
});

View File

@@ -3,4 +3,8 @@ var TemplatedCell = require('./TemplatedCell');
module.exports = TemplatedCell.extend({
className : 'movie-title-cell',
template : 'Cells/MovieDownloadStatusTemplate',
sortKey : function(model) {
debugger;
return 0;
}
});

View File

@@ -1 +1 @@
<span class="label label-{{DownloadedStatusColor}}">{{DownloadedStatus}}</span>
<span class="label label-{{DownloadedStatusColor}}" title="{{DownloadedQuality}}">{{DownloadedStatus}}</span>

View File

@@ -4,5 +4,7 @@ var QualityCellEditor = require('./Edit/QualityCellEditor');
module.exports = TemplatedCell.extend({
className : 'quality-cell',
template : 'Cells/QualityCellTemplate',
editor : QualityCellEditor
});
editor : QualityCellEditor,
});

View File

@@ -161,6 +161,14 @@ Handlebars.registerHelper('DownloadedStatus', function() {
return "Missing";
});
Handlebars.registerHelper("DownloadedQuality", function() {
if (this.movieFile) {
return this.movieFile.quality.quality.name;
}
return "";
})
Handlebars.registerHelper('inCinemas', function() {
var monthNames = ["January", "February", "March", "April", "May", "June",
@@ -173,10 +181,13 @@ Handlebars.registerHelper('inCinemas', function() {
var year = d.getFullYear();
return "Available: " + day + ". " + month + " " + year;
}
var cinemasDate = new Date(this.inCinemas);
var year = cinemasDate.getFullYear();
var month = monthNames[cinemasDate.getMonth()];
return "In Cinemas: " + month + " " + year;
if (this.inCinemas) {
var cinemasDate = new Date(this.inCinemas);
var year = cinemasDate.getFullYear();
var month = monthNames[cinemasDate.getMonth()];
return "In Cinemas: " + month + " " + year;
}
return "To be announced";
});
Handlebars.registerHelper('tvRageUrl', function() {

View File

@@ -0,0 +1,43 @@
var vent = require('../../vent');
var NzbDroneCell = require('../../Cells/NzbDroneCell');
var SelectMovieLayout = require('../Movie/SelectMovieLayout');
module.exports = NzbDroneCell.extend({
className : 'series-title-cell editable',
events : {
'click' : '_onClick'
},
render : function() {
this.$el.empty();
var movie = this.model.get('movie');
if (movie)
{
this.$el.html(movie.title + " (" + movie.year + ")" );
}
this.delegateEvents();
return this;
},
_onClick : function () {
var view = new SelectMovieLayout();
this.listenTo(view, 'manualimport:selected:movie', this._setMovie);
vent.trigger(vent.Commands.OpenModal2Command, view);
},
_setMovie : function (e) {
if (this.model.has('movie') && e.model.id === this.model.get('movie').id) {
return;
}
this.model.set({
movie : e.model.toJSON()
});
}
});

View File

@@ -16,6 +16,7 @@ var QualityCell = require('./Cells/QualityCell');
var FileSizeCell = require('../Cells/FileSizeCell');
var ApprovalStatusCell = require('../Cells/ApprovalStatusCell');
var ManualImportCollection = require('./ManualImportCollection');
var MovieCell = require('./Cells/MovieCell');
var Messenger = require('../Shared/Messenger');
module.exports = Marionette.Layout.extend({
@@ -49,23 +50,29 @@ module.exports = Marionette.Layout.extend({
sortable : true
},
{
name : 'series',
label : 'Series',
cell : SeriesCell,
name : 'movie',
label : 'Movie',
cell : MovieCell,
sortable : true
},
{
name : 'seasonNumber',
label : 'Season',
cell : SeasonCell,
sortable : true
},
{
name : 'episodes',
label : 'Episode(s)',
cell : EpisodesCell,
sortable : false
},
// {
// name : 'series',
// label : 'Series',
// cell : SeriesCell,
// sortable : true
// },
// {
// name : 'seasonNumber',
// label : 'Season',
// cell : SeasonCell,
// sortable : true
// },
// {
// name : 'episodes',
// label : 'Episode(s)',
// cell : EpisodesCell,
// sortable : false
// },
{
name : 'quality',
label : 'Quality',
@@ -161,8 +168,8 @@ module.exports = Marionette.Layout.extend({
},
_automaticImport : function (e) {
CommandController.Execute('downloadedEpisodesScan', {
name : 'downloadedEpisodesScan',
CommandController.Execute('downloadedMovieScan', {
name : 'downloadedMovieScan',
path : e.folder
});
@@ -176,29 +183,36 @@ module.exports = Marionette.Layout.extend({
return;
}
if (_.any(selected, function (model) {
return !model.has('series');
})) {
this._showErrorMessage('Series must be chosen for each selected file');
if(_.any(selected, function(model) {
return !model.has('movie');
})) {
this._showErrorMessage('Movie must be chosen for each selected file');
return;
}
if (_.any(selected, function (model) {
return !model.has('seasonNumber');
})) {
// if (_.any(selected, function (model) {
// return !model.has('series');
// })) {
this._showErrorMessage('Season must be chosen for each selected file');
return;
}
// this._showErrorMessage('Series must be chosen for each selected file');
// return;
// }
if (_.any(selected, function (model) {
return !model.has('episodes') || model.get('episodes').length === 0;
})) {
// if (_.any(selected, function (model) {
// return !model.has('seasonNumber');
// })) {
this._showErrorMessage('One or more episodes must be chosen for each selected file');
return;
}
// this._showErrorMessage('Season must be chosen for each selected file');
// return;
// }
// if (_.any(selected, function (model) {
// return !model.has('episodes') || model.get('episodes').length === 0;
// })) {
// this._showErrorMessage('One or more episodes must be chosen for each selected file');
// return;
// }
var importMode = this.ui.importMode.val();
@@ -207,8 +221,9 @@ module.exports = Marionette.Layout.extend({
files : _.map(selected, function (file) {
return {
path : file.get('path'),
seriesId : file.get('series').id,
episodeIds : _.map(file.get('episodes'), 'id'),
movieId : file.get('movie').id,
// seriesId : file.get('series').id,
// episodeIds : _.map(file.get('episodes'), 'id'),
quality : file.get('quality'),
downloadId : file.get('downloadId')
};

View File

@@ -0,0 +1,101 @@
var _ = require('underscore');
var vent = require('vent');
var Marionette = require('marionette');
var Backgrid = require('backgrid');
var MoviesCollection = require('../../Movies/MoviesCollection');
var SelectRow = require('./SelectMovieRow');
module.exports = Marionette.Layout.extend({
template : 'ManualImport/Movie/SelectMovieLayoutTemplate',
regions : {
movie : '.x-movie'
},
ui : {
filter : '.x-filter'
},
columns : [
{
name : 'title',
label : 'Title',
cell : 'String',
sortValue : 'title'
}
],
initialize : function() {
this.movieCollection = MoviesCollection.clone();
this._setModelCollection();
this.listenTo(this.movieCollection, 'row:selected', this._onSelected);
this.listenTo(this, 'modal:afterShow', this._setFocus);
},
onRender : function() {
this.movieView = new Backgrid.Grid({
columns : this.columns,
collection : this.movieCollection,
className : 'table table-hover season-grid',
row : SelectRow
});
this.movie.show(this.movieView);
this._setupFilter();
},
_setupFilter : function () {
var self = this;
//TODO: This should be a mixin (same as Add Series searching)
this.ui.filter.keyup(function(e) {
if (_.contains([
9,
16,
17,
18,
19,
20,
33,
34,
35,
36,
37,
38,
39,
40,
91,
92,
93
], e.keyCode)) {
return;
}
self._filter(self.ui.filter.val());
});
},
_filter : function (term) {
this.movieCollection.setFilter(['title', term, 'contains']);
this._setModelCollection();
},
_onSelected : function (e) {
this.trigger('manualimport:selected:movie', { model: e.model });
vent.trigger(vent.Commands.CloseModal2Command);
},
_setFocus : function () {
this.ui.filter.focus();
},
_setModelCollection: function () {
var self = this;
_.each(this.movieCollection.models, function (model) {
model.collection = self.movieCollection;
});
}
});

View File

@@ -0,0 +1,30 @@
<div class="modal-content">
<div class="manual-import-modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>
Manual Import - Select Movie
</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<input type="text" class="form-control x-filter" placeholder="Filter movies" />
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 x-movie"></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,13 @@
var Backgrid = require('backgrid');
module.exports = Backgrid.Row.extend({
className : 'select-row select-series-row',
events : {
'click' : '_onClick'
},
_onClick : function() {
this.model.collection.trigger('row:selected', { model: this.model });
}
});

View File

@@ -4,16 +4,25 @@ var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({
template : 'ManualImport/Summary/ManualImportSummaryViewTemplate',
// initialize : function (options) {
// var episodes = _.map(options.episodes, function (episode) {
// return episode.toJSON();
// });
// this.templateHelpers = {
// file : options.file,
// series : options.series,
// season : options.season,
// episodes : episodes,
// quality : options.quality
// };
// }
initialize : function (options) {
var episodes = _.map(options.episodes, function (episode) {
return episode.toJSON();
});
this.templateHelpers = {
file : options.file,
series : options.series,
season : options.season,
episodes : episodes,
movie : options.movie,
quality : options.quality
};
}

View File

@@ -3,16 +3,8 @@
<dt>Path:</dt>
<dd>{{file}}</dd>
<dt>Series:</dt>
<dd>{{series.title}}</dd>
<dt>Season:</dt>
<dd>{{season.seasonNumber}}</dd>
{{#each episodes}}
<dt>Episode:</dt>
<dd>{{episodeNumber}} - {{title}}</dd>
{{/each}}
<dt>Movie:</dt>
<dd>{{movie.title}} ({{movie.year}})</dd>
<dt>Quality:</dt>
<dd>{{quality.name}}</dd>

View File

@@ -26,7 +26,7 @@
{{else}}
<span class="label label-default">Announced</span>
{{/if_eq}}
<span class="label label-{{DownloadedStatusColor}}">{{DownloadedStatus}}</span>
<span class="label label-{{DownloadedStatusColor}}" title="{{DownloadedQuality}}">{{DownloadedStatus}}</span>
</div>
<div class="col-md-4">
<span class="series-info-links">

View File

@@ -13,6 +13,7 @@ var MovieLinksCell = require('../../Cells/MovieLinksCell');
var MovieActionCell = require('../../Cells/MovieActionCell');
var MovieStatusCell = require('../../Cells/MovieStatusCell');
var MovieDownloadStatusCell = require('../../Cells/MovieDownloadStatusCell');
var DownloadedQualityCell = require('../../Cells/DownloadedQualityCell');
var FooterView = require('./FooterView');
var FooterModel = require('./FooterModel');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
@@ -40,6 +41,11 @@ module.exports = Marionette.Layout.extend({
cell : MovieTitleCell,
cellValue : 'this',
},
{
name : "downloadedQuality",
label : "Downloaded",
cell : DownloadedQualityCell,
},
{
name : 'profileId',
label : 'Profile',
@@ -54,12 +60,19 @@ module.exports = Marionette.Layout.extend({
name : 'this',
label : 'Links',
cell : MovieLinksCell,
className : "movie-links-cell"
className : "movie-links-cell",
sortable : false,
},
{
name : "this",
label : "Status",
cell : MovieDownloadStatusCell,
sortValue : function(m, k) {
if (m.get("downloaded")) {
return -1;
}
return 0;
}
},
{
name : 'this',

View File

@@ -41,7 +41,7 @@
{{profile profileId}}
<span class="label label-{{DownloadedStatusColor}}">{{DownloadedStatus}}</span>
<span class="label label-{{DownloadedStatusColor}}" title="{{DownloadedQuality}}">{{DownloadedStatus}}</span>
</div>
<div class="col-md-4 col-xs-4">
<span class="movie-info-links">

View File

@@ -9,5 +9,31 @@ module.exports = Backbone.Model.extend({
episodeCount : 0,
isExisting : false,
status : 0
},
getStatus : function() {
var monitored = this.get("monitored");
var status = this.get("status");
var inCinemas = this.get("inCinemas");
var date = new Date(inCinemas);
var timeSince = new Date().getTime() - date.getTime();
var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
if (status === "announced") {
return "announced"
}
if (numOfMonths < 3 && numOfMonths > 0) {
return "inCinemas";
}
if (status === 'released') {
return "released";
}
if (numOfMonths > 3) {
return "released";//TODO: Update for PreDB.me
}
}
});

View File

@@ -15,10 +15,10 @@ var Collection = PageableCollection.extend({
tableName : 'movie',
state : {
sortKey : 'title',
sortKey : 'sortTitle',
order : 1,
pageSize : 100000,
secondarySortKey : 'title',
secondarySortKey : 'sortTitle',
secondarySortOrder : -1
},
@@ -73,9 +73,28 @@ var Collection = PageableCollection.extend({
sortMappings : {
title : {
sortKey : 'title'
sortKey : 'sortTitle'
},
statusWeight : {
sortValue : function(model, attr) {
if (model.getStatus() == "released") {
return 1;
}
if (model.getStatus() == "inCinemas") {
return 0;
}
return -1;
}
},
downloadedQuality : {
sortValue : function(model, attr) {
if (model.get("movieFile")) {
return 1000-model.get("movieFile").quality.quality.id;
}
return -1;
}
},
nextAiring : {
sortValue : function(model, attr, order) {
var nextAiring = model.get(attr);
@@ -91,7 +110,15 @@ var Collection = PageableCollection.extend({
return Number.MAX_VALUE;
}
},
status: {
sortValue : function(model, attr) {
debugger;
if (model.get("downloaded")) {
return -1;
}
return 0;
}
},
percentOfEpisodes : {
sortValue : function(model, attr) {
var percentOfEpisodes = model.get(attr);
@@ -100,7 +127,18 @@ var Collection = PageableCollection.extend({
return percentOfEpisodes + episodeCount / 1000000;
}
},
inCinemas : {
sortValue : function(model, attr) {
var monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
if (model.get("inCinemas")) {
return model.get("inCinemas");
}
return "2100-01-01";
}
},
path : {
sortValue : function(model) {
var path = model.get('path');

View File

@@ -37,7 +37,7 @@ module.exports = Marionette.Layout.extend({
name : 'edition',
label : 'Edition',
cell : EditionCell,
title : "Edition"
title : "Edition",
},
{
name : 'indexer',
@@ -57,7 +57,7 @@ module.exports = Marionette.Layout.extend({
{
name : 'quality',
label : 'Quality',
cell : QualityCell
cell : QualityCell,
},
{
name : 'rejections',

View File

@@ -1,2 +1 @@
<div id="episode-release-grid" class="table-responsive"></div>
<button class="btn x-search-back">Back</button>

View File

@@ -7,12 +7,12 @@
<span class="icon-sonarr-navbar-collapsed fa-lg"></span>
</button>
<a class="navbar-brand" href="{{UrlBase}}/">
<!--<img src="{{UrlBase}}/Content/Images/logo.png?v=2" alt="Sonarr">-->
<!--<img src="{{UrlBase}}/Content/Images/logo.png?v=2" alt="Radarr">-->
<img src="{{UrlBase}}/Content/Images/logos/128.png" class="visible-lg"/>
<img src="{{UrlBase}}/Content/Images/logos/64.png" class="visible-md visible-sm"/>
<span class="visible-xs">
<img src="{{UrlBase}}/Content/Images/logos/32.png"/>
<span class="logo-text">sonarr</span>
<span class="logo-text">Radarr</span>
</span>
</a>

View File

@@ -16,7 +16,7 @@ var Collection = PagableCollection.extend({
sortMappings : {
'quality' : {
sortKey : 'qualityWeight'
sortKey : "qualityWeight"
},
'rejections' : {
sortValue : function(model) {
@@ -30,6 +30,9 @@ var Collection = PagableCollection.extend({
return releaseWeight;
}
},
"edition" : {
sortKey : "edition"
},
'download' : {
sortKey : 'releaseWeight'
},

View File

@@ -78,7 +78,7 @@ module.exports = Marionette.Layout.extend({
CommandController.Execute('renameMovieFiles', {
name : 'renameMovieFiles',
movieId : this.model.id,
movieId : this.model.id,
files : files
});

View File

@@ -27,7 +27,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-sonarr-form-info" title="Should Sonarr download episodes for this series?"/>
<i class="icon-sonarr-form-info" title="Should Radarr download episodes for this series?"/>
</span>
</div>
</div>

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