Added better support for Jellyfin, we will now auto detect if it's a jellyfin server after pressing the discover button

This commit is contained in:
tidusjar 2019-10-12 21:29:02 +01:00
commit e5ef111087
15 changed files with 96 additions and 24 deletions

View file

@ -46,6 +46,17 @@ namespace Ombi.Api.Emby
return obj; return obj;
} }
public async Task<PublicInfo> GetPublicInformation(string baseUrl)
{
var request = new Request("emby/System/Info/public", baseUrl, HttpMethod.Get);
AddHeaders(request, string.Empty);
var obj = await Api.Request<PublicInfo>(request);
return obj;
}
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri) public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
{ {
var request = new Request("emby/users/authenticatebyname", baseUri, HttpMethod.Post); var request = new Request("emby/users/authenticatebyname", baseUri, HttpMethod.Post);
@ -124,6 +135,7 @@ namespace Ombi.Api.Emby
{ {
return await GetInformation<MovieInformation>(mediaId, apiKey, userId, baseUrl); return await GetInformation<MovieInformation>(mediaId, apiKey, userId, baseUrl);
} }
public async Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl) public async Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl)
{ {
return await GetInformation<EpisodeInformation>(mediaId, apiKey, userId, baseUrl); return await GetInformation<EpisodeInformation>(mediaId, apiKey, userId, baseUrl);

View file

@ -29,5 +29,6 @@ namespace Ombi.Api.Emby
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl); Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl); Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<PublicInfo> GetPublicInformation(string baseUrl);
} }
} }

View file

@ -0,0 +1,19 @@
namespace Ombi.Api.Emby.Models
{
public class PublicInfo
{
public string LocalAddress { get; set; }
public string ServerName { get; set; }
public string Version { get; set; }
/// <summary>
/// Only populated for Jellyfin
/// </summary>
public string ProductName { get; set; }
public bool IsJellyfin => !string.IsNullOrEmpty(ProductName) && ProductName.Contains("Jellyfin");
public string OperatingSystem { get; set; }
public string Id { get; set; }
}
}

View file

@ -68,11 +68,11 @@ namespace Ombi.Core.Rule.Rules.Search
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null); var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
if ((server?.ServerHostname ?? string.Empty).HasValue()) if ((server?.ServerHostname ?? string.Empty).HasValue())
{ {
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerHostname); obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerHostname, s.IsJellyfin);
} }
else else
{ {
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId); obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, null, s.IsJellyfin);
} }
if (obj.Type == RequestType.TvShow) if (obj.Type == RequestType.TvShow)

View file

@ -1,21 +1,21 @@
using System; namespace Ombi.Helpers
using System.Globalization;
using System.Collections.Generic;
using System.Text;
namespace Ombi.Helpers
{ {
public class EmbyHelper public class EmbyHelper
{ {
public static string GetEmbyMediaUrl(string mediaId, string customerServerUrl = null) public static string GetEmbyMediaUrl(string mediaId, string customerServerUrl = null, bool isJellyfin = false)
{ {
string path = "item/item";
if (isJellyfin)
{
path = "itemdetails";
}
if (customerServerUrl.HasValue()) if (customerServerUrl.HasValue())
{ {
return $"{customerServerUrl}#!/item/item.html?id={mediaId}"; return $"{customerServerUrl}#!/{path}.html?id={mediaId}";
} }
else else
{ {
return $"https://app.emby.media/#!/item/item.html?id={mediaId}"; return $"https://app.emby.media/#!/{path}.html?id={mediaId}";
} }
} }
} }

View file

@ -45,11 +45,11 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
try try
{ {
await StartServerCache(server); await StartServerCache(server, embySettings);
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name); _logger.LogError(e, "Exception when caching {1} for server {0}", server.Name, embySettings.IsJellyfin ? "Jellyfin" : "Emby");
} }
} }
@ -60,7 +60,7 @@ namespace Ombi.Schedule.Jobs.Emby
} }
private async Task StartServerCache(EmbyServers server) private async Task StartServerCache(EmbyServers server, EmbySettings settings)
{ {
if (!ValidateSettings(server)) if (!ValidateSettings(server))
return; return;
@ -135,7 +135,7 @@ namespace Ombi.Schedule.Jobs.Emby
Title = tvShow.Name, Title = tvShow.Name,
Type = EmbyMediaType.Series, Type = EmbyMediaType.Series,
EmbyId = tvShow.Id, EmbyId = tvShow.Id,
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server.ServerHostname), Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server.ServerHostname, settings.IsJellyfin),
AddedAt = DateTime.UtcNow AddedAt = DateTime.UtcNow
}); });
} }

View file

@ -6,6 +6,7 @@ namespace Ombi.Core.Settings.Models.External
public sealed class EmbySettings : Ombi.Settings.Settings.Models.Settings public sealed class EmbySettings : Ombi.Settings.Settings.Models.Settings
{ {
public bool Enable { get; set; } public bool Enable { get; set; }
public bool IsJellyfin { get; set; }
public List<EmbyServers> Servers { get; set; } = new List<EmbyServers>(); public List<EmbyServers> Servers { get; set; } = new List<EmbyServers>();
} }

View file

@ -33,6 +33,7 @@ export interface IUpdateSettings extends ISettings {
export interface IEmbySettings extends ISettings { export interface IEmbySettings extends ISettings {
enable: boolean; enable: boolean;
isJellyfin: boolean;
servers: IEmbyServer[]; servers: IEmbyServer[];
} }
@ -44,6 +45,11 @@ export interface IEmbyServer extends IExternalSettings {
serverHostname: string; serverHostname: string;
} }
export interface IPublicInfo {
serverName: string;
isJellyfin: boolean;
}
export interface IPlexSettings extends ISettings { export interface IPlexSettings extends ISettings {
enable: boolean; enable: boolean;
servers: IPlexServer[]; servers: IPlexServer[];

View file

@ -5,7 +5,7 @@ import { Observable } from "rxjs";
import { ServiceHelpers } from "../service.helpers"; import { ServiceHelpers } from "../service.helpers";
import { IEmbySettings, IUsersModel } from "../../interfaces"; import { IEmbyServer, IEmbySettings, IPublicInfo, IUsersModel } from "../../interfaces";
@Injectable() @Injectable()
export class EmbyService extends ServiceHelpers { export class EmbyService extends ServiceHelpers {
@ -16,8 +16,13 @@ export class EmbyService extends ServiceHelpers {
public logIn(settings: IEmbySettings): Observable<IEmbySettings> { public logIn(settings: IEmbySettings): Observable<IEmbySettings> {
return this.http.post<IEmbySettings>(`${this.url}`, JSON.stringify(settings), {headers: this.headers}); return this.http.post<IEmbySettings>(`${this.url}`, JSON.stringify(settings), {headers: this.headers});
} }
public getUsers(): Observable<IUsersModel[]> { public getUsers(): Observable<IUsersModel[]> {
return this.http.get<IUsersModel[]>(`${this.url}users`, {headers: this.headers}); return this.http.get<IUsersModel[]>(`${this.url}users`, {headers: this.headers});
} }
public getPublicInfo(server: IEmbyServer): Observable<IPublicInfo> {
return this.http.post<IPublicInfo>(`${this.url}info`, JSON.stringify(server), {headers: this.headers});
}
} }

View file

@ -3,7 +3,7 @@
<div *ngIf="settings"> <div *ngIf="settings">
<fieldset> <fieldset>
<legend> <legend>
Emby Configuration Emby/Jellyfin Configuration
</legend> </legend>
<div class="row"> <div class="row">
@ -71,8 +71,8 @@
</label> </label>
<div> <div>
<input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="server.serverHostname" placeholder="e.g. https://jellyfin.server.com/" value="{{server.serverHostname}}"> <input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="server.serverHostname" placeholder="e.g. https://jellyfin.server.com/" value="{{server.serverHostname}}">
<small><span *ngIf="server.serverHostname">Current URL: "{{server.serverHostname}}/#!/item/item.html?id=1"</span> <small><span *ngIf="server.serverHostname">Current URL: "{{server.serverHostname}}/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1"</span>
<span *ngIf="!server.serverHostname">Current URL: "https://app.emby.media/#!/item/item.html?id=1</span></small> <span *ngIf="!server.serverHostname">Current URL: "https://app.emby.media/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1</span></small>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -80,6 +80,11 @@
<button id="testEmby" type="button" (click)="test(server)" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button> <button id="testEmby" type="button" (click)="test(server)" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button>
</div> </div>
</div> </div>
<div class="form-group">
<div>
<button id="discover" type="button" (click)="discoverServerInfo(server)" class="btn btn-primary-outline">Discover Server Information <div id="spinner"></div></button>
</div>
</div>
</div> </div>
</ng-template> </ng-template>
</ngb-tab> </ngb-tab>
@ -88,7 +93,7 @@
<div class="col-md-1"> <div class="col-md-1">
<div class="form-group"> <div class="form-group">
<div> <div>
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button> <button [disabled]="!hasDiscovered" (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,7 +1,7 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { IEmbyServer, IEmbySettings } from "../../interfaces"; import { IEmbyServer, IEmbySettings } from "../../interfaces";
import { JobService, NotificationService, SettingsService, TesterService } from "../../services"; import { EmbyService, JobService, NotificationService, SettingsService, TesterService } from "../../services";
@Component({ @Component({
templateUrl: "./emby.component.html", templateUrl: "./emby.component.html",
@ -9,16 +9,25 @@ import { JobService, NotificationService, SettingsService, TesterService } from
export class EmbyComponent implements OnInit { export class EmbyComponent implements OnInit {
public settings: IEmbySettings; public settings: IEmbySettings;
public hasDiscovered: boolean;
constructor(private settingsService: SettingsService, constructor(private settingsService: SettingsService,
private notificationService: NotificationService, private notificationService: NotificationService,
private testerService: TesterService, private testerService: TesterService,
private jobService: JobService) { } private jobService: JobService,
private embyService: EmbyService) { }
public ngOnInit() { public ngOnInit() {
this.settingsService.getEmby().subscribe(x => this.settings = x); this.settingsService.getEmby().subscribe(x => this.settings = x);
} }
public async discoverServerInfo(server: IEmbyServer) {
const result = await this.embyService.getPublicInfo(server).toPromise();
this.settings.isJellyfin = result.isJellyfin;
server.name = result.serverName;
this.hasDiscovered = true;
}
public addTab() { public addTab() {
if (this.settings.servers == null) { if (this.settings.servers == null) {
this.settings.servers = []; this.settings.servers = [];

View file

@ -22,7 +22,7 @@
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Plex']">Plex</a></li> <li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Plex']">Plex</a></li>
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Emby']">Emby</a></li> <li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Emby']">Emby/Jellyfin</a></li>
</ul> </ul>
</li> </li>

View file

@ -3,7 +3,7 @@
<div class="landing-block shadow"> <div class="landing-block shadow">
<div class="media"> <div class="media">
<div id="contentBody" class="media-body"> <div id="contentBody" class="media-body">
<h4 class="media-heading landing-title">Emby Authentication</h4> <h4 class="media-heading landing-title">Emby/Jellyfin Authentication</h4>
<div *ngIf="embySettings"> <div *ngIf="embySettings">
<div *ngIf="embySettings.servers"> <div *ngIf="embySettings.servers">
<div *ngFor="let server of embySettings.servers"> <div *ngFor="let server of embySettings.servers">
@ -26,6 +26,11 @@
<input type="checkbox" [(ngModel)]="server.ssl" id="Ssl" name="Ssl"><label for="Ssl">SSL</label> <input type="checkbox" [(ngModel)]="server.ssl" id="Ssl" name="Ssl"><label for="Ssl">SSL</label>
</div> </div>
</div> </div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" [(ngModel)]="embySettings.isJellyfin" id="isJellyfin" name="isJellyfin"><label for="isJellyfin">Jellyfin Install</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="username" class="control-label">Api Key</label> <label for="username" class="control-label">Api Key</label>
<div> <div>

View file

@ -28,6 +28,7 @@ export class EmbyComponent implements OnInit {
} }
this.embySettings = { this.embySettings = {
servers: [], servers: [],
isJellyfin: false,
id: 0, id: 0,
enable: true, enable: true,
}; };

View file

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Ombi.Api.Emby; using Ombi.Api.Emby;
using Ombi.Api.Emby.Models;
using Ombi.Api.Plex; using Ombi.Api.Plex;
using Ombi.Attributes; using Ombi.Attributes;
using Ombi.Core.Settings; using Ombi.Core.Settings;
@ -60,6 +61,13 @@ namespace Ombi.Controllers.External
return null; return null;
} }
[HttpPost("info")]
public async Task<PublicInfo> GetServerInfo([FromBody] EmbyServers server)
{
var result = await EmbyApi.GetPublicInformation(server.FullUri);
return result;
}
/// <summary> /// <summary>
/// Gets the emby users. /// Gets the emby users.
/// </summary> /// </summary>