!wip on adding music to v4

pull/3895/head
tidusjar 5 years ago
parent b5043d0580
commit ea24e33d31

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.MusicBrainz.Models.Lookup;
using Ombi.Api.MusicBrainz.Models.Search;
namespace Ombi.Api.MusicBrainz
{
public interface IMusicBrainzApi
{
Task<IEnumerable<Artist>> SearchArtist(string artistQuery);
Task<IEnumerable<ReleaseGroups>> GetReleaseGroups(string artistId);
}
}

@ -0,0 +1,27 @@
using Newtonsoft.Json;
namespace Ombi.Api.MusicBrainz.Models.Lookup
{
[JsonPluralName("release-groups")]
public class ReleaseGroups
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "primary-type-ids")]
public string PrimaryTypeId { get; set; }
[JsonProperty(PropertyName = "disambiguation")]
public string Disambiguation { get; set; }
[JsonProperty(PropertyName = "secondary-types")]
public string[] SecondaryTypes { get; set; }
[JsonProperty(PropertyName = "primary-type")]
public string PrimaryType { get; set; } // Album / Single / Live / EP
[JsonProperty(PropertyName = "first-release-date")]
public string FirstReleaseDate { get; set; }
[JsonProperty(PropertyName = "secondary-type-ids")]
public string[] SecondaryTypeIds { get; set; }
[JsonProperty(PropertyName = "title")]
public string Title { get; set; } // Release title
}
}

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Ombi.Api.MusicBrainz.Models
{
public class MusicBrainzResult<T>
{
public DateTime created { get; set; }
public int count { get; set; }
public int offset { get; set; }
[JsonPropertyNameBasedOnItemClass]
public List<T> Data { get; set; }
}
}

@ -0,0 +1,83 @@
using Newtonsoft.Json;
namespace Ombi.Api.MusicBrainz.Models.Search
{
[JsonPluralName("artists")]
public class Artist
{
public string id { get; set; }
public string type { get; set; }
[JsonProperty(PropertyName = "type-id")]
public string typeid { get; set; }
public int score { get; set; }
public string name { get; set; }
[JsonProperty(PropertyName = "sort-name")]
public string sortname { get; set; }
public string country { get; set; }
public Area area { get; set; }
[JsonProperty(PropertyName = "begin-area")]
public BeginArea beginarea { get; set; }
[JsonProperty(PropertyName = "life-span")]
public LifeSpan2 lifespan { get; set; }
public Tag[] tags { get; set; }
[JsonProperty(PropertyName = "isni-list")]
public IsniList[] isnilist { get; set; }
public string disambiguation { get; set; }
}
public class Area
{
public string id { get; set; }
public string type { get; set; }
[JsonProperty(PropertyName = "type-id")]
public string typeid { get; set; }
public string name { get; set; }
[JsonProperty(PropertyName = "sort-name")]
public string sortname { get; set; }
[JsonProperty(PropertyName = "life-span")]
public LifeSpan lifespan { get; set; }
}
public class LifeSpan
{
public object ended { get; set; }
}
public class BeginArea
{
public string id { get; set; }
public string type { get; set; }
[JsonProperty(PropertyName = "type-id")]
public string typeid { get; set; }
public string name { get; set; }
[JsonProperty(PropertyName = "sort-name")]
public string sortname { get; set; }
[JsonProperty(PropertyName = "life-span")]
public LifeSpan1 lifespan { get; set; }
}
public class LifeSpan1
{
public object ended { get; set; }
}
public class LifeSpan2
{
public string begin { get; set; }
public object ended { get; set; }
}
public class Tag
{
public int count { get; set; }
public string name { get; set; }
}
public class IsniList
{
public string isni { get; set; }
}
}

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Ombi.Api;
using Ombi.Api.MusicBrainz.Models;
using Ombi.Api.MusicBrainz.Models.Lookup;
using Ombi.Api.MusicBrainz.Models.Search;
namespace Ombi.Api.MusicBrainz
{
public class MusicBrainzApi : IMusicBrainzApi
{
public MusicBrainzApi(IApi api)
{
_api = api;
}
private readonly IApi _api;
private const string _baseUrl = "https://musicbrainz.org/ws/2/";
public async Task<IEnumerable<Artist>> SearchArtist(string artistQuery)
{
var request = new Request("artist", _baseUrl, HttpMethod.Get);
request.AddQueryString("query", artistQuery);
AddHeaders(request);
var albums = await _api.Request<MusicBrainzResult<Artist>>(request);
return albums.Data.Where(x => !x.type.Equals("Person", StringComparison.CurrentCultureIgnoreCase));
}
public async Task<IEnumerable<ReleaseGroups>> GetReleaseGroups(string artistId)
{
var request = new Request("release-group", _baseUrl, HttpMethod.Get);
request.AddQueryString("artist", artistId);
AddHeaders(request);
// The count properties for release groups is called releasegroupcount... Will sort this out if I need paging
var releases = await _api.Request<MusicBrainzResult<ReleaseGroups>>(request);
return releases.Data;
}
private void AddHeaders(Request req)
{
req.AddHeader("Accept", "application/json");
}
}
}

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
</ItemGroup>
</Project>

@ -27,7 +27,8 @@ namespace Ombi.Api
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new PluralPropertyContractResolver()
};
public async Task<T> Request<T>(Request request, CancellationToken cancellationToken = default(CancellationToken))

@ -0,0 +1,17 @@
using System;
namespace Ombi.Api
{
public class JsonPropertyNameBasedOnItemClassAttribute : Attribute
{
}
public class JsonPluralNameAttribute : Attribute
{
public string PluralName { get; set; }
public JsonPluralNameAttribute(string pluralName)
{
PluralName = pluralName;
}
}
}

@ -0,0 +1,33 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Reflection;
namespace Ombi.Api
{
public class PluralPropertyContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
if (prop.PropertyType.IsGenericType && member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
{
Type itemType = prop.PropertyType.GetGenericArguments()[0];
JsonPluralNameAttribute att = itemType.GetCustomAttribute<JsonPluralNameAttribute>();
prop.PropertyName = att != null ? att.PluralName : Pluralize(itemType.Name);
}
return prop;
}
protected string Pluralize(string name)
{
if (name.EndsWith("y") && !name.EndsWith("ay") && !name.EndsWith("ey") && !name.EndsWith("oy") && !name.EndsWith("uy"))
return name.Substring(0, name.Length - 1) + "ies";
if (name.EndsWith("s"))
return name + "es";
return name + "s";
}
}
}

@ -47,6 +47,7 @@ namespace Ombi.Api
{
_cache = cache;
_settings = s;
_runtimeVersion = AssemblyHelper.GetRuntimeVersion();
}
private static HttpClient _client;
@ -54,6 +55,7 @@ namespace Ombi.Api
private readonly ICacheService _cache;
private readonly ISettingsService<OmbiSettings> _settings;
private readonly string _runtimeVersion;
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
@ -84,7 +86,7 @@ namespace Ombi.Api
_handler = await GetHandler();
}
_client = new HttpClient(_handler);
_client.DefaultRequestHeaders.Add("User-Agent","Ombi");
_client.DefaultRequestHeaders.Add("User-Agent",$"Ombi/{_runtimeVersion} (https://ombi.io/)");
}
}

@ -64,6 +64,7 @@ using Ombi.Schedule.Jobs.SickRage;
using Ombi.Schedule.Processor;
using Ombi.Store.Entities;
using Quartz.Spi;
using Ombi.Api.MusicBrainz;
namespace Ombi.DependencyInjection
{
@ -145,6 +146,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IOneSignalApi, OneSignalApi>();
services.AddTransient<ILidarrApi, LidarrApi>();
services.AddTransient<IGroupMeApi, GroupMeApi>();
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
}
public static void RegisterStore(this IServiceCollection services) {

@ -24,6 +24,7 @@
<ProjectReference Include="..\Ombi.Api.GroupMe\Ombi.Api.GroupMe.csproj" />
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
<ProjectReference Include="..\Ombi.Api.Mattermost\Ombi.Api.Mattermost.csproj" />
<ProjectReference Include="..\Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj" />
<ProjectReference Include="..\Ombi.Api.Notifications\Ombi.Api.Notifications.csproj" />
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />

@ -9,7 +9,7 @@ namespace Ombi.Helpers
var version = Assembly.GetEntryAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
.InformationalVersion;
return version.Equals("1.0.0") ? "3.0.0-develop" : version;
return version.Equals("1.0.0") ? "4.0.0-develop" : version;
}
}
}

@ -106,7 +106,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Test.Common", "Ombi.Te
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Hubs", "Ombi.Hubs\Ombi.Hubs.csproj", "{67416CC5-13B2-44BB-98CE-39DA93D6F70E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.GroupMe", "Ombi.Api.GroupMe\Ombi.Api.GroupMe.csproj", "{9266403C-B04D-4C0F-AC39-82F12C781949}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.GroupMe", "Ombi.Api.GroupMe\Ombi.Api.GroupMe.csproj", "{9266403C-B04D-4C0F-AC39-82F12C781949}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.MusicBrainz", "Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj", "{C5C1769B-4197-4410-A160-0EEF39EDDC98}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -286,6 +288,10 @@ Global
{9266403C-B04D-4C0F-AC39-82F12C781949}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9266403C-B04D-4C0F-AC39-82F12C781949}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9266403C-B04D-4C0F-AC39-82F12C781949}.Release|Any CPU.Build.0 = Release|Any CPU
{C5C1769B-4197-4410-A160-0EEF39EDDC98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5C1769B-4197-4410-A160-0EEF39EDDC98}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5C1769B-4197-4410-A160-0EEF39EDDC98}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5C1769B-4197-4410-A160-0EEF39EDDC98}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -327,6 +333,7 @@ Global
{F3969B69-3B07-4884-A7AB-0BAB8B84DF94} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
{27111E7C-748E-4996-BD71-2117027C6460} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
{9266403C-B04D-4C0F-AC39-82F12C781949} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{C5C1769B-4197-4410-A160-0EEF39EDDC98} = {9293CA11-360A-4C20-A674-B9E794431BF5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869}

@ -186,6 +186,7 @@ export interface IAbout {
osDescription: string;
processArchitecture: string;
applicationBasePath: string;
notSupported: boolean;
}
export interface ICouchPotatoSettings extends IExternalSettings {

@ -0,0 +1,211 @@
<div *ngIf="!movie" class="justify-content-md-center top-spacing loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
<div *ngIf="movie" class="dark-theme">
<top-banner [background]="movie.background" [available]="movie.available" [title]="movie.title"
[releaseDate]="movie.releaseDate" [tagline]="movie.tagline"></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>
<!--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>
</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">
<span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
<ng-template #requestedBtn>
<button mat-raised-button *ngIf="!hasRequest || hasRequest && movieRequest && !movieRequest.denied"
class="btn-spacing" color="warn" [disabled]><i class="fa fa-check"></i>
{{ 'Common.Requested' | translate }}</button>
</ng-template>
<ng-template #notRequestedBtn>
<button mat-raised-button class="btn-spacing" color="primary" (click)="request()">
<i *ngIf="movie.requestProcessing" class="fa fa-circle-o-notch fa-spin fa-fw"></i> <i
*ngIf="!movie.requestProcessing && !movie.processed" class="fa fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing" class="fa fa-check"></i> {{
'Common.Request' | translate }}</button>
</ng-template>
</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>
<button *ngIf="!movie.available" (click)="markAvailable()" mat-raised-button class="btn-spacing"
color="accent">
<i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
</button>
<button *ngIf="movieRequest && !movieRequest.denied" mat-raised-button class="btn-spacing" color="warn"
(click)="deny()">
<i class="fa fa-times"></i> {{
'Requests.Deny' | translate }}</button>
<button *ngIf="movieRequest && movieRequest.denied" [matTooltip]="movieRequest.deniedReason"
mat-raised-button class="btn-spacing" color="warn">
<i class="fa fa-times"></i> {{
'MediaDetails.Denied' | translate }}</button>
</span>
<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>
</div>
</div>
<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>
</mat-card-content>
</mat-card>
</div>
<div class="col-12 col-md-10">
<div class="row">
<div class="col-12">
<mat-card class=" mat-elevation-z8 spacing-below">
<mat-card-content>
{{movie.overview}}
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row">
<div class="col-12">
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
</div>
</div>
<div class="row">
<div class="col-12">
<mat-accordion class="mat-elevation-z8 spacing-below">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.RecommendationsTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer" *ngIf="movie.recommendations.results.length > 0">
<div class="col-md-2" *ngFor="let r of movie.recommendations.results">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<a [routerLink]="'/details/movie/'+r.id">
<img class="real grow" matTooltip="{{r.title}}"
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}" alt="Poster"
style="display: block;">
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.SimilarTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer" *ngIf="movie.similar.results.length > 0">
<div class="col-md-2" *ngFor="let r of movie.similar.results">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster ">
<a [routerLink]="'/details/movie/'+r.id">
<img class="real grow" matTooltip="{{r.title}}"
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}" alt="Poster"
style="display: block;">
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.VideosTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer" *ngIf="movie.videos.results.length > 0">
<div class="col-md-6" *ngFor="let video of movie.videos.results">
<iframe width="100%" height="315px" [src]="'https://www.youtube.com/embed/' + video.key | safe"
frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
</div>
</div>
</div>
<div class="bottom-page-gap">
</div>
</section>
</div>

@ -0,0 +1,115 @@
import { Component, ViewEncapsulation } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, MessageService } from "../../../services";
import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";
import { MatDialog } from "@angular/material";
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
import { AuthService } from "../../../auth/auth.service";
import { IMovieRequests, RequestType, IAdvancedData } from "../../../interfaces";
import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component";
import { NewIssueComponent } from "../shared/new-issue/new-issue.component";
@Component({
templateUrl: "./artist-details.component.html",
styleUrls: ["../../media-details.component.scss"],
})
export class ArtistDetailsComponent {
public movie: ISearchMovieResultV2;
public hasRequest: boolean;
public movieRequest: IMovieRequests;
public isAdmin: boolean;
public advancedOptions: IAdvancedData;
private theMovidDbId: number;
constructor(private searchService: SearchV2Service, private route: ActivatedRoute,
private sanitizer: DomSanitizer, private imageService: ImageService,
public dialog: MatDialog, private requestService: RequestService,
public messageService: MessageService, private auth: AuthService) {
this.route.params.subscribe((params: any) => {
this.theMovidDbId = params.movieDbId;
this.load();
});
}
public load() {
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => {
this.movie = x;
if (this.movie.requestId > 0) {
// Load up this request
this.hasRequest = true;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
}
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => {
this.movie.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")");
});
});
}
public async request() {
const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null }).toPromise();
if (result.result) {
this.movie.requested = true;
this.messageService.send(result.message, "Ok");
} else {
this.messageService.send(result.errorMessage, "Ok");
}
}
public openDialog() {
this.dialog.open(YoutubeTrailerComponent, {
width: '560px',
data: this.movie.videos.results[0].key
});
}
public async deny() {
const dialogRef = this.dialog.open(DenyDialogComponent, {
width: '250px',
data: {requestId: this.movieRequest.id, requestType: RequestType.movie}
});
dialogRef.afterClosed().subscribe(result => {
this.movieRequest.denied = result;
if(this.movieRequest.denied) {
this.movie.approved = false;
}
});
}
public async issue() {
const dialogRef = this.dialog.open(NewIssueComponent, {
width: '500px',
data: {requestId: this.movieRequest ? this.movieRequest.id : null, requestType: RequestType.movie, imdbid: this.movie.imdbId}
});
}
public async approve() {
const result = await this.requestService.approveMovie({ id: this.movieRequest.id }).toPromise();
if (result.result) {
this.movie.approved = false;
this.messageService.send("Successfully Approved", "Ok");
} else {
this.messageService.send(result.errorMessage, "Ok");
}
}
public async markAvailable() {
const result = await this.requestService.markMovieAvailable({id: this.movieRequest.id}).toPromise();
if (result.result) {
this.movie.available = true;
this.messageService.send(result.message, "Ok");
} else {
this.messageService.send(result.errorMessage, "Ok");
}
}
public setAdvancedOptions(data: any) {
this.advancedOptions = data;
}
}

@ -14,6 +14,7 @@ import { MovieAdvancedOptionsComponent } from "./movie/panels/movie-advanced-opt
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";
export const components: any[] = [
MovieDetailsComponent,
@ -30,6 +31,7 @@ export const components: any[] = [
MovieAdminPanelComponent,
MovieAdvancedOptionsComponent,
NewIssueComponent,
ArtistDetailsComponent,
];
export const entryComponents: any[] = [

@ -3,6 +3,9 @@
<legend>About</legend>
<div class="mat-table">
<div class="mat-row" style="background:red;" *ngIf="!notSupported">
<div class="mat-cell" style="text-align: center;"><b>NOT SUPPORTED OS. Please use the docker image available on the Container Station</b></div>
</div>
<div class="mat-row" *ngIf="connectedUsers">
<div class="mat-cell">Users Online</div>
<div class="mat-cell">{{connectedUsers.length}}</div>

@ -122,7 +122,8 @@ namespace Ombi.Controllers.V1
OsArchitecture = RuntimeInformation.OSArchitecture.ToString(),
OsDescription = RuntimeInformation.OSDescription,
ProcessArchitecture = RuntimeInformation.ProcessArchitecture.ToString(),
ApplicationBasePath =Directory.GetCurrentDirectory()
ApplicationBasePath = Directory.GetCurrentDirectory(),
NotSupported = Directory.GetCurrentDirectory().Contains("qpkg")
};
var version = AssemblyHelper.GetRuntimeVersion();

@ -5,7 +5,8 @@ using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using Ombi.Api.MusicBrainz;
using Ombi.Api.MusicBrainz.Models.Search;
using Ombi.Core;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Engine.V2;
@ -22,7 +23,7 @@ namespace Ombi.Controllers.V2
public class SearchController : ControllerBase
{
public SearchController(IMultiSearchEngine multiSearchEngine, ITvSearchEngine tvSearchEngine,
IMovieEngineV2 v2Movie, ITVSearchEngineV2 v2Tv)
IMovieEngineV2 v2Movie, ITVSearchEngineV2 v2Tv, IMusicBrainzApi musicApi)
{
_multiSearchEngine = multiSearchEngine;
_tvSearchEngine = tvSearchEngine;
@ -30,12 +31,14 @@ namespace Ombi.Controllers.V2
_movieEngineV2 = v2Movie;
_movieEngineV2.ResultLimit = 12;
_tvEngineV2 = v2Tv;
music = musicApi;
}
private readonly IMultiSearchEngine _multiSearchEngine;
private readonly IMovieEngineV2 _movieEngineV2;
private readonly ITVSearchEngineV2 _tvEngineV2;
private readonly ITvSearchEngine _tvSearchEngine;
private readonly IMusicBrainzApi music;
/// <summary>
/// Returns search results for both TV and Movies
@ -334,5 +337,15 @@ namespace Ombi.Controllers.V2
{
return await _movieEngineV2.GetMoviesByActor(actorId, null);
}
[HttpGet("artist/{name}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<Artist>> GetArtists(string name)
{
return await music.SearchArtist(name);
}
}
}

@ -9,5 +9,6 @@
public string OsDescription { get; set; }
public string ProcessArchitecture { get; set; }
public string ApplicationBasePath { get; set; }
public bool NotSupported { get; set; }
}
}
Loading…
Cancel
Save