mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 05:13:18 -07:00
Got the main artist page in
This commit is contained in:
parent
6c443469af
commit
e231d701cc
12 changed files with 190 additions and 45 deletions
|
@ -8,6 +8,8 @@ using Hqub.MusicBrainz.API.Entities;
|
|||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.MusicBrainz;
|
||||
using Ombi.Core.Engine.V2;
|
||||
using Ombi.Core.Models.Requests;
|
||||
|
@ -20,6 +22,7 @@ using Ombi.Settings.Settings.Models.External;
|
|||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Test.Common;
|
||||
using Artist = Hqub.MusicBrainz.API.Entities.Artist;
|
||||
|
||||
namespace Ombi.Core.Tests.Engine.V2
|
||||
{
|
||||
|
@ -30,6 +33,8 @@ namespace Ombi.Core.Tests.Engine.V2
|
|||
private MusicSearchEngineV2 _engine;
|
||||
|
||||
private Mock<IMusicBrainzApi> _musicApi;
|
||||
private Mock<ILidarrApi> _lidarrApi;
|
||||
private Mock<ISettingsService<LidarrSettings>> _lidarrSettings;
|
||||
private Fixture F;
|
||||
|
||||
[SetUp]
|
||||
|
@ -48,10 +53,11 @@ namespace Ombi.Core.Tests.Engine.V2
|
|||
var ombiSettings = new Mock<ISettingsService<OmbiSettings>>();
|
||||
var requestSub = new Mock<IRepository<RequestSubscription>>();
|
||||
_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,
|
||||
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("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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Hqub.MusicBrainz.API.Entities;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.MusicBrainz;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
|
@ -16,6 +18,7 @@ using Ombi.Settings.Settings.Models;
|
|||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Artist = Hqub.MusicBrainz.API.Entities.Artist;
|
||||
using ReleaseGroup = Ombi.Core.Models.Search.V2.Music.ReleaseGroup;
|
||||
|
||||
namespace Ombi.Core.Engine.V2
|
||||
|
@ -24,19 +27,28 @@ namespace Ombi.Core.Engine.V2
|
|||
{
|
||||
private readonly IMusicBrainzApi _musicBrainzApi;
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
|
||||
public MusicSearchEngineV2(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules,
|
||||
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)
|
||||
{
|
||||
_musicBrainzApi = musicBrainzApi;
|
||||
_lidarrSettings = lidarrSettings;
|
||||
_lidarrApi = lidarrApi;
|
||||
}
|
||||
|
||||
public async Task<ArtistInformation> GetArtistInformation(string 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
|
||||
{
|
||||
|
@ -65,6 +77,17 @@ namespace Ombi.Core.Engine.V2
|
|||
|
||||
info.Links = GetLinksForArtist(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;
|
||||
}
|
||||
|
||||
|
@ -166,5 +189,11 @@ namespace Ombi.Core.Engine.V2
|
|||
|
||||
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 Region { 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 ArtistLinks Links { get; set; }
|
||||
public List<BandMember> Members { get; set; }
|
||||
|
|
|
@ -7,9 +7,16 @@ export interface IArtistSearchResult {
|
|||
country: string;
|
||||
region: string;
|
||||
disambiguation: string;
|
||||
banner: string;
|
||||
logo: string;
|
||||
poster: string;
|
||||
fanArt: string;
|
||||
releaseGroups: IReleaseGroups[];
|
||||
links: IArtistLinks;
|
||||
members: IBandMembers[];
|
||||
overview: string;
|
||||
|
||||
background: any;
|
||||
}
|
||||
|
||||
export interface IReleaseGroups {
|
||||
|
|
|
@ -4,31 +4,33 @@
|
|||
|
||||
<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">
|
||||
<div class="small-middle-container">
|
||||
|
||||
<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-->
|
||||
<div class="col-12 col-lg-3 col-xl-3 media-row">
|
||||
|
||||
<social-icons [homepage]="movie.homepage" [theMoviedbId]="movie.id"
|
||||
[hasTrailer]="movie.videos.results.length > 0" (openTrailer)="openDialog()" [imdbId]="movie.imdbId"
|
||||
[twitter]="movie.externalIds.twitterId" [facebook]="movie.externalIds.facebookId"
|
||||
[instagram]="movie.externalIds.instagramId" [available]="movie.available" [plexUrl]="movie.plexUrl"
|
||||
[embyUrl]="movie.embyUrl"></social-icons>
|
||||
<social-icons
|
||||
[homepage]="artist.links.homePage"
|
||||
[doNotAppend]="true"
|
||||
[imdbId]="artist.links.imdb"
|
||||
[twitter]="artist.links.twitter"
|
||||
[facebook]="artist.links.facebook"
|
||||
[instagram]="artist.links.instagram"></social-icons>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 col-xl-6 media-row">
|
||||
|
||||
<button mat-raised-button class="btn-green btn-spacing" *ngIf="movie.available"> {{
|
||||
'Common.Available' | translate }}</button>
|
||||
<span *ngIf="!movie.available">
|
||||
<!-- <button mat-raised-button class="btn-green btn-spacing" *ngIf="movie.available"> {{
|
||||
'Common.Available' | translate }}</button> -->
|
||||
<!-- <span *ngIf="!movie.available">
|
||||
<span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
|
||||
|
||||
<ng-template #requestedBtn>
|
||||
|
@ -43,8 +45,8 @@
|
|||
<i *ngIf="movie.processed && !movie.requestProcessing" class="fa fa-check"></i> {{
|
||||
'Common.Request' | translate }}</button>
|
||||
</ng-template>
|
||||
</span>
|
||||
<span *ngIf="isAdmin && hasRequest">
|
||||
</span> -->
|
||||
<!-- <span *ngIf="isAdmin && hasRequest">
|
||||
<button (click)="approve()" mat-raised-button class="btn-spacing" color="accent">
|
||||
<i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||
</button>
|
||||
|
@ -61,12 +63,12 @@
|
|||
mat-raised-button class="btn-spacing" color="warn">
|
||||
<i class="fa fa-times"></i> {{
|
||||
'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()">
|
||||
<i class="fa fa-exclamation"></i> {{
|
||||
'Requests.ReportIssue' | translate }}</button>
|
||||
'Requests.ReportIssue' | translate }}</button> -->
|
||||
|
||||
|
||||
|
||||
|
@ -77,20 +79,10 @@
|
|||
<div class="row">
|
||||
|
||||
<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-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>
|
||||
|
||||
|
@ -102,19 +94,19 @@
|
|||
<div class="col-12">
|
||||
<mat-card class=" mat-elevation-z8 spacing-below">
|
||||
<mat-card-content>
|
||||
{{movie.overview}}
|
||||
{{artist.overview}}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- <div class="row">
|
||||
<div class="col-12">
|
||||
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="row">
|
||||
<!-- <div class="row">
|
||||
<div class="col-12">
|
||||
<mat-accordion class="mat-elevation-z8 spacing-below">
|
||||
<mat-expansion-panel>
|
||||
|
@ -181,7 +173,7 @@
|
|||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import { IArtistSearchResult } from "../../../interfaces/IMusicSearchResultV2";
|
|||
export class ArtistDetailsComponent {
|
||||
private artistId: string;
|
||||
|
||||
public artist: IArtistSearchResult;
|
||||
public artist: IArtistSearchResult = null;
|
||||
|
||||
public isAdmin: boolean;
|
||||
|
||||
|
@ -35,6 +35,26 @@ export class ArtistDetailsComponent {
|
|||
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() {
|
||||
// const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null }).toPromise();
|
||||
// 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 { NewIssueComponent } from "./shared/new-issue/new-issue.component";
|
||||
import { ArtistDetailsComponent } from "./artist/artist-details.component";
|
||||
import { ArtistInformationPanel } from "./artist/panels/artist-information-panel/artist-information-panel.component";
|
||||
|
||||
export const components: any[] = [
|
||||
MovieDetailsComponent,
|
||||
|
@ -32,6 +33,7 @@ export const components: any[] = [
|
|||
MovieAdvancedOptionsComponent,
|
||||
NewIssueComponent,
|
||||
ArtistDetailsComponent,
|
||||
ArtistInformationPanel
|
||||
];
|
||||
|
||||
export const entryComponents: any[] = [
|
||||
|
|
|
@ -13,19 +13,19 @@
|
|||
<a *ngIf="hasTrailer" class="media-icons" (click)="openDialog()"><i
|
||||
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">
|
||||
<i matTooltip="Imdb" class="fa fa-imdb fa-2x grow-social"></i>
|
||||
</a>
|
||||
<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>
|
||||
</a>
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ export class SocialIconsComponent {
|
|||
@Input() available: boolean;
|
||||
@Input() plexUrl: string;
|
||||
@Input() embyUrl: string;
|
||||
@Input() doNotAppend: boolean;
|
||||
|
||||
@Output() openTrailer: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, Inject, Input } from "@angular/core";
|
||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
|
||||
|
||||
@Component({
|
||||
selector: "top-banner",
|
||||
|
@ -12,4 +13,10 @@ export class TopBannerComponent {
|
|||
@Input() available: boolean;
|
||||
@Input() background: any;
|
||||
|
||||
|
||||
constructor(private sanitizer:DomSanitizer){}
|
||||
|
||||
public getBackgroundImage(): SafeStyle {
|
||||
return this.sanitizer.bypassSecurityTrustStyle(this.background);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue