mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
Completely overhauled how import exclusions work.
Currently new exclusions can only be added when adding new movies or deleting old ones. Not manually in the settings menu. Movies can now be hidden in the new discover feature by using the new import exclusions!
This commit is contained in:
parent
ab28bfead2
commit
fc1585e900
45
src/NzbDrone.Api/NetImport/ImportExclusionsModule.cs
Normal file
45
src/NzbDrone.Api/NetImport/ImportExclusionsModule.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace NzbDrone.Api.NetImport
|
||||
{
|
||||
public class ImportExclusionsModule : NzbDroneRestModule<ImportExclusionsResource>
|
||||
{
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
public ImportExclusionsModule(NetImportFactory netImportFactory, IImportExclusionsService exclusionService) : base("exclusions")
|
||||
{
|
||||
_exclusionService = exclusionService;
|
||||
GetResourceAll = GetAll;
|
||||
CreateResource = AddExclusion;
|
||||
DeleteResource = RemoveExclusion;
|
||||
GetResourceById = GetById;
|
||||
}
|
||||
|
||||
public List<ImportExclusionsResource> GetAll()
|
||||
{
|
||||
return _exclusionService.GetAllExclusions().ToResource();
|
||||
}
|
||||
|
||||
public ImportExclusionsResource GetById(int id)
|
||||
{
|
||||
return _exclusionService.GetById(id).ToResource();
|
||||
}
|
||||
|
||||
public int AddExclusion(ImportExclusionsResource exclusionResource)
|
||||
{
|
||||
var model = exclusionResource.ToModel();
|
||||
|
||||
return _exclusionService.AddExclusion(model).Id;
|
||||
}
|
||||
|
||||
public void RemoveExclusion (int id)
|
||||
{
|
||||
_exclusionService.RemoveExclusion(new ImportExclusion { Id = id });
|
||||
}
|
||||
}
|
||||
}
|
46
src/NzbDrone.Api/NetImport/ImportExclusionsResource.cs
Normal file
46
src/NzbDrone.Api/NetImport/ImportExclusionsResource.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Api.NetImport
|
||||
{
|
||||
public class ImportExclusionsResource : ProviderResource
|
||||
{
|
||||
//public int Id { get; set; }
|
||||
public int TmdbId { get; set; }
|
||||
public string MovieTitle { get; set; }
|
||||
public int MovieYear { get; set; }
|
||||
}
|
||||
|
||||
public static class ImportExclusionsResourceMapper
|
||||
{
|
||||
public static ImportExclusionsResource ToResource(this Core.NetImport.ImportExclusions.ImportExclusion model)
|
||||
{
|
||||
if (model == null) return null;
|
||||
|
||||
return new ImportExclusionsResource
|
||||
{
|
||||
Id = model.Id,
|
||||
TmdbId = model.TmdbId,
|
||||
MovieTitle = model.MovieTitle,
|
||||
MovieYear = model.MovieYear
|
||||
};
|
||||
}
|
||||
|
||||
public static List<ImportExclusionsResource> ToResource(this IEnumerable<Core.NetImport.ImportExclusions.ImportExclusion> exclusions)
|
||||
{
|
||||
return exclusions.Select(ToResource).ToList();
|
||||
}
|
||||
|
||||
public static Core.NetImport.ImportExclusions.ImportExclusion ToModel(this ImportExclusionsResource resource)
|
||||
{
|
||||
return new Core.NetImport.ImportExclusions.ImportExclusion
|
||||
{
|
||||
TmdbId = resource.TmdbId,
|
||||
MovieTitle = resource.MovieTitle,
|
||||
MovieYear = resource.MovieYear
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -271,6 +271,8 @@
|
||||
<Compile Include="Wanted\MovieCutoffModule.cs" />
|
||||
<Compile Include="Wanted\MovieMissingModule.cs" />
|
||||
<Compile Include="Series\MovieDiscoverModule.cs" />
|
||||
<Compile Include="NetImport\ImportExclusionsModule.cs" />
|
||||
<Compile Include="NetImport\ImportExclusionsResource.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@ -80,5 +80,30 @@ public static List<TResult> SelectList<TSource, TResult>(this IEnumerable<TSourc
|
||||
{
|
||||
return source.Select(predicate).ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source, int n)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
if (n < 0)
|
||||
throw new ArgumentOutOfRangeException("n",
|
||||
"Argument n should be non-negative.");
|
||||
|
||||
return InternalDropLast(source, n);
|
||||
}
|
||||
|
||||
private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
|
||||
{
|
||||
Queue<T> buffer = new Queue<T>(n + 1);
|
||||
|
||||
foreach (T x in source)
|
||||
{
|
||||
buffer.Enqueue(x);
|
||||
|
||||
if (buffer.Count == n + 1)
|
||||
yield return buffer.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
@ -16,7 +18,10 @@ protected override void MainDbUpgrade()
|
||||
{
|
||||
if (!this.Schema.Schema("dbo").Table("ImportExclusions").Exists())
|
||||
{
|
||||
Create.Table("ImportExclusions").WithColumn("tmdbid").AsInt64().NotNullable().Unique().PrimaryKey();
|
||||
Create.TableForModel("ImportExclusions")
|
||||
.WithColumn("TmdbId").AsInt64().NotNullable().Unique().PrimaryKey()
|
||||
.WithColumn("MovieTitle").AsString().Nullable()
|
||||
.WithColumn("MovieYear").AsInt64().Nullable().WithDefault(0);
|
||||
}
|
||||
Execute.WithConnection(AddExisting);
|
||||
}
|
||||
@ -27,18 +32,23 @@ private void AddExisting(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
getSeriesCmd.Transaction = tran;
|
||||
getSeriesCmd.CommandText = @"SELECT Key, Value FROM Config WHERE Key = 'importexclusions'";
|
||||
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
|
||||
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (seriesReader.Read())
|
||||
{
|
||||
var Key = seriesReader.GetString(0);
|
||||
var Value = seriesReader.GetString(1);
|
||||
var importExclusions = Value.Split(',').Select(x => "(\""+Regex.Replace(x, @"^.*\-(.*)$", "$1")+"\")").ToList();
|
||||
|
||||
var importExclusions = Value.Split(',').Select(x => {
|
||||
return string.Format("(\"{0}\", \"{1}\")", Regex.Replace(x, @"^.*\-(.*)$", "$1"),
|
||||
textInfo.ToTitleCase(string.Join(" ", x.Split('-').DropLast(1))));
|
||||
}).ToList();
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "INSERT INTO ImportExclusions (tmdbid) VALUES " + string.Join(", ", importExclusions);
|
||||
updateCmd.CommandText = "INSERT INTO ImportExclusions (tmdbid, MovieTitle) VALUES " + string.Join(", ", importExclusions);
|
||||
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
using NzbDrone.Core.Extras.Subtitles;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
@ -105,6 +106,8 @@ public static void Map()
|
||||
.Relationship()
|
||||
.HasOne(s => s.Profile, s => s.ProfileId)
|
||||
.HasOne(m => m.MovieFile, m => m.MovieFileId);
|
||||
|
||||
Mapper.Entity<ImportExclusion>().RegisterModel("ImportExclusions");
|
||||
|
||||
|
||||
Mapper.Entity<Episode>().RegisterModel("Episodes")
|
||||
|
@ -16,6 +16,7 @@
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
@ -29,8 +30,10 @@ public class SkyHookProxy : IProvideSeriesInfo, ISearchForNewSeries, IProvideMov
|
||||
private readonly ITmdbConfigService _configService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IPreDBService _predbService;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService, IPreDBService predbService, Logger logger)
|
||||
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService,
|
||||
IPreDBService predbService, IImportExclusionsService exclusionService, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_requestBuilder = requestBuilder.SkyHookTvdb;
|
||||
@ -38,6 +41,7 @@ public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBu
|
||||
_configService = configService;
|
||||
_movieService = movieService;
|
||||
_predbService = predbService;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -351,7 +355,10 @@ public Movie GetMovieInfo(string imdbId)
|
||||
|
||||
public List<Movie> DiscoverNewMovies(string action)
|
||||
{
|
||||
string allIds = string.Join(",", _movieService.GetAllMovies().Select(m => m.TmdbId));
|
||||
var allMovies = _movieService.GetAllMovies();
|
||||
var allExclusions = _exclusionService.GetAllExclusions();
|
||||
string allIds = string.Join(",", allMovies.Select(m => m.TmdbId));
|
||||
string ignoredIds = string.Join(",", allExclusions.Select(ex => ex.TmdbId));
|
||||
|
||||
HttpRequest request;
|
||||
List<MovieResult> results;
|
||||
@ -387,7 +394,7 @@ public List<Movie> DiscoverNewMovies(string action)
|
||||
request.AllowAutoRedirect = true;
|
||||
request.Method = HttpMethod.POST;
|
||||
request.Headers.ContentType = "application/x-www-form-urlencoded";
|
||||
request.SetContent($"tmdbids={allIds}");
|
||||
request.SetContent($"tmdbids={allIds}&ignoredIds={ignoredIds}");
|
||||
|
||||
var response = _httpClient.Post<List<MovieResult>>(request);
|
||||
|
||||
@ -399,6 +406,8 @@ public List<Movie> DiscoverNewMovies(string action)
|
||||
results = response.Resource;
|
||||
}
|
||||
|
||||
results = results.Where(m => allMovies.None(mo => mo.TmdbId == m.id) && allExclusions.None(ex => ex.TmdbId == m.id)).ToList();
|
||||
|
||||
return results.SelectList(MapMovie);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using System.IO;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.ImportExclusions
|
||||
{
|
||||
public class ImportExclusion : ModelBase
|
||||
{
|
||||
public int TmdbId { get; set; }
|
||||
public string MovieTitle { get; set; }
|
||||
public int MovieYear { get; set; }
|
||||
|
||||
new public string ToString()
|
||||
{
|
||||
return string.Format("Excluded Movie: [{0}][{1} {2}]", TmdbId, MovieTitle, MovieYear);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
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;
|
||||
using NzbDrone.Core.Parser.RomanNumerals;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using CoreParser = NzbDrone.Core.Parser.Parser;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.ImportExclusions
|
||||
{
|
||||
public interface IImportExclusionsRepository : IBasicRepository<ImportExclusion>
|
||||
{
|
||||
bool IsMovieExcluded(int tmdbid);
|
||||
}
|
||||
|
||||
public class ImportExclusionsRepository : BasicRepository<ImportExclusion>, IImportExclusionsRepository
|
||||
{
|
||||
protected IMainDatabase _database;
|
||||
|
||||
public ImportExclusionsRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public bool IsMovieExcluded(int tmdbid)
|
||||
{
|
||||
return Query.Where(ex => ex.TmdbId == tmdbid).Any();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.ImportExclusions
|
||||
{
|
||||
public interface IImportExclusionsService
|
||||
{
|
||||
List<ImportExclusion> GetAllExclusions();
|
||||
bool IsMovieExcluded(int tmdbid);
|
||||
ImportExclusion AddExclusion(ImportExclusion exclusion);
|
||||
void RemoveExclusion(ImportExclusion exclusion);
|
||||
ImportExclusion GetById(int id);
|
||||
}
|
||||
|
||||
public class ImportExclusionsService : IImportExclusionsService
|
||||
{
|
||||
private readonly IImportExclusionsRepository _exclusionRepository;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
public ImportExclusionsService(IImportExclusionsRepository exclusionRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_exclusionRepository = exclusionRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public ImportExclusion AddExclusion(ImportExclusion exclusion)
|
||||
{
|
||||
return _exclusionRepository.Insert(exclusion);
|
||||
}
|
||||
|
||||
public List<ImportExclusion> GetAllExclusions()
|
||||
{
|
||||
return _exclusionRepository.All().ToList();
|
||||
}
|
||||
|
||||
public bool IsMovieExcluded(int tmdbid)
|
||||
{
|
||||
return _exclusionRepository.IsMovieExcluded(tmdbid);
|
||||
}
|
||||
|
||||
public void RemoveExclusion(ImportExclusion exclusion)
|
||||
{
|
||||
_exclusionRepository.Delete(exclusion);
|
||||
}
|
||||
|
||||
public ImportExclusion GetById(int id)
|
||||
{
|
||||
return _exclusionRepository.Get(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.NetImport
|
||||
{
|
||||
@ -32,11 +33,14 @@ public class NetImportSearchService : IFetchNetImport, IExecute<NetImportSyncCom
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ISearchForNzb _nzbSearchService;
|
||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
|
||||
|
||||
public NetImportSearchService(INetImportFactory netImportFactory, IMovieService movieService,
|
||||
ISearchForNewMovie movieSearch, IRootFolderService rootFolder, ISearchForNzb nzbSearchService,
|
||||
IProcessDownloadDecisions processDownloadDecisions, IConfigService configService, Logger logger)
|
||||
IProcessDownloadDecisions processDownloadDecisions, IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
Logger logger)
|
||||
{
|
||||
_netImportFactory = netImportFactory;
|
||||
_movieService = movieService;
|
||||
@ -44,6 +48,7 @@ public NetImportSearchService(INetImportFactory netImportFactory, IMovieService
|
||||
_nzbSearchService = nzbSearchService;
|
||||
_processDownloadDecisions = processDownloadDecisions;
|
||||
_rootFolder = rootFolder;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
}
|
||||
@ -119,18 +124,12 @@ public void Execute(NetImportSyncCommand message)
|
||||
|
||||
|
||||
var importExclusions = new List<string>();
|
||||
if (_configService.ImportExclusions != null)
|
||||
{
|
||||
// Replace `movie-title-tmdbid` with just tmdbid in exclusions
|
||||
importExclusions = _configService.ImportExclusions.Split(',').Select(x => Regex.Replace(x, @"^.*\-(.*)$", "$1")).ToList();
|
||||
// listedMovies = listedMovies.Where(ah => importExclusions.Any(h => ah.TmdbId.ToString() != h)).ToList();
|
||||
}
|
||||
|
||||
//var downloadedCount = 0;
|
||||
foreach (var movie in listedMovies)
|
||||
{
|
||||
var mapped = _movieSearch.MapMovieToTmdbMovie(movie);
|
||||
if (mapped != null && !importExclusions.Any(x => x == mapped.TmdbId.ToString()))
|
||||
if (mapped != null && !_exclusionService.IsMovieExcluded(mapped.TmdbId))
|
||||
{
|
||||
//List<DownloadDecision> decisions;
|
||||
mapped.AddOptions = new AddMovieOptions {SearchForMovie = true};
|
||||
|
@ -1281,6 +1281,9 @@
|
||||
<Compile Include="Datastore\Migration\136_add_pathstate_to_movies.cs" />
|
||||
<Compile Include="MetadataSource\IDiscoverNewMovies.cs" />
|
||||
<Compile Include="Datastore\Migration\137_add_import_exclusions_table.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusion.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusionsRepository.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusionsService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
||||
@ -1350,6 +1353,9 @@
|
||||
<Compile Include="Notifications\Telegram\TelegramError.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Folder Include="NetImport\ImportExclusions\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
@ -14,6 +14,7 @@
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
@ -51,6 +52,7 @@ public class MovieService : IMovieService, IHandle<MovieFileAddedEvent>,
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
@ -60,12 +62,14 @@ public MovieService(IMovieRepository movieRepository,
|
||||
IEpisodeService episodeService,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
IConfigService configService,
|
||||
IImportExclusionsService exclusionService,
|
||||
Logger logger)
|
||||
{
|
||||
_movieRepository = movieRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_configService = configService;
|
||||
_exclusionService = exclusionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -286,14 +290,7 @@ public void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false
|
||||
var movie = _movieRepository.Get(movieId);
|
||||
if (addExclusion)
|
||||
{
|
||||
if (_configService.ImportExclusions.Empty())
|
||||
{
|
||||
_configService.ImportExclusions = movie.TitleSlug;
|
||||
}
|
||||
else if (!_configService.ImportExclusions.Contains(movie.TitleSlug))
|
||||
{
|
||||
_configService.ImportExclusions += ',' + movie.TitleSlug;
|
||||
}
|
||||
_exclusionService.AddExclusion(new ImportExclusion {TmdbId = movie.TmdbId, MovieTitle = movie.Title, MovieYear = movie.Year } );
|
||||
}
|
||||
_movieRepository.Delete(movieId);
|
||||
_eventAggregator.PublishEvent(new MovieDeletedEvent(movie, deleteFiles));
|
||||
|
@ -6,6 +6,7 @@ var AddMoviesCollection = require('./AddMoviesCollection');
|
||||
var SearchResultCollectionView = require('./SearchResultCollectionView');
|
||||
var EmptyView = require('./EmptyView');
|
||||
var NotFoundView = require('./NotFoundView');
|
||||
var DiscoverEmptyView = require('./DiscoverEmptyView');
|
||||
var ErrorView = require('./ErrorView');
|
||||
var LoadingView = require('../Shared/LoadingView');
|
||||
var FullMovieCollection = require("../Movies/FullMovieCollection");
|
||||
@ -112,14 +113,10 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
if (this.isDiscover) {
|
||||
this.ui.searchBar.hide();
|
||||
if (FullMovieCollection.length > 0) {
|
||||
this._discoverRecos();
|
||||
} else {
|
||||
this.listenTo(FullMovieCollection, "sync", this._discover);
|
||||
}
|
||||
if (this.collection.length == 0) {
|
||||
this._discoverRecos();
|
||||
/*if (this.collection.length == 0) {
|
||||
this.searchResult.show(new LoadingView());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
},
|
||||
|
||||
@ -192,8 +189,13 @@ module.exports = Marionette.Layout.extend({
|
||||
_showResults : function() {
|
||||
if (!this.isClosed) {
|
||||
if (this.collection.length === 0) {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
||||
if (this.isDiscover) {
|
||||
this.searchResult.show(new DiscoverEmptyView());
|
||||
} else {
|
||||
this.ui.searchBar.show();
|
||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
||||
}
|
||||
|
||||
} else {
|
||||
this.searchResult.show(this.resultCollectionView);
|
||||
if (!this.showingAll) {
|
||||
@ -224,11 +226,10 @@ module.exports = Marionette.Layout.extend({
|
||||
if (this.collection.action === action) {
|
||||
return
|
||||
}
|
||||
this.collection.reset();
|
||||
this.searchResult.show(new LoadingView());
|
||||
this.collection.action = action;
|
||||
this.collection.fetch({
|
||||
data : { action : action }
|
||||
});
|
||||
this.currentSearchPromise = this.collection.fetch();
|
||||
},
|
||||
|
||||
_discoverRecos : function() {
|
||||
|
5
src/UI/AddMovies/DiscoverEmptyView.js
Normal file
5
src/UI/AddMovies/DiscoverEmptyView.js
Normal file
@ -0,0 +1,5 @@
|
||||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.CompositeView.extend({
|
||||
template : 'AddMovies/DiscoverEmptyViewTemplate'
|
||||
});
|
6
src/UI/AddMovies/DiscoverEmptyViewTemplate.hbs
Normal file
6
src/UI/AddMovies/DiscoverEmptyViewTemplate.hbs
Normal file
@ -0,0 +1,6 @@
|
||||
<div class="text-center col-md-12">
|
||||
<h3>
|
||||
No movies left to discover. Come back at another time :)
|
||||
</h3>
|
||||
|
||||
</div>
|
@ -7,6 +7,7 @@ var Profiles = require('../Profile/ProfileCollection');
|
||||
var RootFolders = require('./RootFolders/RootFolderCollection');
|
||||
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
|
||||
var FullMovieCollection = require('../Movies/FullMovieCollection');
|
||||
var ImportExclusionModel = require("../Settings/NetImport/ImportExclusionModel");
|
||||
var Config = require('../Config');
|
||||
var Messenger = require('../Shared/Messenger');
|
||||
var AsValidatedView = require('../Mixins/AsValidatedView');
|
||||
@ -33,6 +34,7 @@ var view = Marionette.ItemView.extend({
|
||||
events : {
|
||||
'click .x-add' : '_addWithoutSearch',
|
||||
'click .x-add-search' : '_addAndSearch',
|
||||
"click .x-ignore" : "_ignoreMovie",
|
||||
'change .x-profile' : '_profileChanged',
|
||||
'change .x-root-folder' : '_rootFolderChanged',
|
||||
'change .x-season-folder' : '_seasonFolderChanged',
|
||||
@ -239,6 +241,13 @@ var view = Marionette.ItemView.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_ignoreMovie : function() {
|
||||
var exclusion = new ImportExclusionModel({tmdbId : this.model.get("tmdbId"),
|
||||
movieTitle : this.model.get("title"), movieYear : this.model.get("year")});
|
||||
exclusion.save();
|
||||
this.remove();
|
||||
},
|
||||
|
||||
_rootFoldersUpdated : function() {
|
||||
this._configureTemplateHelpers();
|
||||
this.render();
|
||||
|
@ -106,6 +106,10 @@
|
||||
<button class="btn btn-success add x-add-search" title="Add and Search for movie">
|
||||
<i class="icon-sonarr-search"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-warning ignore x-ignore" title="Ignore this movie, so it does not show up anymore">
|
||||
<i class="icon-sonarr-ignore"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
|
@ -304,6 +304,10 @@
|
||||
.fa-icon-color(@brand-danger);
|
||||
}
|
||||
|
||||
.icon-sonarr-ignore {
|
||||
.fa-icon-content(@fa-var-eye-slash);
|
||||
}
|
||||
|
||||
.icon-sonarr-deleted {
|
||||
.fa-icon-content(@fa-var-trash);
|
||||
}
|
||||
|
24
src/UI/Settings/NetImport/DeleteExclusionCell.js
Normal file
24
src/UI/Settings/NetImport/DeleteExclusionCell.js
Normal file
@ -0,0 +1,24 @@
|
||||
var vent = require('vent');
|
||||
var Backgrid = require('backgrid');
|
||||
|
||||
module.exports = Backgrid.Cell.extend({
|
||||
className : 'delete-episode-file-cell',
|
||||
|
||||
events : {
|
||||
'click' : '_onClick'
|
||||
},
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
this.$el.html('<i class="icon-sonarr-delete" title="Delete exclusion."></i>');
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onClick : function() {
|
||||
var self = this;
|
||||
|
||||
this.model.destroy();
|
||||
|
||||
}
|
||||
});
|
18
src/UI/Settings/NetImport/ExclusionTitleCell.js
Normal file
18
src/UI/Settings/NetImport/ExclusionTitleCell.js
Normal file
@ -0,0 +1,18 @@
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'exclusion-title-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
var title = this.model.get("movieTitle");
|
||||
var year = this.model.get("movieYear");
|
||||
var str = title;
|
||||
if (year > 1800) {
|
||||
str += " ("+year+")";
|
||||
}
|
||||
this.$el.html(str);
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
7
src/UI/Settings/NetImport/ImportExclusionModel.js
Normal file
7
src/UI/Settings/NetImport/ImportExclusionModel.js
Normal file
@ -0,0 +1,7 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/exclusions',
|
||||
|
||||
});
|
9
src/UI/Settings/NetImport/ImportExclusionsCollection.js
Normal file
9
src/UI/Settings/NetImport/ImportExclusionsCollection.js
Normal file
@ -0,0 +1,9 @@
|
||||
var Backbone = require('backbone');
|
||||
var NetImportModel = require('./ImportExclusionModel');
|
||||
|
||||
var ImportExclusionsCollection = Backbone.Collection.extend({
|
||||
model : NetImportModel,
|
||||
url : window.NzbDrone.ApiRoot + '/exclusions',
|
||||
});
|
||||
|
||||
module.exports = new ImportExclusionsCollection();
|
@ -3,6 +3,14 @@ var NetImportCollection = require('./NetImportCollection');
|
||||
var CollectionView = require('./NetImportCollectionView');
|
||||
var OptionsView = require('./Options/NetImportOptionsView');
|
||||
var RootFolderCollection = require('../../AddMovies/RootFolders/RootFolderCollection');
|
||||
var ImportExclusionsCollection = require('./ImportExclusionsCollection');
|
||||
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||
var DeleteExclusionCell = require('./DeleteExclusionCell');
|
||||
var ExclusionTitleCell = require("./ExclusionTitleCell");
|
||||
var _ = require('underscore');
|
||||
var vent = require('vent');
|
||||
var Backgrid = require('backgrid');
|
||||
var $ = require('jquery');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Settings/NetImport/NetImportLayoutTemplate',
|
||||
@ -10,18 +18,58 @@ module.exports = Marionette.Layout.extend({
|
||||
regions : {
|
||||
lists : '#x-lists-region',
|
||||
listOption : '#x-list-options-region',
|
||||
importExclusions : "#exclusions"
|
||||
},
|
||||
|
||||
columns: [{
|
||||
name: '',
|
||||
cell: SelectAllCell,
|
||||
headerCell: 'select-all',
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'tmdbId',
|
||||
label: 'TMDBID',
|
||||
cell: Backgrid.StringCell,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'movieTitle',
|
||||
label: 'Title',
|
||||
cell: ExclusionTitleCell,
|
||||
cellValue: 'this',
|
||||
}, {
|
||||
name: 'this',
|
||||
label: '',
|
||||
cell: DeleteExclusionCell,
|
||||
sortable: false,
|
||||
}],
|
||||
|
||||
|
||||
initialize : function() {
|
||||
this.indexersCollection = new NetImportCollection();
|
||||
this.indexersCollection.fetch();
|
||||
RootFolderCollection.fetch().done(function() {
|
||||
RootFolderCollection.synced = true;
|
||||
});
|
||||
ImportExclusionsCollection.fetch().done(function() {
|
||||
ImportExclusionsCollection.synced = true;
|
||||
});
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.listenTo(ImportExclusionsCollection, "sync", this._showExclusions);
|
||||
if (ImportExclusionsCollection.synced === true) {
|
||||
this._showExclusions();
|
||||
}
|
||||
this.lists.show(new CollectionView({ collection : this.indexersCollection }));
|
||||
this.listOption.show(new OptionsView({ model : this.model }));
|
||||
},
|
||||
|
||||
_showExclusions : function() {
|
||||
this.exclusionGrid = new Backgrid.Grid({
|
||||
collection: ImportExclusionsCollection,
|
||||
columns: this.columns,
|
||||
className: 'table table-hover'
|
||||
});
|
||||
this.importExclusions.show(this.exclusionGrid);
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,9 @@
|
||||
<div id="x-lists-region"></div>
|
||||
<div class="form-horizontal">
|
||||
<div id="x-list-options-region"></div>
|
||||
<fieldset>
|
||||
<legend>Import Exclusions</legend>
|
||||
<div id="exclusions">
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
@ -1,6 +1,11 @@
|
||||
var Marionette = require('marionette');
|
||||
var AsModelBoundView = require('../../../Mixins/AsModelBoundView');
|
||||
var AsValidatedView = require('../../../Mixins/AsValidatedView');
|
||||
var ImportExclusionsCollection = require('./../ImportExclusionsCollection');
|
||||
var SelectAllCell = require('../../../Cells/SelectAllCell');
|
||||
var _ = require('underscore');
|
||||
var vent = require('vent');
|
||||
var Backgrid = require('backgrid');
|
||||
var $ = require('jquery');
|
||||
require('../../../Mixins/TagInput');
|
||||
require('bootstrap');
|
||||
@ -22,10 +27,10 @@ var view = Marionette.ItemView.extend({
|
||||
'click .x-revoke-trakt-tokens' : '_revokeTraktTokens'
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
initialize : function() {
|
||||
|
||||
},
|
||||
|
||||
|
||||
onShow : function() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var oauth = params.get('access');
|
||||
@ -39,78 +44,25 @@ var view = Marionette.ItemView.extend({
|
||||
//Config.setValue("traktRefreshToken", refresh);
|
||||
var tokenExpiry = Math.floor(Date.now() / 1000) + 4838400;
|
||||
this.ui.tokenExpiry.val(tokenExpiry).trigger('change'); // this means the token will expire in 8 weeks (4838400 seconds)
|
||||
//Config.setValue("traktTokenExpiry",tokenExpiry);
|
||||
//Config.setValue("traktTokenExpiry",tokenExpiry);
|
||||
//this.model.isSaved = false;
|
||||
//window.alert("Trakt Authentication Complete - Click Save to make the change take effect");
|
||||
}
|
||||
if (this.ui.authToken.val() && this.ui.refreshToken.val()){
|
||||
this.ui.resetTokensButton.hide();
|
||||
this.ui.revokeTokensButton.show();
|
||||
this.ui.resetTokensButton.hide();
|
||||
this.ui.revokeTokensButton.show();
|
||||
} else {
|
||||
this.ui.resetTokensButton.show();
|
||||
this.ui.revokeTokensButton.hide();
|
||||
this.ui.resetTokensButton.show();
|
||||
this.ui.revokeTokensButton.hide();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
this.ui.importExclusions.tagsinput({
|
||||
trimValue : true,
|
||||
tagClass : 'label label-danger',
|
||||
/*itemText : function(item) {
|
||||
var uri;
|
||||
var text;
|
||||
if (item.startsWith('tt')) {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/imdb?imdbId='+item;
|
||||
}
|
||||
else {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/tmdb?tmdbId='+item;
|
||||
}
|
||||
var promise = $.ajax({
|
||||
url : uri,
|
||||
type : 'GET',
|
||||
async : false,
|
||||
});
|
||||
promise.success(function(response) {
|
||||
text=response['title']+' ('+response['year']+')';
|
||||
});
|
||||
|
||||
promise.error(function(request, status, error) {
|
||||
text=item;
|
||||
});
|
||||
return text;
|
||||
}*/
|
||||
});
|
||||
this.ui.importExclusions.on('beforeItemAdd', function(event) {
|
||||
var uri;
|
||||
if (event.item.startsWith('tt')) {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/imdb?imdbId='+event.item;
|
||||
}
|
||||
else {
|
||||
uri = window.NzbDrone.ApiRoot + '/movies/lookup/tmdb?tmdbId='+event.item;
|
||||
}
|
||||
var promise = $.ajax({
|
||||
url : uri,
|
||||
type : 'GET',
|
||||
async : false,
|
||||
});
|
||||
promise.success(function(response) {
|
||||
event.cancel=false;
|
||||
|
||||
//var newText = response['tmdbId']+'-';
|
||||
//if (event.item.startsWith('tt')) {
|
||||
// newText = newText+'['+event.item+']';
|
||||
//}
|
||||
event.item = response.titleSlug;//+' ('+response['year']+')-'+response['tmdbId'];
|
||||
});
|
||||
|
||||
promise.error(function(request, status, error) {
|
||||
event.cancel = true;
|
||||
window.alert(event.item+' is not a valid! Must be valid tt#### IMDB ID or #### TMDB ID');
|
||||
});
|
||||
return event;
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
ui : {
|
||||
resetTraktTokens : '.x-reset-trakt-tokens',
|
||||
@ -118,7 +70,7 @@ var view = Marionette.ItemView.extend({
|
||||
refreshToken : '.x-trakt-refresh-token',
|
||||
resetTokensButton : '.x-reset-trakt-tokens',
|
||||
revokeTokensButton : '.x-revoke-trakt-tokens',
|
||||
tokenExpiry : '.x-trakt-token-expiry',
|
||||
tokenExpiry : '.x-trakt-token-expiry',
|
||||
importExclusions : '.x-import-exclusions'
|
||||
},
|
||||
|
||||
@ -131,15 +83,16 @@ var view = Marionette.ItemView.extend({
|
||||
|
||||
_revokeTraktTokens : function() {
|
||||
if (window.confirm("Log out of trakt.tv?")){
|
||||
//TODO: need to implement this: http://docs.trakt.apiary.io/#reference/authentication-oauth/revoke-token/revoke-an-access_token
|
||||
//TODO: need to implement this: http://docs.trakt.apiary.io/#reference/authentication-oauth/revoke-token/revoke-an-access_token
|
||||
this.ui.authToken.val('').trigger('change');
|
||||
this.ui.refreshToken.val('').trigger('change');
|
||||
this.ui.tokenExpiry.val(0).trigger('change');
|
||||
this.ui.refreshToken.val('').trigger('change');
|
||||
this.ui.tokenExpiry.val(0).trigger('change');
|
||||
this.ui.resetTokensButton.show();
|
||||
this.ui.revokeTokensButton.hide();
|
||||
window.alert("Logged out of Trakt.tv - Click Save to make the change take effect");
|
||||
this.ui.revokeTokensButton.hide();
|
||||
window.alert("Logged out of Trakt.tv - Click Save to make the change take effect");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -30,17 +30,17 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<!--<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Import Exclusions</label>
|
||||
<div class="col-sm-1 col-sm-push-4 help-inline">
|
||||
<i class="icon-sonarr-form-warning" title="Movies in this field will not be imported even if they exist on your lists."/>
|
||||
<i class="icon-sonarr-form-info" title="Comma separated imdbid or tmdbid: tt0120915,216138,tt0121765"/>
|
||||
</div>
|
||||
<div class="col-sm-4 col-sm-pull-1">
|
||||
|
||||
|
||||
<input type="text" name="importExclusions" class="form-control x-import-exclusions"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
<legend>Trakt Authentication</legend>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-1 control-label">Auth Token</label>
|
||||
@ -58,5 +58,6 @@
|
||||
<button class="btn btn-danger btn-icon-only x-reset-trakt-tokens" title="Reset Trakt Tokens"><i class="icon-sonarr-refresh"></i></button>
|
||||
<button class="btn btn-danger btn-icon-only x-revoke-trakt-tokens" title="Revoke Trakt Tokens"><i class="icon-sonarr-logout"></i></button>
|
||||
</div >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
@ -14,6 +14,7 @@ var IndexerCollection = require('./Indexers/IndexerCollection');
|
||||
var IndexerSettingsModel = require('./Indexers/IndexerSettingsModel');
|
||||
var NetImportSettingsModel = require("./NetImport/NetImportSettingsModel");
|
||||
var NetImportCollection = require('./NetImport/NetImportCollection');
|
||||
var ImportExclusionsCollection = require('./NetImport/ImportExclusionsCollection');
|
||||
var NetImportLayout = require('./NetImport/NetImportLayout');
|
||||
var DownloadClientLayout = require('./DownloadClient/DownloadClientLayout');
|
||||
var DownloadClientSettingsModel = require('./DownloadClient/DownloadClientSettingsModel');
|
||||
|
Loading…
Reference in New Issue
Block a user