From 0715962ec5a7a9bb4ed3a9d8b06788fc03f6186e Mon Sep 17 00:00:00 2001 From: Leonardo Galli Date: Wed, 4 Jan 2017 20:27:14 +0100 Subject: [PATCH] TheMovieDB.org is now used as metadata source. --- src/NzbDrone.Api/Series/MovieModule.cs | 2 +- src/NzbDrone.Api/Series/MovieResource.cs | 11 +- .../Cloud/SonarrCloudRequestBuilder.cs | 6 + .../Datastore/Migration/106_add_tmdb_stuff.cs | 21 ++++ .../MetadataSource/IProvideMovieInfo.cs | 1 + .../SkyHook/Resource/TMDBResources.cs | 108 ++++++++++++++++++ .../MetadataSource/SkyHook/SkyHookProxy.cs | 100 ++++++++++++++-- src/NzbDrone.Core/NzbDrone.Core.csproj | 2 + src/NzbDrone.Core/Tv/Movie.cs | 6 +- src/NzbDrone.Core/Tv/MovieService.cs | 2 +- src/NzbDrone.Core/Tv/MovieTitleNormalizer.cs | 10 +- src/NzbDrone.Core/Tv/RefreshMovieService.cs | 10 +- .../Validation/Paths/MovieExistsValidator.cs | 4 +- src/UI/Handlebars/Helpers/Series.js | 17 ++- src/UI/Movies/Details/InfoViewTemplate.hbs | 26 ++--- src/UI/Movies/Details/MoviesDetailsLayout.js | 2 +- .../Movies/Details/MoviesDetailsTemplate.hbs | 20 ++-- src/UI/Movies/movies.less | 22 ++++ 18 files changed, 317 insertions(+), 53 deletions(-) create mode 100644 src/NzbDrone.Core/Datastore/Migration/106_add_tmdb_stuff.cs create mode 100644 src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs diff --git a/src/NzbDrone.Api/Series/MovieModule.cs b/src/NzbDrone.Api/Series/MovieModule.cs index 5a8e5f52f..a40695a1c 100644 --- a/src/NzbDrone.Api/Series/MovieModule.cs +++ b/src/NzbDrone.Api/Series/MovieModule.cs @@ -73,7 +73,7 @@ namespace NzbDrone.Api.Movie PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.Title).NotEmpty(); - PostValidator.RuleFor(s => s.ImdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator); + PostValidator.RuleFor(s => s.TmdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator); PutValidator.RuleFor(s => s.Path).IsValidPath(); } diff --git a/src/NzbDrone.Api/Series/MovieResource.cs b/src/NzbDrone.Api/Series/MovieResource.cs index a35b2d210..b1924629d 100644 --- a/src/NzbDrone.Api/Series/MovieResource.cs +++ b/src/NzbDrone.Api/Series/MovieResource.cs @@ -28,6 +28,7 @@ namespace NzbDrone.Api.Movie public string Overview { get; set; } public DateTime? InCinemas { get; set; } public List Images { get; set; } + public string Website { get; set; } public string RemotePoster { get; set; } public int Year { get; set; } @@ -42,6 +43,7 @@ namespace NzbDrone.Api.Movie public DateTime? LastInfoSync { get; set; } public string CleanTitle { get; set; } public string ImdbId { get; set; } + public int TmdbId { get; set; } public string TitleSlug { get; set; } public string RootFolderPath { get; set; } public string Certification { get; set; } @@ -50,6 +52,7 @@ namespace NzbDrone.Api.Movie public DateTime Added { get; set; } public AddMovieOptions AddOptions { get; set; } public Ratings Ratings { get; set; } + public List AlternativeTitles { get; set; } //TODO: Add series statistics as a property of the series (instead of individual properties) @@ -79,7 +82,7 @@ namespace NzbDrone.Api.Movie return new MovieResource { Id = model.Id, - + TmdbId = model.TmdbId, Title = model.Title, //AlternateTitles SortTitle = model.SortTitle, @@ -108,10 +111,12 @@ namespace NzbDrone.Api.Movie TitleSlug = model.TitleSlug, RootFolderPath = model.RootFolderPath, Certification = model.Certification, + Website = model.Website, Genres = model.Genres, Tags = model.Tags, Added = model.Added, AddOptions = model.AddOptions, + AlternativeTitles = model.AlternativeTitles, Ratings = model.Ratings }; } @@ -123,6 +128,7 @@ namespace NzbDrone.Api.Movie return new Core.Tv.Movie { Id = resource.Id, + TmdbId = resource.TmdbId, Title = resource.Title, //AlternateTitles @@ -151,10 +157,12 @@ namespace NzbDrone.Api.Movie TitleSlug = resource.TitleSlug, RootFolderPath = resource.RootFolderPath, Certification = resource.Certification, + Website = resource.Website, Genres = resource.Genres, Tags = resource.Tags, Added = resource.Added, AddOptions = resource.AddOptions, + AlternativeTitles = resource.AlternativeTitles, Ratings = resource.Ratings }; } @@ -162,6 +170,7 @@ namespace NzbDrone.Api.Movie public static Core.Tv.Movie ToModel(this MovieResource resource, Core.Tv.Movie movie) { movie.ImdbId = resource.ImdbId; + movie.TmdbId = resource.TmdbId; movie.Path = resource.Path; movie.ProfileId = resource.ProfileId; diff --git a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs index 5c3712d85..e5347f71a 100644 --- a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs +++ b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs @@ -6,6 +6,7 @@ namespace NzbDrone.Common.Cloud { IHttpRequestBuilderFactory Services { get; } IHttpRequestBuilderFactory SkyHookTvdb { get; } + IHttpRequestBuilderFactory TMDB { get; } } public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder @@ -18,10 +19,15 @@ namespace NzbDrone.Common.Cloud SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/") .SetSegment("language", "en") .CreateFactory(); + + TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}") + .AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212") + .CreateFactory(); } public IHttpRequestBuilderFactory Services { get; private set; } public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; } + public IHttpRequestBuilderFactory TMDB { get; private set; } } } diff --git a/src/NzbDrone.Core/Datastore/Migration/106_add_tmdb_stuff.cs b/src/NzbDrone.Core/Datastore/Migration/106_add_tmdb_stuff.cs new file mode 100644 index 000000000..106dcdbd1 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/106_add_tmdb_stuff.cs @@ -0,0 +1,21 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(106)] + public class add_tmdb_stuff : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Movies") + .AddColumn("TmdbId").AsInt32().WithDefaultValue(0); + Alter.Table("Movies") + .AddColumn("Website").AsString().Nullable(); + Alter.Table("Movies") + .AlterColumn("ImdbId").AsString().Nullable(); + Alter.Table("Movies") + .AddColumn("AlternativeTitles").AsString().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs b/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs index 861564bc4..e5256a6dc 100644 --- a/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs +++ b/src/NzbDrone.Core/MetadataSource/IProvideMovieInfo.cs @@ -7,5 +7,6 @@ namespace NzbDrone.Core.MetadataSource public interface IProvideMovieInfo { Movie GetMovieInfo(string ImdbId); + Movie GetMovieInfo(int TmdbId); } } \ No newline at end of file diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs new file mode 100644 index 000000000..d6076d3fd --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TMDBResources.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MetadataSource.SkyHook.Resource +{ + + public class MovieSearchRoot + { + public int page { get; set; } + public MovieResult[] results { get; set; } + public int total_results { get; set; } + public int total_pages { get; set; } + } + + public class MovieResult + { + public string poster_path { get; set; } + public bool adult { get; set; } + public string overview { get; set; } + public string release_date { get; set; } + public int?[] genre_ids { get; set; } + public int id { get; set; } + public string original_title { get; set; } + public string original_language { get; set; } + public string title { get; set; } + public string backdrop_path { get; set; } + public float popularity { get; set; } + public int vote_count { get; set; } + public bool video { get; set; } + public float vote_average { get; set; } + } + + + public class MovieResourceRoot + { + public bool adult { get; set; } + public string backdrop_path { get; set; } + public Belongs_To_Collection belongs_to_collection { get; set; } + public int budget { get; set; } + public Genre[] genres { get; set; } + public string homepage { get; set; } + public int id { get; set; } + public string imdb_id { get; set; } + public string original_language { get; set; } + public string original_title { get; set; } + public string overview { get; set; } + public float popularity { get; set; } + public string poster_path { get; set; } + public Production_Companies[] production_companies { get; set; } + public Production_Countries[] production_countries { get; set; } + public string release_date { get; set; } + public int revenue { get; set; } + public int runtime { get; set; } + public Spoken_Languages[] spoken_languages { get; set; } + public string status { get; set; } + public string tagline { get; set; } + public string title { get; set; } + public bool video { get; set; } + public float vote_average { get; set; } + public int vote_count { get; set; } + public AlternativeTitles alternative_titles { get; set; } + } + + public class Belongs_To_Collection + { + public int id { get; set; } + public string name { get; set; } + public string poster_path { get; set; } + public string backdrop_path { get; set; } + } + + public class Genre + { + public int id { get; set; } + public string name { get; set; } + } + + public class Production_Companies + { + public string name { get; set; } + public int id { get; set; } + } + + public class Production_Countries + { + public string iso_3166_1 { get; set; } + public string name { get; set; } + } + + public class Spoken_Languages + { + public string iso_639_1 { get; set; } + public string name { get; set; } + } + + public class AlternativeTitles + { + public List titles { get; set; } + } + + public class Title + { + public string iso_3166_1 { get; set; } + public string title { get; set; } + } +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 97c1bb520..bdf52a171 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -20,11 +20,13 @@ namespace NzbDrone.Core.MetadataSource.SkyHook private readonly Logger _logger; private readonly IHttpRequestBuilderFactory _requestBuilder; + private readonly IHttpRequestBuilderFactory _movieBuilder; public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, Logger logger) { _httpClient = httpClient; _requestBuilder = requestBuilder.SkyHookTvdb; + _movieBuilder = requestBuilder.TMDB; _logger = logger; } @@ -58,6 +60,65 @@ namespace NzbDrone.Core.MetadataSource.SkyHook return new Tuple<Series, List<Episode>>(series, episodes.ToList()); } + public Movie GetMovieInfo(int TmdbId) + { + var request = _movieBuilder.Create() + .SetSegment("route", "movie") + .SetSegment("id", TmdbId.ToString()) + .SetSegment("secondaryRoute", "") + .AddQueryParam("append_to_response", "alternative_titles") + .AddQueryParam("country", "US") + .Build(); + + request.AllowAutoRedirect = true; + request.SuppressHttpError = true; + + var response = _httpClient.Get<MovieResourceRoot>(request); + + var resource = response.Resource; + + var movie = new Movie(); + + movie.TmdbId = TmdbId; + movie.ImdbId = resource.imdb_id; + movie.Title = resource.title; + movie.TitleSlug = movie.Title.ToLower().Replace(" ", "-"); + movie.CleanTitle = Parser.Parser.CleanSeriesTitle(movie.Title); + movie.Overview = resource.overview; + movie.Website = resource.homepage; + movie.InCinemas = DateTime.Parse(resource.release_date); + movie.Year = movie.InCinemas.Value.Year; + + movie.Images.Add(new MediaCover.MediaCover(MediaCoverTypes.Poster, "http://image.tmdb.org/t/p/"+"w500"+resource.poster_path));//TODO: Update to load image specs from tmdb page! + movie.Images.Add(new MediaCover.MediaCover(MediaCoverTypes.Banner, "http://image.tmdb.org/t/p/" + "w1280" + resource.backdrop_path)); + movie.Runtime = resource.runtime; + + foreach(Title title in resource.alternative_titles.titles) + { + movie.AlternativeTitles.Add(title.title); + } + + movie.Ratings = new Ratings(); + movie.Ratings.Votes = resource.vote_count; + movie.Ratings.Value = (decimal)resource.vote_average; + + foreach(Genre genre in resource.genres) + { + movie.Genres.Add(genre.name); + } + + if (resource.status == "Released") + { + movie.Status = MovieStatusType.Released; + } + else + { + movie.Status = MovieStatusType.Announced; + } + + return movie; + } + public Movie GetMovieInfo(string ImdbId) { var imdbRequest = new HttpRequest("http://www.omdbapi.com/?i=" + ImdbId + "&plot=full&r=json"); @@ -136,11 +197,22 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } } - var searchTerm = lowerTitle.Replace("+", "_").Replace(" ", "_"); + var searchTerm = lowerTitle.Replace("_", "+").Replace(" ", "+"); var firstChar = searchTerm.First(); - var imdbRequest = new HttpRequest("https://v2.sg.media-imdb.com/suggests/" + firstChar + "/" + searchTerm + ".json"); + var request = _movieBuilder.Create() + .SetSegment("route", "search") + .SetSegment("id", "movie") + .SetSegment("secondaryRoute", "") + .AddQueryParam("query", searchTerm) + .AddQueryParam("include_adult", false) + .Build(); + + request.AllowAutoRedirect = true; + request.SuppressHttpError = true; + + /*var imdbRequest = new HttpRequest("https://v2.sg.media-imdb.com/suggests/" + firstChar + "/" + searchTerm + ".json"); var response = _httpClient.Get(imdbRequest); @@ -154,31 +226,35 @@ namespace NzbDrone.Core.MetadataSource.SkyHook _logger.Warn("Json object: " + json); - _logger.Warn("Crash ahead."); + _logger.Warn("Crash ahead.");*/ + + var response = _httpClient.Get<MovieSearchRoot>(request); + + var movieResults = response.Resource.results; var imdbMovies = new List<Movie>(); - foreach (MovieResource entry in json.d) + foreach (MovieResult result in movieResults) { var imdbMovie = new Movie(); - imdbMovie.ImdbId = entry.id; + imdbMovie.TmdbId = result.id; try { - imdbMovie.SortTitle = entry.l; - imdbMovie.Title = entry.l; - string titleSlug = entry.l; + imdbMovie.SortTitle = result.title; + imdbMovie.Title = result.title; + string titleSlug = result.title; imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-"); - imdbMovie.Year = entry.y; + imdbMovie.Year = DateTime.Parse(result.release_date).Year; imdbMovie.Images = new List<MediaCover.MediaCover>(); try { - string url = (string)entry.i[0]; - var imdbPoster = new MediaCover.MediaCover(MediaCoverTypes.Poster, url); + string url = result.poster_path; + var imdbPoster = new MediaCover.MediaCover(MediaCoverTypes.Poster, "http://image.tmdb.org/t/p/" + "w500" + url); imdbMovie.Images.Add(imdbPoster); } catch (Exception e) { - _logger.Debug(entry); + _logger.Debug(result); continue; } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 860b3241f..8b7a9d121 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -183,6 +183,7 @@ <Compile Include="Datastore\Migration\002_remove_tvrage_imdb_unique_constraint.cs" /> <Compile Include="Datastore\Migration\003_remove_clean_title_from_scene_mapping.cs" /> <Compile Include="Datastore\Migration\004_updated_history.cs" /> + <Compile Include="Datastore\Migration\106_add_tmdb_stuff.cs" /> <Compile Include="Datastore\Migration\105_fix_history_movieId.cs" /> <Compile Include="Datastore\Migration\005_added_eventtype_to_history.cs" /> <Compile Include="Datastore\Migration\006_add_index_to_log_time.cs" /> @@ -811,6 +812,7 @@ <Compile Include="MetadataSource\SkyHook\Resource\MovieResource.cs" /> <Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" /> <Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" /> + <Compile Include="MetadataSource\SkyHook\Resource\TMDBResources.cs" /> <Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" /> <Compile Include="MetadataSource\SearchSeriesComparer.cs" /> <Compile Include="MetadataSource\SkyHook\SkyHookException.cs" /> diff --git a/src/NzbDrone.Core/Tv/Movie.cs b/src/NzbDrone.Core/Tv/Movie.cs index d5b49cda0..e31a66896 100644 --- a/src/NzbDrone.Core/Tv/Movie.cs +++ b/src/NzbDrone.Core/Tv/Movie.cs @@ -16,8 +16,9 @@ namespace NzbDrone.Core.Tv Genres = new List<string>(); Actors = new List<Actor>(); Tags = new HashSet<int>(); + AlternativeTitles = new List<string>(); } - + public int TmdbId { get; set; } public string ImdbId { get; set; } public string Title { get; set; } public string CleanTitle { get; set; } @@ -30,6 +31,7 @@ namespace NzbDrone.Core.Tv public int Runtime { get; set; } public List<MediaCover.MediaCover> Images { get; set; } public string TitleSlug { get; set; } + public string Website { get; set; } public string Path { get; set; } public int Year { get; set; } public Ratings Ratings { get; set; } @@ -44,7 +46,7 @@ namespace NzbDrone.Core.Tv public AddMovieOptions AddOptions { get; set; } public LazyLoaded<MovieFile> MovieFile { get; set; } public int MovieFileId { get; set; } - + public List<string> AlternativeTitles { get; set; } public override string ToString() { return string.Format("[{0}][{1}]", ImdbId, Title.NullSafe()); diff --git a/src/NzbDrone.Core/Tv/MovieService.cs b/src/NzbDrone.Core/Tv/MovieService.cs index e255cb215..e61279cac 100644 --- a/src/NzbDrone.Core/Tv/MovieService.cs +++ b/src/NzbDrone.Core/Tv/MovieService.cs @@ -77,7 +77,7 @@ namespace NzbDrone.Core.Tv _logger.Info("Adding Movie {0} Path: [{1}]", newMovie, newMovie.Path); newMovie.CleanTitle = newMovie.Title.CleanSeriesTitle(); - newMovie.SortTitle = MovieTitleNormalizer.Normalize(newMovie.Title, newMovie.ImdbId); + newMovie.SortTitle = MovieTitleNormalizer.Normalize(newMovie.Title, newMovie.TmdbId); newMovie.Added = DateTime.UtcNow; _movieRepository.Insert(newMovie); diff --git a/src/NzbDrone.Core/Tv/MovieTitleNormalizer.cs b/src/NzbDrone.Core/Tv/MovieTitleNormalizer.cs index fd2f87cd1..c82dd014d 100644 --- a/src/NzbDrone.Core/Tv/MovieTitleNormalizer.cs +++ b/src/NzbDrone.Core/Tv/MovieTitleNormalizer.cs @@ -4,16 +4,16 @@ namespace NzbDrone.Core.Tv { public static class MovieTitleNormalizer { - private readonly static Dictionary<string, string> PreComputedTitles = new Dictionary<string, string> + private readonly static Dictionary<int, string> PreComputedTitles = new Dictionary<int, string> { - { "tt_109823457098", "a to z" }, + { 999999999, "a to z" }, }; - public static string Normalize(string title, string imdbid) + public static string Normalize(string title, int tmdbid) { - if (PreComputedTitles.ContainsKey(imdbid)) + if (PreComputedTitles.ContainsKey(tmdbid)) { - return PreComputedTitles[imdbid]; + return PreComputedTitles[tmdbid]; } return Parser.Parser.NormalizeTitle(title).ToLower(); diff --git a/src/NzbDrone.Core/Tv/RefreshMovieService.cs b/src/NzbDrone.Core/Tv/RefreshMovieService.cs index 7b01bcf87..a4ac0d8ec 100644 --- a/src/NzbDrone.Core/Tv/RefreshMovieService.cs +++ b/src/NzbDrone.Core/Tv/RefreshMovieService.cs @@ -51,7 +51,7 @@ namespace NzbDrone.Core.Tv try { - movieInfo = _movieInfo.GetMovieInfo(movie.ImdbId); + movieInfo = _movieInfo.GetMovieInfo(movie.TmdbId); } catch (MovieNotFoundException) { @@ -59,10 +59,10 @@ namespace NzbDrone.Core.Tv return; } - if (movie.ImdbId != movieInfo.ImdbId) + if (movie.TmdbId != movieInfo.TmdbId) { - _logger.Warn("Movie '{0}' (tvdbid {1}) was replaced with '{2}' (tvdbid {3}), because the original was a duplicate.", movie.Title, movie.ImdbId, movieInfo.Title, movieInfo.ImdbId); - movie.ImdbId = movieInfo.ImdbId; + _logger.Warn("Movie '{0}' (tvdbid {1}) was replaced with '{2}' (tvdbid {3}), because the original was a duplicate.", movie.Title, movie.TmdbId, movieInfo.Title, movieInfo.TmdbId); + movie.TmdbId = movieInfo.TmdbId; } movie.Title = movieInfo.Title; @@ -80,6 +80,8 @@ namespace NzbDrone.Core.Tv movie.Genres = movieInfo.Genres; movie.Certification = movieInfo.Certification; movie.InCinemas = movieInfo.InCinemas; + movie.Website = movieInfo.Website; + movie.AlternativeTitles = movieInfo.AlternativeTitles; movie.Year = movieInfo.Year; try diff --git a/src/NzbDrone.Core/Validation/Paths/MovieExistsValidator.cs b/src/NzbDrone.Core/Validation/Paths/MovieExistsValidator.cs index 88519e41f..5aec9a132 100644 --- a/src/NzbDrone.Core/Validation/Paths/MovieExistsValidator.cs +++ b/src/NzbDrone.Core/Validation/Paths/MovieExistsValidator.cs @@ -18,9 +18,9 @@ namespace NzbDrone.Core.Validation.Paths { if (context.PropertyValue == null) return true; - var imdbid = context.PropertyValue.ToString(); + int tmdbId = (int)context.PropertyValue; - return (!_seriesService.GetAllMovies().Exists(s => s.ImdbId == imdbid)); + return (!_seriesService.GetAllMovies().Exists(s => s.TmdbId == tmdbId)); } } } \ No newline at end of file diff --git a/src/UI/Handlebars/Helpers/Series.js b/src/UI/Handlebars/Helpers/Series.js index 016279e6e..fe1b4e313 100644 --- a/src/UI/Handlebars/Helpers/Series.js +++ b/src/UI/Handlebars/Helpers/Series.js @@ -33,7 +33,7 @@ Handlebars.registerHelper('remotePoster', function() { } return new Handlebars.SafeString('<img class="series-poster placeholder-image" src="{0}">'.format(placeholder)); -}) +}); Handlebars.registerHelper('traktUrl', function() { return 'http://trakt.tv/search/tvdb/' + this.tvdbId + '?id_type=show'; @@ -47,6 +47,19 @@ Handlebars.registerHelper('tvdbUrl', function() { return 'http://imdb.com/title/tt' + this.imdbId; }); +Handlebars.registerHelper('tmdbUrl', function() { + return 'https://www.themoviedb.org/movie/' + this.tmdbId; +}); + +Handlebars.registerHelper('homepage', function() { + return this.website; +}); + +Handlebars.registerHelper('alternativeTitlesString', function() { + var titles = this.alternativeTitles; + return titles.slice(0,titles.length-1).join(", ") + " and " + titles[titles.length-1]; +}); + Handlebars.registerHelper('inCinemas', function() { var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" @@ -55,7 +68,7 @@ Handlebars.registerHelper('inCinemas', function() { var year = cinemasDate.getFullYear(); var month = monthNames[cinemasDate.getMonth()]; return "In Cinemas " + month + " " + year; -}) +}); Handlebars.registerHelper('tvRageUrl', function() { return 'http://www.tvrage.com/shows/id-' + this.tvRageId; diff --git a/src/UI/Movies/Details/InfoViewTemplate.hbs b/src/UI/Movies/Details/InfoViewTemplate.hbs index 355db6750..1897c841a 100644 --- a/src/UI/Movies/Details/InfoViewTemplate.hbs +++ b/src/UI/Movies/Details/InfoViewTemplate.hbs @@ -1,5 +1,5 @@ <div class="row"> - <div class="col-md-9"> + <div class="col-md-8"> {{profile profileId}} {{#if network}} @@ -27,11 +27,13 @@ <span class="label label-default">Announced</span> {{/if_eq}} </div> - <div class="col-md-3"> + <div class="col-md-4"> <span class="series-info-links"> - <!--<a href="{{traktUrl}}" class="label label-info">Trakt</a> - - <a href="{{tvdbUrl}}" class="label label-info">The TVDB</a>--> + <!--<a href="{{traktUrl}}" class="label label-info">Trakt</a>--> + {{#if website}} + <a href="{{homepage}}" class="label label-info">Homepage</a> + {{/if}} + <a href="{{tmdbUrl}}" class="label label-info">The Movie DB</a> {{#if imdbId}} <a href="{{imdbUrl}}" class="label label-info">IMDB</a> @@ -40,18 +42,12 @@ </div> </div> -{{#if alternateTitles}} +{{#if alternativeTitles}} <div class="row"> <div class="col-md-12"> - {{#each alternateTitles}} - {{#if_eq seasonNumber compare="-1"}} - <span class="label label-default">{{title}}</span> - {{/if_eq}} - - {{#if_eq sceneSeasonNumber compare="-1"}} - <span class="label label-default">{{title}}</span> - {{/if_eq}} - {{/each}} + <span class="alternative-titles"> + Also known as: {{alternativeTitlesString}}. + </span> </div> </div> {{/if}} diff --git a/src/UI/Movies/Details/MoviesDetailsLayout.js b/src/UI/Movies/Details/MoviesDetailsLayout.js index 4396c22de..fc251a049 100644 --- a/src/UI/Movies/Details/MoviesDetailsLayout.js +++ b/src/UI/Movies/Details/MoviesDetailsLayout.js @@ -274,7 +274,7 @@ module.exports = Marionette.Layout.extend({ _showBackdrop : function () { $('body').addClass('backdrop'); - var fanArt = this._getImage('fanart'); + var fanArt = this._getImage('banner'); if (fanArt) { this._backstrech = $.backstretch(fanArt); diff --git a/src/UI/Movies/Details/MoviesDetailsTemplate.hbs b/src/UI/Movies/Details/MoviesDetailsTemplate.hbs index 3a63cd045..9c3f6675a 100644 --- a/src/UI/Movies/Details/MoviesDetailsTemplate.hbs +++ b/src/UI/Movies/Details/MoviesDetailsTemplate.hbs @@ -36,12 +36,18 @@ </div> </div> <div id="movie-info"> - <ul class="nav nav-tabs" id="myTab"> - <li><a href="#movie-history" class="x-movie-history">History</a></li> - <li><a href="#movie-search" class="x-movie-search">Search</a></li> - </ul> - <div class="tab-content"> - <div class="tab-pane" id="movie-history"/> - <div class="tab-pane" id="movie-search"/> + <div class="movie-tabs"> + <div> + <div class="movie-tabs-card"> + <ul class="nav nav-tabs" id="myTab"> + <li><a href="#movie-history" class="x-movie-history">History</a></li> + <li><a href="#movie-search" class="x-movie-search">Search</a></li> + </ul> + <div class="tab-content"> + <div class="tab-pane" id="movie-history"/> + <div class="tab-pane" id="movie-search"/> + </div> + </div> + </div> </div> </div> diff --git a/src/UI/Movies/movies.less b/src/UI/Movies/movies.less index 57ff91ed4..95c7b87ea 100644 --- a/src/UI/Movies/movies.less +++ b/src/UI/Movies/movies.less @@ -8,6 +8,22 @@ max-width: 100%; } +.movie-tabs-card { + .card; + .opacity(0.9); + margin : 30px 10px; + padding : 10px 25px; + + .show-hide-episodes { + .clickable(); + text-align : center; + + i { + .clickable(); + } + } +} + .edit-movie-modal, .delete-movie-modal { overflow : visible; @@ -253,6 +269,12 @@ margin-bottom : 50px; } +.alternative-titles { + font-size: 12px; + color: rgba(255, 255, 255, 180); + opacity: .75; +} + .movie-season { .episode-number-cell {