More work on music!

pull/3895/head
Jamie Rees 5 years ago
parent 98346d1c86
commit 12a80c7903

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Hqub.MusicBrainz.API.Entities;
using Ombi.Api.MusicBrainz.Models;
namespace Ombi.Api.MusicBrainz
{
@ -9,5 +11,6 @@ namespace Ombi.Api.MusicBrainz
Task<IEnumerable<Artist>> SearchArtist(string artistQuery);
Task<IEnumerable<Release>> GetReleaseForArtist(string artistId);
Task<Artist> GetArtistInformation(string artistId);
Task<ReleaseGroupArt> GetCoverArtForReleaseGroup(string musicBrainzId, CancellationToken token);
}
}

@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace Ombi.Api.MusicBrainz.Models
{
public class ReleaseGroupArt
{
public string release { get; set; }
public Image[] images { get; set; }
}
public class Image
{
public int edit { get; set; }
public string id { get; set; }
public string image { get; set; }
public Thumbnails thumbnails { get; set; }
public string comment { get; set; }
public bool approved { get; set; }
public bool front { get; set; }
public string[] types { get; set; }
public bool back { get; set; }
}
public class Thumbnails
{
//[JsonProperty("250")]
//public string px250 { get; set; }
//[JsonProperty("500")]
//public string px500 { get; set; }
//[JsonProperty("1200")]
//public string px1200 { get; set; }
public string small { get; set; }
public string large { get; set; }
}
}

@ -2,14 +2,24 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Hqub.MusicBrainz.API;
using Hqub.MusicBrainz.API.Entities;
using Newtonsoft.Json;
using Ombi.Api.MusicBrainz.Models;
namespace Ombi.Api.MusicBrainz
{
public class MusicBrainzApi : IMusicBrainzApi
{
private readonly IApi _api;
public MusicBrainzApi(IApi api)
{
_api = api;
}
public async Task<IEnumerable<Artist>> SearchArtist(string artistQuery)
{
var artist = await Artist.SearchAsync(artistQuery, 10);
@ -34,10 +44,21 @@ namespace Ombi.Api.MusicBrainz
// Search for a release by title.
var releases = await Release.SearchAsync(query);
return releases.Items;
}
public async Task<ReleaseGroupArt> GetCoverArtForReleaseGroup(string musicBrainzId, CancellationToken token)
{
var request = new Request($"release-group/{musicBrainzId}", "http://coverartarchive.org", HttpMethod.Get);
var result = await _api.Request(request, token);
if (result.IsSuccessStatusCode)
{
var jsonContent = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ReleaseGroupArt>(jsonContent, Api.Settings);
}
return null;
}
private void AddHeaders(Request req)
{
req.AddHeader("Accept", "application/json");

@ -25,7 +25,7 @@ namespace Ombi.Api
private ILogger<Api> Logger { get; }
private readonly IOmbiHttpClient _client;
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new PluralPropertyContractResolver()
@ -66,7 +66,7 @@ namespace Ombi.Api
{
using (var req = await httpRequestMessage.Clone())
{
return await _client.SendAsync(req);
return await _client.SendAsync(req, cancellationToken);
}
});
}
@ -119,12 +119,12 @@ namespace Ombi.Api
}
public async Task Request(Request request)
public async Task<HttpResponseMessage> Request(Request request, CancellationToken token = default(CancellationToken))
{
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))
{
AddHeadersBody(request, httpRequestMessage);
var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
var httpResponseMessage = await _client.SendAsync(httpRequestMessage, token);
await LogDebugContent(httpResponseMessage);
if (!httpResponseMessage.IsSuccessStatusCode)
{
@ -133,6 +133,8 @@ namespace Ombi.Api
await LogError(request, httpResponseMessage);
}
}
return httpResponseMessage;
}
}

@ -1,11 +1,12 @@
using System.Threading;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Api
{
public interface IApi
{
Task Request(Request request);
Task<HttpResponseMessage> Request(Request request, CancellationToken token = default(CancellationToken));
Task<T> Request<T>(Request request, CancellationToken cancellationToken = default(CancellationToken));
Task<string> RequestContent(Request request);
T DeserializeXml<T>(string receivedString);

@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
using Ombi.Core.Models.Search.V2.Music;
@ -6,5 +7,6 @@ namespace Ombi.Core.Engine.Interfaces
public interface IMusicSearchEngineV2
{
Task<ArtistInformation> GetArtistInformation(string artistId);
Task<AlbumArt> GetReleaseGroupArt(string musicBrainzId, CancellationToken token);
}
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Ombi.Api.Lidarr;
using Ombi.Api.Lidarr.Models;
@ -81,7 +82,7 @@ namespace Ombi.Core.Engine.V2
if (lidarrArtistTask != null)
{
var artistResult = await lidarrArtistTask;
info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.Replace("http","https");
info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.Replace("http", "https");
info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.Replace("http", "https");
info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.Replace("http", "https");
info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.Replace("http", "https");
@ -90,7 +91,39 @@ namespace Ombi.Core.Engine.V2
return info;
}
public async Task<AlbumArt> GetReleaseGroupArt(string musicBrainzId, CancellationToken token)
{
var art = await _musicBrainzApi.GetCoverArtForReleaseGroup(musicBrainzId, token);
if (art == null || !art.images.Any())
{
return new AlbumArt();
}
foreach (var cover in art.images)
{
//if ((cover.thumbnails?.px250 ?? string.Empty).HasValue())
//{
// return new AlbumArt(cover.thumbnails.px250);
//}
if ((cover.thumbnails?.small ?? string.Empty).HasValue())
{
return new AlbumArt(cover.thumbnails.small);
}
//if ((cover.thumbnails?.px500 ?? string.Empty).HasValue())
//{
// return new AlbumArt(cover.thumbnails.px500);
//}
if ((cover.thumbnails?.large ?? string.Empty).HasValue())
{
return new AlbumArt(cover.thumbnails.large);
}
}
return new AlbumArt();
}
private List<BandMember> GetBandMembers(Artist artist)
{
var members = new List<BandMember>();

@ -0,0 +1,16 @@
namespace Ombi.Core.Models.Search.V2.Music
{
public class AlbumArt
{
public AlbumArt()
{
}
public AlbumArt(string url)
{
Image = url;
}
public string Image { get; set; }
}
}

@ -24,6 +24,8 @@ export interface IReleaseGroups {
title: string;
releaseDate: string;
type: string;
image: string; // Set by another api call
}
export interface IArtistLinks {
@ -53,4 +55,8 @@ export interface IBandMembers {
isCurrentMember: boolean;
start: string;
end: string;
}
export interface IAlbumArt {
image: string;
}

@ -1,71 +1,28 @@
<mat-accordion class="mat-elevation-z8 spacing-below">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.AlbumsTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer">
<div class="col-md-2" *ngFor="let r of albums">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<!-- <img class="real grow" matTooltip="{{r.title}}"
src="{{r.title}}" alt="Poster"
style="display: block;"> -->
{{r.title}} | {{r.type}}
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="ep">
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.SinglesTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer">
<div class="col-md-2" *ngFor="let r of singles">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<!-- <img class="real grow" matTooltip="{{r.title}}"
src="{{r.title}}" alt="Poster"
style="display: block;"> -->
{{r.title}} | {{r.type}}
</div>
</div>
<mat-card class="example-card">
<mat-card-header>
<mat-card-title>
{{'MediaDetails.AlbumsTitle' | translate}}
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="row card-spacer">
<span *ngFor="let r of albums">
<div class="col-md-2" *ngIf="r.image" >
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<img class="real grow" matTooltip="{{r.title}}"
src="{{r.image}}" alt="Poster"
style="display: block;">
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.EpTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer">
<div class="col-md-2" *ngFor="let r of ep">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<!-- <img class="real grow" matTooltip="{{r.title}}"
src="{{r.title}}" alt="Poster"
style="display: block;"> -->
{{r.title}} | {{r.type}}
</div>
</div>
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
</div>
</span>
</div>
</mat-card-content>
</mat-card>

@ -1,5 +1,6 @@
import { Component, Input, ViewEncapsulation, OnInit } from "@angular/core";
import { IReleaseGroups } from "../../../../../interfaces/IMusicSearchResultV2";
import { SearchV2Service } from "../../../../../services/searchV2.service";
@Component({
templateUrl: "./artist-release-panel.component.html",
@ -12,12 +13,14 @@ export class ArtistReleasePanel implements OnInit {
@Input() public releases: IReleaseGroups[];
public albums: IReleaseGroups[];
public singles: IReleaseGroups[];
public ep: IReleaseGroups[];
public ngOnInit(): void {
constructor(private searchService: SearchV2Service) { }
public ngOnInit() {
this.albums = this.releases.filter(x => x.type === "Album");
this.singles = this.releases.filter(x => x.type === "Single");
this.ep = this.releases.filter(x => x.type === "EP");
this.albums.forEach(a => {
this.searchService.getReleaseGroupArt(a.id).subscribe(x => a.image = x.image);
});
}
}

@ -9,7 +9,7 @@ import { ServiceHelpers } from "./service.helpers";
import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2";
import { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from "../interfaces/ISearchTvResultV2";
import { IArtistSearchResult } from "../interfaces/IMusicSearchResultV2";
import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2";
@Injectable()
export class SearchV2Service extends ServiceHelpers {
@ -103,4 +103,8 @@ export class SearchV2Service extends ServiceHelpers {
public getArtistInformation(artistId: string): Observable<IArtistSearchResult> {
return this.http.get<IArtistSearchResult>(`${this.url}/artist/${artistId}`);
}
public getReleaseGroupArt(mbid: string): Observable<IAlbumArt> {
return this.http.get<IAlbumArt>(`${this.url}/releasegroupart/${mbid}`);
}
}

@ -94,7 +94,7 @@
</mat-option>
</mat-select>
</mat-form-field>
<div><button mat-raised-button (click)="getLanguaggetMetadataProfileseProfiles(form)" color="primary">Load Metadata <span
<div><button mat-raised-button (click)="getMetadataProfiles(form)" color="primary">Load Metadata <span
*ngIf="metadataRunning" class="fa fa-spinner fa-spin"></span></button></div>
</div>

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading;
using Ombi.Core;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Engine.V2;
@ -343,5 +344,12 @@ namespace Ombi.Controllers.V2
return await _musicEngine.GetArtistInformation(artistId);
}
[HttpGet("releasegroupart/{musicBrainzId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<AlbumArt> GetReleaseGroupARt(string musicBrainzId)
{
return await _musicEngine.GetReleaseGroupArt(musicBrainzId, CancellationToken);
}
}
}

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization;
using System.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Ombi.Controllers.V2
@ -9,6 +10,6 @@ namespace Ombi.Controllers.V2
[ApiController]
public class V2Controller : ControllerBase
{
public CancellationToken CancellationToken => HttpContext.RequestAborted;
}
}

@ -203,6 +203,7 @@
"RecommendationsTitle": "Recommendations",
"SimilarTitle": "Similar",
"VideosTitle": "Videos",
"AlbumsTitle":"Albums",
"Casts": {
"CastTitle": "Cast",
"Character": "Character",

Loading…
Cancel
Save