mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
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:
parent
2cfad10a82
commit
e5ef111087
15 changed files with 96 additions and 24 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
19
src/Ombi.Api.Emby/Models/PublicInfo.cs
Normal file
19
src/Ombi.Api.Emby/Models/PublicInfo.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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[];
|
||||||
|
|
|
@ -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});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 = [];
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue