1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-20 16:44:37 -04:00

Compare commits

...

19 Commits

Author SHA1 Message Date
Leonardo Galli
3d67f6237e Should fix 4K releases not getting parsed. 2017-01-21 00:39:44 +01:00
Krystian Charubin
a691ffa7b7 adds 'Movie Title, The' filename option (#359)
* adds 'Movie Title, The' filename option
* updates the FileNameBuilder.MovieTitleRegex to include new format
2017-01-20 17:42:14 -05:00
Leonardo Galli
aa9537c201 Fix issue when movie file is null. 2017-01-20 20:30:36 +01:00
Leonardo Galli
a3d9fb1c20 Should fix upgrading of existing movie files. 2017-01-20 18:26:18 +01:00
Leonardo Galli
62a1e70c86 Add tests for 4K quality. 2017-01-20 18:21:31 +01:00
Leonardo Galli
93d0d21846 Hopefully a fix for corrupt media covers. 2017-01-20 18:06:58 +01:00
Leonardo Galli
b1c5a3ac14 Fixed blacklist being ignored by download decision maker. 2017-01-20 17:48:47 +01:00
vertigo235
55a525ba2f Add helptext to nzbget "add paused" settings. (#363)
Requires nzbget 16 or greater.
2017-01-20 09:14:15 -05:00
Devin Buhl
a53768463b Merge pull request #358 from Radarr/patch/add-year-to-search
add year to search
2017-01-19 20:07:33 -05:00
Tim Turner
24cbd6bcef Fix issue with reimporting on movie fresh (#357)
Fixes #314. Still have multiple movieFiles issue to clean up.
2017-01-19 19:58:57 -05:00
Devin Buhl
3ab3e66853 Add year to quick search results 2017-01-19 19:56:26 -05:00
Leonardo Galli
40ca469339 Update MovieModule 2017-01-20 00:00:12 +01:00
Tim Turner
2cbd2f719f Merge branch 'develop' of https://github.com/Radarr/Radarr into develop 2017-01-19 16:45:39 -05:00
Tim Turner
53cbfa803b Fix MediaCoversUpdatedEvent broadcast
Cleans exception when updating media covers expects a series Id.
2017-01-19 16:45:29 -05:00
Leonardo Galli
c0b0310bbd Update ISSUE_TEMPLATE.md 2017-01-19 22:10:59 +01:00
Devin Buhl
30e50062a8 Merge pull request #352 from baltoaca/develop
bug fix for 15 movie wanted tab limit
2017-01-19 13:17:40 -05:00
Vlad Ilies
85fd8f2c65 bug fix for 15 movie wanted tab (#348) 2017-01-19 20:08:15 +02:00
Leonardo Galli
f72b042d5d Blacklisting works now. 2017-01-19 17:43:23 +01:00
Leonardo Galli
2d3a3a0677 Update sizing information in settings tab. 2017-01-19 17:40:25 +01:00
28 changed files with 311 additions and 214 deletions

View File

@@ -1,6 +1,8 @@
Please use the search bar and make sure you are not submitting an already submitted issue.
Provide a description of the feature request or bug, the more details the better.
When possible include a log!

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using NzbDrone.Api.Movie;
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Series;
@@ -11,13 +12,14 @@ namespace NzbDrone.Api.Blacklist
{
public int SeriesId { get; set; }
public List<int> EpisodeIds { get; set; }
public int MovieId { get; set; }
public string SourceTitle { get; set; }
public QualityModel Quality { get; set; }
public DateTime Date { get; set; }
public DownloadProtocol Protocol { get; set; }
public string Indexer { get; set; }
public string Message { get; set; }
public MovieResource Movie { get; set; }
public SeriesResource Series { get; set; }
}
@@ -30,7 +32,7 @@ namespace NzbDrone.Api.Blacklist
return new BlacklistResource
{
Id = model.Id,
MovieId = model.MovieId,
SeriesId = model.SeriesId,
EpisodeIds = model.EpisodeIds,
SourceTitle = model.SourceTitle,
@@ -39,7 +41,7 @@ namespace NzbDrone.Api.Blacklist
Protocol = model.Protocol,
Indexer = model.Indexer,
Message = model.Message,
Movie = model.Movie.ToResource(),
Series = model.Series.ToResource()
};
}

View File

@@ -201,6 +201,8 @@ namespace NzbDrone.Api.Movie
//var mappings = null;//_sceneMappingService.FindByTvdbId(resource.TvdbId);
//if (mappings == null) return;
//Not necessary anymore
//resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
}
@@ -239,7 +241,7 @@ namespace NzbDrone.Api.Movie
public void Handle(MediaCoversUpdatedEvent message)
{
//BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
BroadcastResourceChange(ModelAction.Updated, message.Movie.Id);
}
}
}

View File

@@ -236,7 +236,7 @@ namespace NzbDrone.Api.Series
public void Handle(MediaCoversUpdatedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
//BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
}
}
}

View File

@@ -47,5 +47,17 @@ namespace NzbDrone.Core.Test.ParserTests
{
QualityParser.ParseQuality(title).Revision.Version.Should().Be(version);
}
[TestCase("Deadpool 2016 2160p 4K UltraHD BluRay DTS-HD MA 7 1 x264-Whatevs", 19)]
[TestCase("Deadpool 2016 2160p 4K UltraHD DTS-HD MA 7 1 x264-Whatevs", 16)]
[TestCase("Deadpool 2016 4K 2160p UltraHD BluRay AAC2 0 HEVC x265", 19)]
[TestCase("The Revenant 2015 2160p UHD BluRay DTS x264-Whatevs", 19)]
[TestCase("The Revenant 2015 2160p UHD BluRay FLAC 7 1 x264-Whatevs", 19)]
[TestCase("The Martian 2015 2160p Ultra HD BluRay DTS-HD MA 7 1 x264-Whatevs", 19)]
[TestCase("Into the Inferno 2016 2160p Netflix WEBRip DD5 1 x264-Whatevs", 18)]
public void should_parse_ultrahd_from_title(string title, int version)
{
QualityParser.ParseQuality(title).Quality.Id.Should().Be(version);
}
}
}

View File

@@ -11,6 +11,8 @@ namespace NzbDrone.Core.Blacklisting
{
public int SeriesId { get; set; }
public Series Series { get; set; }
public int MovieId { get; set; }
public Movie Movie { get; set; }
public List<int> EpisodeIds { get; set; }
public string SourceTitle { get; set; }
public QualityModel Quality { get; set; }

View File

@@ -10,7 +10,7 @@ namespace NzbDrone.Core.Blacklisting
{
List<Blacklist> BlacklistedByTitle(int seriesId, string sourceTitle);
List<Blacklist> BlacklistedByTorrentInfoHash(int seriesId, string torrentInfoHash);
List<Blacklist> BlacklistedBySeries(int seriesId);
List<Blacklist> BlacklistedByMovie(int seriesId);
}
public class BlacklistRepository : BasicRepository<Blacklist>, IBlacklistRepository
@@ -20,15 +20,15 @@ namespace NzbDrone.Core.Blacklisting
{
}
public List<Blacklist> BlacklistedByTitle(int seriesId, string sourceTitle)
public List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle)
{
return Query.Where(e => e.SeriesId == seriesId)
return Query.Where(e => e.MovieId == movieId)
.AndWhere(e => e.SourceTitle.Contains(sourceTitle));
}
public List<Blacklist> BlacklistedByTorrentInfoHash(int seriesId, string torrentInfoHash)
public List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash)
{
return Query.Where(e => e.SeriesId == seriesId)
return Query.Where(e => e.MovieId == movieId)
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash));
}
@@ -37,9 +37,14 @@ namespace NzbDrone.Core.Blacklisting
return Query.Where(b => b.SeriesId == seriesId);
}
public List<Blacklist> BlacklistedByMovie(int movieId)
{
return Query.Where(b => b.MovieId == movieId);
}
protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec)
{
var baseQuery = query.Join<Blacklist, Series>(JoinType.Inner, h => h.Series, (h, s) => h.SeriesId == s.Id);
var baseQuery = query.Join<Blacklist, Movie>(JoinType.Inner, h => h.Movie, (h, s) => h.MovieId == s.Id);
return base.GetPagedQuery(baseQuery, pagingSpec);
}

View File

@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Blacklisting
IExecute<ClearBlacklistCommand>,
IHandle<DownloadFailedEvent>,
IHandleAsync<SeriesDeletedEvent>
IHandleAsync<MovieDeletedEvent>
{
private readonly IBlacklistRepository _blacklistRepository;
@@ -128,8 +128,9 @@ namespace NzbDrone.Core.Blacklisting
{
var blacklist = new Blacklist
{
SeriesId = message.SeriesId,
SeriesId = 0,
EpisodeIds = message.EpisodeIds,
MovieId = message.MovieId,
SourceTitle = message.SourceTitle,
Quality = message.Quality,
Date = DateTime.UtcNow,
@@ -144,9 +145,9 @@ namespace NzbDrone.Core.Blacklisting
_blacklistRepository.Insert(blacklist);
}
public void HandleAsync(SeriesDeletedEvent message)
public void HandleAsync(MovieDeletedEvent message)
{
var blacklisted = _blacklistRepository.BlacklistedBySeries(message.Series.Id);
var blacklisted = _blacklistRepository.BlacklistedByMovie(message.Movie.Id);
_blacklistRepository.DeleteMany(blacklisted);
}

View File

@@ -0,0 +1,22 @@
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(122)]
public class add_movieid_to_blacklist : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Blacklist").AddColumn("MovieId").AsInt32().Nullable().WithDefaultValue(0);
Alter.Table("Blacklist").AlterColumn("SeriesId").AsInt32().Nullable();
Alter.Table("Blacklist").AlterColumn("EpisodeIds").AsString().Nullable();
}
}
}

View File

@@ -33,7 +33,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{
throw new NotImplementedException();
if (_blacklistService.Blacklisted(subject.Movie.Id, subject.Release))
{
_logger.Debug("{0} is blacklisted, rejecting.", subject.Release.Title);
return Decision.Reject("Release is blacklisted");
}
return Decision.Accept();
}

View File

@@ -57,7 +57,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; }
[FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox)]
[FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NzbGet version 16.0")]
public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate()

View File

@@ -114,7 +114,7 @@ namespace NzbDrone.Core.MediaCover
}
}
private void EnsureCovers(Movie movie)
private void EnsureCovers(Movie movie, int retried = 0)
{
foreach (var cover in movie.Images)
{
@@ -130,7 +130,25 @@ namespace NzbDrone.Core.MediaCover
}
catch (WebException e)
{
_logger.Warn(string.Format("Couldn't download media cover for {0}. {1}", movie, e.Message));
if (e.Status == WebExceptionStatus.ProtocolError)
{
_logger.Warn(e, "Server returned different code than 200. The poster is probably not available yet.");
return;
}
_logger.Warn(e, string.Format("Couldn't download media cover for {0}. {1}", movie, e.Message));
if (retried < 3)
{
retried = +1;
_logger.Warn("Retrying for the {0}. time in ten seconds.", retried);
System.Threading.Thread.Sleep(10*1000);
EnsureCovers(movie, retried);
}
else
{
_logger.Warn(e, "Couldn't download media cover even after retrying five times :(.");
}
}
catch (Exception e)
{

View File

@@ -68,22 +68,22 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{
//check if already imported
if (importResults.Select(r => r.ImportDecision.LocalMovie.Movie)
.Select(e => e.Id).Contains(localMovie.Movie.Id))
.Select(m => m.Id).Contains(localMovie.Movie.Id))
{
importResults.Add(new ImportResult(importDecision, "Movie has already been imported"));
continue;
}
var episodeFile = new MovieFile();
episodeFile.DateAdded = DateTime.UtcNow;
episodeFile.MovieId = localMovie.Movie.Id;
episodeFile.Path = localMovie.Path.CleanFilePath();
episodeFile.Size = _diskProvider.GetFileSize(localMovie.Path);
episodeFile.Quality = localMovie.Quality;
episodeFile.MediaInfo = localMovie.MediaInfo;
episodeFile.Movie = localMovie.Movie;
episodeFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup;
episodeFile.Edition = localMovie.ParsedMovieInfo.Edition;
var movieFile = new MovieFile();
movieFile.DateAdded = DateTime.UtcNow;
movieFile.MovieId = localMovie.Movie.Id;
movieFile.Path = localMovie.Path.CleanFilePath();
movieFile.Size = _diskProvider.GetFileSize(localMovie.Path);
movieFile.Quality = localMovie.Quality;
movieFile.MediaInfo = localMovie.MediaInfo;
movieFile.Movie = localMovie.Movie;
movieFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup;
movieFile.Edition = localMovie.ParsedMovieInfo.Edition;
bool copyOnly;
switch (importMode)
@@ -102,17 +102,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (newDownload)
{
episodeFile.SceneName = GetSceneName(downloadClientItem, localMovie);
movieFile.SceneName = GetSceneName(downloadClientItem, localMovie);
var moveResult = _episodeFileUpgrader.UpgradeMovieFile(episodeFile, localMovie, copyOnly);
var moveResult = _episodeFileUpgrader.UpgradeMovieFile(movieFile, localMovie, copyOnly); //TODO: Check if this works
oldFiles = moveResult.OldFiles;
}
else
{
episodeFile.RelativePath = localMovie.Movie.Path.GetRelativePath(episodeFile.Path);
movieFile.RelativePath = localMovie.Movie.Path.GetRelativePath(movieFile.Path);
}
_mediaFileService.Add(episodeFile);
_mediaFileService.Add(movieFile);
importResults.Add(new ImportResult(importDecision));
if (newDownload)
@@ -122,22 +122,22 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (downloadClientItem != null)
{
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, episodeFile, newDownload, downloadClientItem.DownloadClient, downloadClientItem.DownloadId, downloadClientItem.IsReadOnly));
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, movieFile, newDownload, downloadClientItem.DownloadClient, downloadClientItem.DownloadId, downloadClientItem.IsReadOnly));
}
else
{
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, episodeFile, newDownload));
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, movieFile, newDownload));
}
if (newDownload)
{
_eventAggregator.PublishEvent(new MovieDownloadedEvent(localMovie, episodeFile, oldFiles));
_eventAggregator.PublishEvent(new MovieDownloadedEvent(localMovie, movieFile, oldFiles));
}
}
catch (Exception e)
{
_logger.Warn(e, "Couldn't import episode " + localMovie);
importResults.Add(new ImportResult(importDecision, "Failed to import episode"));
_logger.Warn(e, "Couldn't import movie " + localMovie);
importResults.Add(new ImportResult(importDecision, "Failed to import movie"));
}
}

View File

@@ -111,11 +111,11 @@ namespace NzbDrone.Core.MediaFiles
public List<string> FilterExistingFiles(List<string> files, Movie movie)
{
var seriesFiles = GetFilesBySeries(movie.Id).Select(f => Path.Combine(movie.Path, f.RelativePath)).ToList();
var movieFiles = GetFilesByMovie(movie.Id).Select(f => Path.Combine(movie.Path, f.RelativePath)).ToList();
if (!seriesFiles.Any()) return files;
if (!movieFiles.Any()) return files;
return files.Except(seriesFiles, PathEqualityComparer.Instance).ToList();
return files.Except(movieFiles, PathEqualityComparer.Instance).ToList();
}
public EpisodeFile Get(int id)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using NLog;
@@ -18,14 +18,17 @@ namespace NzbDrone.Core.MediaFiles
public class MediaFileTableCleanupService : IMediaFileTableCleanupService
{
private readonly IMediaFileService _mediaFileService;
private readonly IMovieService _movieService;
private readonly IEpisodeService _episodeService;
private readonly Logger _logger;
public MediaFileTableCleanupService(IMediaFileService mediaFileService,
IMovieService movieService,
IEpisodeService episodeService,
Logger logger)
{
_mediaFileService = mediaFileService;
_movieService = movieService;
_episodeService = episodeService;
_logger = logger;
}
@@ -89,61 +92,39 @@ namespace NzbDrone.Core.MediaFiles
public void Clean(Movie movie, List<string> filesOnDisk)
{
//TODO: Update implementation for movies.
var seriesFiles = _mediaFileService.GetFilesBySeries(movie.Id);
var episodes = _episodeService.GetEpisodeBySeries(movie.Id);
var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id);
var filesOnDiskKeys = new HashSet<string>(filesOnDisk, PathEqualityComparer.Instance);
foreach (var seriesFile in seriesFiles)
foreach(var movieFile in movieFiles)
{
var episodeFile = seriesFile;
var episodeFilePath = Path.Combine(movie.Path, episodeFile.RelativePath);
var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath);
try
{
if (!filesOnDiskKeys.Contains(episodeFilePath))
if (!filesOnDiskKeys.Contains(movieFilePath))
{
_logger.Debug("File [{0}] no longer exists on disk, removing from db", episodeFilePath);
_mediaFileService.Delete(seriesFile, DeleteMediaFileReason.MissingFromDisk);
_logger.Debug("File [{0}] no longer exists on disk, removing from db", movieFilePath);
_mediaFileService.Delete(movieFile, DeleteMediaFileReason.MissingFromDisk);
continue;
}
if (episodes.None(e => e.EpisodeFileId == episodeFile.Id))
{
_logger.Debug("File [{0}] is not assigned to any episodes, removing from db", episodeFilePath);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.NoLinkedEpisodes);
continue;
}
//var localMovie = _parsingService.GetLocalMovie(movieFile.Path, movie);
// var localEpsiode = _parsingService.GetLocalEpisode(episodeFile.Path, series);
//
// if (localEpsiode == null || episodes.Count != localEpsiode.Episodes.Count)
// {
// _logger.Debug("File [{0}] parsed episodes has changed, removing from db", episodeFile.Path);
// _mediaFileService.Delete(episodeFile);
// continue;
// }
//if (localMovie == null)
//{
// _logger.Debug("File [{0}] parsed episodes has changed, removing from db", localMovie.Path);
// _mediaFileService.Delete(localMovie);
// continue;
//}
}
catch (Exception ex)
{
var errorMessage = string.Format("Unable to cleanup EpisodeFile in DB: {0}", episodeFile.Id);
var errorMessage = string.Format("Unable to cleanup MovieFile in DB: {0}", movieFile.Id);
_logger.Error(ex, errorMessage);
}
}
foreach (var e in episodes)
{
var episode = e;
if (episode.EpisodeFileId > 0 && seriesFiles.None(f => f.Id == episode.EpisodeFileId))
{
episode.EpisodeFileId = 0;
_episodeService.UpdateEpisode(episode);
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -107,7 +107,7 @@ namespace NzbDrone.Core.MediaFiles
return;
}
/* var movies = _movieService.MoviesWithFiles(message.Series.Id);
var movies = _movieService.MoviesWithFiles(message.Movie.Id);
var movieFiles = new List<MovieFile>();
var updated = new List<MovieFile>();
@@ -119,7 +119,7 @@ namespace NzbDrone.Core.MediaFiles
movieFiles.Add(movieFile);
if (ChangeFileDate(movieFile, message.Series, moviesInFile))
if (ChangeFileDate(movieFile, message.Movie))
{
updated.Add(movieFile);
}
@@ -127,13 +127,13 @@ namespace NzbDrone.Core.MediaFiles
if (updated.Any())
{
_logger.ProgressDebug("Changed file date for {0} files of {1} in {2}", updated.Count, movieFiles.Count, message.Series.Title);
_logger.ProgressDebug("Changed file date for {0} files of {1} in {2}", updated.Count, movieFiles.Count, message.Movie.Title);
}
else
{
_logger.ProgressDebug("No file dates changed for {0}", message.Series.Title);
}*/
_logger.ProgressDebug("No file dates changed for {0}", message.Movie.Title);
}
}
private bool ChangeFileDateToLocalAirDate(string filePath, string fileDate, string fileTime)

View File

@@ -38,10 +38,13 @@ namespace NzbDrone.Core.MediaFiles
public MovieFileMoveResult UpgradeMovieFile(MovieFile episodeFile, LocalMovie localEpisode, bool copyOnly = false)
{
_logger.Trace("Upgrading existing episode file.");
var moveFileResult = new MovieFileMoveResult();
localEpisode.Movie.MovieFile.LazyLoad();
var existingFile = localEpisode.Movie.MovieFile;
existingFile.LazyLoad();
if (existingFile.IsLoaded)
if (existingFile.IsLoaded && existingFile.Value != null)
{
var file = existingFile.Value;
var episodeFilePath = Path.Combine(localEpisode.Movie.Path, file.RelativePath);
@@ -55,6 +58,10 @@ namespace NzbDrone.Core.MediaFiles
moveFileResult.OldFiles.Add(file);
_mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade);
}
else
{
_logger.Warn("The existing movie file was not lazy loaded.");
}

View File

@@ -183,6 +183,7 @@
<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\122_add_movieid_to_blacklist.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" />

View File

@@ -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|Original))(?<separator>[- ._])(Clean)?Title)\})",
public static readonly Regex MovieTitleRegex = new Regex(@"(?<token>\{((?:(Movie|Original))(?<separator>[- ._])(Clean)?Title(The)?)\})",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
@@ -316,6 +316,22 @@ namespace NzbDrone.Core.Organizer
return title;
}
public static string TitleThe(string title)
{
string[] prefixes = { "The ", "An ", "A " };
foreach (string prefix in prefixes)
{
int prefix_length = prefix.Length;
if (prefix.ToLower() == title.Substring(0, prefix_length).ToLower())
{
title = title.Substring(prefix_length) + ", " + prefix.Trim();
break;
}
}
return title.Trim();
}
public static string CleanFileName(string name, bool replace = true)
{
string result = name;
@@ -472,6 +488,7 @@ namespace NzbDrone.Core.Organizer
{
tokenHandlers["{Movie Title}"] = m => movie.Title;
tokenHandlers["{Movie CleanTitle}"] = m => CleanTitle(movie.Title);
tokenHandlers["{Movie Title The}"] = m => TitleThe(movie.Title);
}
private void AddReleaseDateTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, int releaseYear)

View File

@@ -46,7 +46,7 @@ namespace NzbDrone.Core.Organizer
_movie = new Movie
{
Title = "Movie Title",
Title = "The Movie Title",
Year = 2010,
ImdbId = "tt0066921"
};

View File

@@ -268,7 +268,7 @@ namespace NzbDrone.Core.Parser
private static readonly Regex ReportImdbId = new Regex(@"(?<imdbid>tt\d{9})", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex SimpleTitleRegex = new Regex(@"(?:480[ip]|576[ip]|720[ip]|1080[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*:|]|848x480|1280x720|1920x1080|(8|10)b(it)?)\s*",
private static readonly Regex SimpleTitleRegex = new Regex(@"(?:480[ip]|576[ip]|720[ip]|1080[ip]|2160[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*:|]|848x480|1280x720|1920x1080|(8|10)b(it)?)\s*",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex WebsitePrefixRegex = new Regex(@"^\[\s*[a-z]+(\.[a-z]+)+\s*\][- ]*",

View File

@@ -4,6 +4,8 @@ using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Datastore.Extensions;
using Marr.Data.QGen;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Core.Tv
{
@@ -15,6 +17,7 @@ namespace NzbDrone.Core.Tv
Movie FindByImdbId(string imdbid);
Movie FindByTitleSlug(string slug);
List<Movie> MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
List<Movie> MoviesWithFiles(int movieId);
PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec);
List<Movie> GetMoviesByFileId(int fileId);
void SetFileId(int fileId, int movieId);
@@ -134,20 +137,29 @@ namespace NzbDrone.Core.Tv
return query.ToList();
}
public List<Movie> MoviesWithFiles(int movieId)
{
return Query.Join<Movie, MovieFile>(JoinType.Inner, m => m.MovieFile, (m, mf) => m.MovieFileId == mf.Id)
.Where(m => m.Id == movieId);
}
public PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec)
{
var query = Query.Where(pagingSpec.FilterExpression)
pagingSpec.TotalRecords = GetMoviesWithoutFilesQuery(pagingSpec).GetRowCount();
pagingSpec.Records = GetMoviesWithoutFilesQuery(pagingSpec).ToList();
return pagingSpec;
}
public SortBuilder<Movie> GetMoviesWithoutFilesQuery(PagingSpec<Movie> pagingSpec)
{
return Query.Where(pagingSpec.FilterExpression)
.AndWhere(m => m.MovieFileId == 0)
.AndWhere(m => m.Status == MovieStatusType.Released)
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
.Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize);
pagingSpec.Records = query.ToList();
pagingSpec.TotalRecords = pagingSpec.Records.Count;
return pagingSpec;
}
}
}

View File

@@ -35,6 +35,7 @@ namespace NzbDrone.Core.Tv
List<Movie> UpdateMovie(List<Movie> movie);
bool MoviePathExists(string folder);
void RemoveAddOptions(Movie movie);
List<Movie> MoviesWithFiles(int movieId);
}
public class MovieService : IMovieService, IHandle<MovieFileAddedEvent>,
@@ -235,6 +236,11 @@ namespace NzbDrone.Core.Tv
return episodes;
}
public List<Movie> MoviesWithFiles(int movieId)
{
return _movieRepository.MoviesWithFiles(movieId);
}
public PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec)
{
var movieResult = _movieRepository.MoviesWithoutFiles(pagingSpec);

View File

@@ -25,7 +25,9 @@ $.fn.bindSearch = function() {
minLength : 1
}, {
name : 'series',
displayKey : 'title',
displayKey : function(series) {
return series.title + ' (' + series.year + ')';
},
source : substringMatcher()
});

View File

@@ -4,6 +4,7 @@
<li><a href="#" data-token="Movie Title">Movie Title</a></li>
<li><a href="#" data-token="Movie.Title">Movie.Title</a></li>
<li><a href="#" data-token="Movie_Title">Movie_Title</a></li>
<li><a href="#" data-token="Movie TitleThe">Movie Title, The</a></li>
<li><a href="#" data-token="Movie CleanTitle">Movie CleanTitle</a></li>
<li><a href="#" data-token="Movie.CleanTitle">Movie.CleanTitle</a></li>
<li><a href="#" data-token="Movie_CleanTitle">Movie_CleanTitle</a></li>

View File

@@ -1,16 +1,16 @@
<fieldset>
<legend>Quality Definitions</legend>
<div class="col-md-11">
<div id="quality-definition-list">
<div class="quality-header x-header hidden-xs">
<div class="row">
<span class="col-md-2 col-sm-3">Quality</span>
<span class="col-md-2 col-sm-3">Title</span>
<span class="col-md-4 col-sm-6">Size Limit <i class="icon-sonarr-info" title="Limits are automatically adjusted for the series runtime and number of episodes in the file." /></span>
</div>
</div>
<div class="rows x-rows">
</div>
</div>
</div>
<legend>Quality Definitions</legend>
<div class="col-md-11">
<div id="quality-definition-list">
<div class="quality-header x-header hidden-xs">
<div class="row">
<span class="col-md-2 col-sm-3">Quality</span>
<span class="col-md-2 col-sm-3">Title</span>
<span class="col-md-4 col-sm-6">Size Limit <i class="icon-sonarr-warning" title="Limits are automatically adjusted for the movie runtime." /></span>
</div>
</div>
<div class="rows x-rows">
</div>
</div>
</div>
</fieldset>

View File

@@ -4,92 +4,92 @@ require('jquery-ui');
var FormatHelpers = require('../../../Shared/FormatHelpers');
var view = Marionette.ItemView.extend({
template : 'Settings/Quality/Definition/QualityDefinitionItemViewTemplate',
className : 'row',
slider : {
min : 0,
max : 200,
step : 0.1
},
template : 'Settings/Quality/Definition/QualityDefinitionItemViewTemplate',
className : 'row',
ui : {
sizeSlider : '.x-slider',
thirtyMinuteMinSize : '.x-min-thirty',
sixtyMinuteMinSize : '.x-min-sixty',
thirtyMinuteMaxSize : '.x-max-thirty',
sixtyMinuteMaxSize : '.x-max-sixty'
},
slider : {
min : 0,
max : 200,
step : 0.1
},
events : {
'slide .x-slider' : '_updateSize'
},
ui : {
sizeSlider : '.x-slider',
thirtyMinuteMinSize : '.x-min-thirty',
sixtyMinuteMinSize : '.x-min-sixty',
thirtyMinuteMaxSize : '.x-max-thirty',
sixtyMinuteMaxSize : '.x-max-sixty'
},
initialize : function(options) {
this.profileCollection = options.profiles;
},
events : {
'slide .x-slider' : '_updateSize'
},
onRender : function() {
if (this.model.get('quality').id === 0) {
this.$el.addClass('row advanced-setting');
}
initialize : function(options) {
this.profileCollection = options.profiles;
},
this.ui.sizeSlider.slider({
range : true,
min : this.slider.min,
max : this.slider.max,
step : this.slider.step,
values : [
this.model.get('minSize') || this.slider.min,
this.model.get('maxSize') || this.slider.max
]
});
onRender : function() {
if (this.model.get('quality').id === 0) {
this.$el.addClass('row advanced-setting');
}
this._changeSize();
},
this.ui.sizeSlider.slider({
range : true,
min : this.slider.min,
max : this.slider.max,
step : this.slider.step,
values : [
this.model.get('minSize') || this.slider.min,
this.model.get('maxSize') || this.slider.max
]
});
_updateSize : function(event, ui) {
var minSize = ui.values[0];
var maxSize = ui.values[1];
if (maxSize === this.slider.max) {
maxSize = null;
}
this.model.set('minSize', minSize);
this.model.set('maxSize', maxSize);
this._changeSize();
},
this._changeSize();
},
_updateSize : function(event, ui) {
var minSize = ui.values[0];
var maxSize = ui.values[1];
_changeSize : function() {
var minSize = this.model.get('minSize') || this.slider.min;
var maxSize = this.model.get('maxSize') || null;
{
var minBytes = minSize * 1024 * 1024;
var minThirty = FormatHelpers.bytes(minBytes * 30, 2);
var minSixty = FormatHelpers.bytes(minBytes * 60, 2);
if (maxSize === this.slider.max) {
maxSize = null;
}
this.ui.thirtyMinuteMinSize.html(minThirty);
this.ui.sixtyMinuteMinSize.html(minSixty);
}
this.model.set('minSize', minSize);
this.model.set('maxSize', maxSize);
{
if (maxSize === 0 || maxSize === null) {
this.ui.thirtyMinuteMaxSize.html('Unlimited');
this.ui.sixtyMinuteMaxSize.html('Unlimited');
} else {
var maxBytes = maxSize * 1024 * 1024;
var maxThirty = FormatHelpers.bytes(maxBytes * 30, 2);
var maxSixty = FormatHelpers.bytes(maxBytes * 60, 2);
this._changeSize();
},
this.ui.thirtyMinuteMaxSize.html(maxThirty);
this.ui.sixtyMinuteMaxSize.html(maxSixty);
}
}
}
_changeSize : function() {
var minSize = this.model.get('minSize') || this.slider.min;
var maxSize = this.model.get('maxSize') || null;
{
var minBytes = minSize * 1024 * 1024;
var minThirty = FormatHelpers.bytes(minBytes * 90, 2);
var minSixty = FormatHelpers.bytes(minBytes * 140, 2);
this.ui.thirtyMinuteMinSize.html(minThirty);
this.ui.sixtyMinuteMinSize.html(minSixty);
}
{
if (maxSize === 0 || maxSize === null) {
this.ui.thirtyMinuteMaxSize.html('Unlimited');
this.ui.sixtyMinuteMaxSize.html('Unlimited');
} else {
var maxBytes = maxSize * 1024 * 1024;
var maxThirty = FormatHelpers.bytes(maxBytes * 90, 2);
var maxSixty = FormatHelpers.bytes(maxBytes * 140, 2);
this.ui.thirtyMinuteMaxSize.html(maxThirty);
this.ui.sixtyMinuteMaxSize.html(maxSixty);
}
}
}
});
view = AsModelBoundView.call(view);
module.exports = view;
module.exports = view;

View File

@@ -1,31 +1,31 @@
<span class="col-md-2 col-sm-3">
{{quality.name}}
</span>
<span class="col-md-2 col-sm-3">
<input type="text" class="form-control" name="title">
</span>
<span class="col-md-4 col-sm-6">
<div class="x-slider"></div>
<div class="size-label-wrapper">
<div class="pull-left">
<span class="label label-warning x-min-thirty"
name="thirtyMinuteMinSize"
title="Minimum size for a 30 minute episode">
</span>
<span class="label label-info x-min-sixty"
name="sixtyMinuteMinSize"
title="Minimum size for a 60 minute episode">
</span>
</div>
<div class="pull-right">
<span class="label label-warning x-max-thirty"
name="thirtyMinuteMaxSize"
title="Maximum size for a 30 minute episode">
</span>
<span class="label label-info x-max-sixty"
name="sixtyMinuteMaxSize"
title="Maximum size for a 60 minute episode">
</span>
</div>
</div>
</span>
<span class="col-md-2 col-sm-3">
{{quality.name}}
</span>
<span class="col-md-2 col-sm-3">
<input type="text" class="form-control" name="title">
</span>
<span class="col-md-4 col-sm-6">
<div class="x-slider"></div>
<div class="size-label-wrapper">
<div class="pull-left">
<span class="label label-warning x-min-thirty"
name="thirtyMinuteMinSize"
title="Minimum size for a 90 minute episode">
</span>
<span class="label label-info x-min-sixty"
name="sixtyMinuteMinSize"
title="Minimum size for a 140 minute episode">
</span>
</div>
<div class="pull-right">
<span class="label label-warning x-max-thirty"
name="thirtyMinuteMaxSize"
title="Maximum size for a 90 minute movie">
</span>
<span class="label label-info x-max-sixty"
name="sixtyMinuteMaxSize"
title="Maximum size for a 140 minute movie">
</span>
</div>
</div>
</span>