mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-05 02:22:31 +01:00
Added discovery tab based on tmdb recommendations based on your existing movies. (#1450)
Keep scroll position on more in search result view. Added TMDB score to search results.
This commit is contained in:
parent
3eb351823e
commit
446d661345
@ -270,6 +270,7 @@
|
||||
<Compile Include="Wanted\MissingModule.cs" />
|
||||
<Compile Include="Wanted\MovieCutoffModule.cs" />
|
||||
<Compile Include="Wanted\MovieMissingModule.cs" />
|
||||
<Compile Include="Series\MovieDiscoverModule.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
44
src/NzbDrone.Api/Series/MovieDiscoverModule.cs
Normal file
44
src/NzbDrone.Api/Series/MovieDiscoverModule.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class MovieDiscoverModule : NzbDroneRestModule<MovieResource>
|
||||
{
|
||||
private readonly IDiscoverNewMovies _searchProxy;
|
||||
|
||||
public MovieDiscoverModule(IDiscoverNewMovies searchProxy)
|
||||
: base("/movies/discover")
|
||||
{
|
||||
_searchProxy = searchProxy;
|
||||
Get["/"] = x => Search();
|
||||
}
|
||||
|
||||
private Response Search()
|
||||
{
|
||||
var imdbResults = _searchProxy.DiscoverNewMovies();
|
||||
return MapToResource(imdbResults).AsResponse();
|
||||
}
|
||||
|
||||
private static IEnumerable<MovieResource> MapToResource(IEnumerable<Core.Tv.Movie> movies)
|
||||
{
|
||||
foreach (var currentSeries in movies)
|
||||
{
|
||||
var resource = currentSeries.ToResource();
|
||||
var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||
if (poster != null)
|
||||
{
|
||||
resource.RemotePoster = poster.Url;
|
||||
}
|
||||
|
||||
yield return resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
src/NzbDrone.Core/MetadataSource/IDiscoverNewMovies.cs
Normal file
10
src/NzbDrone.Core/MetadataSource/IDiscoverNewMovies.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource
|
||||
{
|
||||
public interface IDiscoverNewMovies
|
||||
{
|
||||
List<Movie> DiscoverNewMovies();
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
public class SkyHookProxy : IProvideSeriesInfo, ISearchForNewSeries, IProvideMovieInfo, ISearchForNewMovie
|
||||
public class SkyHookProxy : IProvideSeriesInfo, ISearchForNewSeries, IProvideMovieInfo, ISearchForNewMovie, IDiscoverNewMovies
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
@ -348,6 +348,29 @@ public Movie GetMovieInfo(string imdbId)
|
||||
return resources.movie_results.SelectList(MapMovie).FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Movie> DiscoverNewMovies()
|
||||
{
|
||||
string allIds = string.Join(",", _movieService.GetAllMovies().Select(m => m.TmdbId));
|
||||
var request = new HttpRequestBuilder("https://radarr.video/recommendations/api.php").AddQueryParam("tmdbids", allIds).Build();
|
||||
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
var response = _httpClient.Get<List<MovieResult>>(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new HttpException(request, response);
|
||||
}
|
||||
|
||||
if (response.Headers.ContentType != HttpAccept.Json.Value)
|
||||
{
|
||||
throw new HttpException(request, response);
|
||||
}
|
||||
|
||||
var movieResults = response.Resource;
|
||||
|
||||
return movieResults.SelectList(MapMovie);
|
||||
}
|
||||
|
||||
private string StripTrailingTheFromTitle(string title)
|
||||
{
|
||||
if(title.EndsWith(",the"))
|
||||
@ -551,6 +574,8 @@ private Movie MapMovie(MovieResult result)
|
||||
|
||||
imdbMovie.Images = new List<MediaCover.MediaCover>();
|
||||
imdbMovie.Overview = result.overview;
|
||||
imdbMovie.Ratings = new Ratings { Value = (decimal)result.vote_average, Votes = result.vote_count};
|
||||
|
||||
try
|
||||
{
|
||||
var imdbPoster = _configService.GetCoverForURL(result.poster_path, MediaCoverTypes.Poster);
|
||||
|
@ -1279,6 +1279,7 @@
|
||||
<Compile Include="MediaFiles\Commands\RenameMovieFolderCommand.cs" />
|
||||
<Compile Include="Tv\QueryExtensions.cs" />
|
||||
<Compile Include="Datastore\Migration\136_add_pathstate_to_movies.cs" />
|
||||
<Compile Include="MetadataSource\IDiscoverNewMovies.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
||||
|
@ -8,6 +8,7 @@ var ProfileCollection = require('../Profile/ProfileCollection');
|
||||
var AddFromListView = require("./List/AddFromListView");
|
||||
var RootFolderCollection = require('./RootFolders/RootFolderCollection');
|
||||
var BulkImportView = require("./BulkImport/BulkImportView");
|
||||
var DiscoverMoviesCollection = require("./DiscoverMoviesCollection");
|
||||
require('../Movies/MoviesCollection');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
@ -22,7 +23,7 @@ module.exports = Marionette.Layout.extend({
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-import' : '_importMovies',
|
||||
'click .x-discover' : '_discoverMovies',
|
||||
'click .x-bulk-import' : '_bulkImport',
|
||||
'click .x-add-new' : '_addMovies',
|
||||
"click .x-add-lists" : "_addFromList",
|
||||
@ -70,10 +71,11 @@ module.exports = Marionette.Layout.extend({
|
||||
this.workspace.show(new BulkImportView({ model : options.model}));
|
||||
},
|
||||
|
||||
_importMovies : function() {
|
||||
this.rootFolderLayout = new RootFolderLayout();
|
||||
this.listenTo(this.rootFolderLayout, 'folderSelected', this._folderSelected);
|
||||
AppLayout.modalRegion.show(this.rootFolderLayout);
|
||||
_discoverMovies : function(options) {
|
||||
options = options || {};
|
||||
options.action = "discover";
|
||||
options.collection = new DiscoverMoviesCollection();
|
||||
this.workspace.show(new AddMoviesView(options));
|
||||
},
|
||||
|
||||
_addMovies : function(options) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group add-movies-btn-group btn-group-lg btn-block btn-group-collapse">
|
||||
<button class="btn btn-default col-md-3 col-xs-12 x-bulk-import"><i class="icon-sonarr-view-list hidden-xs"></i> Bulk Import Movies</button>
|
||||
<button type="button" class="btn btn-default col-md-4 col-xs-12 add-movies-import-btn x-import"><i class="icon-sonarr-hdd"/>Import existing movies on disk</button>
|
||||
<button type="button" class="btn btn-default col-md-4 col-xs-12 add-movies-import-btn x-discover"><i class="icon-sonarr-star"/> Discover new movies</button>
|
||||
<button class="btn btn-default col-md-2 col-xs-12 x-add-new"><i class="icon-sonarr-active hidden-xs"></i> Add New Movie</button>
|
||||
<button class="btn btn-default col-md-3 col-xs-12 x-add-lists"><i class="icon-sonarr-active hidden-xs"></i> Add Movies from Lists</button>
|
||||
</div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var AddMoviesCollection = require('./AddMoviesCollection');
|
||||
@ -7,6 +8,7 @@ var EmptyView = require('./EmptyView');
|
||||
var NotFoundView = require('./NotFoundView');
|
||||
var ErrorView = require('./ErrorView');
|
||||
var LoadingView = require('../Shared/LoadingView');
|
||||
var FullMovieCollection = require("../Movies/FullMovieCollection");
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'AddMovies/AddMoviesViewTemplate',
|
||||
@ -18,7 +20,8 @@ module.exports = Marionette.Layout.extend({
|
||||
ui : {
|
||||
moviesSearch : '.x-movies-search',
|
||||
searchBar : '.x-search-bar',
|
||||
loadMore : '.x-load-more'
|
||||
loadMore : '.x-load-more',
|
||||
discoverHeader : ".x-discover-header"
|
||||
},
|
||||
|
||||
events : {
|
||||
@ -27,7 +30,7 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
initialize : function(options) {
|
||||
this.isExisting = options.isExisting;
|
||||
this.collection = new AddMoviesCollection();
|
||||
this.collection = options.collection || new AddMoviesCollection();
|
||||
|
||||
if (this.isExisting) {
|
||||
this.collection.unmappedFolderModel = this.model;
|
||||
@ -51,6 +54,13 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
if (options.action === "search") {
|
||||
this.search({term: options.query});
|
||||
} else if (options.action == "discover") {
|
||||
this.isDiscover = true;
|
||||
if (FullMovieCollection.length > 0) {
|
||||
this._discover();
|
||||
} else {
|
||||
this.listenTo(FullMovieCollection, "sync", this._discover);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
@ -58,6 +68,8 @@ module.exports = Marionette.Layout.extend({
|
||||
onRender : function() {
|
||||
var self = this;
|
||||
|
||||
|
||||
|
||||
this.$el.addClass(this.className);
|
||||
|
||||
this.ui.moviesSearch.keyup(function(e) {
|
||||
@ -95,10 +107,21 @@ module.exports = Marionette.Layout.extend({
|
||||
if (this.isExisting) {
|
||||
this.ui.searchBar.hide();
|
||||
}
|
||||
|
||||
if (this.isDiscover) {
|
||||
this.ui.searchBar.hide();
|
||||
if (this.collection.length == 0) {
|
||||
this.searchResult.show(new LoadingView());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.ui.discoverHeader.hide();
|
||||
this.ui.moviesSearch.focus();
|
||||
if (this.isDiscover) {
|
||||
this.ui.discoverHeader.show();
|
||||
}
|
||||
},
|
||||
|
||||
search : function(options) {
|
||||
@ -140,7 +163,10 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
_onLoadMore : function() {
|
||||
var showingAll = this.resultCollectionView.showMore();
|
||||
if (!this.isDiscover) {
|
||||
this.ui.searchBar.show();
|
||||
}
|
||||
|
||||
|
||||
if (showingAll) {
|
||||
this.ui.loadMore.hide();
|
||||
@ -185,5 +211,9 @@ module.exports = Marionette.Layout.extend({
|
||||
this.searchResult.show(new ErrorView({ term : this.collection.term }));
|
||||
this.collection.term = '';
|
||||
}
|
||||
},
|
||||
|
||||
_discover : function() {
|
||||
this.collection.fetch()
|
||||
}
|
||||
});
|
||||
|
@ -4,6 +4,9 @@
|
||||
{{folder.path}}
|
||||
</div>
|
||||
</div>{{/if}}
|
||||
<h2 class="x-discover-header">
|
||||
Recommendations by The Movie Database based on your library:
|
||||
</h2>
|
||||
<div class="x-search-bar">
|
||||
<div class="input-group input-group-lg add-movies-search">
|
||||
<span class="input-group-addon"><i class="icon-sonarr-search"/></span>
|
||||
|
22
src/UI/AddMovies/DiscoverMoviesCollection.js
Normal file
22
src/UI/AddMovies/DiscoverMoviesCollection.js
Normal file
@ -0,0 +1,22 @@
|
||||
var Backbone = require('backbone');
|
||||
var MovieModel = require('../Movies/MovieModel');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + "/movies/discover",
|
||||
model : MovieModel,
|
||||
|
||||
parse : function(response) {
|
||||
var self = this;
|
||||
|
||||
_.each(response, function(model) {
|
||||
model.id = undefined;
|
||||
|
||||
if (self.unmappedFolderModel) {
|
||||
model.path = self.unmappedFolderModel.get('folder').path;
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
});
|
@ -2,6 +2,7 @@ var Marionette = require('marionette');
|
||||
var SearchResultView = require('./SearchResultView');
|
||||
var FullMovieCollection = require('../Movies/FullMovieCollection');
|
||||
var vent = require('vent');
|
||||
var $ = require("jquery");
|
||||
|
||||
module.exports = Marionette.CollectionView.extend({
|
||||
itemView : SearchResultView,
|
||||
@ -28,9 +29,10 @@ module.exports = Marionette.CollectionView.extend({
|
||||
},
|
||||
|
||||
showMore : function() {
|
||||
var pos = $(window).scrollTop();
|
||||
this.showing += 5;
|
||||
this.render();
|
||||
|
||||
$(window).scrollTop(pos);
|
||||
return this.showing >= this.collection.length;
|
||||
},
|
||||
|
||||
|
@ -26,7 +26,10 @@
|
||||
{{#if_eq status compare="inCinemas"}}
|
||||
<span class="label label-warning">In Cinemas</span>
|
||||
{{/if_eq}}
|
||||
<span class="label label-default" title="{{ratings.votes}} Vote(s)">{{ratings.value}}</span>
|
||||
</span>
|
||||
|
||||
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -307,6 +307,9 @@
|
||||
.icon-sonarr-deleted {
|
||||
.fa-icon-content(@fa-var-trash);
|
||||
}
|
||||
.icon-sonarr-star {
|
||||
.fa-icon-content(@fa-var-star);
|
||||
}
|
||||
|
||||
.icon-sonarr-clear {
|
||||
.fa-icon-content(@fa-var-trash);
|
||||
|
Loading…
Reference in New Issue
Block a user