mirror of https://github.com/Radarr/Radarr
Patch/bulk import qol (#785)
* Filter out existing movies upon import * Update collection based on what is imported * Ensure root folders are loaded before collectionview TODO: * Ensure grid region exists * Return information about what wasn't imported * Filter collection based on duplicates
This commit is contained in:
parent
3edc2b80cf
commit
056fb154a8
|
@ -34,9 +34,10 @@ namespace NzbDrone.Api.Movie
|
|||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly ICached<Core.Tv.Movie> _mappedMovies;
|
||||
private readonly IMovieService _movieService;
|
||||
|
||||
public MovieBulkImportModule(ISearchForNewMovie searchProxy, IRootFolderService rootFolderService, IMakeImportDecision importDecisionMaker,
|
||||
IDiskScanService diskScanService, ICacheManager cacheManager)
|
||||
IDiskScanService diskScanService, ICacheManager cacheManager, IMovieService movieService)
|
||||
: base("/movies/bulkimport")
|
||||
{
|
||||
_searchProxy = searchProxy;
|
||||
|
@ -44,6 +45,7 @@ namespace NzbDrone.Api.Movie
|
|||
_importDecisionMaker = importDecisionMaker;
|
||||
_diskScanService = diskScanService;
|
||||
_mappedMovies = cacheManager.GetCache<Core.Tv.Movie>(GetType(), "mappedMoviesCache");
|
||||
_movieService = movieService;
|
||||
Get["/"] = x => Search();
|
||||
}
|
||||
|
||||
|
@ -55,6 +57,8 @@ namespace NzbDrone.Api.Movie
|
|||
//Todo error handling
|
||||
}
|
||||
|
||||
//var existingMovieTmdbIds = _movieService.GetAllMovies().Select(m => m.TmdbId);
|
||||
|
||||
RootFolder rootFolder = _rootFolderService.Get(Request.Query.Id);
|
||||
|
||||
int page = Request.Query.page;
|
||||
|
@ -108,8 +112,6 @@ namespace NzbDrone.Api.Movie
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
var files = _diskScanService.GetVideoFiles(f.Path);
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(files.ToList(), m);
|
||||
|
@ -133,8 +135,10 @@ namespace NzbDrone.Api.Movie
|
|||
|
||||
mappedMovie = _searchProxy.MapMovieToTmdbMovie(m);
|
||||
|
||||
if (mappedMovie != null)
|
||||
if (mappedMovie != null /*&& !existingMovieTmdbIds.Contains(mappedMovie.TmdbId)*/)
|
||||
{
|
||||
//Could split these checks to flag movie as possible duplicate
|
||||
|
||||
mappedMovie.Monitored = true;
|
||||
|
||||
_mappedMovies.Set(f.Name, mappedMovie, TimeSpan.FromDays(2));
|
||||
|
@ -144,7 +148,7 @@ namespace NzbDrone.Api.Movie
|
|||
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
return new PagingResource<MovieResource>
|
||||
{
|
||||
Page = page,
|
||||
|
@ -159,10 +163,10 @@ namespace NzbDrone.Api.Movie
|
|||
|
||||
private static IEnumerable<MovieResource> MapToResource(IEnumerable<Core.Tv.Movie> movies)
|
||||
{
|
||||
foreach (var currentSeries in movies)
|
||||
foreach (var currentMovie in movies)
|
||||
{
|
||||
var resource = currentSeries.ToResource();
|
||||
var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||
var resource = currentMovie.ToResource();
|
||||
var poster = currentMovie.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||
if (poster != null)
|
||||
{
|
||||
resource.RemotePoster = poster.Url;
|
||||
|
|
|
@ -95,8 +95,6 @@ namespace NzbDrone.Core.Tv
|
|||
|
||||
public List<Movie> AddMovies(List<Movie> newMovies)
|
||||
{
|
||||
_logger.Debug("Adding {0} movies", newMovies.Count);
|
||||
|
||||
newMovies.ForEach(m => Ensure.That(m, () => m).IsNotNull());
|
||||
|
||||
newMovies.ForEach(m =>
|
||||
|
@ -112,8 +110,16 @@ namespace NzbDrone.Core.Tv
|
|||
m.Added = DateTime.UtcNow;
|
||||
});
|
||||
|
||||
//var existingMovies = GetAllMovies();
|
||||
//var potentialMovieCount = newMovies.Count;
|
||||
|
||||
//newMovies = newMovies.ExceptBy(n => n.TitleSlug, existingMovies, e => e.TitleSlug, StringComparer.InvariantCultureIgnoreCase).ToList();
|
||||
|
||||
_movieRepository.InsertMany(newMovies);
|
||||
|
||||
//_logger.Debug("Adding {0} movies, {1} duplicates detected", newMovies.Count, potentialMovieCount - newMovies.Count);
|
||||
_logger.Debug("Adding {0} movies", newMovies.Count);
|
||||
|
||||
newMovies.ForEach(m =>
|
||||
{
|
||||
_eventAggregator.PublishEvent(new MovieAddedEvent(m));
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||
var Backgrid = require('backgrid');
|
||||
var MoviesCollection = require('../../Movies/MoviesCollection');
|
||||
|
||||
module.exports = SelectAllCell.extend({
|
||||
_originalRender : SelectAllCell.prototype.render,
|
||||
|
||||
_originalInit : SelectAllCell.prototype.initialize,
|
||||
|
||||
initialize : function() {
|
||||
this._originalInit.apply(this, arguments);
|
||||
|
||||
var tmdbId = this.model.get('tmdbId');
|
||||
var existingMovie = MoviesCollection.where({ tmdbId: tmdbId });
|
||||
this.isDuplicate = existingMovie.length > 0 ? true : false;
|
||||
|
||||
this.listenTo(this.model, 'change', this._refresh);
|
||||
},
|
||||
|
||||
onChange : function(e) {
|
||||
if(!this.isDuplicate) {
|
||||
var checked = $(e.target).prop('checked');
|
||||
this.$el.parent().toggleClass('selected', checked);
|
||||
this.model.trigger('backgrid:selected', this.model, checked);
|
||||
} else {
|
||||
$(e.target).prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
render : function() {
|
||||
this._originalRender.apply(this, arguments);
|
||||
|
||||
this.$el.children(':first').prop('disabled', this.isDuplicate);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_refresh: function() {
|
||||
this.render();
|
||||
}
|
||||
});
|
|
@ -7,7 +7,7 @@ var BulkImportCollection = require("./BulkImportCollection");
|
|||
var QualityCell = require('./QualityCell');
|
||||
var TmdbIdCell = require('./TmdbIdCell');
|
||||
var GridPager = require('../../Shared/Grid/Pager');
|
||||
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||
var SelectAllCell = require('./BulkImportSelectAllCell');
|
||||
var ProfileCell = require('./BulkImportProfileCellT');
|
||||
var MonitorCell = require('./BulkImportMonitorCell');
|
||||
var MoviePathCell = require("./MoviePathCell");
|
||||
|
@ -33,7 +33,7 @@ module.exports = Marionette.Layout.extend({
|
|||
|
||||
ui : {
|
||||
addSelectdBtn : '.x-add-selected',
|
||||
addAllBtn : '.x-add-all',
|
||||
//addAllBtn : '.x-add-all',
|
||||
pageSizeSelector : '.x-page-size'
|
||||
},
|
||||
|
||||
|
@ -66,7 +66,8 @@ module.exports = Marionette.Layout.extend({
|
|||
name : '',
|
||||
cell : SelectAllCell,
|
||||
headerCell : 'select-all',
|
||||
sortable : false
|
||||
sortable : false,
|
||||
cellValue : 'this'
|
||||
},
|
||||
{
|
||||
name : 'movie',
|
||||
|
@ -107,7 +108,6 @@ module.exports = Marionette.Layout.extend({
|
|||
cell : QualityCell,
|
||||
cellValue : 'this',
|
||||
sortable : false
|
||||
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -132,14 +132,14 @@ module.exports = Marionette.Layout.extend({
|
|||
callback : this._addSelected,
|
||||
ownerContext : this,
|
||||
className : 'x-add-selected'
|
||||
},
|
||||
{
|
||||
title : 'Add All',
|
||||
icon : 'icon-sonarr-add',
|
||||
callback : this._addAll,
|
||||
ownerContext : this,
|
||||
className : 'x-add-all'
|
||||
}
|
||||
}//,
|
||||
// {
|
||||
// title : 'Add All',
|
||||
// icon : 'icon-sonarr-add',
|
||||
// callback : this._addAll,
|
||||
// ownerContext : this,
|
||||
// className : 'x-add-all'
|
||||
// }
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -155,13 +155,13 @@ module.exports = Marionette.Layout.extend({
|
|||
_addSelected : function() {
|
||||
var selected = _.filter(this.bulkImportCollection.fullCollection.models, function(elem){
|
||||
return elem.selected;
|
||||
})
|
||||
});
|
||||
console.log(selected);
|
||||
|
||||
var promise = MoviesCollection.importFromList(selected);
|
||||
this.ui.addSelectdBtn.spinForPromise(promise);
|
||||
this.ui.addSelectdBtn.addClass('disabled');
|
||||
this.ui.addAllBtn.addClass('disabled');
|
||||
//this.ui.addAllBtn.addClass('disabled');
|
||||
|
||||
if (selected.length === 0) {
|
||||
Messenger.show({
|
||||
|
@ -169,7 +169,7 @@ module.exports = Marionette.Layout.extend({
|
|||
message : 'No movies selected'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Messenger.show({
|
||||
message : "Importing {0} movies. This can take multiple minutes depending on how many movies should be imported. Don't close this browser window until it is finished!".format(selected.length),
|
||||
|
@ -178,12 +178,19 @@ module.exports = Marionette.Layout.extend({
|
|||
type : "error"
|
||||
});
|
||||
|
||||
var _this = this;
|
||||
|
||||
promise.done(function() {
|
||||
Messenger.show({
|
||||
message : "Imported movies from list.",
|
||||
hideAfter : 8,
|
||||
hideOnNavigate : true
|
||||
});
|
||||
Messenger.show({
|
||||
message : "Imported movies from folder.",
|
||||
hideAfter : 8,
|
||||
hideOnNavigate : true
|
||||
});
|
||||
|
||||
|
||||
_.forEach(selected, function(movie) {
|
||||
movie.destroy(); //update the collection without the added movies
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -192,8 +199,8 @@ module.exports = Marionette.Layout.extend({
|
|||
},
|
||||
|
||||
_handleEvent : function(event_name, data) {
|
||||
if (event_name == "sync" || event_name == "content") {
|
||||
this._showContent()
|
||||
if (event_name === "sync" || event_name === "content") {
|
||||
this._showContent();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -207,6 +214,7 @@ module.exports = Marionette.Layout.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
//TODO: override row in order to set an opacity based on duplication state of the movie
|
||||
this.importGrid = new Backgrid.Grid({
|
||||
columns : this.columns,
|
||||
collection : this.bulkImportCollection,
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<div id="x-toolbar"/>
|
||||
{{> PageSizePartial }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<span><b>Disabled movies are possible duplicates. If the match is incorrect, update the Tmdb Id cell to import the proper movie.</b><span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="x-movies-bulk" class="queue table-responsive"/>
|
||||
|
|
|
@ -21,7 +21,7 @@ module.exports = NzbDroneCell.extend({
|
|||
},
|
||||
|
||||
_updateId : function() {
|
||||
var field = this.$el.find('.x-tmdbId');
|
||||
var field = this.$el.find('.x-tmdbId');
|
||||
var data = field.val();
|
||||
|
||||
var promise = $.ajax({
|
||||
|
@ -31,7 +31,7 @@ module.exports = NzbDroneCell.extend({
|
|||
|
||||
//field.spinForPromise(promise);
|
||||
|
||||
field.prop("disabled", true)
|
||||
field.prop("disabled", true);
|
||||
|
||||
var icon = this.$(".icon-sonarr-info");
|
||||
|
||||
|
@ -48,15 +48,15 @@ module.exports = NzbDroneCell.extend({
|
|||
promise.success(function(response) {
|
||||
_self.model.set(response);
|
||||
_self.model.set('monitored', cacheMonitored); //reset to the previous monitored value
|
||||
_self.model.set('profileId', cacheProfile);
|
||||
_self.model.set('path', cachePath);
|
||||
_self.model.set('movieFile', cacheFile); // may be unneccessary.
|
||||
field.prop("disabled", false)
|
||||
_self.model.set('profileId', cacheProfile);
|
||||
_self.model.set('path', cachePath);
|
||||
_self.model.set('movieFile', cacheFile); // may be unneccessary.
|
||||
field.prop("disabled", false);
|
||||
});
|
||||
|
||||
promise.error(function(request, status, error) {
|
||||
console.error("Status: " + status, "Error: " + error);
|
||||
field.prop("disabled", false)
|
||||
field.prop("disabled", false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,9 +24,9 @@ var Layout = Marionette.Layout.extend({
|
|||
|
||||
initialize : function() {
|
||||
this.collection = RootFolderCollection;
|
||||
this.rootfolderListView = new RootFolderCollectionView({ collection : RootFolderCollection });
|
||||
this.rootfolderListView = null;
|
||||
|
||||
this.listenTo(this.rootfolderListView, 'itemview:folderSelected', this._onFolderSelected);
|
||||
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
|
@ -60,7 +60,12 @@ var Layout = Marionette.Layout.extend({
|
|||
},
|
||||
|
||||
_showCurrentDirs : function() {
|
||||
this.currentDirs.show(this.rootfolderListView);
|
||||
if(!this.rootfolderListView)
|
||||
{
|
||||
this.rootfolderListView = new RootFolderCollectionView({ collection : RootFolderCollection });
|
||||
this.currentDirs.show(this.rootfolderListView);
|
||||
this.listenTo(this.rootfolderListView, 'itemview:folderSelected', this._onFolderSelected);
|
||||
}
|
||||
},
|
||||
|
||||
_keydown : function(e) {
|
||||
|
|
|
@ -42,9 +42,9 @@ module.exports = Marionette.Layout.extend({
|
|||
cell : FileTitleCell
|
||||
},
|
||||
{
|
||||
name : "mediaInfo",
|
||||
label : "Media Info",
|
||||
cell : MediaInfoCell
|
||||
name : "mediaInfo",
|
||||
label : "Media Info",
|
||||
cell : MediaInfoCell
|
||||
},
|
||||
{
|
||||
name : 'edition',
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
<div id="movie-files-region"><div id="movie-files-grid" class="table-responsive"></div></div>
|
||||
<div id="movie-files-region">
|
||||
<div id="movie-files-grid" class="table-responsive"></div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue