mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-19 16:34:34 -04:00
Compare commits
72 Commits
v0.2.0.596
...
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 | ||
|
|
8ca66fb61a | ||
|
|
064844ac0c | ||
|
|
5540594ecf | ||
|
|
433ae019de | ||
|
|
10091b9454 | ||
|
|
475851775f | ||
|
|
0fff862fd2 | ||
|
|
23754c49dc | ||
|
|
fbf790e9fd | ||
|
|
6d00bd0f7a | ||
|
|
5bf95e0d9e | ||
|
|
8bb4b02be7 | ||
|
|
b26a036eed | ||
|
|
ef57882291 | ||
|
|
32a2407ad1 | ||
|
|
33b48eec95 | ||
|
|
3790dc9109 | ||
|
|
343d849536 | ||
|
|
c36b259fa9 | ||
|
|
6463913f22 | ||
|
|
e39deb4bdc | ||
|
|
9ca2c21547 | ||
|
|
f376360611 | ||
|
|
3e966d4d58 | ||
|
|
e036267c33 | ||
|
|
7d4378ca7a | ||
|
|
ee1ebfd893 | ||
|
|
403fd0f0c0 | ||
|
|
fc5ac8219f | ||
|
|
600a433faa | ||
|
|
5609facd9d | ||
|
|
36ea6c6b99 | ||
|
|
56ac87c760 | ||
|
|
47753c47a5 | ||
|
|
712c0eb84a | ||
|
|
8765155223 | ||
|
|
63d7596e98 |
@@ -37,6 +37,11 @@ cache:
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
on_failure:
|
||||
- ps: Get-ChildItem .\_artifacts\*.zip | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- ps: Get-ChildItem .\_artifacts\*.exe | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- ps: Get-ChildItem .\_artifacts\*.tar.gz | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
|
||||
only_commits:
|
||||
files:
|
||||
- src/
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
6
build.sh
6
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']"
|
||||
}
|
||||
@@ -208,9 +208,9 @@ PackageTests()
|
||||
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||
$nuget install NUnit.Runners -Version 3.2.1 -Output $testPackageFolder
|
||||
else
|
||||
mono $nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||
mono $nuget install NUnit.Runners -Version 3.2.1 -Output $testPackageFolder
|
||||
fi
|
||||
|
||||
cp $outputFolder/*.dll $testPackageFolder
|
||||
|
||||
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>
|
||||
|
||||
67
package.sh
67
package.sh
@@ -23,20 +23,19 @@ if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||
DAY="`date +%d`"
|
||||
else
|
||||
VERSION=$1
|
||||
BRANCH=$2
|
||||
BRANCH=${BRANCH#refs\/heads\/}
|
||||
BRANCH=${BRANCH//\//-}
|
||||
fi
|
||||
outputFolder='./_output'
|
||||
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
|
||||
|
||||
cp -r $outputFolder/ Radarr_Windows_$VERSION
|
||||
cp -r $outputFolderMono/ Radarr_Mono_$VERSION
|
||||
cp -r $outputFolderOsxApp/ Radarr_OSX_$VERSION
|
||||
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/*
|
||||
@@ -44,25 +43,35 @@ if [ $runtime = "dotnet" ] ; then
|
||||
./7za.exe a -ttar -so Radarr_OSX_$VERSION.tar ./_output_osx/* | ./7za.exe a -si Radarr_OSX_$VERSION.tar.gz
|
||||
./7za.exe a -ttar -so Radarr_OSX_App_$VERSION.tar ./_output_osx_app/* | ./7za.exe a -si Radarr_OSX_App_$VERSION.tar.gz
|
||||
else
|
||||
zip -r Radarr_Windows_$VERSION.zip Radarr_Windows_$VERSION/* >& /dev/null
|
||||
zip -r Radarr_Mono_$VERSION.zip Radarr_Mono_$VERSION/* >& /dev/null #TODO update for tar.gz
|
||||
zip -r Radarr_OSX_$VERSION_App.zip Radarr_OSX_$VERSION/* >& /dev/null
|
||||
cp -r $outputFolder/ Radarr
|
||||
zip -r Radarr.$BRANCH.$VERSION.windows.zip Radarr
|
||||
rm -rf Radarr
|
||||
cp -r $outputFolderMono/ Radarr
|
||||
tar -zcvf Radarr.$BRANCH.$VERSION.linux.tar.gz Radarr
|
||||
rm -rf Radarr
|
||||
cp -r $outputFolderOsx/ Radarr
|
||||
tar -zcvf Radarr.$BRANCH.$VERSION.osx.tar.gz Radarr
|
||||
rm -rf Radarr
|
||||
#TODO update for tar.gz
|
||||
|
||||
cd _output_osx_app/
|
||||
zip -r ../Radarr.$BRANCH.$VERSION.osx-app.zip *
|
||||
fi
|
||||
ftp -n ftp.leonardogalli.ch << END_SCRIPT
|
||||
passive
|
||||
quote USER $FTP_USER
|
||||
quote PASS $FTP_PASS
|
||||
mkdir builds
|
||||
cd builds
|
||||
mkdir $YEAR
|
||||
cd $YEAR
|
||||
mkdir $MONTH
|
||||
cd $MONTH
|
||||
mkdir $DAY
|
||||
cd $DAY
|
||||
binary
|
||||
put Radarr_Windows_$VERSION.zip
|
||||
put Radarr_Mono_$VERSION.zip
|
||||
put Radarr_OSX_$VERSION.zip
|
||||
quit
|
||||
END_SCRIPT
|
||||
# ftp -n ftp.leonardogalli.ch << END_SCRIPT
|
||||
# passive
|
||||
# quote USER $FTP_USER
|
||||
# quote PASS $FTP_PASS
|
||||
# mkdir builds
|
||||
# cd builds
|
||||
# mkdir $YEAR
|
||||
# cd $YEAR
|
||||
# mkdir $MONTH
|
||||
# cd $MONTH
|
||||
# mkdir $DAY
|
||||
# cd $DAY
|
||||
# binary
|
||||
# put Radarr_Windows_$VERSION.zip
|
||||
# put Radarr_Mono_$VERSION.zip
|
||||
# put Radarr_OSX_$VERSION.zip
|
||||
# quit
|
||||
# END_SCRIPT
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -8,9 +8,10 @@ namespace NzbDrone.Api.Config
|
||||
public int MinimumAge { get; set; }
|
||||
public int Retention { get; set; }
|
||||
public int RssSyncInterval { get; set; }
|
||||
public int AvailabilityDelay { get; set; }
|
||||
public bool AllowHardcodedSubs { get; set; }
|
||||
public string WhitelistedHardcodedSubs { get; set; }
|
||||
public bool PreferIndexerFlags { get; set; }
|
||||
public int AvailabilityDelay { get; set; }
|
||||
public bool AllowHardcodedSubs { get; set; }
|
||||
public string WhitelistedHardcodedSubs { get; set; }
|
||||
}
|
||||
|
||||
public static class IndexerConfigResourceMapper
|
||||
@@ -22,9 +23,10 @@ namespace NzbDrone.Api.Config
|
||||
MinimumAge = model.MinimumAge,
|
||||
Retention = model.Retention,
|
||||
RssSyncInterval = model.RssSyncInterval,
|
||||
AvailabilityDelay = model.AvailabilityDelay,
|
||||
AllowHardcodedSubs = model.AllowHardcodedSubs,
|
||||
WhitelistedHardcodedSubs = model.WhitelistedHardcodedSubs,
|
||||
PreferIndexerFlags = model.PreferIndexerFlags,
|
||||
AvailabilityDelay = model.AvailabilityDelay,
|
||||
AllowHardcodedSubs = model.AllowHardcodedSubs,
|
||||
WhitelistedHardcodedSubs = model.WhitelistedHardcodedSubs,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,6 +46,7 @@ namespace NzbDrone.Api.Indexers
|
||||
public bool DownloadAllowed { get; set; }
|
||||
public int ReleaseWeight { get; set; }
|
||||
|
||||
public IEnumerable<string> IndexerFlags { get; set; }
|
||||
|
||||
public string MagnetUrl { get; set; }
|
||||
public string InfoHash { get; set; }
|
||||
@@ -132,7 +133,7 @@ namespace NzbDrone.Api.Indexers
|
||||
Seeders = torrentInfo.Seeders,
|
||||
Leechers = (torrentInfo.Peers.HasValue && torrentInfo.Seeders.HasValue) ? (torrentInfo.Peers.Value - torrentInfo.Seeders.Value) : (int?)null,
|
||||
Protocol = releaseInfo.DownloadProtocol,
|
||||
|
||||
IndexerFlags = torrentInfo.IndexerFlags.ToString().Split(new string[] { ", " }, StringSplitOptions.None),
|
||||
Edition = parsedMovieInfo.Edition,
|
||||
|
||||
IsDaily = false,
|
||||
|
||||
@@ -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)
|
||||
@@ -213,7 +236,7 @@ namespace NzbDrone.Api.Movie
|
||||
|
||||
private void LinkMovieStatistics(MovieResource resource, MovieStatistics moviesStatistics)
|
||||
{
|
||||
resource.SizeOnDisk = moviesStatistics.SizeOnDisk;
|
||||
//resource.SizeOnDisk = 0;//TODO: incorporate movie statistics moviesStatistics.SizeOnDisk;
|
||||
}
|
||||
|
||||
private void PopulateAlternateTitles(List<MovieResource> resources)
|
||||
|
||||
@@ -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; }
|
||||
@@ -120,7 +121,7 @@ namespace NzbDrone.Api.Movie
|
||||
//TotalEpisodeCount
|
||||
//EpisodeCount
|
||||
//EpisodeFileCount
|
||||
//SizeOnDisk
|
||||
SizeOnDisk = size,
|
||||
Status = model.Status,
|
||||
Overview = model.Overview,
|
||||
//NextAiring
|
||||
@@ -131,6 +132,7 @@ namespace NzbDrone.Api.Movie
|
||||
|
||||
Path = model.Path,
|
||||
ProfileId = model.ProfileId,
|
||||
PathState = model.PathState,
|
||||
|
||||
Monitored = model.Monitored,
|
||||
MinimumAvailability = model.MinimumAvailability,
|
||||
@@ -138,7 +140,7 @@ namespace NzbDrone.Api.Movie
|
||||
IsAvailable = model.IsAvailable(),
|
||||
FolderName = model.FolderName(),
|
||||
|
||||
SizeOnDisk = size,
|
||||
//SizeOnDisk = size,
|
||||
|
||||
Runtime = model.Runtime,
|
||||
LastInfoSync = model.LastInfoSync,
|
||||
@@ -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;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
public void should_be_able_to_check_space_on_ramdrive()
|
||||
{
|
||||
MonoOnly();
|
||||
Subject.GetAvailableSpace("/run/").Should().NotBe(0);
|
||||
Subject.GetAvailableSpace("/").Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace NzbDrone.Common.Test
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
[TestCase("http://")]
|
||||
public void DownloadString_should_throw_on_error(string url)
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Subject.DownloadString(url));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
private RemoteEpisode parseResultMulti;
|
||||
private RemoteEpisode parseResultSingle;
|
||||
private Series series;
|
||||
private Movie movie;
|
||||
private RemoteMovie remoteMovie;
|
||||
private QualityDefinition qualityType;
|
||||
|
||||
[SetUp]
|
||||
@@ -28,6 +30,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
series = Builder<Series>.CreateNew()
|
||||
.Build();
|
||||
|
||||
movie = Builder<Movie>.CreateNew().Build();
|
||||
|
||||
remoteMovie = new RemoteMovie
|
||||
{
|
||||
Movie = movie,
|
||||
Release = new ReleaseInfo(),
|
||||
ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
|
||||
|
||||
};
|
||||
|
||||
parseResultMultiSet = new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
@@ -216,5 +228,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_110_minutes_if_runtime_is_0()
|
||||
{
|
||||
movie.Runtime = 0;
|
||||
remoteMovie.Movie = movie;
|
||||
remoteMovie.Release.Size = 1095.Megabytes();
|
||||
|
||||
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().Be(true);
|
||||
remoteMovie.Release.Size = 1105.Megabytes();
|
||||
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().Be(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
69
src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs
Normal file
69
src/NzbDrone.Core.Test/IndexerTests/PTPTests/PTPFixture.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.PassThePopcorn;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.PTPTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PTPFixture : CoreTest<PassThePopcorn>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
{
|
||||
Name = "PTP",
|
||||
Settings = new PassThePopcornSettings() { Passkey = "fakekey", Username = "asdf", Password = "sad" }
|
||||
};
|
||||
}
|
||||
|
||||
[TestCase("Files/Indexers/PTP/imdbsearch.json")]
|
||||
public void should_parse_feed_from_PTP(string fileName)
|
||||
{
|
||||
var authResponse = new PassThePopcornAuthResponse { Result = "Ok" };
|
||||
|
||||
System.IO.StringWriter authStream = new System.IO.StringWriter();
|
||||
Json.Serialize(authResponse, authStream);
|
||||
var responseJson = ReadAllText(fileName);
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.POST)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r,new HttpHeader(), authStream.ToString()));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader {ContentType = HttpAccept.Json.Value}, responseJson));
|
||||
|
||||
var torrents = Subject.FetchRecent();
|
||||
|
||||
torrents.Should().HaveCount(293);
|
||||
torrents.First().Should().BeOfType<PassThePopcornInfo>();
|
||||
|
||||
var first = torrents.First() as TorrentInfo;
|
||||
|
||||
first.Guid.Should().Be("PassThePopcorn-483521");
|
||||
first.Title.Should().Be("The.Night.Of.S01.720p.HDTV.x264-BTN");
|
||||
first.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=483521&authkey=00000000000000000000000000000000&torrent_pass=00000000000000000000000000000000");
|
||||
first.InfoUrl.Should().Be("https://passthepopcorn.me/torrents.php?id=148131&torrentid=483521");
|
||||
//first.PublishDate.Should().Be(DateTime.Parse("2017-04-17T12:13:42+0000").ToUniversalTime()); stupid timezones
|
||||
first.Size.Should().Be(9370933376);
|
||||
first.InfoHash.Should().BeNullOrEmpty();
|
||||
first.MagnetUrl.Should().BeNullOrEmpty();
|
||||
first.Peers.Should().Be(3);
|
||||
first.Seeders.Should().Be(1);
|
||||
|
||||
torrents.Any(t => t.IndexerFlags.HasFlag(IndexerFlags.G_Freeleech)).Should().Be(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
@@ -389,6 +390,10 @@
|
||||
<Content Include="Files\ArabicRomanNumeralDictionary.JSON">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Compile Include="IndexerTests\PTPTests\PTPFixture.cs" />
|
||||
<None Include="Files\Indexers\PTP\imdbsearch.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj">
|
||||
@@ -574,6 +579,8 @@
|
||||
<Folder Include="DataAugmentation\SceneNumbering\" />
|
||||
<Folder Include="Providers\" />
|
||||
<Folder Include="ProviderTests\UpdateProviderTests\" />
|
||||
<Folder Include="IndexerTests\PTPTests\" />
|
||||
<Folder Include="Files\Indexers\PTP\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@@ -74,6 +74,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("To.Live.and.Die.in.L.A.1985.1080p.BluRay", "To Live and Die in L.A.")]
|
||||
[TestCase("A.I.Artificial.Intelligence.(2001)", "A.I. Artificial Intelligence")]
|
||||
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
|
||||
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
|
||||
[TestCase("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);
|
||||
@@ -86,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
private ParsedMovieInfo _wrongYearInfo;
|
||||
private ParsedMovieInfo _romanTitleInfo;
|
||||
private ParsedMovieInfo _alternativeTitleInfo;
|
||||
private ParsedMovieInfo _umlautInfo;
|
||||
private ParsedMovieInfo _umlautAltInfo;
|
||||
private MovieSearchCriteria _movieSearchCriteria;
|
||||
private List<Episode> _episodes;
|
||||
private ParsedEpisodeInfo _parsedEpisodeInfo;
|
||||
@@ -37,10 +39,10 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
.Build();
|
||||
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(m => m.Title = "Mission Impossible 3")
|
||||
.With(m => m.CleanTitle = "missionimpossible3")
|
||||
.With(m => m.Year = 2006)
|
||||
.With(m => m.AlternativeTitles = new List<string> { "Mission Impossible 3: Same same" })
|
||||
.With(m => m.Title = "Fack Ju Göthe 2")
|
||||
.With(m => m.CleanTitle = "fackjugoethe2")
|
||||
.With(m => m.Year = 2015)
|
||||
.With(m => m.AlternativeTitles = new List<string> { "Fack Ju Göthe 2: Same same" })
|
||||
.Build();
|
||||
|
||||
_episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
@@ -77,10 +79,22 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
|
||||
_romanTitleInfo = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = "Mission Impossible III",
|
||||
MovieTitle = "Fack Ju Göthe II",
|
||||
Year = _movie.Year,
|
||||
};
|
||||
|
||||
_umlautInfo = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = "Fack Ju Goethe 2",
|
||||
Year = _movie.Year
|
||||
};
|
||||
|
||||
_umlautAltInfo = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = "Fack Ju Goethe 2: Same same",
|
||||
Year = _movie.Year
|
||||
};
|
||||
|
||||
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
|
||||
{
|
||||
Series = _series,
|
||||
@@ -115,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]
|
||||
@@ -148,5 +162,12 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
Subject.Map(_romanTitleInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_match_umlauts()
|
||||
{
|
||||
Subject.Map(_umlautInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie);
|
||||
Subject.Map(_umlautAltInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,11 +105,11 @@ namespace NzbDrone.Core.Configuration
|
||||
set { SetValue("RssSyncInterval", value); }
|
||||
}
|
||||
|
||||
public int AvailabilityDelay
|
||||
{
|
||||
get { return GetValueInt("AvailabilityDelay",0); }
|
||||
set { SetValue("AvailabilityDelay", value); }
|
||||
}
|
||||
public int AvailabilityDelay
|
||||
{
|
||||
get { return GetValueInt("AvailabilityDelay",0); }
|
||||
set { SetValue("AvailabilityDelay", value); }
|
||||
}
|
||||
|
||||
public int NetImportSyncInterval
|
||||
{
|
||||
@@ -190,19 +190,26 @@ namespace NzbDrone.Core.Configuration
|
||||
set { SetValue("EnableCompletedDownloadHandling", value); }
|
||||
}
|
||||
|
||||
public bool AllowHardcodedSubs
|
||||
{
|
||||
get { return GetValueBoolean("AllowHardcodedSubs", false); }
|
||||
public bool PreferIndexerFlags
|
||||
{
|
||||
get { return GetValueBoolean("PreferIndexerFlags", false); }
|
||||
|
||||
set { SetValue("AllowHardcodedSubs", value); }
|
||||
}
|
||||
set {SetValue("PreferIndexerFlags", value);}
|
||||
}
|
||||
|
||||
public string WhitelistedHardcodedSubs
|
||||
{
|
||||
get { return GetValue("WhitelistedHardcodedSubs", ""); }
|
||||
public bool AllowHardcodedSubs
|
||||
{
|
||||
get { return GetValueBoolean("AllowHardcodedSubs", false); }
|
||||
|
||||
set { SetValue("WhitelistedHardcodedSubs", value); }
|
||||
}
|
||||
set { SetValue("AllowHardcodedSubs", value); }
|
||||
}
|
||||
|
||||
public string WhitelistedHardcodedSubs
|
||||
{
|
||||
get { return GetValue("WhitelistedHardcodedSubs", ""); }
|
||||
|
||||
set { SetValue("WhitelistedHardcodedSubs", value); }
|
||||
}
|
||||
|
||||
public bool RemoveCompletedDownloads
|
||||
{
|
||||
@@ -287,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; }
|
||||
@@ -46,20 +48,22 @@ namespace NzbDrone.Core.Configuration
|
||||
int RssSyncInterval { get; set; }
|
||||
int MinimumAge { get; set; }
|
||||
|
||||
int AvailabilityDelay { get; set; }
|
||||
bool PreferIndexerFlags { get; set; }
|
||||
|
||||
bool AllowHardcodedSubs { get; set; }
|
||||
string WhitelistedHardcodedSubs { get; set; }
|
||||
int AvailabilityDelay { get; set; }
|
||||
|
||||
bool AllowHardcodedSubs { get; set; }
|
||||
string WhitelistedHardcodedSubs { get; set; }
|
||||
|
||||
int NetImportSyncInterval { get; set; }
|
||||
string ListSyncLevel { get; set; }
|
||||
string ImportExclusions { get; set; }
|
||||
string ListSyncLevel { get; set; }
|
||||
string ImportExclusions { get; set; }
|
||||
string TraktAuthToken { get; set; }
|
||||
string TraktRefreshToken { get; set; }
|
||||
int TraktTokenExpiry { get; set; }
|
||||
string NewTraktAuthToken { get; set; }
|
||||
string NewTraktRefreshToken {get; set; }
|
||||
int NewTraktTokenExpiry { get; set; }
|
||||
string NewTraktAuthToken { get; set; }
|
||||
string NewTraktRefreshToken {get; set; }
|
||||
int NewTraktTokenExpiry { get; set; }
|
||||
|
||||
//UI
|
||||
int FirstDayOfWeek { 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")
|
||||
|
||||
@@ -4,18 +4,21 @@ using System.Linq;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles.Delay;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public class DownloadDecisionComparer : IComparer<DownloadDecision>
|
||||
{
|
||||
private readonly IDelayProfileService _delayProfileService;
|
||||
private readonly IConfigService _configService;
|
||||
public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y);
|
||||
public delegate int CompareDelegate<TSubject, TValue>(DownloadDecision x, DownloadDecision y);
|
||||
|
||||
public DownloadDecisionComparer(IDelayProfileService delayProfileService)
|
||||
public DownloadDecisionComparer(IDelayProfileService delayProfileService, IConfigService configService)
|
||||
{
|
||||
_delayProfileService = delayProfileService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
public int Compare(DownloadDecision x, DownloadDecision y)
|
||||
@@ -24,6 +27,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
CompareQuality,
|
||||
ComparePreferredWords,
|
||||
CompareIndexerFlags,
|
||||
CompareProtocol,
|
||||
ComparePeersIfTorrent,
|
||||
CompareAgeIfUsenet,
|
||||
@@ -84,7 +88,22 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
return num;
|
||||
|
||||
});
|
||||
; }
|
||||
}
|
||||
|
||||
private int CompareIndexerFlags(DownloadDecision x, DownloadDecision y)
|
||||
{
|
||||
var releaseX = x.RemoteMovie.Release;
|
||||
var releaseY = y.RemoteMovie.Release;
|
||||
|
||||
if (_configService.PreferIndexerFlags)
|
||||
{
|
||||
return CompareBy(x.RemoteMovie.Release, y.RemoteMovie.Release, release => ScoreFlags(release.IndexerFlags));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int CompareProtocol(DownloadDecision x, DownloadDecision y)
|
||||
{
|
||||
@@ -185,5 +204,34 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
|
||||
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
|
||||
}
|
||||
|
||||
private int ScoreFlags(IndexerFlags flags)
|
||||
{
|
||||
var flagValues = Enum.GetValues(typeof(IndexerFlags));
|
||||
|
||||
var score = 0;
|
||||
|
||||
foreach (IndexerFlags value in flagValues)
|
||||
{
|
||||
if ((flags & value) == value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case IndexerFlags.G_DoubleUpload:
|
||||
case IndexerFlags.G_Freeleech:
|
||||
case IndexerFlags.PTP_Approved:
|
||||
case IndexerFlags.PTP_Golden:
|
||||
case IndexerFlags.HDB_Internal:
|
||||
score += 2;
|
||||
break;
|
||||
case IndexerFlags.G_Halfleech:
|
||||
score += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Profiles.Delay;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
@@ -13,10 +14,12 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
public class DownloadDecisionPriorizationService : IPrioritizeDownloadDecision
|
||||
{
|
||||
private readonly IDelayProfileService _delayProfileService;
|
||||
private readonly IConfigService _configService;
|
||||
|
||||
public DownloadDecisionPriorizationService(IDelayProfileService delayProfileService)
|
||||
public DownloadDecisionPriorizationService(IDelayProfileService delayProfileService, IConfigService configService)
|
||||
{
|
||||
_delayProfileService = delayProfileService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
public List<DownloadDecision> PrioritizeDecisions(List<DownloadDecision> decisions)
|
||||
@@ -24,7 +27,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
return decisions.Where(c => c.RemoteEpisode.Series != null)
|
||||
.GroupBy(c => c.RemoteEpisode.Series.Id, (seriesId, downloadDecisions) =>
|
||||
{
|
||||
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService));
|
||||
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService, _configService));
|
||||
})
|
||||
.SelectMany(c => c)
|
||||
.Union(decisions.Where(c => c.RemoteEpisode.Series == null))
|
||||
@@ -36,7 +39,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
return decisions.Where(c => c.RemoteMovie.Movie != null)
|
||||
.GroupBy(c => c.RemoteMovie.Movie.Id, (movieId, downloadDecisions) =>
|
||||
{
|
||||
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService));
|
||||
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService, _configService));
|
||||
})
|
||||
.SelectMany(c => c)
|
||||
.Union(decisions.Where(c => c.RemoteMovie.Movie == null))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@@ -121,6 +121,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
|
||||
var qualityDefinition = _qualityDefinitionService.Get(quality);
|
||||
if (subject.Movie.Runtime == 0)
|
||||
{
|
||||
_logger.Info("{0} has no runtime information using median movie runtime of 110 minutes.", subject.Movie);
|
||||
subject.Movie.Runtime = 110;
|
||||
}
|
||||
if (qualityDefinition.MinSize.HasValue)
|
||||
{
|
||||
var minSize = qualityDefinition.MinSize.Value.Megabytes();
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,14 @@ namespace NzbDrone.Core.Download
|
||||
{
|
||||
var statusMessages = importResults
|
||||
.Where(v => v.Result != ImportResultType.Imported)
|
||||
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalMovie.Path), v.Errors))
|
||||
.Select(v =>
|
||||
{
|
||||
if (v.ImportDecision.LocalMovie == null)
|
||||
{
|
||||
return new TrackedDownloadStatusMessage("", v.Errors);
|
||||
}
|
||||
return new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalMovie.Path), v.Errors);
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
trackedDownload.Warn(statusMessages);
|
||||
|
||||
@@ -226,7 +226,12 @@ namespace NzbDrone.Core.Download.Pending
|
||||
Title = decision.RemoteMovie.Release.Title,
|
||||
Added = DateTime.UtcNow
|
||||
};
|
||||
_repository.Insert(release);
|
||||
if (release.ParsedMovieInfo == null)
|
||||
{
|
||||
_logger.Warn("Pending release {0} does not have ParsedMovieInfo, will cause issues.", release.Title);
|
||||
}
|
||||
|
||||
_repository.Insert(release);
|
||||
|
||||
_eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent());
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@ namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
{
|
||||
var id = torrent.Id;
|
||||
var title = $"{torrent.Name}.{torrent.Year}.{torrent.Resolution}.{torrent.Media}.{torrent.Encoding}.{torrent.AudioFormat}-{torrent.ReleaseGroup}";
|
||||
IndexerFlags flags = 0;
|
||||
|
||||
if (torrent.Freeleech == "1.00")
|
||||
{
|
||||
flags |= IndexerFlags.G_Freeleech;
|
||||
}
|
||||
|
||||
torrentInfos.Add(new TorrentInfo()
|
||||
{
|
||||
@@ -80,7 +86,8 @@ namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.Time.ToUniversalTime(),
|
||||
ImdbId = int.Parse(torrent.ImdbId.Substring(2))
|
||||
ImdbId = int.Parse(torrent.ImdbId.Substring(2)),
|
||||
IndexerFlags = flags,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,18 @@ namespace NzbDrone.Core.Indexers.HDBits
|
||||
var id = result.Id;
|
||||
var internalRelease = (result.TypeOrigin == 1 ? true : false);
|
||||
|
||||
IndexerFlags flags = 0;
|
||||
|
||||
if (result.FreeLeech == "yes")
|
||||
{
|
||||
flags |= IndexerFlags.G_Freeleech;
|
||||
}
|
||||
|
||||
if (internalRelease)
|
||||
{
|
||||
flags |= IndexerFlags.HDB_Internal;
|
||||
}
|
||||
|
||||
torrentInfos.Add(new HDBitsInfo()
|
||||
{
|
||||
Guid = string.Format("HDBits-{0}", id),
|
||||
@@ -65,7 +77,8 @@ namespace NzbDrone.Core.Indexers.HDBits
|
||||
Peers = result.Leechers + result.Seeders,
|
||||
PublishDate = result.Added.ToUniversalTime(),
|
||||
Internal = internalRelease,
|
||||
ImdbId = result.ImdbInfo?.Id ?? 0
|
||||
ImdbId = result.ImdbInfo?.Id ?? 0,
|
||||
IndexerFlags = flags
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
public string ReleaseName { get; set; }
|
||||
public bool Checked { get; set; }
|
||||
public bool GoldenPopcorn { get; set; }
|
||||
public string FreeleechType { get; set; }
|
||||
}
|
||||
|
||||
public class Movie
|
||||
|
||||
@@ -55,60 +55,69 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
var id = torrent.Id;
|
||||
var title = torrent.ReleaseName;
|
||||
IndexerFlags flags = 0;
|
||||
|
||||
if (torrent.GoldenPopcorn)
|
||||
{
|
||||
title = $"{title} 🍿";
|
||||
flags |= IndexerFlags.PTP_Golden;//title = $"{title} 🍿";
|
||||
}
|
||||
|
||||
if (torrent.Checked)
|
||||
{
|
||||
title = $"{title} ✔";
|
||||
flags |= IndexerFlags.PTP_Approved;//title = $"{title} ✔";
|
||||
}
|
||||
|
||||
if (torrent.FreeleechType == "Freeleech")
|
||||
{
|
||||
flags |= IndexerFlags.G_Freeleech;
|
||||
}
|
||||
|
||||
// Only add approved torrents
|
||||
if (_settings.RequireApproved && torrent.Checked)
|
||||
{
|
||||
torrentInfos.Add(new PassThePopcornInfo()
|
||||
if (_settings.RequireApproved && torrent.Checked)
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
Golden = torrent.GoldenPopcorn,
|
||||
Scene = torrent.Scene,
|
||||
Approved = torrent.Checked,
|
||||
ImdbId = (result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0)
|
||||
});
|
||||
}
|
||||
// Add all torrents
|
||||
else if (!_settings.RequireApproved)
|
||||
{
|
||||
torrentInfos.Add(new PassThePopcornInfo()
|
||||
torrentInfos.Add(new PassThePopcornInfo()
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
Golden = torrent.GoldenPopcorn,
|
||||
Scene = torrent.Scene,
|
||||
Approved = torrent.Checked,
|
||||
ImdbId = (result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0),
|
||||
IndexerFlags = flags
|
||||
});
|
||||
}
|
||||
|
||||
// Add all torrents
|
||||
else if (!_settings.RequireApproved)
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
Golden = torrent.GoldenPopcorn,
|
||||
Scene = torrent.Scene,
|
||||
Approved = torrent.Checked,
|
||||
ImdbId = (result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0)
|
||||
});
|
||||
}
|
||||
// Don't add any torrents
|
||||
else if (_settings.RequireApproved && !torrent.Checked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
torrentInfos.Add(new PassThePopcornInfo()
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
Golden = torrent.GoldenPopcorn,
|
||||
Scene = torrent.Scene,
|
||||
Approved = torrent.Checked,
|
||||
ImdbId = (result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0),
|
||||
IndexerFlags = flags
|
||||
});
|
||||
}
|
||||
// Don't add any torrents
|
||||
else if (_settings.RequireApproved && !torrent.Checked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
torrentInfo.ImdbId = int.Parse(GetImdbId(item).Substring(2));
|
||||
}
|
||||
}
|
||||
|
||||
torrentInfo.IndexerFlags = GetFlags(item);
|
||||
|
||||
return torrentInfo;
|
||||
}
|
||||
|
||||
@@ -151,6 +154,32 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
return base.GetPeers(item);
|
||||
}
|
||||
|
||||
protected IndexerFlags GetFlags(XElement item)
|
||||
{
|
||||
IndexerFlags flags = 0;
|
||||
|
||||
var downloadFactor = TryGetFloatTorznabAttribute(item, "downloadvolumefactor", 1);
|
||||
|
||||
var uploadFactor = TryGetFloatTorznabAttribute(item, "uploadvolumefactor", 1);
|
||||
|
||||
if (uploadFactor == 2)
|
||||
{
|
||||
flags |= IndexerFlags.G_DoubleUpload;
|
||||
}
|
||||
|
||||
if (downloadFactor == 0.5)
|
||||
{
|
||||
flags |= IndexerFlags.G_Halfleech;
|
||||
}
|
||||
|
||||
if (downloadFactor == 0.0)
|
||||
{
|
||||
flags |= IndexerFlags.G_Freeleech;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
protected string TryGetTorznabAttribute(XElement item, string key, string defaultValue = "")
|
||||
{
|
||||
var attr = item.Elements(ns + "attr").FirstOrDefault(e => e.Attribute("name").Value.Equals(key, StringComparison.CurrentCultureIgnoreCase));
|
||||
@@ -160,6 +189,20 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
return attr.Attribute("value").Value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
protected float TryGetFloatTorznabAttribute(XElement item, string key, float defaultValue = 0)
|
||||
{
|
||||
var attr = TryGetTorznabAttribute(item, key, defaultValue.ToString());
|
||||
|
||||
float result = 0;
|
||||
|
||||
if (float.TryParse(attr, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ namespace NzbDrone.Core.MetadataSource.PreDB
|
||||
|
||||
private List<PreDBResult> GetResults(string category = "", string search = "")
|
||||
{
|
||||
return new List<PreDBResult>();
|
||||
var builder = new HttpRequestBuilder("http://predb.me").AddQueryParam("rss", "1");
|
||||
if (category.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
@@ -171,10 +172,12 @@ namespace NzbDrone.Core.MetadataSource.PreDB
|
||||
|
||||
public bool HasReleases(Movie movie)
|
||||
{
|
||||
var results = GetResults("movies", movie.Title);
|
||||
try
|
||||
{
|
||||
var results = GetResults("movies", movie.Title);
|
||||
|
||||
foreach (PreDBResult result in results)
|
||||
{
|
||||
foreach (PreDBResult result in results)
|
||||
{
|
||||
var parsed = Parser.Parser.ParseMovieTitle(result.Title);
|
||||
if (parsed == null)
|
||||
{
|
||||
@@ -182,13 +185,20 @@ namespace NzbDrone.Core.MetadataSource.PreDB
|
||||
}
|
||||
var match = _parsingService.Map(parsed, "", new MovieSearchCriteria { Movie = movie });
|
||||
|
||||
if (match != null && match.Movie != null && match.Movie.Id == movie.Id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (match != null && match.Movie != null && match.Movie.Id == movie.Id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Error while looking on predb.me.");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Notifications.Email
|
||||
|
||||
public EmailSettings()
|
||||
{
|
||||
Server = "smtp.google.com";
|
||||
Server = "smtp.gmail.com";
|
||||
Port = 587;
|
||||
Ssl = 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,6 +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">
|
||||
@@ -1344,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)),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace NzbDrone.Core.Parser.Model
|
||||
public string Codec { get; set; }
|
||||
public string Resolution { get; set; }
|
||||
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
|
||||
public int Age
|
||||
{
|
||||
get
|
||||
@@ -91,4 +93,15 @@ namespace NzbDrone.Core.Parser.Model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum IndexerFlags
|
||||
{
|
||||
G_Freeleech = 1, //General
|
||||
G_Halfleech = 2, //General, only 1/2 of download counted
|
||||
G_DoubleUpload = 4, //General
|
||||
PTP_Golden = 8, //PTP
|
||||
PTP_Approved = 16, //PTP
|
||||
HDB_Internal = 32 //HDBits
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -273,7 +273,7 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
private static readonly Regex ReportImdbId = new Regex(@"(?<imdbid>tt\d{7})", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex SimpleTitleRegex = new Regex(@"(?:480[ip]|576[ip]|720[ip]|1080[ip]|2160[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*:|]|848x480|1280x720|1920x1080|(8|10)b(it)?)\s*",
|
||||
private static readonly Regex SimpleTitleRegex = new Regex(@"\s*(?:480[ip]|576[ip]|720[ip]|1080[ip]|2160[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*:|]|848x480|1280x720|1920x1080|(8|10)b(it)?)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex WebsitePrefixRegex = new Regex(@"^\[\s*[a-z]+(\.[a-z]+)+\s*\][- ]*",
|
||||
@@ -310,6 +310,12 @@ namespace NzbDrone.Core.Parser
|
||||
private static readonly Regex RequestInfoRegex = new Regex(@"\[.+?\]", RegexOptions.Compiled);
|
||||
|
||||
private static readonly string[] Numbers = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
|
||||
private static Dictionary<String, String> _umlautMappings = new Dictionary<string, string>
|
||||
{
|
||||
{"ö", "oe"},
|
||||
{"ä", "ae"},
|
||||
{"ü", "ue"},
|
||||
};
|
||||
|
||||
public static ParsedEpisodeInfo ParsePath(string path)
|
||||
{
|
||||
@@ -405,8 +411,13 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
var languageTitle = simpleTitle;
|
||||
if (match[0].Groups["title"].Success && match[0].Groups["title"].Value.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
languageTitle = simpleTitle.Replace(match[0].Groups["title"].Value, "A Movie");
|
||||
}
|
||||
|
||||
result.Language = LanguageParser.ParseLanguage(simpleTitle.Replace(result.MovieTitle, "A Movie"));
|
||||
result.Language = LanguageParser.ParseLanguage(languageTitle);
|
||||
Logger.Debug("Language parsed: {0}", result.Language);
|
||||
|
||||
result.Quality = QualityParser.ParseQuality(title);
|
||||
@@ -655,7 +666,7 @@ namespace NzbDrone.Core.Parser
|
||||
if (long.TryParse(title, out number))
|
||||
return title;
|
||||
|
||||
return NormalizeRegex.Replace(title, string.Empty).ToLower().RemoveAccent();
|
||||
return ReplaceGermanUmlauts(NormalizeRegex.Replace(title, string.Empty).ToLower()).RemoveAccent();
|
||||
}
|
||||
|
||||
public static string NormalizeEpisodeTitle(string title)
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace NzbDrone.Core.Parser
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly Logger _logger;
|
||||
private static HashSet<ArabicRomanNumeral> _arabicRomanNumeralMappings;
|
||||
|
||||
|
||||
public ParsingService(IEpisodeService episodeService,
|
||||
ISeriesService seriesService,
|
||||
@@ -178,7 +179,7 @@ namespace NzbDrone.Core.Parser
|
||||
return _movieService.FindByTitle(title);
|
||||
}
|
||||
|
||||
var movies = _movieService.FindByTitle(parsedMovieInfo.MovieTitle);
|
||||
var movies = _movieService.FindByTitle(parsedMovieInfo.MovieTitle, parsedMovieInfo.Year);
|
||||
|
||||
if (movies == null)
|
||||
{
|
||||
@@ -349,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))
|
||||
@@ -402,6 +403,8 @@ namespace NzbDrone.Core.Parser
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitle);
|
||||
if (isNotNull(movieByTitleAndOrYear))
|
||||
@@ -412,7 +415,7 @@ namespace NzbDrone.Core.Parser
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetMovieBySearchCriteria(ParsedMovieInfo parsedMovieInfo, SearchCriteriaBase searchCriteria, out Movie possibleMovie)
|
||||
private bool TryGetMovieBySearchCriteria(ParsedMovieInfo parsedMovieInfo, SearchCriteriaBase searchCriteria, out Movie possibleMovie)
|
||||
{
|
||||
possibleMovie = null;
|
||||
List<string> possibleTitles = new List<string>();
|
||||
@@ -424,6 +427,8 @@ namespace NzbDrone.Core.Parser
|
||||
possibleTitles.Add(altTitle.CleanSeriesTitle());
|
||||
}
|
||||
|
||||
string cleanTitle = parsedMovieInfo.MovieTitle.CleanSeriesTitle();
|
||||
|
||||
foreach (string title in possibleTitles)
|
||||
{
|
||||
if (title == parsedMovieInfo.MovieTitle.CleanSeriesTitle())
|
||||
@@ -436,12 +441,14 @@ namespace NzbDrone.Core.Parser
|
||||
string arabicNumeral = numeralMapping.ArabicNumeralAsString;
|
||||
string romanNumeral = numeralMapping.RomanNumeralLowerCase;
|
||||
|
||||
_logger.Debug(cleanTitle);
|
||||
|
||||
if (title.Replace(arabicNumeral, romanNumeral) == parsedMovieInfo.MovieTitle.CleanSeriesTitle())
|
||||
{
|
||||
possibleMovie = searchCriteria.Movie;
|
||||
}
|
||||
|
||||
if (title.Replace(romanNumeral, arabicNumeral) == parsedMovieInfo.MovieTitle.CleanSeriesTitle())
|
||||
if (title == parsedMovieInfo.MovieTitle.CleanSeriesTitle().Replace(arabicNumeral, romanNumeral))
|
||||
{
|
||||
possibleMovie = searchCriteria.Movie;
|
||||
}
|
||||
@@ -681,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; }
|
||||
@@ -98,12 +99,18 @@ namespace NzbDrone.Core.Tv
|
||||
return true;
|
||||
}
|
||||
|
||||
return DateTime.Now >= MinimumAvailabilityDate.AddDays((double)delay);
|
||||
if (MinimumAvailabilityDate == DateTime.MinValue || MinimumAvailabilityDate == DateTime.MaxValue)
|
||||
{
|
||||
return DateTime.Now >= MinimumAvailabilityDate;
|
||||
}
|
||||
|
||||
|
||||
return DateTime.Now >= MinimumAvailabilityDate.AddDays((double)delay);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}][{1}]", ImdbId, Title.NullSafe());
|
||||
return string.Format("[{0}][{1} ({2})]", ImdbId, Title.NullSafe(), Year.NullSafe());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +118,11 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public bool SearchForMovie { get; set; }
|
||||
}
|
||||
|
||||
public enum MoviePathState
|
||||
{
|
||||
Dynamic,
|
||||
StaticOnce,
|
||||
Static,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,29 +238,31 @@ namespace NzbDrone.Core.Tv
|
||||
cleanTitleWithArabicNumbers = cleanTitleWithArabicNumbers.Replace(romanNumber, arabicNumber);
|
||||
}
|
||||
|
||||
IEnumerable<Movie> results = Query.Where(s => s.CleanTitle == cleanTitle);
|
||||
Movie result = Query.Where(s => s.CleanTitle == cleanTitle).FirstWithYear(year);
|
||||
|
||||
if (results == null)
|
||||
if (result == null)
|
||||
{
|
||||
results = Query.Where(movie => movie.CleanTitle == cleanTitleWithArabicNumbers) ??
|
||||
Query.Where(movie => movie.CleanTitle == cleanTitleWithRomanNumbers);
|
||||
result = Query.Where(movie => movie.CleanTitle == cleanTitleWithArabicNumbers).FirstWithYear(year) ??
|
||||
Query.Where(movie => movie.CleanTitle == cleanTitleWithRomanNumbers).FirstWithYear(year);
|
||||
|
||||
if (results == null)
|
||||
if (result == null)
|
||||
{
|
||||
IEnumerable<Movie> movies = All();
|
||||
Func<string, string> titleCleaner = title => CoreParser.CleanSeriesTitle(title.ToLower());
|
||||
Func<IEnumerable<string>, string, bool> altTitleComparer =
|
||||
(alternativeTitles, atitle) =>
|
||||
alternativeTitles.Any(altTitle => altTitle == titleCleaner(atitle));
|
||||
alternativeTitles.Any(altTitle => titleCleaner(altTitle) == atitle);
|
||||
|
||||
results = movies.Where(m => altTitleComparer(m.AlternativeTitles, cleanTitle) ||
|
||||
result = movies.Where(m => altTitleComparer(m.AlternativeTitles, cleanTitle) ||
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithRomanNumbers) ||
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithArabicNumbers));
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithArabicNumbers)).FirstWithYear(year);
|
||||
|
||||
}
|
||||
}
|
||||
return year.HasValue
|
||||
return result;
|
||||
/*return year.HasValue
|
||||
? results?.FirstOrDefault(movie => movie.Year == year.Value)
|
||||
: results?.FirstOrDefault();
|
||||
: results?.FirstOrDefault();*/
|
||||
}
|
||||
|
||||
public Movie FindByTmdbId(int tmdbid)
|
||||
|
||||
@@ -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,20 +52,24 @@ namespace NzbDrone.Core.Tv
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
public MovieService(IMovieRepository movieRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
ISceneMappingService sceneMappingService,
|
||||
IEpisodeService episodeService,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
Logger logger)
|
||||
{
|
||||
_movieRepository = movieRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_configService = configService;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -149,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);
|
||||
@@ -173,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();
|
||||
@@ -265,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));
|
||||
|
||||
30
src/NzbDrone.Core/Tv/QueryExtensions.cs
Normal file
30
src/NzbDrone.Core/Tv/QueryExtensions.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
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 NzbDrone.Core.Tv;
|
||||
using CoreParser = NzbDrone.Core.Parser.Parser;
|
||||
namespace NzbDrone.Core
|
||||
{
|
||||
public static class QueryExtensions
|
||||
{
|
||||
public static Movie FirstWithYear(this SortBuilder<Movie> query, int? year)
|
||||
{
|
||||
return year.HasValue ? query.FirstOrDefault(movie => movie.Year == year) : query.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static Movie FirstWithYear(this IEnumerable<Movie> query, int? year)
|
||||
{
|
||||
return year.HasValue ? query.FirstOrDefault(movie => movie.Year == year) : query.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Mono.Unix;
|
||||
using Mono.Unix.Native;
|
||||
using NLog;
|
||||
@@ -17,21 +18,50 @@ namespace NzbDrone.Mono.Disk
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProvider));
|
||||
|
||||
private readonly IProcMountProvider _procMountProvider;
|
||||
private readonly NzbDrone.Mono.Disk.ISymbolicLinkResolver _symLinkResolver;
|
||||
private readonly ISymbLinkResolver _symLinkResolver;
|
||||
private readonly Logger _logger;
|
||||
|
||||
// Mono supports sending -1 for a uint to indicate that the owner or group should not be set
|
||||
// `unchecked((uint)-1)` and `uint.MaxValue` are the same thing.
|
||||
private const uint UNCHANGED_ID = uint.MaxValue;
|
||||
|
||||
public DiskProvider(IProcMountProvider procMountProvider, NzbDrone.Mono.Disk.ISymbolicLinkResolver symLinkResolver)
|
||||
public DiskProvider(IProcMountProvider procMountProvider, ISymbLinkResolver symLinkResolver, Logger logger)
|
||||
{
|
||||
_procMountProvider = procMountProvider;
|
||||
_symLinkResolver = symLinkResolver;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override IMount GetMount(string path)
|
||||
{
|
||||
path = _symLinkResolver.GetCompleteRealPath(path);
|
||||
if (path == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
string[] dirs;
|
||||
int lastIndex;
|
||||
GetPathComponents(path, out dirs, out lastIndex);
|
||||
|
||||
var realPath = new StringBuilder();
|
||||
if (dirs.Length > 0)
|
||||
{
|
||||
var dir = UnixPath.IsPathRooted(path) ? "/" : "";
|
||||
dir += dirs[0];
|
||||
realPath.Append(GetRealPath(dir));
|
||||
}
|
||||
for (var i = 1; i < lastIndex; ++i)
|
||||
{
|
||||
realPath.Append("/").Append(dirs[i]);
|
||||
var realSubPath = GetRealPath(realPath.ToString());
|
||||
realPath.Remove(0, realPath.Length);
|
||||
realPath.Append(realSubPath);
|
||||
}
|
||||
path = realPath.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Debug(ex, string.Format("Failed to check for symlinks in the path {0}", path));
|
||||
}
|
||||
|
||||
return base.GetMount(path);
|
||||
}
|
||||
@@ -219,5 +249,65 @@ namespace NzbDrone.Mono.Disk
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void GetPathComponents(string path, out string[] components, out int lastIndex)
|
||||
{
|
||||
var dirs = path.Split(UnixPath.DirectorySeparatorChar);
|
||||
var target = 0;
|
||||
for (var i = 0; i < dirs.Length; ++i)
|
||||
{
|
||||
if (dirs[i] == "." || dirs[i] == string.Empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dirs[i] == "..")
|
||||
{
|
||||
if (target != 0)
|
||||
{
|
||||
target--;
|
||||
}
|
||||
else
|
||||
{
|
||||
target++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dirs[target++] = dirs[i];
|
||||
}
|
||||
}
|
||||
components = dirs;
|
||||
lastIndex = target;
|
||||
}
|
||||
|
||||
public string GetRealPath(string path)
|
||||
{
|
||||
do
|
||||
{
|
||||
var link = UnixPath.TryReadLink(path);
|
||||
|
||||
if (link == null)
|
||||
{
|
||||
var errno = Stdlib.GetLastError();
|
||||
if (errno != Errno.EINVAL)
|
||||
{
|
||||
_logger.Trace("Checking path {0} for symlink returned error {1}, assuming it's not a symlink.", path, errno);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
if (UnixPath.IsPathRooted(link))
|
||||
{
|
||||
path = link;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = UnixPath.GetDirectoryName(path) + UnixPath.DirectorySeparatorChar + link;
|
||||
path = UnixPath.GetCanonicalPath(path);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,23 +6,23 @@ using NLog;
|
||||
|
||||
namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
public interface ISymbolicLinkResolver
|
||||
public interface ISymbLinkResolver
|
||||
{
|
||||
string GetCompleteRealPath(string path);
|
||||
string GetCompletePath(string path);
|
||||
}
|
||||
|
||||
// Mono's own implementation doesn't handle exceptions very well.
|
||||
// All of this code was copied from mono with minor changes.
|
||||
public class SymbolicLinkResolver : ISymbolicLinkResolver
|
||||
public class SymbLinkResolver : ISymbLinkResolver
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SymbolicLinkResolver(Logger logger)
|
||||
public SymbLinkResolver(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GetCompleteRealPath(string path)
|
||||
public string GetCompletePath(string path)
|
||||
{
|
||||
if (path == null) return null;
|
||||
|
||||
|
||||
@@ -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}}
|
||||
|
||||
59
src/UI/Cells/IndexerFlagsCell.js
Normal file
59
src/UI/Cells/IndexerFlagsCell.js
Normal file
@@ -0,0 +1,59 @@
|
||||
var Backgrid = require('backgrid');
|
||||
var Marionette = require('marionette');
|
||||
require('bootstrap');
|
||||
|
||||
module.exports = Backgrid.Cell.extend({
|
||||
className : 'edition-cell',
|
||||
//template : 'Cells/EditionCellTemplate',
|
||||
|
||||
render : function() {
|
||||
|
||||
var flags = this.model.get("indexerFlags");
|
||||
if (!flags) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var html = "";
|
||||
|
||||
if (flags) {
|
||||
_.each(flags, function(flag){
|
||||
var addon = "";
|
||||
var title = "";
|
||||
|
||||
switch (flag) {
|
||||
case "G_Freeleech":
|
||||
addon = "⬇";
|
||||
title = "Freeleech";
|
||||
break;
|
||||
case "G_Halfleech":
|
||||
addon = "⇩";
|
||||
title = "50% Freeleech";
|
||||
break;
|
||||
case "G_DoubleUpload":
|
||||
addon = "⬆";
|
||||
title = "Double upload";
|
||||
break;
|
||||
case "PTP_Golden":
|
||||
addon = "🍿";
|
||||
title = "Golden";
|
||||
break;
|
||||
case "PTP_Approved":
|
||||
addon = "✔";
|
||||
title = "Approved by PTP"
|
||||
break;
|
||||
case "HDB_Internal":
|
||||
addon = "⭐️";
|
||||
title = "HDBits Internal";
|
||||
break;
|
||||
}
|
||||
if (addon != "") {
|
||||
html += "<span title='{0}'>{1}</span> ".format(title, addon);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.$el.html(html);
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
@@ -9,6 +9,9 @@ module.exports = NzbDroneCell.extend({
|
||||
var title = this.model.get('title');
|
||||
var infoUrl = this.model.get('infoUrl');
|
||||
|
||||
var flags = this.model.get("indexerFlags");
|
||||
|
||||
|
||||
if (infoUrl) {
|
||||
this.$el.html('<a href="{0}">{1}</a>'.format(infoUrl, title));
|
||||
} 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);
|
||||
},
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user