got the view albums button working !wip

This commit is contained in:
Jamie 2018-08-26 10:10:58 +01:00
parent 3750243f11
commit 5df232f3f5
14 changed files with 151 additions and 94 deletions

View file

@ -12,9 +12,9 @@ namespace Ombi.Api.Lidarr
Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl); Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl); Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl);
Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl); Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl);
Task<AlbumByArtistResponse> GetAlbumsByArtist(int artistId, string apiKey, string baseUrl); Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId);
Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl); Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl);
Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl); Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl);
Task<List<AlbumByArtistResponse>> GetAllAlbums(string apiKey, string baseUrl); Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl);
} }
} }

View file

@ -82,12 +82,10 @@ namespace Ombi.Api.Lidarr
return albums.FirstOrDefault(); return albums.FirstOrDefault();
} }
public Task<AlbumByArtistResponse> GetAlbumsByArtist(int artistId, string apiKey, string baseUrl) public Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId)
{ {
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get); var request = new Request(string.Empty, $"https://api.lidarr.audio/api/v0.3/artist/{foreignArtistId}",
HttpMethod.Get) {IgnoreBaseUrlAppend = true};
request.AddQueryString("artistId", artistId.ToString());
AddHeaders(request, apiKey);
return Api.Request<AlbumByArtistResponse>(request); return Api.Request<AlbumByArtistResponse>(request);
} }
@ -99,12 +97,12 @@ namespace Ombi.Api.Lidarr
return Api.Request<List<ArtistResult>>(request); return Api.Request<List<ArtistResult>>(request);
} }
public Task<List<AlbumByArtistResponse>> GetAllAlbums(string apiKey, string baseUrl) public Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl)
{ {
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get); var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey); AddHeaders(request, apiKey);
return Api.Request<List<AlbumByArtistResponse>>(request); return Api.Request<List<AlbumResponse>>(request);
} }
private void AddHeaders(Request request, string key) private void AddHeaders(Request request, string key)

View file

@ -1,27 +1,34 @@
using System; namespace Ombi.Api.Lidarr.Models
namespace Ombi.Api.Lidarr.Models
{ {
public class AlbumByArtistResponse public class AlbumByArtistResponse
{ {
public string title { get; set; } public Album[] Albums { get; set; }
public string disambiguation { get; set; } public string ArtistName { get; set; }
public int artistId { get; set; } public string Disambiguation { get; set; }
public string foreignAlbumId { get; set; } public string Id { get; set; }
public bool monitored { get; set; } public Image[] Images { get; set; }
public int profileId { get; set; } public Link[] Links { get; set; }
public int duration { get; set; } public string Overview { get; set; }
public string albumType { get; set; } public Rating Rating { get; set; }
public object[] secondaryTypes { get; set; } public string SortName { get; set; }
public int mediumCount { get; set; } public string Status { get; set; }
public Ratings ratings { get; set; } public string Type { get; set; }
public DateTime releaseDate { get; set; } }
public Currentrelease currentRelease { get; set; }
public Release[] releases { get; set; } public class Rating
public object[] genres { get; set; } {
public Medium[] media { get; set; } public int Count { get; set; }
public Image[] images { get; set; } public decimal Value { get; set; }
public Statistics statistics { get; set; } }
public int id { get; set; }
public class Album
{
public string Disambiguation { get; set; }
public string Id { get; set; }
public string ReleaseDate { get; set; }
public string[] ReleaseStatuses { get; set; }
public string[] SecondaryTypes { get; set; }
public string Title { get; set; }
public string Type { get; set; }
} }
} }

View file

@ -0,0 +1,27 @@
using System;
namespace Ombi.Api.Lidarr.Models
{
public class AlbumResponse
{
public string title { get; set; }
public string disambiguation { get; set; }
public int artistId { get; set; }
public string foreignAlbumId { get; set; }
public bool monitored { get; set; }
public int profileId { get; set; }
public int duration { get; set; }
public string albumType { get; set; }
public object[] secondaryTypes { get; set; }
public int mediumCount { get; set; }
public Ratings ratings { get; set; }
public DateTime releaseDate { get; set; }
public Currentrelease currentRelease { get; set; }
public Release[] releases { get; set; }
public object[] genres { get; set; }
public Medium[] media { get; set; }
public Image[] images { get; set; }
public Statistics statistics { get; set; }
public int id { get; set; }
}
}

View file

@ -28,6 +28,7 @@ namespace Ombi.Api
public bool IgnoreErrors { get; set; } public bool IgnoreErrors { get; set; }
public bool Retry { get; set; } public bool Retry { get; set; }
public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>(); public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>();
public bool IgnoreBaseUrlAppend { get; set; }
public Action<string> OnBeforeDeserialization { get; set; } public Action<string> OnBeforeDeserialization { get; set; }
@ -38,7 +39,7 @@ namespace Ombi.Api
var sb = new StringBuilder(); var sb = new StringBuilder();
if (!string.IsNullOrEmpty(BaseUrl)) if (!string.IsNullOrEmpty(BaseUrl))
{ {
sb.Append(!BaseUrl.EndsWith("/") ? string.Format("{0}/", BaseUrl) : BaseUrl); sb.Append(!BaseUrl.EndsWith("/") && !IgnoreBaseUrlAppend ? string.Format("{0}/", BaseUrl) : BaseUrl);
} }
sb.Append(Endpoint.StartsWith("/") ? Endpoint.Remove(0, 1) : Endpoint); sb.Append(Endpoint.StartsWith("/") ? Endpoint.Remove(0, 1) : Endpoint);
return sb.ToString(); return sb.ToString();

View file

@ -19,12 +19,14 @@ namespace Ombi.Core.Tests.Rule.Search
MovieMock = new Mock<IMovieRequestRepository>(); MovieMock = new Mock<IMovieRequestRepository>();
TvMock = new Mock<ITvRequestRepository>(); TvMock = new Mock<ITvRequestRepository>();
Rule = new ExistingRule(MovieMock.Object, TvMock.Object); MusicMock = new Mock<IMusicRequestRepository>();
Rule = new ExistingRule(MovieMock.Object, TvMock.Object, MusicMock.Object);
} }
private ExistingRule Rule { get; set; } private ExistingRule Rule { get; set; }
private Mock<IMovieRequestRepository> MovieMock { get; set; } private Mock<IMovieRequestRepository> MovieMock { get; set; }
private Mock<ITvRequestRepository> TvMock { get; set; } private Mock<ITvRequestRepository> TvMock { get; set; }
private Mock<IMusicRequestRepository> MusicMock { get; set; }
[Test] [Test]

View file

@ -9,7 +9,7 @@ namespace Ombi.Core.Engine
{ {
Task<ArtistResult> GetAlbumArtist(string foreignArtistId); Task<ArtistResult> GetAlbumArtist(string foreignArtistId);
Task<ArtistResult> GetArtist(int artistId); Task<ArtistResult> GetArtist(int artistId);
Task<ArtistResult> GetArtistAlbums(string foreignArtistId); Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId);
Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search); Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search);
Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search); Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search);
} }

View file

@ -82,12 +82,21 @@ namespace Ombi.Core.Engine
/// <summary> /// <summary>
/// Returns all albums by the specified artist /// Returns all albums by the specified artist
/// </summary> /// </summary>
/// <param name="artistId"></param> /// <param name="foreignArtistId"></param>
/// <returns></returns> /// <returns></returns>
public async Task<ArtistResult> GetArtistAlbums(string foreignArtistId) public async Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId)
{ {
var settings = await GetSettings(); var settings = await GetSettings();
return await _lidarrApi.GetArtistByForeignId(foreignArtistId, settings.ApiKey, settings.FullUri); var result = await _lidarrApi.GetAlbumsByArtist(foreignArtistId);
// We do not want any Singles (This will include EP's)
var albumsOnly =
result.Albums.Where(x => !x.Type.Equals("Single", StringComparison.InvariantCultureIgnoreCase));
var vm = new List<SearchAlbumViewModel>();
foreach (var album in albumsOnly)
{
vm.Add(await MapIntoAlbumVm(album, result.Id, result.ArtistName, settings));
}
return vm;
} }
/// <summary> /// <summary>
@ -147,6 +156,7 @@ namespace Ombi.Core.Engine
}; };
if (vm.Monitored) if (vm.Monitored)
{ {
//TODO THEY HAVE FIXED THIS IN DEV
// The JSON is different for some stupid reason // The JSON is different for some stupid reason
// Need to lookup the artist now and all the images -.-" // Need to lookup the artist now and all the images -.-"
var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri); var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri);
@ -171,6 +181,35 @@ namespace Ombi.Core.Engine
return vm; return vm;
} }
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(Album a, string artistId, string artistName, LidarrSettings settings)
{
var fullAlbum = await _lidarrApi.GetAlbumByForeignId(a.Id, settings.ApiKey, settings.FullUri);
var vm = new SearchAlbumViewModel
{
ForeignAlbumId = a.Id,
Monitored = fullAlbum.monitored,
Rating = fullAlbum.ratings?.value ?? 0m,
ReleaseDate = fullAlbum.releaseDate,
Title = a.Title,
Disk = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
ForeignArtistId = artistId,
ArtistName = artistName,
Cover = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url
};
if (vm.Cover.IsNullOrEmpty())
{
vm.Cover = fullAlbum.remoteCover;
}
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
await RunSearchRules(vm);
return vm;
}
private LidarrSettings _settings; private LidarrSettings _settings;
private async Task<LidarrSettings> GetSettings() private async Task<LidarrSettings> GetSettings()
{ {

View file

@ -51,7 +51,7 @@
<a *ngIf="result.showSubscribe && result.subscribed" style="color:red" (click)="unSubscribe(result)" pTooltip="Unsubscribe notification"> <i class="fa fa-rss"></i></a> --> <a *ngIf="result.showSubscribe && result.subscribed" style="color:red" (click)="unSubscribe(result)" pTooltip="Unsubscribe notification"> <i class="fa fa-rss"></i></a> -->
</div> </div>
</div> </div>
<button style="text-align: right" class="btn btn-info-outline"> <button style="text-align: right" class="btn btn-info-outline" (click)="viewAllAlbums()">
<i class="fa fa-eye"></i> View Albums</button> <i class="fa fa-eye"></i> View Albums</button>
</div> </div>

View file

@ -1,10 +1,7 @@
import { Component, Input } from "@angular/core"; import { Component, EventEmitter, Input, Output } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { AuthService } from "../../auth/auth.service"; import { ISearchAlbumResult, ISearchArtistResult } from "../../interfaces/ISearchMusicResult";
import { IRequestEngineResult, ISearchMovieResult } from "../../interfaces"; import { SearchService } from "../../services";
import { ISearchArtistResult } from "../../interfaces/ISearchMusicResult";
import { NotificationService, RequestService } from "../../services";
@Component({ @Component({
selector: "artist-search", selector: "artist-search",
@ -13,51 +10,16 @@ import { NotificationService, RequestService } from "../../services";
export class ArtistSearchComponent { export class ArtistSearchComponent {
@Input() public result: ISearchArtistResult; @Input() public result: ISearchArtistResult;
public engineResult: IRequestEngineResult;
@Input() public defaultPoster: string; @Input() public defaultPoster: string;
constructor( @Output() public viewAlbumsResult = new EventEmitter<ISearchAlbumResult[]>();
private requestService: RequestService,
private notificationService: NotificationService, private authService: AuthService, constructor(private searchService: SearchService) {
private readonly translate: TranslateService) {
} }
public request(searchResult: ISearchMovieResult) { public viewAllAlbums() {
searchResult.requested = true; this.searchService.getAlbumsForArtist(this.result.forignArtistId).subscribe(x => {
searchResult.requestProcessing = true; this.viewAlbumsResult.emit(x);
searchResult.showSubscribe = false;
if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) {
searchResult.approved = true;
}
try {
this.requestService.requestMovie({ theMovieDbId: searchResult.id })
.subscribe(x => {
this.engineResult = x;
if (this.engineResult.result) {
this.translate.get("Search.RequestAdded", { title: searchResult.title }).subscribe(x => {
this.notificationService.success(x);
searchResult.processed = true;
}); });
} else {
if (this.engineResult.errorMessage && this.engineResult.message) {
this.notificationService.warning("Request Added", `${this.engineResult.message} - ${this.engineResult.errorMessage}`);
} else {
this.notificationService.warning("Request Added", this.engineResult.message ? this.engineResult.message : this.engineResult.errorMessage);
}
searchResult.requested = false;
searchResult.approved = false;
searchResult.processed = false;
searchResult.requestProcessing = false;
}
});
} catch (e) {
searchResult.processed = false;
searchResult.requestProcessing = false;
this.notificationService.error(e);
}
} }
} }

View file

@ -8,10 +8,10 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="radio"> <div class="radio">
<input type="radio" id="Artist" name="Mode" [checked]="!searchAlbum" (click)="searchMode(false)">
<label for="Artist">Artist Search</label>
<input type="radio" id="Album" name="Mode" [checked]="searchAlbum" (click)="searchMode(true)"> <input type="radio" id="Album" name="Mode" [checked]="searchAlbum" (click)="searchMode(true)">
<label for="Album">Album Search</label> <label for="Album">Album Search</label>
<input type="radio" id="Artist" name="Mode" [checked]="!searchAlbum" (click)="searchMode(false)">
<label for="Artist">Artist Search</label>
</div> </div>
</div> </div>
@ -29,7 +29,7 @@
</div> </div>
<div *ngFor="let result of artistResult"> <div *ngFor="let result of artistResult">
<artist-search [result]="result" [defaultPoster]="defaultPoster"></artist-search> <artist-search [result]="result" [defaultPoster]="defaultPoster" (viewAlbumsResult)="viewAlbumsForArtist($event)"></artist-search>
<br/> <br/>
<br/> <br/>
</div> </div>

View file

@ -22,7 +22,7 @@ export class MusicSearchComponent implements OnInit {
public albumResult: ISearchAlbumResult[]; public albumResult: ISearchAlbumResult[];
public result: IRequestEngineResult; public result: IRequestEngineResult;
public searchApplied = false; public searchApplied = false;
public searchAlbum: boolean = false; public searchAlbum: boolean = true;
@Input() public issueCategories: IIssueCategory[]; @Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean; @Input() public issuesEnabled: boolean;
@ -148,6 +148,13 @@ export class MusicSearchComponent implements OnInit {
} }
} }
public viewAlbumsForArtist(albums: ISearchAlbumResult[]) {
this.clearArtistResults();
this.searchAlbum = true;
this.albumResult = albums;
this.setAlbumBackground();
}
private clearArtistResults() { private clearArtistResults() {
this.artistResult = []; this.artistResult = [];
this.searchApplied = false; this.searchApplied = false;

View file

@ -7,7 +7,7 @@ import { Observable } from "rxjs";
import { TreeNode } from "primeng/primeng"; import { TreeNode } from "primeng/primeng";
import { ISearchMovieResult } from "../interfaces"; import { ISearchMovieResult } from "../interfaces";
import { ISearchTvResult } from "../interfaces"; import { ISearchTvResult } from "../interfaces";
import { ISearchArtistResult } from "../interfaces/ISearchMusicResult"; import { ISearchAlbumResult, ISearchArtistResult } from "../interfaces/ISearchMusicResult";
import { ServiceHelpers } from "./service.helpers"; import { ServiceHelpers } from "./service.helpers";
@Injectable() @Injectable()
@ -73,7 +73,10 @@ export class SearchService extends ServiceHelpers {
public searchArtist(searchTerm: string): Observable<ISearchArtistResult[]> { public searchArtist(searchTerm: string): Observable<ISearchArtistResult[]> {
return this.http.get<ISearchArtistResult[]>(`${this.url}/Music/Artist/` + searchTerm); return this.http.get<ISearchArtistResult[]>(`${this.url}/Music/Artist/` + searchTerm);
} }
public searchAlbum(searchTerm: string): Observable<any> { public searchAlbum(searchTerm: string): Observable<ISearchAlbumResult[]> {
return this.http.get<any>(`${this.url}/Music/Album/` + searchTerm); return this.http.get<ISearchAlbumResult[]>(`${this.url}/Music/Album/` + searchTerm);
}
public getAlbumsForArtist(foreignArtistId: string): Observable<ISearchAlbumResult[]> {
return this.http.get<ISearchAlbumResult[]>(`${this.url}/Music/Artist/Album/${foreignArtistId}`);
} }
} }

View file

@ -206,5 +206,16 @@ namespace Ombi.Controllers
{ {
return await MusicEngine.SearchAlbum(searchTerm); return await MusicEngine.SearchAlbum(searchTerm);
} }
/// <summary>
/// Returns all albums for the artist using the ForeignArtistId
/// </summary>
/// <remarks>We use Lidarr as the Provider</remarks>
/// <returns></returns>
[HttpGet("music/artist/album/{foreignArtistId}")]
public async Task<IEnumerable<SearchAlbumViewModel>> GetAlbumsByArtist(string foreignArtistId)
{
return await MusicEngine.GetArtistAlbums(foreignArtistId);
}
} }
} }