diff --git a/src/NzbDrone.Api/Indexers/ReleaseResource.cs b/src/NzbDrone.Api/Indexers/ReleaseResource.cs
index 4a8e7e041..291879a44 100644
--- a/src/NzbDrone.Api/Indexers/ReleaseResource.cs
+++ b/src/NzbDrone.Api/Indexers/ReleaseResource.cs
@@ -24,6 +24,7 @@ namespace NzbDrone.Api.Indexers
public string Indexer { get; set; }
public string ReleaseGroup { get; set; }
public string ReleaseHash { get; set; }
+ public string Edition { get; set; }
public string Title { get; set; }
public bool FullSeason { get; set; }
public int SeasonNumber { get; set; }
@@ -90,6 +91,55 @@ namespace NzbDrone.Api.Indexers
if (model.IsForMovie)
{
downloadAllowed = model.RemoteMovie.DownloadAllowed;
+ var parsedMovieInfo = model.RemoteMovie.ParsedMovieInfo;
+
+ return new ReleaseResource
+ {
+ Guid = releaseInfo.Guid,
+ Quality = parsedMovieInfo.Quality,
+ //QualityWeight
+ Age = releaseInfo.Age,
+ AgeHours = releaseInfo.AgeHours,
+ AgeMinutes = releaseInfo.AgeMinutes,
+ Size = releaseInfo.Size,
+ IndexerId = releaseInfo.IndexerId,
+ Indexer = releaseInfo.Indexer,
+ ReleaseGroup = parsedMovieInfo.ReleaseGroup,
+ ReleaseHash = parsedMovieInfo.ReleaseHash,
+ Title = releaseInfo.Title,
+ FullSeason = parsedMovieInfo.FullSeason,
+ SeasonNumber = parsedMovieInfo.SeasonNumber,
+ Language = parsedMovieInfo.Language,
+ AirDate = "",
+ SeriesTitle = parsedMovieInfo.MovieTitle,
+ EpisodeNumbers = new int[0],
+ AbsoluteEpisodeNumbers = new int[0],
+ Approved = model.Approved,
+ TemporarilyRejected = model.TemporarilyRejected,
+ Rejected = model.Rejected,
+ TvdbId = releaseInfo.TvdbId,
+ TvRageId = releaseInfo.TvRageId,
+ Rejections = model.Rejections.Select(r => r.Reason).ToList(),
+ PublishDate = releaseInfo.PublishDate,
+ CommentUrl = releaseInfo.CommentUrl,
+ DownloadUrl = releaseInfo.DownloadUrl,
+ InfoUrl = releaseInfo.InfoUrl,
+ DownloadAllowed = downloadAllowed,
+ //ReleaseWeight
+
+ MagnetUrl = torrentInfo.MagnetUrl,
+ InfoHash = torrentInfo.InfoHash,
+ Seeders = torrentInfo.Seeders,
+ Leechers = (torrentInfo.Peers.HasValue && torrentInfo.Seeders.HasValue) ? (torrentInfo.Peers.Value - torrentInfo.Seeders.Value) : (int?)null,
+ Protocol = releaseInfo.DownloadProtocol,
+
+ Edition = parsedMovieInfo.Edition,
+
+ IsDaily = false,
+ IsAbsoluteNumbering = false,
+ IsPossibleSpecialEpisode = false,
+ Special = parsedMovieInfo.Special,
+ };
}
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?)
diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs
index af071b15d..198756c9f 100644
--- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs
+++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs
@@ -66,9 +66,9 @@ namespace NzbDrone.Core.DecisionEngine
try
{
- var parsedEpisodeInfo = Parser.Parser.ParseTitle(report.Title);
+ var parsedEpisodeInfo = Parser.Parser.ParseMovieTitle(report.Title);
- if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace())
+ if (parsedEpisodeInfo != null && !parsedEpisodeInfo.MovieTitle.IsNullOrWhiteSpace())
{
RemoteMovie remoteEpisode = _parsingService.Map(parsedEpisodeInfo, "", searchCriteria);
remoteEpisode.Release = report;
diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs
index c7ba36cbc..97d5ac608 100644
--- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs
+++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs
@@ -57,12 +57,13 @@ namespace NzbDrone.Core.Download.TrackedDownloads
try
{
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
+ var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title);
var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId);
if (parsedEpisodeInfo != null)
{
trackedDownload.RemoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
- trackedDownload.RemoteMovie = _parsingService.Map(parsedEpisodeInfo, "", null);
+ trackedDownload.RemoteMovie = _parsingService.Map(parsedMovieInfo, "", null);
}
if (historyItems.Any())
diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj
index 8b7a9d121..89ed34e9f 100644
--- a/src/NzbDrone.Core/NzbDrone.Core.csproj
+++ b/src/NzbDrone.Core/NzbDrone.Core.csproj
@@ -893,6 +893,7 @@
+
diff --git a/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs
new file mode 100644
index 000000000..ff1f87cfd
--- /dev/null
+++ b/src/NzbDrone.Core/Parser/Model/ParsedMovieInfo.cs
@@ -0,0 +1,31 @@
+using System.Linq;
+using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Qualities;
+
+namespace NzbDrone.Core.Parser.Model
+{
+ public class ParsedMovieInfo
+ {
+ public string MovieTitle { get; set; }
+ public SeriesTitleInfo MovieTitleInfo { get; set; }
+ public QualityModel Quality { get; set; }
+ public int SeasonNumber { get; set; }
+ public Language Language { get; set; }
+ public bool FullSeason { get; set; }
+ public bool Special { get; set; }
+ public string ReleaseGroup { get; set; }
+ public string ReleaseHash { get; set; }
+ public string Edition { get; set;}
+ public int Year { get; set; }
+
+ public ParsedMovieInfo()
+ {
+
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0} - {1} {2}", MovieTitle, MovieTitleInfo.Year, Quality);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs b/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs
index d8ba07803..1e6f5f5cc 100644
--- a/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs
+++ b/src/NzbDrone.Core/Parser/Model/RemoteMovie.cs
@@ -9,6 +9,7 @@ namespace NzbDrone.Core.Parser.Model
{
public ReleaseInfo Release { get; set; }
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } //TODO: Change to ParsedMovieInfo, for now though ParsedEpisodeInfo will do.
+ public ParsedMovieInfo ParsedMovieInfo { get; set; }
public Movie Movie { get; set; }
public bool DownloadAllowed { get; set; }
diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs
index f482c2c9a..81c816873 100644
--- a/src/NzbDrone.Core/Parser/Parser.cs
+++ b/src/NzbDrone.Core/Parser/Parser.cs
@@ -15,6 +15,26 @@ namespace NzbDrone.Core.Parser
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(Parser));
+ private static readonly Regex[] ReportMovieTitleRegex = new[]
+ {
+ //Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011
+ new Regex(@"^(?
.+?)?(?:(?:[-_\W](?(\w+\.?edition))\.(?(?.+?)?(?:(?:[-_\W](?(?(\w+\.?edition))",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ //Cut Movies, e.g: Mission.Impossible.3.Directors.Cut.2011
+ new Regex(@"^(?.+?)?(?:(?:[-_\W](?(\w+\.?cut))\.(?(?.+?)?(?:(?:[-_\W](?(?(\w+\.?cut))",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+
+ //Normal movie format, e.g: Mission.Impossible.3.2011
+ new Regex(@"^(?.+?)?(?:(?:[-_\W](?(? episodeIds);
- RemoteMovie Map(ParsedEpisodeInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
+ RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
}
@@ -33,6 +33,20 @@ namespace NzbDrone.Core.Parser
private readonly ISceneMappingService _sceneMappingService;
private readonly IMovieService _movieService;
private readonly Logger _logger;
+ private readonly Dictionary romanNumeralsMapper = new Dictionary
+ {
+ { "1", "I"},
+ { "2", "II"},
+ { "3", "III"},
+ { "4", "IV"},
+ { "5", "V"},
+ { "6", "VI"},
+ { "7", "VII"},
+ { "8", "VII"},
+ { "9", "IX"},
+ { "10", "X"},
+
+ }; //If a movie has more than 10 parts fuck 'em.
public ParsingService(IEpisodeService episodeService,
ISeriesService seriesService,
@@ -163,19 +177,19 @@ namespace NzbDrone.Core.Parser
public Movie GetMovie(string title)
{
- var parsedEpisodeInfo = Parser.ParseTitle(title);
+ var parsedEpisodeInfo = Parser.ParseMovieTitle(title);
if (parsedEpisodeInfo == null)
{
return _movieService.FindByTitle(title);
}
- var series = _movieService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
+ var series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle);
if (series == null)
{
- series = _movieService.FindByTitle(parsedEpisodeInfo.SeriesTitleInfo.TitleWithoutYear,
- parsedEpisodeInfo.SeriesTitleInfo.Year);
+ series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitleInfo.TitleWithoutYear,
+ parsedEpisodeInfo.MovieTitleInfo.Year);
}
return series;
@@ -201,11 +215,11 @@ namespace NzbDrone.Core.Parser
return remoteEpisode;
}
- public RemoteMovie Map(ParsedEpisodeInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null)
+ public RemoteMovie Map(ParsedMovieInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null)
{
var remoteEpisode = new RemoteMovie
{
- ParsedEpisodeInfo = parsedEpisodeInfo,
+ ParsedMovieInfo = parsedEpisodeInfo,
};
var movie = GetMovie(parsedEpisodeInfo, imdbId, searchCriteria);
@@ -334,23 +348,46 @@ namespace NzbDrone.Core.Parser
return null;
}
- private Movie GetMovie(ParsedEpisodeInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria)
+ private Movie GetMovie(ParsedMovieInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria)
{
if (searchCriteria != null)
{
- if (searchCriteria.Movie.CleanTitle == parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle())
+ var possibleTitles = new List();
+
+ possibleTitles.Add(searchCriteria.Movie.CleanTitle);
+
+ foreach (string altTitle in searchCriteria.Movie.AlternativeTitles)
{
- return searchCriteria.Movie;
+ possibleTitles.Add(altTitle.CleanSeriesTitle());
}
- if (imdbId.IsNotNullOrWhiteSpace() && imdbId == searchCriteria.Movie.ImdbId)
+ foreach (string title in possibleTitles)
{
- //TODO: If series is found by TvdbId, we should report it as a scene naming exception, since it will fail to import
- return searchCriteria.Movie;
+ if (title == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle())
+ {
+ return searchCriteria.Movie;
+ }
+
+ foreach (KeyValuePair entry in romanNumeralsMapper)
+ {
+ string num = entry.Key;
+ string roman = entry.Value.ToLower();
+
+ if (title.Replace(num, roman) == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle())
+ {
+ return searchCriteria.Movie;
+ }
+
+ if (title.Replace(roman, num) == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle())
+ {
+ return searchCriteria.Movie;
+ }
+ }
}
+
}
- Movie movie = _movieService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
+ Movie movie = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle); //Todo: same as above!
if (movie == null && imdbId.IsNotNullOrWhiteSpace())
{
@@ -360,7 +397,7 @@ namespace NzbDrone.Core.Parser
if (movie == null)
{
- _logger.Debug("No matching movie {0}", parsedEpisodeInfo.SeriesTitle);
+ _logger.Debug("No matching movie {0}", parsedEpisodeInfo.MovieTitle);
return null;
}
diff --git a/src/UI/Cells/EditionCell.js b/src/UI/Cells/EditionCell.js
new file mode 100644
index 000000000..1d3c8e554
--- /dev/null
+++ b/src/UI/Cells/EditionCell.js
@@ -0,0 +1,41 @@
+var Backgrid = require('backgrid');
+var Marionette = require('marionette');
+require('bootstrap');
+
+module.exports = Backgrid.Cell.extend({
+ className : 'edition-cell',
+ //template : 'Cells/EditionCellTemplate',
+
+ render : function() {
+
+ var edition = this.model.get(this.column.get('name'));
+ if (!edition) {
+ return this;
+ }
+ var cut = false;
+
+ if (edition.toLowerCase().contains("cut")) {
+ cut = true;
+ }
+
+ //this.templateFunction = Marionette.TemplateCache.get(this.template);
+
+ //var html = this.templateFunction(edition);
+ if (cut) {
+ this.$el.html(''.format(edition));
+ } else {
+ this.$el.html(''.format(edition));
+ }
+
+ /*this.$el.popover({
+ content : html,
+ html : true,
+ trigger : 'hover',
+ title : this.column.get('title'),
+ placement : 'left',
+ container : this.$el
+ });*/
+
+ return this;
+ }
+});
diff --git a/src/UI/Cells/EditionCellTemplate.hbs b/src/UI/Cells/EditionCellTemplate.hbs
new file mode 100644
index 000000000..9b4f43449
--- /dev/null
+++ b/src/UI/Cells/EditionCellTemplate.hbs
@@ -0,0 +1,5 @@
+
diff --git a/src/UI/Content/icons.less b/src/UI/Content/icons.less
index cce09293a..7b00ed13b 100644
--- a/src/UI/Content/icons.less
+++ b/src/UI/Content/icons.less
@@ -121,6 +121,10 @@
.fa-icon-color(@brand-danger);
}
+.icon-sonarr-form-cut {
+ .fa-icon-content(@fa-var-scissors);
+}
+
.icon-sonarr-form-info-link {
.clickable();
.fa-icon-content(@fa-var-info-circle);
@@ -502,4 +506,4 @@
.icon-sonarr-header-rejections {
.fa-icon-content(@fa-var-exclamation-circle);
-}
\ No newline at end of file
+}
diff --git a/src/UI/Episode/Search/ManualLayout.js b/src/UI/Episode/Search/ManualLayout.js
index 58c792063..3b7463e19 100644
--- a/src/UI/Episode/Search/ManualLayout.js
+++ b/src/UI/Episode/Search/ManualLayout.js
@@ -8,6 +8,7 @@ var DownloadReportCell = require('../../Release/DownloadReportCell');
var AgeCell = require('../../Release/AgeCell');
var ProtocolCell = require('../../Release/ProtocolCell');
var PeersCell = require('../../Release/PeersCell');
+var EditionCell = require('../../Cells/EditionCell');
module.exports = Marionette.Layout.extend({
template : 'Episode/Search/ManualLayoutTemplate',
@@ -32,6 +33,12 @@ module.exports = Marionette.Layout.extend({
label : 'Title',
cell : ReleaseTitleCell
},
+ {
+ name : 'edition',
+ label : 'Edition',
+ cell : EditionCell,
+ title : "Edition"
+ },
{
name : 'indexer',
label : 'Indexer',
@@ -83,4 +90,4 @@ module.exports = Marionette.Layout.extend({
}));
}
}
-});
\ No newline at end of file
+});
diff --git a/src/UI/Movies/Search/ManualLayout.js b/src/UI/Movies/Search/ManualLayout.js
index daae5d781..af5413779 100644
--- a/src/UI/Movies/Search/ManualLayout.js
+++ b/src/UI/Movies/Search/ManualLayout.js
@@ -8,6 +8,7 @@ var DownloadReportCell = require('../../Release/DownloadReportCell');
var AgeCell = require('../../Release/AgeCell');
var ProtocolCell = require('../../Release/ProtocolCell');
var PeersCell = require('../../Release/PeersCell');
+var EditionCell = require('../../Cells/EditionCell');
module.exports = Marionette.Layout.extend({
template : 'Movies/Search/ManualLayoutTemplate',
@@ -32,6 +33,12 @@ module.exports = Marionette.Layout.extend({
label : 'Title',
cell : ReleaseTitleCell
},
+ {
+ name : 'edition',
+ label : 'Edition',
+ cell : EditionCell,
+ title : "Edition"
+ },
{
name : 'indexer',
label : 'Indexer',
diff --git a/src/UI/Release/ReleaseLayout.js b/src/UI/Release/ReleaseLayout.js
index 07f4a1af6..9b1791ff6 100644
--- a/src/UI/Release/ReleaseLayout.js
+++ b/src/UI/Release/ReleaseLayout.js
@@ -7,6 +7,7 @@ var FileSizeCell = require('../Cells/FileSizeCell');
var QualityCell = require('../Cells/QualityCell');
var ApprovalStatusCell = require('../Cells/ApprovalStatusCell');
var LoadingView = require('../Shared/LoadingView');
+var EditionCell = require('../Cells/EditionCell');
module.exports = Marionette.Layout.extend({
template : 'Release/ReleaseLayoutTemplate',
@@ -17,6 +18,12 @@ module.exports = Marionette.Layout.extend({
},
columns : [
+ {
+ name : 'edition',
+ label : 'Edition',
+ sortable : false,
+ cell : EditionCell
+ },
{
name : 'indexer',
label : 'Indexer',
@@ -29,12 +36,12 @@ module.exports = Marionette.Layout.extend({
sortable : true,
cell : Backgrid.StringCell
},
- {
+ /*{
name : 'episodeNumbers',
episodes : 'episodeNumbers',
label : 'season',
cell : EpisodeNumberCell
- },
+ },*/
{
name : 'size',
label : 'Size',
@@ -75,4 +82,4 @@ module.exports = Marionette.Layout.extend({
}));
}
}
-});
\ No newline at end of file
+});