Got the main artist page in

pull/3895/head
tidusjar 5 years ago
parent 6c443469af
commit e231d701cc

@ -8,6 +8,8 @@ using Hqub.MusicBrainz.API.Entities;
using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Internal;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Api.Lidarr;
using Ombi.Api.Lidarr.Models;
using Ombi.Api.MusicBrainz; using Ombi.Api.MusicBrainz;
using Ombi.Core.Engine.V2; using Ombi.Core.Engine.V2;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
@ -20,6 +22,7 @@ using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Test.Common; using Ombi.Test.Common;
using Artist = Hqub.MusicBrainz.API.Entities.Artist;
namespace Ombi.Core.Tests.Engine.V2 namespace Ombi.Core.Tests.Engine.V2
{ {
@ -30,6 +33,8 @@ namespace Ombi.Core.Tests.Engine.V2
private MusicSearchEngineV2 _engine; private MusicSearchEngineV2 _engine;
private Mock<IMusicBrainzApi> _musicApi; private Mock<IMusicBrainzApi> _musicApi;
private Mock<ILidarrApi> _lidarrApi;
private Mock<ISettingsService<LidarrSettings>> _lidarrSettings;
private Fixture F; private Fixture F;
[SetUp] [SetUp]
@ -48,10 +53,11 @@ namespace Ombi.Core.Tests.Engine.V2
var ombiSettings = new Mock<ISettingsService<OmbiSettings>>(); var ombiSettings = new Mock<ISettingsService<OmbiSettings>>();
var requestSub = new Mock<IRepository<RequestSubscription>>(); var requestSub = new Mock<IRepository<RequestSubscription>>();
_musicApi = new Mock<IMusicBrainzApi>(); _musicApi = new Mock<IMusicBrainzApi>();
var lidarrSettings = new Mock<ISettingsService<LidarrSettings>>(); _lidarrSettings = new Mock<ISettingsService<LidarrSettings>>();
_lidarrApi = new Mock<ILidarrApi>();
_engine = new MusicSearchEngineV2(principle.Object, requestService.Object, ruleEval.Object, _engine = new MusicSearchEngineV2(principle.Object, requestService.Object, ruleEval.Object,
um.Object, cache.Object, ombiSettings.Object, requestSub.Object, _musicApi.Object, um.Object, cache.Object, ombiSettings.Object, requestSub.Object, _musicApi.Object,
lidarrSettings.Object); _lidarrSettings.Object, _lidarrApi.Object);
} }
@ -155,6 +161,52 @@ namespace Ombi.Core.Tests.Engine.V2
yield return new TestCaseData("play.google.com", RelationLinks.Download, new Func<ArtistInformation, string>(artist => artist.Links.Google)).Returns("play.google.com").SetName("ArtistInformation_Links_Google"); yield return new TestCaseData("play.google.com", RelationLinks.Download, new Func<ArtistInformation, string>(artist => artist.Links.Google)).Returns("play.google.com").SetName("ArtistInformation_Links_Google");
yield return new TestCaseData("itunes.apple.com", RelationLinks.Download, new Func<ArtistInformation, string>(artist => artist.Links.Apple)).Returns("itunes.apple.com").SetName("ArtistInformation_Links_Apple"); yield return new TestCaseData("itunes.apple.com", RelationLinks.Download, new Func<ArtistInformation, string>(artist => artist.Links.Apple)).Returns("itunes.apple.com").SetName("ArtistInformation_Links_Apple");
} }
}
[Test]
public async Task GetArtistInformation_WithPosters()
{
_lidarrSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new LidarrSettings
{
Enabled = true,
ApiKey = "dasdsa",
Ip = "192.168.1.7"
});
_lidarrApi.Setup(x => x.GetArtistByForeignId(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(new ArtistResult
{
images = new Image[]
{
new Image
{
coverType = "poster",
url = "posterUrl"
},
new Image
{
coverType = "logo",
url = "logoUrl"
},
new Image
{
coverType = "banner",
url = "bannerUrl"
},
new Image
{
coverType = "fanArt",
url = "fanartUrl"
},
}
});
_musicApi.Setup(x => x.GetArtistInformation("pretend-artist-id")).ReturnsAsync(F.Create<Artist>());
var result = await _engine.GetArtistInformation("pretend-artist-id");
Assert.That(result.Banner, Is.EqualTo("bannerUrl"));
Assert.That(result.Poster, Is.EqualTo("posterUrl"));
Assert.That(result.Logo, Is.EqualTo("logoUrl"));
Assert.That(result.FanArt, Is.EqualTo("fanartUrl"));
}
} }
} }
}

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using Hqub.MusicBrainz.API.Entities; using Ombi.Api.Lidarr;
using Ombi.Api.Lidarr.Models;
using Ombi.Api.MusicBrainz; using Ombi.Api.MusicBrainz;
using Ombi.Core.Authentication; using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces; using Ombi.Core.Engine.Interfaces;
@ -16,6 +18,7 @@ using Ombi.Settings.Settings.Models;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Artist = Hqub.MusicBrainz.API.Entities.Artist;
using ReleaseGroup = Ombi.Core.Models.Search.V2.Music.ReleaseGroup; using ReleaseGroup = Ombi.Core.Models.Search.V2.Music.ReleaseGroup;
namespace Ombi.Core.Engine.V2 namespace Ombi.Core.Engine.V2
@ -24,19 +27,28 @@ namespace Ombi.Core.Engine.V2
{ {
private readonly IMusicBrainzApi _musicBrainzApi; private readonly IMusicBrainzApi _musicBrainzApi;
private readonly ISettingsService<LidarrSettings> _lidarrSettings; private readonly ISettingsService<LidarrSettings> _lidarrSettings;
private readonly ILidarrApi _lidarrApi;
public MusicSearchEngineV2(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules, public MusicSearchEngineV2(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules,
OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings, OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings,
IRepository<RequestSubscription> sub, IMusicBrainzApi musicBrainzApi, ISettingsService<LidarrSettings> lidarrSettings) IRepository<RequestSubscription> sub, IMusicBrainzApi musicBrainzApi, ISettingsService<LidarrSettings> lidarrSettings,
ILidarrApi lidarrApi)
: base(identity, requestService, rules, um, cache, ombiSettings, sub) : base(identity, requestService, rules, um, cache, ombiSettings, sub)
{ {
_musicBrainzApi = musicBrainzApi; _musicBrainzApi = musicBrainzApi;
_lidarrSettings = lidarrSettings; _lidarrSettings = lidarrSettings;
_lidarrApi = lidarrApi;
} }
public async Task<ArtistInformation> GetArtistInformation(string artistId) public async Task<ArtistInformation> GetArtistInformation(string artistId)
{ {
var artist = await _musicBrainzApi.GetArtistInformation(artistId); var artist = await _musicBrainzApi.GetArtistInformation(artistId);
var lidarrSettings = await GetLidarrSettings();
Task<ArtistResult> lidarrArtistTask = null;
if (lidarrSettings.Enabled)
{
lidarrArtistTask = _lidarrApi.GetArtistByForeignId(artistId, lidarrSettings.ApiKey, lidarrSettings.FullUri);
}
var info = new ArtistInformation var info = new ArtistInformation
{ {
@ -65,6 +77,17 @@ namespace Ombi.Core.Engine.V2
info.Links = GetLinksForArtist(artist); info.Links = GetLinksForArtist(artist);
info.Members = GetBandMembers(artist); info.Members = GetBandMembers(artist);
if (lidarrArtistTask != null)
{
var artistResult = await lidarrArtistTask;
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");
info.Overview = artistResult.overview;
}
return info; return info;
} }
@ -166,5 +189,11 @@ namespace Ombi.Core.Engine.V2
return links; return links;
} }
private LidarrSettings __lidarrSettings;
private async Task<LidarrSettings> GetLidarrSettings()
{
return __lidarrSettings ?? (__lidarrSettings = await _lidarrSettings.GetSettingsAsync());
}
} }
} }

@ -13,6 +13,11 @@ namespace Ombi.Core.Models.Search.V2.Music
public string Country { get; set; } public string Country { get; set; }
public string Region { get; set; } public string Region { get; set; }
public string Disambiguation { get; set; } public string Disambiguation { get; set; }
public string Banner { get; set; }
public string Logo { get; set; }
public string Poster { get; set; }
public string FanArt { get; set; }
public string Overview { get; set; }
public List<ReleaseGroup> ReleaseGroups { get; set; } public List<ReleaseGroup> ReleaseGroups { get; set; }
public ArtistLinks Links { get; set; } public ArtistLinks Links { get; set; }
public List<BandMember> Members { get; set; } public List<BandMember> Members { get; set; }

@ -7,9 +7,16 @@ export interface IArtistSearchResult {
country: string; country: string;
region: string; region: string;
disambiguation: string; disambiguation: string;
banner: string;
logo: string;
poster: string;
fanArt: string;
releaseGroups: IReleaseGroups[]; releaseGroups: IReleaseGroups[];
links: IArtistLinks; links: IArtistLinks;
members: IBandMembers[]; members: IBandMembers[];
overview: string;
background: any;
} }
export interface IReleaseGroups { export interface IReleaseGroups {

@ -4,31 +4,33 @@
<div *ngIf="artist" class="dark-theme"> <div *ngIf="artist" class="dark-theme">
<top-banner [title]="artist.name"></top-banner> <top-banner [title]="artist.name" [background]="getBackground()" [tagline]="artist.disambiguation"></top-banner>
<section id="info-wrapper"> <section id="info-wrapper">
<div class="small-middle-container"> <div class="small-middle-container">
<div class="row"> <div class="row">
<media-poster [posterPath]="'https://image.tmdb.org/t/p/w300/' + movie.posterPath"></media-poster> <media-poster [posterPath]="artist.poster"></media-poster>
<!--Next to poster--> <!--Next to poster-->
<div class="col-12 col-lg-3 col-xl-3 media-row"> <div class="col-12 col-lg-3 col-xl-3 media-row">
<social-icons [homepage]="movie.homepage" [theMoviedbId]="movie.id" <social-icons
[hasTrailer]="movie.videos.results.length > 0" (openTrailer)="openDialog()" [imdbId]="movie.imdbId" [homepage]="artist.links.homePage"
[twitter]="movie.externalIds.twitterId" [facebook]="movie.externalIds.facebookId" [doNotAppend]="true"
[instagram]="movie.externalIds.instagramId" [available]="movie.available" [plexUrl]="movie.plexUrl" [imdbId]="artist.links.imdb"
[embyUrl]="movie.embyUrl"></social-icons> [twitter]="artist.links.twitter"
[facebook]="artist.links.facebook"
[instagram]="artist.links.instagram"></social-icons>
</div> </div>
<div class="col-12 col-lg-6 col-xl-6 media-row"> <div class="col-12 col-lg-6 col-xl-6 media-row">
<button mat-raised-button class="btn-green btn-spacing" *ngIf="movie.available"> {{ <!-- <button mat-raised-button class="btn-green btn-spacing" *ngIf="movie.available"> {{
'Common.Available' | translate }}</button> 'Common.Available' | translate }}</button> -->
<span *ngIf="!movie.available"> <!-- <span *ngIf="!movie.available">
<span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span> <span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
<ng-template #requestedBtn> <ng-template #requestedBtn>
@ -43,8 +45,8 @@
<i *ngIf="movie.processed && !movie.requestProcessing" class="fa fa-check"></i> {{ <i *ngIf="movie.processed && !movie.requestProcessing" class="fa fa-check"></i> {{
'Common.Request' | translate }}</button> 'Common.Request' | translate }}</button>
</ng-template> </ng-template>
</span> </span> -->
<span *ngIf="isAdmin && hasRequest"> <!-- <span *ngIf="isAdmin && hasRequest">
<button (click)="approve()" mat-raised-button class="btn-spacing" color="accent"> <button (click)="approve()" mat-raised-button class="btn-spacing" color="accent">
<i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }} <i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}
</button> </button>
@ -61,12 +63,12 @@
mat-raised-button class="btn-spacing" color="warn"> mat-raised-button class="btn-spacing" color="warn">
<i class="fa fa-times"></i> {{ <i class="fa fa-times"></i> {{
'MediaDetails.Denied' | translate }}</button> 'MediaDetails.Denied' | translate }}</button>
</span> </span> -->
<button *ngIf="(hasRequest && movieRequest) || movie.available" mat-raised-button class="btn-spacing" <!-- <button *ngIf="(hasRequest && movieRequest) || movie.available" mat-raised-button class="btn-spacing"
color="danger" (click)="issue()"> color="danger" (click)="issue()">
<i class="fa fa-exclamation"></i> {{ <i class="fa fa-exclamation"></i> {{
'Requests.ReportIssue' | translate }}</button> 'Requests.ReportIssue' | translate }}</button> -->
@ -77,20 +79,10 @@
<div class="row"> <div class="row">
<div class="col-12 col-md-2"> <div class="col-12 col-md-2">
<button *ngIf="movie.belongsToCollection"
[routerLink]="'/discover/collection/' + movie.belongsToCollection.id" mat-raised-button
class="spacing-below full-width mat-elevation-z8">{{movie.belongsToCollection.name}}</button>
<mat-card class="mat-elevation-z8 spacing-below" *ngIf="isAdmin && movieRequest">
<mat-card-content class="medium-font">
<movie-admin-panel [movie]="movieRequest" (advancedOptionsChange)="setAdvancedOptions($event)">
</movie-admin-panel>
</mat-card-content>
</mat-card>
<mat-card class="mat-elevation-z8"> <mat-card class="mat-elevation-z8">
<mat-card-content class="medium-font"> <mat-card-content class="medium-font">
<movie-information-panel [movie]="movie" [advancedOptions]="advancedOptions"></movie-information-panel> <artist-information-panel [artist]="artist"></artist-information-panel>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
@ -102,19 +94,19 @@
<div class="col-12"> <div class="col-12">
<mat-card class=" mat-elevation-z8 spacing-below"> <mat-card class=" mat-elevation-z8 spacing-below">
<mat-card-content> <mat-card-content>
{{movie.overview}} {{artist.overview}}
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</div> </div>
</div> </div>
<div class="row"> <!-- <div class="row">
<div class="col-12"> <div class="col-12">
<cast-carousel [cast]="movie.credits.cast"></cast-carousel> <cast-carousel [cast]="movie.credits.cast"></cast-carousel>
</div> </div>
</div> </div> -->
<div class="row"> <!-- <div class="row">
<div class="col-12"> <div class="col-12">
<mat-accordion class="mat-elevation-z8 spacing-below"> <mat-accordion class="mat-elevation-z8 spacing-below">
<mat-expansion-panel> <mat-expansion-panel>
@ -181,7 +173,7 @@
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>
</div> </div>
</div> </div> -->

@ -16,7 +16,7 @@ import { IArtistSearchResult } from "../../../interfaces/IMusicSearchResultV2";
export class ArtistDetailsComponent { export class ArtistDetailsComponent {
private artistId: string; private artistId: string;
public artist: IArtistSearchResult; public artist: IArtistSearchResult = null;
public isAdmin: boolean; public isAdmin: boolean;
@ -35,6 +35,26 @@ export class ArtistDetailsComponent {
this.searchService.getArtistInformation(this.artistId).subscribe(x => this.artist = x); this.searchService.getArtistInformation(this.artistId).subscribe(x => this.artist = x);
} }
public getBackground(): string {
if(this.artist.fanArt) {
this.artist.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + this.artist.fanArt + ")");
return this.artist.background
}
if(this.artist.logo) {
this.artist.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + this.artist.logo + ")");
return this.artist.background
}
if(this.artist.poster) {
this.artist.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + this.artist.poster + ")");
return this.artist.background
}
return this.artist.background
}
public async request() { public async request() {
// const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null }).toPromise(); // const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null }).toPromise();
// if (result.result) { // if (result.result) {

@ -0,0 +1,18 @@
<div *ngIf="artist">
<div>
<strong>Type:</strong>
<div>{{artist.type}}</div>
</div>
<div>
<strong>Country</strong>
<div>{{artist.country}}</div>
</div>
<div>
<strong>Start Date</strong>
<div>{{artist.startYear}}</div>
</div>
<div *ngIf="artist.endYear">
<strong>End Date</strong>
<div>{{artist.endYear}}</div>
</div>
</div>

@ -0,0 +1,12 @@
import { Component, Input, ViewEncapsulation } from "@angular/core";
import { ISearchArtistResult } from "../../../../../interfaces";
@Component({
templateUrl: "./artist-information-panel.component.html",
styleUrls: ["../../../../media-details.component.scss"],
selector: "artist-information-panel",
encapsulation: ViewEncapsulation.None
})
export class ArtistInformationPanel {
@Input() public artist: ISearchArtistResult;
}

@ -15,6 +15,7 @@ import { SearchService, RequestService, RadarrService } from "../../services";
import { RequestServiceV2 } from "../../services/requestV2.service"; import { RequestServiceV2 } from "../../services/requestV2.service";
import { NewIssueComponent } from "./shared/new-issue/new-issue.component"; import { NewIssueComponent } from "./shared/new-issue/new-issue.component";
import { ArtistDetailsComponent } from "./artist/artist-details.component"; import { ArtistDetailsComponent } from "./artist/artist-details.component";
import { ArtistInformationPanel } from "./artist/panels/artist-information-panel/artist-information-panel.component";
export const components: any[] = [ export const components: any[] = [
MovieDetailsComponent, MovieDetailsComponent,
@ -32,6 +33,7 @@ export const components: any[] = [
MovieAdvancedOptionsComponent, MovieAdvancedOptionsComponent,
NewIssueComponent, NewIssueComponent,
ArtistDetailsComponent, ArtistDetailsComponent,
ArtistInformationPanel
]; ];
export const entryComponents: any[] = [ export const entryComponents: any[] = [

@ -13,19 +13,19 @@
<a *ngIf="hasTrailer" class="media-icons" (click)="openDialog()"><i <a *ngIf="hasTrailer" class="media-icons" (click)="openDialog()"><i
matTooltip="Trailer" class="fa fa-youtube-play fa-2x grow-social"></i></a> matTooltip="Trailer" class="fa fa-youtube-play fa-2x grow-social"></i></a>
<a *ngIf="imdbId" class="media-icons" href="https://imdb.com/title/{{imdbId}}" <a *ngIf="imdbId" class="media-icons" [href]="doNotAppend ? imdbid : 'https://imdb.com/title/' + imdbId"
target="_blank"> target="_blank">
<i matTooltip="Imdb" class="fa fa-imdb fa-2x grow-social"></i> <i matTooltip="Imdb" class="fa fa-imdb fa-2x grow-social"></i>
</a> </a>
<a *ngIf="twitter" class="media-icons" <a *ngIf="twitter" class="media-icons"
href="https://twitter.com/{{twitter}}" target="_blank"> [href]="doNotAppend ? twitter : 'https://twitter.com/' + twitter" target="_blank">
<i matTooltip="Twitter" class="fa fa-twitter fa-2x grow-social"></i> <i matTooltip="Twitter" class="fa fa-twitter fa-2x grow-social"></i>
</a> </a>
<a *ngIf="facebook" class="media-icons" <a *ngIf="facebook" class="media-icons"
href="https://facebook.com/{{facebook}}" target="_blank"> [href]="doNotAppend ? facebook : 'https://facebook.com/' + facebook" target="_blank">
<i matTooltip="Facebook" class="fa fa-facebook fa-2x grow-social"></i> <i matTooltip="Facebook" class="fa fa-facebook fa-2x grow-social"></i>
</a> <a *ngIf="instagram" class="media-icons" </a> <a *ngIf="instagram" class="media-icons"
href="https://instagram.com/{{instagram}}" target="_blank"> [href]="doNotAppend ? instagram : 'https://instagram.com/' + instagram" target="_blank">
<i matTooltip="Instagram" class="fa fa-instagram fa-2x grow-social"></i> <i matTooltip="Instagram" class="fa fa-instagram fa-2x grow-social"></i>
</a> </a>

@ -18,6 +18,7 @@ export class SocialIconsComponent {
@Input() available: boolean; @Input() available: boolean;
@Input() plexUrl: string; @Input() plexUrl: string;
@Input() embyUrl: string; @Input() embyUrl: string;
@Input() doNotAppend: boolean;
@Output() openTrailer: EventEmitter<any> = new EventEmitter(); @Output() openTrailer: EventEmitter<any> = new EventEmitter();

@ -1,4 +1,5 @@
import { Component, Inject, Input } from "@angular/core"; import { Component, Inject, Input } from "@angular/core";
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
@Component({ @Component({
selector: "top-banner", selector: "top-banner",
@ -12,4 +13,10 @@ export class TopBannerComponent {
@Input() available: boolean; @Input() available: boolean;
@Input() background: any; @Input() background: any;
constructor(private sanitizer:DomSanitizer){}
public getBackgroundImage(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(this.background);
}
} }

Loading…
Cancel
Save