Changed: Alternative Titles were reworked greatly. This should speed up RSS Sync massively, especially for large libraries (up to 4x).
parent
8927e7c2c6
commit
cfcb66fba1
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Nancy;
|
||||
using NzbDrone.Api;
|
||||
using NzbDrone.Api.Movie;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.MetadataSource.RadarrAPI;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class AlternativeTitleModule : NzbDroneRestModule<AlternativeTitleResource>
|
||||
{
|
||||
private readonly IAlternativeTitleService _altTitleService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IRadarrAPIClient _radarrApi;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public AlternativeTitleModule(IAlternativeTitleService altTitleService, IMovieService movieService, IRadarrAPIClient radarrApi, IEventAggregator eventAggregator)
|
||||
: base("/alttitle")
|
||||
{
|
||||
_altTitleService = altTitleService;
|
||||
_movieService = movieService;
|
||||
_radarrApi = radarrApi;
|
||||
CreateResource = AddTitle;
|
||||
GetResourceById = GetTitle;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
private int AddTitle(AlternativeTitleResource altTitle)
|
||||
{
|
||||
var title = altTitle.ToModel();
|
||||
var movie = _movieService.GetMovie(altTitle.MovieId);
|
||||
var newTitle = _radarrApi.AddNewAlternativeTitle(title, movie.TmdbId);
|
||||
|
||||
var addedTitle = _altTitleService.AddAltTitle(newTitle, movie);
|
||||
_eventAggregator.PublishEvent(new MovieUpdatedEvent(movie));
|
||||
return addedTitle.Id;
|
||||
}
|
||||
|
||||
private AlternativeTitleResource GetTitle(int id)
|
||||
{
|
||||
return _altTitleService.GetById(id).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Nancy;
|
||||
using NzbDrone.Api;
|
||||
using NzbDrone.Api.Movie;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.MetadataSource.RadarrAPI;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class AlternativeYearModule : NzbDroneRestModule<AlternativeYearResource>
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IRadarrAPIClient _radarrApi;
|
||||
private readonly ICached<int> _yearCache;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public AlternativeYearModule(IMovieService movieService, IRadarrAPIClient radarrApi, ICacheManager cacheManager, IEventAggregator eventAggregator)
|
||||
: base("/altyear")
|
||||
{
|
||||
_movieService = movieService;
|
||||
_radarrApi = radarrApi;
|
||||
CreateResource = AddYear;
|
||||
GetResourceById = GetYear;
|
||||
_yearCache = cacheManager.GetCache<int>(GetType(), "altYears");
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
private int AddYear(AlternativeYearResource altYear)
|
||||
{
|
||||
var id = new Random().Next();
|
||||
_yearCache.Set(id.ToString(), altYear.Year, TimeSpan.FromMinutes(1));
|
||||
var movie = _movieService.GetMovie(altYear.MovieId);
|
||||
var newYear = _radarrApi.AddNewAlternativeYear(altYear.Year, movie.TmdbId);
|
||||
movie.SecondaryYear = newYear.Year;
|
||||
movie.SecondaryYearSourceId = newYear.SourceId;
|
||||
_movieService.UpdateMovie(movie);
|
||||
_eventAggregator.PublishEvent(new MovieUpdatedEvent(movie));
|
||||
return id;
|
||||
}
|
||||
|
||||
private AlternativeYearResource GetYear(int id)
|
||||
{
|
||||
return new AlternativeYearResource
|
||||
{
|
||||
Year = _yearCache.Find(id.ToString())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'language-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
var language = this.model.get("language");
|
||||
|
||||
this.$el.html(this.toTitleCase(language));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
toTitleCase : function(str)
|
||||
{
|
||||
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||
}
|
||||
|
||||
|
||||
});
|
@ -0,0 +1,5 @@
|
||||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Movies/Titles/NoTitlesViewTemplate'
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
<p class="text-warning">
|
||||
No alternative titles for this movie.
|
||||
</p>
|
@ -0,0 +1,42 @@
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'title-source-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
var link = undefined;
|
||||
var sourceTitle = this.model.get("sourceType");
|
||||
var sourceId = this.model.get("sourceId");
|
||||
|
||||
switch (sourceTitle) {
|
||||
case "tmdb":
|
||||
sourceTitle = "TMDB";
|
||||
link = "https://themoviedb.org/movie/" + sourceId;
|
||||
break;
|
||||
case "mappings":
|
||||
sourceTitle = "Radarr Mappings";
|
||||
link = "https://mappings.radarr.video/mapping/" + sourceId;
|
||||
break;
|
||||
case "user":
|
||||
sourceTitle = "Force Download";
|
||||
break;
|
||||
case "indexer":
|
||||
sourceTitle = "Indexer";
|
||||
break;
|
||||
}
|
||||
|
||||
var a = "{0}";
|
||||
|
||||
if (link) {
|
||||
a = "<a href='"+link+"' target='_blank'>{0}</a>"
|
||||
}
|
||||
|
||||
this.$el.html(a.format(sourceTitle));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
});
|
@ -0,0 +1,6 @@
|
||||
var TemplatedCell = require('../../Cells/TemplatedCell');
|
||||
|
||||
module.exports = TemplatedCell.extend({
|
||||
className : 'series-title-cell',
|
||||
template : 'Movies/Titles/TitleTemplate'
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Model.extend({});
|
@ -0,0 +1 @@
|
||||
{{this}}
|
@ -0,0 +1,30 @@
|
||||
var PagableCollection = require('backbone.pageable');
|
||||
var TitleModel = require('./TitleModel');
|
||||
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
|
||||
|
||||
var Collection = PagableCollection.extend({
|
||||
url : window.NzbDrone.ApiRoot + "/aka",
|
||||
model : TitleModel,
|
||||
|
||||
state : {
|
||||
pageSize : 2000,
|
||||
sortKey : 'title',
|
||||
order : -1
|
||||
},
|
||||
|
||||
mode : 'client',
|
||||
|
||||
sortMappings : {
|
||||
"source" : {
|
||||
sortKey : "sourceType"
|
||||
},
|
||||
"language" : {
|
||||
sortKey : "language"
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
Collection = AsSortedCollection.call(Collection);
|
||||
|
||||
module.exports = Collection;
|
@ -0,0 +1,117 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
//var ButtonsView = require('./ButtonsView');
|
||||
//var ManualSearchLayout = require('./ManualLayout');
|
||||
var TitlesCollection = require('./TitlesCollection');
|
||||
var CommandController = require('../../Commands/CommandController');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
var NoResultsView = require('./NoTitlesView');
|
||||
var TitleModel = require("./TitleModel");
|
||||
var TitleCell = require("./TitleCell");
|
||||
var SourceCell = require("./SourceCell");
|
||||
var LanguageCell = require("./LanguageCell");
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Movies/Titles/TitlesLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
main : '#movie-titles-region',
|
||||
grid : "#movie-titles-grid"
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-search-auto' : '_searchAuto',
|
||||
'click .x-search-manual' : '_searchManual',
|
||||
'click .x-search-back' : '_showButtons'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'title',
|
||||
label : 'Title',
|
||||
cell : Backgrid.StringCell
|
||||
},
|
||||
{
|
||||
name : "this",
|
||||
label : "Source",
|
||||
cell : SourceCell,
|
||||
sortKey : "sourceType",
|
||||
},
|
||||
{
|
||||
name : "this",
|
||||
label : "Language",
|
||||
cell : LanguageCell
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
initialize : function(movie) {
|
||||
this.titlesCollection = new TitlesCollection();
|
||||
var titles = movie.model.get("alternativeTitles");
|
||||
this.movie = movie;
|
||||
this.titlesCollection.add(titles);
|
||||
//this.listenTo(this.releaseCollection, 'sync', this._showSearchResults);
|
||||
this.listenTo(this.model, 'change', function(model, options) {
|
||||
if (options && options.changeSource === 'signalr') {
|
||||
this._refresh(model);
|
||||
}
|
||||
});
|
||||
|
||||
//vent.on(vent.Commands.MovieFileEdited, this._showGrid, this);
|
||||
},
|
||||
|
||||
_refresh : function(model) {
|
||||
this.titlesCollection = new TitlesCollection();
|
||||
var file = model.get("alternativeTitles");
|
||||
this.titlesCollection.add(file);
|
||||
|
||||
|
||||
this.onShow();
|
||||
},
|
||||
|
||||
_refreshClose : function(options) {
|
||||
this.titlesCollection = new TitlesCollection();
|
||||
var file = this.movie.model.get("alternativeTitles");
|
||||
this.titlesCollection.add(file);
|
||||
this._showGrid();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.grid.show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.titlesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showGrid : function() {
|
||||
this.regionManager.get('grid').show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.titlesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showMainView : function() {
|
||||
this.main.show(this.mainView);
|
||||
},
|
||||
|
||||
_showButtons : function() {
|
||||
this._showMainView();
|
||||
},
|
||||
|
||||
_showSearchResults : function() {
|
||||
if (this.releaseCollection.length === 0) {
|
||||
this.mainView = new NoResultsView();
|
||||
}
|
||||
|
||||
else {
|
||||
//this.mainView = new ManualSearchLayout({ collection : this.releaseCollection });
|
||||
}
|
||||
|
||||
this._showMainView();
|
||||
}
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
<div id="movie-titles-region">
|
||||
<div id="movie-titles-grid" class="table-responsive"></div>
|
||||
</div>
|
@ -0,0 +1,6 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/alttitle',
|
||||
});
|
@ -0,0 +1,6 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/altyear',
|
||||
});
|
@ -0,0 +1,81 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var vent = require('vent');
|
||||
var AppLayout = require('../AppLayout');
|
||||
var Marionette = require('marionette');
|
||||
var Config = require('../Config');
|
||||
var LanguageCollection = require('../Settings/Profile/Language/LanguageCollection');
|
||||
var AltTitleModel = require("./AlternativeTitleModel");
|
||||
var AltYearModel = require("./AlternativeYearModel");
|
||||
var Messenger = require('../Shared/Messenger');
|
||||
require('../Form/FormBuilder');
|
||||
require('bootstrap');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Release/ForceDownloadViewTemplate',
|
||||
|
||||
events : {
|
||||
'click .x-download' : '_forceDownload',
|
||||
},
|
||||
|
||||
ui : {
|
||||
titleMapping : "#title-mapping",
|
||||
yearMapping : "#year-mapping",
|
||||
language : "#language-selection",
|
||||
indicator : ".x-indicator",
|
||||
},
|
||||
|
||||
initialize : function(options) {
|
||||
this.release = options.release;
|
||||
this.templateHelpers = {};
|
||||
|
||||
this._configureTemplateHelpers();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
if (this.release.get("mappingResult") == "wrongYear") {
|
||||
this.ui.titleMapping.hide();
|
||||
} else {
|
||||
this.ui.yearMapping.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_configureTemplateHelpers : function() {
|
||||
this.templateHelpers.release = this.release.toJSON();
|
||||
this.templateHelpers.languages = LanguageCollection.toJSON()
|
||||
},
|
||||
|
||||
_forceDownload : function() {
|
||||
this.ui.indicator.show();
|
||||
var self = this;
|
||||
|
||||
if (this.release.get("mappingResult") == "wrongYear") {
|
||||
var altYear = new AltYearModel({
|
||||
movieId : this.release.get("suspectedMovieId"),
|
||||
year : this.release.get("year")
|
||||
});
|
||||
this.savePromise = altYear.save();
|
||||
} else {
|
||||
var altTitle = new AltTitleModel({
|
||||
movieId : this.release.get("suspectedMovieId"),
|
||||
title : this.release.get("movieTitle"),
|
||||
language : this.ui.language.val(),
|
||||
});
|
||||
|
||||
this.savePromise = altTitle.save();
|
||||
}
|
||||
|
||||
this.savePromise.always(function(){
|
||||
self.ui.indicator.hide();
|
||||
});
|
||||
|
||||
this.savePromise.success(function(){
|
||||
self.release.save(null, {
|
||||
success : function() {
|
||||
self.release.set('queued', true);
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" aria-hidden="true" data-dismiss="modal">×</button>
|
||||
<h3>Force Download</h3>
|
||||
</div>
|
||||
<div class="modal-body indexer-modal">
|
||||
<div id="title-mapping">
|
||||
<p>The title "{{release.movieTitle}}" could not be found amongst the alternative titles of the movie. This could lead to problems when Radarr wants to import your movie.
|
||||
If you click force download below, the title will be added to the alternative titles using the language selected below.</p>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Language</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<select id="language-selection" class="form-control" name="language">
|
||||
{{#each languages}}
|
||||
{{#unless_eq nameLower compare="unknown"}}
|
||||
<option value="{{nameLower}}" {{#if_eq nameLower compare="english"}} selected {{/if_eq}}>{{name}}</option>
|
||||
{{/unless_eq}}
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1 help-inline">
|
||||
<i class="icon-sonarr-form-info" title="Language of the alternative title."/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="year-mapping">
|
||||
<p>The year {{release.year}} does not match the expected release year. This could lead to problems when Radarr wants to import your movie.
|
||||
If you click force download below, the year will be added as a secondary year for this movie.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span class="indicator x-indicator"><i class="icon-sonarr-spinner fa-spin"></i></span>
|
||||
<button class="btn" data-dismiss="modal">Cancel</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary x-download">Force Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,3 +1,11 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Model.extend({});
|
||||
module.exports = Backbone.Model.extend({
|
||||
downloadOk : function() {
|
||||
return this.get("mappingResult") == "success" || this.get("mappingResult") == "successLenientMapping";
|
||||
},
|
||||
|
||||
forceDownloadOk : function() {
|
||||
return this.get("mappingResult") == "wrongYear" || this.get("mappingResult") == "wrongTitle";
|
||||
}
|
||||
});
|
Loading…
Reference in new issue