mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-13 15:34:56 -04:00
Compare commits
99 Commits
v0.2.0.99
...
v0.2.0.166
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dea305e921 | ||
|
|
e2eab31548 | ||
|
|
fe62e18f0d | ||
|
|
f1fa1553cf | ||
|
|
b576ae813d | ||
|
|
99123be936 | ||
|
|
dd0a033b0f | ||
|
|
c64597c9f1 | ||
|
|
6d2f81e3ed | ||
|
|
4263808360 | ||
|
|
c5ca2babf7 | ||
|
|
08db74d6e6 | ||
|
|
b309a9b01f | ||
|
|
2730745607 | ||
|
|
ae0df2aef0 | ||
|
|
6d665aeb21 | ||
|
|
967d3fd5c0 | ||
|
|
199d9c93ed | ||
|
|
30d2b41fbb | ||
|
|
75bb2533a3 | ||
|
|
9b3b4eb55b | ||
|
|
42f84b830c | ||
|
|
eb0f825cfc | ||
|
|
56a5b6ec89 | ||
|
|
af478d3799 | ||
|
|
c2d40051d4 | ||
|
|
281e516495 | ||
|
|
f63c3091f4 | ||
|
|
50f49863b7 | ||
|
|
941b3bd701 | ||
|
|
f60b8cefca | ||
|
|
7c5f2ca54e | ||
|
|
401a650273 | ||
|
|
105af5cf11 | ||
|
|
637c2e43eb | ||
|
|
53373e6f4a | ||
|
|
ad147ed425 | ||
|
|
ed35e2f194 | ||
|
|
674919dbf5 | ||
|
|
15b77e303f | ||
|
|
757ca1d72c | ||
|
|
8c8c7a99e3 | ||
|
|
b3dfb960b5 | ||
|
|
98856e6747 | ||
|
|
8fb5049899 | ||
|
|
3f62911a4f | ||
|
|
170349c950 | ||
|
|
6c413d83eb | ||
|
|
3afd27cad3 | ||
|
|
149faf467d | ||
|
|
ce52bb8f68 | ||
|
|
4bb5857444 | ||
|
|
620f09ef8b | ||
|
|
16c1a2ee50 | ||
|
|
4b559cf29c | ||
|
|
ca03f21b03 | ||
|
|
f481fe9ea9 | ||
|
|
3a0278d0a1 | ||
|
|
d18b15d504 | ||
|
|
dc7e75187c | ||
|
|
68a0ef65df | ||
|
|
fe11928487 | ||
|
|
41dda3af48 | ||
|
|
8c3260c545 | ||
|
|
bd0ec5dfce | ||
|
|
ad38437b70 | ||
|
|
4447b7cd62 | ||
|
|
d33de0d158 | ||
|
|
dd5049b483 | ||
|
|
29586667cb | ||
|
|
5daece0ed4 | ||
|
|
5dc63e5607 | ||
|
|
7df283e57f | ||
|
|
e9b6c250f7 | ||
|
|
10515156d1 | ||
|
|
4260b58535 | ||
|
|
3a386f2e18 | ||
|
|
d3bd0c9b69 | ||
|
|
3180e648b4 | ||
|
|
d0c93759c6 | ||
|
|
f7471940c4 | ||
|
|
3a6873cc4d | ||
|
|
330ae38ec2 | ||
|
|
a0ecb19e32 | ||
|
|
38c966c07b | ||
|
|
ad824d4da5 | ||
|
|
e76c160afe | ||
|
|
6a62546a4d | ||
|
|
0efdc78f8d | ||
|
|
fdd06127fc | ||
|
|
317f8917b9 | ||
|
|
4038ce18c3 | ||
|
|
7f3ca85953 | ||
|
|
b93a9719fe | ||
|
|
2ef18edf08 | ||
|
|
d61b9ab207 | ||
|
|
abf4b137f1 | ||
|
|
774c85f06b | ||
|
|
a060335bbc |
26
.gitignore
vendored
26
.gitignore
vendored
@@ -101,16 +101,21 @@ App_Data/*.ldf
|
||||
_NCrunch_*
|
||||
_TeamCity*
|
||||
|
||||
# Sonarr
|
||||
config.xml
|
||||
nzbdrone.log*txt
|
||||
# Radarr
|
||||
Backups/
|
||||
logs/
|
||||
MediaCover/
|
||||
UpdateLogs/
|
||||
xdg/
|
||||
config.xml
|
||||
logs.db*
|
||||
nzbdrone.db*
|
||||
nzbdrone.pid
|
||||
*workspace.xml
|
||||
*.test-cache
|
||||
*.userprefs
|
||||
*/test-results/*
|
||||
src/UI/.idea/*
|
||||
*log.txt
|
||||
node_modules/
|
||||
_output*
|
||||
_rawPackage/
|
||||
@@ -122,23 +127,26 @@ setup/Output/
|
||||
|
||||
UI.Phantom/
|
||||
|
||||
#VS outout folders
|
||||
# VS outout folders
|
||||
bin
|
||||
obj
|
||||
output/*
|
||||
|
||||
#Packages
|
||||
# Packages
|
||||
Radarr_*/
|
||||
Radarr_*.zip
|
||||
Radarr_*.gz
|
||||
|
||||
#OS X metadata files
|
||||
# macOS metadata files
|
||||
._*
|
||||
.DS_Store
|
||||
|
||||
_start
|
||||
_temp_*/**/*
|
||||
|
||||
#AppVeyor
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
|
||||
# AppVeyor
|
||||
/tools-cake/
|
||||
/_artifacts/
|
||||
/_artifacts/
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -1,12 +1,14 @@
|
||||
language: csharp
|
||||
solution: src/NzbDrone.sln
|
||||
script: # the following commands are just examples, use whatever your build process requires
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- nodejs
|
||||
- npm
|
||||
script:
|
||||
- ./build.sh
|
||||
- chmod +x test.sh
|
||||
# - ./test.sh Linux Unit Takes far too long, maybe even crashes travis :/
|
||||
install:
|
||||
- sudo apt-get install nodejs
|
||||
- sudo apt-get install npm
|
||||
after_success:
|
||||
- chmod +x package.sh
|
||||
- ./package.sh
|
||||
|
||||
BIN
Logo/Thumbs.db
BIN
Logo/Thumbs.db
Binary file not shown.
94
README.md
Normal file
94
README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
## Status
|
||||
|
||||
[](https://github.com/Radarr/Radarr/issues)
|
||||
[](https://github.com/Radarr/Radarr/pulls)
|
||||
[](http://www.gnu.org/licenses/gpl.html)
|
||||
[](https://github.com/Radarr/Radarr)
|
||||
[](https://github.com/Radar/Radarr/releases/latest)
|
||||
[](https://hub.docker.com/r/linuxserver/radarr/)
|
||||
|
||||
| Service | Master | Develop |
|
||||
|----------|:---------------------------:|:----------------------------:|
|
||||
| AppVeyor | [](https://ci.appveyor.com/project/galli-leo/Radarr) | [](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
|
||||
| Travis | [](https://travis-ci.org/Radarr/Radarr) | [](https://travis-ci.org/Radarr/Radarr) |
|
||||
|
||||
This fork of Sonarr aims to turn it into something like CouchPotato.
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://github.com/Radarr/Radarr/releases)
|
||||
|
||||
[](https://ci.appveyor.com/project/galli-leo/radarr-usby1/build/artifacts)
|
||||
|
||||
[](https://store.docker.com/community/images/linuxserver/radarr)
|
||||
[](https://store.docker.com/community/images/lsioarmhf/radarr)
|
||||
[](https://store.docker.com/community/images/lsioarmhf/radarr-aarch64)
|
||||
|
||||
To connect to the UI, fire up your browser and open <http://localhost:7878> or <http://your-ip:7878>.
|
||||
|
||||
## Support
|
||||
|
||||
[](https://discord.gg/AD3UP37)
|
||||
[](https://www.reddit.com/r/radarr)
|
||||
[](https://github.com/Radarr/Radarr/issues)
|
||||
|
||||
## Features
|
||||
|
||||
### Currently Working
|
||||
|
||||
* Adding new movies
|
||||
* Manually searching for releases of movies
|
||||
* Automatically searching for releases
|
||||
* Automatically importing downloaded movies
|
||||
* Recognizing Special Editions, Director's Cut, etc.
|
||||
* Identifying releases with hardcoded subs
|
||||
* Rarbg.to, Torznab and Newznab Indexer
|
||||
* QBittorrent and Deluge download client (Other clients are coming)
|
||||
* New TorrentPotato Indexer (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
||||
|
||||
### Planned Features
|
||||
|
||||
* Scanning PreDB to know when a new release is available
|
||||
* Fixing the other Indexers and download clients
|
||||
* Importing of Sonarr config
|
||||
|
||||
### Major Features
|
||||
|
||||
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
|
||||
* Can watch for better quality of the movies you have and do an automatic upgrade. *eg. from DVD to Blu-Ray*
|
||||
* Automatic failed download handling will try another release if one fails
|
||||
* Manual search so you can pick any release or to see why a release was not downloaded automatically
|
||||
* Full integration with SABnzbd and NZBGet
|
||||
* Full integration with Kodi, Plex (notification, library update, metadata)
|
||||
* And a beautiful UI
|
||||
|
||||
## Configuring Development Environment
|
||||
|
||||
### Requirements
|
||||
|
||||
* [Visual Studio Community](https://www.visualstudio.com/vs/community/) or [MonoDevelop](http://www.monodevelop.com)
|
||||
* [Git](https://git-scm.com/downloads)
|
||||
* [Node.js](https://nodejs.org/en/download/)
|
||||
|
||||
### Setup
|
||||
|
||||
* Make sure all the required software mentioned above are installed
|
||||
* Clone the repository into your development machine ([*info*](https://help.github.com/desktop/guides/contributing/working-with-your-remote-repository-on-github-or-github-enterprise))
|
||||
* Grab the submodules `git submodule init && git submodule update`
|
||||
* Install the required Node Packages `npm install`
|
||||
* Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
|
||||
|
||||
> **Notice**
|
||||
> Gulp must be running at all times while you are working with Radarr client source files.
|
||||
|
||||
### Development
|
||||
|
||||
* Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed
|
||||
* Make sure `NzbDrone.Console` is set as the startup project
|
||||
|
||||
## Sponsors
|
||||
|
||||
[JetBrains](http://www.jetbrains.com) for providing us with free licenses to their great tools:
|
||||
* [ReSharper](http://www.jetbrains.com/resharper)
|
||||
* [WebStorm](http://www.jetbrains.com/webstorm)
|
||||
* [TeamCity](http://www.jetbrains.com/teamcity)
|
||||
@@ -36,6 +36,9 @@ artifacts:
|
||||
cache:
|
||||
- '%USERPROFILE%\.nuget\packages'
|
||||
- node_modules
|
||||
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
only_commits:
|
||||
files:
|
||||
|
||||
81
readme.md
81
readme.md
@@ -1,81 +0,0 @@
|
||||
# Radarr
|
||||
|
||||
| Service | Master | Develop |
|
||||
|----------|:---------------------------:|:----------------------------:|
|
||||
| AppVeyor | [](https://ci.appveyor.com/project/galli-leo/Radarr) | [](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
|
||||
| Travis | [](https://travis-ci.org/galli-leo/Radarr) | [](https://travis-ci.org/galli-leo/Radarr) |
|
||||
|
||||
This fork of Sonarr aims to turn it into something like Couchpotato.
|
||||
|
||||
## Currently working:
|
||||
* Adding new movies
|
||||
* Manually searching for releases of movies.
|
||||
* Automatically searching for releases.
|
||||
* Automatically importing downloaded movies.
|
||||
* Recognizing Special Editions, Director's Cut, etc.
|
||||
* Identifying releases with hardcoded subs.
|
||||
* Rarbg.to, Torznab and Newznab Indexer.
|
||||
* QBittorrent and Deluge download client (Other clients are coming)
|
||||
* New TorrentPotato Indexer (Works well with [Jackett](https://github.com/Jackett/Jackett))
|
||||
|
||||
## Planned Features:
|
||||
* Scanning PreDB to know when a new release is available.
|
||||
* Fixing the other Indexers and download clients.
|
||||
* Importing of Sonarr config.
|
||||
|
||||
## Download
|
||||
The latest precompiled binary versions can be found here: https://github.com/galli-leo/Radarr/releases.
|
||||
|
||||
Docker containers from [linuxserver.io](https://linuxserver.io) can be found here.
|
||||
* [Radarr (x64)](https://hub.docker.com/r/linuxserver/radarr/)
|
||||
* [Radarr (armhf)](https://hub.docker.com/r/lsioarmhf/radarr/)
|
||||
* [Radarr (aarch64)](https://hub.docker.com/r/lsioarmhf/radarr-aarch64/)
|
||||
|
||||
For more up to date versions (but also sometimes broken), daily builds can be found here:
|
||||
* [OSX](https://leonardogalli.ch/radarr/builds/latest.php?os=osx)
|
||||
* [Windows](https://leonardogalli.ch/radarr/builds/latest.php?os=windows)
|
||||
* [Linux](https://leonardogalli.ch/radarr/builds/latest.php?os=mono)
|
||||
|
||||
## Major Features Include: ##
|
||||
|
||||
* Support for major platforms: Windows, Linux, OSX, Raspberry Pi, etc.
|
||||
* Can watch for better quality of the movies you have and do an upgrade.
|
||||
* Automatic failed download handling will try another release if one fails
|
||||
* Manual search so you can pick any release or to see why a release was not downloaded automatically.
|
||||
* Full integration with SABNzbd and NzbGet.
|
||||
* Full integration with XBMC, Plex (notification, library update, metadata).
|
||||
* And a beautiful UI
|
||||
|
||||
## Configuring Development Environment: ##
|
||||
|
||||
### Requirements ###
|
||||
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) or Mono
|
||||
- [Git](http://git-scm.com/downloads)
|
||||
- [NodeJS](http://nodejs.org/download/)
|
||||
|
||||
### Setup ###
|
||||
|
||||
- Make sure all the required software mentioned above are installed.
|
||||
- Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
|
||||
- Grab the submodules `git submodule init && git submodule update`
|
||||
- install the required Node Packages `npm install`
|
||||
- start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
|
||||
|
||||
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
|
||||
|
||||
|
||||
### Development ###
|
||||
- Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed.
|
||||
- Make sure `NzbDrone.Console` is set as the startup project
|
||||
|
||||
|
||||
### License ###
|
||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
Copyright 2010-2016
|
||||
|
||||
|
||||
### Sponsors ###
|
||||
- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
|
||||
- [ReSharper](http://www.jetbrains.com/resharper/)
|
||||
- [WebStorm](http://www.jetbrains.com/webstorm/)
|
||||
- [TeamCity](http://www.jetbrains.com/teamcity/)
|
||||
@@ -2,24 +2,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.Episodes;
|
||||
using NzbDrone.Api.Movie;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MovieStats;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace NzbDrone.Api.Calendar
|
||||
{
|
||||
public class CalendarModule : EpisodeModuleWithSignalR
|
||||
public class CalendarModule : MovieModule
|
||||
{
|
||||
public CalendarModule(IEpisodeService episodeService,
|
||||
ISeriesService seriesService,
|
||||
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||
IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "calendar")
|
||||
public CalendarModule(IBroadcastSignalRMessage signalR,
|
||||
IMovieService moviesService,
|
||||
IMovieStatisticsService moviesStatisticsService,
|
||||
ISceneMappingService sceneMappingService,
|
||||
IMapCoversToLocal coverMapper)
|
||||
: base(signalR, moviesService, moviesStatisticsService, sceneMappingService, coverMapper, "calendar")
|
||||
{
|
||||
|
||||
GetResourceAll = GetCalendar;
|
||||
}
|
||||
|
||||
private List<EpisodeResource> GetCalendar()
|
||||
private List<MovieResource> GetCalendar()
|
||||
{
|
||||
var start = DateTime.Today;
|
||||
var end = DateTime.Today.AddDays(2);
|
||||
@@ -33,9 +47,9 @@ namespace NzbDrone.Api.Calendar
|
||||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
||||
if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value);
|
||||
|
||||
var resources = MapToResource(_episodeService.EpisodesBetweenDates(start, end, includeUnmonitored), true, true);
|
||||
var resources = _moviesService.GetMoviesBetweenDates(start, end, includeUnmonitored).Select(MapToResource);
|
||||
|
||||
return resources.OrderBy(e => e.AirDateUtc).ToList();
|
||||
return resources.OrderBy(e => e.InCinemas).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
src/NzbDrone.Api/Movies/MovieFileModule.cs
Normal file
89
src/NzbDrone.Api/Movies/MovieFileModule.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Api.Movie;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace NzbDrone.Api.EpisodeFiles
|
||||
{
|
||||
public class MovieFileModule : NzbDroneRestModuleWithSignalR<MovieFileResource, MovieFile>
|
||||
//IHandle<EpisodeFileAddedEvent>
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly IMovieService _seriesService;
|
||||
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MovieFileModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMediaFileService mediaFileService,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IMovieService seriesService,
|
||||
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||
Logger logger)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_mediaFileService = mediaFileService;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_seriesService = seriesService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_logger = logger;
|
||||
/*GetResourceById = GetEpisodeFile;
|
||||
GetResourceAll = GetEpisodeFiles;
|
||||
UpdateResource = SetQuality;*/
|
||||
DeleteResource = DeleteEpisodeFile;
|
||||
}
|
||||
|
||||
/*private EpisodeFileResource GetEpisodeFile(int id)
|
||||
{
|
||||
var episodeFile = _mediaFileService.Get(id);
|
||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
||||
|
||||
return episodeFile.ToResource(series, _qualityUpgradableSpecification);
|
||||
}
|
||||
|
||||
private List<EpisodeFileResource> GetEpisodeFiles()
|
||||
{
|
||||
if (!Request.Query.SeriesId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("seriesId is missing");
|
||||
}
|
||||
|
||||
var seriesId = (int)Request.Query.SeriesId;
|
||||
|
||||
var series = _seriesService.GetSeries(seriesId);
|
||||
|
||||
return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _qualityUpgradableSpecification));
|
||||
}
|
||||
|
||||
private void SetQuality(EpisodeFileResource episodeFileResource)
|
||||
{
|
||||
var episodeFile = _mediaFileService.Get(episodeFileResource.Id);
|
||||
episodeFile.Quality = episodeFileResource.Quality;
|
||||
_mediaFileService.Update(episodeFile);
|
||||
}*/
|
||||
|
||||
private void DeleteEpisodeFile(int id)
|
||||
{
|
||||
var episodeFile = _mediaFileService.GetMovie(id);
|
||||
var series = _seriesService.GetMovie(episodeFile.MovieId);
|
||||
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
|
||||
|
||||
_logger.Info("Deleting episode file: {0}", fullPath);
|
||||
_recycleBinProvider.DeleteFile(fullPath);
|
||||
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
|
||||
}
|
||||
|
||||
public void Handle(EpisodeFileAddedEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace NzbDrone.Api.Movies
|
||||
private readonly IRenameMovieFileService _renameMovieFileService;
|
||||
|
||||
public RenameMovieModule(IRenameMovieFileService renameMovieFileService)
|
||||
: base("rename")
|
||||
: base("renameMovie")
|
||||
{
|
||||
_renameMovieFileService = renameMovieFileService;
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||
<Compile Include="Indexers\ReleasePushModule.cs" />
|
||||
<Compile Include="Movies\MovieFileModule.cs" />
|
||||
<Compile Include="Movies\MovieModule.cs" />
|
||||
<Compile Include="Movies\RenameMovieModule.cs" />
|
||||
<Compile Include="Movies\RenameMovieResource.cs" />
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace NzbDrone.Api.Movie
|
||||
public string ReleaseGroup { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public MovieResource Movie { get; set; }
|
||||
|
||||
|
||||
public string Edition { get; set; }
|
||||
public Core.MediaFiles.MediaInfo.MediaInfoModel MediaInfo { get; set; }
|
||||
|
||||
//TODO: Add series statistics as a property of the series (instead of individual properties)
|
||||
}
|
||||
@@ -63,7 +63,8 @@ namespace NzbDrone.Api.Movie
|
||||
ReleaseGroup = model.ReleaseGroup,
|
||||
Quality = model.Quality,
|
||||
Movie = movie,
|
||||
|
||||
MediaInfo = model.MediaInfo,
|
||||
Edition = model.Edition
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Api.Movie
|
||||
IHandle<MediaCoversUpdatedEvent>
|
||||
|
||||
{
|
||||
private readonly IMovieService _moviesService;
|
||||
protected readonly IMovieService _moviesService;
|
||||
private readonly IMovieStatisticsService _moviesStatisticsService;
|
||||
private readonly IMapCoversToLocal _coverMapper;
|
||||
|
||||
@@ -78,13 +78,33 @@ namespace NzbDrone.Api.Movie
|
||||
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
||||
}
|
||||
|
||||
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMovieService moviesService,
|
||||
IMovieStatisticsService moviesStatisticsService,
|
||||
ISceneMappingService sceneMappingService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
string resource)
|
||||
: base(signalRBroadcaster, resource)
|
||||
{
|
||||
_moviesService = moviesService;
|
||||
_moviesStatisticsService = moviesStatisticsService;
|
||||
|
||||
_coverMapper = coverMapper;
|
||||
|
||||
GetResourceAll = AllMovie;
|
||||
GetResourceById = GetMovie;
|
||||
CreateResource = AddMovie;
|
||||
UpdateResource = UpdateMovie;
|
||||
DeleteResource = DeleteMovie;
|
||||
}
|
||||
|
||||
private MovieResource GetMovie(int id)
|
||||
{
|
||||
var movies = _moviesService.GetMovie(id);
|
||||
return MapToResource(movies);
|
||||
}
|
||||
|
||||
private MovieResource MapToResource(Core.Tv.Movie movies)
|
||||
protected MovieResource MapToResource(Core.Tv.Movie movies)
|
||||
{
|
||||
if (movies == null) return null;
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace NzbDrone.Api.Movie
|
||||
public string RemotePoster { get; set; }
|
||||
public int Year { get; set; }
|
||||
public bool HasFile { get; set; }
|
||||
public string YouTubeTrailerId { get; set; }
|
||||
public string Studio { get; set; }
|
||||
|
||||
//View & Edit
|
||||
public string Path { get; set; }
|
||||
@@ -144,7 +146,9 @@ namespace NzbDrone.Api.Movie
|
||||
AddOptions = model.AddOptions,
|
||||
AlternativeTitles = model.AlternativeTitles,
|
||||
Ratings = model.Ratings,
|
||||
MovieFile = movieFile
|
||||
MovieFile = movieFile,
|
||||
YouTubeTrailerId = model.YouTubeTrailerId,
|
||||
Studio = model.Studio
|
||||
};
|
||||
}
|
||||
|
||||
@@ -191,7 +195,9 @@ namespace NzbDrone.Api.Movie
|
||||
Added = resource.Added,
|
||||
AddOptions = resource.AddOptions,
|
||||
AlternativeTitles = resource.AlternativeTitles,
|
||||
Ratings = resource.Ratings
|
||||
Ratings = resource.Ratings,
|
||||
YouTubeTrailerId = resource.YouTubeTrailerId,
|
||||
Studio = resource.Studio
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
|
||||
Subject.Definition = new DownloadClientDefinition();
|
||||
Subject.Definition.Settings = new DelugeSettings()
|
||||
{
|
||||
TvCategory = null
|
||||
MovieCategory = null
|
||||
};
|
||||
|
||||
_queued = new DelugeTorrent
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
Port = 2222,
|
||||
Username = "admin",
|
||||
Password = "pass",
|
||||
TvCategory = "tv"
|
||||
MovieCategory = "movies-radarr"
|
||||
};
|
||||
|
||||
Mocker.GetMock<ITorrentFileInfoReader>()
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
|
||||
Subject.Definition = new DownloadClientDefinition();
|
||||
Subject.Definition.Settings = new RTorrentSettings()
|
||||
{
|
||||
TvCategory = null
|
||||
MovieCategory = null
|
||||
};
|
||||
|
||||
_downloading = new RTorrentTorrent
|
||||
|
||||
@@ -112,12 +112,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
|
||||
protected void GivenTvCategory()
|
||||
{
|
||||
_settings.TvCategory = "sonarr";
|
||||
_settings.MovieCategory = "radarr";
|
||||
}
|
||||
|
||||
protected void GivenTvDirectory()
|
||||
{
|
||||
_settings.TvDirectory = @"C:/Downloads/Finished/sonarr";
|
||||
_settings.MovieDirectory = @"C:/Downloads/Finished/radarr";
|
||||
}
|
||||
|
||||
protected void GivenFailedDownload()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,14 +42,14 @@ namespace NzbDrone.Core.Test.IndexerTests.OmgwtfnzbsTests
|
||||
|
||||
var releaseInfo = releases.First();
|
||||
|
||||
releaseInfo.Title.Should().Be("Stephen.Fry.Gadget.Man.S01E05.HDTV.x264-C4TV");
|
||||
releaseInfo.Title.Should().Be("Un.Petit.Boulot.2016.FRENCH.720p.BluRay.DTS.x264-LOST");
|
||||
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
|
||||
releaseInfo.DownloadUrl.Should().Be("http://api.omgwtfnzbs.org/sn.php?id=OAl4g&user=nzbdrone&api=nzbdrone");
|
||||
releaseInfo.InfoUrl.Should().Be("http://omgwtfnzbs.org/details.php?id=OAl4g");
|
||||
releaseInfo.DownloadUrl.Should().Be("https://api.omgwtfnzbs.me/nzb/?id=8a2Bw&user=nzbdrone&api=nzbdrone");
|
||||
releaseInfo.InfoUrl.Should().Be("https://omgwtfnzbs.me/details.php?id=8a2Bw");
|
||||
releaseInfo.CommentUrl.Should().BeNullOrEmpty();
|
||||
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
|
||||
releaseInfo.PublishDate.Should().Be(DateTime.Parse("2012/12/17 23:30:13"));
|
||||
releaseInfo.Size.Should().Be(236822906);
|
||||
releaseInfo.PublishDate.Should().Be(DateTime.Parse("2017/01/09 00:16:54"));
|
||||
releaseInfo.Size.Should().Be(5354909355);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(116)]
|
||||
public class update_movie_sorttitle_again : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(SetSortTitles);
|
||||
}
|
||||
|
||||
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand getSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
getSeriesCmd.Transaction = tran;
|
||||
getSeriesCmd.CommandText = @"SELECT Id, Title FROM Movies";
|
||||
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (seriesReader.Read())
|
||||
{
|
||||
var id = seriesReader.GetInt32(0);
|
||||
var title = seriesReader.GetString(1);
|
||||
|
||||
var sortTitle = Parser.Parser.NormalizeTitle(title).ToLower();
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "UPDATE Movies SET SortTitle = ? WHERE Id = ?";
|
||||
updateCmd.AddParameter(sortTitle);
|
||||
updateCmd.AddParameter(id);
|
||||
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(117)]
|
||||
public class update_movie_file : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Create.Column("Edition").OnTable("MovieFiles").AsString().Nullable();
|
||||
//Execute.WithConnection(SetSortTitles);
|
||||
}
|
||||
|
||||
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand getSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
getSeriesCmd.Transaction = tran;
|
||||
getSeriesCmd.CommandText = @"SELECT Id, RelativePath FROM MovieFiles";
|
||||
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (seriesReader.Read())
|
||||
{
|
||||
var id = seriesReader.GetInt32(0);
|
||||
var relativePath = seriesReader.GetString(1);
|
||||
|
||||
var result = Parser.Parser.ParseMovieTitle(relativePath);
|
||||
|
||||
var edition = "";
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
edition = Parser.Parser.ParseMovieTitle(relativePath).Edition;
|
||||
}
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "UPDATE MovieFiles SET Edition = ? WHERE Id = ?";
|
||||
updateCmd.AddParameter(edition);
|
||||
updateCmd.AddParameter(id);
|
||||
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(118)]
|
||||
public class update_movie_slug : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(SetTitleSlug);
|
||||
}
|
||||
|
||||
private void SetTitleSlug(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand getSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
getSeriesCmd.Transaction = tran;
|
||||
getSeriesCmd.CommandText = @"SELECT Id, Title, Year FROM Movies";
|
||||
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (seriesReader.Read())
|
||||
{
|
||||
var id = seriesReader.GetInt32(0);
|
||||
var title = seriesReader.GetString(1);
|
||||
var year = seriesReader.GetInt32(2);
|
||||
|
||||
var titleSlug = ToUrlSlug(title + "-" + year);
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "UPDATE Movies SET TitleSlug = ? WHERE Id = ?";
|
||||
updateCmd.AddParameter(titleSlug);
|
||||
updateCmd.AddParameter(id);
|
||||
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToUrlSlug(string value)
|
||||
{
|
||||
//First to lower case
|
||||
value = value.ToLowerInvariant();
|
||||
|
||||
//Remove all accents
|
||||
var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(value);
|
||||
value = Encoding.ASCII.GetString(bytes);
|
||||
|
||||
//Replace spaces
|
||||
value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled);
|
||||
|
||||
//Remove invalid chars
|
||||
value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled);
|
||||
|
||||
//Trim dashes from end
|
||||
value = value.Trim('-', '_');
|
||||
|
||||
//Replace double occurences of - or _
|
||||
value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Data;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(119)]
|
||||
public class add_youtube_trailer_id : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Movies").AddColumn("YouTubeTrailerId").AsString().Nullable();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Data;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(120)]
|
||||
public class add_studio : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Movies").AddColumn("Studio").AsString().Nullable();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(121)]
|
||||
public class update_filedate_config : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(SetTitleSlug);
|
||||
}
|
||||
|
||||
private void SetTitleSlug(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand getSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
getSeriesCmd.Transaction = tran;
|
||||
getSeriesCmd.CommandText = @"SELECT Id, Value FROM Config WHERE Key = 'filedate'";
|
||||
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (seriesReader.Read())
|
||||
{
|
||||
var id = seriesReader.GetInt32(0);
|
||||
var value = seriesReader.GetString(1);
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "UPDATE Config SET Value = 'Release' WHERE Id = ?";
|
||||
updateCmd.AddParameter(id);
|
||||
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToUrlSlug(string value)
|
||||
{
|
||||
//First to lower case
|
||||
value = value.ToLowerInvariant();
|
||||
|
||||
//Remove all accents
|
||||
var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(value);
|
||||
value = Encoding.ASCII.GetString(bytes);
|
||||
|
||||
//Replace spaces
|
||||
value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled);
|
||||
|
||||
//Remove invalid chars
|
||||
value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled);
|
||||
|
||||
//Trim dashes from end
|
||||
value = value.Trim('-', '_');
|
||||
|
||||
//Replace double occurences of - or _
|
||||
value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
|
||||
public List<DownloadDecision> GetRssDecision(List<ReleaseInfo> reports)
|
||||
{
|
||||
return GetDecisions(reports).ToList();
|
||||
return GetMovieDecisions(reports).ToList();
|
||||
}
|
||||
|
||||
public List<DownloadDecision> GetSearchDecision(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteriaBase)
|
||||
@@ -83,7 +83,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
if (parsedEpisodeInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
remoteEpisode.DownloadAllowed = false;
|
||||
remoteEpisode.DownloadAllowed = true;
|
||||
decision = new DownloadDecision(remoteEpisode, new Rejection("Hardcoded subs found: " + parsedEpisodeInfo.Quality.HardcodedSubs));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, remoteEpisode.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
|
||||
return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteEpisode.ParsedMovieInfo.Quality);
|
||||
}
|
||||
|
||||
_logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedMovieInfo.Quality);
|
||||
|
||||
@@ -31,25 +31,17 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink)
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
|
||||
{
|
||||
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
|
||||
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
if (!Settings.MovieCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
_proxy.SetLabel(actualHash, Settings.MovieCategory, Settings);
|
||||
}
|
||||
|
||||
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||
|
||||
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)DelugePriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(actualHash, Settings);
|
||||
}*/
|
||||
|
||||
return actualHash.ToUpper();
|
||||
}
|
||||
|
||||
@@ -57,66 +49,24 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
{
|
||||
var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
if (!Settings.MovieCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
_proxy.SetLabel(actualHash, Settings.MovieCategory, Settings);
|
||||
}
|
||||
|
||||
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||
|
||||
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)DelugePriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(actualHash, Settings);
|
||||
}*/
|
||||
|
||||
return actualHash.ToUpper();
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
|
||||
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
}
|
||||
|
||||
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)DelugePriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(actualHash, Settings);
|
||||
}
|
||||
|
||||
return actualHash.ToUpper();
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||
{
|
||||
var actualHash = _proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
}
|
||||
|
||||
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)DelugePriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(actualHash, Settings);
|
||||
}
|
||||
|
||||
return actualHash.ToUpper();
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
public override string Name => "Deluge";
|
||||
@@ -127,9 +77,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
|
||||
try
|
||||
{
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
if (!Settings.MovieCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings);
|
||||
torrents = _proxy.GetTorrentsByLabel(Settings.MovieCategory, Settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -149,7 +99,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
var item = new DownloadClientItem();
|
||||
item.DownloadId = torrent.Hash.ToUpper();
|
||||
item.Title = torrent.Name;
|
||||
item.Category = Settings.TvCategory;
|
||||
item.Category = Settings.MovieCategory;
|
||||
|
||||
item.DownloadClient = Definition.Name;
|
||||
|
||||
@@ -280,7 +230,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
|
||||
private ValidationFailure TestCategory()
|
||||
{
|
||||
if (Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
if (Settings.MovieCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -297,14 +247,14 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
|
||||
var labels = _proxy.GetAvailableLabels(Settings);
|
||||
|
||||
if (!labels.Contains(Settings.TvCategory))
|
||||
if (!labels.Contains(Settings.MovieCategory))
|
||||
{
|
||||
_proxy.AddLabel(Settings.TvCategory, Settings);
|
||||
_proxy.AddLabel(Settings.MovieCategory, Settings);
|
||||
labels = _proxy.GetAvailableLabels(Settings);
|
||||
|
||||
if (!labels.Contains(Settings.TvCategory))
|
||||
if (!labels.Contains(Settings.MovieCategory))
|
||||
{
|
||||
return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed")
|
||||
return new NzbDroneValidationFailure("MovieCategory", "Configuration of label failed")
|
||||
{
|
||||
DetailedDescription = "Radarr as unable to add the label to Deluge."
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
RuleFor(c => c.Host).ValidHost();
|
||||
RuleFor(c => c.Port).GreaterThan(0);
|
||||
|
||||
RuleFor(c => c.TvCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -");
|
||||
RuleFor(c => c.MovieCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
Host = "localhost";
|
||||
Port = 8112;
|
||||
Password = "deluge";
|
||||
TvCategory = "movie-radarr";
|
||||
MovieCategory = "movie-radarr";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||
@@ -41,15 +41,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
public string TvCategory { get; set; }
|
||||
public string MovieCategory { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||
public int RecentTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(5, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -33,81 +33,35 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
||||
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
|
||||
}
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
|
||||
}
|
||||
|
||||
return hash;
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, Byte[] fileContent)
|
||||
{
|
||||
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
|
||||
}
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
|
||||
}
|
||||
|
||||
return hash;
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink)
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
||||
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
|
||||
_proxy.SetTorrentLabel(hash.ToLower(), Settings.MovieCategory, Settings);
|
||||
}
|
||||
|
||||
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
|
||||
}*/ //TODO: Maybe reimplement for movies
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteMovie remoteEpisode, string hash, string filename, Byte[] fileContent)
|
||||
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, Byte[] fileContent)
|
||||
{
|
||||
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
|
||||
_proxy.SetTorrentLabel(hash.ToLower(), Settings.MovieCategory, Settings);
|
||||
}
|
||||
|
||||
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)QBittorrentPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash.ToLower(), Settings);
|
||||
}*/
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -236,7 +190,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
else if (version < 6)
|
||||
{
|
||||
// API version 6 introduced support for labels
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return new NzbDroneValidationFailure("Category", "Category is not supported")
|
||||
{
|
||||
@@ -244,7 +198,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
else if (Settings.MovieCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
// warn if labels are supported, but category is not provided
|
||||
return new NzbDroneValidationFailure("TvCategory", "Category is recommended")
|
||||
|
||||
@@ -58,8 +58,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
public List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings).Resource("/query/torrents")
|
||||
.AddQueryParam("label", settings.TvCategory)
|
||||
.AddQueryParam("category", settings.TvCategory);
|
||||
.AddQueryParam("label", settings.MovieCategory)
|
||||
.AddQueryParam("category", settings.MovieCategory);
|
||||
|
||||
var response = ProcessRequest<List<QBittorrentTorrent>>(request, settings);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
{
|
||||
Host = "localhost";
|
||||
Port = 9091;
|
||||
TvCategory = "movie-radarr";
|
||||
MovieCategory = "movie-radarr";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||
@@ -38,16 +38,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
public string TvCategory { get; set; }
|
||||
public string MovieCategory { get; set; }
|
||||
|
||||
//Todo: update this shit.
|
||||
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||
public int RecentTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")]
|
||||
[FieldDefinition(5, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -30,13 +30,13 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
}
|
||||
|
||||
// patch can be a number (releases) or 'x' (git)
|
||||
private static readonly Regex VersionRegex = new Regex(@"(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+|x)(?<candidate>.*)", RegexOptions.Compiled);
|
||||
private static readonly Regex VersionRegex = new Regex(@"(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+|x)", RegexOptions.Compiled);
|
||||
|
||||
protected override string AddFromNzbFile(RemoteEpisode remoteEpisode, string filename, byte[] fileContents)
|
||||
{
|
||||
var category = Settings.TvCategory;
|
||||
var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority;
|
||||
|
||||
|
||||
var response = _proxy.DownloadNzb(fileContents, filename, category, priority, Settings);
|
||||
|
||||
if (response != null && response.Ids.Any())
|
||||
@@ -284,110 +284,103 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
failures.AddIfNotNull(TestCategory());
|
||||
}
|
||||
|
||||
private bool HasVersion(int major, int minor, int patch = 0, string candidate = null)
|
||||
private bool HasVersion(int major, int minor, int patch = 0)
|
||||
{
|
||||
candidate = candidate ?? string.Empty;
|
||||
var rawVersion = _proxy.GetVersion(Settings);
|
||||
var version = ParseVersion(rawVersion);
|
||||
|
||||
var version = _proxy.GetVersion(Settings);
|
||||
if (version == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version.Major > major)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (version.Major < major)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version.Minor > minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (version.Minor < minor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version.Build > patch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (version.Build < patch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Version ParseVersion(string version)
|
||||
{
|
||||
var parsed = VersionRegex.Match(version);
|
||||
|
||||
int actualMajor;
|
||||
int actualMinor;
|
||||
int actualPatch;
|
||||
string actualCandidate;
|
||||
int major;
|
||||
int minor;
|
||||
int patch;
|
||||
|
||||
if (!parsed.Success)
|
||||
if (parsed.Success)
|
||||
{
|
||||
major = Convert.ToInt32(parsed.Groups["major"].Value);
|
||||
minor = Convert.ToInt32(parsed.Groups["minor"].Value);
|
||||
patch = Convert.ToInt32(parsed.Groups["patch"].Value.Replace("x", "0"));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!version.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
actualMajor = 1;
|
||||
actualMinor = 1;
|
||||
actualPatch = 0;
|
||||
actualCandidate = null;
|
||||
major = 1;
|
||||
minor = 1;
|
||||
patch = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
actualMajor = Convert.ToInt32(parsed.Groups["major"].Value);
|
||||
actualMinor = Convert.ToInt32(parsed.Groups["minor"].Value);
|
||||
actualPatch = Convert.ToInt32(parsed.Groups["patch"].Value.Replace("x", ""));
|
||||
actualCandidate = parsed.Groups["candidate"].Value.ToUpper();
|
||||
}
|
||||
|
||||
if (actualMajor > major)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (actualMajor < major)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualMinor > minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (actualMinor < minor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualPatch > patch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (actualPatch < patch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualCandidate.IsNullOrWhiteSpace())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (candidate.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return actualCandidate.CompareTo(candidate) > 0;
|
||||
}
|
||||
return new Version(major, minor, patch);
|
||||
}
|
||||
|
||||
private ValidationFailure TestConnectionAndVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
var version = _proxy.GetVersion(Settings);
|
||||
var parsed = VersionRegex.Match(version);
|
||||
var rawVersion = _proxy.GetVersion(Settings);
|
||||
var version = ParseVersion(rawVersion);
|
||||
|
||||
if (!parsed.Success)
|
||||
if (version == null)
|
||||
{
|
||||
if (version.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 1.1.0 or higher.")
|
||||
{
|
||||
IsWarning = true,
|
||||
DetailedDescription = "Radarr may not be able to support new features added to SABnzbd when running develop versions."
|
||||
};
|
||||
}
|
||||
|
||||
return new ValidationFailure("Version", "Unknown Version: " + version);
|
||||
}
|
||||
|
||||
var major = Convert.ToInt32(parsed.Groups["major"].Value);
|
||||
var minor = Convert.ToInt32(parsed.Groups["minor"].Value);
|
||||
if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 1.1.0 or higher.")
|
||||
{
|
||||
IsWarning = true,
|
||||
DetailedDescription = "Radarr may not be able to support new features added to SABnzbd when running develop versions."
|
||||
};
|
||||
}
|
||||
|
||||
if (major >= 1)
|
||||
if (version.Major >= 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (minor >= 7)
|
||||
if (version.Minor >= 7)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -54,21 +54,21 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
var outputPath = new OsPath(torrent.DownloadDir);
|
||||
|
||||
if (Settings.TvDirectory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
if (!new OsPath(Settings.TvDirectory).Contains(outputPath)) continue;
|
||||
if (!new OsPath(Settings.MovieDirectory).Contains(outputPath)) continue;
|
||||
}
|
||||
else if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
else if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var directories = outputPath.FullPath.Split('\\', '/');
|
||||
if (!directories.Contains(Settings.TvCategory)) continue;
|
||||
if (!directories.Contains(Settings.MovieCategory)) continue;
|
||||
}
|
||||
|
||||
outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath);
|
||||
|
||||
var item = new DownloadClientItem();
|
||||
item.DownloadId = torrent.HashString.ToUpper();
|
||||
item.Category = Settings.TvCategory;
|
||||
item.Category = Settings.MovieCategory;
|
||||
item.Title = torrent.Name;
|
||||
|
||||
item.DownloadClient = Definition.Name;
|
||||
@@ -123,9 +123,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
var destDir = config.GetValueOrDefault("download-dir") as string;
|
||||
|
||||
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
destDir = string.Format("{0}/.{1}", destDir, Settings.TvCategory);
|
||||
destDir = string.Format("{0}/.{1}", destDir, Settings.MovieCategory);
|
||||
}
|
||||
|
||||
return new DownloadClientStatus
|
||||
@@ -137,56 +137,23 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings);
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)TransmissionPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash, Settings);
|
||||
}
|
||||
|
||||
return hash;
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||
{
|
||||
_proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings);
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)TransmissionPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash, Settings);
|
||||
}
|
||||
|
||||
return hash;
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings);
|
||||
if (remoteMovie.Release.Age < 14 && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
|
||||
remoteMovie.Release.Age > 14 && Settings.OlderTvPriority == (int)TransmissionPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash, Settings);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string hash, string filename, byte[] fileContent)
|
||||
{
|
||||
_proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings);
|
||||
|
||||
if (remoteMovie.Release.Age < 14 && Settings.RecentTvPriority == (int)TransmissionPriority.First ||
|
||||
remoteMovie.Release.Age > 14 && Settings.OlderTvPriority == (int)TransmissionPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash, Settings);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -204,16 +171,16 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
protected string GetDownloadDirectory()
|
||||
{
|
||||
if (Settings.TvDirectory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return Settings.TvDirectory;
|
||||
return Settings.MovieDirectory;
|
||||
}
|
||||
else if (Settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
else if (Settings.MovieCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
var destDir = (string)config.GetValueOrDefault("download-dir");
|
||||
|
||||
return string.Format("{0}/{1}", destDir.TrimEnd('/'), Settings.TvCategory);
|
||||
return string.Format("{0}/{1}", destDir.TrimEnd('/'), Settings.MovieCategory);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
RuleFor(c => c.UrlBase).ValidUrlBase();
|
||||
|
||||
RuleFor(c => c.TvCategory).Matches(@"^\.?[-a-z]*$", RegexOptions.IgnoreCase).WithMessage("Allowed characters a-z and -");
|
||||
RuleFor(c => c.MovieCategory).Matches(@"^\.?[-a-z]*$", RegexOptions.IgnoreCase).WithMessage("Allowed characters a-z and -");
|
||||
|
||||
RuleFor(c => c.TvCategory).Empty()
|
||||
.When(c => c.TvDirectory.IsNotNullOrWhiteSpace())
|
||||
RuleFor(c => c.MovieCategory).Empty()
|
||||
.When(c => c.MovieDirectory.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Cannot use Category and Directory");
|
||||
}
|
||||
}
|
||||
@@ -51,18 +51,12 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
|
||||
public string TvCategory { get; set; }
|
||||
public string MovieCategory { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")]
|
||||
public string TvDirectory { get; set; }
|
||||
public string MovieDirectory { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that we're released within the last 14 days")]
|
||||
public int RecentTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing movies that we're released over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -37,51 +37,23 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
_rTorrentDirectoryValidator = rTorrentDirectoryValidator;
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||
{
|
||||
throw new NotImplementedException("Episodes are not working with Radarr");
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
||||
|
||||
// Download the magnet to the appropriate directory.
|
||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||
//SetPriority(remoteEpisode, hash);
|
||||
_proxy.SetTorrentLabel(hash, Settings.MovieCategory, Settings);
|
||||
SetDownloadDirectory(hash);
|
||||
|
||||
// Once the magnet meta download finishes, rTorrent replaces it with the actual torrent download with default settings.
|
||||
// Schedule an event to apply the appropriate settings when that happens.
|
||||
// var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||
//_proxy.SetDeferredMagnetProperties(hash, Settings.TvCategory, Settings.TvDirectory, priority, Settings);
|
||||
|
||||
_proxy.StartTorrent(hash, Settings);
|
||||
|
||||
// Wait for the magnet to be resolved.
|
||||
var tries = 10;
|
||||
var retryDelay = 500;
|
||||
if (WaitForTorrent(hash, tries, retryDelay))
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn("rTorrent could not resolve magnet within {0} seconds, download may remain stuck: {1}.", tries * retryDelay / 1000, magnetLink);
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
||||
|
||||
// Download the magnet to the appropriate directory.
|
||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||
SetPriority(remoteEpisode, hash);
|
||||
SetDownloadDirectory(hash);
|
||||
|
||||
// Once the magnet meta download finishes, rTorrent replaces it with the actual torrent download with default settings.
|
||||
// Schedule an event to apply the appropriate settings when that happens.
|
||||
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||
_proxy.SetDeferredMagnetProperties(hash, Settings.TvCategory, Settings.TvDirectory, priority, Settings);
|
||||
|
||||
_proxy.StartTorrent(hash, Settings);
|
||||
|
||||
// Wait for the magnet to be resolved.
|
||||
@@ -107,13 +79,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
var retryDelay = 100;
|
||||
if (WaitForTorrent(hash, tries, retryDelay))
|
||||
{
|
||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||
|
||||
//SetPriority(remoteEpisode, hash);
|
||||
_proxy.SetTorrentLabel(hash, Settings.MovieCategory, Settings);
|
||||
SetDownloadDirectory(hash);
|
||||
|
||||
_proxy.StartTorrent(hash, Settings);
|
||||
|
||||
return hash;
|
||||
}
|
||||
else
|
||||
@@ -125,32 +93,6 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
}
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||
{
|
||||
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||
|
||||
var tries = 2;
|
||||
var retryDelay = 100;
|
||||
if (WaitForTorrent(hash, tries, retryDelay))
|
||||
{
|
||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||
|
||||
SetPriority(remoteEpisode, hash);
|
||||
SetDownloadDirectory(hash);
|
||||
|
||||
_proxy.StartTorrent(hash, Settings);
|
||||
|
||||
return hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("rTorrent could not add file");
|
||||
|
||||
RemoveItem(hash, true);
|
||||
throw new ReleaseDownloadException(remoteEpisode.Release, "Downloading torrent failed");
|
||||
}
|
||||
}
|
||||
|
||||
public override string Name => "rTorrent";
|
||||
|
||||
public override ProviderMessage Message => new ProviderMessage("Radarr is unable to remove torrents that have finished seeding when using rTorrent", ProviderMessageType.Warning);
|
||||
@@ -167,7 +109,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
foreach (RTorrentTorrent torrent in torrents)
|
||||
{
|
||||
// Don't concern ourselves with categories other than specified
|
||||
if (torrent.Category != Settings.TvCategory) continue;
|
||||
if (torrent.Category != Settings.MovieCategory) continue;
|
||||
|
||||
if (torrent.Path.StartsWith("."))
|
||||
{
|
||||
@@ -287,17 +229,11 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
return result.Errors.First();
|
||||
}
|
||||
|
||||
private void SetPriority(RemoteEpisode remoteEpisode, string hash)
|
||||
{
|
||||
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||
_proxy.SetTorrentPriority(hash, priority, Settings);
|
||||
}
|
||||
|
||||
private void SetDownloadDirectory(string hash)
|
||||
{
|
||||
if (Settings.TvDirectory.IsNotNullOrWhiteSpace())
|
||||
if (Settings.MovieDirectory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetTorrentDownloadDirectory(hash, Settings.TvDirectory, Settings);
|
||||
_proxy.SetTorrentDownloadDirectory(hash, Settings.MovieDirectory, Settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ namespace NzbDrone.Core.Download.Clients.rTorrent
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator)
|
||||
{
|
||||
RuleFor(c => c.TvDirectory).Cascade(CascadeMode.StopOnFirstFailure)
|
||||
RuleFor(c => c.MovieDirectory).Cascade(CascadeMode.StopOnFirstFailure)
|
||||
.IsValidPath()
|
||||
.SetValidator(rootFolderValidator)
|
||||
.SetValidator(droneFactoryValidator)
|
||||
.SetValidator(mappedNetworkDriveValidator)
|
||||
.SetValidator(pathExistsValidator)
|
||||
.When(c => c.TvDirectory.IsNotNullOrWhiteSpace())
|
||||
.When(c => c.MovieDirectory.IsNotNullOrWhiteSpace())
|
||||
.When(c => c.Host == "localhost" || c.Host == "127.0.0.1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
{
|
||||
RuleFor(c => c.Host).ValidHost();
|
||||
RuleFor(c => c.Port).InclusiveBetween(0, 65535);
|
||||
RuleFor(c => c.TvCategory).NotEmpty()
|
||||
RuleFor(c => c.MovieCategory).NotEmpty()
|
||||
.WithMessage("A category is recommended")
|
||||
.AsWarning();
|
||||
}
|
||||
@@ -26,9 +26,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
Host = "localhost";
|
||||
Port = 8080;
|
||||
UrlBase = "RPC2";
|
||||
TvCategory = "movies-radarr";
|
||||
OlderTvPriority = (int)RTorrentPriority.Normal;
|
||||
RecentTvPriority = (int)RTorrentPriority.Normal;
|
||||
MovieCategory = "movies-radarr";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||
@@ -50,16 +48,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional.")]
|
||||
public string TvCategory { get; set; }
|
||||
public string MovieCategory { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")]
|
||||
public string TvDirectory { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||
public int RecentTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
public string MovieDirectory { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
|
||||
@@ -68,6 +68,38 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
return hash;
|
||||
}
|
||||
|
||||
protected override string AddFromMagnetLink(RemoteMovie remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||
|
||||
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)UTorrentPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash, Settings);
|
||||
}*/
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteMovie remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||
{
|
||||
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||
|
||||
/*var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First ||
|
||||
!isRecentEpisode && Settings.OlderTvPriority == (int)UTorrentPriority.First)
|
||||
{
|
||||
_proxy.MoveTorrentToTopInQueue(hash, Settings);
|
||||
}*/
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override string Name => "uTorrent";
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
|
||||
@@ -94,34 +94,18 @@ namespace NzbDrone.Core.Download
|
||||
return;
|
||||
}
|
||||
|
||||
var series = _parsingService.GetSeries(trackedDownload.DownloadItem.Title);
|
||||
|
||||
if (series == null)
|
||||
var movie = _parsingService.GetMovie(trackedDownload.DownloadItem.Title);
|
||||
if (movie == null)
|
||||
{
|
||||
if (historyItem != null)
|
||||
{
|
||||
//series = _seriesService.GetSeries(historyItem.SeriesId);
|
||||
movie = _movieService.GetMovie(historyItem.MovieId);
|
||||
}
|
||||
|
||||
if (series == null)
|
||||
if (movie == null)
|
||||
{
|
||||
var movie = _parsingService.GetMovie(trackedDownload.DownloadItem.Title);
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
if (historyItem != null)
|
||||
{
|
||||
movie = _movieService.GetMovie(historyItem.MovieId);
|
||||
}
|
||||
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
trackedDownload.Warn("Movie title mismatch, automatic import is not possible.");
|
||||
}
|
||||
}
|
||||
//trackedDownload.Warn("Series title mismatch, automatic import is not possible.");
|
||||
//return;
|
||||
trackedDownload.Warn("Movie title mismatch, automatic import is not possible.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,61 +116,30 @@ namespace NzbDrone.Core.Download
|
||||
private void Import(TrackedDownload trackedDownload)
|
||||
{
|
||||
var outputPath = trackedDownload.DownloadItem.OutputPath.FullPath;
|
||||
if (trackedDownload.RemoteMovie.Movie != null)
|
||||
var importResults = _downloadedMovieImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteMovie.Movie, trackedDownload.DownloadItem);
|
||||
|
||||
if (importResults.Empty())
|
||||
{
|
||||
var importResults = _downloadedMovieImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteMovie.Movie, trackedDownload.DownloadItem);
|
||||
|
||||
if (importResults.Empty())
|
||||
{
|
||||
trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResults.Count(c => c.Result == ImportResultType.Imported) >= 1)
|
||||
{
|
||||
trackedDownload.State = TrackedDownloadStage.Imported;
|
||||
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResults.Any(c => c.Result != ImportResultType.Imported))
|
||||
{
|
||||
var statusMessages = importResults
|
||||
.Where(v => v.Result != ImportResultType.Imported)
|
||||
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), v.Errors))
|
||||
.ToArray();
|
||||
|
||||
trackedDownload.Warn(statusMessages);
|
||||
}
|
||||
}
|
||||
else if (trackedDownload.RemoteEpisode.Series != null)
|
||||
{
|
||||
var importResults = _downloadedEpisodesImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteEpisode.Series, trackedDownload.DownloadItem);
|
||||
|
||||
if (importResults.Empty())
|
||||
{
|
||||
trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResults.Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count))
|
||||
{
|
||||
trackedDownload.State = TrackedDownloadStage.Imported;
|
||||
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResults.Any(c => c.Result != ImportResultType.Imported))
|
||||
{
|
||||
var statusMessages = importResults
|
||||
.Where(v => v.Result != ImportResultType.Imported)
|
||||
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), v.Errors))
|
||||
.ToArray();
|
||||
|
||||
trackedDownload.Warn(statusMessages);
|
||||
}
|
||||
trackedDownload.Warn("No files found are eligible for import in {0}", outputPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResults.Count(c => c.Result == ImportResultType.Imported) >= 1)
|
||||
{
|
||||
trackedDownload.State = TrackedDownloadStage.Imported;
|
||||
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResults.Any(c => c.Result != ImportResultType.Imported))
|
||||
{
|
||||
var statusMessages = importResults
|
||||
.Where(v => v.Result != ImportResultType.Imported)
|
||||
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalMovie.Path), v.Errors))
|
||||
.ToArray();
|
||||
|
||||
trackedDownload.Warn(statusMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
||||
public void Clean()
|
||||
{
|
||||
CleanupOrphanedBySeries();
|
||||
CleanupOrphanedByEpisode();
|
||||
//CleanupOrphanedBySeries();
|
||||
//CleanupOrphanedByEpisode();
|
||||
CleanupOrphanedByMovie();
|
||||
}
|
||||
|
||||
private void CleanupOrphanedBySeries()
|
||||
@@ -29,6 +30,18 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
WHERE Series.Id IS NULL)");
|
||||
}
|
||||
|
||||
private void CleanupOrphanedByMovie()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM History
|
||||
WHERE Id IN (
|
||||
SELECT History.Id FROM History
|
||||
LEFT OUTER JOIN Movies
|
||||
ON History.MovieId = Movies.Id
|
||||
WHERE Movies.Id IS NULL)");
|
||||
}
|
||||
|
||||
private void CleanupOrphanedByEpisode()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
30
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHD.cs
Normal file
30
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHD.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
{
|
||||
public class AwesomeHD : HttpIndexerBase<AwesomeHDSettings>
|
||||
{
|
||||
public override string Name => "AwesomeHD";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
public override int PageSize => 50;
|
||||
|
||||
public AwesomeHD(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
|
||||
: base(httpClient, indexerStatusService, configService, parsingService, logger)
|
||||
{ }
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AwesomeHDRequestGenerator() { Settings = Settings };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AwesomeHDRssParser(Settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDApi.cs
Normal file
80
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDApi.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using System.Xml.Serialization;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
{
|
||||
public class Torrent
|
||||
{
|
||||
[XmlElement(ElementName = "id")]
|
||||
public string Id { get; set; }
|
||||
[XmlElement(ElementName = "groupid")]
|
||||
public string GroupId { get; set; }
|
||||
[XmlElement(ElementName = "time")]
|
||||
public DateTime Time { get; set; }
|
||||
[XmlElement(ElementName = "userid")]
|
||||
public string Userid { get; set; }
|
||||
[XmlElement(ElementName = "size")]
|
||||
public long Size { get; set; }
|
||||
[XmlElement(ElementName = "snatched")]
|
||||
public string Snatched { get; set; }
|
||||
[XmlElement(ElementName = "seeders")]
|
||||
public string Seeders { get; set; }
|
||||
[XmlElement(ElementName = "leechers")]
|
||||
public string Leechers { get; set; }
|
||||
[XmlElement(ElementName = "releasegroup")]
|
||||
public string Releasegroup { get; set; }
|
||||
[XmlElement(ElementName = "resolution")]
|
||||
public string Resolution { get; set; }
|
||||
[XmlElement(ElementName = "media")]
|
||||
public string Media { get; set; }
|
||||
[XmlElement(ElementName = "format")]
|
||||
public string Format { get; set; }
|
||||
[XmlElement(ElementName = "encoding")]
|
||||
public string Encoding { get; set; }
|
||||
[XmlElement(ElementName = "audioformat")]
|
||||
public string Audioformat { get; set; }
|
||||
[XmlElement(ElementName = "audiobitrate")]
|
||||
public string Audiobitrate { get; set; }
|
||||
[XmlElement(ElementName = "audiochannels")]
|
||||
public string Audiochannels { get; set; }
|
||||
[XmlElement(ElementName = "subtitles")]
|
||||
public string Subtitles { get; set; }
|
||||
[XmlElement(ElementName = "encodestatus")]
|
||||
public string Encodestatus { get; set; }
|
||||
[XmlElement(ElementName = "freeleech")]
|
||||
public string Freeleech { get; set; }
|
||||
[XmlElement(ElementName = "cover")]
|
||||
public string Cover { get; set; }
|
||||
[XmlElement(ElementName = "smallcover")]
|
||||
public string Smallcover { get; set; }
|
||||
[XmlElement(ElementName = "year")]
|
||||
public string Year { get; set; }
|
||||
[XmlElement(ElementName = "name")]
|
||||
public string Name { get; set; }
|
||||
[XmlElement(ElementName = "imdb")]
|
||||
public string Imdb { get; set; }
|
||||
[XmlElement(ElementName = "type")]
|
||||
public string Type { get; set; }
|
||||
[XmlElement(ElementName = "plotoutline")]
|
||||
public string Plotoutline { get; set; }
|
||||
}
|
||||
|
||||
public class SearchResults
|
||||
{
|
||||
[XmlElement(ElementName = "authkey")]
|
||||
public string AuthKey { get; set; }
|
||||
[XmlElement(ElementName = "torrent")]
|
||||
public List<Torrent> Torrent { get; set; }
|
||||
}
|
||||
|
||||
public class AwesomeHDSearchResponse
|
||||
{
|
||||
[XmlElement(ElementName = "?xml")]
|
||||
public string Xml { get; set; }
|
||||
[XmlElement(ElementName = "searchresults")]
|
||||
public SearchResults SearchResults { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
{
|
||||
public class AwesomeHDRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public AwesomeHDSettings Settings { get; set; }
|
||||
|
||||
public virtual IndexerPageableRequestChain GetRecentRequests()
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetRequest(null));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(SingleEpisodeSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(AnimeEpisodeSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(SpecialEpisodeSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
pageableRequests.Add(GetRequest(searchCriteria.Movie.ImdbId));
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
||||
{
|
||||
if (searchParameters != null)
|
||||
{
|
||||
yield return new IndexerRequest(string.Format("{0}/searchapi.php?action=imdbsearch&passkey={1}&imdb={2}", Settings.BaseUrl.Trim().TrimEnd('/'), Settings.Passkey.Trim(), searchParameters), HttpAccept.Rss);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new IndexerRequest(string.Format("{0}/searchapi.php?action=latestmovies&passkey={1}", Settings.BaseUrl.Trim().TrimEnd('/'), Settings.Passkey.Trim()), HttpAccept.Rss);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
92
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRssParser.cs
Normal file
92
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRssParser.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
{
|
||||
public class AwesomeHDRssParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly AwesomeHDSettings _settings;
|
||||
|
||||
public AwesomeHDRssParser(AwesomeHDSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse,
|
||||
"Unexpected response status {0} code from API request",
|
||||
indexerResponse.HttpResponse.StatusCode);
|
||||
}
|
||||
|
||||
// Hacky ¯\_(ツ)_/¯
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(indexerResponse.Content);
|
||||
|
||||
var json = JsonConvert.SerializeXmlNode(doc);
|
||||
|
||||
Console.WriteLine(json);
|
||||
|
||||
var jsonResponse = JsonConvert.DeserializeObject<AwesomeHDSearchResponse>(json);
|
||||
|
||||
if (jsonResponse == null)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, "Unexpected response from request");
|
||||
}
|
||||
|
||||
foreach (var torrent in jsonResponse.SearchResults.Torrent)
|
||||
{
|
||||
var id = torrent.Id;
|
||||
var title = $"{torrent.Name}.{torrent.Year}.{torrent.Resolution}.{torrent.Media}.{torrent.Encoding}.{torrent.Audioformat}-{torrent.Releasegroup}";
|
||||
|
||||
torrentInfos.Add(new TorrentInfo()
|
||||
{
|
||||
Guid = string.Format("AwesomeHD-{0}", id),
|
||||
Title = title,
|
||||
Size = torrent.Size,
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.SearchResults.AuthKey, _settings.Passkey),
|
||||
InfoUrl = GetInfoUrl(torrent.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.Time.ToUniversalTime()
|
||||
});
|
||||
}
|
||||
|
||||
return torrentInfos.OrderByDescending(o => ((dynamic)o).Seeders).ToArray();
|
||||
}
|
||||
|
||||
private string GetDownloadUrl(string torrentId, string authKey, string passKey)
|
||||
{
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/torrents.php")
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("id", torrentId)
|
||||
.AddQueryParam("authkey", authKey)
|
||||
.AddQueryParam("torrent_pass", passKey);
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
|
||||
private string GetInfoUrl(string groupId, string torrentId)
|
||||
{
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/torrents.php")
|
||||
.AddQueryParam("id", groupId)
|
||||
.AddQueryParam("torrentid", torrentId);
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDSettings.cs
Normal file
37
src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDSettings.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.AwesomeHD
|
||||
{
|
||||
public class AwesomeHDSettingsValidator : AbstractValidator<AwesomeHDSettings>
|
||||
{
|
||||
public AwesomeHDSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||
RuleFor(c => c.Passkey).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class AwesomeHDSettings : IProviderConfig
|
||||
{
|
||||
private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator();
|
||||
|
||||
public AwesomeHDSettings()
|
||||
{
|
||||
BaseUrl = "https://awesome-hd.me";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since you Passkey will be sent to that host.")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Passkey")]
|
||||
public string Passkey { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,14 +39,15 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return GetDefinition("Dognzb.cr", GetSettings("https://api.dognzb.cr"));
|
||||
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
|
||||
yield return GetDefinition("DrunkenSlug", GetSettings("https://api.drunkenslug.com"));
|
||||
yield return GetDefinition("Nzb-Tortuga", GetSettings("https://www.nzb-tortuga.com"));
|
||||
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
|
||||
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
|
||||
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws", 5010, 5030, 5040, 5045));
|
||||
yield return GetDefinition("NZBFinder.ws", GetSettings("https://nzbfinder.ws"));
|
||||
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
|
||||
yield return GetDefinition("nzbplanet.net", GetSettings("https://api.nzbplanet.net"));
|
||||
yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org", 2000));
|
||||
yield return GetDefinition("Nzbs.org", GetSettings("http://nzbs.org"));
|
||||
yield return GetDefinition("OZnzb.com", GetSettings("https://api.oznzb.com"));
|
||||
yield return GetDefinition("PFmonkey", GetSettings("https://www.pfmonkey.com"));
|
||||
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
//Let's try anyways with q parameter, worst case nothing found.
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.Categories, "search",
|
||||
string.Format("&q={0}", searchCriteria.Movie.Title)));
|
||||
string.Format("&q={0}", Parser.Parser.NormalizeTitle(searchCriteria.Movie.Title))));
|
||||
}
|
||||
|
||||
return pageableRequests;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
|
||||
public NewznabSettings()
|
||||
{
|
||||
Categories = new[] { 2030, 2035, 2040, 2045, 2050 };
|
||||
Categories = new[] { 2000, 2010, 2020, 2030, 2035, 2040, 2045, 2050, 2060 };
|
||||
AnimeCategories = Enumerable.Empty<int>();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
|
||||
protected override string GetInfoUrl(XElement item)
|
||||
{
|
||||
//Todo: Me thinks I need to parse details to get this...
|
||||
var match = Regex.Match(item.Description(), @"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+)(?:\""\starget)",
|
||||
var match = Regex.Match(item.Description(), @"(?:\<b\>View NZB\:\<\/b\>\s\<a\shref\=\"")(?<URL>.+)(?:\"")",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
if (match.Success)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Http;
|
||||
@@ -36,11 +37,11 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
torrentInfo.Size = (long)torrent.size*1000*1000;
|
||||
torrentInfo.DownloadUrl = torrent.download_url;
|
||||
torrentInfo.InfoUrl = torrent.details_url;
|
||||
torrentInfo.PublishDate = new System.DateTime();
|
||||
torrentInfo.PublishDate = torrent.publish_date.ToUniversalTime();
|
||||
torrentInfo.Seeders = torrent.seeders;
|
||||
torrentInfo.Peers = torrent.leechers + torrent.seeders;
|
||||
torrentInfo.Freeleech = torrent.freeleech;
|
||||
|
||||
|
||||
results.Add(torrentInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,9 +78,16 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
requestBuilder.AddQueryParam("passkey", Settings.Passkey);
|
||||
requestBuilder.AddQueryParam("user", Settings.User);
|
||||
// requestBuilder.AddQueryParam("imdbid", "tt0076759"); //For now just search for Star Wars.
|
||||
requestBuilder.AddQueryParam("search", "the"); // there has to be movies with 'the' in the title on any indexer
|
||||
if (!string.IsNullOrWhiteSpace(Settings.User))
|
||||
{
|
||||
requestBuilder.AddQueryParam("user", Settings.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestBuilder.AddQueryParam("user", "");
|
||||
}
|
||||
|
||||
requestBuilder.AddQueryParam("search", "the");
|
||||
|
||||
yield return new IndexerRequest(requestBuilder.Build());
|
||||
}
|
||||
@@ -91,7 +98,15 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
requestBuilder.AddQueryParam("passkey", Settings.Passkey);
|
||||
requestBuilder.AddQueryParam("user", Settings.User);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Settings.User))
|
||||
{
|
||||
requestBuilder.AddQueryParam("user", Settings.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestBuilder.AddQueryParam("user", "");
|
||||
}
|
||||
|
||||
if (searchCriteria.Movie.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
public int size { get; set; }
|
||||
public int leechers { get; set; }
|
||||
public int seeders { get; set; }
|
||||
public DateTime publish_date { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
|
||||
public TorrentPotatoSettings()
|
||||
{
|
||||
BaseUrl = "";
|
||||
BaseUrl = "http://127.0.0.1";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")]
|
||||
|
||||
@@ -97,8 +97,7 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
}
|
||||
|
||||
if (capabilities.SupportedTvSearchParameters != null &&
|
||||
new[] { "q", "tvdbid", "rid" }.Any(v => capabilities.SupportedTvSearchParameters.Contains(v)) &&
|
||||
new[] { "season", "ep" }.All(v => capabilities.SupportedTvSearchParameters.Contains(v)))
|
||||
new[] { "q", "imdbid" }.Any(v => capabilities.SupportedTvSearchParameters.Contains(v)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,13 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
protected override ReleaseInfo ProcessItem(XElement item, ReleaseInfo releaseInfo)
|
||||
{
|
||||
var torrentInfo = base.ProcessItem(item, releaseInfo) as TorrentInfo;
|
||||
|
||||
torrentInfo.TvdbId = GetTvdbId(item);
|
||||
torrentInfo.TvRageId = GetTvRageId(item);
|
||||
|
||||
if (GetImdbId(item) != null)
|
||||
{
|
||||
if (torrentInfo != null)
|
||||
{
|
||||
torrentInfo.ImdbId = int.Parse(GetImdbId(item).Substring(2));
|
||||
}
|
||||
}
|
||||
return torrentInfo;
|
||||
}
|
||||
|
||||
@@ -100,31 +103,12 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
return url;
|
||||
}
|
||||
|
||||
protected virtual int GetTvdbId(XElement item)
|
||||
protected virtual string GetImdbId(XElement item)
|
||||
{
|
||||
var tvdbIdString = TryGetTorznabAttribute(item, "tvdbid");
|
||||
int tvdbId;
|
||||
|
||||
if (!tvdbIdString.IsNullOrWhiteSpace() && int.TryParse(tvdbIdString, out tvdbId))
|
||||
{
|
||||
return tvdbId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
var imdbIdString = TryGetTorznabAttribute(item, "imdbid");
|
||||
return (!imdbIdString.IsNullOrWhiteSpace() ? imdbIdString.Substring(2) : null);
|
||||
}
|
||||
|
||||
protected virtual int GetTvRageId(XElement item)
|
||||
{
|
||||
var tvRageIdString = TryGetTorznabAttribute(item, "rageid");
|
||||
int tvRageId;
|
||||
|
||||
if (!tvRageIdString.IsNullOrWhiteSpace() && int.TryParse(tvRageIdString, out tvRageId))
|
||||
{
|
||||
return tvRageId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
protected override string GetInfoHash(XElement item)
|
||||
{
|
||||
return TryGetTorznabAttribute(item, "infohash");
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace NzbDrone.Core.Jobs
|
||||
new ScheduledTask{ Interval = 6*60, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
||||
new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
||||
new ScheduledTask{ Interval = 6*60, TypeName = typeof(CheckHealthCommand).FullName},
|
||||
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName},
|
||||
new ScheduledTask{ Interval = 24*60, TypeName = typeof(RefreshMovieCommand).FullName},
|
||||
new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName},
|
||||
new ScheduledTask{ Interval = 7*24*60, TypeName = typeof(BackupCommand).FullName},
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_mediaFileTableCleanupService.Clean(movie, mediaFileList);
|
||||
|
||||
var decisionsStopwatch = Stopwatch.StartNew();
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie, true);
|
||||
decisionsStopwatch.Stop();
|
||||
_logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed);
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true, false);
|
||||
var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
|
||||
|
||||
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) &&
|
||||
@@ -237,7 +237,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, null, true);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, null, true, false);
|
||||
|
||||
return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
episodeFile.MediaInfo = localMovie.MediaInfo;
|
||||
episodeFile.Movie = localMovie.Movie;
|
||||
episodeFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup;
|
||||
episodeFile.Edition = localMovie.ParsedMovieInfo.Edition;
|
||||
|
||||
bool copyOnly;
|
||||
switch (importMode)
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource); //TODO: Needs changing to ParsedMovieInfo!!
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality); //TODO: Needs changing to ParsedMovieInfo!!
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
||||
}
|
||||
|
||||
@@ -31,6 +32,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly IQualityDefinitionService _qualitiesService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
||||
@@ -39,6 +41,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
IDiskProvider diskProvider,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IDetectSample detectSample,
|
||||
IQualityDefinitionService qualitiesService,
|
||||
Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
@@ -47,6 +50,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_detectSample = detectSample;
|
||||
_qualitiesService = qualitiesService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -57,7 +61,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, movie, null, false);
|
||||
return GetImportDecisions(videoFiles, movie, null, true, false);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality = false)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, movie, null, true, shouldCheckQuality);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource)
|
||||
@@ -77,7 +86,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
return decisions;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource)
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality = false)
|
||||
{
|
||||
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie);
|
||||
|
||||
@@ -88,13 +97,13 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
|
||||
foreach (var file in newFiles)
|
||||
{
|
||||
decisions.AddIfNotNull(GetDecision(file, movie, folderInfo, sceneSource, shouldUseFolderName));
|
||||
decisions.AddIfNotNull(GetDecision(file, movie, folderInfo, sceneSource, shouldUseFolderName, shouldCheckQuality));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
private ImportDecision GetDecision(string file, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName)
|
||||
private ImportDecision GetDecision(string file, Movie movie, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName, bool shouldCheckQuality = false)
|
||||
{
|
||||
ImportDecision decision = null;
|
||||
|
||||
@@ -113,6 +122,106 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
if (sceneSource)
|
||||
{
|
||||
localMovie.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
||||
if (shouldCheckQuality)
|
||||
{
|
||||
var width = localMovie.MediaInfo.Width;
|
||||
var current = localMovie.Quality;
|
||||
var qualityName = current.Quality.Name.ToLower();
|
||||
QualityModel updated = null;
|
||||
if (width > 1400)
|
||||
{
|
||||
if (qualityName.Contains("bluray"))
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray1080p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("webdl"))
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL1080p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("hdtv"))
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV1080p);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
var def = _qualitiesService.Get(Quality.HDTV1080p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV1080p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.WEBDL1080p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL1080p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.Bluray1080p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray1080p);
|
||||
}
|
||||
if (updated == null)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray1080p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (width > 900)
|
||||
{
|
||||
if (qualityName.Contains("bluray"))
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray720p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("webdl"))
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL720p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("hdtv"))
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV720p);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
var def = _qualitiesService.Get(Quality.HDTV720p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV720p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.WEBDL720p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL720p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.Bluray720p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray720p);
|
||||
}
|
||||
if (updated == null)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray720p);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (updated != null && updated != current)
|
||||
{
|
||||
updated.QualitySource = QualitySource.MediaInfo;
|
||||
localMovie.Quality = updated;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
decision = GetDecision(localMovie);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
}
|
||||
|
||||
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
|
||||
movie, null, SceneSource(movie, folder));
|
||||
movie, null, SceneSource(movie, folder), true);
|
||||
|
||||
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
public enum FileDateType
|
||||
{
|
||||
None = 0,
|
||||
LocalAirDate = 1,
|
||||
UtcAirDate = 2
|
||||
Cinemas = 1,
|
||||
Release = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
List<string> FilterExistingFiles(List<string> files, Series series);
|
||||
List<string> FilterExistingFiles(List<string> files, Movie movie);
|
||||
EpisodeFile Get(int id);
|
||||
MovieFile GetMovie(int id);
|
||||
List<EpisodeFile> Get(IEnumerable<int> ids);
|
||||
List<MovieFile> GetMovies(IEnumerable<int> ids);
|
||||
|
||||
@@ -150,5 +151,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_movieFileRepository.Update(episodeFile);
|
||||
}
|
||||
|
||||
public MovieFile GetMovie(int id)
|
||||
{
|
||||
return _movieFileRepository.Get(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,11 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||
return AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? AudioChannels - 1 + 0.1m : AudioChannels;
|
||||
}
|
||||
|
||||
return AudioChannelPositions.Split('/').Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
|
||||
decimal channels = 0;
|
||||
|
||||
decimal.TryParse(AudioChannelPositions.Split('/').First(), out channels);
|
||||
|
||||
return channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
public string ReleaseGroup { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public string Edition { get; set; }
|
||||
public LazyLoaded<Movie> Movie { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
switch (_configService.FileDate)
|
||||
{
|
||||
case FileDateType.LocalAirDate:
|
||||
case FileDateType.Release:
|
||||
{
|
||||
var airDate = episodes.First().AirDate;
|
||||
var airTime = series.AirTime;
|
||||
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
return ChangeFileDateToLocalAirDate(episodeFilePath, airDate, airTime);
|
||||
}
|
||||
|
||||
case FileDateType.UtcAirDate:
|
||||
case FileDateType.Cinemas:
|
||||
{
|
||||
var airDateUtc = episodes.First().AirDateUtc;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
}
|
||||
|
||||
public class UpdateMovieFileService : IUpdateMovieFileService,
|
||||
IHandle<SeriesScannedEvent>
|
||||
IHandle<MovieScannedEvent>
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigService _configService;
|
||||
@@ -47,10 +47,60 @@ namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath);
|
||||
|
||||
switch (_configService.FileDate)
|
||||
{
|
||||
case FileDateType.Release:
|
||||
{
|
||||
var airDate = movie.PhysicalRelease;
|
||||
|
||||
if (airDate == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ChangeFileDate(movieFilePath, airDate.Value);
|
||||
}
|
||||
|
||||
case FileDateType.Cinemas:
|
||||
{
|
||||
var airDate = movie.InCinemas;
|
||||
|
||||
if (airDate == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ChangeFileDate(movieFilePath, airDate.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Handle(SeriesScannedEvent message)
|
||||
private bool ChangeFileDate(string filePath, DateTime date)
|
||||
{
|
||||
DateTime oldDateTime = _diskProvider.FileGetLastWrite(filePath);
|
||||
|
||||
if (!DateTime.Equals(date, oldDateTime))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.FileSetLastWriteTime(filePath, date);
|
||||
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldDateTime, date);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Handle(MovieScannedEvent message)
|
||||
{
|
||||
if (_configService.FileDate == FileDateType.None)
|
||||
{
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||
public int vote_count { get; set; }
|
||||
public AlternativeTitles alternative_titles { get; set; }
|
||||
public ReleaseDatesResource release_dates { get; set; }
|
||||
public VideosResource videos { get; set; }
|
||||
}
|
||||
|
||||
public class ReleaseDatesResource
|
||||
@@ -130,4 +131,21 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||
public string iso_3166_1 { get; set; }
|
||||
public string title { get; set; }
|
||||
}
|
||||
|
||||
public class VideosResource
|
||||
{
|
||||
public List<Video> results { get; set; }
|
||||
}
|
||||
|
||||
public class Video
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string iso_639_1 { get; set; }
|
||||
public string iso_3166_1 { get; set; }
|
||||
public string key { get; set; }
|
||||
public string name { get; set; }
|
||||
public string site { get; set; }
|
||||
public string size { get; set; }
|
||||
public string type { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Tv;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
@@ -72,7 +73,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
.SetSegment("route", "movie")
|
||||
.SetSegment("id", TmdbId.ToString())
|
||||
.SetSegment("secondaryRoute", "")
|
||||
.AddQueryParam("append_to_response", "alternative_titles,release_dates")
|
||||
.AddQueryParam("append_to_response", "alternative_titles,release_dates,videos")
|
||||
.AddQueryParam("country", "US")
|
||||
.Build();
|
||||
|
||||
@@ -88,8 +89,9 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
movie.TmdbId = TmdbId;
|
||||
movie.ImdbId = resource.imdb_id;
|
||||
movie.Title = resource.title;
|
||||
movie.TitleSlug = movie.Title.ToLower().Replace(" ", "-");
|
||||
movie.CleanTitle = Parser.Parser.CleanSeriesTitle(movie.Title);
|
||||
movie.TitleSlug = ToUrlSlug(resource.title);
|
||||
movie.CleanTitle = Parser.Parser.CleanSeriesTitle(resource.title);
|
||||
movie.SortTitle = Parser.Parser.NormalizeTitle(resource.title);
|
||||
movie.Overview = resource.overview;
|
||||
movie.Website = resource.homepage;
|
||||
if (resource.release_date.IsNotNullOrWhiteSpace())
|
||||
@@ -98,11 +100,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
movie.Year = movie.InCinemas.Value.Year;
|
||||
}
|
||||
|
||||
var slugResult = _movieService.FindByTitleSlug(movie.TitleSlug);
|
||||
if (slugResult != null)
|
||||
{
|
||||
_logger.Debug("Movie with this title slug already exists. Adding year...");
|
||||
}
|
||||
movie.TitleSlug += "-" + movie.Year.ToString();
|
||||
|
||||
movie.Images.Add(_configService.GetCoverForURL(resource.poster_path, MediaCoverTypes.Poster));//TODO: Update to load image specs from tmdb page!
|
||||
@@ -152,6 +149,23 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
movie.Status = MovieStatusType.Announced;
|
||||
}
|
||||
|
||||
if (resource.videos != null)
|
||||
{
|
||||
foreach(Video video in resource.videos.results)
|
||||
{
|
||||
if(video.type == "Trailer" && video.site == "YouTube")
|
||||
{
|
||||
movie.YouTubeTrailerId = video.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.production_companies != null && resource.production_companies.Count() > 0)
|
||||
{
|
||||
movie.Studio = resource.production_companies[0].name;
|
||||
}
|
||||
|
||||
return movie;
|
||||
}
|
||||
@@ -189,7 +203,9 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
var lowerTitle = title.ToLower();
|
||||
|
||||
var parserResult = Parser.Parser.ParseMovieTitle(title, true);
|
||||
lowerTitle = lowerTitle.Replace(".", "");
|
||||
|
||||
var parserResult = Parser.Parser.ParseMovieTitle(title.Replace(".", ""), true);
|
||||
|
||||
var yearTerm = "";
|
||||
|
||||
@@ -329,21 +345,18 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
imdbMovie.SortTitle = Parser.Parser.NormalizeTitle(result.title);
|
||||
imdbMovie.Title = result.title;
|
||||
string titleSlug = result.title;
|
||||
string titleSlug = ToUrlSlug(result.title);
|
||||
imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-");
|
||||
|
||||
if (result.release_date.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
imdbMovie.Year = DateTime.Parse(result.release_date).Year;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var slugResult = _movieService.FindByTitleSlug(imdbMovie.TitleSlug);
|
||||
if (slugResult != null)
|
||||
{
|
||||
_logger.Debug("Movie with this title slug already exists. Adding year...");
|
||||
}
|
||||
//var slugResult = _movieService.FindByTitleSlug(imdbMovie.TitleSlug);
|
||||
//if (slugResult != null)
|
||||
//{
|
||||
// _logger.Debug("Movie with this title slug already exists. Adding year...");
|
||||
//}
|
||||
imdbMovie.TitleSlug += "-" + imdbMovie.Year.ToString();
|
||||
|
||||
imdbMovie.Images = new List<MediaCover.MediaCover>();
|
||||
@@ -524,5 +537,29 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
return MediaCoverTypes.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToUrlSlug(string value)
|
||||
{
|
||||
//First to lower case
|
||||
value = value.ToLowerInvariant();
|
||||
|
||||
//Remove all accents
|
||||
var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(value);
|
||||
value = Encoding.ASCII.GetString(bytes);
|
||||
|
||||
//Replace spaces
|
||||
value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled);
|
||||
|
||||
//Remove invalid chars
|
||||
value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled);
|
||||
|
||||
//Trim dashes from end
|
||||
value = value.Trim('-', '_');
|
||||
|
||||
//Replace double occurences of - or _
|
||||
value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,12 @@
|
||||
<Compile Include="Datastore\Migration\002_remove_tvrage_imdb_unique_constraint.cs" />
|
||||
<Compile Include="Datastore\Migration\003_remove_clean_title_from_scene_mapping.cs" />
|
||||
<Compile Include="Datastore\Migration\004_updated_history.cs" />
|
||||
<Compile Include="Datastore\Migration\121_update_filedate_config.cs" />
|
||||
<Compile Include="Datastore\Migration\120_add_studio_to_table.cs" />
|
||||
<Compile Include="Datastore\Migration\119_add_youtube_trailer_id_table .cs" />
|
||||
<Compile Include="Datastore\Migration\118_update_movie_slug.cs" />
|
||||
<Compile Include="Datastore\Migration\117_update_movie_file.cs" />
|
||||
<Compile Include="Datastore\Migration\116_update_movie_sorttitle_again.cs" />
|
||||
<Compile Include="Datastore\Migration\115_update_movie_sorttitle.cs" />
|
||||
<Compile Include="Datastore\Migration\111_remove_bitmetv.cs" />
|
||||
<Compile Include="Datastore\Migration\112_remove_torrentleech.cs" />
|
||||
@@ -578,6 +584,7 @@
|
||||
<Compile Include="IndexerSearch\Definitions\MovieSearchCriteria.cs" />
|
||||
<Compile Include="IndexerSearch\MoviesSearchCommand.cs" />
|
||||
<Compile Include="IndexerSearch\MoviesSearchService.cs" />
|
||||
<Compile Include="Indexers\AwesomeHD\AwesomeHDRssParser.cs" />
|
||||
<Compile Include="Indexers\DownloadProtocol.cs" />
|
||||
<Compile Include="Indexers\Exceptions\ApiKeyException.cs" />
|
||||
<Compile Include="Indexers\Exceptions\IndexerException.cs" />
|
||||
@@ -585,6 +592,10 @@
|
||||
<Compile Include="Indexers\Exceptions\UnsupportedFeedException.cs" />
|
||||
<Compile Include="Indexers\EzrssTorrentRssParser.cs" />
|
||||
<Compile Include="Indexers\FetchAndParseRssService.cs" />
|
||||
<Compile Include="Indexers\AwesomeHD\AwesomeHD.cs" />
|
||||
<Compile Include="Indexers\AwesomeHD\AwesomeHDApi.cs" />
|
||||
<Compile Include="Indexers\AwesomeHD\AwesomeHDRequestGenerator.cs" />
|
||||
<Compile Include="Indexers\AwesomeHD\AwesomeHDSettings.cs" />
|
||||
<Compile Include="Indexers\PassThePopcorn\PassThePopcorn.cs" />
|
||||
<Compile Include="Indexers\PassThePopcorn\PassThePopcornApi.cs" />
|
||||
<Compile Include="Indexers\PassThePopcorn\PassThePopcornInfo.cs" />
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Organizer
|
||||
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{(?:Movie)(?<separator>[- ._])(Clean)?Title\})",
|
||||
public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{((?:(Movie|Original))(?<separator>[- ._])(Clean)?Title)\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
|
||||
@@ -163,6 +163,7 @@ namespace NzbDrone.Core.Organizer
|
||||
AddReleaseDateTokens(tokenHandlers, movie.Year); //In case we want to separate the year
|
||||
AddQualityTokens(tokenHandlers, movie, movieFile);
|
||||
AddMediaInfoTokens(tokenHandlers, movieFile);
|
||||
AddMovieFileTokens(tokenHandlers, movieFile);
|
||||
|
||||
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
||||
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
||||
@@ -317,7 +318,7 @@ namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
string result = name;
|
||||
string[] badCharacters = { "\\", "/", "<", ">", "?", "*", ":", "|", "\"" };
|
||||
string[] goodCharacters = { "+", "+", "", "", "!", "-", "-", "", "" };
|
||||
string[] goodCharacters = { "+", "+", "", "", "!", "-", "", "", "" };
|
||||
|
||||
for (int i = 0; i < badCharacters.Length; i++)
|
||||
{
|
||||
@@ -503,6 +504,13 @@ namespace NzbDrone.Core.Organizer
|
||||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
|
||||
}
|
||||
|
||||
private void AddMovieFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile episodeFile)
|
||||
{
|
||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile);
|
||||
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile);
|
||||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Sonarr");
|
||||
}
|
||||
|
||||
private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series, EpisodeFile episodeFile)
|
||||
{
|
||||
var qualityTitle = _qualityDefinitionService.Get(episodeFile.Quality.Quality).Title;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Parser
|
||||
)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static readonly Regex HardcodedSubsRegex = new Regex(@"\b(?<hcsub>(\w+SUB))|(?<hc>(HC))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
private static readonly Regex HardcodedSubsRegex = new Regex(@"\b(?<hcsub>(\w+SUB)\b)|(?<hc>(HC))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static readonly Regex RawHDRegex = new Regex(@"\b(?<rawhd>RawHD|1080i[-_. ]HDTV|Raw[-_. ]HD|MPEG[-_. ]?2)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
@@ -44,14 +44,14 @@ namespace NzbDrone.Core.Queue
|
||||
|
||||
private IEnumerable<Queue> MapQueue(TrackedDownload trackedDownload)
|
||||
{
|
||||
if (trackedDownload.RemoteEpisode.Episodes != null && trackedDownload.RemoteEpisode.Episodes.Any())
|
||||
if (trackedDownload.RemoteEpisode != null && trackedDownload.RemoteEpisode.Episodes != null && trackedDownload.RemoteEpisode.Episodes.Any())
|
||||
{
|
||||
foreach (var episode in trackedDownload.RemoteEpisode.Episodes)
|
||||
{
|
||||
yield return MapEpisode(trackedDownload, episode);
|
||||
}
|
||||
}
|
||||
else if (trackedDownload.RemoteMovie.Movie != null)
|
||||
else if (trackedDownload.RemoteMovie != null && trackedDownload.RemoteMovie.Movie != null)
|
||||
{
|
||||
yield return MapMovie(trackedDownload, trackedDownload.RemoteMovie.Movie);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace NzbDrone.Core.Tv
|
||||
public LazyLoaded<MovieFile> MovieFile { get; set; }
|
||||
public int MovieFileId { get; set; }
|
||||
public List<string> AlternativeTitles { get; set; }
|
||||
public string YouTubeTrailerId{ get; set; }
|
||||
public string Studio { get; set; }
|
||||
|
||||
public bool HasFile => MovieFileId > 0;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace NzbDrone.Core.Tv
|
||||
Movie FindByTitle(string cleanTitle, int year);
|
||||
Movie FindByImdbId(string imdbid);
|
||||
Movie FindByTitleSlug(string slug);
|
||||
List<Movie> MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
List<Movie> GetMoviesByFileId(int fileId);
|
||||
void SetFileId(int fileId, int movieId);
|
||||
}
|
||||
@@ -119,5 +120,17 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
return Query.Where(m => m.TitleSlug == slug).FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Movie> MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
||||
{
|
||||
var query = Query.Where(m => m.InCinemas >= start && m.InCinemas <= end).OrWhere(m => m.PhysicalRelease >= start && m.PhysicalRelease <= end);
|
||||
|
||||
if (!includeUnmonitored)
|
||||
{
|
||||
query.AndWhere(e => e.Monitored);
|
||||
}
|
||||
|
||||
return query.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ namespace NzbDrone.Core.Tv
|
||||
Movie FindByTitleInexact(string title);
|
||||
Movie FindByTitleSlug(string slug);
|
||||
Movie GetMovieByFileId(int fileId);
|
||||
List<Movie> GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
void DeleteMovie(int movieId, bool deleteFiles);
|
||||
List<Movie> GetAllMovies();
|
||||
Movie UpdateMovie(Movie movie);
|
||||
@@ -224,5 +225,12 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
return _movieRepository.FindByTitleSlug(slug);
|
||||
}
|
||||
|
||||
public List<Movie> GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
||||
{
|
||||
var episodes = _movieRepository.MoviesBetweenDates(start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored);
|
||||
|
||||
return episodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ namespace NzbDrone.Core.Tv
|
||||
movie.AlternativeTitles = movieInfo.AlternativeTitles;
|
||||
movie.Year = movieInfo.Year;
|
||||
movie.PhysicalRelease = movieInfo.PhysicalRelease;
|
||||
movie.YouTubeTrailerId = movieInfo.YouTubeTrailerId;
|
||||
movie.Studio = movieInfo.Studio;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Binary file not shown.
BIN
src/Thumbs.db
BIN
src/Thumbs.db
Binary file not shown.
@@ -26,6 +26,12 @@ var QueueCollection = PageableCollection.extend({
|
||||
});
|
||||
},
|
||||
|
||||
findMovie : function(movieId) {
|
||||
return _.find(this.fullCollection.models, function(queueModel) {
|
||||
return queueModel.get('movie').id === movieId;
|
||||
});
|
||||
},
|
||||
|
||||
sortMappings : {
|
||||
series : {
|
||||
sortValue : function(model, attr) {
|
||||
|
||||
@@ -17,7 +17,8 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
events : {
|
||||
'click .x-import' : '_importMovies',
|
||||
'click .x-add-new' : '_addMovies'
|
||||
'click .x-add-new' : '_addMovies',
|
||||
'click .x-show-existing' : '_toggleExisting'
|
||||
},
|
||||
|
||||
attributes : {
|
||||
@@ -31,13 +32,20 @@ module.exports = Marionette.Layout.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_toggleExisting : function(e) {
|
||||
var showExisting = e.target.checked;
|
||||
|
||||
vent.trigger(vent.Commands.ShowExistingCommand, {
|
||||
showExisting: showExisting
|
||||
});
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.workspace.show(new AddMoviesView());
|
||||
},
|
||||
|
||||
_folderSelected : function(options) {
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
//TODO: Fix this shit.
|
||||
this.workspace.show(new ExistingMoviesCollectionView({ model : options.model }));
|
||||
},
|
||||
|
||||
|
||||
@@ -9,6 +9,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-horizontal" style="margin-top: 15px;">
|
||||
<div class="form-group" style="margin-bottom: 0px;">
|
||||
<label class="col-sm-3 control-label">Display Existing Movies</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input class="x-show-existing" type="checkbox" checked="checked" name="showExisting"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="Should Radarr display movies already in your collection?"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="add-movies-workspace"></div>
|
||||
|
||||
@@ -9,177 +9,178 @@ var ErrorView = require('./ErrorView');
|
||||
var LoadingView = require('../Shared/LoadingView');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'AddMovies/AddMoviesViewTemplate',
|
||||
template : 'AddMovies/AddMoviesViewTemplate',
|
||||
|
||||
regions : {
|
||||
searchResult : '#search-result'
|
||||
},
|
||||
regions : {
|
||||
searchResult : '#search-result'
|
||||
},
|
||||
|
||||
ui : {
|
||||
moviesSearch : '.x-movies-search',
|
||||
searchBar : '.x-search-bar',
|
||||
loadMore : '.x-load-more'
|
||||
},
|
||||
ui : {
|
||||
moviesSearch : '.x-movies-search',
|
||||
searchBar : '.x-search-bar',
|
||||
loadMore : '.x-load-more'
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-load-more' : '_onLoadMore'
|
||||
},
|
||||
events : {
|
||||
'click .x-load-more' : '_onLoadMore'
|
||||
},
|
||||
|
||||
initialize : function(options) {
|
||||
console.log(options);
|
||||
this.isExisting = options.isExisting;
|
||||
this.collection = new AddMoviesCollection();
|
||||
initialize : function(options) {
|
||||
console.log(options);
|
||||
|
||||
if (this.isExisting) {
|
||||
this.collection.unmappedFolderModel = this.model;
|
||||
}
|
||||
this.isExisting = options.isExisting;
|
||||
this.collection = new AddMoviesCollection();
|
||||
|
||||
if (this.isExisting) {
|
||||
this.className = 'existing-movies';
|
||||
} else {
|
||||
this.className = 'new-movies';
|
||||
}
|
||||
if (this.isExisting) {
|
||||
this.collection.unmappedFolderModel = this.model;
|
||||
}
|
||||
|
||||
this.listenTo(vent, vent.Events.MoviesAdded, this._onMoviesAdded);
|
||||
this.listenTo(this.collection, 'sync', this._showResults);
|
||||
if (this.isExisting) {
|
||||
this.className = 'existing-movies';
|
||||
} else {
|
||||
this.className = 'new-movies';
|
||||
}
|
||||
|
||||
this.resultCollectionView = new SearchResultCollectionView({
|
||||
collection : this.collection,
|
||||
isExisting : this.isExisting
|
||||
});
|
||||
this.listenTo(vent, vent.Events.MoviesAdded, this._onMoviesAdded);
|
||||
this.listenTo(this.collection, 'sync', this._showResults);
|
||||
|
||||
this.throttledSearch = _.debounce(this.search, 1000, { trailing : true }).bind(this);
|
||||
},
|
||||
this.resultCollectionView = new SearchResultCollectionView({
|
||||
collection : this.collection,
|
||||
isExisting : this.isExisting
|
||||
});
|
||||
|
||||
onRender : function() {
|
||||
var self = this;
|
||||
this.throttledSearch = _.debounce(this.search, 1000, { trailing : true }).bind(this);
|
||||
},
|
||||
|
||||
this.$el.addClass(this.className);
|
||||
onRender : function() {
|
||||
var self = this;
|
||||
|
||||
this.ui.moviesSearch.keyup(function(e) {
|
||||
this.$el.addClass(this.className);
|
||||
|
||||
if (_.contains([
|
||||
9,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
91,
|
||||
92,
|
||||
93
|
||||
], e.keyCode)) {
|
||||
return;
|
||||
}
|
||||
this.ui.moviesSearch.keyup(function(e) {
|
||||
|
||||
self._abortExistingSearch();
|
||||
self.throttledSearch({
|
||||
term : self.ui.moviesSearch.val()
|
||||
});
|
||||
});
|
||||
if (_.contains([
|
||||
9,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
91,
|
||||
92,
|
||||
93
|
||||
], e.keyCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._clearResults();
|
||||
self._abortExistingSearch();
|
||||
self.throttledSearch({
|
||||
term : self.ui.moviesSearch.val()
|
||||
});
|
||||
});
|
||||
|
||||
if (this.isExisting) {
|
||||
this.ui.searchBar.hide();
|
||||
}
|
||||
},
|
||||
this._clearResults();
|
||||
|
||||
onShow : function() {
|
||||
this.ui.moviesSearch.focus();
|
||||
},
|
||||
if (this.isExisting) {
|
||||
this.ui.searchBar.hide();
|
||||
}
|
||||
},
|
||||
|
||||
search : function(options) {
|
||||
var self = this;
|
||||
onShow : function() {
|
||||
this.ui.moviesSearch.focus();
|
||||
},
|
||||
|
||||
this.collection.reset();
|
||||
search : function(options) {
|
||||
var self = this;
|
||||
|
||||
if (!options.term || options.term === this.collection.term) {
|
||||
return Marionette.$.Deferred().resolve();
|
||||
}
|
||||
this.collection.reset();
|
||||
|
||||
this.searchResult.show(new LoadingView());
|
||||
this.collection.term = options.term;
|
||||
this.currentSearchPromise = this.collection.fetch({
|
||||
data : { term : options.term }
|
||||
});
|
||||
if (!options.term || options.term === this.collection.term) {
|
||||
return Marionette.$.Deferred().resolve();
|
||||
}
|
||||
|
||||
this.currentSearchPromise.fail(function() {
|
||||
self._showError();
|
||||
});
|
||||
this.searchResult.show(new LoadingView());
|
||||
this.collection.term = options.term;
|
||||
this.currentSearchPromise = this.collection.fetch({
|
||||
data : { term : options.term }
|
||||
});
|
||||
|
||||
return this.currentSearchPromise;
|
||||
},
|
||||
this.currentSearchPromise.fail(function() {
|
||||
self._showError();
|
||||
});
|
||||
|
||||
_onMoviesAdded : function(options) {
|
||||
if (this.isExisting && options.movie.get('path') === this.model.get('folder').path) {
|
||||
this.close();
|
||||
}
|
||||
return this.currentSearchPromise;
|
||||
},
|
||||
|
||||
else if (!this.isExisting) {
|
||||
this.resultCollectionView.setExisting(options.movie.get('tmdbId'));
|
||||
/*this.collection.term = '';
|
||||
this.collection.reset();
|
||||
this._clearResults();
|
||||
this.ui.moviesSearch.val('');
|
||||
this.ui.moviesSearch.focus();*/ //TODO: Maybe add option wheter to clear search result.
|
||||
}
|
||||
},
|
||||
_onMoviesAdded : function(options) {
|
||||
if (this.isExisting && options.movie.get('path') === this.model.get('folder').path) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
_onLoadMore : function() {
|
||||
var showingAll = this.resultCollectionView.showMore();
|
||||
this.ui.searchBar.show();
|
||||
else if (!this.isExisting) {
|
||||
this.resultCollectionView.setExisting(options.movie.get('tmdbId'));
|
||||
/*this.collection.term = '';
|
||||
this.collection.reset();
|
||||
this._clearResults();
|
||||
this.ui.moviesSearch.val('');
|
||||
this.ui.moviesSearch.focus();*/ //TODO: Maybe add option wheter to clear search result.
|
||||
}
|
||||
},
|
||||
|
||||
if (showingAll) {
|
||||
this.ui.loadMore.hide();
|
||||
}
|
||||
},
|
||||
_onLoadMore : function() {
|
||||
var showingAll = this.resultCollectionView.showMore();
|
||||
this.ui.searchBar.show();
|
||||
|
||||
_clearResults : function() {
|
||||
if (showingAll) {
|
||||
this.ui.loadMore.hide();
|
||||
}
|
||||
},
|
||||
|
||||
if (!this.isExisting) {
|
||||
this.searchResult.show(new EmptyView());
|
||||
} else {
|
||||
this.searchResult.close();
|
||||
}
|
||||
},
|
||||
_clearResults : function() {
|
||||
|
||||
_showResults : function() {
|
||||
if (!this.isClosed) {
|
||||
if (this.collection.length === 0) {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
||||
} else {
|
||||
this.searchResult.show(this.resultCollectionView);
|
||||
if (!this.showingAll && this.isExisting) {
|
||||
this.ui.loadMore.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
if (!this.isExisting) {
|
||||
this.searchResult.show(new EmptyView());
|
||||
} else {
|
||||
this.searchResult.close();
|
||||
}
|
||||
},
|
||||
|
||||
_abortExistingSearch : function() {
|
||||
if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) {
|
||||
console.log('aborting previous pending search request.');
|
||||
this.currentSearchPromise.abort();
|
||||
} else {
|
||||
this._clearResults();
|
||||
}
|
||||
},
|
||||
_showResults : function() {
|
||||
if (!this.isClosed) {
|
||||
if (this.collection.length === 0) {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
||||
} else {
|
||||
this.searchResult.show(this.resultCollectionView);
|
||||
if (!this.showingAll) {
|
||||
this.ui.loadMore.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_showError : function() {
|
||||
if (!this.isClosed) {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new ErrorView({ term : this.collection.term }));
|
||||
this.collection.term = '';
|
||||
}
|
||||
}
|
||||
_abortExistingSearch : function() {
|
||||
if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) {
|
||||
console.log('aborting previous pending search request.');
|
||||
this.currentSearchPromise.abort();
|
||||
} else {
|
||||
this._clearResults();
|
||||
}
|
||||
},
|
||||
|
||||
_showError : function() {
|
||||
if (!this.isClosed) {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new ErrorView({ term : this.collection.term }));
|
||||
this.collection.term = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var Marionette = require('marionette');
|
||||
var AddMoviesView = require('../AddMoviesView');
|
||||
var UnmappedFolderCollection = require('./UnmappedFolderCollection');
|
||||
var vent = require('vent');
|
||||
|
||||
module.exports = Marionette.CompositeView.extend({
|
||||
itemView : AddMoviesView,
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
<dl class="monitor-tooltip-contents">
|
||||
<dt>All</dt>
|
||||
<dd>Monitor all episodes except specials</dd>
|
||||
<dt>Future</dt>
|
||||
<dd>Monitor episodes that have not aired yet</dd>
|
||||
<dt>Missing</dt>
|
||||
<dd>Monitor episodes that do not have files or have not aired yet</dd>
|
||||
<dt>Existing</dt>
|
||||
<dd>Monitor episodes that have files or have not aired yet</dd>
|
||||
<dt>First Season</dt>
|
||||
<dd>Monitor all episodes of the first season. All other seasons will be ignored</dd>
|
||||
<dt>Latest Season</dt>
|
||||
<dd>Monitor all episodes of the latest season and future seasons</dd>
|
||||
<dt>None</dt>
|
||||
<dd>No episodes will be monitored.</dd>
|
||||
<!--<dt>Latest Season</dt>-->
|
||||
<!--<dd>Monitor all episodes the latest season only, previous seasons will be ignored</dd>-->
|
||||
<dt>Yes</dt>
|
||||
<dd>Monitor for new releases</dd>
|
||||
<dt>No</dt>
|
||||
<dd>Do not monitor for new releases</dd>
|
||||
</dl>
|
||||
@@ -48,7 +48,7 @@ var Layout = Marionette.Layout.extend({
|
||||
var self = this;
|
||||
|
||||
var newDir = new RootFolderModel({
|
||||
Path : this.ui.pathInput.val()
|
||||
Path : this.ui.pathInput.val(),
|
||||
});
|
||||
|
||||
this.bindToModelValidation(newDir);
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
|
||||
<button class="btn" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,41 +1,65 @@
|
||||
var Marionette = require('marionette');
|
||||
var SearchResultView = require('./SearchResultView');
|
||||
var MoviesCollection = require('../Movies/MoviesCollection');
|
||||
var vent = require('vent');
|
||||
|
||||
module.exports = Marionette.CollectionView.extend({
|
||||
itemView : SearchResultView,
|
||||
itemView : SearchResultView,
|
||||
|
||||
initialize : function(options) {
|
||||
this.isExisting = options.isExisting;
|
||||
this.showing = 1;
|
||||
},
|
||||
initialize : function(options) {
|
||||
this.showExisting = true;
|
||||
this.isExisting = options.isExisting;
|
||||
this.showing = 5;
|
||||
if (this.isExisting) {
|
||||
this.showing = 1;
|
||||
}
|
||||
vent.on(vent.Commands.ShowExistingCommand, this._onExistingToggle.bind(this));
|
||||
},
|
||||
|
||||
showAll : function() {
|
||||
this.showingAll = true;
|
||||
this.render();
|
||||
},
|
||||
_onExistingToggle : function(data) {
|
||||
this.showExisting = data.showExisting;
|
||||
|
||||
showMore : function() {
|
||||
this.showing += 5;
|
||||
this.render();
|
||||
this.render();
|
||||
},
|
||||
|
||||
return this.showing >= this.collection.length;
|
||||
},
|
||||
showAll : function() {
|
||||
this.showingAll = true;
|
||||
this.render();
|
||||
},
|
||||
|
||||
setExisting : function(tmdbid) {
|
||||
var movies = this.collection.where({ tmdbId : tmdbid });
|
||||
console.warn(movies);
|
||||
//debugger;
|
||||
if (movies.length > 0) {
|
||||
this.children.findByModel(movies[0])._configureTemplateHelpers();
|
||||
//this.children.findByModel(movies[0])._configureTemplateHelpers();
|
||||
this.children.findByModel(movies[0]).render();
|
||||
//this.templateHelpers.existing = existingMovies[0].toJSON();
|
||||
}
|
||||
},
|
||||
showMore : function() {
|
||||
this.showing += 5;
|
||||
this.render();
|
||||
|
||||
appendHtml : function(collectionView, itemView, index) {
|
||||
if (!this.isExisting || index < this.showing || index === 0) {
|
||||
collectionView.$el.append(itemView.el);
|
||||
}
|
||||
}
|
||||
return this.showing >= this.collection.length;
|
||||
},
|
||||
|
||||
setExisting : function(tmdbid) {
|
||||
var movies = this.collection.where({ tmdbId : tmdbid });
|
||||
console.warn(movies);
|
||||
//debugger;
|
||||
if (movies.length > 0) {
|
||||
this.children.findByModel(movies[0])._configureTemplateHelpers();
|
||||
//this.children.findByModel(movies[0])._configureTemplateHelpers();
|
||||
this.children.findByModel(movies[0]).render();
|
||||
//this.templateHelpers.existing = existingMovies[0].toJSON();
|
||||
}
|
||||
},
|
||||
|
||||
appendHtml : function(collectionView, itemView, index) {
|
||||
var tmdbId = itemView.model.get('tmdbId');
|
||||
var existingMovies = MoviesCollection.where({ tmdbId: tmdbId });
|
||||
if(existingMovies.length > 0) {
|
||||
if(this.showExisting) {
|
||||
if (index < this.showing || index === 0) {
|
||||
collectionView.$el.append(itemView.el);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (index < this.showing || index === 0) {
|
||||
collectionView.$el.append(itemView.el);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ var view = Marionette.ItemView.extend({
|
||||
throw 'model is required';
|
||||
}
|
||||
|
||||
console.log(this.route);
|
||||
//console.log(this.route);
|
||||
|
||||
this.templateHelpers = {};
|
||||
this._configureTemplateHelpers();
|
||||
@@ -84,7 +84,7 @@ var view = Marionette.ItemView.extend({
|
||||
content : content,
|
||||
html : true,
|
||||
trigger : 'hover',
|
||||
title : 'Episode Monitoring Options',
|
||||
title : 'Movie Monitoring Options',
|
||||
placement : 'right',
|
||||
container : this.$el
|
||||
});
|
||||
@@ -92,14 +92,12 @@ var view = Marionette.ItemView.extend({
|
||||
|
||||
_configureTemplateHelpers : function() {
|
||||
var existingMovies = MoviesCollection.where({ tmdbId : this.model.get('tmdbId') });
|
||||
console.log(existingMovies);
|
||||
if (existingMovies.length > 0) {
|
||||
this.templateHelpers.existing = existingMovies[0].toJSON();
|
||||
}
|
||||
|
||||
this.templateHelpers.profiles = Profiles.toJSON();
|
||||
console.log(this.model);
|
||||
console.log(this.templateHelpers.existing);
|
||||
//console.log(this.templateHelpers.isExisting);
|
||||
if (!this.model.get('isExisting')) {
|
||||
this.templateHelpers.rootFolders = RootFolders.toJSON();
|
||||
}
|
||||
@@ -185,8 +183,8 @@ var view = Marionette.ItemView.extend({
|
||||
var self = this;
|
||||
var promise = this.model.save();
|
||||
|
||||
console.log(this.model.save);
|
||||
console.log(promise);
|
||||
//console.log(this.model.save);
|
||||
//console.log(promise);
|
||||
|
||||
if (searchForMovie) {
|
||||
this.ui.addSearchButton.spinForPromise(promise);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var Backbone = require('backbone');
|
||||
var EpisodeModel = require('../Series/EpisodeModel');
|
||||
var EpisodeModel = require('../Movies/MovieModel');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/calendar',
|
||||
@@ -7,8 +7,8 @@ module.exports = Backbone.Collection.extend({
|
||||
tableName : 'calendar',
|
||||
|
||||
comparator : function(model) {
|
||||
var date = new Date(model.get('airDateUtc'));
|
||||
var date = new Date(model.get('inCinemas'));
|
||||
var time = date.getTime();
|
||||
return time;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,75 +1,57 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Radarr Calendar feed</h3>
|
||||
</div>
|
||||
<div class="modal-body edit-series-modal">
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Include Unmonitored</label>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Radarr Calendar feed</h3>
|
||||
</div>
|
||||
<div class="modal-body edit-series-modal">
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Include Unmonitored</label>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="includeUnmonitored" class="form-control x-includeUnmonitored"/>
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="includeUnmonitored" class="form-control x-includeUnmonitored"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Season Premiers Only</label>
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Tags</label>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="premiersOnly" class="form-control x-premiersOnly"/>
|
||||
<div class="col-sm-1 col-sm-push-5 help-inline">
|
||||
<i class="icon-sonarr-form-info" title="One or more tags only show matching series" />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Tags</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-5 help-inline">
|
||||
<i class="icon-sonarr-form-info" title="One or more tags only show matching series" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 col-sm-pull-1">
|
||||
<input type="text" class="form-control x-tags">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">iCal feed</label>
|
||||
<div class="col-sm-1 col-sm-push-8 help-inline">
|
||||
<i class="icon-sonarr-form-info" title="Copy this url into your clients subscription form or use the subscribe button if your browser support webcal" />
|
||||
</div>
|
||||
<div class="col-sm-8 col-sm-pull-1">
|
||||
<div class="input-group ical-url">
|
||||
<input type="text" class="form-control x-ical-url" readonly="readonly" />
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-icon-only x-ical-copy"><i class="icon-sonarr-copy"></i></button>
|
||||
<button class="btn btn-icon-only no-router x-ical-webcal" title="Subscribe" target="_blank"><i class="icon-sonarr-calendar-o"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
<div class="col-sm-5 col-sm-pull-1">
|
||||
<input type="text" class="form-control x-tags">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">iCal feed</label>
|
||||
<div class="col-sm-1 col-sm-push-8 help-inline">
|
||||
<i class="icon-sonarr-form-info" title="Copy this url into your clients subscription form or use the subscribe button if your browser support webcal" />
|
||||
</div>
|
||||
<div class="col-sm-8 col-sm-pull-1">
|
||||
<div class="input-group ical-url">
|
||||
<input type="text" class="form-control x-ical-url" readonly="readonly" />
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-icon-only x-ical-copy"><i class="icon-sonarr-copy"></i></button>
|
||||
<button class="btn btn-icon-only no-router x-ical-webcal" title="Subscribe" target="_blank"><i class="icon-sonarr-calendar-o"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<div id="x-calendar" class="calendar"/>
|
||||
<div class="legend calendar">
|
||||
<ul class='legend-labels'>
|
||||
<li class="legend-label"><span class="premiere" title="Premiere episode hasn't aired yet"></span>Unaired Premiere</li>
|
||||
<li class="legend-label"><span class="primary" title="Episode hasn't aired yet"></span>Unaired</li>
|
||||
<li class="legend-label"><span class="warning" title="Episode is currently airing"></span>On Air</li>
|
||||
<li class="legend-label"><span class="purple" title="Episode is currently downloading"></span>Downloading</li>
|
||||
<li class="legend-label"><span class="danger" title="Episode file has not been found"></span>Missing</li>
|
||||
<li class="legend-label"><span class="success" title="Episode was downloaded and sorted"></span>Downloaded</li>
|
||||
<li class="legend-label"><span class="unmonitored" title="Episode is unmonitored"></span>Unmonitored</li>
|
||||
<li class="legend-label"><span class="premiere" title="This Movie is still in cinemas and hasn't been released yet. Only poor qualities will be available"></span>In Cinemas</li>
|
||||
<li class="legend-label"><span class="primary" title="This movie has only been announced yet."></span>Announced</li>
|
||||
<!--<li class="legend-label"><span class="warning" title="Episode is currently airing"></span>On Air</li>-->
|
||||
<li class="legend-label"><span class="purple" title="Movie is currently downloading"></span>Downloading</li>
|
||||
<li class="legend-label"><span class="danger" title="Movie file has not been found"></span>Missing</li>
|
||||
<li class="legend-label"><span class="success" title="Movie was downloaded and sorted"></span>Downloaded</li>
|
||||
<li class="legend-label"><span class="unmonitored" title="Movie is unmonitored"></span>Unmonitored</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,273 +12,274 @@ require('fullcalendar');
|
||||
require('jquery.easypiechart');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
storageKey : 'calendar.view',
|
||||
storageKey : 'calendar.view',
|
||||
|
||||
initialize : function() {
|
||||
this.showUnmonitored = Config.getValue('calendar.show', 'monitored') === 'all';
|
||||
this.collection = new CalendarCollection().bindSignalR({ updateOnly : true });
|
||||
this.listenTo(this.collection, 'change', this._reloadCalendarEvents);
|
||||
this.listenTo(QueueCollection, 'sync', this._reloadCalendarEvents);
|
||||
},
|
||||
initialize : function() {
|
||||
this.showUnmonitored = Config.getValue('calendar.show', 'monitored') === 'all';
|
||||
this.collection = new CalendarCollection().bindSignalR({ updateOnly : true });
|
||||
this.listenTo(this.collection, 'change', this._reloadCalendarEvents);
|
||||
this.listenTo(QueueCollection, 'sync', this._reloadCalendarEvents);
|
||||
},
|
||||
|
||||
render : function() {
|
||||
this.$el.empty().fullCalendar(this._getOptions());
|
||||
},
|
||||
render : function() {
|
||||
this.$el.empty().fullCalendar(this._getOptions());
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.$('.fc-today-button').click();
|
||||
},
|
||||
onShow : function() {
|
||||
this.$('.fc-today-button').click();
|
||||
},
|
||||
|
||||
setShowUnmonitored : function (showUnmonitored) {
|
||||
if (this.showUnmonitored !== showUnmonitored) {
|
||||
this.showUnmonitored = showUnmonitored;
|
||||
this._getEvents(this.$el.fullCalendar('getView'));
|
||||
}
|
||||
},
|
||||
setShowUnmonitored : function (showUnmonitored) {
|
||||
if (this.showUnmonitored !== showUnmonitored) {
|
||||
this.showUnmonitored = showUnmonitored;
|
||||
this._getEvents(this.$el.fullCalendar('getView'));
|
||||
}
|
||||
},
|
||||
|
||||
_viewRender : function(view, element) {
|
||||
if (Config.getValue(this.storageKey) !== view.name) {
|
||||
Config.setValue(this.storageKey, view.name);
|
||||
}
|
||||
_viewRender : function(view, element) {
|
||||
if (Config.getValue(this.storageKey) !== view.name) {
|
||||
Config.setValue(this.storageKey, view.name);
|
||||
}
|
||||
|
||||
this._getEvents(view);
|
||||
element.find('.fc-day-grid-container').css('height', '');
|
||||
},
|
||||
this._getEvents(view);
|
||||
element.find('.fc-day-grid-container').css('height', '');
|
||||
},
|
||||
|
||||
_eventRender : function(event, element) {
|
||||
element.addClass(event.statusLevel);
|
||||
element.children('.fc-content').addClass(event.statusLevel);
|
||||
_eventRender : function(event, element) {
|
||||
element.addClass(event.statusLevel);
|
||||
element.children('.fc-content').addClass(event.statusLevel);
|
||||
|
||||
if (event.downloading) {
|
||||
var progress = 100 - event.downloading.get('sizeleft') / event.downloading.get('size') * 100;
|
||||
var releaseTitle = event.downloading.get('title');
|
||||
var estimatedCompletionTime = moment(event.downloading.get('estimatedCompletionTime')).fromNow();
|
||||
var status = event.downloading.get('status').toLocaleLowerCase();
|
||||
var errorMessage = event.downloading.get('errorMessage');
|
||||
if (event.downloading) {
|
||||
var progress = 100 - event.downloading.get('sizeleft') / event.downloading.get('size') * 100;
|
||||
var releaseTitle = event.downloading.get('title');
|
||||
var estimatedCompletionTime = moment(event.downloading.get('estimatedCompletionTime')).fromNow();
|
||||
var status = event.downloading.get('status').toLocaleLowerCase();
|
||||
var errorMessage = event.downloading.get('errorMessage');
|
||||
|
||||
if (status === 'pending') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-pending', 'Release will be processed {0}'.format(estimatedCompletionTime));
|
||||
}
|
||||
if (status === 'pending') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-pending', 'Release will be processed {0}'.format(estimatedCompletionTime));
|
||||
}
|
||||
|
||||
else if (errorMessage) {
|
||||
if (status === 'completed') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-import-failed', 'Import failed: {0}'.format(errorMessage));
|
||||
} else {
|
||||
this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: {0}'.format(errorMessage));
|
||||
}
|
||||
}
|
||||
else if (errorMessage) {
|
||||
if (status === 'completed') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-import-failed', 'Import failed: {0}'.format(errorMessage));
|
||||
} else {
|
||||
this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: {0}'.format(errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
else if (status === 'failed') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: check download client for more details');
|
||||
}
|
||||
else if (status === 'failed') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: check download client for more details');
|
||||
}
|
||||
|
||||
else if (status === 'warning') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-download-warning', 'Download warning: check download client for more details');
|
||||
}
|
||||
else if (status === 'warning') {
|
||||
this._addStatusIcon(element, 'icon-sonarr-download-warning', 'Download warning: check download client for more details');
|
||||
}
|
||||
|
||||
else {
|
||||
element.find('.fc-time').after('<span class="chart pull-right" data-percent="{0}"></span>'.format(progress));
|
||||
else {
|
||||
element.find('.fc-time').after('<span class="chart pull-right" data-percent="{0}"></span>'.format(progress));
|
||||
|
||||
element.find('.chart').easyPieChart({
|
||||
barColor : '#ffffff',
|
||||
trackColor : false,
|
||||
scaleColor : false,
|
||||
lineWidth : 2,
|
||||
size : 14,
|
||||
animate : false
|
||||
});
|
||||
element.find('.chart').easyPieChart({
|
||||
barColor : '#ffffff',
|
||||
trackColor : false,
|
||||
scaleColor : false,
|
||||
lineWidth : 2,
|
||||
size : 14,
|
||||
animate : false
|
||||
});
|
||||
|
||||
element.find('.chart').tooltip({
|
||||
title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
|
||||
container : '.fc'
|
||||
});
|
||||
}
|
||||
}
|
||||
element.find('.chart').tooltip({
|
||||
title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
|
||||
container : '.fc'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
else if (event.model.get('unverifiedSceneNumbering')) {
|
||||
this._addStatusIcon(element, 'icon-sonarr-form-warning', 'Scene number hasn\'t been verified yet.');
|
||||
}
|
||||
else if (event.model.get('unverifiedSceneNumbering')) {
|
||||
this._addStatusIcon(element, 'icon-sonarr-form-warning', 'Scene number hasn\'t been verified yet.');
|
||||
}
|
||||
},
|
||||
|
||||
else if (event.model.get('series').seriesType === 'anime' && event.model.get('seasonNumber') > 0 && !event.model.has('absoluteEpisodeNumber')) {
|
||||
this._addStatusIcon(element, 'icon-sonarr-form-warning', 'Episode does not have an absolute episode number');
|
||||
}
|
||||
},
|
||||
_eventAfterAllRender : function () {
|
||||
if ($(window).width() < 768) {
|
||||
this.$('.fc-center').show();
|
||||
this.$('.calendar-title').remove();
|
||||
|
||||
_eventAfterAllRender : function () {
|
||||
if ($(window).width() < 768) {
|
||||
this.$('.fc-center').show();
|
||||
this.$('.calendar-title').remove();
|
||||
var title = this.$('.fc-center').html();
|
||||
var titleDiv = '<div class="calendar-title">{0}</div>'.format(title);
|
||||
|
||||
var title = this.$('.fc-center').html();
|
||||
var titleDiv = '<div class="calendar-title">{0}</div>'.format(title);
|
||||
this.$('.fc-toolbar').before(titleDiv);
|
||||
this.$('.fc-center').hide();
|
||||
}
|
||||
|
||||
this.$('.fc-toolbar').before(titleDiv);
|
||||
this.$('.fc-center').hide();
|
||||
}
|
||||
this._clearScrollBar();
|
||||
},
|
||||
|
||||
this._clearScrollBar();
|
||||
},
|
||||
_windowResize : function () {
|
||||
this._clearScrollBar();
|
||||
},
|
||||
|
||||
_windowResize : function () {
|
||||
this._clearScrollBar();
|
||||
},
|
||||
_getEvents : function(view) {
|
||||
var start = moment(view.start.toISOString()).toISOString();
|
||||
var end = moment(view.end.toISOString()).toISOString();
|
||||
|
||||
_getEvents : function(view) {
|
||||
var start = moment(view.start.toISOString()).toISOString();
|
||||
var end = moment(view.end.toISOString()).toISOString();
|
||||
this.$el.fullCalendar('removeEvents');
|
||||
|
||||
this.$el.fullCalendar('removeEvents');
|
||||
this.collection.fetch({
|
||||
data : {
|
||||
start : start,
|
||||
end : end,
|
||||
unmonitored : this.showUnmonitored
|
||||
},
|
||||
success : this._setEventData.bind(this, new Date(start), new Date(end))
|
||||
});
|
||||
},
|
||||
|
||||
this.collection.fetch({
|
||||
data : {
|
||||
start : start,
|
||||
end : end,
|
||||
unmonitored : this.showUnmonitored
|
||||
},
|
||||
success : this._setEventData.bind(this)
|
||||
});
|
||||
},
|
||||
_setEventData : function(startD, endD, collection) {
|
||||
if (collection.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setEventData : function(collection) {
|
||||
if (collection.length === 0) {
|
||||
return;
|
||||
}
|
||||
var events = [];
|
||||
var self = this;
|
||||
|
||||
var events = [];
|
||||
var self = this;
|
||||
collection.each(function(model) {
|
||||
var seriesTitle = model.get('title');
|
||||
var start = model.get('inCinemas');
|
||||
var startDate = new Date(start);
|
||||
if (!(startD <= startDate && startDate <= endD)) {
|
||||
start = model.get("physicalRelease");
|
||||
}
|
||||
var runtime = model.get('runtime');
|
||||
var end = moment(start).add('minutes', runtime).toISOString();
|
||||
|
||||
collection.each(function(model) {
|
||||
var seriesTitle = model.get('series').title;
|
||||
var start = model.get('airDateUtc');
|
||||
var runtime = model.get('series').runtime;
|
||||
var end = moment(start).add('minutes', runtime).toISOString();
|
||||
var event = {
|
||||
title : seriesTitle,
|
||||
start : moment(start),
|
||||
end : moment(end),
|
||||
allDay : true,
|
||||
statusLevel : self._getStatusLevel(model, end),
|
||||
downloading : QueueCollection.findMovie(model.get('id')),
|
||||
model : model,
|
||||
sortOrder : 0
|
||||
};
|
||||
|
||||
var event = {
|
||||
title : seriesTitle,
|
||||
start : moment(start),
|
||||
end : moment(end),
|
||||
allDay : false,
|
||||
statusLevel : self._getStatusLevel(model, end),
|
||||
downloading : QueueCollection.findEpisode(model.get('id')),
|
||||
model : model,
|
||||
sortOrder : (model.get('seasonNumber') === 0 ? 1000000 : model.get('seasonNumber') * 10000) + model.get('episodeNumber')
|
||||
};
|
||||
events.push(event);
|
||||
});
|
||||
|
||||
events.push(event);
|
||||
});
|
||||
this.$el.fullCalendar('addEventSource', events);
|
||||
},
|
||||
|
||||
this.$el.fullCalendar('addEventSource', events);
|
||||
},
|
||||
_getStatusLevel : function(element, endTime) {
|
||||
var hasFile = element.get('hasFile');
|
||||
var downloading = QueueCollection.findMovie(element.get('id')) || element.get('grabbed');
|
||||
var currentTime = moment();
|
||||
var start = moment(element.get('inCinemas'));
|
||||
var status = element.getStatus();
|
||||
var end = moment(endTime);
|
||||
var monitored = element.get('monitored');
|
||||
|
||||
_getStatusLevel : function(element, endTime) {
|
||||
var hasFile = element.get('hasFile');
|
||||
var downloading = QueueCollection.findEpisode(element.get('id')) || element.get('grabbed');
|
||||
var currentTime = moment();
|
||||
var start = moment(element.get('airDateUtc'));
|
||||
var end = moment(endTime);
|
||||
var monitored = element.get('series').monitored && element.get('monitored');
|
||||
var statusLevel = 'primary';
|
||||
|
||||
var statusLevel = 'primary';
|
||||
if (hasFile) {
|
||||
statusLevel = 'success';
|
||||
}
|
||||
|
||||
if (hasFile) {
|
||||
statusLevel = 'success';
|
||||
}
|
||||
else if (downloading) {
|
||||
statusLevel = 'purple';
|
||||
}
|
||||
|
||||
else if (downloading) {
|
||||
statusLevel = 'purple';
|
||||
}
|
||||
else if (!monitored) {
|
||||
statusLevel = 'unmonitored';
|
||||
}
|
||||
|
||||
else if (!monitored) {
|
||||
statusLevel = 'unmonitored';
|
||||
}
|
||||
else if (status == "inCinemas") {
|
||||
statusLevel = 'premiere';
|
||||
}
|
||||
|
||||
else if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
|
||||
statusLevel = 'warning';
|
||||
}
|
||||
else if (status == "released") {
|
||||
statusLevel = 'danger';
|
||||
}
|
||||
|
||||
else if (start.isBefore(currentTime) && !hasFile) {
|
||||
statusLevel = 'danger';
|
||||
}
|
||||
else if (status == "announced") {
|
||||
statusLevel = 'primary';
|
||||
}
|
||||
|
||||
else if (element.get('episodeNumber') === 1) {
|
||||
statusLevel = 'premiere';
|
||||
}
|
||||
if (end.isBefore(currentTime.startOf('day'))) {
|
||||
statusLevel += ' past';
|
||||
}
|
||||
|
||||
if (end.isBefore(currentTime.startOf('day'))) {
|
||||
statusLevel += ' past';
|
||||
}
|
||||
return statusLevel;
|
||||
},
|
||||
|
||||
return statusLevel;
|
||||
},
|
||||
_reloadCalendarEvents : function() {
|
||||
this.$el.fullCalendar('removeEvents');
|
||||
var view = this.$el.fullCalendar('getView');
|
||||
var start = moment(view.start.toISOString()).toISOString();
|
||||
var end = moment(view.end.toISOString()).toISOString();
|
||||
this._setEventData(new Date(start), new Date(end), this.collection);
|
||||
},
|
||||
|
||||
_reloadCalendarEvents : function() {
|
||||
this.$el.fullCalendar('removeEvents');
|
||||
this._setEventData(this.collection);
|
||||
},
|
||||
_getOptions : function() {
|
||||
var options = {
|
||||
allDayDefault : true,
|
||||
weekMode : 'variable',
|
||||
firstDay : UiSettings.get('firstDayOfWeek'),
|
||||
timeFormat : 'h(:mm)t',
|
||||
viewRender : this._viewRender.bind(this),
|
||||
eventRender : this._eventRender.bind(this),
|
||||
eventAfterAllRender : this._eventAfterAllRender.bind(this),
|
||||
windowResize : this._windowResize.bind(this),
|
||||
eventClick : function(event) {
|
||||
//vent.trigger(vent.Commands.ShowMovieDetails, { movie : event.model });
|
||||
window.location.href = "movies/"+event.model.get("titleSlug");
|
||||
}
|
||||
};
|
||||
|
||||
_getOptions : function() {
|
||||
var options = {
|
||||
allDayDefault : false,
|
||||
weekMode : 'variable',
|
||||
firstDay : UiSettings.get('firstDayOfWeek'),
|
||||
timeFormat : 'h(:mm)t',
|
||||
viewRender : this._viewRender.bind(this),
|
||||
eventRender : this._eventRender.bind(this),
|
||||
eventAfterAllRender : this._eventAfterAllRender.bind(this),
|
||||
windowResize : this._windowResize.bind(this),
|
||||
eventClick : function(event) {
|
||||
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : event.model });
|
||||
}
|
||||
};
|
||||
if ($(window).width() < 768) {
|
||||
options.defaultView = Config.getValue(this.storageKey, 'listYear');
|
||||
|
||||
if ($(window).width() < 768) {
|
||||
options.defaultView = Config.getValue(this.storageKey, 'basicDay');
|
||||
options.header = {
|
||||
left : 'prev,next today',
|
||||
center : 'title',
|
||||
right : 'listYear'
|
||||
};
|
||||
}
|
||||
|
||||
options.header = {
|
||||
left : 'prev,next today',
|
||||
center : 'title',
|
||||
right : 'basicWeek,basicDay'
|
||||
};
|
||||
}
|
||||
else {
|
||||
options.defaultView = Config.getValue(this.storageKey, 'month');
|
||||
|
||||
else {
|
||||
options.defaultView = Config.getValue(this.storageKey, 'basicWeek');
|
||||
options.header = {
|
||||
left : 'prev,next today',
|
||||
center : 'title',
|
||||
right : 'month,listYear'
|
||||
};
|
||||
}
|
||||
|
||||
options.header = {
|
||||
left : 'prev,next today',
|
||||
center : 'title',
|
||||
right : 'month,basicWeek,basicDay'
|
||||
};
|
||||
}
|
||||
options.titleFormat = "L";
|
||||
|
||||
options.titleFormat = {
|
||||
month : 'MMMM YYYY',
|
||||
week : UiSettings.get('shortDateFormat'),
|
||||
day : UiSettings.get('longDateFormat')
|
||||
};
|
||||
options.columnFormat = "L"/*{
|
||||
month : 'ddd',
|
||||
week : UiSettings.get('calendarWeekColumnHeader'),
|
||||
day : 'dddd'
|
||||
};*///For now ignore settings. TODO update that.
|
||||
|
||||
options.columnFormat = {
|
||||
month : 'ddd',
|
||||
week : UiSettings.get('calendarWeekColumnHeader'),
|
||||
day : 'dddd'
|
||||
};
|
||||
options.timeFormat = UiSettings.get('timeFormat');
|
||||
|
||||
options.timeFormat = UiSettings.get('timeFormat');
|
||||
return options;
|
||||
},
|
||||
|
||||
return options;
|
||||
},
|
||||
_addStatusIcon : function(element, icon, tooltip) {
|
||||
element.find('.fc-time').after('<span class="status pull-right"><i class="{0}"></i></span>'.format(icon));
|
||||
element.find('.status').tooltip({
|
||||
title : tooltip,
|
||||
container : '.fc'
|
||||
});
|
||||
},
|
||||
|
||||
_addStatusIcon : function(element, icon, tooltip) {
|
||||
element.find('.fc-time').after('<span class="status pull-right"><i class="{0}"></i></span>'.format(icon));
|
||||
element.find('.status').tooltip({
|
||||
title : tooltip,
|
||||
container : '.fc'
|
||||
});
|
||||
},
|
||||
|
||||
_clearScrollBar : function () {
|
||||
// Remove height from calendar so we don't have another scroll bar
|
||||
this.$('.fc-day-grid-container').css('height', '');
|
||||
this.$('.fc-row.fc-widget-header').attr('style', '');
|
||||
}
|
||||
});
|
||||
_clearScrollBar : function () {
|
||||
// Remove height from calendar so we don't have another scroll bar
|
||||
this.$('.fc-day-grid-container').css('height', '');
|
||||
this.$('.fc-row.fc-widget-header').attr('style', '');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
var Backbone = require('backbone');
|
||||
var moment = require('moment');
|
||||
var EpisodeModel = require('../Series/EpisodeModel');
|
||||
var EpisodeModel = require('../Movies/MovieModel');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/calendar',
|
||||
model : EpisodeModel,
|
||||
|
||||
comparator : function(model1, model2) {
|
||||
var airDate1 = model1.get('airDateUtc');
|
||||
var airDate1 = model1.get('inCinemas');
|
||||
var date1 = moment(airDate1);
|
||||
var time1 = date1.unix();
|
||||
|
||||
var airDate2 = model2.get('airDateUtc');
|
||||
var airDate2 = model2.get('inCinemas');
|
||||
var date2 = moment(airDate2);
|
||||
var time2 = date2.unix();
|
||||
|
||||
@@ -25,4 +25,4 @@ module.exports = Backbone.Collection.extend({
|
||||
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,8 +11,8 @@ module.exports = Marionette.ItemView.extend({
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
var start = this.model.get('airDateUtc');
|
||||
var runtime = this.model.get('series').runtime;
|
||||
var start = this.model.get('inCinemas');
|
||||
var runtime = this.model.get('runtime');
|
||||
var end = moment(start).add('minutes', runtime);
|
||||
|
||||
this.model.set({
|
||||
@@ -25,4 +25,4 @@ module.exports = Marionette.ItemView.extend({
|
||||
_showEpisodeDetails : function() {
|
||||
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : this.model });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,248 +7,259 @@
|
||||
@import "../Content/Overrides/bootstrap";
|
||||
|
||||
.calendar {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
|
||||
th, td {
|
||||
border-color : #eeeeee;
|
||||
}
|
||||
th, td {
|
||||
border-color : #eeeeee;
|
||||
}
|
||||
|
||||
.fc-event-skin {
|
||||
background-color : #007ccd;
|
||||
border : 1px solid #007ccd;
|
||||
border-radius : 4px;
|
||||
text-align : center;
|
||||
}
|
||||
.fc-event-skin {
|
||||
background-color : #007ccd;
|
||||
border : 1px solid #007ccd;
|
||||
border-radius : 4px;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.fc-event {
|
||||
.clickable;
|
||||
.fc-event {
|
||||
.clickable;
|
||||
|
||||
.status {
|
||||
margin-right : 4px;
|
||||
}
|
||||
}
|
||||
.status {
|
||||
margin-right : 4px;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
background-color : #eeeeee;
|
||||
}
|
||||
th {
|
||||
background-color : #eeeeee;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size : 17.5px;
|
||||
}
|
||||
h2 {
|
||||
font-size : 17.5px;
|
||||
}
|
||||
|
||||
.fc-state-highlight {
|
||||
background : #dbdbdb;
|
||||
}
|
||||
.fc-state-highlight {
|
||||
background : #dbdbdb;
|
||||
}
|
||||
|
||||
.past {
|
||||
opacity : 0.8;
|
||||
}
|
||||
.past {
|
||||
opacity : 0.8;
|
||||
}
|
||||
|
||||
.fc-title {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.fc-list-table {
|
||||
.past {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.event {
|
||||
display : inline-block;
|
||||
width : 100%;
|
||||
margin-bottom : 10px;
|
||||
border-top : 1px solid #eeeeee;
|
||||
padding-top : 10px;
|
||||
display : inline-block;
|
||||
width : 100%;
|
||||
margin-bottom : 10px;
|
||||
border-top : 1px solid #eeeeee;
|
||||
padding-top : 10px;
|
||||
|
||||
h4 {
|
||||
font-weight : 500;
|
||||
color : #008dcd;
|
||||
margin : 5px 0px;
|
||||
}
|
||||
h4 {
|
||||
font-weight : 500;
|
||||
color : #008dcd;
|
||||
margin : 5px 0px;
|
||||
}
|
||||
|
||||
p {
|
||||
color : #999999;
|
||||
margin : 0px;
|
||||
}
|
||||
p {
|
||||
color : #999999;
|
||||
margin : 0px;
|
||||
}
|
||||
|
||||
.date {
|
||||
text-align : center;
|
||||
display : inline-block;
|
||||
border-left : 4px solid #eeeeee;
|
||||
padding-left : 16px;
|
||||
float : left;
|
||||
margin-right : 20px;
|
||||
.date {
|
||||
text-align : center;
|
||||
display : inline-block;
|
||||
border-left : 4px solid #eeeeee;
|
||||
padding-left : 16px;
|
||||
float : left;
|
||||
margin-right : 20px;
|
||||
|
||||
h4 {
|
||||
line-height : 1em;
|
||||
color : #555555;
|
||||
font-weight : 300;
|
||||
text-transform : uppercase;
|
||||
}
|
||||
h4 {
|
||||
line-height : 1em;
|
||||
color : #555555;
|
||||
font-weight : 300;
|
||||
text-transform : uppercase;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight : 500;
|
||||
line-height : 0.8em;
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
font-weight : 500;
|
||||
line-height : 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.primary {
|
||||
border-color : @btn-primary-bg;
|
||||
}
|
||||
.primary {
|
||||
border-color : @btn-primary-bg;
|
||||
}
|
||||
|
||||
.info {
|
||||
border-color : @btn-info-bg;
|
||||
}
|
||||
.info {
|
||||
border-color : @btn-info-bg;
|
||||
}
|
||||
|
||||
.inverse {
|
||||
border-color : @btn-link-disabled-color;
|
||||
}
|
||||
.inverse {
|
||||
border-color : @btn-link-disabled-color;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-color : @btn-warning-bg;
|
||||
}
|
||||
.warning {
|
||||
border-color : @btn-warning-bg;
|
||||
}
|
||||
|
||||
.danger {
|
||||
border-color : @btn-danger-bg;
|
||||
}
|
||||
.danger {
|
||||
border-color : @btn-danger-bg;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.success {
|
||||
border-color : @btn-success-bg;
|
||||
}
|
||||
.success {
|
||||
border-color : @btn-success-bg;
|
||||
}
|
||||
|
||||
.purple {
|
||||
border-color : @nzbdronePurple;
|
||||
}
|
||||
.purple {
|
||||
border-color : @nzbdronePurple;
|
||||
}
|
||||
|
||||
.pink {
|
||||
border-color : @nzbdronePink;
|
||||
}
|
||||
.pink {
|
||||
border-color : @nzbdronePink;
|
||||
}
|
||||
|
||||
.premiere {
|
||||
border-color : @droneTeal;
|
||||
}
|
||||
.premiere {
|
||||
border-color : @droneTeal;
|
||||
}
|
||||
|
||||
.unmonitored {
|
||||
border-color : grey;
|
||||
}
|
||||
.unmonitored {
|
||||
border-color : grey;
|
||||
}
|
||||
|
||||
.episode-title {
|
||||
.btn-link;
|
||||
.text-overflow;
|
||||
color : @link-color;
|
||||
margin-top : 1px;
|
||||
display : inline-block;
|
||||
.episode-title {
|
||||
.btn-link;
|
||||
.text-overflow;
|
||||
color : @link-color;
|
||||
margin-top : 1px;
|
||||
display : inline-block;
|
||||
|
||||
@media (max-width: @screen-xs-min) {
|
||||
width : 140px;
|
||||
}
|
||||
@media (max-width: @screen-xs-min) {
|
||||
width : 140px;
|
||||
}
|
||||
|
||||
@media (min-width: @screen-md-min) {
|
||||
width : 135px;
|
||||
}
|
||||
}
|
||||
@media (min-width: @screen-md-min) {
|
||||
width : 135px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.calendar {
|
||||
|
||||
// background-position : -160px -128px;
|
||||
|
||||
.primary {
|
||||
border-color : @btn-primary-bg;
|
||||
background-color : @btn-primary-bg;
|
||||
.primary {
|
||||
border-color : @btn-primary-bg;
|
||||
background-color : @btn-primary-bg;
|
||||
|
||||
.color-impaired-background-gradient(90deg, @btn-primary-bg);
|
||||
}
|
||||
.color-impaired-background-gradient(90deg, @btn-primary-bg);
|
||||
}
|
||||
|
||||
.info {
|
||||
border-color : @btn-info-bg;
|
||||
background-color : @btn-info-bg;
|
||||
}
|
||||
.info {
|
||||
border-color : @btn-info-bg;
|
||||
background-color : @btn-info-bg;
|
||||
}
|
||||
|
||||
.inverse {
|
||||
border-color : @btn-link-disabled-color;
|
||||
background-color : @btn-link-disabled-color;
|
||||
}
|
||||
.inverse {
|
||||
border-color : @btn-link-disabled-color;
|
||||
background-color : @btn-link-disabled-color;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-color : @btn-warning-bg;
|
||||
background-color : @btn-warning-bg;
|
||||
.warning {
|
||||
border-color : @btn-warning-bg;
|
||||
background-color : @btn-warning-bg;
|
||||
|
||||
.color-impaired-background-gradient(90deg, @btn-warning-bg);
|
||||
}
|
||||
.color-impaired-background-gradient(90deg, @btn-warning-bg);
|
||||
}
|
||||
|
||||
.danger {
|
||||
border-color : @btn-danger-bg;
|
||||
background-color : @btn-danger-bg;
|
||||
.danger {
|
||||
border-color : @btn-danger-bg;
|
||||
background-color : @btn-danger-bg;
|
||||
color: white;
|
||||
.color-impaired-background-gradient(90deg, @btn-danger-bg);
|
||||
}
|
||||
|
||||
.color-impaired-background-gradient(90deg, @btn-danger-bg);
|
||||
}
|
||||
.success {
|
||||
border-color : @btn-success-bg;
|
||||
background-color : @btn-success-bg;
|
||||
}
|
||||
|
||||
.success {
|
||||
border-color : @btn-success-bg;
|
||||
background-color : @btn-success-bg;
|
||||
}
|
||||
.purple {
|
||||
border-color : @nzbdronePurple;
|
||||
background-color : @nzbdronePurple;
|
||||
}
|
||||
|
||||
.purple {
|
||||
border-color : @nzbdronePurple;
|
||||
background-color : @nzbdronePurple;
|
||||
}
|
||||
.pink {
|
||||
border-color : @nzbdronePink;
|
||||
background-color : @nzbdronePink;
|
||||
}
|
||||
|
||||
.pink {
|
||||
border-color : @nzbdronePink;
|
||||
background-color : @nzbdronePink;
|
||||
}
|
||||
.premiere {
|
||||
border-color : @droneTeal;
|
||||
background-color : @droneTeal;
|
||||
|
||||
.premiere {
|
||||
border-color : @droneTeal;
|
||||
background-color : @droneTeal;
|
||||
.color-impaired-background-gradient(90deg, @droneTeal);
|
||||
}
|
||||
|
||||
.color-impaired-background-gradient(90deg, @droneTeal);
|
||||
}
|
||||
.unmonitored {
|
||||
border-color : grey;
|
||||
background-color : grey;
|
||||
|
||||
.unmonitored {
|
||||
border-color : grey;
|
||||
background-color : grey;
|
||||
.color-impaired-background-gradient(45deg, grey);
|
||||
}
|
||||
|
||||
.color-impaired-background-gradient(45deg, grey);
|
||||
}
|
||||
.chart {
|
||||
margin-top : 2px;
|
||||
margin-right : 2px;
|
||||
line-height : 12px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
margin-top : 2px;
|
||||
margin-right : 2px;
|
||||
line-height : 12px;
|
||||
}
|
||||
.legend-labels {
|
||||
max-width : 100%;
|
||||
width : 500px;
|
||||
|
||||
.legend-labels {
|
||||
max-width : 100%;
|
||||
width : 500px;
|
||||
@media (max-width: @screen-xs-min) {
|
||||
width : 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-xs-min) {
|
||||
width : 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.legend-label {
|
||||
display : inline-block;
|
||||
width : 150px;
|
||||
}
|
||||
.legend-label {
|
||||
display : inline-block;
|
||||
width : 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.ical {
|
||||
color: @btn-link-disabled-color;
|
||||
cursor: pointer;
|
||||
color: @btn-link-disabled-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ical-url {
|
||||
|
||||
input, input[readonly] {
|
||||
cursor : text;
|
||||
}
|
||||
input, input[readonly] {
|
||||
cursor : text;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-title {
|
||||
text-align : center;
|
||||
text-align : center;
|
||||
|
||||
h2 {
|
||||
margin-top : 0px;
|
||||
margin-bottom : 5px;
|
||||
}
|
||||
h2 {
|
||||
margin-top : 0px;
|
||||
margin-bottom : 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-toolbar {
|
||||
.page-toolbar {
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
.page-toolbar {
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,8 @@ module.exports = Backgrid.Cell.extend({
|
||||
this.$el.empty();
|
||||
if (this.model.get("movieFile")) {
|
||||
var profileId = this.model.get("movieFile").quality.quality.id;
|
||||
|
||||
var profile = _.findWhere(ProfileCollection.models, { id : profileId });
|
||||
|
||||
if (profile) {
|
||||
this.$el.html(profile.get('name'));
|
||||
} else {
|
||||
this.$el.html(this.model.get("movieFile").quality.quality.name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
15
src/UI/Cells/FileTitleCell.js
Normal file
15
src/UI/Cells/FileTitleCell.js
Normal file
@@ -0,0 +1,15 @@
|
||||
var NzbDroneCell = require('./NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'file-title-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
var title = this.model.get('relativePath');
|
||||
this.$el.html(title);
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
23
src/UI/Cells/MediaInfoCell.js
Normal file
23
src/UI/Cells/MediaInfoCell.js
Normal file
@@ -0,0 +1,23 @@
|
||||
var NzbDroneCell = require('./NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'release-title-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
var info = this.model.get('mediaInfo');
|
||||
if (info) {
|
||||
var runtime = info.runTime;
|
||||
if (runtime) {
|
||||
runtime = runtime.split(".")[0];
|
||||
}
|
||||
var video = "{0} ({1}x{2}) ({3})".format(info.videoCodec, info.width, info.height, runtime);
|
||||
var audio = "{0} ({1})".format(info.audioFormat, info.audioLanguages);
|
||||
this.$el.html(video + " " + audio);
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user