mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -07:00
Merge pull request #3924 from joshuaboniface/jellyfin-redux
Add separate Jellyfin server type
This commit is contained in:
commit
14c6170455
138 changed files with 5341 additions and 228 deletions
|
@ -25,10 +25,6 @@ namespace Ombi.Api.Emby
|
|||
|
||||
public IEmbyApi CreateClient(EmbySettings settings)
|
||||
{
|
||||
if (settings.IsJellyfin)
|
||||
{
|
||||
return new JellyfinApi(_api);
|
||||
}
|
||||
return new EmbyApi(_api);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,8 @@
|
|||
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; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
33
src/Ombi.Api.Jellyfin/IBaseJellyfinApi.cs
Normal file
33
src/Ombi.Api.Jellyfin/IBaseJellyfinApi.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Jellyfin.Models.Media.Tv;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public interface IBaseJellyfinApi
|
||||
{
|
||||
Task<JellyfinSystemInfo> GetSystemInformation(string apiKey, string baseUrl);
|
||||
Task<List<JellyfinUser>> GetUsers(string baseUri, string apiKey);
|
||||
Task<JellyfinUser> LogIn(string username, string password, string apiKey, string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinSeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinMovie>> GetCollection(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<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||
Task<PublicInfo> GetPublicInformation(string baseUrl);
|
||||
}
|
||||
}
|
10
src/Ombi.Api.Jellyfin/IJellyfinApi.cs
Normal file
10
src/Ombi.Api.Jellyfin/IJellyfinApi.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public interface IJellyfinApi : IBaseJellyfinApi
|
||||
{
|
||||
Task<JellyfinConnectUser> LoginConnectUser(string username, string password);
|
||||
}
|
||||
}
|
|
@ -3,14 +3,14 @@ using System.Net.Http;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Emby.Models.Media.Tv;
|
||||
using Ombi.Api.Emby.Models.Movie;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Jellyfin.Models.Media.Tv;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Api.Emby
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public class JellyfinApi : IEmbyApi
|
||||
public class JellyfinApi : IJellyfinApi
|
||||
{
|
||||
public JellyfinApi(IApi api)
|
||||
{
|
||||
|
@ -20,27 +20,27 @@ namespace Ombi.Api.Emby
|
|||
private IApi Api { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns all users from the Emby Instance
|
||||
/// Returns all users from the Jellyfin Instance
|
||||
/// </summary>
|
||||
/// <param name="baseUri"></param>
|
||||
/// <param name="apiKey"></param>
|
||||
public async Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey)
|
||||
public async Task<List<JellyfinUser>> GetUsers(string baseUri, string apiKey)
|
||||
{
|
||||
var request = new Request("users", baseUri, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
var obj = await Api.Request<List<EmbyUser>>(request);
|
||||
var obj = await Api.Request<List<JellyfinUser>>(request);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl)
|
||||
public async Task<JellyfinSystemInfo> GetSystemInformation(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("System/Info", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
var obj = await Api.Request<EmbySystemInfo>(request);
|
||||
var obj = await Api.Request<JellyfinSystemInfo>(request);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ namespace Ombi.Api.Emby
|
|||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
|
||||
public async Task<JellyfinUser> LogIn(string username, string password, string apiKey, string baseUri)
|
||||
{
|
||||
var request = new Request("users/authenticatebyname", baseUri, HttpMethod.Post);
|
||||
var body = new
|
||||
|
@ -71,11 +71,11 @@ namespace Ombi.Api.Emby
|
|||
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
var obj = await Api.Request<EmbyUser>(request);
|
||||
var obj = await Api.Request<JellyfinUser>(request);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
public async Task<JellyfinItemContainer<JellyfinMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
{
|
||||
var request = new Request($"users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
|
@ -84,22 +84,22 @@ namespace Ombi.Api.Emby
|
|||
|
||||
request.AddQueryString("IsVirtualItem", "False");
|
||||
|
||||
return await Api.Request<EmbyItemContainer<EmbyMovie>>(request);
|
||||
return await Api.Request<JellyfinItemContainer<JellyfinMovie>>(request);
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
public async Task<JellyfinItemContainer<JellyfinMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
{
|
||||
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
|
||||
return await GetAll<JellyfinMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
public async Task<JellyfinItemContainer<JellyfinEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
{
|
||||
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
|
||||
return await GetAll<JellyfinEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
public async Task<JellyfinItemContainer<JellyfinSeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
{
|
||||
return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
|
||||
return await GetAll<JellyfinSeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
|
||||
}
|
||||
|
||||
public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
|
@ -126,7 +126,7 @@ namespace Ombi.Api.Emby
|
|||
return JsonConvert.DeserializeObject<T>(response);
|
||||
}
|
||||
|
||||
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
|
||||
private async Task<JellyfinItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
|
||||
{
|
||||
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
|
||||
|
||||
|
@ -139,10 +139,10 @@ namespace Ombi.Api.Emby
|
|||
AddHeaders(request, apiKey);
|
||||
|
||||
|
||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
||||
var obj = await Api.Request<JellyfinItemContainer<T>>(request);
|
||||
return obj;
|
||||
}
|
||||
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
|
||||
private async Task<JellyfinItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
|
||||
{
|
||||
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
|
||||
|
||||
|
@ -157,7 +157,7 @@ namespace Ombi.Api.Emby
|
|||
AddHeaders(request, apiKey);
|
||||
|
||||
|
||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
||||
var obj = await Api.Request<JellyfinItemContainer<T>>(request);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ namespace Ombi.Api.Emby
|
|||
req.AddHeader("Device", "Ombi");
|
||||
}
|
||||
|
||||
public Task<EmbyConnectUser> LoginConnectUser(string username, string password)
|
||||
public Task<JellyfinConnectUser> LoginConnectUser(string username, string password)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
37
src/Ombi.Api.Jellyfin/JellyfinApiFactory.cs
Normal file
37
src/Ombi.Api.Jellyfin/JellyfinApiFactory.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using Ombi.Api;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public class JellyfinApiFactory : IJellyfinApiFactory
|
||||
{
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly IApi _api;
|
||||
|
||||
// TODO, if we need to derive futher, need to rework
|
||||
public JellyfinApiFactory(ISettingsService<JellyfinSettings> jellyfinSettings, IApi api)
|
||||
{
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_api = api;
|
||||
}
|
||||
|
||||
public async Task<IJellyfinApi> CreateClient()
|
||||
{
|
||||
var settings = await _jellyfinSettings.GetSettingsAsync();
|
||||
return CreateClient(settings);
|
||||
}
|
||||
|
||||
public IJellyfinApi CreateClient(JellyfinSettings settings)
|
||||
{
|
||||
return new JellyfinApi(_api);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJellyfinApiFactory
|
||||
{
|
||||
Task<IJellyfinApi> CreateClient();
|
||||
IJellyfinApi CreateClient(JellyfinSettings settings);
|
||||
}
|
||||
}
|
45
src/Ombi.Api.Jellyfin/Models/JellyfinConfiguration.cs
Normal file
45
src/Ombi.Api.Jellyfin/Models/JellyfinConfiguration.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinConfiguration.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinConfiguration
|
||||
{
|
||||
public bool PlayDefaultAudioTrack { get; set; }
|
||||
public bool DisplayMissingEpisodes { get; set; }
|
||||
public bool DisplayUnairedEpisodes { get; set; }
|
||||
public object[] GroupedFolders { get; set; }
|
||||
public string SubtitleMode { get; set; }
|
||||
public bool DisplayCollectionsView { get; set; }
|
||||
public bool EnableLocalPassword { get; set; }
|
||||
public object[] OrderedViews { get; set; }
|
||||
public object[] LatestItemsExcludes { get; set; }
|
||||
public bool HidePlayedInLatest { get; set; }
|
||||
public bool RememberAudioSelections { get; set; }
|
||||
public bool RememberSubtitleSelections { get; set; }
|
||||
public bool EnableNextEpisodeAutoPlay { get; set; }
|
||||
}
|
||||
}
|
47
src/Ombi.Api.Jellyfin/Models/JellyfinConnectUser.cs
Normal file
47
src/Ombi.Api.Jellyfin/Models/JellyfinConnectUser.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinConnectUser.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinConnectUser
|
||||
{
|
||||
public string AccessToken { get; set; }
|
||||
public User User { get; set; }
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string IsActive { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public object IsSupporter { get; set; }
|
||||
public object ExpDate { get; set; }
|
||||
}
|
||||
|
||||
}
|
10
src/Ombi.Api.Jellyfin/Models/JellyfinItemContainer.cs
Normal file
10
src/Ombi.Api.Jellyfin/Models/JellyfinItemContainer.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinItemContainer<T>
|
||||
{
|
||||
public List<T> Items { get; set; }
|
||||
public int TotalRecordCount { get; set; }
|
||||
}
|
||||
}
|
10
src/Ombi.Api.Jellyfin/Models/JellyfinMediaType.cs
Normal file
10
src/Ombi.Api.Jellyfin/Models/JellyfinMediaType.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public enum JellyfinMediaType
|
||||
{
|
||||
Movie = 0,
|
||||
Series = 1,
|
||||
Music = 2,
|
||||
Episode = 3
|
||||
}
|
||||
}
|
59
src/Ombi.Api.Jellyfin/Models/JellyfinPolicy.cs
Normal file
59
src/Ombi.Api.Jellyfin/Models/JellyfinPolicy.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinPolicy.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinPolicy
|
||||
{
|
||||
public bool IsAdministrator { get; set; }
|
||||
public bool IsHidden { get; set; }
|
||||
public bool IsDisabled { get; set; }
|
||||
public object[] BlockedTags { get; set; }
|
||||
public bool EnableUserPreferenceAccess { get; set; }
|
||||
public object[] AccessSchedules { get; set; }
|
||||
public object[] BlockUnratedItems { get; set; }
|
||||
public bool EnableRemoteControlOfOtherUsers { get; set; }
|
||||
public bool EnableSharedDeviceControl { get; set; }
|
||||
public bool EnableLiveTvManagement { get; set; }
|
||||
public bool EnableLiveTvAccess { get; set; }
|
||||
public bool EnableMediaPlayback { get; set; }
|
||||
public bool EnableAudioPlaybackTranscoding { get; set; }
|
||||
public bool EnableVideoPlaybackTranscoding { get; set; }
|
||||
public bool EnablePlaybackRemuxing { get; set; }
|
||||
public bool EnableContentDeletion { get; set; }
|
||||
public bool EnableContentDownloading { get; set; }
|
||||
public bool EnableSync { get; set; }
|
||||
public bool EnableSyncTranscoding { get; set; }
|
||||
public object[] EnabledDevices { get; set; }
|
||||
public bool EnableAllDevices { get; set; }
|
||||
public object[] EnabledChannels { get; set; }
|
||||
public bool EnableAllChannels { get; set; }
|
||||
public object[] EnabledFolders { get; set; }
|
||||
public bool EnableAllFolders { get; set; }
|
||||
public int InvalidLoginAttemptCount { get; set; }
|
||||
public bool EnablePublicSharing { get; set; }
|
||||
}
|
||||
}
|
63
src/Ombi.Api.Jellyfin/Models/JellyfinSystemInfo.cs
Normal file
63
src/Ombi.Api.Jellyfin/Models/JellyfinSystemInfo.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinSystemInfo.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinSystemInfo
|
||||
{
|
||||
public string SystemUpdateLevel { get; set; }
|
||||
public string OperatingSystemDisplayName { get; set; }
|
||||
public bool SupportsRunningAsService { get; set; }
|
||||
public string MacAddress { get; set; }
|
||||
public bool HasPendingRestart { get; set; }
|
||||
public bool SupportsLibraryMonitor { get; set; }
|
||||
public object[] InProgressInstallations { get; set; }
|
||||
public int WebSocketPortNumber { get; set; }
|
||||
public object[] CompletedInstallations { get; set; }
|
||||
public bool CanSelfRestart { get; set; }
|
||||
public bool CanSelfUpdate { get; set; }
|
||||
public object[] FailedPluginAssemblies { get; set; }
|
||||
public string ProgramDataPath { get; set; }
|
||||
public string ItemsByNamePath { get; set; }
|
||||
public string CachePath { get; set; }
|
||||
public string LogPath { get; set; }
|
||||
public string InternalMetadataPath { get; set; }
|
||||
public string TranscodingTempPath { get; set; }
|
||||
public int HttpServerPortNumber { get; set; }
|
||||
public bool SupportsHttps { get; set; }
|
||||
public int HttpsPortNumber { get; set; }
|
||||
public bool HasUpdateAvailable { get; set; }
|
||||
public bool SupportsAutoRunAtStartup { get; set; }
|
||||
public string EncoderLocationType { get; set; }
|
||||
public string SystemArchitecture { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
public string WanAddress { get; set; }
|
||||
public string ServerName { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string OperatingSystem { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
47
src/Ombi.Api.Jellyfin/Models/JellyfinUser.cs
Normal file
47
src/Ombi.Api.Jellyfin/Models/JellyfinUser.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinUser.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinUser
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string ConnectUserName { get; set; }
|
||||
public string ConnectLinkType { get; set; }
|
||||
public string Id { get; set; }
|
||||
public bool HasPassword { get; set; }
|
||||
public bool HasConfiguredPassword { get; set; }
|
||||
public bool HasConfiguredEasyPassword { get; set; }
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
public DateTime LastActivityDate { get; set; }
|
||||
public JellyfinConfiguration Configuration { get; set; }
|
||||
public JellyfinPolicy Policy { get; set; }
|
||||
}
|
||||
}
|
7
src/Ombi.Api.Jellyfin/Models/JellyfinUserLogin.cs
Normal file
7
src/Ombi.Api.Jellyfin/Models/JellyfinUserLogin.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinUserLogin
|
||||
{
|
||||
public JellyfinUser User { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinChapter.cs
Normal file
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinChapter.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinChapter
|
||||
{
|
||||
public long StartPositionTicks { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinExternalurl
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
10
src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs
Normal file
10
src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinImagetags
|
||||
{
|
||||
public string Primary { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public string Thumb { get; set; }
|
||||
public string Banner { get; set; }
|
||||
}
|
||||
}
|
30
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediasource.cs
Normal file
30
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediasource.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinMediasource
|
||||
{
|
||||
public string Protocol { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool IsRemote { get; set; }
|
||||
public string ETag { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public bool ReadAtNativeFramerate { get; set; }
|
||||
public bool SupportsTranscoding { get; set; }
|
||||
public bool SupportsDirectStream { get; set; }
|
||||
public bool SupportsDirectPlay { get; set; }
|
||||
public bool IsInfiniteStream { get; set; }
|
||||
public bool RequiresOpening { get; set; }
|
||||
public bool RequiresClosing { get; set; }
|
||||
public bool SupportsProbing { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
public object[] PlayableStreamFileNames { get; set; }
|
||||
public object[] Formats { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public int DefaultAudioStreamIndex { get; set; }
|
||||
|
||||
}
|
||||
}
|
36
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediastream.cs
Normal file
36
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediastream.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinMediastream
|
||||
{
|
||||
public string Codec { get; set; }
|
||||
public string Language { get; set; }
|
||||
public string TimeBase { get; set; }
|
||||
public string CodecTimeBase { get; set; }
|
||||
public string NalLengthSize { get; set; }
|
||||
public bool IsInterlaced { get; set; }
|
||||
public bool IsAVC { get; set; }
|
||||
public int BitRate { get; set; }
|
||||
public int BitDepth { get; set; }
|
||||
public int RefFrames { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
public bool IsForced { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int Width { get; set; }
|
||||
public float AverageFrameRate { get; set; }
|
||||
public float RealFrameRate { get; set; }
|
||||
public string Profile { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string AspectRatio { get; set; }
|
||||
public int Index { get; set; }
|
||||
public bool IsExternal { get; set; }
|
||||
public bool IsTextSubtitleStream { get; set; }
|
||||
public bool SupportsExternalStream { get; set; }
|
||||
public string PixelFormat { get; set; }
|
||||
public int Level { get; set; }
|
||||
public bool IsAnamorphic { get; set; }
|
||||
public string DisplayTitle { get; set; }
|
||||
public string ChannelLayout { get; set; }
|
||||
public int Channels { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Jellyfin/Models/Media/JellyfinPerson.cs
Normal file
11
src/Ombi.Api.Jellyfin/Models/Media/JellyfinPerson.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinPerson
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string PrimaryImageTag { get; set; }
|
||||
}
|
||||
}
|
13
src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs
Normal file
13
src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinProviderids
|
||||
{
|
||||
public string Tmdb { get; set; }
|
||||
public string Imdb { get; set; }
|
||||
public string TmdbCollection { get; set; }
|
||||
|
||||
public string Tvdb { get; set; }
|
||||
public string Zap2It { get; set; }
|
||||
public string TvRage { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinRemotetrailer
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinStudio.cs
Normal file
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinStudio.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinStudio
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
15
src/Ombi.Api.Jellyfin/Models/Media/JellyfinUserdata.cs
Normal file
15
src/Ombi.Api.Jellyfin/Models/Media/JellyfinUserdata.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinUserdata
|
||||
{
|
||||
public double PlaybackPositionTicks { get; set; }
|
||||
public int PlayCount { get; set; }
|
||||
public bool IsFavorite { get; set; }
|
||||
public bool Played { get; set; }
|
||||
public string Key { get; set; }
|
||||
public DateTime LastPlayedDate { get; set; }
|
||||
public int UnplayedItemCount { get; set; }
|
||||
}
|
||||
}
|
34
src/Ombi.Api.Jellyfin/Models/Media/Movie/JellyfinMovie.cs
Normal file
34
src/Ombi.Api.Jellyfin/Models/Media/Movie/JellyfinMovie.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinMovie
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public object[] ProductionLocations { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public int CriticRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
60
src/Ombi.Api.Jellyfin/Models/Media/Movie/MovieInformation.cs
Normal file
60
src/Ombi.Api.Jellyfin/Models/Media/Movie/MovieInformation.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class MovieInformation
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string OriginalTitle { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Etag { get; set; }
|
||||
public DateTime DateCreated { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
public bool CanDownload { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public JellyfinExternalurl[] ExternalUrls { get; set; }
|
||||
public JellyfinMediasource[] MediaSources { get; set; }
|
||||
public string[] ProductionLocations { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string[] Taglines { get; set; }
|
||||
public string[] Genres { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public JellyfinRemotetrailer[] RemoteTrailers { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public JellyfinPerson[] People { get; set; }
|
||||
public JellyfinStudio[] Studios { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string DisplayPreferencesId { get; set; }
|
||||
public object[] Tags { get; set; }
|
||||
public string[] Keywords { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public object[] ScreenshotImageTags { get; set; }
|
||||
public JellyfinChapter[] Chapters { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public string HomePageUrl { get; set; }
|
||||
public int Budget { get; set; }
|
||||
public float Revenue { get; set; }
|
||||
public object[] LockedFields { get; set; }
|
||||
public bool LockData { get; set; }
|
||||
}
|
||||
}
|
71
src/Ombi.Api.Jellyfin/Models/Media/Tv/EpisodeInformation.cs
Normal file
71
src/Ombi.Api.Jellyfin/Models/Media/Tv/EpisodeInformation.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class EpisodeInformation
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Etag { get; set; }
|
||||
public DateTime DateCreated { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
public bool CanDownload { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public JellyfinExternalurl[] ExternalUrls { get; set; }
|
||||
public JellyfinMediasource[] MediaSources { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public object[] Taglines { get; set; }
|
||||
public object[] Genres { get; set; }
|
||||
public string[] SeriesGenres { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public int IndexNumber { get; set; }
|
||||
public int ParentIndexNumber { get; set; }
|
||||
public object[] RemoteTrailers { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public object[] People { get; set; }
|
||||
public object[] Studios { get; set; }
|
||||
public string ParentLogoItemId { get; set; }
|
||||
public string ParentBackdropItemId { get; set; }
|
||||
public string[] ParentBackdropImageTags { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesId { get; set; }
|
||||
public string SeasonId { get; set; }
|
||||
public string DisplayPreferencesId { get; set; }
|
||||
public object[] Tags { get; set; }
|
||||
public object[] Keywords { get; set; }
|
||||
public string SeriesPrimaryImageTag { get; set; }
|
||||
public string SeasonName { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public object[] BackdropImageTags { get; set; }
|
||||
public object[] ScreenshotImageTags { get; set; }
|
||||
public string ParentLogoImageTag { get; set; }
|
||||
public string SeriesStudio { get; set; }
|
||||
public JellyfinSeriesstudioinfo SeriesStudioInfo { get; set; }
|
||||
public string ParentThumbItemId { get; set; }
|
||||
public string ParentThumbImageTag { get; set; }
|
||||
public JellyfinChapter[] Chapters { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public object[] LockedFields { get; set; }
|
||||
public bool LockData { get; set; }
|
||||
}
|
||||
}
|
45
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinEpisodes.cs
Normal file
45
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinEpisodes.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinEpisodes
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public int IndexNumber { get; set; }
|
||||
public int? IndexNumberEnd { get; set; }
|
||||
public int ParentIndexNumber { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string ParentLogoItemId { get; set; }
|
||||
public string ParentBackdropItemId { get; set; }
|
||||
public string[] ParentBackdropImageTags { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesId { get; set; }
|
||||
public string SeasonId { get; set; }
|
||||
public string SeriesPrimaryImageTag { get; set; }
|
||||
public string SeasonName { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public object[] BackdropImageTags { get; set; }
|
||||
public string ParentLogoImageTag { get; set; }
|
||||
public string ParentThumbItemId { get; set; }
|
||||
public string ParentThumbImageTag { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinRemotetrailer
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
32
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinSeries.cs
Normal file
32
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinSeries.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinSeries
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public int ChildCount { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string AirTime { get; set; }
|
||||
public string[] AirDays { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinSeriesstudioinfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
59
src/Ombi.Api.Jellyfin/Models/Media/Tv/SeriesInformation.cs
Normal file
59
src/Ombi.Api.Jellyfin/Models/Media/Tv/SeriesInformation.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class SeriesInformation
|
||||
{
|
||||
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Etag { get; set; }
|
||||
public DateTime DateCreated { get; set; }
|
||||
public DateTime DateLastMediaAdded { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
public bool CanDownload { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public JellyfinExternalurl[] ExternalUrls { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string ShortOverview { get; set; }
|
||||
public object[] Taglines { get; set; }
|
||||
public string[] Genres { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public long CumulativeRunTimeTicks { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public JellyfinRemotetrailer[] RemoteTrailers { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public JellyfinPerson[] People { get; set; }
|
||||
public JellyfinStudio[] Studios { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public int RecursiveItemCount { get; set; }
|
||||
public int ChildCount { get; set; }
|
||||
public string DisplayPreferencesId { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string AirTime { get; set; }
|
||||
public string[] AirDays { get; set; }
|
||||
public object[] Tags { get; set; }
|
||||
public object[] Keywords { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public object[] ScreenshotImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string HomePageUrl { get; set; }
|
||||
public object[] LockedFields { get; set; }
|
||||
public bool LockData { get; set; }
|
||||
|
||||
}
|
||||
}
|
12
src/Ombi.Api.Jellyfin/Models/PublicInfo.cs
Normal file
12
src/Ombi.Api.Jellyfin/Models/PublicInfo.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class PublicInfo
|
||||
{
|
||||
public string LocalAddress { get; set; }
|
||||
public string ServerName { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string OperatingSystem { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
}
|
17
src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj
Normal file
17
src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj
Normal file
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||
<FileVersion>3.0.0.0</FileVersion>
|
||||
<Version></Version>
|
||||
<PackageVersion></PackageVersion>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -30,7 +30,7 @@ namespace Ombi.Core.Tests.Authentication
|
|||
AuthenticationSettings.Setup(x => x.GetSettingsAsync())
|
||||
.ReturnsAsync(new AuthenticationSettings());
|
||||
_um = new OmbiUserManager(UserStore.Object, null, null, null, null, null, null, null, null,
|
||||
PlexApi.Object, null, null, AuthenticationSettings.Object);
|
||||
PlexApi.Object, null, null, null, null, AuthenticationSettings.Object);
|
||||
}
|
||||
|
||||
public OmbiUserManager _um { get; set; }
|
||||
|
|
|
@ -115,4 +115,4 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
Assert.False(search.Available);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
117
src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs
Normal file
117
src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs
Normal file
|
@ -0,0 +1,117 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Rules.Search;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Core.Tests.Rule.Search
|
||||
{
|
||||
public class JellyfinAvailabilityRuleTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
ContextMock = new Mock<IJellyfinContentRepository>();
|
||||
SettingsMock = new Mock<ISettingsService<JellyfinSettings>>();
|
||||
Rule = new JellyfinAvailabilityRule(ContextMock.Object, SettingsMock.Object);
|
||||
}
|
||||
|
||||
private JellyfinAvailabilityRule Rule { get; set; }
|
||||
private Mock<IJellyfinContentRepository> ContextMock { get; set; }
|
||||
private Mock<ISettingsService<JellyfinSettings>> SettingsMock { get; set; }
|
||||
|
||||
[Test]
|
||||
public async Task Movie_ShouldBe_Available_WhenFoundInJellyfin()
|
||||
{
|
||||
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings());
|
||||
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
|
||||
{
|
||||
ProviderId = "123"
|
||||
});
|
||||
var search = new SearchMovieViewModel()
|
||||
{
|
||||
TheMovieDbId = "123",
|
||||
};
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.True(search.Available);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Movie_Has_Custom_Url_When_Specified_In_Settings()
|
||||
{
|
||||
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings
|
||||
{
|
||||
Enable = true,
|
||||
Servers = new List<JellyfinServers>
|
||||
{
|
||||
new JellyfinServers
|
||||
{
|
||||
ServerHostname = "http://test.com/",
|
||||
ServerId = "8"
|
||||
}
|
||||
}
|
||||
});
|
||||
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
|
||||
{
|
||||
ProviderId = "123",
|
||||
JellyfinId = 1.ToString(),
|
||||
});
|
||||
var search = new SearchMovieViewModel()
|
||||
{
|
||||
TheMovieDbId = "123",
|
||||
};
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.That(search.JellyfinUrl, Is.EqualTo("http://test.com/web/index.html#!/details?id=1&serverId=8"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Movie_Uses_Default_Url_When()
|
||||
{
|
||||
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings
|
||||
{
|
||||
Enable = true,
|
||||
Servers = new List<JellyfinServers>
|
||||
{
|
||||
new JellyfinServers
|
||||
{
|
||||
ServerHostname = string.Empty,
|
||||
ServerId = "8"
|
||||
}
|
||||
}
|
||||
});
|
||||
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
|
||||
{
|
||||
ProviderId = "123",
|
||||
JellyfinId = 1.ToString()
|
||||
});
|
||||
var search = new SearchMovieViewModel()
|
||||
{
|
||||
TheMovieDbId = "123",
|
||||
};
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInJellyfin()
|
||||
{
|
||||
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).Returns(Task.FromResult(default(JellyfinContent)));
|
||||
var search = new SearchMovieViewModel();
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.False(search.Available);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Core.Settings;
|
||||
|
@ -49,18 +50,24 @@ namespace Ombi.Core.Authentication
|
|||
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
||||
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
||||
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
|
||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth)
|
||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings,
|
||||
IJellyfinApiFactory jellyfinApi, ISettingsService<JellyfinSettings> jellyfinSettings,
|
||||
ISettingsService<AuthenticationSettings> auth)
|
||||
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
||||
{
|
||||
_plexApi = plexApi;
|
||||
_embyApi = embyApi;
|
||||
_jellyfinApi = jellyfinApi;
|
||||
_embySettings = embySettings;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_authSettings = auth;
|
||||
}
|
||||
|
||||
private readonly IPlexApi _plexApi;
|
||||
private readonly IEmbyApiFactory _embyApi;
|
||||
private readonly IJellyfinApiFactory _jellyfinApi;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||
|
||||
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
||||
|
@ -83,6 +90,10 @@ namespace Ombi.Core.Authentication
|
|||
{
|
||||
return await CheckEmbyPasswordAsync(user, password);
|
||||
}
|
||||
if (user.UserType == UserType.JellyfinUser)
|
||||
{
|
||||
return await CheckJellyfinPasswordAsync(user, password);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -185,5 +196,36 @@ namespace Ombi.Core.Authentication
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sign the user into Jellyfin
|
||||
/// <remarks>We do not check if the user is in the owners "friends" since they must have a local user account to get this far.
|
||||
/// We also have to try and authenticate them with every server, the first server that work we just say it was a success</remarks>
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> CheckJellyfinPasswordAsync(OmbiUser user, string password)
|
||||
{
|
||||
var jellyfinSettings = await _jellyfinSettings.GetSettingsAsync();
|
||||
var client = _jellyfinApi.CreateClient(jellyfinSettings);
|
||||
|
||||
foreach (var server in jellyfinSettings.Servers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
|
||||
if (result != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Jellyfin Login Failed");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,38 +13,43 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public class RecentlyAddedEngine : IRecentlyAddedEngine
|
||||
{
|
||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IJellyfinContentRepository jellyfin, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||
{
|
||||
_plex = plex;
|
||||
_emby = emby;
|
||||
_jellyfin = jellyfin;
|
||||
_recentlyAddedLog = recentlyAdded;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plex;
|
||||
private readonly IEmbyContentRepository _emby;
|
||||
private readonly IJellyfinContentRepository _jellyfin;
|
||||
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||
|
||||
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to)
|
||||
{
|
||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies).Take(30);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies).Take(30);
|
||||
}
|
||||
|
||||
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies()
|
||||
{
|
||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies);
|
||||
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies);
|
||||
}
|
||||
|
||||
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason)
|
||||
{
|
||||
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show && x.AddedAt > from && x.AddedAt < to);
|
||||
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series && x.AddedAt > from && x.AddedAt < to);
|
||||
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series && x.AddedAt > from && x.AddedAt < to);
|
||||
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason).Take(30);
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason).Take(30);
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,14 +57,16 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show);
|
||||
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series);
|
||||
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series);
|
||||
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason);
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateRecentlyAddedDatabase()
|
||||
{
|
||||
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
||||
var embyContent = _emby.GetAll().Include(x => x.Episodes);
|
||||
var jellyfinContent = _jellyfin.GetAll().Include(x => x.Episodes);
|
||||
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||
foreach (var p in plexContent)
|
||||
{
|
||||
|
@ -136,17 +143,56 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in jellyfinContent)
|
||||
{
|
||||
if (e.TheMovieDbId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (e.Type == JellyfinMediaType.Movie)
|
||||
{
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Jellyfin,
|
||||
ContentId = int.Parse(e.TheMovieDbId),
|
||||
ContentType = ContentType.Parent
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the episodes
|
||||
foreach (var ep in e.Episodes)
|
||||
{
|
||||
if (ep.Series.TvDbId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Jellyfin,
|
||||
ContentId = int.Parse(ep.Series.TvDbId),
|
||||
ContentType = ContentType.Episode,
|
||||
EpisodeNumber = ep.EpisodeNumber,
|
||||
SeasonNumber = ep.SeasonNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv,
|
||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv, IQueryable<JellyfinContent> jellyfinTv,
|
||||
bool groupBySeason)
|
||||
{
|
||||
var model = new HashSet<RecentlyAddedTvModel>();
|
||||
TransformPlexShows(plexTv, model);
|
||||
TransformEmbyShows(embyTv, model);
|
||||
TransformJellyfinShows(jellyfinTv, model);
|
||||
|
||||
if (groupBySeason)
|
||||
{
|
||||
|
@ -156,11 +202,12 @@ namespace Ombi.Core.Engine
|
|||
return model;
|
||||
}
|
||||
|
||||
private IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(IQueryable<PlexServerContent> plexMovies, IQueryable<EmbyContent> embyMovies)
|
||||
private IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(IQueryable<PlexServerContent> plexMovies, IQueryable<EmbyContent> embyMovies, IQueryable<JellyfinContent> jellyfinMovies)
|
||||
{
|
||||
var model = new HashSet<RecentlyAddedMovieModel>();
|
||||
TransformPlexMovies(plexMovies, model);
|
||||
TransformEmbyMovies(embyMovies, model);
|
||||
TransformJellyfinMovies(jellyfinMovies, model);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
@ -181,6 +228,22 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
private static void TransformJellyfinMovies(IQueryable<JellyfinContent> jellyfinMovies, HashSet<RecentlyAddedMovieModel> model)
|
||||
{
|
||||
foreach (var jellyfin in jellyfinMovies)
|
||||
{
|
||||
model.Add(new RecentlyAddedMovieModel
|
||||
{
|
||||
Id = jellyfin.Id,
|
||||
ImdbId = jellyfin.ImdbId,
|
||||
TheMovieDbId = jellyfin.TheMovieDbId,
|
||||
TvDbId = jellyfin.TvDbId,
|
||||
AddedAt = jellyfin.AddedAt,
|
||||
Title = jellyfin.Title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void TransformPlexMovies(IQueryable<PlexServerContent> plexMovies, HashSet<RecentlyAddedMovieModel> model)
|
||||
{
|
||||
foreach (var plex in plexMovies)
|
||||
|
@ -244,5 +307,26 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void TransformJellyfinShows(IQueryable<JellyfinContent> jellyfinShows, HashSet<RecentlyAddedTvModel> model)
|
||||
{
|
||||
foreach (var jellyfin in jellyfinShows)
|
||||
{
|
||||
foreach (var episode in jellyfin.Episodes)
|
||||
{
|
||||
model.Add(new RecentlyAddedTvModel
|
||||
{
|
||||
Id = jellyfin.Id,
|
||||
ImdbId = jellyfin.ImdbId,
|
||||
TvDbId = jellyfin.TvDbId,
|
||||
TheMovieDbId = jellyfin.TheMovieDbId,
|
||||
AddedAt = jellyfin.AddedAt,
|
||||
Title = jellyfin.Title,
|
||||
EpisodeNumber = episode.EpisodeNumber,
|
||||
SeasonNumber = episode.SeasonNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models
|
|||
public enum RecentlyAddedType
|
||||
{
|
||||
Plex,
|
||||
Emby
|
||||
Emby,
|
||||
Jellyfin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,12 @@ namespace Ombi.Core.Models.Search
|
|||
public bool Available { get; set; }
|
||||
public string PlexUrl { get; set; }
|
||||
public string EmbyUrl { get; set; }
|
||||
public string JellyfinUrl { get; set; }
|
||||
public string Quality { get; set; }
|
||||
public abstract RequestType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule rule
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule/JellyfinAvailabilityRule rule
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The custom identifier.
|
||||
|
@ -35,4 +36,4 @@ namespace Ombi.Core.Models.Search
|
|||
[NotMapped]
|
||||
public bool ShowSubscribe { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Ombi.Core.Models
|
|||
{
|
||||
LocalUser = 1,
|
||||
PlexUser = 2,
|
||||
EmbyUser = 3
|
||||
EmbyUser = 3,
|
||||
JellyfinUser = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj" />
|
||||
|
@ -40,4 +41,4 @@
|
|||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -108,10 +108,40 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<JellyfinEpisode> allEpisodes, EpisodeRequests episode,
|
||||
SeasonRequests season, JellyfinContent item, bool useTheMovieDb, bool useTvDb)
|
||||
{
|
||||
JellyfinEpisode epExists = null;
|
||||
if (useImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ImdbId == item.ImdbId);
|
||||
}
|
||||
|
||||
if (useTheMovieDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TheMovieDbId == item.TheMovieDbId);
|
||||
}
|
||||
|
||||
if (useTvDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,11 +70,11 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||
{
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname, s.IsJellyfin);
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null, s.IsJellyfin);
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,4 +100,4 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
103
src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs
Normal file
103
src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
public class JellyfinAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public JellyfinAvailabilityRule(IJellyfinContentRepository repo, ISettingsService<JellyfinSettings> s)
|
||||
{
|
||||
JellyfinContentRepository = repo;
|
||||
JellyfinSettings = s;
|
||||
}
|
||||
|
||||
private IJellyfinContentRepository JellyfinContentRepository { get; }
|
||||
private ISettingsService<JellyfinSettings> JellyfinSettings { get; }
|
||||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
JellyfinContent item = null;
|
||||
var useImdb = false;
|
||||
var useTheMovieDb = false;
|
||||
var useTvDb = false;
|
||||
|
||||
if (obj.ImdbId.HasValue())
|
||||
{
|
||||
item = await JellyfinContentRepository.GetByImdbId(obj.ImdbId);
|
||||
if (item != null)
|
||||
{
|
||||
useImdb = true;
|
||||
}
|
||||
}
|
||||
if (item == null)
|
||||
{
|
||||
if (obj.TheMovieDbId.HasValue())
|
||||
{
|
||||
item = await JellyfinContentRepository.GetByTheMovieDbId(obj.TheMovieDbId);
|
||||
if (item != null)
|
||||
{
|
||||
useTheMovieDb = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
if (obj.TheTvDbId.HasValue())
|
||||
{
|
||||
item = await JellyfinContentRepository.GetByTvDbId(obj.TheTvDbId);
|
||||
if (item != null)
|
||||
{
|
||||
useTvDb = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
obj.Available = true;
|
||||
var s = await JellyfinSettings.GetSettingsAsync();
|
||||
if (s.Enable)
|
||||
{
|
||||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||
{
|
||||
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, server?.ServerHostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
var search = (SearchTvShowViewModel)obj;
|
||||
// Let's go through the episodes now
|
||||
if (search.SeasonRequests.Any())
|
||||
{
|
||||
var allEpisodes = JellyfinContentRepository.GetAllEpisodes().Include(x => x.Series);
|
||||
foreach (var season in search.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
using Ombi.Api.Discord;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Api.Sonarr;
|
||||
|
@ -47,6 +48,7 @@ using Ombi.Core.Senders;
|
|||
using Ombi.Helpers;
|
||||
using Ombi.Schedule.Jobs.Couchpotato;
|
||||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Jellyfin;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Schedule.Jobs.Sonarr;
|
||||
|
@ -126,6 +128,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
||||
services.AddTransient<IPlexApi, PlexApi>();
|
||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||
services.AddTransient<IJellyfinApi, JellyfinApi>();
|
||||
services.AddTransient<ISonarrApi, SonarrApi>();
|
||||
services.AddTransient<ISonarrV3Api, SonarrV3Api>();
|
||||
services.AddTransient<ISlackApi, SlackApi>();
|
||||
|
@ -153,8 +156,8 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
|
||||
services.AddTransient<IWhatsAppApi, WhatsAppApi>();
|
||||
services.AddTransient<ICloudMobileNotification, CloudMobileNotification>();
|
||||
services.AddTransient<IBaseEmbyApi, JellyfinApi>();
|
||||
services.AddTransient<IEmbyApiFactory, EmbyApiFactory>();
|
||||
services.AddTransient<IJellyfinApiFactory, JellyfinApiFactory>();
|
||||
}
|
||||
|
||||
public static void RegisterStore(this IServiceCollection services) {
|
||||
|
@ -169,6 +172,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddScoped<ISettingsResolver, SettingsResolver>();
|
||||
services.AddScoped<IPlexContentRepository, PlexServerContentRepository>();
|
||||
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
|
||||
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
|
||||
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
|
||||
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
||||
|
@ -213,6 +217,9 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IEmbyContentSync, EmbyContentSync>();
|
||||
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
|
||||
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
||||
services.AddTransient<IJellyfinContentSync, JellyfinContentSync>();
|
||||
services.AddTransient<IJellyfinEpisodeSync, JellyfinEpisodeSync>();
|
||||
services.AddTransient<IJellyfinAvaliabilityChecker, JellyfinAvaliabilityChecker>();
|
||||
services.AddTransient<IPlexEpisodeSync, PlexEpisodeSync>();
|
||||
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
||||
services.AddTransient<IRadarrSync, RadarrSync>();
|
||||
|
@ -220,6 +227,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
||||
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
||||
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
||||
services.AddTransient<IJellyfinUserImporter, JellyfinUserImporter>();
|
||||
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
|
||||
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
||||
services.AddTransient<IProcessProvider, ProcessProvider>();
|
||||
|
|
|
@ -3,6 +3,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|||
using Ombi.Api.CouchPotato;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Core.Settings;
|
||||
|
|
54
src/Ombi.HealthChecks/Checks/JellyfinHealthCheck.cs
Normal file
54
src/Ombi.HealthChecks/Checks/JellyfinHealthCheck.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.HealthChecks.Checks
|
||||
{
|
||||
public class JellyfinHealthCheck : BaseHealthCheck
|
||||
{
|
||||
public JellyfinHealthCheck(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
|
||||
{
|
||||
}
|
||||
public override async Task<HealthCheckResult> CheckHealthAsync(
|
||||
HealthCheckContext context,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using (var scope = CreateScope())
|
||||
{
|
||||
var settingsProvider = scope.ServiceProvider.GetRequiredService<ISettingsService<JellyfinSettings>>();
|
||||
var api = scope.ServiceProvider.GetRequiredService<IJellyfinApiFactory>();
|
||||
var settings = await settingsProvider.GetSettingsAsync();
|
||||
if (settings == null)
|
||||
{
|
||||
return HealthCheckResult.Healthy("Jellyfin is not configured.");
|
||||
}
|
||||
|
||||
var client = api.CreateClient(settings);
|
||||
var taskResult = new List<Task<JellyfinSystemInfo>>();
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
taskResult.Add(client.GetSystemInformation(server.ApiKey, server.FullUri));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await Task.WhenAll(taskResult.ToArray());
|
||||
return HealthCheckResult.Healthy();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy("Could not communicate with Jellyfin", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|||
using Ombi.Api.CouchPotato;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Api.SickRage;
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Ombi.HealthChecks
|
|||
{
|
||||
builder.AddCheck<PlexHealthCheck>("Plex", tags: new string[] { "MediaServer" });
|
||||
builder.AddCheck<EmbyHealthCheck>("Emby", tags: new string[] { "MediaServer" });
|
||||
builder.AddCheck<JellyfinHealthCheck>("Jellyfin", tags: new string[] { "MediaServer" });
|
||||
builder.AddCheck<LidarrHealthCheck>("Lidarr", tags: new string[] { "DVR" });
|
||||
builder.AddCheck<SonarrHealthCheck>("Sonarr", tags: new string[] { "DVR" });
|
||||
builder.AddCheck<RadarrHealthCheck>("Radarr", tags: new string[] { "DVR" });
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
|
|
|
@ -15,13 +15,6 @@ namespace Ombi.Helpers.Tests
|
|||
return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(JellyfinUrlData))]
|
||||
public string TestJellyfinUrl(string mediaId, string url, string serverId)
|
||||
{
|
||||
// http://192.168.68.X:8097/web/index.html#!/details?id=7ffe222498445d5ebfddb31bc4fa9a6d&serverId=50cce67f0baa425093d189b3017331fb
|
||||
return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url, true);
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> UrlData
|
||||
{
|
||||
get
|
||||
|
@ -33,16 +26,5 @@ namespace Ombi.Helpers.Tests
|
|||
yield return new TestCaseData(mediaId.ToString(), string.Empty, "1").Returns($"https://app.emby.media/web/index.html#!/item?id={mediaId}&serverId=1").SetName("EmbyHelper_GetMediaUrl_WithOutCustomDomain");
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> JellyfinUrlData
|
||||
{
|
||||
get
|
||||
{
|
||||
var mediaId = 1;
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash");
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain");
|
||||
yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_Https");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
29
src/Ombi.Helpers.Tests/JellyfinHelperTests.cs
Normal file
29
src/Ombi.Helpers.Tests/JellyfinHelperTests.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Helpers.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class JellyfinHelperTests
|
||||
{
|
||||
[TestCaseSource(nameof(UrlData))]
|
||||
public string TestUrl(string mediaId, string url, string serverId)
|
||||
{
|
||||
// http://192.168.68.X:8097/web/index.html#!/details?id=7ffe222498445d5ebfddb31bc4fa9a6d&serverId=50cce67f0baa425093d189b3017331fb
|
||||
return JellyfinHelper.GetJellyfinMediaUrl(mediaId, serverId, url);
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> UrlData
|
||||
{
|
||||
get
|
||||
{
|
||||
var mediaId = 1;
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash");
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain");
|
||||
yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_Https");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,14 +2,10 @@
|
|||
{
|
||||
public static class EmbyHelper
|
||||
{
|
||||
public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null, bool isJellyfin = false)
|
||||
public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null)
|
||||
{
|
||||
//web/index.html#!/details|item
|
||||
string path = "item";
|
||||
if (isJellyfin)
|
||||
{
|
||||
path = "details";
|
||||
}
|
||||
if (customerServerUrl.HasValue())
|
||||
{
|
||||
if (!customerServerUrl.EndsWith("/"))
|
||||
|
|
23
src/Ombi.Helpers/JellyfinHelper.cs
Normal file
23
src/Ombi.Helpers/JellyfinHelper.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace Ombi.Helpers
|
||||
{
|
||||
public static class JellyfinHelper
|
||||
{
|
||||
public static string GetJellyfinMediaUrl(string mediaId, string serverId, string customerServerUrl = null)
|
||||
{
|
||||
//web/index.html#!/details|item
|
||||
string path = "details";
|
||||
if (customerServerUrl.HasValue())
|
||||
{
|
||||
if (!customerServerUrl.EndsWith("/"))
|
||||
{
|
||||
return $"{customerServerUrl}/web/index.html#!/{path}?id={mediaId}&serverId={serverId}";
|
||||
}
|
||||
return $"{customerServerUrl}web/index.html#!/{path}?id={mediaId}&serverId={serverId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"http://localhost:8096/web/index.html#!/{path}?id={mediaId}&serverId={serverId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,8 +14,10 @@ namespace Ombi.Helpers
|
|||
public static EventId RadarrCacher => new EventId(2001);
|
||||
public static EventId PlexEpisodeCacher => new EventId(2002);
|
||||
public static EventId EmbyContentCacher => new EventId(2003);
|
||||
public static EventId JellyfinContentCacher => new EventId(2012);
|
||||
public static EventId PlexUserImporter => new EventId(2004);
|
||||
public static EventId EmbyUserImporter => new EventId(2005);
|
||||
public static EventId JellyfinUserImporter => new EventId(2013);
|
||||
public static EventId SonarrCacher => new EventId(2006);
|
||||
public static EventId CouchPotatoCacher => new EventId(2007);
|
||||
public static EventId PlexContentCacher => new EventId(2008);
|
||||
|
@ -43,4 +45,4 @@ namespace Ombi.Helpers
|
|||
public static EventId Updater => new EventId(6000);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Failed");
|
||||
_logger.LogError(e, "Exception when caching {1} for server {0}", server.Name, embySettings.IsJellyfin ? "Jellyfin" : "Emby");
|
||||
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
Title = tvShow.Name,
|
||||
Type = EmbyMediaType.Series,
|
||||
EmbyId = tvShow.Id,
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname, settings.IsJellyfin),
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
|
||||
AddedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
@ -228,4 +228,4 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
Api = _apiFactory.CreateClient(settings);
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, $"{(settings.IsJellyfin ? "Jellyfin" : "Emby")} User Importer Started");
|
||||
.SendAsync(NotificationHub.NotificationEvent, $"Emby User Importer Started");
|
||||
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
|
@ -180,4 +180,4 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public interface IJellyfinAvaliabilityChecker : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinContentSync.cs
Normal file
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinContentSync.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public interface IJellyfinContentSync : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinEpisodeSync.cs
Normal file
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinEpisodeSync.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public interface IJellyfinEpisodeSync : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinUserImporter.cs
Normal file
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinUserImporter.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public interface IJellyfinUserImporter : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
235
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs
Normal file
235
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs
Normal file
|
@ -0,0 +1,235 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinAvaliabilityCheker.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public class JellyfinAvaliabilityChecker : IJellyfinAvaliabilityChecker
|
||||
{
|
||||
public JellyfinAvaliabilityChecker(IJellyfinContentRepository repo, ITvRequestRepository t, IMovieRequestRepository m,
|
||||
INotificationHelper n, ILogger<JellyfinAvaliabilityChecker> log, IHubContext<NotificationHub> notification)
|
||||
{
|
||||
_repo = repo;
|
||||
_tvRepo = t;
|
||||
_movieRepo = m;
|
||||
_notificationService = n;
|
||||
_log = log;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
private readonly ITvRequestRepository _tvRepo;
|
||||
private readonly IMovieRequestRepository _movieRepo;
|
||||
private readonly IJellyfinContentRepository _repo;
|
||||
private readonly INotificationHelper _notificationService;
|
||||
private readonly ILogger<JellyfinAvaliabilityChecker> _log;
|
||||
private readonly IHubContext<NotificationHub> _notification;
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
_log.LogInformation("Starting Jellyfin Availability Check");
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Availability Checker Started");
|
||||
await ProcessMovies();
|
||||
await ProcessTv();
|
||||
|
||||
_log.LogInformation("Finished Jellyfin Availability Check");
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Availability Checker Finished");
|
||||
}
|
||||
|
||||
private async Task ProcessMovies()
|
||||
{
|
||||
var movies = _movieRepo.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available);
|
||||
|
||||
foreach (var movie in movies)
|
||||
{
|
||||
JellyfinContent jellyfinContent = null;
|
||||
if (movie.TheMovieDbId > 0)
|
||||
{
|
||||
jellyfinContent = await _repo.GetByTheMovieDbId(movie.TheMovieDbId.ToString());
|
||||
}
|
||||
else if(movie.ImdbId.HasValue())
|
||||
{
|
||||
jellyfinContent = await _repo.GetByImdbId(movie.ImdbId);
|
||||
}
|
||||
|
||||
if (jellyfinContent == null)
|
||||
{
|
||||
// We don't have this yet
|
||||
continue;
|
||||
}
|
||||
|
||||
_log.LogInformation("We have found the request {0} on Jellyfin, sending the notification", movie?.Title ?? string.Empty);
|
||||
|
||||
movie.Available = true;
|
||||
movie.MarkedAsAvailable = DateTime.Now;
|
||||
if (movie.Available)
|
||||
{
|
||||
var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty;
|
||||
|
||||
_log.LogDebug("MovieId: {0}, RequestUser: {1}", movie.Id, recipient);
|
||||
|
||||
await _notificationService.Notify(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.RequestAvailable,
|
||||
RequestId = movie.Id,
|
||||
RequestType = RequestType.Movie,
|
||||
Recipient = recipient,
|
||||
});
|
||||
}
|
||||
}
|
||||
await _movieRepo.Save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// TODO This is EXCATLY the same as the PlexAvailabilityChecker. Refactor Please future Jamie
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task ProcessTv()
|
||||
{
|
||||
var tv = _tvRepo.GetChild().Where(x => !x.Available);
|
||||
var jellyfinEpisodes = _repo.GetAllEpisodes().Include(x => x.Series);
|
||||
|
||||
foreach (var child in tv)
|
||||
{
|
||||
|
||||
var useImdb = false;
|
||||
var useTvDb = false;
|
||||
if (child.ParentRequest.ImdbId.HasValue())
|
||||
{
|
||||
useImdb = true;
|
||||
}
|
||||
|
||||
if (child.ParentRequest.TvDbId.ToString().HasValue())
|
||||
{
|
||||
useTvDb = true;
|
||||
}
|
||||
|
||||
var tvDbId = child.ParentRequest.TvDbId;
|
||||
var imdbId = child.ParentRequest.ImdbId;
|
||||
IQueryable<JellyfinEpisode> seriesEpisodes = null;
|
||||
if (useImdb)
|
||||
{
|
||||
seriesEpisodes = jellyfinEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
|
||||
}
|
||||
|
||||
if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()))
|
||||
{
|
||||
seriesEpisodes = jellyfinEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
|
||||
}
|
||||
|
||||
if (seriesEpisodes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!seriesEpisodes.Any())
|
||||
{
|
||||
// Let's try and match the series by name
|
||||
seriesEpisodes = jellyfinEpisodes.Where(x =>
|
||||
x.Series.Title == child.Title);
|
||||
}
|
||||
|
||||
foreach (var season in child.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
if (episode.Available)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var foundEp = await seriesEpisodes.FirstOrDefaultAsync(
|
||||
x => x.EpisodeNumber == episode.EpisodeNumber &&
|
||||
x.SeasonNumber == episode.Season.SeasonNumber);
|
||||
|
||||
if (foundEp != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if all of the episodes in all seasons are available for this request
|
||||
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
|
||||
if (allAvailable)
|
||||
{
|
||||
// We have fulfulled this request!
|
||||
child.Available = true;
|
||||
child.MarkedAsAvailable = DateTime.Now;
|
||||
await _notificationService.Notify(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.RequestAvailable,
|
||||
RequestId = child.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
Recipient = child.RequestedUser.Email
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await _tvRepo.Save();
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
231
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs
Normal file
231
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs
Normal file
|
@ -0,0 +1,231 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Quartz;
|
||||
using JellyfinMediaType = Ombi.Store.Entities.JellyfinMediaType;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public class JellyfinContentSync : IJellyfinContentSync
|
||||
{
|
||||
public JellyfinContentSync(ISettingsService<JellyfinSettings> settings, IJellyfinApiFactory api, ILogger<JellyfinContentSync> logger,
|
||||
IJellyfinContentRepository repo, IHubContext<NotificationHub> notification)
|
||||
{
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
_apiFactory = api;
|
||||
_repo = repo;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
private readonly ILogger<JellyfinContentSync> _logger;
|
||||
private readonly ISettingsService<JellyfinSettings> _settings;
|
||||
private readonly IJellyfinApiFactory _apiFactory;
|
||||
private readonly IJellyfinContentRepository _repo;
|
||||
private readonly IHubContext<NotificationHub> _notification;
|
||||
private IJellyfinApi Api { get; set; }
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
var jellyfinSettings = await _settings.GetSettingsAsync();
|
||||
if (!jellyfinSettings.Enable)
|
||||
return;
|
||||
|
||||
Api = _apiFactory.CreateClient(jellyfinSettings);
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Content Sync Started");
|
||||
|
||||
foreach (var server in jellyfinSettings.Servers)
|
||||
{
|
||||
try
|
||||
{
|
||||
await StartServerCache(server, jellyfinSettings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Content Sync Failed");
|
||||
_logger.LogError(e, "Exception when caching Jellyfin for server {0}", server.Name);
|
||||
}
|
||||
}
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Content Sync Finished");
|
||||
// Episodes
|
||||
|
||||
await OmbiQuartz.TriggerJob(nameof(IJellyfinEpisodeSync), "Jellyfin");
|
||||
}
|
||||
|
||||
|
||||
private async Task StartServerCache(JellyfinServers server, JellyfinSettings settings)
|
||||
{
|
||||
if (!ValidateSettings(server))
|
||||
return;
|
||||
|
||||
//await _repo.ExecuteSql("DELETE FROM JellyfinEpisode");
|
||||
//await _repo.ExecuteSql("DELETE FROM JellyfinContent");
|
||||
|
||||
var movies = await Api.GetAllMovies(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
|
||||
var totalCount = movies.TotalRecordCount;
|
||||
var processed = 1;
|
||||
|
||||
var mediaToAdd = new HashSet<JellyfinContent>();
|
||||
|
||||
while (processed < totalCount)
|
||||
{
|
||||
foreach (var movie in movies.Items)
|
||||
{
|
||||
if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var movieInfo =
|
||||
await Api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri);
|
||||
foreach (var item in movieInfo.Items)
|
||||
{
|
||||
await ProcessMovies(item, mediaToAdd, server);
|
||||
}
|
||||
|
||||
processed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
processed++;
|
||||
// Regular movie
|
||||
await ProcessMovies(movie, mediaToAdd, server);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next batch
|
||||
movies = await Api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
|
||||
await _repo.AddRange(mediaToAdd);
|
||||
mediaToAdd.Clear();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TV Time
|
||||
var tv = await Api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
|
||||
var totalTv = tv.TotalRecordCount;
|
||||
processed = 1;
|
||||
while (processed < totalTv)
|
||||
{
|
||||
foreach (var tvShow in tv.Items)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
processed++;
|
||||
if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb))
|
||||
{
|
||||
_logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
var existingTv = await _repo.GetByJellyfinId(tvShow.Id);
|
||||
if (existingTv == null)
|
||||
{
|
||||
_logger.LogDebug("Adding new TV Show {0}", tvShow.Name);
|
||||
mediaToAdd.Add(new JellyfinContent
|
||||
{
|
||||
TvDbId = tvShow.ProviderIds?.Tvdb,
|
||||
ImdbId = tvShow.ProviderIds?.Imdb,
|
||||
TheMovieDbId = tvShow.ProviderIds?.Tmdb,
|
||||
Title = tvShow.Name,
|
||||
Type = JellyfinMediaType.Series,
|
||||
JellyfinId = tvShow.Id,
|
||||
Url = JellyfinHelper.GetJellyfinMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
|
||||
AddedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("We already have TV Show {0}", tvShow.Name);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
// Get the next batch
|
||||
tv = await Api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
|
||||
await _repo.AddRange(mediaToAdd);
|
||||
mediaToAdd.Clear();
|
||||
}
|
||||
|
||||
if (mediaToAdd.Any())
|
||||
await _repo.AddRange(mediaToAdd);
|
||||
}
|
||||
|
||||
private async Task ProcessMovies(JellyfinMovie movieInfo, ICollection<JellyfinContent> content, JellyfinServers server)
|
||||
{
|
||||
// Check if it exists
|
||||
var existingMovie = await _repo.GetByJellyfinId(movieInfo.Id);
|
||||
var alreadyGoingToAdd = content.Any(x => x.JellyfinId == movieInfo.Id);
|
||||
if (existingMovie == null && !alreadyGoingToAdd)
|
||||
{
|
||||
_logger.LogDebug("Adding new movie {0}", movieInfo.Name);
|
||||
content.Add(new JellyfinContent
|
||||
{
|
||||
ImdbId = movieInfo.ProviderIds.Imdb,
|
||||
TheMovieDbId = movieInfo.ProviderIds?.Tmdb,
|
||||
Title = movieInfo.Name,
|
||||
Type = JellyfinMediaType.Movie,
|
||||
JellyfinId = movieInfo.Id,
|
||||
Url = JellyfinHelper.GetJellyfinMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname),
|
||||
AddedAt = DateTime.UtcNow,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have this
|
||||
_logger.LogDebug("We already have movie {0}", movieInfo.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateSettings(JellyfinServers server)
|
||||
{
|
||||
if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey))
|
||||
{
|
||||
_logger.LogInformation(LoggingEvents.JellyfinContentCacher, $"Server {server?.Name} is not configured correctly");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
//_settings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
181
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs
Normal file
181
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs
Normal file
|
@ -0,0 +1,181 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinEpisodeCacher.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Quartz;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public class JellyfinEpisodeSync : IJellyfinEpisodeSync
|
||||
{
|
||||
public JellyfinEpisodeSync(ISettingsService<JellyfinSettings> s, IJellyfinApiFactory api, ILogger<JellyfinEpisodeSync> l, IJellyfinContentRepository repo
|
||||
, IHubContext<NotificationHub> notification)
|
||||
{
|
||||
_apiFactory = api;
|
||||
_logger = l;
|
||||
_settings = s;
|
||||
_repo = repo;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
private readonly ISettingsService<JellyfinSettings> _settings;
|
||||
private readonly IJellyfinApiFactory _apiFactory;
|
||||
private readonly ILogger<JellyfinEpisodeSync> _logger;
|
||||
private readonly IJellyfinContentRepository _repo;
|
||||
private readonly IHubContext<NotificationHub> _notification;
|
||||
private IJellyfinApi Api { get; set; }
|
||||
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
var settings = await _settings.GetSettingsAsync();
|
||||
|
||||
Api = _apiFactory.CreateClient(settings);
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Episode Sync Started");
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
await CacheEpisodes(server);
|
||||
}
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin Episode Sync Finished");
|
||||
_logger.LogInformation("Jellyfin Episode Sync Finished - Triggering Metadata refresh");
|
||||
await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System");
|
||||
}
|
||||
|
||||
private async Task CacheEpisodes(JellyfinServers server)
|
||||
{
|
||||
var allEpisodes = await Api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
|
||||
var total = allEpisodes.TotalRecordCount;
|
||||
var processed = 1;
|
||||
var epToAdd = new HashSet<JellyfinEpisode>();
|
||||
while (processed < total)
|
||||
{
|
||||
foreach (var ep in allEpisodes.Items)
|
||||
{
|
||||
processed++;
|
||||
|
||||
if (ep.LocationType?.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase) ?? false)
|
||||
{
|
||||
// For some reason Jellyfin is not respecting the `IsVirtualItem` field.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Let's make sure we have the parent request, stop those pesky forign key errors,
|
||||
// Damn me having data integrity
|
||||
var parent = await _repo.GetByJellyfinId(ep.SeriesId);
|
||||
if (parent == null)
|
||||
{
|
||||
_logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this",
|
||||
ep.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
var existingEpisode = await _repo.GetEpisodeByJellyfinId(ep.Id);
|
||||
// Make sure it's not in the hashset too
|
||||
var existingInList = epToAdd.Any(x => x.JellyfinId == ep.Id);
|
||||
|
||||
if (existingEpisode == null && !existingInList)
|
||||
{
|
||||
_logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName);
|
||||
// add it
|
||||
epToAdd.Add(new JellyfinEpisode
|
||||
{
|
||||
JellyfinId = ep.Id,
|
||||
EpisodeNumber = ep.IndexNumber,
|
||||
SeasonNumber = ep.ParentIndexNumber,
|
||||
ParentId = ep.SeriesId,
|
||||
TvDbId = ep.ProviderIds.Tvdb,
|
||||
TheMovieDbId = ep.ProviderIds.Tmdb,
|
||||
ImdbId = ep.ProviderIds.Imdb,
|
||||
Title = ep.Name,
|
||||
AddedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
if (ep.IndexNumberEnd.HasValue && ep.IndexNumberEnd.Value != ep.IndexNumber)
|
||||
{
|
||||
epToAdd.Add(new JellyfinEpisode
|
||||
{
|
||||
JellyfinId = ep.Id,
|
||||
EpisodeNumber = ep.IndexNumberEnd.Value,
|
||||
SeasonNumber = ep.ParentIndexNumber,
|
||||
ParentId = ep.SeriesId,
|
||||
TvDbId = ep.ProviderIds.Tvdb,
|
||||
TheMovieDbId = ep.ProviderIds.Tmdb,
|
||||
ImdbId = ep.ProviderIds.Imdb,
|
||||
Title = ep.Name,
|
||||
AddedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _repo.AddRange(epToAdd);
|
||||
epToAdd.Clear();
|
||||
allEpisodes = await Api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
|
||||
}
|
||||
|
||||
if (epToAdd.Any())
|
||||
{
|
||||
await _repo.AddRange(epToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
//_settings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
173
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs
Normal file
173
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs
Normal file
|
@ -0,0 +1,173 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinUserImporter.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public class JellyfinUserImporter : IJellyfinUserImporter
|
||||
{
|
||||
public JellyfinUserImporter(IJellyfinApiFactory api, UserManager<OmbiUser> um, ILogger<JellyfinUserImporter> log,
|
||||
ISettingsService<JellyfinSettings> jellyfinSettings, ISettingsService<UserManagementSettings> ums, IHubContext<NotificationHub> notification)
|
||||
{
|
||||
_apiFactory = api;
|
||||
_userManager = um;
|
||||
_log = log;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_userManagementSettings = ums;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
private readonly IJellyfinApiFactory _apiFactory;
|
||||
private readonly UserManager<OmbiUser> _userManager;
|
||||
private readonly ILogger<JellyfinUserImporter> _log;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
|
||||
private readonly IHubContext<NotificationHub> _notification;
|
||||
private IJellyfinApi Api { get; set; }
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
var userManagementSettings = await _userManagementSettings.GetSettingsAsync();
|
||||
if (!userManagementSettings.ImportJellyfinUsers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var settings = await _jellyfinSettings.GetSettingsAsync();
|
||||
if (!settings.Enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Api = _apiFactory.CreateClient(settings);
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, $"Jellyfin User Importer Started");
|
||||
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.JellyfinUser).ToListAsync();
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
if (string.IsNullOrEmpty(server.ApiKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var jellyfinUsers = await Api.GetUsers(server.FullUri, server.ApiKey);
|
||||
foreach (var jellyfinUser in jellyfinUsers)
|
||||
{
|
||||
// Check if we should import this user
|
||||
if (userManagementSettings.BannedJellyfinUserIds.Contains(jellyfinUser.Id))
|
||||
{
|
||||
// Do not import these, they are not allowed into the country.
|
||||
continue;
|
||||
}
|
||||
// Check if this Jellyfin User already exists
|
||||
var existingJellyfinUser = allUsers.FirstOrDefault(x => x.ProviderUserId == jellyfinUser.Id);
|
||||
if (existingJellyfinUser == null)
|
||||
{
|
||||
|
||||
if (!jellyfinUser.Name.HasValue())
|
||||
{
|
||||
_log.LogInformation("Could not create Jellyfin user since the have no username, JellyfinUserId: {0}", jellyfinUser.Id);
|
||||
continue;
|
||||
}
|
||||
// Create this users
|
||||
var newUser = new OmbiUser
|
||||
{
|
||||
UserName = jellyfinUser.Name,
|
||||
UserType = UserType.JellyfinUser,
|
||||
ProviderUserId = jellyfinUser.Id,
|
||||
MovieRequestLimit = userManagementSettings.MovieRequestLimit,
|
||||
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit
|
||||
};
|
||||
_log.LogInformation("Creating Jellyfin user {0}", newUser.UserName);
|
||||
var result = await _userManager.CreateAsync(newUser);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
foreach (var identityError in result.Errors)
|
||||
{
|
||||
_log.LogError(LoggingEvents.JellyfinUserImporter, identityError.Description);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (userManagementSettings.DefaultRoles.Any())
|
||||
{
|
||||
foreach (var defaultRole in userManagementSettings.DefaultRoles)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(newUser, defaultRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do we need to update this user?
|
||||
existingJellyfinUser.UserName = jellyfinUser.Name;
|
||||
|
||||
await _userManager.UpdateAsync(existingJellyfinUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Jellyfin User Importer Finished");
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_userManager?.Dispose();
|
||||
//_jellyfinSettings?.Dispose();
|
||||
//_userManagementSettings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Jellyfin;
|
||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||
using Ombi.Store.Repository;
|
||||
using Quartz;
|
||||
|
@ -14,12 +15,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
|
||||
{
|
||||
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
|
||||
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo)
|
||||
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IJellyfinContentRepository jellyfinRepo)
|
||||
{
|
||||
_settings = s;
|
||||
_log = log;
|
||||
_plexRepo = plexRepo;
|
||||
_embyRepo = embyRepo;
|
||||
_jellyfinRepo = jellyfinRepo;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
|
@ -27,6 +29,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
private readonly ILogger _log;
|
||||
private readonly IPlexContentRepository _plexRepo;
|
||||
private readonly IEmbyContentRepository _embyRepo;
|
||||
private readonly IJellyfinContentRepository _jellyfinRepo;
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
|
@ -34,6 +37,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
await RemovePlexData();
|
||||
await RemoveEmbyData();
|
||||
await RemoveJellyfinData();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -64,6 +68,28 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
|
||||
private async Task RemoveJellyfinData()
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = await _settings.GetSettingsAsync();
|
||||
if (!s.Enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const string episodeSQL = "DELETE FROM JellyfinEpisode";
|
||||
const string mainSql = "DELETE FROM JellyfinContent";
|
||||
await _jellyfinRepo.ExecuteSql(episodeSQL);
|
||||
await _jellyfinRepo.ExecuteSql(mainSql);
|
||||
|
||||
await OmbiQuartz.TriggerJob(nameof(IJellyfinContentSync), "Jellyfin");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Jellyfin Data Failed");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemovePlexData()
|
||||
{
|
||||
try
|
||||
|
@ -108,4 +134,4 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,16 +38,17 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
public class NewsletterJob : HtmlTemplateGenerator, INewsletterJob
|
||||
{
|
||||
public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> addedLog,
|
||||
public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IJellyfinContentRepository jellyfin, IRepository<RecentlyAddedLog> addedLog,
|
||||
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
||||
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
||||
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
||||
ILidarrApi lidarrApi, IExternalRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings,
|
||||
ISettingsService<OmbiSettings> ombiSettings, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings
|
||||
, IHubContext<NotificationHub> notification, IRefreshMetadata refreshMetadata)
|
||||
ISettingsService<OmbiSettings> ombiSettings, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings, ISettingsService<JellyfinSettings> jellyfinSettings,
|
||||
IHubContext<NotificationHub> notification, IRefreshMetadata refreshMetadata)
|
||||
{
|
||||
_plex = plex;
|
||||
_emby = emby;
|
||||
_jellyfin = jellyfin;
|
||||
_recentlyAddedLog = addedLog;
|
||||
_movieApi = movieApi;
|
||||
_tvApi = tvApi;
|
||||
|
@ -64,6 +65,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_ombiSettings = ombiSettings;
|
||||
_plexSettings = plexSettings;
|
||||
_embySettings = embySettings;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_notification = notification;
|
||||
_ombiSettings.ClearCache();
|
||||
_plexSettings.ClearCache();
|
||||
|
@ -74,6 +76,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
|
||||
private readonly IPlexContentRepository _plex;
|
||||
private readonly IEmbyContentRepository _emby;
|
||||
private readonly IJellyfinContentRepository _jellyfin;
|
||||
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||
private readonly IMovieDbApi _movieApi;
|
||||
private readonly ITvMazeApi _tvApi;
|
||||
|
@ -90,6 +93,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly IHubContext<NotificationHub> _notification;
|
||||
private readonly IRefreshMetadata _refreshMetadata;
|
||||
|
||||
|
@ -123,36 +127,46 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
// Get the Content
|
||||
var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking();
|
||||
var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking();
|
||||
var jellyfinContent = _jellyfin.GetAll().Include(x => x.Episodes).AsNoTracking();
|
||||
var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking().ToList().Where(x => x.FullyAvailable);
|
||||
|
||||
var addedLog = _recentlyAddedLog.GetAll();
|
||||
var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId).ToHashSet();
|
||||
var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId).ToHashSet();
|
||||
var addedJellyfinMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Jellyfin && x.ContentType == ContentType.Parent).Select(x => x.ContentId).ToHashSet();
|
||||
var addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId).ToHashSet();
|
||||
|
||||
var addedPlexEpisodesLogIds =
|
||||
addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode);
|
||||
var addedEmbyEpisodesLogIds =
|
||||
addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode);
|
||||
var addedJellyfinEpisodesLogIds =
|
||||
addedLog.Where(x => x.Type == RecentlyAddedType.Jellyfin && x.ContentType == ContentType.Episode);
|
||||
|
||||
|
||||
// Filter out the ones that we haven't sent yet
|
||||
var plexContentLocalDataset = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
||||
var embyContentLocalDataset = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
||||
var jellyfinContentLocalDataset = jellyfinContent.Where(x => x.Type == JellyfinMediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
||||
var plexContentMoviesToSend = plexContentLocalDataset.Where(x => !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet();
|
||||
var embyContentMoviesToSend = embyContentLocalDataset.Where(x => !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet();
|
||||
var jellyfinContentMoviesToSend = jellyfinContentLocalDataset.Where(x => !addedJellyfinMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet();
|
||||
var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet();
|
||||
_log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count());
|
||||
_log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count());
|
||||
_log.LogInformation("Jellyfin Movies to send: {0}", jellyfinContentMoviesToSend.Count());
|
||||
_log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count());
|
||||
|
||||
// Find the movies that do not yet have MovieDbIds
|
||||
var needsMovieDbPlex = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
||||
var needsMovieDbEmby = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
||||
var needsMovieDbJellyfin = jellyfinContent.Where(x => x.Type == JellyfinMediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
||||
var newPlexMovies = await GetMoviesWithoutId(addedPlexMovieLogIds, needsMovieDbPlex);
|
||||
var newEmbyMovies = await GetMoviesWithoutId(addedEmbyMoviesLogIds, needsMovieDbEmby);
|
||||
var newJellyfinMovies = await GetMoviesWithoutId(addedJellyfinMoviesLogIds, needsMovieDbJellyfin);
|
||||
plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet();
|
||||
embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet();
|
||||
jellyfinContentMoviesToSend = jellyfinContentMoviesToSend.Union(newJellyfinMovies).ToHashSet();
|
||||
|
||||
plexContentMoviesToSend = plexContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet();
|
||||
embyContentMoviesToSend = embyContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet();
|
||||
|
@ -161,24 +175,30 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds);
|
||||
var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(),
|
||||
addedEmbyEpisodesLogIds);
|
||||
var jellyfinEpisodesToSend = FilterJellyfinEpisodes(_jellyfin.GetAllEpisodes().Include(x => x.Series).AsNoTracking(),
|
||||
addedJellyfinEpisodesLogIds);
|
||||
|
||||
_log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count());
|
||||
_log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.Count());
|
||||
_log.LogInformation("Jellyfin Episodes to send: {0}", jellyfinEpisodesToSend.Count());
|
||||
var plexSettings = await _plexSettings.GetSettingsAsync();
|
||||
var embySettings = await _embySettings.GetSettingsAsync();
|
||||
var jellyfinSettings = await _jellyfinSettings.GetSettingsAsync();
|
||||
var body = string.Empty;
|
||||
if (test)
|
||||
{
|
||||
var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10);
|
||||
var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10);
|
||||
var jellyfinm = jellyfinContent.Where(x => x.Type == JellyfinMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10);
|
||||
var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet();
|
||||
var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
var jellyfint = _jellyfin.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings, embySettings, plexSettings);
|
||||
body = await BuildHtml(plexm, embym, jellyfinm, plext, embyt, jellyfint, lidarr, settings, embySettings, jellyfinSettings, plexSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
body = await BuildHtml(plexContentMoviesToSend.AsQueryable(), embyContentMoviesToSend.AsQueryable(), plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings, embySettings, plexSettings);
|
||||
body = await BuildHtml(plexContentMoviesToSend.AsQueryable(), embyContentMoviesToSend.AsQueryable(), jellyfinContentMoviesToSend.AsQueryable(), plexEpisodesToSend, embyEpisodesToSend, jellyfinEpisodesToSend, lidarrContentAlbumsToSend, settings, embySettings, jellyfinSettings, plexSettings);
|
||||
if (body.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
|
@ -285,6 +305,34 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
SeasonNumber = p.SeasonNumber
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var e in jellyfinContentMoviesToSend)
|
||||
{
|
||||
if (e.Type == JellyfinMediaType.Movie)
|
||||
{
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Jellyfin,
|
||||
ContentType = ContentType.Parent,
|
||||
ContentId = StringHelper.IntParseLinq(e.TheMovieDbId),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var p in jellyfinEpisodesToSend)
|
||||
{
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Jellyfin,
|
||||
ContentType = ContentType.Episode,
|
||||
ContentId = StringHelper.IntParseLinq(p.Series.TvDbId),
|
||||
EpisodeNumber = p.EpisodeNumber,
|
||||
SeasonNumber = p.SeasonNumber
|
||||
});
|
||||
}
|
||||
|
||||
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||
}
|
||||
else
|
||||
|
@ -349,6 +397,20 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
return result.ToHashSet();
|
||||
}
|
||||
|
||||
private async Task<HashSet<JellyfinContent>> GetMoviesWithoutId(HashSet<int> addedMovieLogIds, HashSet<JellyfinContent> needsMovieDbJellyfin)
|
||||
{
|
||||
foreach (var movie in needsMovieDbJellyfin)
|
||||
{
|
||||
var id = await _refreshMetadata.GetTheMovieDbId(false, true, null, movie.ImdbId, movie.Title, true);
|
||||
movie.TheMovieDbId = id.ToString();
|
||||
}
|
||||
|
||||
var result = needsMovieDbJellyfin.Where(x => x.HasTheMovieDb && !addedMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId)));
|
||||
await UpdateTheMovieDbId(result);
|
||||
// Filter them out now
|
||||
return result.ToHashSet();
|
||||
}
|
||||
|
||||
private async Task UpdateTheMovieDbId(IEnumerable<PlexServerContent> content)
|
||||
{
|
||||
foreach (var movie in content)
|
||||
|
@ -379,6 +441,21 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
await _plex.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task UpdateTheMovieDbId(IEnumerable<JellyfinContent> content)
|
||||
{
|
||||
foreach (var movie in content)
|
||||
{
|
||||
if (!movie.HasTheMovieDb)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var entity = await _jellyfin.Find(movie.Id);
|
||||
entity.TheMovieDbId = movie.TheMovieDbId;
|
||||
_jellyfin.UpdateWithoutSave(entity);
|
||||
}
|
||||
await _plex.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
var newsletterSettings = await _newsletterSettings.GetSettingsAsync();
|
||||
|
@ -419,6 +496,23 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
return itemsToReturn;
|
||||
}
|
||||
|
||||
private HashSet<JellyfinEpisode> FilterJellyfinEpisodes(IEnumerable<JellyfinEpisode> source, IQueryable<RecentlyAddedLog> recentlyAdded)
|
||||
{
|
||||
var itemsToReturn = new HashSet<JellyfinEpisode>();
|
||||
foreach (var ep in source.Where(x => x.Series.HasTvDb))
|
||||
{
|
||||
var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId);
|
||||
if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
itemsToReturn.Add(ep);
|
||||
}
|
||||
|
||||
return itemsToReturn;
|
||||
}
|
||||
|
||||
private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings)
|
||||
{
|
||||
var resolver = new NotificationMessageResolver();
|
||||
|
@ -429,8 +523,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
return resolver.ParseMessage(template, curlys);
|
||||
}
|
||||
|
||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
|
||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings, EmbySettings embySettings,
|
||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend, IQueryable<JellyfinContent> jellyfinContentToSend,
|
||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<JellyfinEpisode> jellyfinEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings, EmbySettings embySettings, JellyfinSettings jellyfinSettings,
|
||||
PlexSettings plexSettings)
|
||||
{
|
||||
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
||||
|
@ -438,6 +532,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
|
||||
var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||
var embyMovies = embyContentToSend.Where(x => x.Type == EmbyMediaType.Movie);
|
||||
var jellyfinMovies = jellyfinContentToSend.Where(x => x.Type == JellyfinMediaType.Movie);
|
||||
if ((plexMovies.Any() || embyMovies.Any()) && !settings.DisableMovies)
|
||||
{
|
||||
sb.Append("<h1 style=\"text-align: center; max-width: 1042px;\">New Movies</h1><br /><br />");
|
||||
|
@ -457,6 +552,11 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
|
||||
}
|
||||
|
||||
if (jellyfinSettings.Enable)
|
||||
{
|
||||
await ProcessJellyfinMovies(jellyfinMovies, sb, ombiSettings.DefaultLanguageCode, jellyfinSettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
|
||||
}
|
||||
|
||||
sb.Append("</tr>");
|
||||
sb.Append("</table>");
|
||||
sb.Append("</td>");
|
||||
|
@ -464,7 +564,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
sb.Append("</table>");
|
||||
}
|
||||
|
||||
if ((plexEpisodes.Any() || embyEp.Any()) && !settings.DisableTv)
|
||||
if ((plexEpisodes.Any() || embyEp.Any()) || jellyfinEp.Any() && !settings.DisableTv)
|
||||
{
|
||||
sb.Append("<br /><br /><h1 style=\"text-align: center; max-width: 1042px;\">New TV</h1><br /><br />");
|
||||
sb.Append(
|
||||
|
@ -483,6 +583,11 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
await ProcessEmbyTv(embyEp, sb, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
|
||||
}
|
||||
|
||||
if (jellyfinSettings.Enable)
|
||||
{
|
||||
await ProcessJellyfinTv(jellyfinEp, sb, jellyfinSettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
|
||||
}
|
||||
|
||||
sb.Append("</tr>");
|
||||
sb.Append("</table>");
|
||||
sb.Append("</td>");
|
||||
|
@ -638,6 +743,59 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ProcessJellyfinMovies(IQueryable<JellyfinContent> embyContent, StringBuilder sb, string defaultLangaugeCode, string customUrl)
|
||||
{
|
||||
int count = 0;
|
||||
var ordered = embyContent.OrderByDescending(x => x.AddedAt);
|
||||
foreach (var content in ordered)
|
||||
{
|
||||
var theMovieDbId = content.TheMovieDbId;
|
||||
if (!content.TheMovieDbId.HasValue())
|
||||
{
|
||||
var imdbId = content.ImdbId;
|
||||
var findResult = await _movieApi.Find(imdbId, ExternalSource.imdb_id);
|
||||
var result = findResult.movie_results?.FirstOrDefault();
|
||||
if (result == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
theMovieDbId = result.id.ToString();
|
||||
}
|
||||
|
||||
var mediaurl = content.Url;
|
||||
if (customUrl.HasValue())
|
||||
{
|
||||
mediaurl = customUrl;
|
||||
}
|
||||
var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId), defaultLangaugeCode);
|
||||
if (info == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
CreateMovieHtmlContent(sb, info, mediaurl);
|
||||
count += 1;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(e, "Error when processing Jellyfin Movies {0}", info.Title);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndLoopHtml(sb);
|
||||
}
|
||||
|
||||
if (count == 2)
|
||||
{
|
||||
count = 0;
|
||||
sb.Append("</tr>");
|
||||
sb.Append("<tr>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMovieHtmlContent(StringBuilder sb, MovieResponseDto info, string mediaurl)
|
||||
{
|
||||
AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}");
|
||||
|
@ -981,6 +1139,129 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ProcessJellyfinTv(HashSet<JellyfinEpisode> embyContent, StringBuilder sb, string serverUrl)
|
||||
{
|
||||
var series = new List<JellyfinContent>();
|
||||
foreach (var episode in embyContent)
|
||||
{
|
||||
var alreadyAdded = series.FirstOrDefault(x => x.JellyfinId == episode.Series.JellyfinId);
|
||||
if (alreadyAdded != null)
|
||||
{
|
||||
alreadyAdded.Episodes.Add(episode);
|
||||
}
|
||||
else
|
||||
{
|
||||
episode.Series.Episodes = new List<JellyfinEpisode>
|
||||
{
|
||||
episode
|
||||
};
|
||||
series.Add(episode.Series);
|
||||
}
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
var orderedTv = series.OrderByDescending(x => x.AddedAt);
|
||||
foreach (var t in orderedTv)
|
||||
{
|
||||
if (!t.TvDbId.HasValue())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int.TryParse(t.TvDbId, out var tvdbId);
|
||||
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
|
||||
if (info == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var banner = info.image?.original;
|
||||
if (!string.IsNullOrEmpty(banner))
|
||||
{
|
||||
banner = banner.ToHttpsUrl(); // Always use the Https banners
|
||||
}
|
||||
|
||||
var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId);
|
||||
if (tvInfo != null && tvInfo.backdrop_path.HasValue())
|
||||
{
|
||||
|
||||
AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/");
|
||||
}
|
||||
AddPosterInsideTable(sb, banner);
|
||||
AddMediaServerUrl(sb, serverUrl.HasValue() ? serverUrl : t.Url, banner);
|
||||
AddInfoTable(sb);
|
||||
|
||||
var title = "";
|
||||
if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4)
|
||||
{
|
||||
title = $"{t.Title} ({info.premiered.Remove(4)})";
|
||||
}
|
||||
else
|
||||
{
|
||||
title = $"{t.Title}";
|
||||
}
|
||||
AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title);
|
||||
|
||||
// Group by the season number
|
||||
var results = t.Episodes?.GroupBy(p => p.SeasonNumber,
|
||||
(key, g) => new
|
||||
{
|
||||
SeasonNumber = key,
|
||||
Episodes = g.ToList(),
|
||||
EpisodeAirDate = tvInfo?.seasons?.Where(x => x.season_number == key)?.Select(x => x.air_date).FirstOrDefault()
|
||||
}
|
||||
);
|
||||
|
||||
// Group the episodes
|
||||
var finalsb = new StringBuilder();
|
||||
foreach (var epInformation in results.OrderBy(x => x.SeasonNumber))
|
||||
{
|
||||
var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList();
|
||||
var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber));
|
||||
var episodeAirDate = epInformation.EpisodeAirDate;
|
||||
finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString} {episodeAirDate}");
|
||||
finalsb.Append("<br />");
|
||||
}
|
||||
|
||||
var summary = info.summary;
|
||||
if (summary.Length > 280)
|
||||
{
|
||||
summary = summary.Remove(280);
|
||||
summary = summary + "...</p>";
|
||||
}
|
||||
AddTvParagraph(sb, finalsb.ToString(), summary);
|
||||
|
||||
if (info.genres.Any())
|
||||
{
|
||||
AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(e, "Error when processing Jellyfin TV {0}", t.Title);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndLoopHtml(sb);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if (count == 2)
|
||||
{
|
||||
count = 0;
|
||||
sb.Append("</tr>");
|
||||
sb.Append("<tr>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EndLoopHtml(StringBuilder sb)
|
||||
{
|
||||
//NOTE: BR have to be in TD's as per html spec or it will be put outside of the table...
|
||||
|
@ -1040,4 +1321,4 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.SignalR;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Api.TvMaze;
|
||||
|
@ -14,6 +15,7 @@ using Ombi.Core.Settings.Models.External;
|
|||
using Ombi.Helpers;
|
||||
using Ombi.Hubs;
|
||||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Jellyfin;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
@ -23,31 +25,41 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
public class RefreshMetadata : IRefreshMetadata
|
||||
{
|
||||
public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo,
|
||||
public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IJellyfinContentRepository jellyfinRepo,
|
||||
ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings,
|
||||
IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IEmbyApiFactory embyApi, IHubContext<NotificationHub> notification)
|
||||
IMovieDbApi movieApi,
|
||||
ISettingsService<EmbySettings> embySettings, IEmbyApiFactory embyApi,
|
||||
ISettingsService<JellyfinSettings> jellyfinSettings, IJellyfinApiFactory jellyfinApi,
|
||||
IHubContext<NotificationHub> notification)
|
||||
{
|
||||
_plexRepo = plexRepo;
|
||||
_embyRepo = embyRepo;
|
||||
_jellyfinRepo = jellyfinRepo;
|
||||
_log = log;
|
||||
_movieApi = movieApi;
|
||||
_tvApi = tvApi;
|
||||
_plexSettings = plexSettings;
|
||||
_embySettings = embySettings;
|
||||
_embyApiFactory = embyApi;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_jellyfinApiFactory = jellyfinApi;
|
||||
_notification = notification;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plexRepo;
|
||||
private readonly IEmbyContentRepository _embyRepo;
|
||||
private readonly IJellyfinContentRepository _jellyfinRepo;
|
||||
private readonly ILogger _log;
|
||||
private readonly IMovieDbApi _movieApi;
|
||||
private readonly ITvMazeApi _tvApi;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly IEmbyApiFactory _embyApiFactory;
|
||||
private readonly IJellyfinApiFactory _jellyfinApiFactory;
|
||||
private readonly IHubContext<NotificationHub> _notification;
|
||||
private IEmbyApi EmbyApi { get; set; }
|
||||
private IJellyfinApi JellyfinApi { get; set; }
|
||||
|
||||
public async Task Execute(IJobExecutionContext job)
|
||||
{
|
||||
|
@ -72,6 +84,14 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
|
||||
await OmbiQuartz.TriggerJob(nameof(IEmbyAvaliabilityChecker), "Emby");
|
||||
}
|
||||
|
||||
var jellyfinSettings = await _jellyfinSettings.GetSettingsAsync();
|
||||
if (jellyfinSettings.Enable)
|
||||
{
|
||||
await StartJellyfin(jellyfinSettings);
|
||||
|
||||
await OmbiQuartz.TriggerJob(nameof(IJellyfinAvaliabilityChecker), "Jellyfin");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -107,6 +127,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
await StartEmbyTv();
|
||||
}
|
||||
|
||||
private async Task StartJellyfin(JellyfinSettings s)
|
||||
{
|
||||
JellyfinApi = _jellyfinApiFactory.CreateClient(s);
|
||||
await StartJellyfinMovies(s);
|
||||
await StartJellyfinTv();
|
||||
}
|
||||
|
||||
private async Task StartPlexTv(List<PlexServerContent> allTv)
|
||||
{
|
||||
foreach (var show in allTv)
|
||||
|
@ -178,6 +205,41 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
|
||||
private async Task StartJellyfinTv()
|
||||
{
|
||||
var allTv = await _jellyfinRepo.GetAll().Where(x =>
|
||||
x.Type == JellyfinMediaType.Series && (x.TheMovieDbId == null || x.ImdbId == null || x.TvDbId == null)).ToListAsync();
|
||||
|
||||
foreach (var show in allTv)
|
||||
{
|
||||
var hasImdb = show.ImdbId.HasValue();
|
||||
var hasTheMovieDb = show.TheMovieDbId.HasValue();
|
||||
var hasTvDbId = show.TvDbId.HasValue();
|
||||
|
||||
if (!hasTheMovieDb)
|
||||
{
|
||||
var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title, false);
|
||||
show.TheMovieDbId = id;
|
||||
}
|
||||
|
||||
if (!hasImdb)
|
||||
{
|
||||
var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId);
|
||||
show.ImdbId = id;
|
||||
_jellyfinRepo.UpdateWithoutSave(show);
|
||||
}
|
||||
|
||||
if (!hasTvDbId)
|
||||
{
|
||||
var id = await GetTvDbId(hasTheMovieDb, hasImdb, show.TheMovieDbId, show.ImdbId, show.Title);
|
||||
show.TvDbId = id;
|
||||
_jellyfinRepo.UpdateWithoutSave(show);
|
||||
}
|
||||
|
||||
await _jellyfinRepo.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartPlexMovies(List<PlexServerContent> allMovies)
|
||||
{
|
||||
foreach (var movie in allMovies)
|
||||
|
@ -263,6 +325,61 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
|
||||
private async Task StartJellyfinMovies(JellyfinSettings settings)
|
||||
{
|
||||
var allMovies = await _jellyfinRepo.GetAll().Where(x =>
|
||||
x.Type == JellyfinMediaType.Movie && (x.TheMovieDbId == null || x.ImdbId == null)).ToListAsync();
|
||||
foreach (var movie in allMovies)
|
||||
{
|
||||
movie.ImdbId.HasValue();
|
||||
movie.TheMovieDbId.HasValue();
|
||||
// Movies don't really use TheTvDb
|
||||
|
||||
// Check if it even has 1 ID
|
||||
if (!movie.HasImdb && !movie.HasTheMovieDb)
|
||||
{
|
||||
// Ok this sucks,
|
||||
// The only think I can think that has happened is that we scanned Jellyfin before Jellyfin has got the metadata
|
||||
// So let's recheck jellyfin to see if they have got the metadata now
|
||||
//
|
||||
// Yeah your right that does suck - Future Jamie
|
||||
_log.LogInformation($"Movie {movie.Title} does not have a ImdbId or TheMovieDbId, so rechecking jellyfin");
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
_log.LogInformation($"Checking server {server.Name} for upto date metadata");
|
||||
var movieInfo = await JellyfinApi.GetMovieInformation(movie.JellyfinId, server.ApiKey, server.AdministratorId,
|
||||
server.FullUri);
|
||||
|
||||
if (movieInfo.ProviderIds?.Imdb.HasValue() ?? false)
|
||||
{
|
||||
movie.ImdbId = movieInfo.ProviderIds.Imdb;
|
||||
}
|
||||
|
||||
if (movieInfo.ProviderIds?.Tmdb.HasValue() ?? false)
|
||||
{
|
||||
movie.TheMovieDbId = movieInfo.ProviderIds.Tmdb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!movie.HasImdb)
|
||||
{
|
||||
var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty);
|
||||
movie.ImdbId = imdbId;
|
||||
_jellyfinRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
if (!movie.HasTheMovieDb)
|
||||
{
|
||||
var id = await GetTheMovieDbId(false, movie.HasImdb, string.Empty, movie.ImdbId, movie.Title, true);
|
||||
movie.TheMovieDbId = id;
|
||||
_jellyfinRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
|
||||
await _jellyfinRepo.SaveChangesAsync();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title, bool movie)
|
||||
{
|
||||
_log.LogInformation("The Media item {0} does not have a TheMovieDbId, searching for TheMovieDbId", title);
|
||||
|
@ -392,4 +509,4 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
|
@ -37,4 +38,4 @@
|
|||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -7,6 +7,7 @@ using Ombi.Helpers;
|
|||
using Ombi.Schedule.Jobs;
|
||||
using Ombi.Schedule.Jobs.Couchpotato;
|
||||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Jellyfin;
|
||||
using Ombi.Schedule.Jobs.Lidarr;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
|
@ -51,6 +52,7 @@ namespace Ombi.Schedule
|
|||
// Run configuration
|
||||
await AddPlex(s);
|
||||
await AddEmby(s);
|
||||
await AddJellyfin(s);
|
||||
await AddDvrApps(s);
|
||||
await AddSystem(s);
|
||||
await AddNotifications(s);
|
||||
|
@ -98,9 +100,18 @@ namespace Ombi.Schedule
|
|||
await OmbiQuartz.Instance.AddJob<IEmbyAvaliabilityChecker>(nameof(IEmbyAvaliabilityChecker), "Emby", null);
|
||||
await OmbiQuartz.Instance.AddJob<IEmbyUserImporter>(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s));
|
||||
}
|
||||
|
||||
private static async Task AddJellyfin(JobSettings s)
|
||||
{
|
||||
await OmbiQuartz.Instance.AddJob<IJellyfinContentSync>(nameof(IJellyfinContentSync), "Jellyfin", JobSettingsHelper.JellyfinContent(s));
|
||||
await OmbiQuartz.Instance.AddJob<IJellyfinEpisodeSync>(nameof(IJellyfinEpisodeSync), "Jellyfin", null);
|
||||
await OmbiQuartz.Instance.AddJob<IJellyfinAvaliabilityChecker>(nameof(IJellyfinAvaliabilityChecker), "Jellyfin", null);
|
||||
await OmbiQuartz.Instance.AddJob<IJellyfinUserImporter>(nameof(IJellyfinUserImporter), "Jellyfin", JobSettingsHelper.UserImporter(s));
|
||||
}
|
||||
|
||||
private static async Task AddNotifications(JobSettings s)
|
||||
{
|
||||
await OmbiQuartz.Instance.AddJob<INotificationService>(nameof(INotificationService), "Notifications", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ namespace Ombi.Core.Settings.Models.External
|
|||
public sealed class EmbySettings : Ombi.Settings.Settings.Models.Settings
|
||||
{
|
||||
public bool Enable { get; set; }
|
||||
public bool IsJellyfin { get; set; }
|
||||
public List<EmbyServers> Servers { get; set; } = new List<EmbyServers>();
|
||||
}
|
||||
|
||||
|
@ -19,4 +18,4 @@ namespace Ombi.Core.Settings.Models.External
|
|||
public string ServerHostname { get; set; }
|
||||
public bool EnableEpisodeSearching { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
src/Ombi.Settings/Settings/Models/External/JellyfinSettings.cs
vendored
Normal file
21
src/Ombi.Settings/Settings/Models/External/JellyfinSettings.cs
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
|
||||
namespace Ombi.Core.Settings.Models.External
|
||||
{
|
||||
public sealed class JellyfinSettings : Ombi.Settings.Settings.Models.Settings
|
||||
{
|
||||
public bool Enable { get; set; }
|
||||
public List<JellyfinServers> Servers { get; set; } = new List<JellyfinServers>();
|
||||
}
|
||||
|
||||
public class JellyfinServers : ExternalSettings
|
||||
{
|
||||
public string ServerId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
public string AdministratorId { get; set; }
|
||||
public string ServerHostname { get; set; }
|
||||
public bool EnableEpisodeSearching { get; set; }
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
public class JobSettings : Settings
|
||||
{
|
||||
public string EmbyContentSync { get; set; }
|
||||
public string JellyfinContentSync { get; set; }
|
||||
public string SonarrSync { get; set; }
|
||||
public string RadarrSync { get; set; }
|
||||
public string PlexContentSync { get; set; }
|
||||
|
@ -18,4 +19,4 @@
|
|||
public string MediaDatabaseRefresh { get; set; }
|
||||
public string AutoDeleteRequests { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ namespace Ombi.Settings.Settings.Models
|
|||
return ValidateCron(Get(s.EmbyContentSync, Cron.Hourly(5)));
|
||||
}
|
||||
|
||||
public static string JellyfinContent(JobSettings s)
|
||||
{
|
||||
return ValidateCron(Get(s.JellyfinContentSync, Cron.Hourly(5)));
|
||||
}
|
||||
|
||||
public static string PlexContent(JobSettings s)
|
||||
{
|
||||
return ValidateCron(Get(s.PlexContentSync, Cron.Daily(2)));
|
||||
|
@ -97,4 +102,4 @@ namespace Ombi.Settings.Settings.Models
|
|||
return _defaultCron;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ namespace Ombi.Settings.Settings.Models
|
|||
public bool ImportPlexAdmin { get; set; }
|
||||
public bool ImportPlexUsers { get; set; }
|
||||
public bool ImportEmbyUsers { get; set; }
|
||||
public bool ImportJellyfinUsers { get; set; }
|
||||
public int MovieRequestLimit { get; set; }
|
||||
public int EpisodeRequestLimit { get; set; }
|
||||
public List<string> DefaultRoles { get; set; } = new List<string>();
|
||||
public List<string> BannedPlexUserIds { get; set; } = new List<string>();
|
||||
public List<string> BannedEmbyUserIds { get; set; } = new List<string>();
|
||||
public List<string> BannedJellyfinUserIds { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace Ombi.Store.Context
|
|||
public DbSet<CouchPotatoCache> CouchPotatoCache { get; set; }
|
||||
public DbSet<EmbyContent> EmbyContent { get; set; }
|
||||
public DbSet<EmbyEpisode> EmbyEpisode { get; set; }
|
||||
public DbSet<JellyfinEpisode> JellyfinEpisode { get; set; }
|
||||
public DbSet<JellyfinContent> JellyfinContent { get; set; }
|
||||
|
||||
public DbSet<SonarrCache> SonarrCache { get; set; }
|
||||
public DbSet<LidarrArtistCache> LidarrArtistCache { get; set; }
|
||||
|
@ -51,7 +53,13 @@ namespace Ombi.Store.Context
|
|||
.HasPrincipalKey(x => x.EmbyId)
|
||||
.HasForeignKey(p => p.ParentId);
|
||||
|
||||
builder.Entity<JellyfinEpisode>()
|
||||
.HasOne(p => p.Series)
|
||||
.WithMany(b => b.Episodes)
|
||||
.HasPrincipalKey(x => x.JellyfinId)
|
||||
.HasForeignKey(p => p.ParentId);
|
||||
|
||||
base.OnModelCreating(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
71
src/Ombi.Store/Entities/JellyfinContent.cs
Normal file
71
src/Ombi.Store/Entities/JellyfinContent.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinContent.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("JellyfinContent")]
|
||||
public class JellyfinContent : Entity
|
||||
{
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE, Cannot delete due to DB migration issues with SQLite
|
||||
/// </summary>
|
||||
public string ProviderId { get; set; }
|
||||
public string JellyfinId { get; set; }
|
||||
public JellyfinMediaType Type { get; set; }
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
public string ImdbId { get; set; }
|
||||
public string TheMovieDbId { get; set; }
|
||||
public string TvDbId { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
|
||||
public ICollection<JellyfinEpisode> Episodes { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
|
||||
|
||||
[NotMapped]
|
||||
public bool HasTvDb => !string.IsNullOrEmpty(TvDbId);
|
||||
|
||||
[NotMapped]
|
||||
public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId);
|
||||
}
|
||||
|
||||
public enum JellyfinMediaType
|
||||
{
|
||||
Movie = 0,
|
||||
Series = 1,
|
||||
Music = 2
|
||||
}
|
||||
}
|
53
src/Ombi.Store/Entities/JellyfinEpisode.cs
Normal file
53
src/Ombi.Store/Entities/JellyfinEpisode.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinEpisode.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("JellyfinEpisode")]
|
||||
public class JellyfinEpisode : Entity
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string JellyfinId { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
/// <summary>
|
||||
/// NOT USED
|
||||
/// </summary>
|
||||
public string ProviderId { get; set; }
|
||||
public DateTime AddedAt { get; set; }
|
||||
public string TvDbId { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public string TheMovieDbId { get; set; }
|
||||
|
||||
public JellyfinContent Series { get; set; }
|
||||
}
|
||||
}
|
|
@ -19,7 +19,8 @@ namespace Ombi.Store.Entities
|
|||
{
|
||||
Plex = 0,
|
||||
Emby = 1,
|
||||
Lidarr = 2
|
||||
Lidarr = 2,
|
||||
Jellyfin = 3
|
||||
}
|
||||
|
||||
public enum ContentType
|
||||
|
@ -28,4 +29,4 @@ namespace Ombi.Store.Entities
|
|||
Episode = 1,
|
||||
Album = 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,5 +34,6 @@ namespace Ombi.Store.Entities
|
|||
PlexUser = 2,
|
||||
EmbyUser = 3,
|
||||
EmbyConnectUser = 4,
|
||||
JellyfinUser = 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
52
src/Ombi.Store/Migration.md
Normal file
52
src/Ombi.Store/Migration.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
```
|
||||
dotnet ef migrations add Inital --context OmbiSqliteContext --startup-project ../Ombi/Ombi.csproj
|
||||
```
|
||||
|
||||
If running migrations for any db provider other than Sqlite, then ensure the database.json is pointing at the correct DB type
|
||||
|
||||
|
||||
## More detailed explanation
|
||||
|
||||
1. Install dotnet-ef, and include it in your $PATH if necessary:
|
||||
|
||||
```
|
||||
dotnet tool install --global dotnet-ef
|
||||
export PATH="$HOME/.dotnet/tools:$PATH"
|
||||
```
|
||||
|
||||
1. In `src/Ombi`, install the `Microsoft.EntityFrameworkCore.Design` package:
|
||||
|
||||
```
|
||||
cd src/Ombi
|
||||
dotnet add package Microsoft.EntityFrameworkCore.Design
|
||||
```
|
||||
|
||||
1. For some reason, the `StartupSingleton.Instance.SecurityKey` in `src/Ombi/Extensions/StartupExtensions.cs` is invalid when running `dotnet ef migrations add` so we must fix it; apply this patch which seems to do the job:
|
||||
|
||||
```
|
||||
@@ -79,7 +79,7 @@ namespace Ombi
|
||||
var tokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey)),
|
||||
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey + "s")),
|
||||
RequireExpirationTime = true,
|
||||
ValidateLifetime = true,
|
||||
ValidAudience = "Ombi",
|
||||
```
|
||||
|
||||
*WARNING*: Don't forget to undo this before building Ombi, or things will be broken!
|
||||
|
||||
1. List the available `dbcontext`s, and select the one that matches the database your fields will go in:
|
||||
|
||||
```
|
||||
cd src/Ombi.Store
|
||||
dotnet ef dbcontext list
|
||||
```
|
||||
|
||||
1. Run the migration using the command at the start of this document:
|
||||
|
||||
```
|
||||
cd src/Ombi.Store
|
||||
dotnet ef migrations add <name> --context <context> --startup-project ../Ombi/Ombi.csproj
|
||||
```
|
|
@ -1,3 +0,0 @@
|
|||
dotnet ef migrations add Inital --context OmbiSqliteContext --startup-project ../Ombi/Ombi.csproj
|
||||
|
||||
If running migrations for any db provider other than Sqlite, then ensure the database.json is pointing at the correct DB type
|
506
src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs
generated
Normal file
506
src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs
generated
Normal file
|
@ -0,0 +1,506 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context.MySql;
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalMySql
|
||||
{
|
||||
[DbContext(typeof(ExternalMySqlContext))]
|
||||
[Migration("20201212014227_Jellyfin")]
|
||||
partial class Jellyfin
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "5.0.1");
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrAlbumCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrArtistCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GrandparentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ParentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ParentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PlexContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalMySql
|
||||
{
|
||||
public partial class Jellyfin : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JellyfinContent",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
ProviderId = table.Column<string>(nullable: true),
|
||||
JellyfinId = table.Column<string>(nullable: false),
|
||||
Type = table.Column<int>(nullable: false),
|
||||
AddedAt = table.Column<DateTime>(nullable: false),
|
||||
ImdbId = table.Column<string>(nullable: true),
|
||||
TheMovieDbId = table.Column<string>(nullable: true),
|
||||
TvDbId = table.Column<string>(nullable: true),
|
||||
Url = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JellyfinContent", x => x.Id);
|
||||
table.UniqueConstraint("AK_JellyfinContent_JellyfinId", x => x.JellyfinId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JellyfinEpisode",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
JellyfinId = table.Column<string>(nullable: true),
|
||||
EpisodeNumber = table.Column<int>(nullable: false),
|
||||
SeasonNumber = table.Column<int>(nullable: false),
|
||||
ParentId = table.Column<string>(nullable: true),
|
||||
ProviderId = table.Column<string>(nullable: true),
|
||||
AddedAt = table.Column<DateTime>(nullable: false),
|
||||
TvDbId = table.Column<string>(nullable: true),
|
||||
ImdbId = table.Column<string>(nullable: true),
|
||||
TheMovieDbId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JellyfinEpisode", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_JellyfinEpisode_JellyfinContent_ParentId",
|
||||
column: x => x.ParentId,
|
||||
principalTable: "JellyfinContent",
|
||||
principalColumn: "JellyfinId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JellyfinEpisode_ParentId",
|
||||
table: "JellyfinEpisode",
|
||||
column: "ParentId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "JellyfinContent");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "JellyfinEpisode");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -113,6 +113,88 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
|||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("dateime(6)");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -368,6 +450,18 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
|||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
|
@ -378,6 +472,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
|||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
|
@ -386,6 +482,23 @@ namespace Ombi.Store.Migrations.ExternalMySql
|
|||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
|
506
src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.Designer.cs
generated
Normal file
506
src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.Designer.cs
generated
Normal file
|
@ -0,0 +1,506 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Ombi.Store.Context.Sqlite;
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||
{
|
||||
[DbContext(typeof(ExternalSqliteContext))]
|
||||
[Migration("20201212014227_Jellyfin")]
|
||||
partial class Jellyfin
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "5.0.1");
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CouchPotatoCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmbyContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrAlbumCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LidarrArtistCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GrandparentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ParentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GrandparentKey");
|
||||
|
||||
b.ToTable("PlexEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ParentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PlexContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexServerContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexServerContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SickRageEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SonarrEpisodeCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Store.Migrations.ExternalSqlite
|
||||
{
|
||||
public partial class Jellyfin : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JellyfinContent",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Title = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ProviderId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
JellyfinId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Type = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
AddedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
ImdbId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TheMovieDbId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TvDbId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Url = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JellyfinContent", x => x.Id);
|
||||
table.UniqueConstraint("AK_JellyfinContent_JellyfinId", x => x.JellyfinId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JellyfinEpisode",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Title = table.Column<string>(type: "TEXT", nullable: true),
|
||||
JellyfinId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
EpisodeNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeasonNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ParentId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ProviderId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
AddedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
TvDbId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ImdbId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TheMovieDbId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JellyfinEpisode", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_JellyfinEpisode_JellyfinContent_ParentId",
|
||||
column: x => x.ParentId,
|
||||
principalTable: "JellyfinContent",
|
||||
principalColumn: "JellyfinId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JellyfinEpisode_ParentId",
|
||||
table: "JellyfinEpisode",
|
||||
column: "ParentId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "JellyfinContent");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "JellyfinEpisode");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,14 +14,16 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
|
||||
.HasAnnotation("ProductVersion", "5.0.1");
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -31,26 +33,36 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired();
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title");
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type");
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url");
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -60,27 +72,38 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EmbyId");
|
||||
b.Property<string>("EmbyId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId");
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title");
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -89,26 +112,117 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("JellyfinContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JellyfinId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("JellyfinEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ArtistId");
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ForeignAlbumId");
|
||||
b.Property<string>("ForeignAlbumId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored");
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("PercentOfTracks");
|
||||
b.Property<decimal>("PercentOfTracks")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
b.Property<DateTime>("ReleaseDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title");
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TrackCount");
|
||||
b.Property<int>("TrackCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -118,15 +232,20 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ArtistId");
|
||||
b.Property<int>("ArtistId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ArtistName");
|
||||
b.Property<string>("ArtistName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ForeignArtistId");
|
||||
b.Property<string>("ForeignArtistId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Monitored");
|
||||
b.Property<bool>("Monitored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -136,19 +255,26 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GrandparentKey");
|
||||
b.Property<int>("GrandparentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Key");
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ParentKey");
|
||||
b.Property<int>("ParentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title");
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -160,17 +286,23 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ParentKey");
|
||||
b.Property<int>("ParentKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PlexContentId");
|
||||
b.Property<int>("PlexContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("PlexServerContentId");
|
||||
b.Property<int?>("PlexServerContentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonKey");
|
||||
b.Property<int>("SeasonKey")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -182,29 +314,41 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
b.Property<string>("ImdbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Key");
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Quality");
|
||||
b.Property<string>("Quality")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ReleaseYear");
|
||||
b.Property<string>("ReleaseYear")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("RequestId");
|
||||
b.Property<int?>("RequestId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TheMovieDbId");
|
||||
b.Property<string>("TheMovieDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title");
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TvDbId");
|
||||
b.Property<string>("TvDbId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type");
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Url");
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -214,11 +358,14 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile");
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
b.Property<int>("TheMovieDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -228,9 +375,11 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -240,13 +389,17 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -256,9 +409,11 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -268,15 +423,20 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasFile");
|
||||
b.Property<bool>("HasFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
b.Property<int>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
b.Property<int>("TvDbId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
@ -289,6 +449,18 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("JellyfinId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
|
@ -297,15 +469,35 @@ namespace Ombi.Store.Migrations.ExternalSqlite
|
|||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent")
|
||||
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexServerContentId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||
{
|
||||
b.Navigation("Episodes");
|
||||
|
||||
b.Navigation("Seasons");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
|
24
src/Ombi.Store/Repository/IJellyfinContentRepository.cs
Normal file
24
src/Ombi.Store/Repository/IJellyfinContentRepository.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public interface IJellyfinContentRepository : IRepository<JellyfinContent>
|
||||
{
|
||||
IQueryable<JellyfinContent> Get();
|
||||
Task<JellyfinContent> GetByTheMovieDbId(string mov);
|
||||
Task<JellyfinContent> GetByTvDbId(string tv);
|
||||
Task<JellyfinContent> GetByImdbId(string imdbid);
|
||||
Task<JellyfinContent> GetByJellyfinId(string jellyfinId);
|
||||
Task Update(JellyfinContent existingContent);
|
||||
IQueryable<JellyfinEpisode> GetAllEpisodes();
|
||||
Task<JellyfinEpisode> Add(JellyfinEpisode content);
|
||||
Task<JellyfinEpisode> GetEpisodeByJellyfinId(string key);
|
||||
Task AddRange(IEnumerable<JellyfinEpisode> content);
|
||||
|
||||
void UpdateWithoutSave(JellyfinContent existingContent);
|
||||
}
|
||||
}
|
106
src/Ombi.Store/Repository/JellyfinContentRepository.cs
Normal file
106
src/Ombi.Store/Repository/JellyfinContentRepository.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: PlexContentRepository.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public class JellyfinContentRepository : ExternalRepository<JellyfinContent>, IJellyfinContentRepository
|
||||
{
|
||||
|
||||
public JellyfinContentRepository(ExternalContext db):base(db)
|
||||
{
|
||||
Db = db;
|
||||
}
|
||||
|
||||
private ExternalContext Db { get; }
|
||||
|
||||
|
||||
public async Task<JellyfinContent> GetByImdbId(string imdbid)
|
||||
{
|
||||
return await Db.JellyfinContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid);
|
||||
}
|
||||
public async Task<JellyfinContent> GetByTvDbId(string tv)
|
||||
{
|
||||
return await Db.JellyfinContent.FirstOrDefaultAsync(x => x.TvDbId == tv);
|
||||
}
|
||||
public async Task<JellyfinContent> GetByTheMovieDbId(string mov)
|
||||
{
|
||||
return await Db.JellyfinContent.FirstOrDefaultAsync(x => x.TheMovieDbId == mov);
|
||||
}
|
||||
|
||||
public IQueryable<JellyfinContent> Get()
|
||||
{
|
||||
return Db.JellyfinContent.AsQueryable();
|
||||
}
|
||||
|
||||
public async Task<JellyfinContent> GetByJellyfinId(string jellyfinId)
|
||||
{
|
||||
return await Db.JellyfinContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.JellyfinId == jellyfinId);
|
||||
}
|
||||
|
||||
public async Task Update(JellyfinContent existingContent)
|
||||
{
|
||||
Db.JellyfinContent.Update(existingContent);
|
||||
await InternalSaveChanges();
|
||||
}
|
||||
|
||||
public IQueryable<JellyfinEpisode> GetAllEpisodes()
|
||||
{
|
||||
return Db.JellyfinEpisode.AsQueryable();
|
||||
}
|
||||
|
||||
public async Task<JellyfinEpisode> Add(JellyfinEpisode content)
|
||||
{
|
||||
await Db.JellyfinEpisode.AddAsync(content);
|
||||
await InternalSaveChanges();
|
||||
return content;
|
||||
}
|
||||
public async Task<JellyfinEpisode> GetEpisodeByJellyfinId(string key)
|
||||
{
|
||||
return await Db.JellyfinEpisode.FirstOrDefaultAsync(x => x.JellyfinId == key);
|
||||
}
|
||||
|
||||
public async Task AddRange(IEnumerable<JellyfinEpisode> content)
|
||||
{
|
||||
Db.JellyfinEpisode.AddRange(content);
|
||||
await InternalSaveChanges();
|
||||
}
|
||||
|
||||
public void UpdateWithoutSave(JellyfinContent existingContent)
|
||||
{
|
||||
Db.JellyfinContent.Update(existingContent);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ namespace Ombi.Test.Common
|
|||
{
|
||||
var store = new Mock<IUserStore<OmbiUser>>();
|
||||
//var u = new OmbiUserManager(store.Object, null, null, null, null, null, null, null, null,null,null,null,null)
|
||||
var mgr = new Mock<OmbiUserManager>(store.Object, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
var mgr = new Mock<OmbiUserManager>(store.Object, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
mgr.Object.UserValidators.Add(new UserValidator<OmbiUser>());
|
||||
mgr.Object.PasswordValidators.Add(new PasswordValidator<OmbiUser>());
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Schedule", "Ombi.Sched
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Emby", "Ombi.Api.Emby\Ombi.Api.Emby.csproj", "{08FF107D-31E1-470D-AF86-E09B015CEE06}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Jellyfin", "Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj", "{08FF107D-31E1-470D-AF86-E09B015CEE06}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Sonarr", "Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj", "{CFB5E008-D0D0-43C0-AA06-89E49D17F384}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6F42AB98-9196-44C4-B888-D5E409F415A1}"
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
<i matTooltip=" {{'Search.ViewOnEmby' | translate}}"
|
||||
class="fa fa-play-circle fa-2x grow"></i>
|
||||
</a>
|
||||
<a *ngIf="movie.jellyfinUrl" class="media-icons" href="{{movie.jellyfinUrl}}" target="_blank">
|
||||
<i matTooltip=" {{'Search.ViewOnJellyfin' | translate}}"
|
||||
class="fa fa-play-circle fa-2x grow"></i>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<span *ngIf="tv">
|
||||
|
@ -33,6 +37,10 @@
|
|||
<i matTooltip=" {{'Search.ViewOnEmby' | translate}}"
|
||||
class="fa fa-play-circle fa-2x grow"></i>
|
||||
</a>
|
||||
<a *ngIf="tv.jellyfinUrl" class="media-icons" href="{{tv.jellyfinUrl}}" target="_blank">
|
||||
<i matTooltip=" {{'Search.ViewOnJellyfin' | translate}}"
|
||||
class="fa fa-play-circle fa-2x grow"></i>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<a class="media-icons" (click)="close()">
|
||||
|
@ -151,10 +159,13 @@
|
|||
<a *ngIf="tv.embyUrl" mat-raised-button class="btn-green btn-spacing" href="{{tv.embyUrl}}"
|
||||
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
|
||||
translate}}</a>
|
||||
<a *ngIf="tv.jellyfinUrl" mat-raised-button class="btn-green btn-spacing" href="{{tv.jellyfinUrl}}"
|
||||
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnJellyfin' |
|
||||
translate}}</a>
|
||||
</span>
|
||||
<button mat-raised-button class="btn-green btn-spacing" (click)="openDetails()"> {{
|
||||
'Common.ViewDetails' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
<mat-icon style="color:white" matTooltip=" {{'Search.ViewOnEmby' | translate}}">
|
||||
play_circle_outline</mat-icon>
|
||||
</a></mat-chip>
|
||||
<mat-chip *ngIf="movie && movie.jellyfinUrl"> <a href="{{movie.jellyfinUrl}}" target="_blank">
|
||||
<mat-icon style="color:white" matTooltip=" {{'Search.ViewOnJellyfin' | translate}}">
|
||||
play_circle_outline</mat-icon>
|
||||
</a></mat-chip>
|
||||
|
||||
<mat-chip *ngIf="tv && tv.plexUrl"> <a href="{{tv.plexUrl}}" target="_blank">
|
||||
<mat-icon style="color:white" matTooltip=" {{'Search.ViewOnPlex' | translate}}">
|
||||
|
@ -59,6 +63,10 @@
|
|||
<mat-icon style="color:white" matTooltip=" {{'Search.ViewOnEmby' | translate}}">
|
||||
play_circle_outline</mat-icon>
|
||||
</a></mat-chip>
|
||||
<mat-chip *ngIf="tv &&tv.jellyfinUrl"> <a href="{{movie.jellyfinUrl}}" target="_blank">
|
||||
<mat-icon style="color:white" matTooltip=" {{'Search.ViewOnJellyfin' | translate}}">
|
||||
play_circle_outline</mat-icon>
|
||||
</a></mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -148,10 +156,13 @@
|
|||
<a *ngIf="tv.embyUrl" mat-raised-button class="btn-green btn-spacing" href="{{tv.embyUrl}}"
|
||||
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
|
||||
translate}}</a>
|
||||
<a *ngIf="tv.jellyfinUrl" mat-raised-button class="btn-green btn-spacing" href="{{tv.jellyfinUrl}}"
|
||||
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnJellyfin' |
|
||||
translate}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -26,4 +26,5 @@ export interface IRecentlyAddedRangeModel {
|
|||
export enum RecentlyAddedType {
|
||||
Plex,
|
||||
Emby,
|
||||
Jellyfin,
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
available: boolean;
|
||||
plexUrl: string;
|
||||
embyUrl: string;
|
||||
jellyfinUrl: string;
|
||||
quality: string;
|
||||
digitalReleaseDate: Date;
|
||||
subscribed: boolean;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
recommendations: IOtherMovies;
|
||||
plexUrl: string;
|
||||
embyUrl: string;
|
||||
jellyfinUrl: string;
|
||||
quality: string;
|
||||
digitalReleaseDate: Date;
|
||||
subscribed: boolean;
|
||||
|
@ -166,4 +167,4 @@ export interface IOtherMoviesViewModel {
|
|||
vote_average: number;
|
||||
vote_count: number;
|
||||
popularity: number;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ export interface ISearchTvResult {
|
|||
available: boolean;
|
||||
plexUrl: string;
|
||||
embyUrl: string;
|
||||
jellyfinUrl: string;
|
||||
quality: string;
|
||||
firstSeason: boolean;
|
||||
latestSeason: boolean;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue