mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-23 17:14:46 -04:00
Compare commits
35 Commits
v0.2.0.654
...
v0.2.0.696
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c8162f1d6 | ||
|
|
096d24ed91 | ||
|
|
63e3361fb5 | ||
|
|
51854ef73b | ||
|
|
a0486b54a6 | ||
|
|
7ed0db10cb | ||
|
|
e94591a290 | ||
|
|
fccd02a0ca | ||
|
|
b49f0e70ed | ||
|
|
fc1585e900 | ||
|
|
b36ac091fc | ||
|
|
ab28bfead2 | ||
|
|
3ab3fbfd57 | ||
|
|
d133ee3143 | ||
|
|
98b6932ffe | ||
|
|
58e81a916c | ||
|
|
817f48448c | ||
|
|
1eca179b4e | ||
|
|
b05d505bce | ||
|
|
ace426e69f | ||
|
|
a57c9917cc | ||
|
|
9e84b4a782 | ||
|
|
494ef16735 | ||
|
|
5a0f02007f | ||
|
|
b1025e7229 | ||
|
|
446d661345 | ||
|
|
3eb351823e | ||
|
|
835a7cffa1 | ||
|
|
e728330ce4 | ||
|
|
6f3118c142 | ||
|
|
b568072140 | ||
|
|
7db92c6bcf | ||
|
|
f1e8a9acfc | ||
|
|
dae389ce64 | ||
|
|
7c3d8c8ff9 |
@@ -104,11 +104,13 @@ Task("Compile").Does(() => {
|
||||
});
|
||||
|
||||
Task("Gulp").Does(() => {
|
||||
Npm
|
||||
.WithLogLevel(NpmLogLevel.Silent)
|
||||
.FromPath(".")
|
||||
.Install()
|
||||
.RunScript("build");
|
||||
NpmInstall(new NpmInstallSettings {
|
||||
LogLevel = NpmLogLevel.Silent,
|
||||
WorkingDirectory = "./",
|
||||
Production = true
|
||||
});
|
||||
|
||||
NpmRunScript("build");
|
||||
});
|
||||
|
||||
Task("PackageMono").Does(() => {
|
||||
@@ -167,7 +169,7 @@ Task("PackageOsx").Does(() => {
|
||||
CopyFiles(sourceFolder + "/Libraries/MediaInfo/*.dylib", outputFolderOsx);
|
||||
|
||||
// Adding Startup script
|
||||
CopyFile("./osx/Sonarr", outputFolderOsx + "/Sonarr");
|
||||
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
|
||||
});
|
||||
|
||||
Task("PackageOsxApp").Does(() => {
|
||||
|
||||
2
build.sh
2
build.sh
@@ -181,7 +181,7 @@ PackageOsx()
|
||||
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderOsx
|
||||
|
||||
echo "Adding Startup script"
|
||||
cp ./osx/Sonarr $outputFolderOsx
|
||||
cp ./osx/Radarr $outputFolderOsx
|
||||
|
||||
echo "##teamcity[progressFinish 'Creating OS X Package']"
|
||||
}
|
||||
|
||||
44
create_test_cases.py
Normal file
44
create_test_cases.py
Normal file
@@ -0,0 +1,44 @@
|
||||
input1 = """Prometheus.Special.Edition.Fan Edit.2012..BRRip.x264.AAC-m2g
|
||||
Star Wars Episode IV - A New Hope (Despecialized) 1999.mkv
|
||||
Prometheus.(Special.Edition.Remastered).2012.[Bluray-1080p].mkv
|
||||
Prometheus Extended 2012
|
||||
Prometheus Extended Directors Cut Fan Edit 2012
|
||||
Prometheus Director's Cut 2012
|
||||
Prometheus Directors Cut 2012
|
||||
Prometheus.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf
|
||||
2001 A Space Odyssey Director's Cut (1968).mkv
|
||||
2001: A Space Odyssey (Extended Directors Cut FanEdit) Bluray 1080p 1968
|
||||
A Fake Movie 2035 Directors 2012.mkv
|
||||
Blade Runner Director's Cut 2049.mkv
|
||||
Prometheus 50th Anniversary Edition 2012.mkv
|
||||
Movie 2in1 2012.mkv
|
||||
Movie IMAX 2012.mkv"""
|
||||
|
||||
output1 = """Special.Edition.Fan Edit BRRip.x264.AAC-m2g
|
||||
Despecialized mkv
|
||||
Special.Edition.Remastered Bluray-1080p].mkv
|
||||
Extended mkv
|
||||
Extended Directors Cut Fan Edit mkv
|
||||
Director's Cut mkv
|
||||
Directors Cut mkv
|
||||
Extended.Theatrical.Version.IMAX asdf
|
||||
Director's Cut mkv
|
||||
Extended Directors Cut FanEdit mkv
|
||||
Directors mkv
|
||||
Director's Cut mkv
|
||||
50th Anniversary Edition mkv
|
||||
2in1 mkv
|
||||
IMAX mkv"""
|
||||
|
||||
inputs = input1.split("\n")
|
||||
outputs = output1.split("\n")
|
||||
real_o = []
|
||||
for output in outputs:
|
||||
real_o.append(output.split(" ")[0].replace(".", " ").strip())
|
||||
|
||||
count = 0
|
||||
|
||||
for inp in inputs:
|
||||
o = real_o[count]
|
||||
print "[TestCase(\"{0}\", \"{1}\")]".format(inp, o)
|
||||
count += 1
|
||||
@@ -5,7 +5,7 @@ DIR=$(cd "$(dirname "$0")"; pwd)
|
||||
|
||||
#change these values to match your app
|
||||
EXE_PATH="$DIR/Radarr.exe"
|
||||
APPNAME="Sonarr"
|
||||
APPNAME="Radarr"
|
||||
|
||||
#set up environment
|
||||
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Sonarr</string>
|
||||
<string>Radarr</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>radarr.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
@@ -32,10 +32,10 @@ outputFolderMono='./_output_mono'
|
||||
outputFolderOsx='./_output_osx'
|
||||
outputFolderOsxApp='./_output_osx_app'
|
||||
|
||||
tr -d "\r" < $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr > $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2
|
||||
rm $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr
|
||||
chmod +x $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2
|
||||
mv $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr2 $outputFolderOsxApp/Radarr.app/Contents/MacOS/Sonarr >& error.log
|
||||
tr -d "\r" < $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr > $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr2
|
||||
rm $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr
|
||||
chmod +x $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr2
|
||||
mv $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr2 $outputFolderOsxApp/Radarr.app/Contents/MacOS/Radarr >& error.log
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
./7za.exe a Radarr_Windows_$VERSION.zip ./Radarr_Windows_$VERSION/*
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
RedirectUrl = _configFileProvider.UrlBase + "/login",
|
||||
UserMapper = _authenticationService,
|
||||
Path = _configFileProvider.UrlBase,
|
||||
CryptographyConfiguration = cryptographyConfiguration
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.Validation;
|
||||
using NzbDrone.Common;
|
||||
@@ -17,14 +18,17 @@ namespace NzbDrone.Api.Commands
|
||||
{
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IServiceFactory _serviceFactory;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CommandModule(IManageCommandQueue commandQueueManager,
|
||||
IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IServiceFactory serviceFactory)
|
||||
IServiceFactory serviceFactory,
|
||||
Logger logger)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_commandQueueManager = commandQueueManager;
|
||||
_serviceFactory = serviceFactory;
|
||||
_logger = logger;
|
||||
|
||||
GetResourceById = GetCommand;
|
||||
CreateResource = StartCommand;
|
||||
@@ -41,7 +45,13 @@ namespace NzbDrone.Api.Commands
|
||||
private int StartCommand(CommandResource commandResource)
|
||||
{
|
||||
var commandType = _serviceFactory.GetImplementations(typeof(Command))
|
||||
.Single(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
||||
.SingleOrDefault(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (commandType == null)
|
||||
{
|
||||
_logger.Error("Found no matching command for {0}", commandResource.Name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dynamic command = Request.Body.FromJson(commandType);
|
||||
command.Trigger = CommandTrigger.Manual;
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace NzbDrone.Api.Config
|
||||
public bool AutoDownloadPropers { get; set; }
|
||||
public bool CreateEmptySeriesFolders { get; set; }
|
||||
public FileDateType FileDate { get; set; }
|
||||
public bool AutoRenameFolders { get; set; }
|
||||
public bool PathsDefaultStatic { get; set; }
|
||||
|
||||
public bool SetPermissionsLinux { get; set; }
|
||||
public string FileChmod { get; set; }
|
||||
@@ -35,6 +37,8 @@ namespace NzbDrone.Api.Config
|
||||
AutoDownloadPropers = model.AutoDownloadPropers,
|
||||
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
|
||||
FileDate = model.FileDate,
|
||||
AutoRenameFolders = model.AutoRenameFolders,
|
||||
PathsDefaultStatic = model.PathsDefaultStatic,
|
||||
|
||||
SetPermissionsLinux = model.SetPermissionsLinux,
|
||||
FileChmod = model.FileChmod,
|
||||
|
||||
@@ -46,11 +46,14 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
return movie.ToResource();
|
||||
}
|
||||
|
||||
|
||||
private void SetQuality(MovieFileResource movieFileResource)
|
||||
{
|
||||
{
|
||||
var movieFile = _mediaFileService.GetMovie(movieFileResource.Id);
|
||||
movieFile.Quality = movieFileResource.Quality;
|
||||
_mediaFileService.Update(movieFile);
|
||||
|
||||
BroadcastResourceChange(ModelAction.Updated, movieFile.Id);
|
||||
}
|
||||
|
||||
private void DeleteMovieFile(int id)
|
||||
|
||||
45
src/NzbDrone.Api/NetImport/ImportExclusionsModule.cs
Normal file
45
src/NzbDrone.Api/NetImport/ImportExclusionsModule.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace NzbDrone.Api.NetImport
|
||||
{
|
||||
public class ImportExclusionsModule : NzbDroneRestModule<ImportExclusionsResource>
|
||||
{
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
public ImportExclusionsModule(NetImportFactory netImportFactory, IImportExclusionsService exclusionService) : base("exclusions")
|
||||
{
|
||||
_exclusionService = exclusionService;
|
||||
GetResourceAll = GetAll;
|
||||
CreateResource = AddExclusion;
|
||||
DeleteResource = RemoveExclusion;
|
||||
GetResourceById = GetById;
|
||||
}
|
||||
|
||||
public List<ImportExclusionsResource> GetAll()
|
||||
{
|
||||
return _exclusionService.GetAllExclusions().ToResource();
|
||||
}
|
||||
|
||||
public ImportExclusionsResource GetById(int id)
|
||||
{
|
||||
return _exclusionService.GetById(id).ToResource();
|
||||
}
|
||||
|
||||
public int AddExclusion(ImportExclusionsResource exclusionResource)
|
||||
{
|
||||
var model = exclusionResource.ToModel();
|
||||
|
||||
return _exclusionService.AddExclusion(model).Id;
|
||||
}
|
||||
|
||||
public void RemoveExclusion (int id)
|
||||
{
|
||||
_exclusionService.RemoveExclusion(new ImportExclusion { Id = id });
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/NzbDrone.Api/NetImport/ImportExclusionsResource.cs
Normal file
46
src/NzbDrone.Api/NetImport/ImportExclusionsResource.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Api.NetImport
|
||||
{
|
||||
public class ImportExclusionsResource : ProviderResource
|
||||
{
|
||||
//public int Id { get; set; }
|
||||
public int TmdbId { get; set; }
|
||||
public string MovieTitle { get; set; }
|
||||
public int MovieYear { get; set; }
|
||||
}
|
||||
|
||||
public static class ImportExclusionsResourceMapper
|
||||
{
|
||||
public static ImportExclusionsResource ToResource(this Core.NetImport.ImportExclusions.ImportExclusion model)
|
||||
{
|
||||
if (model == null) return null;
|
||||
|
||||
return new ImportExclusionsResource
|
||||
{
|
||||
Id = model.Id,
|
||||
TmdbId = model.TmdbId,
|
||||
MovieTitle = model.MovieTitle,
|
||||
MovieYear = model.MovieYear
|
||||
};
|
||||
}
|
||||
|
||||
public static List<ImportExclusionsResource> ToResource(this IEnumerable<Core.NetImport.ImportExclusions.ImportExclusion> exclusions)
|
||||
{
|
||||
return exclusions.Select(ToResource).ToList();
|
||||
}
|
||||
|
||||
public static Core.NetImport.ImportExclusions.ImportExclusion ToModel(this ImportExclusionsResource resource)
|
||||
{
|
||||
return new Core.NetImport.ImportExclusions.ImportExclusion
|
||||
{
|
||||
TmdbId = resource.TmdbId,
|
||||
MovieTitle = resource.MovieTitle,
|
||||
MovieYear = resource.MovieYear
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,6 +270,9 @@
|
||||
<Compile Include="Wanted\MissingModule.cs" />
|
||||
<Compile Include="Wanted\MovieCutoffModule.cs" />
|
||||
<Compile Include="Wanted\MovieMissingModule.cs" />
|
||||
<Compile Include="Series\MovieDiscoverModule.cs" />
|
||||
<Compile Include="NetImport\ImportExclusionsModule.cs" />
|
||||
<Compile Include="NetImport\ImportExclusionsResource.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
||||
44
src/NzbDrone.Api/Series/MovieDiscoverModule.cs
Normal file
44
src/NzbDrone.Api/Series/MovieDiscoverModule.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class MovieDiscoverModule : NzbDroneRestModule<MovieResource>
|
||||
{
|
||||
private readonly IDiscoverNewMovies _searchProxy;
|
||||
|
||||
public MovieDiscoverModule(IDiscoverNewMovies searchProxy)
|
||||
: base("/movies/discover")
|
||||
{
|
||||
_searchProxy = searchProxy;
|
||||
Get["/{action?recommendations}"] = x => Search(x.action);
|
||||
}
|
||||
|
||||
private Response Search(string action)
|
||||
{
|
||||
var imdbResults = _searchProxy.DiscoverNewMovies(action);
|
||||
return MapToResource(imdbResults).AsResponse();
|
||||
}
|
||||
|
||||
private static IEnumerable<MovieResource> MapToResource(IEnumerable<Core.Tv.Movie> movies)
|
||||
{
|
||||
foreach (var currentSeries in movies)
|
||||
{
|
||||
var resource = currentSeries.ToResource();
|
||||
var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||
if (poster != null)
|
||||
{
|
||||
resource.RemotePoster = poster.Url;
|
||||
}
|
||||
|
||||
yield return resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.SignalR;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using Microsoft.CSharp.RuntimeBinder;
|
||||
using Nancy;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
@@ -58,9 +60,12 @@ namespace NzbDrone.Api.Movie
|
||||
GetResourceAll = AllMovie;
|
||||
GetResourcePaged = GetMoviePaged;
|
||||
GetResourceById = GetMovie;
|
||||
Get[TITLE_SLUG_ROUTE] = (options) => {
|
||||
return ReqResExtensions.AsResponse(GetByTitleSlug(options.slug));
|
||||
};
|
||||
Get[TITLE_SLUG_ROUTE] = GetByTitleSlug; /*(options) => {
|
||||
return ReqResExtensions.AsResponse(GetByTitleSlug(options.slug), Nancy.HttpStatusCode.OK);
|
||||
};*/
|
||||
|
||||
|
||||
|
||||
CreateResource = AddMovie;
|
||||
UpdateResource = UpdateMovie;
|
||||
DeleteResource = DeleteMovie;
|
||||
@@ -145,9 +150,27 @@ namespace NzbDrone.Api.Movie
|
||||
return moviesResources;
|
||||
}
|
||||
|
||||
private MovieResource GetByTitleSlug(string slug)
|
||||
private Response GetByTitleSlug(dynamic options)
|
||||
{
|
||||
return MapToResource(_moviesService.FindByTitleSlug(slug));
|
||||
var slug = "";
|
||||
try
|
||||
{
|
||||
slug = options.slug;
|
||||
// do stuff with x
|
||||
}
|
||||
catch (RuntimeBinderException)
|
||||
{
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return MapToResource(_moviesService.FindByTitleSlug(slug)).AsResponse(Nancy.HttpStatusCode.OK);
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
}
|
||||
|
||||
private int AddMovie(MovieResource moviesResource)
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace NzbDrone.Api.Movie
|
||||
//View & Edit
|
||||
public string Path { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public MoviePathState PathState { get; set; }
|
||||
|
||||
//Editing Only
|
||||
public bool Monitored { get; set; }
|
||||
@@ -131,6 +132,7 @@ namespace NzbDrone.Api.Movie
|
||||
|
||||
Path = model.Path,
|
||||
ProfileId = model.ProfileId,
|
||||
PathState = model.PathState,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MinimumAvailability = model.MinimumAvailability,
|
||||
@@ -187,6 +189,7 @@ namespace NzbDrone.Api.Movie
|
||||
|
||||
Path = resource.Path,
|
||||
ProfileId = resource.ProfileId,
|
||||
PathState = resource.PathState,
|
||||
|
||||
Monitored = resource.Monitored,
|
||||
MinimumAvailability = resource.MinimumAvailability,
|
||||
@@ -217,6 +220,7 @@ namespace NzbDrone.Api.Movie
|
||||
|
||||
movie.Path = resource.Path;
|
||||
movie.ProfileId = resource.ProfileId;
|
||||
movie.PathState = resource.PathState;
|
||||
|
||||
movie.Monitored = resource.Monitored;
|
||||
movie.MinimumAvailability = resource.MinimumAvailability;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -80,5 +80,30 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
return source.Select(predicate).ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source, int n)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
if (n < 0)
|
||||
throw new ArgumentOutOfRangeException("n",
|
||||
"Argument n should be non-negative.");
|
||||
|
||||
return InternalDropLast(source, n);
|
||||
}
|
||||
|
||||
private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
|
||||
{
|
||||
Queue<T> buffer = new Queue<T>(n + 1);
|
||||
|
||||
foreach (T x in source)
|
||||
{
|
||||
buffer.Enqueue(x);
|
||||
|
||||
if (buffer.Count == n + 1)
|
||||
yield return buffer.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,8 +145,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
|
||||
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
|
||||
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
|
||||
public void GetItems_should_return_queued_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
|
||||
{
|
||||
_queued.Status = apiStatus;
|
||||
@@ -160,7 +160,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
|
||||
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
|
||||
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
|
||||
public void GetItems_should_return_downloading_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
|
||||
{
|
||||
_downloading.Status = apiStatus;
|
||||
|
||||
@@ -13,6 +13,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
[TestFixture]
|
||||
public class VuzeFixture : TransmissionFixtureBase<Vuze>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup_Vuze()
|
||||
{
|
||||
// Vuze never sets isFinished.
|
||||
_completed.IsFinished = false;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void queued_item_should_have_required_properties()
|
||||
{
|
||||
@@ -147,8 +154,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
|
||||
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
|
||||
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
|
||||
public void GetItems_should_return_queued_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
|
||||
{
|
||||
_queued.Status = apiStatus;
|
||||
@@ -162,7 +169,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
|
||||
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
|
||||
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
|
||||
public void GetItems_should_return_downloading_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
|
||||
{
|
||||
_downloading.Status = apiStatus;
|
||||
@@ -177,7 +184,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
[TestCase(TransmissionTorrentStatus.Stopped, DownloadItemStatus.Completed, false)]
|
||||
[TestCase(TransmissionTorrentStatus.CheckWait, DownloadItemStatus.Downloading, true)]
|
||||
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading, true)]
|
||||
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Completed, true)]
|
||||
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued, true)]
|
||||
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed, true)]
|
||||
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed, true)]
|
||||
public void GetItems_should_return_completed_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus, bool expectedReadOnly)
|
||||
@@ -294,7 +301,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_have_correct_output_directory()
|
||||
public void should_have_correct_output_directory_for_multifile_torrents()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
@@ -311,5 +318,25 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
items.First().OutputPath.Should().Be(@"C:\Downloads\" + _title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_have_correct_output_directory_for_singlefile_torrents()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
var fileName = _title + ".mkv";
|
||||
_downloading.Name = fileName;
|
||||
_downloading.DownloadDir = @"C:/Downloads";
|
||||
|
||||
GivenTorrents(new List<TransmissionTorrent>
|
||||
{
|
||||
_downloading
|
||||
});
|
||||
|
||||
var items = Subject.GetItems().ToList();
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
items.First().OutputPath.Should().Be(@"C:\Downloads\" + fileName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +296,7 @@
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\EpisodeTitleCollapseFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
|
||||
<Compile Include="OrganizerTests\GetMovieFolderFixture.cs" />
|
||||
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
|
||||
<Compile Include="ParserTests\RomanNumeralTests\RomanNumeralConversionFixture.cs" />
|
||||
<Compile Include="Qualities\RevisionComparableFixture.cs" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
@@ -734,5 +734,7 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||
.Should().Be(releaseGroup);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.Tv;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace NzbDrone.Core.Test.OrganizerTests
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class GetMovieFolderFixture : CoreTest<FileNameBuilder>
|
||||
{
|
||||
private NamingConfig namingConfig;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
namingConfig = NamingConfig.Default;
|
||||
|
||||
Mocker.GetMock<INamingConfigService>()
|
||||
.Setup(c => c.GetConfig()).Returns(namingConfig);
|
||||
}
|
||||
|
||||
[TestCase("Arrival", 2016, "{Movie Title} ({Release Year})", "Arrival (2016)")]
|
||||
[TestCase("The Big Short", 2015, "{Movie TitleThe} ({Release Year})", "Big Short, The (2015)")]
|
||||
[TestCase("The Big Short", 2015, "{Movie Title} ({Release Year})", "The Big Short (2015)")]
|
||||
public void should_use_movieFolderFormat_to_build_folder_name(string movieTitle, int year, string format, string expected)
|
||||
{
|
||||
namingConfig.MovieFolderFormat = format;
|
||||
|
||||
var movie = new Movie { Title = movieTitle, Year = year };
|
||||
|
||||
Subject.GetMovieFolder(movie).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("A.I.Artificial.Intelligence.(2001)", "A.I. Artificial Intelligence")]
|
||||
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
|
||||
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
|
||||
[TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")]
|
||||
public void should_parse_movie_title(string postTitle, string title)
|
||||
{
|
||||
Parser.Parser.ParseMovieTitle(postTitle).MovieTitle.Should().Be(title);
|
||||
@@ -87,9 +88,48 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
}
|
||||
|
||||
[TestCase("The Danish Girl 2015")]
|
||||
[TestCase("The.Danish.Girl.2015.1080p.BluRay.x264.DTS-HD.MA.5.1-RARBG")]
|
||||
public void should_not_parse_language_in_movie_title(string postTitle)
|
||||
{
|
||||
Parser.Parser.ParseMovieTitle(postTitle).Language.Should().Be(Language.English);
|
||||
}
|
||||
|
||||
[TestCase("Prometheus 2012 Directors Cut", "Directors Cut")]
|
||||
[TestCase("Star Wars Episode IV - A New Hope 1999 (Despecialized).mkv", "Despecialized")]
|
||||
[TestCase("Prometheus.2012.(Special.Edition.Remastered).[Bluray-1080p].mkv", "Special Edition Remastered")]
|
||||
[TestCase("Prometheus 2012 Extended", "Extended")]
|
||||
[TestCase("Prometheus 2012 Extended Directors Cut Fan Edit", "Extended Directors Cut Fan Edit")]
|
||||
[TestCase("Prometheus 2012 Director's Cut", "Director's Cut")]
|
||||
[TestCase("Prometheus 2012 Directors Cut", "Directors Cut")]
|
||||
[TestCase("Prometheus.2012.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf", "Extended Theatrical Version IMAX")]
|
||||
[TestCase("2001 A Space Odyssey (1968) Director's Cut .mkv", "Director's Cut")]
|
||||
[TestCase("2001: A Space Odyssey 1968 (Extended Directors Cut FanEdit)", "Extended Directors Cut FanEdit")]
|
||||
[TestCase("A Fake Movie 2035 2012 Directors.mkv", "Directors")]
|
||||
[TestCase("Blade Runner 2049 Director's Cut.mkv", "Director's Cut")]
|
||||
[TestCase("Prometheus 2012 50th Anniversary Edition.mkv", "50th Anniversary Edition")]
|
||||
[TestCase("Movie 2012 2in1.mkv", "2in1")]
|
||||
[TestCase("Movie 2012 IMAX.mkv", "IMAX")]
|
||||
[TestCase("Movie 2012 Restored.mkv", "Restored")]
|
||||
[TestCase("Prometheus.Special.Edition.Fan Edit.2012..BRRip.x264.AAC-m2g", "Special Edition Fan Edit")]
|
||||
[TestCase("Star Wars Episode IV - A New Hope (Despecialized) 1999.mkv", "Despecialized")]
|
||||
[TestCase("Prometheus.(Special.Edition.Remastered).2012.[Bluray-1080p].mkv", "Special Edition Remastered")]
|
||||
[TestCase("Prometheus Extended 2012", "Extended")]
|
||||
[TestCase("Prometheus Extended Directors Cut Fan Edit 2012", "Extended Directors Cut Fan Edit")]
|
||||
[TestCase("Prometheus Director's Cut 2012", "Director's Cut")]
|
||||
[TestCase("Prometheus Directors Cut 2012", "Directors Cut")]
|
||||
[TestCase("Prometheus.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf", "Extended Theatrical Version IMAX")]
|
||||
[TestCase("2001 A Space Odyssey Director's Cut (1968).mkv", "Director's Cut")]
|
||||
[TestCase("2001: A Space Odyssey (Extended Directors Cut FanEdit) Bluray 1080p 1968", "Extended Directors Cut FanEdit")]
|
||||
[TestCase("A Fake Movie 2035 Directors 2012.mkv", "Directors")]
|
||||
[TestCase("Blade Runner Director's Cut 2049.mkv", "Director's Cut")]
|
||||
[TestCase("Prometheus 50th Anniversary Edition 2012.mkv", "50th Anniversary Edition")]
|
||||
[TestCase("Movie 2in1 2012.mkv", "2in1")]
|
||||
[TestCase("Movie IMAX 2012.mkv", "IMAX")]
|
||||
[TestCase("Fake Movie Final Cut 2016", "Final Cut")]
|
||||
[TestCase("Fake Movie 2016 Final Cut ", "Final Cut")]
|
||||
public void should_parse_edition(string postTitle, string edition)
|
||||
{
|
||||
Parser.Parser.ParseMovieTitle(postTitle).Edition.Should().Be(edition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
Subject.Map(_parsedMovieInfo, "", null);
|
||||
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Verify(v => v.FindByTitle(It.IsAny<string>()), Times.Once());
|
||||
.Verify(v => v.FindByTitle(It.IsAny<string>(), It.IsAny<int>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -294,6 +294,20 @@ namespace NzbDrone.Core.Configuration
|
||||
set { SetValue("ExtraFileExtensions", value); }
|
||||
}
|
||||
|
||||
public bool AutoRenameFolders
|
||||
{
|
||||
get { return GetValueBoolean("AutoRenameFolders", false); }
|
||||
|
||||
set { SetValue("AutoRenameFolders", value); }
|
||||
}
|
||||
|
||||
public bool PathsDefaultStatic
|
||||
{
|
||||
get { return GetValueBoolean("PathsDefaultStatic", true); }
|
||||
|
||||
set { SetValue("PathsDefaultStatic", value); }
|
||||
}
|
||||
|
||||
public bool SetPermissionsLinux
|
||||
{
|
||||
get { return GetValueBoolean("SetPermissionsLinux", false); }
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace NzbDrone.Core.Configuration
|
||||
bool CopyUsingHardlinks { get; set; }
|
||||
bool EnableMediaInfo { get; set; }
|
||||
string ExtraFileExtensions { get; set; }
|
||||
bool AutoRenameFolders { get; set; }
|
||||
bool PathsDefaultStatic { get; set; }
|
||||
|
||||
//Permissions (Media Management)
|
||||
bool SetPermissionsLinux { get; set; }
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(136)]
|
||||
public class add_pathstate_to_movies : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Movies").AddColumn("PathState").AsInt32().WithDefaultValue(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(137)]
|
||||
public class add_import_exclusions_table : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
if (!this.Schema.Schema("dbo").Table("ImportExclusions").Exists())
|
||||
{
|
||||
Create.TableForModel("ImportExclusions")
|
||||
.WithColumn("TmdbId").AsInt64().NotNullable().Unique().PrimaryKey()
|
||||
.WithColumn("MovieTitle").AsString().Nullable()
|
||||
.WithColumn("MovieYear").AsInt64().Nullable().WithDefault(0);
|
||||
}
|
||||
Execute.WithConnection(AddExisting);
|
||||
}
|
||||
|
||||
private void AddExisting(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand getSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
getSeriesCmd.Transaction = tran;
|
||||
getSeriesCmd.CommandText = @"SELECT Key, Value FROM Config WHERE Key = 'importexclusions'";
|
||||
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
|
||||
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (seriesReader.Read())
|
||||
{
|
||||
var Key = seriesReader.GetString(0);
|
||||
var Value = seriesReader.GetString(1);
|
||||
|
||||
var importExclusions = Value.Split(',').Select(x => {
|
||||
return string.Format("(\"{0}\", \"{1}\")", Regex.Replace(x, @"^.*\-(.*)$", "$1"),
|
||||
textInfo.ToTitleCase(string.Join(" ", x.Split('-').DropLast(1))));
|
||||
}).ToList();
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "INSERT INTO ImportExclusions (tmdbid, MovieTitle) VALUES " + string.Join(", ", importExclusions);
|
||||
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ using NzbDrone.Core.Extras.Others;
|
||||
using NzbDrone.Core.Extras.Subtitles;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
@@ -105,6 +106,8 @@ namespace NzbDrone.Core.Datastore
|
||||
.Relationship()
|
||||
.HasOne(s => s.Profile, s => s.ProfileId)
|
||||
.HasOne(m => m.MovieFile, m => m.MovieFileId);
|
||||
|
||||
Mapper.Entity<ImportExclusion>().RegisterModel("ImportExclusions");
|
||||
|
||||
|
||||
Mapper.Entity<Episode>().RegisterModel("Episodes")
|
||||
|
||||
@@ -86,8 +86,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
item.Status = DownloadItemStatus.Warning;
|
||||
item.Message = torrent.ErrorString;
|
||||
}
|
||||
else if (torrent.Status == TransmissionTorrentStatus.Seeding ||
|
||||
torrent.Status == TransmissionTorrentStatus.SeedingWait)
|
||||
else if (torrent.LeftUntilDone == 0 && (torrent.Status == TransmissionTorrentStatus.Stopped ||
|
||||
torrent.Status == TransmissionTorrentStatus.Seeding ||
|
||||
torrent.Status == TransmissionTorrentStatus.SeedingWait))
|
||||
{
|
||||
item.Status = DownloadItemStatus.Completed;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,19 @@ namespace NzbDrone.Core.Download.Clients.Vuze
|
||||
|
||||
protected override OsPath GetOutputPath(OsPath outputPath, TransmissionTorrent torrent)
|
||||
{
|
||||
_logger.Debug("Vuze output directory: {0}", outputPath);
|
||||
// Vuze has similar behavior as uTorrent:
|
||||
// - A multi-file torrent is downloaded in a job folder and 'outputPath' points to that directory directly.
|
||||
// - A single-file torrent is downloaded in the root folder and 'outputPath' poinst to that root folder.
|
||||
// We have to make sure the return value points to the job folder OR file.
|
||||
if (outputPath == null || outputPath.FileName == torrent.Name)
|
||||
{
|
||||
_logger.Trace("Vuze output directory: {0}", outputPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputPath = outputPath + torrent.Name;
|
||||
_logger.Trace("Vuze output file: {0}", outputPath);
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
@@ -50,4 +62,4 @@ namespace NzbDrone.Core.Download.Clients.Vuze
|
||||
|
||||
public override string Name => "Vuze";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +134,9 @@ namespace NzbDrone.Core.Jobs
|
||||
{
|
||||
var interval = _configService.RssSyncInterval;
|
||||
|
||||
if (interval > 0 && interval < 10)
|
||||
if (interval > 0 && interval < 5)
|
||||
{
|
||||
return 10;
|
||||
return 5;
|
||||
}
|
||||
|
||||
if (interval < 0)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Commands
|
||||
{
|
||||
public class RenameMovieFolderCommand : Command
|
||||
{
|
||||
public List<int> MovieIds { get; set; }
|
||||
|
||||
public override bool SendUpdatesToClient => true;
|
||||
|
||||
public RenameMovieFolderCommand(List<int> ids)
|
||||
{
|
||||
MovieIds = ids;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IMovieFileRepository _movieFileRepository;
|
||||
private readonly IRenameMovieFileService _renameMovieFiles;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DiskScanService(IDiskProvider diskProvider,
|
||||
@@ -57,6 +58,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
IEventAggregator eventAggregator,
|
||||
IMovieService movieService,
|
||||
IMovieFileRepository movieFileRepository,
|
||||
IRenameMovieFileService renameMovieFiles,
|
||||
Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
@@ -69,6 +71,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_eventAggregator = eventAggregator;
|
||||
_movieService = movieService;
|
||||
_movieFileRepository = movieFileRepository;
|
||||
_renameMovieFiles = renameMovieFiles;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -135,6 +138,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
public void Scan(Movie movie)
|
||||
{
|
||||
//Try renaming the movie path in case anything changed such as year, title or something else.
|
||||
_renameMovieFiles.RenameMoviePath(movie, true);
|
||||
|
||||
var rootFolder = _diskProvider.GetParentFolder(movie.Path);
|
||||
|
||||
if (!_diskProvider.FolderExists(rootFolder))
|
||||
|
||||
18
src/NzbDrone.Core/MediaFiles/Events/MovieFileUpdatedEvent.cs
Normal file
18
src/NzbDrone.Core/MediaFiles/Events/MovieFileUpdatedEvent.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Events
|
||||
{
|
||||
public class MovieFileUpdatedEvent : IEvent
|
||||
{
|
||||
public MovieFile MovieFile { get; private set; }
|
||||
|
||||
public MovieFileUpdatedEvent(MovieFile movieFile)
|
||||
{
|
||||
MovieFile = movieFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
private readonly IDiskTransferService _diskTransferService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
@@ -40,6 +41,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
IDiskTransferService diskTransferService,
|
||||
IDiskProvider diskProvider,
|
||||
IMediaFileAttributeService mediaFileAttributeService,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
@@ -50,6 +52,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_diskTransferService = diskTransferService;
|
||||
_diskProvider = diskProvider;
|
||||
_mediaFileAttributeService = mediaFileAttributeService;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
@@ -116,6 +119,15 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
_diskTransferService.TransferFile(movieFilePath, destinationFilePath, mode);
|
||||
|
||||
var oldMoviePath = movie.Path;
|
||||
|
||||
var newMoviePath = new OsPath(destinationFilePath).Directory.FullPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
movie.Path = newMoviePath;
|
||||
if (oldMoviePath != newMoviePath)
|
||||
{
|
||||
_movieService.UpdateMovie(movie);
|
||||
}
|
||||
|
||||
movieFile.RelativePath = movie.Path.GetRelativePath(destinationFilePath);
|
||||
|
||||
_updateMovieFileService.ChangeFileDateForFile(movieFile, movie);
|
||||
@@ -132,6 +144,14 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
_mediaFileAttributeService.SetFilePermissions(destinationFilePath);
|
||||
|
||||
if(oldMoviePath != newMoviePath)
|
||||
{
|
||||
if (_diskProvider.GetFiles(oldMoviePath, SearchOption.AllDirectories).Count() == 0)
|
||||
{
|
||||
_recycleBinProvider.DeleteFolder(oldMoviePath);
|
||||
}
|
||||
}
|
||||
|
||||
return movieFile;
|
||||
}
|
||||
|
||||
@@ -143,7 +163,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||
private void EnsureMovieFolder(MovieFile movieFile, Movie movie, string filePath)
|
||||
{
|
||||
var movieFolder = Path.GetDirectoryName(filePath);
|
||||
movie.Path = movieFolder;
|
||||
var rootFolder = new OsPath(movieFolder).Directory.FullPath;
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
|
||||
if (!_diskProvider.FolderExists(rootFolder))
|
||||
{
|
||||
@@ -156,7 +178,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
if (!_diskProvider.FolderExists(movieFolder))
|
||||
{
|
||||
CreateFolder(movieFolder);
|
||||
newEvent.SeriesFolder = movieFolder;
|
||||
newEvent.MovieFolder = movieFolder;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,23 +13,29 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public interface IRenameMovieFileService
|
||||
{
|
||||
List<RenameMovieFilePreview> GetRenamePreviews(int movieId);
|
||||
void RenameMoviePath(Movie movie, bool shouldRenameFiles);
|
||||
}
|
||||
|
||||
public class RenameMovieFileService : IRenameMovieFileService,
|
||||
IExecute<RenameMovieFilesCommand>,
|
||||
IExecute<RenameMovieCommand>
|
||||
IExecute<RenameMovieCommand>,
|
||||
IExecute<RenameMovieFolderCommand>
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IMoveMovieFiles _movieFileMover;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IBuildFileNames _filenameBuilder;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RenameMovieFileService(IMovieService movieService,
|
||||
@@ -37,6 +43,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||
IMoveMovieFiles movieFileMover,
|
||||
IEventAggregator eventAggregator,
|
||||
IBuildFileNames filenameBuilder,
|
||||
IConfigService configService,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IDiskProvider diskProvider,
|
||||
Logger logger)
|
||||
{
|
||||
_movieService = movieService;
|
||||
@@ -44,6 +53,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_movieFileMover = movieFileMover;
|
||||
_eventAggregator = eventAggregator;
|
||||
_filenameBuilder = filenameBuilder;
|
||||
_configService = configService;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_diskProvider = diskProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -71,8 +83,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
MovieId = movie.Id,
|
||||
MovieFileId = file.Id,
|
||||
ExistingPath = file.RelativePath,
|
||||
NewPath = movie.Path.GetRelativePath(newPath)
|
||||
ExistingPath = movieFilePath,
|
||||
//NewPath = movie.Path.GetRelativePath(newPath)
|
||||
NewPath = newPath
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,13 +93,19 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
}
|
||||
|
||||
private void RenameFiles(List<MovieFile> movieFiles, Movie movie)
|
||||
private void RenameFiles(List<MovieFile> movieFiles, Movie movie, string oldMoviePath = null)
|
||||
{
|
||||
var renamed = new List<MovieFile>();
|
||||
|
||||
foreach(var movieFile in movieFiles)
|
||||
if (oldMoviePath == null)
|
||||
{
|
||||
var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath);
|
||||
oldMoviePath = movie.Path;
|
||||
}
|
||||
|
||||
foreach (var movieFile in movieFiles)
|
||||
{
|
||||
var oldMovieFilePath = Path.Combine(oldMoviePath, movieFile.RelativePath);
|
||||
movieFile.Path = oldMovieFilePath;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -94,22 +113,64 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_movieFileMover.MoveMovieFile(movieFile, movie);
|
||||
|
||||
_mediaFileService.Update(movieFile);
|
||||
_movieService.UpdateMovie(movie);
|
||||
renamed.Add(movieFile);
|
||||
|
||||
_logger.Debug("Renamed movie file: {0}", movieFile);
|
||||
|
||||
}
|
||||
catch(SameFilenameException ex)
|
||||
catch (SameFilenameException ex)
|
||||
{
|
||||
_logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to rename file: " + movieFilePath);
|
||||
_logger.Error(ex, "Failed to rename file: " + oldMovieFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameMoviePath(Movie movie, bool shouldRenameFiles = true)
|
||||
{
|
||||
var newFolder = _filenameBuilder.BuildMoviePath(movie);
|
||||
if (newFolder != movie.Path && movie.PathState == MoviePathState.Dynamic)
|
||||
{
|
||||
|
||||
if (!_configService.AutoRenameFolders)
|
||||
{
|
||||
_logger.Info("{0}'s movie should be {1} according to your naming config.", movie, newFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Info("{0}'s movie folder changed to: {1}", movie, newFolder);
|
||||
var oldFolder = movie.Path;
|
||||
movie.Path = newFolder;
|
||||
|
||||
if (shouldRenameFiles)
|
||||
{
|
||||
var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id);
|
||||
_logger.ProgressInfo("Renaming movie files for {0}", movie.Title);
|
||||
RenameFiles(movieFiles, movie, oldFolder);
|
||||
_logger.ProgressInfo("All movie files renamed for {0}", movie.Title);
|
||||
}
|
||||
|
||||
_movieService.UpdateMovie(movie);
|
||||
|
||||
if (_diskProvider.GetFiles(oldFolder, SearchOption.AllDirectories).Count() == 0)
|
||||
{
|
||||
_recycleBinProvider.DeleteFolder(oldFolder);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (movie.PathState == MoviePathState.StaticOnce)
|
||||
{
|
||||
movie.PathState = MoviePathState.Dynamic;
|
||||
_movieService.UpdateMovie(movie);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(RenameMovieFilesCommand message)
|
||||
{
|
||||
var movie = _movieService.GetMovie(message.MovieId);
|
||||
@@ -134,5 +195,17 @@ namespace NzbDrone.Core.MediaFiles
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Execute(RenameMovieFolderCommand message)
|
||||
{
|
||||
_logger.Debug("Renaming movie folder for selected movie if necessary");
|
||||
var moviesToRename = _movieService.GetMovies(message.MovieIds);
|
||||
foreach(var movie in moviesToRename)
|
||||
{
|
||||
var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id);
|
||||
_logger.ProgressInfo("Renaming movie folder for {0}", movie.Title);
|
||||
RenameMoviePath(movie);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
||||
private readonly IMoveMovieFiles _movieFileMover;
|
||||
private readonly IRenameMovieFileService _movieFileRenamer;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -26,6 +27,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
IMoveEpisodeFiles episodeFileMover,
|
||||
IMoveMovieFiles movieFileMover,
|
||||
IDiskProvider diskProvider,
|
||||
IRenameMovieFileService movieFileRenamer,
|
||||
Logger logger)
|
||||
{
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
@@ -33,6 +35,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_episodeFileMover = episodeFileMover;
|
||||
_movieFileMover = movieFileMover;
|
||||
_diskProvider = diskProvider;
|
||||
_movieFileRenamer = movieFileRenamer;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -57,6 +60,10 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_mediaFileService.Delete(existingFile, DeleteMediaFileReason.Upgrade);
|
||||
}
|
||||
|
||||
//Temporary for correctly getting path
|
||||
localMovie.Movie.MovieFileId = 1;
|
||||
localMovie.Movie.MovieFile = movieFile;
|
||||
|
||||
if (copyOnly)
|
||||
{
|
||||
moveFileResult.MovieFile = _movieFileMover.CopyMovieFile(movieFile, localMovie);
|
||||
@@ -66,6 +73,8 @@ namespace NzbDrone.Core.MediaFiles
|
||||
moveFileResult.MovieFile = _movieFileMover.MoveMovieFile(movieFile, localMovie);
|
||||
}
|
||||
|
||||
//_movieFileRenamer.RenameMoviePath(localMovie.Movie, false);
|
||||
|
||||
return moveFileResult;
|
||||
}
|
||||
|
||||
|
||||
10
src/NzbDrone.Core/MetadataSource/IDiscoverNewMovies.cs
Normal file
10
src/NzbDrone.Core/MetadataSource/IDiscoverNewMovies.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource
|
||||
{
|
||||
public interface IDiscoverNewMovies
|
||||
{
|
||||
List<Movie> DiscoverNewMovies(string action);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||
public int vote_count { get; set; }
|
||||
public bool video { get; set; }
|
||||
public float vote_average { get; set; }
|
||||
public string trailer_key { get; set; }
|
||||
public string trailer_site { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,10 +15,12 @@ using NzbDrone.Core.Tv;
|
||||
using System.Threading;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
public class SkyHookProxy : IProvideSeriesInfo, ISearchForNewSeries, IProvideMovieInfo, ISearchForNewMovie
|
||||
public class SkyHookProxy : IProvideSeriesInfo, ISearchForNewSeries, IProvideMovieInfo, ISearchForNewMovie, IDiscoverNewMovies
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
@@ -28,8 +30,10 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
private readonly ITmdbConfigService _configService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IPreDBService _predbService;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService, IPreDBService predbService, Logger logger)
|
||||
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService,
|
||||
IPreDBService predbService, IImportExclusionsService exclusionService, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_requestBuilder = requestBuilder.SkyHookTvdb;
|
||||
@@ -37,6 +41,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
_configService = configService;
|
||||
_movieService = movieService;
|
||||
_predbService = predbService;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -348,6 +353,64 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
return resources.movie_results.SelectList(MapMovie).FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Movie> DiscoverNewMovies(string action)
|
||||
{
|
||||
var allMovies = _movieService.GetAllMovies();
|
||||
var allExclusions = _exclusionService.GetAllExclusions();
|
||||
string allIds = string.Join(",", allMovies.Select(m => m.TmdbId));
|
||||
string ignoredIds = string.Join(",", allExclusions.Select(ex => ex.TmdbId));
|
||||
|
||||
HttpRequest request;
|
||||
List<MovieResult> results;
|
||||
|
||||
if (action == "upcoming")
|
||||
{
|
||||
var lastWeek = DateTime.Now.AddDays(-7);
|
||||
var threeWeeks = DateTime.Now.AddDays(7 * 3);
|
||||
|
||||
request = _movieBuilder.Create().SetSegment("route", "discover")
|
||||
.SetSegment("id", "movie")
|
||||
.SetSegment("secondaryRoute", "")
|
||||
.AddQueryParam("region", "us")
|
||||
.AddQueryParam("with_release_type", "5|4|6")
|
||||
.AddQueryParam("release_date.gte", lastWeek.ToString("yyyy-MM-dd"))
|
||||
.AddQueryParam("sort_by", "popularity.desc")
|
||||
.AddQueryParam("release_date.lte", threeWeeks.ToString("yyyy-MM-dd")).Build();
|
||||
|
||||
|
||||
var response = _httpClient.Get<MovieSearchRoot>(request);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new HttpException(request, response);
|
||||
}
|
||||
|
||||
results = response.Resource.results.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
request = new HttpRequestBuilder("https://radarr.video/api/{action}/").SetSegment("action", action).Build();
|
||||
|
||||
request.AllowAutoRedirect = true;
|
||||
request.Method = HttpMethod.POST;
|
||||
request.Headers.ContentType = "application/x-www-form-urlencoded";
|
||||
request.SetContent($"tmdbids={allIds}&ignoredIds={ignoredIds}");
|
||||
|
||||
var response = _httpClient.Post<List<MovieResult>>(request);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new HttpException(request, response);
|
||||
}
|
||||
|
||||
results = response.Resource;
|
||||
}
|
||||
|
||||
results = results.Where(m => allMovies.None(mo => mo.TmdbId == m.id) && allExclusions.None(ex => ex.TmdbId == m.id)).ToList();
|
||||
|
||||
return results.SelectList(MapMovie);
|
||||
}
|
||||
|
||||
private string StripTrailingTheFromTitle(string title)
|
||||
{
|
||||
if(title.EndsWith(",the"))
|
||||
@@ -551,6 +614,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
|
||||
imdbMovie.Images = new List<MediaCover.MediaCover>();
|
||||
imdbMovie.Overview = result.overview;
|
||||
imdbMovie.Ratings = new Ratings { Value = (decimal)result.vote_average, Votes = result.vote_count};
|
||||
|
||||
try
|
||||
{
|
||||
var imdbPoster = _configService.GetCoverForURL(result.poster_path, MediaCoverTypes.Poster);
|
||||
@@ -561,6 +626,15 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
_logger.Debug(result);
|
||||
}
|
||||
|
||||
if (result.trailer_key.IsNotNullOrWhiteSpace() && result.trailer_site.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
if (result.trailer_site == "youtube")
|
||||
{
|
||||
imdbMovie.YouTubeTrailerId = result.trailer_key;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return imdbMovie;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using System.IO;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.ImportExclusions
|
||||
{
|
||||
public class ImportExclusion : ModelBase
|
||||
{
|
||||
public int TmdbId { get; set; }
|
||||
public string MovieTitle { get; set; }
|
||||
public int MovieYear { get; set; }
|
||||
|
||||
new public string ToString()
|
||||
{
|
||||
return string.Format("Excluded Movie: [{0}][{1} {2}]", TmdbId, MovieTitle, MovieYear);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using Marr.Data.QGen;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser.RomanNumerals;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using CoreParser = NzbDrone.Core.Parser.Parser;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.ImportExclusions
|
||||
{
|
||||
public interface IImportExclusionsRepository : IBasicRepository<ImportExclusion>
|
||||
{
|
||||
bool IsMovieExcluded(int tmdbid);
|
||||
}
|
||||
|
||||
public class ImportExclusionsRepository : BasicRepository<ImportExclusion>, IImportExclusionsRepository
|
||||
{
|
||||
protected IMainDatabase _database;
|
||||
|
||||
public ImportExclusionsRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public bool IsMovieExcluded(int tmdbid)
|
||||
{
|
||||
return Query.Where(ex => ex.TmdbId == tmdbid).Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.ImportExclusions
|
||||
{
|
||||
public interface IImportExclusionsService
|
||||
{
|
||||
List<ImportExclusion> GetAllExclusions();
|
||||
bool IsMovieExcluded(int tmdbid);
|
||||
ImportExclusion AddExclusion(ImportExclusion exclusion);
|
||||
void RemoveExclusion(ImportExclusion exclusion);
|
||||
ImportExclusion GetById(int id);
|
||||
}
|
||||
|
||||
public class ImportExclusionsService : IImportExclusionsService
|
||||
{
|
||||
private readonly IImportExclusionsRepository _exclusionRepository;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
public ImportExclusionsService(IImportExclusionsRepository exclusionRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_exclusionRepository = exclusionRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public ImportExclusion AddExclusion(ImportExclusion exclusion)
|
||||
{
|
||||
return _exclusionRepository.Insert(exclusion);
|
||||
}
|
||||
|
||||
public List<ImportExclusion> GetAllExclusions()
|
||||
{
|
||||
return _exclusionRepository.All().ToList();
|
||||
}
|
||||
|
||||
public bool IsMovieExcluded(int tmdbid)
|
||||
{
|
||||
return _exclusionRepository.IsMovieExcluded(tmdbid);
|
||||
}
|
||||
|
||||
public void RemoveExclusion(ImportExclusion exclusion)
|
||||
{
|
||||
_exclusionRepository.Delete(exclusion);
|
||||
}
|
||||
|
||||
public ImportExclusion GetById(int id)
|
||||
{
|
||||
return _exclusionRepository.Get(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.NetImport
|
||||
{
|
||||
@@ -32,11 +33,14 @@ namespace NzbDrone.Core.NetImport
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ISearchForNzb _nzbSearchService;
|
||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
|
||||
public NetImportSearchService(INetImportFactory netImportFactory, IMovieService movieService,
|
||||
ISearchForNewMovie movieSearch, IRootFolderService rootFolder, ISearchForNzb nzbSearchService,
|
||||
IProcessDownloadDecisions processDownloadDecisions, IConfigService configService, Logger logger)
|
||||
IProcessDownloadDecisions processDownloadDecisions, IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
Logger logger)
|
||||
{
|
||||
_netImportFactory = netImportFactory;
|
||||
_movieService = movieService;
|
||||
@@ -44,6 +48,7 @@ namespace NzbDrone.Core.NetImport
|
||||
_nzbSearchService = nzbSearchService;
|
||||
_processDownloadDecisions = processDownloadDecisions;
|
||||
_rootFolder = rootFolder;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
}
|
||||
@@ -119,18 +124,12 @@ namespace NzbDrone.Core.NetImport
|
||||
|
||||
|
||||
var importExclusions = new List<string>();
|
||||
if (_configService.ImportExclusions != null)
|
||||
{
|
||||
// Replace `movie-title-tmdbid` with just tmdbid in exclusions
|
||||
importExclusions = _configService.ImportExclusions.Split(',').Select(x => Regex.Replace(x, @"^.*\-(.*)$", "$1")).ToList();
|
||||
// listedMovies = listedMovies.Where(ah => importExclusions.Any(h => ah.TmdbId.ToString() != h)).ToList();
|
||||
}
|
||||
|
||||
//var downloadedCount = 0;
|
||||
foreach (var movie in listedMovies)
|
||||
{
|
||||
var mapped = _movieSearch.MapMovieToTmdbMovie(movie);
|
||||
if (mapped != null && !importExclusions.Any(x => x == mapped.TmdbId.ToString()))
|
||||
if (mapped != null && !_exclusionService.IsMovieExcluded(mapped.TmdbId))
|
||||
{
|
||||
//List<DownloadDecision> decisions;
|
||||
mapped.AddOptions = new AddMovieOptions {SearchForMovie = true};
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
|
||||
var parameters = new Dictionary<string, object>();
|
||||
parameters.Add("title", title);
|
||||
parameters.Add("message", message);
|
||||
parameters.Add("image", "https://raw.github.com/Sonarr/Sonarr/develop/Logo/64.png");
|
||||
parameters.Add("image", "https://raw.github.com/Radarr/Radarr/develop/Logo/64.png");
|
||||
parameters.Add("displaytime", settings.DisplayTime * 1000);
|
||||
|
||||
ProcessRequest(request, settings, "GUI.ShowNotification", parameters);
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
<Compile Include="Authentication\UserRepository.cs" />
|
||||
<Compile Include="Authentication\UserService.cs" />
|
||||
<Compile Include="Datastore\Migration\123_create_netimport_table.cs" />
|
||||
<Compile Include="MediaFiles\Events\MovieFileUpdatedEvent.cs" />
|
||||
<Compile Include="Datastore\Migration\134_add_remux_qualities_for_the_wankers.cs" />
|
||||
<Compile Include="Datastore\Migration\129_add_parsed_movie_info_to_pending_release.cs" />
|
||||
<Compile Include="Datastore\Migration\128_remove_kickass.cs" />
|
||||
@@ -1275,7 +1276,14 @@
|
||||
<Compile Include="Datastore\Migration\131_make_parsed_episode_info_nullable.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\FixWronglyMatchedMovieFiles.cs" />
|
||||
<Compile Include="Datastore\Migration\135_add_haspredbentry_to_movies.cs" />
|
||||
<Compile Include="MediaFiles\Commands\RenameMovieFolderCommand.cs" />
|
||||
<Compile Include="Tv\QueryExtensions.cs" />
|
||||
<Compile Include="Datastore\Migration\136_add_pathstate_to_movies.cs" />
|
||||
<Compile Include="MetadataSource\IDiscoverNewMovies.cs" />
|
||||
<Compile Include="Datastore\Migration\137_add_import_exclusions_table.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusion.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusionsRepository.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusionsService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
||||
@@ -1345,6 +1353,9 @@
|
||||
<Compile Include="Notifications\Telegram\TelegramError.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Folder Include="NetImport\ImportExclusions\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace NzbDrone.Core.Organizer
|
||||
string BuildFileName(Movie movie, MovieFile movieFile, NamingConfig namingConfig = null);
|
||||
string BuildFilePath(Movie movie, string fileName, string extension);
|
||||
string BuildFilePath(Series series, int seasonNumber, string fileName, string extension);
|
||||
string BuildMoviePath(Movie movie, NamingConfig namingConfig = null);
|
||||
string BuildSeasonPath(Series series, int seasonNumber);
|
||||
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
||||
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
|
||||
@@ -158,12 +159,11 @@ namespace NzbDrone.Core.Organizer
|
||||
return GetOriginalTitle(movieFile);
|
||||
}
|
||||
|
||||
//TODO: Update namingConfig for Movies!
|
||||
var pattern = namingConfig.StandardMovieFormat;
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddMovieTokens(tokenHandlers, movie);
|
||||
AddReleaseDateTokens(tokenHandlers, movie.Year); //In case we want to separate the year
|
||||
AddReleaseDateTokens(tokenHandlers, movie.Year);
|
||||
AddImdbIdTokens(tokenHandlers, movie.ImdbId);
|
||||
AddQualityTokens(tokenHandlers, movie, movieFile);
|
||||
AddMediaInfoTokens(tokenHandlers, movieFile);
|
||||
@@ -190,11 +190,61 @@ namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
||||
|
||||
var path = movie.Path;
|
||||
var path = "";
|
||||
|
||||
if (movie.PathState > 0)
|
||||
{
|
||||
path = movie.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = BuildMoviePath(movie);
|
||||
}
|
||||
|
||||
return Path.Combine(path, fileName + extension);
|
||||
}
|
||||
|
||||
public string BuildMoviePath(Movie movie, NamingConfig namingConfig = null)
|
||||
{
|
||||
if (namingConfig == null)
|
||||
{
|
||||
namingConfig = _namingConfigService.GetConfig();
|
||||
}
|
||||
|
||||
var path = movie.Path;
|
||||
var directory = new DirectoryInfo(path).Name;
|
||||
var parentDirectoryPath = new DirectoryInfo(path).Parent.FullName;
|
||||
|
||||
var movieFile = movie.MovieFile;
|
||||
|
||||
var pattern = namingConfig.MovieFolderFormat;
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddMovieTokens(tokenHandlers, movie);
|
||||
AddReleaseDateTokens(tokenHandlers, movie.Year);
|
||||
AddImdbIdTokens(tokenHandlers, movie.ImdbId);
|
||||
|
||||
if(movie.MovieFileId != 0)
|
||||
{
|
||||
movieFile.LazyLoad();
|
||||
AddQualityTokens(tokenHandlers, movie, movieFile);
|
||||
AddMediaInfoTokens(tokenHandlers, movieFile);
|
||||
AddMovieFileTokens(tokenHandlers, movieFile);
|
||||
AddTagsTokens(tokenHandlers, movieFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMovieFileTokens(tokenHandlers, new MovieFile { SceneName = $"{movie.Title} {movie.Year}", RelativePath = $"{movie.Title} {movie.Year}" });
|
||||
}
|
||||
|
||||
|
||||
var directoryName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
||||
directoryName = FileNameCleanupRegex.Replace(directoryName, match => match.Captures[0].Value[0].ToString());
|
||||
directoryName = TrimSeparatorsRegex.Replace(directoryName, string.Empty);
|
||||
|
||||
return Path.Combine(parentDirectoryPath, directoryName);
|
||||
}
|
||||
|
||||
public string BuildSeasonPath(Series series, int seasonNumber)
|
||||
{
|
||||
var path = series.Path;
|
||||
@@ -302,12 +352,28 @@ namespace NzbDrone.Core.Organizer
|
||||
namingConfig = _namingConfigService.GetConfig();
|
||||
}
|
||||
|
||||
var movieFile = movie.MovieFile;
|
||||
|
||||
var pattern = namingConfig.MovieFolderFormat;
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddMovieTokens(tokenHandlers, movie);
|
||||
AddReleaseDateTokens(tokenHandlers, movie.Year);
|
||||
AddImdbIdTokens(tokenHandlers, movie.ImdbId);
|
||||
|
||||
if (movie.MovieFileId != 0)
|
||||
{
|
||||
movieFile.LazyLoad();
|
||||
AddQualityTokens(tokenHandlers, movie, movieFile);
|
||||
AddMediaInfoTokens(tokenHandlers, movieFile);
|
||||
AddMovieFileTokens(tokenHandlers, movieFile);
|
||||
AddTagsTokens(tokenHandlers, movieFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMovieFileTokens(tokenHandlers, new MovieFile { SceneName = $"{movie.Title} {movie.Year}", RelativePath = $"{movie.Title} {movie.Year}"});
|
||||
}
|
||||
|
||||
return CleanFolderName(ReplaceTokens(namingConfig.MovieFolderFormat, tokenHandlers, namingConfig));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,13 +44,6 @@ namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
_buildFileNames = buildFileNames;
|
||||
|
||||
_movie = new Movie
|
||||
{
|
||||
Title = "The Movie Title",
|
||||
Year = 2010,
|
||||
ImdbId = "tt0066921"
|
||||
};
|
||||
|
||||
_standardSeries = new Series
|
||||
{
|
||||
SeriesType = SeriesTypes.Standard,
|
||||
@@ -122,13 +115,22 @@ namespace NzbDrone.Core.Organizer
|
||||
_movieFile = new MovieFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.Bluray1080p, new Revision(2)),
|
||||
RelativePath = "Movie.Title.2010.1080p.BluRay.DTS.x264-EVOLVE.mkv",
|
||||
SceneName = "Movie.Title.2010.1080p.BluRay.DTS.x264-EVOLVE",
|
||||
ReleaseGroup = "RlsGrp",
|
||||
RelativePath = "The.Movie.Title.2010.1080p.BluRay.DTS.x264-EVOLVE.mkv",
|
||||
SceneName = "The.Movie.Title.2010.1080p.BluRay.DTS.x264-EVOLVE",
|
||||
ReleaseGroup = "EVOLVE",
|
||||
MediaInfo = mediaInfo,
|
||||
Edition = "Ultimate extended edition",
|
||||
};
|
||||
|
||||
_movie = new Movie
|
||||
{
|
||||
Title = "The Movie: Title",
|
||||
Year = 2010,
|
||||
ImdbId = "tt0066921",
|
||||
MovieFile = _movieFile,
|
||||
MovieFileId = 1,
|
||||
};
|
||||
|
||||
_singleEpisodeFile = new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.HDTV720p, new Revision(2)),
|
||||
|
||||
@@ -19,11 +19,11 @@ 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>(\.?((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+|_|$)(?!\\)",
|
||||
new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*\(?(?<edition>(((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final(?=(.(Cut|Edition|Version)))|Extended|Rogue|Special|Despecialized|\d{2,3}(th)?.Anniversary)(.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit|Edition|Restored|((2|3|4)in1))))))\)?.+(?<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>((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX))",
|
||||
new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|(19|20)\d{2}|\]|\W(19|20)\d{2})))+(\W+|_|$)(?!\\)\(?(?<edition>(((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final(?=(.(Cut|Edition|Version)))|Extended|Rogue|Special|Despecialized|\d{2,3}(th)?.Anniversary)(.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit|Edition|Restored|((2|3|4)in1))))))\)?",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Normal movie format, e.g: Mission.Impossible.3.2011
|
||||
@@ -412,9 +412,9 @@ namespace NzbDrone.Core.Parser
|
||||
if (result != null)
|
||||
{
|
||||
var languageTitle = simpleTitle;
|
||||
if (result.MovieTitle.IsNotNullOrWhiteSpace() )
|
||||
if (match[0].Groups["title"].Success && match[0].Groups["title"].Value.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
languageTitle = simpleTitle.Replace(result.MovieTitle, "A Movie");
|
||||
languageTitle = simpleTitle.Replace(match[0].Groups["title"].Value, "A Movie");
|
||||
}
|
||||
|
||||
result.Language = LanguageParser.ParseLanguage(languageTitle);
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace NzbDrone.Core.Parser
|
||||
private Movie GetMovie(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
// TODO: Answer me this: Wouldn't it be smarter to start out looking for a movie if we have an ImDb Id?
|
||||
if (!String.IsNullOrWhiteSpace(imdbId))
|
||||
if (!String.IsNullOrWhiteSpace(imdbId) && imdbId != "0")
|
||||
{
|
||||
Movie movieByImDb;
|
||||
if (TryGetMovieByImDbId(parsedMovieInfo, imdbId, out movieByImDb))
|
||||
@@ -403,6 +403,8 @@ namespace NzbDrone.Core.Parser
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitle);
|
||||
if (isNotNull(movieByTitleAndOrYear))
|
||||
@@ -686,4 +688,30 @@ namespace NzbDrone.Core.Parser
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class MappingException : Exception
|
||||
{
|
||||
public virtual string Reason()
|
||||
{
|
||||
return "Parsed movie does not match wanted movie";
|
||||
}
|
||||
}
|
||||
|
||||
public class YearDoesNotMatchException : MappingException
|
||||
{
|
||||
public int ExpectedYear { get; set; }
|
||||
public int? ParsedYear { get; set; }
|
||||
|
||||
override public string Reason()
|
||||
{
|
||||
if (ParsedYear.HasValue && ParsedYear > 1800)
|
||||
{
|
||||
return $"Expected {ExpectedYear}, but found {ParsedYear} for year";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Did not find a valid year";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ namespace NzbDrone.Core.Tv
|
||||
public List<Actor> Actors { get; set; }
|
||||
public string Certification { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
public MoviePathState PathState { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public DateTime? InCinemas { get; set; }
|
||||
public DateTime? PhysicalRelease { get; set; }
|
||||
@@ -117,4 +118,11 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public bool SearchForMovie { get; set; }
|
||||
}
|
||||
|
||||
public enum MoviePathState
|
||||
{
|
||||
Dynamic,
|
||||
StaticOnce,
|
||||
Static,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
@@ -51,6 +52,7 @@ namespace NzbDrone.Core.Tv
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
@@ -60,12 +62,14 @@ namespace NzbDrone.Core.Tv
|
||||
IEpisodeService episodeService,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
Logger logger)
|
||||
{
|
||||
_movieRepository = movieRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_configService = configService;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -150,13 +154,23 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
Ensure.That(newMovie, () => newMovie).IsNotNull();
|
||||
|
||||
MoviePathState defaultState = MoviePathState.Static;
|
||||
if (!_configService.PathsDefaultStatic)
|
||||
{
|
||||
defaultState = MoviePathState.Dynamic;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(newMovie.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(newMovie);
|
||||
newMovie.Path = Path.Combine(newMovie.RootFolderPath, folderName);
|
||||
newMovie.PathState = defaultState;
|
||||
}
|
||||
else
|
||||
{
|
||||
newMovie.PathState = defaultState == MoviePathState.Dynamic ? MoviePathState.StaticOnce : MoviePathState.Static;
|
||||
}
|
||||
|
||||
_logger.Info("Adding Movie {0} Path: [{1}]", newMovie, newMovie.Path);
|
||||
_logger.Info("Adding Movie {0} Path: [{1}]", newMovie, newMovie.Path);
|
||||
|
||||
newMovie.CleanTitle = newMovie.Title.CleanSeriesTitle();
|
||||
newMovie.SortTitle = MovieTitleNormalizer.Normalize(newMovie.Title, newMovie.TmdbId);
|
||||
@@ -174,10 +188,20 @@ namespace NzbDrone.Core.Tv
|
||||
|
||||
newMovies.ForEach(m =>
|
||||
{
|
||||
MoviePathState defaultState = MoviePathState.Static;
|
||||
if (!_configService.PathsDefaultStatic)
|
||||
{
|
||||
defaultState = MoviePathState.Dynamic;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(m.Path))
|
||||
{
|
||||
var folderName = _fileNameBuilder.GetMovieFolder(m);
|
||||
m.Path = Path.Combine(m.RootFolderPath, folderName);
|
||||
m.PathState = defaultState;
|
||||
}
|
||||
else
|
||||
{
|
||||
m.PathState = defaultState == MoviePathState.Dynamic ? MoviePathState.StaticOnce : MoviePathState.Static;
|
||||
}
|
||||
|
||||
m.CleanTitle = m.Title.CleanSeriesTitle();
|
||||
@@ -266,14 +290,7 @@ namespace NzbDrone.Core.Tv
|
||||
var movie = _movieRepository.Get(movieId);
|
||||
if (addExclusion)
|
||||
{
|
||||
if (_configService.ImportExclusions.Empty())
|
||||
{
|
||||
_configService.ImportExclusions = movie.TitleSlug;
|
||||
}
|
||||
else if (!_configService.ImportExclusions.Contains(movie.TitleSlug))
|
||||
{
|
||||
_configService.ImportExclusions += ',' + movie.TitleSlug;
|
||||
}
|
||||
_exclusionService.AddExclusion(new ImportExclusion {TmdbId = movie.TmdbId, MovieTitle = movie.Title, MovieYear = movie.Year } );
|
||||
}
|
||||
_movieRepository.Delete(movieId);
|
||||
_eventAggregator.PublishEvent(new MovieDeletedEvent(movie, deleteFiles));
|
||||
|
||||
@@ -13,6 +13,7 @@ using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
@@ -22,6 +23,7 @@ namespace NzbDrone.Core.Tv
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IRefreshEpisodeService _refreshEpisodeService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly ICheckIfMovieShouldBeRefreshed _checkIfMovieShouldBeRefreshed;
|
||||
private readonly Logger _logger;
|
||||
@@ -32,12 +34,14 @@ namespace NzbDrone.Core.Tv
|
||||
IEventAggregator eventAggregator,
|
||||
IDiskScanService diskScanService,
|
||||
ICheckIfMovieShouldBeRefreshed checkIfMovieShouldBeRefreshed,
|
||||
IManageCommandQueue commandQueue,
|
||||
Logger logger)
|
||||
{
|
||||
_movieInfo = movieInfo;
|
||||
_movieService = movieService;
|
||||
_refreshEpisodeService = refreshEpisodeService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_commandQueueManager = commandQueue;
|
||||
_diskScanService = diskScanService;
|
||||
_checkIfMovieShouldBeRefreshed = checkIfMovieShouldBeRefreshed;
|
||||
_logger = logger;
|
||||
@@ -136,6 +140,7 @@ namespace NzbDrone.Core.Tv
|
||||
try
|
||||
{
|
||||
_logger.Info("Skipping refresh of movie: {0}", movie.Title);
|
||||
_commandQueueManager.Push(new RenameMovieFolderCommand(new List<int>{movie.Id}));
|
||||
_diskScanService.Scan(movie);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -8,6 +8,7 @@ var ProfileCollection = require('../Profile/ProfileCollection');
|
||||
var AddFromListView = require("./List/AddFromListView");
|
||||
var RootFolderCollection = require('./RootFolders/RootFolderCollection');
|
||||
var BulkImportView = require("./BulkImport/BulkImportView");
|
||||
var DiscoverMoviesCollection = require("./DiscoverMoviesCollection");
|
||||
require('../Movies/MoviesCollection');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
@@ -22,7 +23,7 @@ module.exports = Marionette.Layout.extend({
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-import' : '_importMovies',
|
||||
'click .x-discover' : '_discoverMovies',
|
||||
'click .x-bulk-import' : '_bulkImport',
|
||||
'click .x-add-new' : '_addMovies',
|
||||
"click .x-add-lists" : "_addFromList",
|
||||
@@ -70,10 +71,11 @@ module.exports = Marionette.Layout.extend({
|
||||
this.workspace.show(new BulkImportView({ model : options.model}));
|
||||
},
|
||||
|
||||
_importMovies : function() {
|
||||
this.rootFolderLayout = new RootFolderLayout();
|
||||
this.listenTo(this.rootFolderLayout, 'folderSelected', this._folderSelected);
|
||||
AppLayout.modalRegion.show(this.rootFolderLayout);
|
||||
_discoverMovies : function(options) {
|
||||
options = options || {};
|
||||
options.action = "discover";
|
||||
options.collection = new DiscoverMoviesCollection();
|
||||
this.workspace.show(new AddMoviesView(options));
|
||||
},
|
||||
|
||||
_addMovies : function(options) {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group add-movies-btn-group btn-group-lg btn-block">
|
||||
<button class="btn btn-default col-md-3 col-xs-4 x-bulk-import"><i class="icon-sonarr-view-list hidden-xs"></i> Bulk Import Movies</button>
|
||||
<button type="button" class="btn btn-default col-md-4 col-xs-4 add-movies-import-btn x-import">
|
||||
<i class="icon-sonarr-hdd"/>
|
||||
Import existing movies on disk
|
||||
</button>
|
||||
<button class="btn btn-default col-md-2 col-xs-4 x-add-new"><i class="icon-sonarr-active hidden-xs"></i> Add New Movie</button>
|
||||
<button class="btn btn-default col-md-3 col-xs-4 x-add-lists"><i class="icon-sonarr-active hidden-xs"></i> Add Movies from Lists</button>
|
||||
<div class="btn-group add-movies-btn-group btn-group-lg btn-block btn-group-collapse">
|
||||
<button class="btn btn-default col-md-3 col-xs-12 x-bulk-import"><i class="icon-sonarr-view-list hidden-xs"></i> Bulk Import Movies</button>
|
||||
<button type="button" class="btn btn-default col-md-4 col-xs-12 add-movies-import-btn x-discover"><i class="icon-sonarr-star"/> Discover new movies</button>
|
||||
<button class="btn btn-default col-md-2 col-xs-12 x-add-new"><i class="icon-sonarr-active hidden-xs"></i> Add New Movie</button>
|
||||
<button class="btn btn-default col-md-3 col-xs-12 x-add-lists"><i class="icon-sonarr-active hidden-xs"></i> Add Movies from Lists</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var AddMoviesCollection = require('./AddMoviesCollection');
|
||||
var SearchResultCollectionView = require('./SearchResultCollectionView');
|
||||
var EmptyView = require('./EmptyView');
|
||||
var NotFoundView = require('./NotFoundView');
|
||||
var DiscoverEmptyView = require('./DiscoverEmptyView');
|
||||
var ErrorView = require('./ErrorView');
|
||||
var LoadingView = require('../Shared/LoadingView');
|
||||
var FullMovieCollection = require("../Movies/FullMovieCollection");
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'AddMovies/AddMoviesViewTemplate',
|
||||
@@ -18,16 +21,24 @@ module.exports = Marionette.Layout.extend({
|
||||
ui : {
|
||||
moviesSearch : '.x-movies-search',
|
||||
searchBar : '.x-search-bar',
|
||||
loadMore : '.x-load-more'
|
||||
loadMore : '.x-load-more',
|
||||
discoverHeader : ".x-discover-header",
|
||||
discoverBefore : ".x-discover-before",
|
||||
discoverRecos : ".x-recommendations-tab",
|
||||
discoverPopular : ".x-popular-tab" ,
|
||||
discoverUpcoming : ".x-upcoming-tab"
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-load-more' : '_onLoadMore'
|
||||
'click .x-load-more' : '_onLoadMore',
|
||||
"click .x-recommendations-tab" : "_discoverRecos",
|
||||
"click .x-popular-tab" : "_discoverPopular",
|
||||
"click .x-upcoming-tab" : "_discoverUpcoming"
|
||||
},
|
||||
|
||||
initialize : function(options) {
|
||||
this.isExisting = options.isExisting;
|
||||
this.collection = new AddMoviesCollection();
|
||||
this.collection = options.collection || new AddMoviesCollection();
|
||||
|
||||
if (this.isExisting) {
|
||||
this.collection.unmappedFolderModel = this.model;
|
||||
@@ -51,6 +62,8 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
if (options.action === "search") {
|
||||
this.search({term: options.query});
|
||||
} else if (options.action == "discover") {
|
||||
this.isDiscover = true;
|
||||
}
|
||||
|
||||
},
|
||||
@@ -58,6 +71,8 @@ module.exports = Marionette.Layout.extend({
|
||||
onRender : function() {
|
||||
var self = this;
|
||||
|
||||
|
||||
|
||||
this.$el.addClass(this.className);
|
||||
|
||||
this.ui.moviesSearch.keyup(function(e) {
|
||||
@@ -95,10 +110,24 @@ module.exports = Marionette.Layout.extend({
|
||||
if (this.isExisting) {
|
||||
this.ui.searchBar.hide();
|
||||
}
|
||||
|
||||
if (this.isDiscover) {
|
||||
this.ui.searchBar.hide();
|
||||
this._discoverRecos();
|
||||
/*if (this.collection.length == 0) {
|
||||
this.searchResult.show(new LoadingView());
|
||||
}*/
|
||||
}
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.ui.discoverBefore.hide();
|
||||
this.ui.moviesSearch.focus();
|
||||
this.ui.loadMore.hide();
|
||||
|
||||
if (this.isDiscover) {
|
||||
this.ui.discoverBefore.show();
|
||||
}
|
||||
},
|
||||
|
||||
search : function(options) {
|
||||
@@ -140,7 +169,10 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
_onLoadMore : function() {
|
||||
var showingAll = this.resultCollectionView.showMore();
|
||||
this.ui.searchBar.show();
|
||||
if (!this.isDiscover) {
|
||||
this.ui.searchBar.show();
|
||||
}
|
||||
|
||||
|
||||
if (showingAll) {
|
||||
this.ui.loadMore.hide();
|
||||
@@ -159,8 +191,14 @@ module.exports = Marionette.Layout.extend({
|
||||
_showResults : function() {
|
||||
if (!this.isClosed) {
|
||||
if (this.collection.length === 0) {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
||||
this.ui.loadMore.hide();
|
||||
if (this.isDiscover) {
|
||||
this.searchResult.show(new DiscoverEmptyView());
|
||||
} else {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
||||
}
|
||||
|
||||
} else {
|
||||
this.searchResult.show(this.resultCollectionView);
|
||||
if (!this.showingAll) {
|
||||
@@ -185,5 +223,35 @@ module.exports = Marionette.Layout.extend({
|
||||
this.searchResult.show(new ErrorView({ term : this.collection.term }));
|
||||
this.collection.term = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_discover : function(action) {
|
||||
if (this.collection.action === action) {
|
||||
return
|
||||
}
|
||||
this.collection.reset();
|
||||
this.searchResult.show(new LoadingView());
|
||||
this.collection.action = action;
|
||||
this.currentSearchPromise = this.collection.fetch();
|
||||
},
|
||||
|
||||
_discoverRecos : function() {
|
||||
this.ui.discoverRecos.tab("show");
|
||||
this.ui.discoverHeader.html("Recommendations by The Movie Database for you");
|
||||
this._discover("recommendations");
|
||||
},
|
||||
|
||||
_discoverPopular : function() {
|
||||
this.ui.discoverPopular.tab("show");
|
||||
this.ui.discoverHeader.html("Currently Popular Movies");
|
||||
this._discover("popular");
|
||||
},
|
||||
|
||||
_discoverUpcoming : function() {
|
||||
this.ui.discoverUpcoming.tab("show");
|
||||
this.ui.discoverHeader.html("Movies coming to Blu-Ray in the next weeks");
|
||||
this._discover("upcoming");
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -4,6 +4,18 @@
|
||||
{{folder.path}}
|
||||
</div>
|
||||
</div>{{/if}}
|
||||
|
||||
<div class="x-discover-before">
|
||||
<ul class="nav nav-tabs nav-justified settings-tabs">
|
||||
<li><a href="#media-management" class="x-recommendations-tab no-router">Recommendations</a></li>
|
||||
<li><a href="#popular" class="x-popular-tab no-router">Popular</a></li>
|
||||
<li><a href="#upcoming" class="x-upcoming-tab no-router">Upcoming</a></li>
|
||||
</ul>
|
||||
<h2 class="x-discover-header">
|
||||
Recommendations by The Movie Database based on your library:
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="x-search-bar">
|
||||
<div class="input-group input-group-lg add-movies-search">
|
||||
<span class="input-group-addon"><i class="icon-sonarr-search"/></span>
|
||||
|
||||
5
src/UI/AddMovies/DiscoverEmptyView.js
Normal file
5
src/UI/AddMovies/DiscoverEmptyView.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.CompositeView.extend({
|
||||
template : 'AddMovies/DiscoverEmptyViewTemplate'
|
||||
});
|
||||
6
src/UI/AddMovies/DiscoverEmptyViewTemplate.hbs
Normal file
6
src/UI/AddMovies/DiscoverEmptyViewTemplate.hbs
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="text-center col-md-12">
|
||||
<h3>
|
||||
No movies left to discover. Come back at another time :)
|
||||
</h3>
|
||||
|
||||
</div>
|
||||
26
src/UI/AddMovies/DiscoverMoviesCollection.js
Normal file
26
src/UI/AddMovies/DiscoverMoviesCollection.js
Normal file
@@ -0,0 +1,26 @@
|
||||
var Backbone = require('backbone');
|
||||
var MovieModel = require('../Movies/MovieModel');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : function() {
|
||||
var route = this.action || "";
|
||||
return window.NzbDrone.ApiRoot + "/movies/discover/" + route;
|
||||
},
|
||||
|
||||
model : MovieModel,
|
||||
|
||||
parse : function(response) {
|
||||
var self = this;
|
||||
|
||||
_.each(response, function(model) {
|
||||
model.id = undefined;
|
||||
|
||||
if (self.unmappedFolderModel) {
|
||||
model.path = self.unmappedFolderModel.get('folder').path;
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
});
|
||||
@@ -2,6 +2,7 @@ var Marionette = require('marionette');
|
||||
var SearchResultView = require('./SearchResultView');
|
||||
var FullMovieCollection = require('../Movies/FullMovieCollection');
|
||||
var vent = require('vent');
|
||||
var $ = require("jquery");
|
||||
|
||||
module.exports = Marionette.CollectionView.extend({
|
||||
itemView : SearchResultView,
|
||||
@@ -9,7 +10,7 @@ module.exports = Marionette.CollectionView.extend({
|
||||
initialize : function(options) {
|
||||
this.showExisting = true;
|
||||
this.isExisting = options.isExisting;
|
||||
this.showing = 5;
|
||||
this.showing = 10;
|
||||
if (this.isExisting) {
|
||||
this.showing = 1;
|
||||
}
|
||||
@@ -28,9 +29,10 @@ module.exports = Marionette.CollectionView.extend({
|
||||
},
|
||||
|
||||
showMore : function() {
|
||||
this.showing += 5;
|
||||
var pos = $(window).scrollTop();
|
||||
this.showing += 10;
|
||||
this.render();
|
||||
|
||||
$(window).scrollTop(pos);
|
||||
return this.showing >= this.collection.length;
|
||||
},
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ var Profiles = require('../Profile/ProfileCollection');
|
||||
var RootFolders = require('./RootFolders/RootFolderCollection');
|
||||
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
|
||||
var FullMovieCollection = require('../Movies/FullMovieCollection');
|
||||
var ImportExclusionModel = require("../Settings/NetImport/ImportExclusionModel");
|
||||
var Config = require('../Config');
|
||||
var Messenger = require('../Shared/Messenger');
|
||||
var AsValidatedView = require('../Mixins/AsValidatedView');
|
||||
@@ -33,6 +34,7 @@ var view = Marionette.ItemView.extend({
|
||||
events : {
|
||||
'click .x-add' : '_addWithoutSearch',
|
||||
'click .x-add-search' : '_addAndSearch',
|
||||
"click .x-ignore" : "_ignoreMovie",
|
||||
'change .x-profile' : '_profileChanged',
|
||||
'change .x-root-folder' : '_rootFolderChanged',
|
||||
'change .x-season-folder' : '_seasonFolderChanged',
|
||||
@@ -239,6 +241,14 @@ var view = Marionette.ItemView.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_ignoreMovie : function() {
|
||||
var exclusion = new ImportExclusionModel({tmdbId : this.model.get("tmdbId"),
|
||||
movieTitle : this.model.get("title"), movieYear : this.model.get("year")});
|
||||
exclusion.save();
|
||||
this.model.destroy();
|
||||
this.remove();
|
||||
},
|
||||
|
||||
_rootFoldersUpdated : function() {
|
||||
this._configureTemplateHelpers();
|
||||
this.render();
|
||||
|
||||
@@ -26,7 +26,16 @@
|
||||
{{#if_eq status compare="inCinemas"}}
|
||||
<span class="label label-warning">In Cinemas</span>
|
||||
{{/if_eq}}
|
||||
<span class="label label-default" title="{{ratings.votes}} Vote(s)">{{ratings.value}}</span>
|
||||
|
||||
{{#if youTubeTrailerId}}
|
||||
<span class="label label-info">
|
||||
<a href="{{youTubeTrailerUrl}}" style="color: white;">Trailer</a>
|
||||
</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
|
||||
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,6 +106,10 @@
|
||||
<button class="btn btn-success add x-add-search" title="Add and Search for movie">
|
||||
<i class="icon-sonarr-search"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-warning ignore x-ignore" title="Ignore this movie, so it does not show up anymore">
|
||||
<i class="icon-sonarr-ignore"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
|
||||
@@ -304,9 +304,16 @@
|
||||
.fa-icon-color(@brand-danger);
|
||||
}
|
||||
|
||||
.icon-sonarr-ignore {
|
||||
.fa-icon-content(@fa-var-eye-slash);
|
||||
}
|
||||
|
||||
.icon-sonarr-deleted {
|
||||
.fa-icon-content(@fa-var-trash);
|
||||
}
|
||||
.icon-sonarr-star {
|
||||
.fa-icon-content(@fa-var-star);
|
||||
}
|
||||
|
||||
.icon-sonarr-clear {
|
||||
.fa-icon-content(@fa-var-trash);
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
display : block;
|
||||
float : none;
|
||||
border-radius : @border-radius-base !important;
|
||||
word-wrap : normal;
|
||||
white-space : normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@ module.exports = Marionette.Layout.extend({
|
||||
},
|
||||
|
||||
_automaticImport : function (e) {
|
||||
CommandController.Execute('downloadedMovieScan', {
|
||||
name : 'downloadedMovieScan',
|
||||
CommandController.Execute('downloadedMoviesScan', {
|
||||
name : 'downloadedMoviesScan',
|
||||
path : e.folder
|
||||
});
|
||||
|
||||
@@ -271,4 +271,4 @@ module.exports = Marionette.Layout.extend({
|
||||
hideAfter : 5
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,27 +47,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Use Season Folder</label>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Static Path</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="seasonFolder"/>
|
||||
<input type="checkbox" name="pathState"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
<div class="btn btn-primary slide-button">
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="Should downloaded episodes be stored in season folders?"/>
|
||||
<i class="icon-sonarr-form-info" title="Should movie path stay static or should it change on each disk scan according to your naming config? Note: Auto Rename Folders under Settings -> Media Management must be enabled too."/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Profile</label>
|
||||
|
||||
@@ -22,6 +22,12 @@ var view = Marionette.ItemView.extend({
|
||||
|
||||
initialize : function() {
|
||||
this.model.set('profiles', Profiles);
|
||||
var pathState = this.model.get("pathState");
|
||||
if (pathState == "static") {
|
||||
this.model.set("pathState", true);
|
||||
} else {
|
||||
this.model.set("pathState", false);
|
||||
}
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
@@ -30,11 +36,18 @@ var view = Marionette.ItemView.extend({
|
||||
model : this.model,
|
||||
property : 'tags'
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_onBeforeSave : function() {
|
||||
var profileId = this.ui.profile.val();
|
||||
this.model.set({ profileId : profileId });
|
||||
var pathState = this.model.get("pathState");
|
||||
if (pathState === true) {
|
||||
this.model.set("pathState", "static");
|
||||
} else {
|
||||
this.model.set("pathState", "dynamic");
|
||||
}
|
||||
},
|
||||
|
||||
_onAfterSave : function() {
|
||||
|
||||
@@ -15,7 +15,7 @@ module.exports = Marionette.ItemView.extend({
|
||||
monitored : '.x-monitored',
|
||||
profile : '.x-profiles',
|
||||
minimumAvailability : '.x-minimumavailability',
|
||||
seasonFolder : '.x-season-folder',
|
||||
staticPath : '.x-static-path',
|
||||
rootFolder : '.x-root-folder',
|
||||
selectedCount : '.x-selected-count',
|
||||
container : '.series-editor-footer',
|
||||
@@ -52,7 +52,6 @@ module.exports = Marionette.ItemView.extend({
|
||||
|
||||
this.listenTo(FullMovieCollection, 'save', function() {
|
||||
window.alert(' Done Saving');
|
||||
|
||||
var selected = FullMovieCollection.where({ selected : true });
|
||||
});
|
||||
|
||||
@@ -71,7 +70,7 @@ module.exports = Marionette.ItemView.extend({
|
||||
var monitored = this.ui.monitored.val();
|
||||
var minAvail = this.ui.minimumAvailability.val();
|
||||
var profile = this.ui.profile.val();
|
||||
var seasonFolder = this.ui.seasonFolder.val();
|
||||
var staticPath = this.ui.staticPath.val();
|
||||
var rootFolder = this.ui.rootFolder.val();
|
||||
|
||||
var i = 0;
|
||||
@@ -94,10 +93,8 @@ module.exports = Marionette.ItemView.extend({
|
||||
model.set('profileId', parseInt(profile, 10));
|
||||
}
|
||||
|
||||
if (seasonFolder === 'true') {
|
||||
model.set('seasonFolder', true);
|
||||
} else if (seasonFolder === 'false') {
|
||||
model.set('seasonFolder', false);
|
||||
if (staticPath !== 'noChange') {
|
||||
model.set('pathState', staticPath);
|
||||
}
|
||||
|
||||
if (rootFolder !== 'noChange') {
|
||||
@@ -129,10 +126,8 @@ module.exports = Marionette.ItemView.extend({
|
||||
m.set('profileId', parseInt(profile, 10));
|
||||
}
|
||||
|
||||
if (seasonFolder === 'true') {
|
||||
m.set('seasonFolder', true);
|
||||
} else if (seasonFolder === 'false') {
|
||||
m.set('seasonFolder', false);
|
||||
if (staticPath !== 'noChange') {
|
||||
m.set('pathState', staticPath);
|
||||
}
|
||||
|
||||
if (rootFolder !== 'noChange') {
|
||||
@@ -181,7 +176,7 @@ module.exports = Marionette.ItemView.extend({
|
||||
},
|
||||
|
||||
_organizeFiles : function() {
|
||||
var selected = this.editorGrid.getSelectedModels();
|
||||
var selected = FullMovieCollection.where({ selected : true });
|
||||
var updateFilesMoviesView = new UpdateFilesMoviesView({ movies : selected });
|
||||
this.listenToOnce(updateFilesMoviesView, 'updatingFiles', this._afterSave);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="series-editor-footer">
|
||||
<div class="row">
|
||||
<div class="form-group col-md-2">
|
||||
<div class="form-group col-md-1">
|
||||
<label>Monitored</label>
|
||||
|
||||
<select class="form-control x-action x-monitored">
|
||||
@@ -33,15 +33,15 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{!--<div class="form-group col-md-2">
|
||||
<label>Season Folder</label>
|
||||
<div class="form-group col-md-2">
|
||||
<label>Static Path</label>
|
||||
|
||||
<select class="form-control x-action x-season-folder">
|
||||
<select class="form-control x-action x-static-path">
|
||||
<option value="noChange">No change</option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
<option value="static">Yes</option>
|
||||
<option value="dynamic">No</option>
|
||||
</select>
|
||||
</div>--}}
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<label>Root Folder</label>
|
||||
@@ -55,7 +55,7 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3 actions">
|
||||
<div class="form-group col-md-2 actions">
|
||||
<label class="x-selected-count">0 movies selected</label>
|
||||
<div>
|
||||
<button class="btn btn-primary x-action x-save">Save</button>
|
||||
|
||||
@@ -165,7 +165,7 @@ module.exports = Marionette.Layout.extend({
|
||||
onRender : function() {
|
||||
//this._showToolbar();
|
||||
//this._showTable();
|
||||
//this._showPager();
|
||||
//this._showPager();
|
||||
//if (window.shownOnce){
|
||||
// this.movieCollection.fetch();
|
||||
//}
|
||||
|
||||
@@ -48,6 +48,7 @@ var view = Marionette.ItemView.extend({
|
||||
|
||||
_onAfterSave : function() {
|
||||
this.trigger('saved');
|
||||
vent.trigger(vent.Commands.MovieFileEdited);
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
},
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ module.exports = Marionette.Layout.extend({
|
||||
}
|
||||
});
|
||||
|
||||
vent.on(vent.Commands.CloseModalCommand, this._refreshClose, this);
|
||||
vent.on(vent.Commands.MovieFileEdited, this._showGrid, this);
|
||||
},
|
||||
|
||||
_refresh : function(model) {
|
||||
@@ -105,7 +105,7 @@ module.exports = Marionette.Layout.extend({
|
||||
this.filesCollection = new FilesCollection();
|
||||
var file = this.movie.model.get("movieFile");
|
||||
this.filesCollection.add(file);
|
||||
this.onShow();
|
||||
this._showGrid();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
@@ -117,6 +117,15 @@ module.exports = Marionette.Layout.extend({
|
||||
}));
|
||||
},
|
||||
|
||||
_showGrid : function() {
|
||||
this.regionManager.get('grid').show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.filesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showMainView : function() {
|
||||
this.main.show(this.mainView);
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ var UiSettings = require('../Shared/UiSettingsModel');
|
||||
require('../Mixins/backbone.signalr.mixin');
|
||||
var Config = require('../Config');
|
||||
|
||||
var pageSize = parseInt(Config.getValue("pageSize")) || 1000;
|
||||
var pageSize = parseInt(Config.getValue("pageSize")) || 250;
|
||||
|
||||
var filterModes = {
|
||||
'all' : [
|
||||
@@ -163,7 +163,7 @@ var Collection = PageableCollection.extend({
|
||||
},
|
||||
|
||||
filterModes : filterModes,
|
||||
|
||||
|
||||
sortMappings : {
|
||||
movie : {
|
||||
sortKey : 'series.sortTitle'
|
||||
|
||||
@@ -9,6 +9,7 @@ module.exports = Marionette.ItemView.extend({
|
||||
//var type = this.model.get('seriesType');
|
||||
return {
|
||||
rename : this.naming.get('renameEpisodes'),
|
||||
folderFormat: this.naming.get('movieFolderFormat'),
|
||||
format : this.naming.get('standardMovieFormat')
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{{#if rename}}
|
||||
Folder Naming pattern: {{folderFormat}}<br>
|
||||
Naming pattern: {{format}}
|
||||
{{/if}}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info">
|
||||
<div class="path-info x-path-info">All paths are relative to: <strong>{{path}}</strong></div>
|
||||
<div class="path-info x-path-info">Your movie may be moved; see the paths below</strong></div>
|
||||
<div class="x-format-region"></div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
}
|
||||
|
||||
.delete-files-info {
|
||||
margin-top : 10px;
|
||||
display : none;
|
||||
margin: 10px 0;
|
||||
display: none;
|
||||
float:none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,10 @@
|
||||
<ul class="dropdown-menu">
|
||||
{{> MovieTitleNamingPartial}}
|
||||
{{> ReleaseYearNamingPartial}}
|
||||
{{> QualityNamingPartial}}
|
||||
{{> MediaInfoNamingPartial}}
|
||||
{{> ReleaseGroupNamingPartial}}
|
||||
{{> OriginalTitleNamingPartial}}
|
||||
{{> ImdbIdNamingPartial}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,52 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Automatically Rename Folders</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="autoRenameFolders"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-warning" title="CURRENTLY ALPHA! This feature must be enabled for namings schemes beyond '{Movie Title} {Year}' to work. With it folders are automatically renamed according to your naming scheme on each disk scan. If your folder naming scheme contains things such as quality, etc., the movie folder will be automatically adjusted for that regardless of this setting."/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Movie Paths Default to Static</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="pathsDefaultStatic"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-warning" title="CURRENTLY ALPHA! If enabled, the path of new movies is static and won't change."/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="advanced-setting">
|
||||
|
||||
24
src/UI/Settings/NetImport/DeleteExclusionCell.js
Normal file
24
src/UI/Settings/NetImport/DeleteExclusionCell.js
Normal file
@@ -0,0 +1,24 @@
|
||||
var vent = require('vent');
|
||||
var Backgrid = require('backgrid');
|
||||
|
||||
module.exports = Backgrid.Cell.extend({
|
||||
className : 'delete-episode-file-cell',
|
||||
|
||||
events : {
|
||||
'click' : '_onClick'
|
||||
},
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
this.$el.html('<i class="icon-sonarr-delete" title="Delete exclusion."></i>');
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onClick : function() {
|
||||
var self = this;
|
||||
|
||||
this.model.destroy();
|
||||
|
||||
}
|
||||
});
|
||||
18
src/UI/Settings/NetImport/ExclusionTitleCell.js
Normal file
18
src/UI/Settings/NetImport/ExclusionTitleCell.js
Normal file
@@ -0,0 +1,18 @@
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'exclusion-title-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
var title = this.model.get("movieTitle");
|
||||
var year = this.model.get("movieYear");
|
||||
var str = title;
|
||||
if (year > 1800) {
|
||||
str += " ("+year+")";
|
||||
}
|
||||
this.$el.html(str);
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
7
src/UI/Settings/NetImport/ImportExclusionModel.js
Normal file
7
src/UI/Settings/NetImport/ImportExclusionModel.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/exclusions',
|
||||
|
||||
});
|
||||
9
src/UI/Settings/NetImport/ImportExclusionsCollection.js
Normal file
9
src/UI/Settings/NetImport/ImportExclusionsCollection.js
Normal file
@@ -0,0 +1,9 @@
|
||||
var Backbone = require('backbone');
|
||||
var NetImportModel = require('./ImportExclusionModel');
|
||||
|
||||
var ImportExclusionsCollection = Backbone.Collection.extend({
|
||||
model : NetImportModel,
|
||||
url : window.NzbDrone.ApiRoot + '/exclusions',
|
||||
});
|
||||
|
||||
module.exports = new ImportExclusionsCollection();
|
||||
@@ -3,6 +3,14 @@ var NetImportCollection = require('./NetImportCollection');
|
||||
var CollectionView = require('./NetImportCollectionView');
|
||||
var OptionsView = require('./Options/NetImportOptionsView');
|
||||
var RootFolderCollection = require('../../AddMovies/RootFolders/RootFolderCollection');
|
||||
var ImportExclusionsCollection = require('./ImportExclusionsCollection');
|
||||
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||
var DeleteExclusionCell = require('./DeleteExclusionCell');
|
||||
var ExclusionTitleCell = require("./ExclusionTitleCell");
|
||||
var _ = require('underscore');
|
||||
var vent = require('vent');
|
||||
var Backgrid = require('backgrid');
|
||||
var $ = require('jquery');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Settings/NetImport/NetImportLayoutTemplate',
|
||||
@@ -10,18 +18,58 @@ module.exports = Marionette.Layout.extend({
|
||||
regions : {
|
||||
lists : '#x-lists-region',
|
||||
listOption : '#x-list-options-region',
|
||||
importExclusions : "#exclusions"
|
||||
},
|
||||
|
||||
columns: [{
|
||||
name: '',
|
||||
cell: SelectAllCell,
|
||||
headerCell: 'select-all',
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'tmdbId',
|
||||
label: 'TMDBID',
|
||||
cell: Backgrid.StringCell,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'movieTitle',
|
||||
label: 'Title',
|
||||
cell: ExclusionTitleCell,
|
||||
cellValue: 'this',
|
||||
}, {
|
||||
name: 'this',
|
||||
label: '',
|
||||
cell: DeleteExclusionCell,
|
||||
sortable: false,
|
||||
}],
|
||||
|
||||
|
||||
initialize : function() {
|
||||
this.indexersCollection = new NetImportCollection();
|
||||
this.indexersCollection.fetch();
|
||||
RootFolderCollection.fetch().done(function() {
|
||||
RootFolderCollection.synced = true;
|
||||
});
|
||||
ImportExclusionsCollection.fetch().done(function() {
|
||||
ImportExclusionsCollection.synced = true;
|
||||
});
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.listenTo(ImportExclusionsCollection, "sync", this._showExclusions);
|
||||
if (ImportExclusionsCollection.synced === true) {
|
||||
this._showExclusions();
|
||||
}
|
||||
this.lists.show(new CollectionView({ collection : this.indexersCollection }));
|
||||
this.listOption.show(new OptionsView({ model : this.model }));
|
||||
},
|
||||
|
||||
_showExclusions : function() {
|
||||
this.exclusionGrid = new Backgrid.Grid({
|
||||
collection: ImportExclusionsCollection,
|
||||
columns: this.columns,
|
||||
className: 'table table-hover'
|
||||
});
|
||||
this.importExclusions.show(this.exclusionGrid);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<div id="x-lists-region"></div>
|
||||
<div class="form-horizontal">
|
||||
<div id="x-list-options-region"></div>
|
||||
<fieldset>
|
||||
<legend>Import Exclusions</legend>
|
||||
<div id="exclusions">
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
var Marionette = require('marionette');
|
||||
var AsModelBoundView = require('../../../Mixins/AsModelBoundView');
|
||||
var AsValidatedView = require('../../../Mixins/AsValidatedView');
|
||||
var ImportExclusionsCollection = require('./../ImportExclusionsCollection');
|
||||
var SelectAllCell = require('../../../Cells/SelectAllCell');
|
||||
var _ = require('underscore');
|
||||
var vent = require('vent');
|
||||
var Backgrid = require('backgrid');
|
||||
var $ = require('jquery');
|
||||
require('../../../Mixins/TagInput');
|
||||
require('bootstrap');
|
||||
@@ -22,10 +27,10 @@ var view = Marionette.ItemView.extend({
|
||||
'click .x-revoke-trakt-tokens' : '_revokeTraktTokens'
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
initialize : function() {
|
||||
|
||||
},
|
||||
|
||||
|
||||
onShow : function() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var oauth = params.get('access');
|
||||
@@ -39,78 +44,25 @@ var view = Marionette.ItemView.extend({
|
||||
//Config.setValue("traktRefreshToken", refresh);
|
||||
var tokenExpiry = Math.floor(Date.now() / 1000) + 4838400;
|
||||
this.ui.tokenExpiry.val(tokenExpiry).trigger('change'); // this means the token will expire in 8 weeks (4838400 seconds)
|
||||
//Config.setValue("traktTokenExpiry",tokenExpiry);
|
||||
//Config.setValue("traktTokenExpiry",tokenExpiry);
|
||||
//this.model.isSaved = false;
|
||||
//window.alert("Trakt Authentication Complete - Click Save to make the change take effect");
|
||||
}
|
||||
if (this.ui.authToken.val() && this.ui.refreshToken.val()){
|
||||
this.ui.resetTokensButton.hide();
|
||||
this.ui.revokeTokensButton.show();
|
||||
this.ui.resetTokensButton.hide();
|
||||
this.ui.revokeTokensButton.show();
|
||||
} else {
|
||||
this.ui.resetTokensButton.show();
|
||||
this.ui.revokeTokensButton.hide();
|
||||
this.ui.resetTokensButton.show();
|
||||
this.ui.revokeTokensButton.hide();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
this.ui.importExclusions.tagsinput({
|
||||
trimValue : true,
|
||||
tagClass : 'label label-danger',
|
||||
/*itemText : function(item) {
|
||||
var uri;
|
||||
var text;
|
||||
if (item.startsWith('tt')) {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/imdb?imdbId='+item;
|
||||
}
|
||||
else {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/tmdb?tmdbId='+item;
|
||||
}
|
||||
var promise = $.ajax({
|
||||
url : uri,
|
||||
type : 'GET',
|
||||
async : false,
|
||||
});
|
||||
promise.success(function(response) {
|
||||
text=response['title']+' ('+response['year']+')';
|
||||
});
|
||||
|
||||
promise.error(function(request, status, error) {
|
||||
text=item;
|
||||
});
|
||||
return text;
|
||||
}*/
|
||||
});
|
||||
this.ui.importExclusions.on('beforeItemAdd', function(event) {
|
||||
var uri;
|
||||
if (event.item.startsWith('tt')) {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/imdb?imdbId='+event.item;
|
||||
}
|
||||
else {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/tmdb?tmdbId='+event.item;
|
||||
}
|
||||
var promise = $.ajax({
|
||||
url : uri,
|
||||
type : 'GET',
|
||||
async : false,
|
||||
});
|
||||
promise.success(function(response) {
|
||||
event.cancel=false;
|
||||
|
||||
//var newText = response['tmdbId']+'-';
|
||||
//if (event.item.startsWith('tt')) {
|
||||
// newText = newText+'['+event.item+']';
|
||||
//}
|
||||
event.item = response.titleSlug;//+' ('+response['year']+')-'+response['tmdbId'];
|
||||
});
|
||||
|
||||
promise.error(function(request, status, error) {
|
||||
event.cancel = true;
|
||||
window.alert(event.item+' is not a valid! Must be valid tt#### IMDB ID or #### TMDB ID');
|
||||
});
|
||||
return event;
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
ui : {
|
||||
resetTraktTokens : '.x-reset-trakt-tokens',
|
||||
@@ -118,7 +70,7 @@ var view = Marionette.ItemView.extend({
|
||||
refreshToken : '.x-trakt-refresh-token',
|
||||
resetTokensButton : '.x-reset-trakt-tokens',
|
||||
revokeTokensButton : '.x-revoke-trakt-tokens',
|
||||
tokenExpiry : '.x-trakt-token-expiry',
|
||||
tokenExpiry : '.x-trakt-token-expiry',
|
||||
importExclusions : '.x-import-exclusions'
|
||||
},
|
||||
|
||||
@@ -131,15 +83,16 @@ var view = Marionette.ItemView.extend({
|
||||
|
||||
_revokeTraktTokens : function() {
|
||||
if (window.confirm("Log out of trakt.tv?")){
|
||||
//TODO: need to implement this: http://docs.trakt.apiary.io/#reference/authentication-oauth/revoke-token/revoke-an-access_token
|
||||
//TODO: need to implement this: http://docs.trakt.apiary.io/#reference/authentication-oauth/revoke-token/revoke-an-access_token
|
||||
this.ui.authToken.val('').trigger('change');
|
||||
this.ui.refreshToken.val('').trigger('change');
|
||||
this.ui.tokenExpiry.val(0).trigger('change');
|
||||
this.ui.refreshToken.val('').trigger('change');
|
||||
this.ui.tokenExpiry.val(0).trigger('change');
|
||||
this.ui.resetTokensButton.show();
|
||||
this.ui.revokeTokensButton.hide();
|
||||
window.alert("Logged out of Trakt.tv - Click Save to make the change take effect");
|
||||
this.ui.revokeTokensButton.hide();
|
||||
window.alert("Logged out of Trakt.tv - Click Save to make the change take effect");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -30,17 +30,17 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<!--<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Import Exclusions</label>
|
||||
<div class="col-sm-1 col-sm-push-4 help-inline">
|
||||
<i class="icon-sonarr-form-warning" title="Movies in this field will not be imported even if they exist on your lists."/>
|
||||
<i class="icon-sonarr-form-info" title="Comma separated imdbid or tmdbid: tt0120915,216138,tt0121765"/>
|
||||
</div>
|
||||
<div class="col-sm-4 col-sm-pull-1">
|
||||
|
||||
|
||||
<input type="text" name="importExclusions" class="form-control x-import-exclusions"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
<legend>Trakt Authentication</legend>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-1 control-label">Auth Token</label>
|
||||
@@ -58,5 +58,6 @@
|
||||
<button class="btn btn-danger btn-icon-only x-reset-trakt-tokens" title="Reset Trakt Tokens"><i class="icon-sonarr-refresh"></i></button>
|
||||
<button class="btn btn-danger btn-icon-only x-revoke-trakt-tokens" title="Revoke Trakt Tokens"><i class="icon-sonarr-logout"></i></button>
|
||||
</div >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
@@ -14,6 +14,7 @@ var IndexerCollection = require('./Indexers/IndexerCollection');
|
||||
var IndexerSettingsModel = require('./Indexers/IndexerSettingsModel');
|
||||
var NetImportSettingsModel = require("./NetImport/NetImportSettingsModel");
|
||||
var NetImportCollection = require('./NetImport/NetImportCollection');
|
||||
var ImportExclusionsCollection = require('./NetImport/ImportExclusionsCollection');
|
||||
var NetImportLayout = require('./NetImport/NetImportLayout');
|
||||
var DownloadClientLayout = require('./DownloadClient/DownloadClientLayout');
|
||||
var DownloadClientSettingsModel = require('./DownloadClient/DownloadClientSettingsModel');
|
||||
|
||||
@@ -10,7 +10,7 @@ module.exports = SettingsModelBase.extend({
|
||||
origInit : SettingsModelBase.prototype.initialize,
|
||||
|
||||
initialize : function() {
|
||||
this.set("pageSize", Config.getValue("pageSize", 1000));
|
||||
this.set("pageSize", Config.getValue("pageSize", 250));
|
||||
this.origInit.call(this);
|
||||
},
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ module.exports = Marionette.Layout.extend({
|
||||
{
|
||||
title : 'Rescan Drone Factory Folder',
|
||||
icon : 'icon-sonarr-refresh',
|
||||
command : 'downloadedMovieScan',
|
||||
command : 'downloadedMoviesScan',
|
||||
properties : { sendUpdates : true }
|
||||
},
|
||||
{
|
||||
|
||||
@@ -32,7 +32,8 @@ vent.Commands = {
|
||||
CloseFileBrowser : 'closeFileBrowser',
|
||||
OpenControlPanelCommand : 'OpenControlPanelCommand',
|
||||
CloseControlPanelCommand : 'CloseControlPanelCommand',
|
||||
ShowExistingCommand : 'ShowExistingCommand'
|
||||
ShowExistingCommand : 'ShowExistingCommand',
|
||||
MovieFileEdited : 'MovieFileEdited'
|
||||
};
|
||||
|
||||
vent.Hotkeys = {
|
||||
|
||||
Reference in New Issue
Block a user