From 1596dce6293001b41dba06c61077259b8f522235 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 1 Apr 2016 10:15:09 +0100 Subject: [PATCH] Basic search working for #32 --- .../Music/MusicBrainzSearchResults.cs | 152 ++++++++++++++++++ .../PlexRequests.Api.Models.csproj | 1 + PlexRequests.Api/MusicBrainzApi.cs | 70 ++++++++ PlexRequests.Api/PlexRequests.Api.csproj | 1 + PlexRequests.UI/Content/search.js | 67 ++++++-- PlexRequests.UI/Modules/SearchModule.cs | 11 +- PlexRequests.UI/Views/Search/Index.cshtml | 9 +- 7 files changed, 296 insertions(+), 15 deletions(-) create mode 100644 PlexRequests.Api.Models/Music/MusicBrainzSearchResults.cs create mode 100644 PlexRequests.Api/MusicBrainzApi.cs diff --git a/PlexRequests.Api.Models/Music/MusicBrainzSearchResults.cs b/PlexRequests.Api.Models/Music/MusicBrainzSearchResults.cs new file mode 100644 index 000000000..658b8ca4b --- /dev/null +++ b/PlexRequests.Api.Models/Music/MusicBrainzSearchResults.cs @@ -0,0 +1,152 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: MusicBrainzSearchResults.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System.Collections.Generic; + +using Newtonsoft.Json; + +namespace PlexRequests.Api.Models.Music +{ + public class TextRepresentation + { + public string language { get; set; } + public string script { get; set; } + } + + public class Alias + { + [JsonProperty(PropertyName = "sort-name")] + public string SortName { get; set; } + public string name { get; set; } + public object locale { get; set; } + public string type { get; set; } + public object primary { get; set; } + [JsonProperty(PropertyName = "begin-date")] + public object BeginDate { get; set; } + [JsonProperty(PropertyName = "end-date")] + public object EndDate { get; set; } + } + + public class Artist + { + public string id { get; set; } + public string name { get; set; } + [JsonProperty(PropertyName = "sort-date")] + public string SortName { get; set; } + public string disambiguation { get; set; } + public List aliases { get; set; } + } + + public class ArtistCredit + { + public Artist artist { get; set; } + } + + public class ReleaseGroup + { + public string id { get; set; } + [JsonProperty(PropertyName = "primary-type")] + public string PrimaryType { get; set; } + [JsonProperty(PropertyName = "secondary-types")] + public List SecondaryTypes { get; set; } + } + + public class Area + { + public string id { get; set; } + public string name { get; set; } + [JsonProperty(PropertyName = "sort-name")] + public string SortName { get; set; } + [JsonProperty(PropertyName = "iso-3166-1-codes")] + public List ISO31661Codes { get; set; } + } + + public class ReleaseEvent + { + public string date { get; set; } + public Area area { get; set; } + } + + public class Label + { + public string id { get; set; } + public string name { get; set; } + } + + public class LabelInfo + { + [JsonProperty(PropertyName = "catalog-number")] + public string CatalogNumber { get; set; } + public Label label { get; set; } + } + + public class Medium + { + public string format { get; set; } + [JsonProperty(PropertyName = "disc-count")] + public int DiscCount { get; set; } + [JsonProperty(PropertyName = "catalog-number")] + public int CatalogNumber { get; set; } + } + + public class Release + { + public string id { get; set; } + public string score { get; set; } + public int count { get; set; } + public string title { get; set; } + public string status { get; set; } + public string disambiguation { get; set; } + public string packaging { get; set; } + + [JsonProperty(PropertyName = "text-representation")] + public TextRepresentation TextRepresentation { get; set; } + [JsonProperty(PropertyName = "artist-credit")] + public List ArtistCredit { get; set; } + [JsonProperty(PropertyName = "release-group")] + public ReleaseGroup ReleaseGroup { get; set; } + public string date { get; set; } + public string country { get; set; } + [JsonProperty(PropertyName = "release-events")] + public List ReleaseEvents { get; set; } + public string barcode { get; set; } + public string asin { get; set; } + [JsonProperty(PropertyName = "label-info")] + public List LabelInfo { get; set; } + [JsonProperty(PropertyName = "track-count")] + public int TrackCount { get; set; } + public List media { get; set; } + } + + public class MusicBrainzSearchResults + { + public string created { get; set; } + public int count { get; set; } + public int offset { get; set; } + public List releases { get; set; } + } + +} \ No newline at end of file diff --git a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj index 35def3d76..61a33d5e5 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -50,6 +50,7 @@ + diff --git a/PlexRequests.Api/MusicBrainzApi.cs b/PlexRequests.Api/MusicBrainzApi.cs new file mode 100644 index 000000000..f419fe851 --- /dev/null +++ b/PlexRequests.Api/MusicBrainzApi.cs @@ -0,0 +1,70 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: MusicBrainzApi.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System; + +using Newtonsoft.Json; + +using NLog; + +using PlexRequests.Api.Models.Music; + +using RestSharp; + +namespace PlexRequests.Api +{ + public class MusicBrainsApi + { + public MusicBrainsApi() + { + Api = new ApiRequest(); + } + private ApiRequest Api { get; } + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private readonly Uri BaseUri = new Uri("http://musicbrainz.org/ws/2/"); + + public MusicBrainzSearchResults SearchAlbum(string searchTerm) + { + Log.Trace("Searching for album: {0}", searchTerm); + var request = new RestRequest + { + Resource = "release/?query={searchTerm}&fmt=json", + Method = Method.GET + }; + request.AddUrlSegment("searchTerm", searchTerm); + + try + { + return Api.ExecuteJson(request, BaseUri); + } + catch (JsonSerializationException jse) + { + Log.Warn(jse); + return new MusicBrainzSearchResults(); // If there is no matching result we do not get returned a JSON string, it just returns "false". + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Api/PlexRequests.Api.csproj b/PlexRequests.Api/PlexRequests.Api.csproj index 9484f59a3..b655cc2be 100644 --- a/PlexRequests.Api/PlexRequests.Api.csproj +++ b/PlexRequests.Api/PlexRequests.Api.csproj @@ -66,6 +66,7 @@ + True True diff --git a/PlexRequests.UI/Content/search.js b/PlexRequests.UI/Content/search.js index 0f47d3ace..2d9d65d50 100644 --- a/PlexRequests.UI/Content/search.js +++ b/PlexRequests.UI/Content/search.js @@ -6,19 +6,22 @@ }); var searchSource = $("#search-template").html(); +var musicSource = $("#music-template").html(); var searchTemplate = Handlebars.compile(searchSource); +var musicTemplate = Handlebars.compile(musicSource); var noResultsHtml = "
" + "
Sorry, we didn't find any results!
"; -var movieTimer = 0; -var tvimer = 0; +var noResultsMusic = "
" + + "
Sorry, we didn't find any results!
"; +var searchTimer = 0; // Type in movie search $("#movieSearchContent").on("input", function () { - if (movieTimer) { - clearTimeout(movieTimer); + if (searchTimer) { + clearTimeout(searchTimer); } $('#movieSearchButton').attr("class","fa fa-spinner fa-spin"); - movieTimer = setTimeout(movieSearch, 400); + searchTimer = setTimeout(movieSearch, 400); }); @@ -34,11 +37,11 @@ $('#moviesInTheaters').on('click', function (e) { // Type in TV search $("#tvSearchContent").on("input", function () { - if (tvimer) { - clearTimeout(tvimer); + if (searchTimer) { + clearTimeout(searchTimer); } $('#tvSearchButton').attr("class", "fa fa-spinner fa-spin"); - tvimer = setTimeout(tvSearch, 400); + searchTimer = setTimeout(tvSearch, 400); }); // Click TV dropdown option @@ -72,6 +75,16 @@ $(document).on("click", ".dropdownTv", function (e) { sendRequestAjax(data, type, url, buttonId); }); +// Search Music +$("#musicSearchContent").on("input", function () { + if (searchTimer) { + clearTimeout(searchTimer); + } + $('#musicSearchButton').attr("class", "fa fa-spinner fa-spin"); + searchTimer = setTimeout(musicSearch, 400); + +}); + // Click Request for movie $(document).on("click", ".requestMovie", function (e) { e.preventDefault(); @@ -133,7 +146,7 @@ function moviesComingSoon() { } function moviesInTheaters() { - getMovies("/search/movie/playing") + getMovies("/search/movie/playing"); } function getMovies(url) { @@ -179,6 +192,29 @@ function getTvShows(url) { }); }; +function musicSearch() { + var query = $("#musicSearchContent").val(); + getMusic("/search/music/" + query); +} + +function getMusic(url) { + $("#musicList").html(""); + + $.ajax(url).success(function (results) { + if (results.count > 0) { + results.releases.forEach(function (result) { + var context = buildMusicContext(result); + + var html = musicTemplate(context); + $("#musicList").append(html); + }); + } + else { + $("#musicList").html(noResultsMusic); + } + $('#musicSearchButton').attr("class", "fa fa-search"); + }); +}; function buildMovieContext(result) { var date = new Date(result.releaseDate); @@ -212,3 +248,16 @@ function buildTvShowContext(result) { }; return context; } + +function buildMusicContext(result) { + + var context = { + id: result.id, + title: result.title, + overview: result.disambiguation, + year: result.date, + type: "music" + }; + + return context; +} diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index 066223d8f..3cf44c370 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -74,6 +74,7 @@ namespace PlexRequests.UI.Modules Get["movie/{searchTerm}"] = parameters => SearchMovie((string)parameters.searchTerm); Get["tv/{searchTerm}"] = parameters => SearchTvShow((string)parameters.searchTerm); + Get["music/{searchTerm}"] = parameters => SearchMusic((string)parameters.searchTerm); Get["movie/upcoming"] = parameters => UpcomingMovies(); Get["movie/playing"] = parameters => CurrentlyPlayingMovies(); @@ -152,6 +153,13 @@ namespace PlexRequests.UI.Modules return Response.AsJson(model); } + private Response SearchMusic(string searchTerm) + { + var api = new MusicBrainsApi(); + var albums = api.SearchAlbum(searchTerm); + return Response.AsJson(albums); + } + private Response UpcomingMovies() // TODO : Not used { var movies = MovieApi.GetUpcomingMovies(); @@ -174,7 +182,7 @@ namespace PlexRequests.UI.Modules { var movieApi = new TheMovieDbApi(); var movieInfo = movieApi.GetMovieInformation(movieId).Result; - string fullMovieName = string.Format("{0}{1}", movieInfo.Title, movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty); + var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}"; Log.Trace("Getting movie info from TheMovieDb"); Log.Trace(movieInfo.DumpJson); //#if !DEBUG @@ -192,6 +200,7 @@ namespace PlexRequests.UI.Modules existingRequest.RequestedUsers.Add(Username); RequestService.UpdateRequest(existingRequest); } + return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} was successfully added!" : $"{fullMovieName} has already been requested!" }); } diff --git a/PlexRequests.UI/Views/Search/Index.cshtml b/PlexRequests.UI/Views/Search/Index.cshtml index c485391f8..42d4d30a1 100644 --- a/PlexRequests.UI/Views/Search/Index.cshtml +++ b/PlexRequests.UI/Views/Search/Index.cshtml @@ -161,17 +161,16 @@
- poster + @*poster*@ - poster

{{overview}}

@@ -179,7 +178,7 @@
- +