mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-31 03:50:08 -07:00
merge
This commit is contained in:
commit
eedd67b205
269 changed files with 10168 additions and 1028 deletions
|
@ -25,10 +25,6 @@ namespace Ombi.Api.Emby
|
||||||
|
|
||||||
public IEmbyApi CreateClient(EmbySettings settings)
|
public IEmbyApi CreateClient(EmbySettings settings)
|
||||||
{
|
{
|
||||||
if (settings.IsJellyfin)
|
|
||||||
{
|
|
||||||
return new JellyfinApi(_api);
|
|
||||||
}
|
|
||||||
return new EmbyApi(_api);
|
return new EmbyApi(_api);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,8 @@
|
||||||
public string LocalAddress { get; set; }
|
public string LocalAddress { get; set; }
|
||||||
public string ServerName { get; set; }
|
public string ServerName { get; set; }
|
||||||
public string Version { 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 OperatingSystem { get; set; }
|
||||||
public string Id { 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 System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore.Internal;
|
using Microsoft.EntityFrameworkCore.Internal;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ombi.Api.Emby.Models;
|
using Ombi.Api.Jellyfin.Models;
|
||||||
using Ombi.Api.Emby.Models.Media.Tv;
|
using Ombi.Api.Jellyfin.Models.Media.Tv;
|
||||||
using Ombi.Api.Emby.Models.Movie;
|
using Ombi.Api.Jellyfin.Models.Movie;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Api.Emby
|
namespace Ombi.Api.Jellyfin
|
||||||
{
|
{
|
||||||
public class JellyfinApi : IEmbyApi
|
public class JellyfinApi : IJellyfinApi
|
||||||
{
|
{
|
||||||
public JellyfinApi(IApi api)
|
public JellyfinApi(IApi api)
|
||||||
{
|
{
|
||||||
|
@ -20,27 +20,27 @@ namespace Ombi.Api.Emby
|
||||||
private IApi Api { get; }
|
private IApi Api { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all users from the Emby Instance
|
/// Returns all users from the Jellyfin Instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseUri"></param>
|
/// <param name="baseUri"></param>
|
||||||
/// <param name="apiKey"></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);
|
var request = new Request("users", baseUri, HttpMethod.Get);
|
||||||
|
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
var obj = await Api.Request<List<EmbyUser>>(request);
|
var obj = await Api.Request<List<JellyfinUser>>(request);
|
||||||
|
|
||||||
return obj;
|
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);
|
var request = new Request("System/Info", baseUrl, HttpMethod.Get);
|
||||||
|
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
var obj = await Api.Request<EmbySystemInfo>(request);
|
var obj = await Api.Request<JellyfinSystemInfo>(request);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ namespace Ombi.Api.Emby
|
||||||
return obj;
|
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 request = new Request("users/authenticatebyname", baseUri, HttpMethod.Post);
|
||||||
var body = new
|
var body = new
|
||||||
|
@ -71,11 +71,11 @@ namespace Ombi.Api.Emby
|
||||||
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
|
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
var obj = await Api.Request<EmbyUser>(request);
|
var obj = await Api.Request<JellyfinUser>(request);
|
||||||
return obj;
|
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);
|
var request = new Request($"users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
@ -84,22 +84,22 @@ namespace Ombi.Api.Emby
|
||||||
|
|
||||||
request.AddQueryString("IsVirtualItem", "False");
|
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)
|
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);
|
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);
|
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
|
||||||
|
|
||||||
|
@ -139,10 +139,10 @@ namespace Ombi.Api.Emby
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
|
|
||||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
var obj = await Api.Request<JellyfinItemContainer<T>>(request);
|
||||||
return obj;
|
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);
|
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ namespace Ombi.Api.Emby
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
|
|
||||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
var obj = await Api.Request<JellyfinItemContainer<T>>(request);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ namespace Ombi.Api.Emby
|
||||||
req.AddHeader("Device", "Ombi");
|
req.AddHeader("Device", "Ombi");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<EmbyConnectUser> LoginConnectUser(string username, string password)
|
public Task<JellyfinConnectUser> LoginConnectUser(string username, string password)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
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>
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Hqub.MusicBrainz.API.Entities;
|
using Hqub.MusicBrainz.API.Entities;
|
||||||
|
using Hqub.MusicBrainz.API.Entities.Collections;
|
||||||
using Ombi.Api.MusicBrainz.Models;
|
using Ombi.Api.MusicBrainz.Models;
|
||||||
|
|
||||||
namespace Ombi.Api.MusicBrainz
|
namespace Ombi.Api.MusicBrainz
|
||||||
|
@ -11,6 +12,7 @@ namespace Ombi.Api.MusicBrainz
|
||||||
Task<IEnumerable<Artist>> SearchArtist(string artistQuery);
|
Task<IEnumerable<Artist>> SearchArtist(string artistQuery);
|
||||||
Task<IEnumerable<Release>> GetReleaseForArtist(string artistId);
|
Task<IEnumerable<Release>> GetReleaseForArtist(string artistId);
|
||||||
Task<Artist> GetArtistInformation(string artistId);
|
Task<Artist> GetArtistInformation(string artistId);
|
||||||
|
Task<Release> GetAlbumInformation(string albumId);
|
||||||
Task<ReleaseGroupArt> GetCoverArtForReleaseGroup(string musicBrainzId, CancellationToken token);
|
Task<ReleaseGroupArt> GetCoverArtForReleaseGroup(string musicBrainzId, CancellationToken token);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Hqub.MusicBrainz.API;
|
using Hqub.MusicBrainz.API;
|
||||||
using Hqub.MusicBrainz.API.Entities;
|
using Hqub.MusicBrainz.API.Entities;
|
||||||
|
using Hqub.MusicBrainz.API.Entities.Collections;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Ombi.Api.MusicBrainz.Models;
|
using Ombi.Api.MusicBrainz.Models;
|
||||||
|
|
||||||
|
@ -20,6 +21,12 @@ namespace Ombi.Api.MusicBrainz
|
||||||
_api = api;
|
_api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<Release> GetAlbumInformation(string albumId)
|
||||||
|
{
|
||||||
|
var album = Release.GetAsync(albumId);
|
||||||
|
return album;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Artist>> SearchArtist(string artistQuery)
|
public async Task<IEnumerable<Artist>> SearchArtist(string artistQuery)
|
||||||
{
|
{
|
||||||
var artist = await Artist.SearchAsync(artistQuery, 10);
|
var artist = await Artist.SearchAsync(artistQuery, 10);
|
||||||
|
|
14
src/Ombi.Api.RottenTomatoes/IRottenTomatoesApi.cs
Normal file
14
src/Ombi.Api.RottenTomatoes/IRottenTomatoesApi.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using Ombi.Api.RottenTomatoes.Models;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Api.RottenTomatoes
|
||||||
|
{
|
||||||
|
public interface IRottenTomatoesApi
|
||||||
|
{
|
||||||
|
Task<MovieRatings> GetMovieRatings(string movieName, int movieYear);
|
||||||
|
Task<TvRatings> GetTvRatings(string showName, int showYear);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Api.RottenTomatoes.Models
|
||||||
|
{
|
||||||
|
public class RottenTomatoesMovieResponse
|
||||||
|
{
|
||||||
|
public int total { get; set; }
|
||||||
|
public List<Movie> movies { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Movie
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public int year { get; set; }
|
||||||
|
public string mpaa_rating { get; set; }
|
||||||
|
public object runtime { get; set; }
|
||||||
|
public string critics_consensus { get; set; }
|
||||||
|
public MovieRatings ratings { get; set; }
|
||||||
|
public Links links { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MovieRatings
|
||||||
|
{
|
||||||
|
public string critics_rating { get; set; }
|
||||||
|
public int critics_score { get; set; }
|
||||||
|
public string audience_rating { get; set; }
|
||||||
|
public int audience_score { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Links
|
||||||
|
{
|
||||||
|
public string alternate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
namespace Ombi.Api.RottenTomatoes.Models
|
||||||
|
{
|
||||||
|
public class RottenTomatoesTvResponse
|
||||||
|
{
|
||||||
|
public int tvCount { get; set; }
|
||||||
|
public TvSeries[] tvSeries { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TvSeries
|
||||||
|
{
|
||||||
|
public string title { get; set; }
|
||||||
|
public int startYear { get; set; }
|
||||||
|
public int endYear { get; set; }
|
||||||
|
public string url { get; set; }
|
||||||
|
public string meterClass { get; set; }
|
||||||
|
public int meterScore { get; set; }
|
||||||
|
public string image { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
src/Ombi.Api.RottenTomatoes/Models/TvRatings.cs
Normal file
8
src/Ombi.Api.RottenTomatoes/Models/TvRatings.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ombi.Api.RottenTomatoes.Models
|
||||||
|
{
|
||||||
|
public class TvRatings
|
||||||
|
{
|
||||||
|
public string Class { get; set; }
|
||||||
|
public int Score { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj
Normal file
12
src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<LangVersion>8.0</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
56
src/Ombi.Api.RottenTomatoes/RottenTomatoesApi.cs
Normal file
56
src/Ombi.Api.RottenTomatoes/RottenTomatoesApi.cs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
using Ombi.Api.RottenTomatoes.Models;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Api.RottenTomatoes
|
||||||
|
{
|
||||||
|
public class RottenTomatoesApi : IRottenTomatoesApi
|
||||||
|
{
|
||||||
|
public RottenTomatoesApi(IApi api)
|
||||||
|
{
|
||||||
|
_api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Endpoint => "https://www.rottentomatoes.com/api/private";
|
||||||
|
private IApi _api { get; }
|
||||||
|
|
||||||
|
public async Task<MovieRatings> GetMovieRatings(string movieName, int movieYear)
|
||||||
|
{
|
||||||
|
var request = new Request("/v1.0/movies", Endpoint, HttpMethod.Get);
|
||||||
|
request.AddHeader("Accept", "application/json");
|
||||||
|
request.AddQueryString("q", movieName);
|
||||||
|
var result = await _api.Request<RottenTomatoesMovieResponse>(request);
|
||||||
|
|
||||||
|
var movieFound = result.movies.FirstOrDefault(x => x.year == movieYear);
|
||||||
|
if (movieFound == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return movieFound.ratings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TvRatings> GetTvRatings(string showName, int showYear)
|
||||||
|
{
|
||||||
|
var request = new Request("/v2.0/search/", Endpoint, HttpMethod.Get);
|
||||||
|
request.AddHeader("Accept", "application/json");
|
||||||
|
request.AddQueryString("q", showName);
|
||||||
|
request.AddQueryString("limit", 10.ToString());
|
||||||
|
var result = await _api.Request<RottenTomatoesTvResponse>(request);
|
||||||
|
|
||||||
|
var showFound = result.tvSeries.FirstOrDefault(x => x.startYear == showYear);
|
||||||
|
if (showFound == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TvRatings
|
||||||
|
{
|
||||||
|
Class = showFound.meterClass,
|
||||||
|
Score = showFound.meterScore
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ namespace Ombi.Core.Tests.Authentication
|
||||||
AuthenticationSettings.Setup(x => x.GetSettingsAsync())
|
AuthenticationSettings.Setup(x => x.GetSettingsAsync())
|
||||||
.ReturnsAsync(new AuthenticationSettings());
|
.ReturnsAsync(new AuthenticationSettings());
|
||||||
_um = new OmbiUserManager(UserStore.Object, null, null, null, null, null, null, null, null,
|
_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; }
|
public OmbiUserManager _um { get; set; }
|
||||||
|
|
|
@ -115,4 +115,4 @@ namespace Ombi.Core.Tests.Rule.Search
|
||||||
Assert.False(search.Available);
|
Assert.False(search.Available);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
119
src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs
Normal file
119
src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
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
|
||||||
|
{
|
||||||
|
Ip = "8080",
|
||||||
|
Port = 9090,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
src/Ombi.Core.Tests/WatchProviderParserTests.cs
Normal file
94
src/Ombi.Core.Tests/WatchProviderParserTests.cs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using Ombi.Core.Helpers;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class WatchProviderParserTests
|
||||||
|
{
|
||||||
|
[TestCase("GB", TestName = "UpperCase")]
|
||||||
|
[TestCase("gb", TestName = "LowerCase")]
|
||||||
|
[TestCase("gB", TestName = "MixedCase")]
|
||||||
|
public void GetValidStreamData(string streamingCountry)
|
||||||
|
{
|
||||||
|
var result = WatchProviderParser.GetUserWatchProviders(new WatchProviders
|
||||||
|
{
|
||||||
|
Results = new Results
|
||||||
|
{
|
||||||
|
GB = new WatchProviderData()
|
||||||
|
{
|
||||||
|
StreamInformation = new List<StreamData>
|
||||||
|
{
|
||||||
|
new StreamData
|
||||||
|
{
|
||||||
|
provider_name = "Netflix",
|
||||||
|
display_priority = 0,
|
||||||
|
logo_path = "logo",
|
||||||
|
provider_id = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, new OmbiUser { StreamingCountry = streamingCountry });
|
||||||
|
|
||||||
|
Assert.That(result[0].provider_name, Is.EqualTo("Netflix"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("GB", TestName = "Missing_UpperCase")]
|
||||||
|
[TestCase("gb", TestName = "Missing_LowerCase")]
|
||||||
|
[TestCase("gB", TestName = "Missing_MixedCase")]
|
||||||
|
public void GetMissingStreamData(string streamingCountry)
|
||||||
|
{
|
||||||
|
var result = WatchProviderParser.GetUserWatchProviders(new WatchProviders
|
||||||
|
{
|
||||||
|
Results = new Results
|
||||||
|
{
|
||||||
|
AR = new WatchProviderData()
|
||||||
|
{
|
||||||
|
StreamInformation = new List<StreamData>
|
||||||
|
{
|
||||||
|
new StreamData
|
||||||
|
{
|
||||||
|
provider_name = "Netflix",
|
||||||
|
display_priority = 0,
|
||||||
|
logo_path = "logo",
|
||||||
|
provider_id = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, new OmbiUser { StreamingCountry = streamingCountry });
|
||||||
|
|
||||||
|
Assert.That(result, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetInvalidStreamData()
|
||||||
|
{
|
||||||
|
var result = WatchProviderParser.GetUserWatchProviders(new WatchProviders
|
||||||
|
{
|
||||||
|
Results = new Results
|
||||||
|
{
|
||||||
|
AR = new WatchProviderData()
|
||||||
|
{
|
||||||
|
StreamInformation = new List<StreamData>
|
||||||
|
{
|
||||||
|
new StreamData
|
||||||
|
{
|
||||||
|
provider_name = "Netflix",
|
||||||
|
display_priority = 0,
|
||||||
|
logo_path = "logo",
|
||||||
|
provider_id = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, new OmbiUser { StreamingCountry = "BLAH" });
|
||||||
|
|
||||||
|
Assert.That(result, Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
|
using Ombi.Api.Jellyfin;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Plex.Models;
|
using Ombi.Api.Plex.Models;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
@ -49,18 +50,24 @@ namespace Ombi.Core.Authentication
|
||||||
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
||||||
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
||||||
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
|
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)
|
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
||||||
{
|
{
|
||||||
_plexApi = plexApi;
|
_plexApi = plexApi;
|
||||||
_embyApi = embyApi;
|
_embyApi = embyApi;
|
||||||
|
_jellyfinApi = jellyfinApi;
|
||||||
_embySettings = embySettings;
|
_embySettings = embySettings;
|
||||||
|
_jellyfinSettings = jellyfinSettings;
|
||||||
_authSettings = auth;
|
_authSettings = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexApi _plexApi;
|
private readonly IPlexApi _plexApi;
|
||||||
private readonly IEmbyApiFactory _embyApi;
|
private readonly IEmbyApiFactory _embyApi;
|
||||||
|
private readonly IJellyfinApiFactory _jellyfinApi;
|
||||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||||
|
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||||
|
|
||||||
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
||||||
|
@ -83,6 +90,10 @@ namespace Ombi.Core.Authentication
|
||||||
{
|
{
|
||||||
return await CheckEmbyPasswordAsync(user, password);
|
return await CheckEmbyPasswordAsync(user, password);
|
||||||
}
|
}
|
||||||
|
if (user.UserType == UserType.JellyfinUser)
|
||||||
|
{
|
||||||
|
return await CheckJellyfinPasswordAsync(user, password);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,5 +196,36 @@ namespace Ombi.Core.Authentication
|
||||||
}
|
}
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ using Ombi.Core.Settings;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using Ombi.Core.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -179,6 +181,12 @@ namespace Ombi.Core.Engine
|
||||||
return user.Language;
|
return user.Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task<List<StreamData>> GetUserWatchProvider(WatchProviders providers)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
return WatchProviderParser.GetUserWatchProviders(providers, user);
|
||||||
|
}
|
||||||
|
|
||||||
private OmbiSettings ombiSettings;
|
private OmbiSettings ombiSettings;
|
||||||
protected async Task<OmbiSettings> GetOmbiSettings()
|
protected async Task<OmbiSettings> GetOmbiSettings()
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,5 +26,6 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
int ResultLimit { get; set; }
|
int ResultLimit { get; set; }
|
||||||
|
|
||||||
Task<MovieFullInfoViewModel> GetMovieInfoByImdbId(string imdbId, CancellationToken requestAborted);
|
Task<MovieFullInfoViewModel> GetMovieInfoByImdbId(string imdbId, CancellationToken requestAborted);
|
||||||
|
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,5 +9,6 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<ArtistInformation> GetArtistInformation(string artistId);
|
Task<ArtistInformation> GetArtistInformation(string artistId);
|
||||||
Task<ArtistInformation> GetArtistInformationByRequestId(int requestId);
|
Task<ArtistInformation> GetArtistInformationByRequestId(int requestId);
|
||||||
Task<AlbumArt> GetReleaseGroupArt(string musicBrainzId, CancellationToken token);
|
Task<AlbumArt> GetReleaseGroupArt(string musicBrainzId, CancellationToken token);
|
||||||
|
Task<ReleaseGroup> GetAlbum(string albumId);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Threading.Tasks;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Models.Search.V2;
|
using Ombi.Core.Models.Search.V2;
|
||||||
|
|
||||||
namespace Ombi.Core
|
namespace Ombi.Core
|
||||||
|
@ -7,5 +9,6 @@ namespace Ombi.Core
|
||||||
{
|
{
|
||||||
Task<SearchFullInfoTvShowViewModel> GetShowInformation(int tvdbid);
|
Task<SearchFullInfoTvShowViewModel> GetShowInformation(int tvdbid);
|
||||||
Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId);
|
Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId);
|
||||||
|
Task<IEnumerable<StreamingData>> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -67,6 +67,22 @@ namespace Ombi.Core.Engine
|
||||||
$"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}";
|
$"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}";
|
||||||
|
|
||||||
var userDetails = await GetUser();
|
var userDetails = await GetUser();
|
||||||
|
var canRequestOnBehalf = false;
|
||||||
|
|
||||||
|
if (model.RequestOnBehalf.HasValue())
|
||||||
|
{
|
||||||
|
canRequestOnBehalf = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
|
||||||
|
|
||||||
|
if (!canRequestOnBehalf)
|
||||||
|
{
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = false,
|
||||||
|
Message = "You do not have the correct permissions to request on behalf of users!",
|
||||||
|
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var requestModel = new MovieRequests
|
var requestModel = new MovieRequests
|
||||||
{
|
{
|
||||||
|
@ -82,7 +98,7 @@ namespace Ombi.Core.Engine
|
||||||
Status = movieInfo.Status,
|
Status = movieInfo.Status,
|
||||||
RequestedDate = DateTime.UtcNow,
|
RequestedDate = DateTime.UtcNow,
|
||||||
Approved = false,
|
Approved = false,
|
||||||
RequestedUserId = userDetails.Id,
|
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
|
||||||
Background = movieInfo.BackdropPath,
|
Background = movieInfo.BackdropPath,
|
||||||
LangCode = model.LanguageCode,
|
LangCode = model.LanguageCode,
|
||||||
RequestedByAlias = model.RequestedByAlias
|
RequestedByAlias = model.RequestedByAlias
|
||||||
|
@ -103,7 +119,7 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
if (requestModel.Approved) // The rules have auto approved this
|
if (requestModel.Approved) // The rules have auto approved this
|
||||||
{
|
{
|
||||||
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName);
|
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
|
||||||
if (requestEngineResult.Result)
|
if (requestEngineResult.Result)
|
||||||
{
|
{
|
||||||
var result = await ApproveMovie(requestModel);
|
var result = await ApproveMovie(requestModel);
|
||||||
|
@ -124,7 +140,7 @@ namespace Ombi.Core.Engine
|
||||||
// If there are no providers then it's successful but movie has not been sent
|
// If there are no providers then it's successful but movie has not been sent
|
||||||
}
|
}
|
||||||
|
|
||||||
return await AddMovieRequest(requestModel, fullMovieName);
|
return await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,7 +286,7 @@ namespace Ombi.Core.Engine
|
||||||
allRequests = allRequests.Where(x => x.Available);
|
allRequests = allRequests.Where(x => x.Available);
|
||||||
break;
|
break;
|
||||||
case RequestStatus.Denied:
|
case RequestStatus.Denied:
|
||||||
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available);
|
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -429,7 +445,7 @@ namespace Ombi.Core.Engine
|
||||||
public async Task<MovieRequests> GetRequest(int requestId)
|
public async Task<MovieRequests> GetRequest(int requestId)
|
||||||
{
|
{
|
||||||
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||||
await CheckForSubscription(new HideResult(), new List<MovieRequests>{request });
|
await CheckForSubscription(new HideResult(), new List<MovieRequests> { request });
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
@ -654,19 +670,19 @@ namespace Ombi.Core.Engine
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName)
|
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf)
|
||||||
{
|
{
|
||||||
await MovieRepository.Add(model);
|
await MovieRepository.Add(model);
|
||||||
|
|
||||||
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
await NotificationHelper.NewRequest(model);
|
await NotificationHelper.NewRequest(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _requestLog.Add(new RequestLog
|
await _requestLog.Add(new RequestLog
|
||||||
{
|
{
|
||||||
UserId = (await GetUser()).Id,
|
UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id,
|
||||||
RequestDate = DateTime.UtcNow,
|
RequestDate = DateTime.UtcNow,
|
||||||
RequestId = model.Id,
|
RequestId = model.Id,
|
||||||
RequestType = RequestType.Movie,
|
RequestType = RequestType.Movie,
|
||||||
|
|
|
@ -13,15 +13,17 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public class RecentlyAddedEngine : IRecentlyAddedEngine
|
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;
|
_plex = plex;
|
||||||
_emby = emby;
|
_emby = emby;
|
||||||
|
_jellyfin = jellyfin;
|
||||||
_recentlyAddedLog = recentlyAdded;
|
_recentlyAddedLog = recentlyAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexContentRepository _plex;
|
private readonly IPlexContentRepository _plex;
|
||||||
private readonly IEmbyContentRepository _emby;
|
private readonly IEmbyContentRepository _emby;
|
||||||
|
private readonly IJellyfinContentRepository _jellyfin;
|
||||||
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,23 +32,26 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie && x.AddedAt > from && x.AddedAt < 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 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()
|
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies()
|
||||||
{
|
{
|
||||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.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)
|
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 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 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,14 +59,16 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show);
|
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 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()
|
public async Task<bool> UpdateRecentlyAddedDatabase()
|
||||||
{
|
{
|
||||||
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
||||||
var embyContent = _emby.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>();
|
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||||
foreach (var p in plexContent)
|
foreach (var p in plexContent)
|
||||||
{
|
{
|
||||||
|
@ -138,17 +145,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);
|
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||||
|
|
||||||
return true;
|
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)
|
bool groupBySeason)
|
||||||
{
|
{
|
||||||
var model = new HashSet<RecentlyAddedTvModel>();
|
var model = new HashSet<RecentlyAddedTvModel>();
|
||||||
TransformPlexShows(plexTv, model);
|
TransformPlexShows(plexTv, model);
|
||||||
TransformEmbyShows(embyTv, model);
|
TransformEmbyShows(embyTv, model);
|
||||||
|
TransformJellyfinShows(jellyfinTv, model);
|
||||||
|
|
||||||
if (groupBySeason)
|
if (groupBySeason)
|
||||||
{
|
{
|
||||||
|
@ -158,11 +204,12 @@ namespace Ombi.Core.Engine
|
||||||
return model;
|
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>();
|
var model = new HashSet<RecentlyAddedMovieModel>();
|
||||||
TransformPlexMovies(plexMovies, model);
|
TransformPlexMovies(plexMovies, model);
|
||||||
TransformEmbyMovies(embyMovies, model);
|
TransformEmbyMovies(embyMovies, model);
|
||||||
|
TransformJellyfinMovies(jellyfinMovies, model);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -183,6 +230,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)
|
private static void TransformPlexMovies(IQueryable<PlexServerContent> plexMovies, HashSet<RecentlyAddedMovieModel> model)
|
||||||
{
|
{
|
||||||
foreach (var plex in plexMovies)
|
foreach (var plex in plexMovies)
|
||||||
|
@ -246,5 +309,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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,12 +51,28 @@ namespace Ombi.Core.Engine
|
||||||
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
||||||
{
|
{
|
||||||
var user = await GetUser();
|
var user = await GetUser();
|
||||||
|
var canRequestOnBehalf = false;
|
||||||
|
|
||||||
|
if (tv.RequestOnBehalf.HasValue())
|
||||||
|
{
|
||||||
|
canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
|
||||||
|
|
||||||
|
if (!canRequestOnBehalf)
|
||||||
|
{
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = false,
|
||||||
|
Message = "You do not have the correct permissions to request on behalf of users!",
|
||||||
|
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi);
|
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi);
|
||||||
(await tvBuilder
|
(await tvBuilder
|
||||||
.GetShowInfo(tv.TvDbId))
|
.GetShowInfo(tv.TvDbId))
|
||||||
.CreateTvList(tv)
|
.CreateTvList(tv)
|
||||||
.CreateChild(tv, user.Id);
|
.CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id);
|
||||||
|
|
||||||
await tvBuilder.BuildEpisodes(tv);
|
await tvBuilder.BuildEpisodes(tv);
|
||||||
|
|
||||||
|
@ -124,12 +140,12 @@ namespace Ombi.Core.Engine
|
||||||
ErrorMessage = "This has already been requested"
|
ErrorMessage = "This has already been requested"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest);
|
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a new request
|
// This is a new request
|
||||||
var newRequest = tvBuilder.CreateNewRequest(tv);
|
var newRequest = tvBuilder.CreateNewRequest(tv);
|
||||||
return await AddRequest(newRequest.NewRequest);
|
return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, OrderFilterModel type)
|
public async Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, OrderFilterModel type)
|
||||||
|
@ -736,21 +752,21 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest)
|
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf)
|
||||||
{
|
{
|
||||||
// Add the child
|
// Add the child
|
||||||
existingRequest.ChildRequests.Add(newRequest);
|
existingRequest.ChildRequests.Add(newRequest);
|
||||||
|
|
||||||
await TvRepository.Update(existingRequest);
|
await TvRepository.Update(existingRequest);
|
||||||
|
|
||||||
return await AfterRequest(newRequest);
|
return await AfterRequest(newRequest, requestOnBehalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<RequestEngineResult> AddRequest(TvRequests model)
|
private async Task<RequestEngineResult> AddRequest(TvRequests model, string requestOnBehalf)
|
||||||
{
|
{
|
||||||
await TvRepository.Add(model);
|
await TvRepository.Add(model);
|
||||||
// This is a new request so we should only have 1 child
|
// This is a new request so we should only have 1 child
|
||||||
return await AfterRequest(model.ChildRequests.FirstOrDefault());
|
return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
|
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
|
||||||
|
@ -766,7 +782,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<RequestEngineResult> AfterRequest(ChildRequests model)
|
private async Task<RequestEngineResult> AfterRequest(ChildRequests model, string requestOnBehalf)
|
||||||
{
|
{
|
||||||
var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
||||||
if (sendRuleResult.Success)
|
if (sendRuleResult.Success)
|
||||||
|
@ -776,7 +792,7 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
await _requestLog.Add(new RequestLog
|
await _requestLog.Add(new RequestLog
|
||||||
{
|
{
|
||||||
UserId = (await GetUser()).Id,
|
UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id,
|
||||||
RequestDate = DateTime.UtcNow,
|
RequestDate = DateTime.UtcNow,
|
||||||
RequestId = model.Id,
|
RequestId = model.Id,
|
||||||
RequestType = RequestType.TvShow,
|
RequestType = RequestType.TvShow,
|
||||||
|
|
|
@ -249,6 +249,26 @@ namespace Ombi.Core.Engine.V2
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var providers = await MovieApi.GetMovieWatchProviders(movieDbId, cancellationToken);
|
||||||
|
var results = await GetUserWatchProvider(providers);
|
||||||
|
|
||||||
|
var data = new List<StreamingData>();
|
||||||
|
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
data.Add(new StreamingData
|
||||||
|
{
|
||||||
|
Logo = result.logo_path,
|
||||||
|
Order = result.display_priority,
|
||||||
|
StreamingProvider = result.provider_name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||||
IEnumerable<MovieSearchResult> movies)
|
IEnumerable<MovieSearchResult> movies)
|
||||||
{
|
{
|
||||||
|
@ -287,6 +307,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
mapped.Requested = viewMovie.Requested;
|
mapped.Requested = viewMovie.Requested;
|
||||||
mapped.PlexUrl = viewMovie.PlexUrl;
|
mapped.PlexUrl = viewMovie.PlexUrl;
|
||||||
mapped.EmbyUrl = viewMovie.EmbyUrl;
|
mapped.EmbyUrl = viewMovie.EmbyUrl;
|
||||||
|
mapped.JellyfinUrl = viewMovie.JellyfinUrl;
|
||||||
mapped.Subscribed = viewMovie.Subscribed;
|
mapped.Subscribed = viewMovie.Subscribed;
|
||||||
mapped.ShowSubscribe = viewMovie.ShowSubscribe;
|
mapped.ShowSubscribe = viewMovie.ShowSubscribe;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Ombi.Api.Lidarr;
|
using Ombi.Api.Lidarr;
|
||||||
using Ombi.Api.Lidarr.Models;
|
using Ombi.Api.Lidarr.Models;
|
||||||
using Ombi.Api.MusicBrainz;
|
using Ombi.Api.MusicBrainz;
|
||||||
|
@ -41,6 +42,21 @@ namespace Ombi.Core.Engine.V2
|
||||||
_lidarrApi = lidarrApi;
|
_lidarrApi = lidarrApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ReleaseGroup> GetAlbum(string albumId)
|
||||||
|
{
|
||||||
|
var g = await _musicBrainzApi.GetAlbumInformation(albumId);
|
||||||
|
var release = new ReleaseGroup
|
||||||
|
{
|
||||||
|
ReleaseType = g.ReleaseGroup.PrimaryType,
|
||||||
|
Id = g.Id,
|
||||||
|
Title = g.Title,
|
||||||
|
ReleaseDate = g.ReleaseGroup.FirstReleaseDate,
|
||||||
|
};
|
||||||
|
|
||||||
|
await RunSearchRules(release);
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ArtistInformation> GetArtistInformation(string artistId)
|
public async Task<ArtistInformation> GetArtistInformation(string artistId)
|
||||||
{
|
{
|
||||||
var artist = await _musicBrainzApi.GetArtistInformation(artistId);
|
var artist = await _musicBrainzApi.GetArtistInformation(artistId);
|
||||||
|
@ -84,12 +100,19 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
if (lidarrArtistTask != null)
|
if (lidarrArtistTask != null)
|
||||||
{
|
{
|
||||||
var artistResult = await lidarrArtistTask;
|
try
|
||||||
info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
{
|
||||||
info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
var artistResult = await lidarrArtistTask;
|
||||||
info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||||
info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||||
info.Overview = artistResult.overview;
|
info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||||
|
info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||||
|
info.Overview = artistResult.overview;
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException)
|
||||||
|
{
|
||||||
|
// swallow, Lidarr probably doesn't have this artist
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
@ -118,7 +141,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
return new AlbumArt();
|
return new AlbumArt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ArtistInformation> GetArtistInformationByRequestId(int requestId)
|
public async Task<ArtistInformation> GetArtistInformationByRequestId(int requestId)
|
||||||
{
|
{
|
||||||
var request = await RequestService.MusicRequestRepository.Find(requestId);
|
var request = await RequestService.MusicRequestRepository.Find(requestId);
|
||||||
|
|
|
@ -19,6 +19,8 @@ using Ombi.Core.Settings;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using TraktSharp.Entities;
|
using TraktSharp.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Threading;
|
||||||
|
using Ombi.Api.TheMovieDb;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine.V2
|
namespace Ombi.Core.Engine.V2
|
||||||
{
|
{
|
||||||
|
@ -27,15 +29,17 @@ namespace Ombi.Core.Engine.V2
|
||||||
private readonly ITvMazeApi _tvMaze;
|
private readonly ITvMazeApi _tvMaze;
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
private readonly ITraktApi _traktApi;
|
private readonly ITraktApi _traktApi;
|
||||||
|
private readonly IMovieDbApi _movieApi;
|
||||||
|
|
||||||
public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
||||||
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService<OmbiSettings> s,
|
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService<OmbiSettings> s,
|
||||||
IRepository<RequestSubscription> sub)
|
IRepository<RequestSubscription> sub, IMovieDbApi movieApi)
|
||||||
: base(identity, service, r, um, memCache, s, sub)
|
: base(identity, service, r, um, memCache, s, sub)
|
||||||
{
|
{
|
||||||
_tvMaze = tvMaze;
|
_tvMaze = tvMaze;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_traktApi = trakt;
|
_traktApi = trakt;
|
||||||
|
_movieApi = movieApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,6 +110,39 @@ namespace Ombi.Core.Engine.V2
|
||||||
return await ProcessResult(mapped, traktInfoTask);
|
return await ProcessResult(mapped, traktInfoTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeId,
|
||||||
|
async () => await _tvMaze.ShowLookupByTheTvDbId(tvMazeId), DateTime.Now.AddHours(12));
|
||||||
|
if (tvdbshow == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// this is a best effort guess since TV maze do not provide the TheMovieDbId
|
||||||
|
var movieDbResults = await _movieApi.SearchTv(tvdbshow.name, tvdbshow.premiered.Substring(0, 4));
|
||||||
|
var potential = movieDbResults.FirstOrDefault();
|
||||||
|
tvDbId = potential.Id;
|
||||||
|
// end guess
|
||||||
|
|
||||||
|
var providers = await _movieApi.GetTvWatchProviders(tvDbId, cancellationToken);
|
||||||
|
var results = await GetUserWatchProvider(providers);
|
||||||
|
|
||||||
|
var data = new List<StreamingData>();
|
||||||
|
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
data.Add(new StreamingData
|
||||||
|
{
|
||||||
|
Logo = result.logo_path,
|
||||||
|
Order = result.display_priority,
|
||||||
|
StreamingProvider = result.provider_name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
|
private IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
|
||||||
{
|
{
|
||||||
var retVal = new List<SearchTvShowViewModel>();
|
var retVal = new List<SearchTvShowViewModel>();
|
||||||
|
@ -141,7 +178,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
{
|
{
|
||||||
item.Images.Medium = item.Images.Medium.ToHttpsUrl();
|
item.Images.Medium = item.Images.Medium.ToHttpsUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Cast?.Any() ?? false)
|
if (item.Cast?.Any() ?? false)
|
||||||
{
|
{
|
||||||
foreach (var cast in item.Cast)
|
foreach (var cast in item.Cast)
|
||||||
|
|
35
src/Ombi.Core/Helpers/WatchProviderParser.cs
Normal file
35
src/Ombi.Core/Helpers/WatchProviderParser.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Helpers
|
||||||
|
{
|
||||||
|
public static class WatchProviderParser
|
||||||
|
{
|
||||||
|
public static List<StreamData> GetUserWatchProviders(WatchProviders providers, OmbiUser user)
|
||||||
|
{
|
||||||
|
var data = new List<StreamData>();
|
||||||
|
|
||||||
|
if (providers?.Results == null)
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultsProp = providers.Results.GetType().GetProperties();
|
||||||
|
var matchingStreamingCountry = resultsProp.FirstOrDefault(x => x.Name.Equals(user.StreamingCountry, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
if (matchingStreamingCountry == null)
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = (WatchProviderData)matchingStreamingCountry.GetValue(providers.Results);
|
||||||
|
if (result == null || result.StreamInformation == null)
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return result.StreamInformation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models
|
||||||
public enum RecentlyAddedType
|
public enum RecentlyAddedType
|
||||||
{
|
{
|
||||||
Plex,
|
Plex,
|
||||||
Emby
|
Emby,
|
||||||
|
Jellyfin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ namespace Ombi.Core.Models.Requests
|
||||||
{
|
{
|
||||||
public int TheMovieDbId { get; set; }
|
public int TheMovieDbId { get; set; }
|
||||||
public string LanguageCode { get; set; } = "en";
|
public string LanguageCode { get; set; } = "en";
|
||||||
|
public string RequestOnBehalf { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is only set from a HTTP Header
|
/// This is only set from a HTTP Header
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Ombi.Core.Models.Requests
|
||||||
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string RequestedByAlias { get; set; }
|
public string RequestedByAlias { get; set; }
|
||||||
|
|
||||||
|
public string RequestOnBehalf { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SeasonsViewModel
|
public class SeasonsViewModel
|
||||||
|
|
|
@ -14,11 +14,12 @@ namespace Ombi.Core.Models.Search
|
||||||
public bool Available { get; set; }
|
public bool Available { get; set; }
|
||||||
public string PlexUrl { get; set; }
|
public string PlexUrl { get; set; }
|
||||||
public string EmbyUrl { get; set; }
|
public string EmbyUrl { get; set; }
|
||||||
|
public string JellyfinUrl { get; set; }
|
||||||
public string Quality { get; set; }
|
public string Quality { get; set; }
|
||||||
public abstract RequestType Type { get; }
|
public abstract RequestType Type { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule rule
|
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule/JellyfinAvailabilityRule rule
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
/// <value>
|
||||||
/// The custom identifier.
|
/// The custom identifier.
|
||||||
|
@ -35,4 +36,4 @@ namespace Ombi.Core.Models.Search
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool ShowSubscribe { get; set; }
|
public bool ShowSubscribe { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
src/Ombi.Core/Models/Search/V2/StreamingData.cs
Normal file
9
src/Ombi.Core/Models/Search/V2/StreamingData.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ombi.Core.Models.Search.V2
|
||||||
|
{
|
||||||
|
public class StreamingData
|
||||||
|
{
|
||||||
|
public int Order { get; set; }
|
||||||
|
public string StreamingProvider { get; set; }
|
||||||
|
public string Logo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models.UI
|
||||||
public UserType UserType { get; set; }
|
public UserType UserType { get; set; }
|
||||||
public int MovieRequestLimit { get; set; }
|
public int MovieRequestLimit { get; set; }
|
||||||
public int EpisodeRequestLimit { get; set; }
|
public int EpisodeRequestLimit { get; set; }
|
||||||
|
public string StreamingCountry { get; set; }
|
||||||
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
||||||
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
||||||
public RequestQuotaCountModel MusicRequestQuota { get; set; }
|
public RequestQuotaCountModel MusicRequestQuota { get; set; }
|
||||||
|
@ -30,4 +31,10 @@ namespace Ombi.Core.Models.UI
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class UserViewModelDropdown
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,6 +19,7 @@ namespace Ombi.Core.Models
|
||||||
{
|
{
|
||||||
LocalUser = 1,
|
LocalUser = 1,
|
||||||
PlexUser = 2,
|
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.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.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.FanartTv\Ombi.Api.FanartTv.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj" />
|
<ProjectReference Include="..\Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj" />
|
||||||
|
@ -40,4 +41,4 @@
|
||||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -108,10 +108,40 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
x.Series.TvDbId == item.TvDbId);
|
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)
|
if (epExists != null)
|
||||||
{
|
{
|
||||||
episode.Available = true;
|
episode.Available = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,11 +70,11 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||||
{
|
{
|
||||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname, s.IsJellyfin);
|
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname);
|
||||||
}
|
}
|
||||||
else
|
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();
|
return Success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
104
src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs
Normal file
104
src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
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
|
||||||
|
{
|
||||||
|
var firstServer = s.Servers?.FirstOrDefault();
|
||||||
|
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, firstServer.ServerId, firstServer.FullUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Discord;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
|
using Ombi.Api.Jellyfin;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Radarr;
|
using Ombi.Api.Radarr;
|
||||||
using Ombi.Api.Sonarr;
|
using Ombi.Api.Sonarr;
|
||||||
|
@ -47,6 +48,7 @@ using Ombi.Core.Senders;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Schedule.Jobs.Couchpotato;
|
using Ombi.Schedule.Jobs.Couchpotato;
|
||||||
using Ombi.Schedule.Jobs.Emby;
|
using Ombi.Schedule.Jobs.Emby;
|
||||||
|
using Ombi.Schedule.Jobs.Jellyfin;
|
||||||
using Ombi.Schedule.Jobs.Ombi;
|
using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
using Ombi.Schedule.Jobs.Sonarr;
|
using Ombi.Schedule.Jobs.Sonarr;
|
||||||
|
@ -65,6 +67,7 @@ using Quartz.Spi;
|
||||||
using Ombi.Api.MusicBrainz;
|
using Ombi.Api.MusicBrainz;
|
||||||
using Ombi.Api.Twilio;
|
using Ombi.Api.Twilio;
|
||||||
using Ombi.Api.CloudService;
|
using Ombi.Api.CloudService;
|
||||||
|
using Ombi.Api.RottenTomatoes;
|
||||||
|
|
||||||
namespace Ombi.DependencyInjection
|
namespace Ombi.DependencyInjection
|
||||||
{
|
{
|
||||||
|
@ -126,6 +129,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
||||||
services.AddTransient<IPlexApi, PlexApi>();
|
services.AddTransient<IPlexApi, PlexApi>();
|
||||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||||
|
services.AddTransient<IJellyfinApi, JellyfinApi>();
|
||||||
services.AddTransient<ISonarrApi, SonarrApi>();
|
services.AddTransient<ISonarrApi, SonarrApi>();
|
||||||
services.AddTransient<ISonarrV3Api, SonarrV3Api>();
|
services.AddTransient<ISonarrV3Api, SonarrV3Api>();
|
||||||
services.AddTransient<ISlackApi, SlackApi>();
|
services.AddTransient<ISlackApi, SlackApi>();
|
||||||
|
@ -153,8 +157,9 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
|
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
|
||||||
services.AddTransient<IWhatsAppApi, WhatsAppApi>();
|
services.AddTransient<IWhatsAppApi, WhatsAppApi>();
|
||||||
services.AddTransient<ICloudMobileNotification, CloudMobileNotification>();
|
services.AddTransient<ICloudMobileNotification, CloudMobileNotification>();
|
||||||
services.AddTransient<IBaseEmbyApi, JellyfinApi>();
|
|
||||||
services.AddTransient<IEmbyApiFactory, EmbyApiFactory>();
|
services.AddTransient<IEmbyApiFactory, EmbyApiFactory>();
|
||||||
|
services.AddTransient<IJellyfinApiFactory, JellyfinApiFactory>();
|
||||||
|
services.AddTransient<IRottenTomatoesApi, RottenTomatoesApi>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisterStore(this IServiceCollection services) {
|
public static void RegisterStore(this IServiceCollection services) {
|
||||||
|
@ -169,6 +174,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddScoped<ISettingsResolver, SettingsResolver>();
|
services.AddScoped<ISettingsResolver, SettingsResolver>();
|
||||||
services.AddScoped<IPlexContentRepository, PlexServerContentRepository>();
|
services.AddScoped<IPlexContentRepository, PlexServerContentRepository>();
|
||||||
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
|
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
|
||||||
|
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
|
||||||
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||||
|
|
||||||
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
||||||
|
@ -213,6 +219,9 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IEmbyContentSync, EmbyContentSync>();
|
services.AddTransient<IEmbyContentSync, EmbyContentSync>();
|
||||||
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
|
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
|
||||||
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
||||||
|
services.AddTransient<IJellyfinContentSync, JellyfinContentSync>();
|
||||||
|
services.AddTransient<IJellyfinEpisodeSync, JellyfinEpisodeSync>();
|
||||||
|
services.AddTransient<IJellyfinAvaliabilityChecker, JellyfinAvaliabilityChecker>();
|
||||||
services.AddTransient<IPlexEpisodeSync, PlexEpisodeSync>();
|
services.AddTransient<IPlexEpisodeSync, PlexEpisodeSync>();
|
||||||
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
||||||
services.AddTransient<IRadarrSync, RadarrSync>();
|
services.AddTransient<IRadarrSync, RadarrSync>();
|
||||||
|
@ -220,6 +229,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
||||||
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
||||||
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
||||||
|
services.AddTransient<IJellyfinUserImporter, JellyfinUserImporter>();
|
||||||
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
|
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
|
||||||
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
||||||
services.AddTransient<IProcessProvider, ProcessProvider>();
|
services.AddTransient<IProcessProvider, ProcessProvider>();
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Pushover\Ombi.Api.Pushover.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Pushover\Ombi.Api.Pushover.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||||
|
<ProjectReference Include="..\Ombi.Api.RottenTomatoes\Ombi.Api.RottenTomatoes.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Slack\Ombi.Api.Slack.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Slack\Ombi.Api.Slack.csproj" />
|
||||||
|
|
|
@ -3,6 +3,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
using Ombi.Api.CouchPotato;
|
using Ombi.Api.CouchPotato;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
using Ombi.Api.Emby.Models;
|
using Ombi.Api.Emby.Models;
|
||||||
|
using Ombi.Api.Jellyfin;
|
||||||
|
using Ombi.Api.Jellyfin.Models;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Plex.Models.Status;
|
using Ombi.Api.Plex.Models.Status;
|
||||||
using Ombi.Core.Settings;
|
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.CouchPotato;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
using Ombi.Api.Emby.Models;
|
using Ombi.Api.Emby.Models;
|
||||||
|
using Ombi.Api.Jellyfin;
|
||||||
|
using Ombi.Api.Jellyfin.Models;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Plex.Models.Status;
|
using Ombi.Api.Plex.Models.Status;
|
||||||
using Ombi.Api.SickRage;
|
using Ombi.Api.SickRage;
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace Ombi.HealthChecks
|
||||||
{
|
{
|
||||||
builder.AddCheck<PlexHealthCheck>("Plex", tags: new string[] { "MediaServer" });
|
builder.AddCheck<PlexHealthCheck>("Plex", tags: new string[] { "MediaServer" });
|
||||||
builder.AddCheck<EmbyHealthCheck>("Emby", 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<LidarrHealthCheck>("Lidarr", tags: new string[] { "DVR" });
|
||||||
builder.AddCheck<SonarrHealthCheck>("Sonarr", tags: new string[] { "DVR" });
|
builder.AddCheck<SonarrHealthCheck>("Sonarr", tags: new string[] { "DVR" });
|
||||||
builder.AddCheck<RadarrHealthCheck>("Radarr", tags: new string[] { "DVR" });
|
builder.AddCheck<RadarrHealthCheck>("Radarr", tags: new string[] { "DVR" });
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.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.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||||
|
|
|
@ -15,13 +15,6 @@ namespace Ombi.Helpers.Tests
|
||||||
return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url);
|
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
|
public static IEnumerable<TestCaseData> UrlData
|
||||||
{
|
{
|
||||||
get
|
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");
|
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 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
|
//web/index.html#!/details|item
|
||||||
string path = "item";
|
string path = "item";
|
||||||
if (isJellyfin)
|
|
||||||
{
|
|
||||||
path = "details";
|
|
||||||
}
|
|
||||||
if (customerServerUrl.HasValue())
|
if (customerServerUrl.HasValue())
|
||||||
{
|
{
|
||||||
if (!customerServerUrl.EndsWith("/"))
|
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 RadarrCacher => new EventId(2001);
|
||||||
public static EventId PlexEpisodeCacher => new EventId(2002);
|
public static EventId PlexEpisodeCacher => new EventId(2002);
|
||||||
public static EventId EmbyContentCacher => new EventId(2003);
|
public static EventId EmbyContentCacher => new EventId(2003);
|
||||||
|
public static EventId JellyfinContentCacher => new EventId(2012);
|
||||||
public static EventId PlexUserImporter => new EventId(2004);
|
public static EventId PlexUserImporter => new EventId(2004);
|
||||||
public static EventId EmbyUserImporter => new EventId(2005);
|
public static EventId EmbyUserImporter => new EventId(2005);
|
||||||
|
public static EventId JellyfinUserImporter => new EventId(2013);
|
||||||
public static EventId SonarrCacher => new EventId(2006);
|
public static EventId SonarrCacher => new EventId(2006);
|
||||||
public static EventId CouchPotatoCacher => new EventId(2007);
|
public static EventId CouchPotatoCacher => new EventId(2007);
|
||||||
public static EventId PlexContentCacher => new EventId(2008);
|
public static EventId PlexContentCacher => new EventId(2008);
|
||||||
|
@ -43,4 +45,4 @@ namespace Ombi.Helpers
|
||||||
public static EventId Updater => new EventId(6000);
|
public static EventId Updater => new EventId(6000);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,8 @@
|
||||||
public string StoragePath { get; set; }
|
public string StoragePath { get; set; }
|
||||||
|
|
||||||
public string SecurityKey { get; set; }
|
public string SecurityKey { get; set; }
|
||||||
|
#if DEBUG
|
||||||
|
= "test";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -58,7 +58,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
{
|
{
|
||||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||||
.SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Failed");
|
.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,
|
Title = tvShow.Name,
|
||||||
Type = EmbyMediaType.Series,
|
Type = EmbyMediaType.Series,
|
||||||
EmbyId = tvShow.Id,
|
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
|
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);
|
Api = _apiFactory.CreateClient(settings);
|
||||||
|
|
||||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
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();
|
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
|
||||||
foreach (var server in settings.Servers)
|
foreach (var server in settings.Servers)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,8 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
ProviderUserId = embyUser.Id,
|
ProviderUserId = embyUser.Id,
|
||||||
Alias = isConnectUser ? embyUser.Name : string.Empty,
|
Alias = isConnectUser ? embyUser.Name : string.Empty,
|
||||||
MovieRequestLimit = userManagementSettings.MovieRequestLimit,
|
MovieRequestLimit = userManagementSettings.MovieRequestLimit,
|
||||||
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit
|
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit,
|
||||||
|
StreamingCountry = userManagementSettings.DefaultStreamingCountry
|
||||||
};
|
};
|
||||||
var result = await _userManager.CreateAsync(newUser);
|
var result = await _userManager.CreateAsync(newUser);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
|
@ -180,4 +181,4 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
GC.SuppressFinalize(this);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
174
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs
Normal file
174
src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
#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,
|
||||||
|
StreamingCountry = userManagementSettings.DefaultStreamingCountry
|
||||||
|
};
|
||||||
|
_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.Core.Settings.Models.External;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Schedule.Jobs.Emby;
|
using Ombi.Schedule.Jobs.Emby;
|
||||||
|
using Ombi.Schedule.Jobs.Jellyfin;
|
||||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using Quartz;
|
using Quartz;
|
||||||
|
@ -14,12 +15,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
|
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
|
||||||
{
|
{
|
||||||
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
|
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
|
||||||
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo)
|
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IJellyfinContentRepository jellyfinRepo)
|
||||||
{
|
{
|
||||||
_settings = s;
|
_settings = s;
|
||||||
_log = log;
|
_log = log;
|
||||||
_plexRepo = plexRepo;
|
_plexRepo = plexRepo;
|
||||||
_embyRepo = embyRepo;
|
_embyRepo = embyRepo;
|
||||||
|
_jellyfinRepo = jellyfinRepo;
|
||||||
_settings.ClearCache();
|
_settings.ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +29,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
private readonly ILogger _log;
|
private readonly ILogger _log;
|
||||||
private readonly IPlexContentRepository _plexRepo;
|
private readonly IPlexContentRepository _plexRepo;
|
||||||
private readonly IEmbyContentRepository _embyRepo;
|
private readonly IEmbyContentRepository _embyRepo;
|
||||||
|
private readonly IJellyfinContentRepository _jellyfinRepo;
|
||||||
|
|
||||||
public async Task Execute(IJobExecutionContext job)
|
public async Task Execute(IJobExecutionContext job)
|
||||||
{
|
{
|
||||||
|
@ -34,6 +37,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
await RemovePlexData();
|
await RemovePlexData();
|
||||||
await RemoveEmbyData();
|
await RemoveEmbyData();
|
||||||
|
await RemoveJellyfinData();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
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()
|
private async Task RemovePlexData()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -108,4 +134,4 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,16 +38,17 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
public class NewsletterJob : HtmlTemplateGenerator, INewsletterJob
|
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,
|
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
||||||
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
||||||
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
||||||
ILidarrApi lidarrApi, IExternalRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings,
|
ILidarrApi lidarrApi, IExternalRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings,
|
||||||
ISettingsService<OmbiSettings> ombiSettings, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings
|
ISettingsService<OmbiSettings> ombiSettings, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings, ISettingsService<JellyfinSettings> jellyfinSettings,
|
||||||
, IHubContext<NotificationHub> notification, IRefreshMetadata refreshMetadata)
|
IHubContext<NotificationHub> notification, IRefreshMetadata refreshMetadata)
|
||||||
{
|
{
|
||||||
_plex = plex;
|
_plex = plex;
|
||||||
_emby = emby;
|
_emby = emby;
|
||||||
|
_jellyfin = jellyfin;
|
||||||
_recentlyAddedLog = addedLog;
|
_recentlyAddedLog = addedLog;
|
||||||
_movieApi = movieApi;
|
_movieApi = movieApi;
|
||||||
_tvApi = tvApi;
|
_tvApi = tvApi;
|
||||||
|
@ -64,6 +65,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
_ombiSettings = ombiSettings;
|
_ombiSettings = ombiSettings;
|
||||||
_plexSettings = plexSettings;
|
_plexSettings = plexSettings;
|
||||||
_embySettings = embySettings;
|
_embySettings = embySettings;
|
||||||
|
_jellyfinSettings = jellyfinSettings;
|
||||||
_notification = notification;
|
_notification = notification;
|
||||||
_ombiSettings.ClearCache();
|
_ombiSettings.ClearCache();
|
||||||
_plexSettings.ClearCache();
|
_plexSettings.ClearCache();
|
||||||
|
@ -74,6 +76,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
|
|
||||||
private readonly IPlexContentRepository _plex;
|
private readonly IPlexContentRepository _plex;
|
||||||
private readonly IEmbyContentRepository _emby;
|
private readonly IEmbyContentRepository _emby;
|
||||||
|
private readonly IJellyfinContentRepository _jellyfin;
|
||||||
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||||
private readonly IMovieDbApi _movieApi;
|
private readonly IMovieDbApi _movieApi;
|
||||||
private readonly ITvMazeApi _tvApi;
|
private readonly ITvMazeApi _tvApi;
|
||||||
|
@ -90,6 +93,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||||
|
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||||
private readonly IHubContext<NotificationHub> _notification;
|
private readonly IHubContext<NotificationHub> _notification;
|
||||||
private readonly IRefreshMetadata _refreshMetadata;
|
private readonly IRefreshMetadata _refreshMetadata;
|
||||||
|
|
||||||
|
@ -123,36 +127,46 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
// Get the Content
|
// Get the Content
|
||||||
var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking();
|
var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking();
|
||||||
var embyContent = _emby.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 lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking().ToList().Where(x => x.FullyAvailable);
|
||||||
|
|
||||||
var addedLog = _recentlyAddedLog.GetAll();
|
var addedLog = _recentlyAddedLog.GetAll();
|
||||||
var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId).ToHashSet();
|
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 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 addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId).ToHashSet();
|
||||||
|
|
||||||
var addedPlexEpisodesLogIds =
|
var addedPlexEpisodesLogIds =
|
||||||
addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode);
|
addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode);
|
||||||
var addedEmbyEpisodesLogIds =
|
var addedEmbyEpisodesLogIds =
|
||||||
addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode);
|
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
|
// 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 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 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 plexContentMoviesToSend = plexContentLocalDataset.Where(x => !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet();
|
||||||
var embyContentMoviesToSend = embyContentLocalDataset.Where(x => !addedEmbyMoviesLogIds.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();
|
var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet();
|
||||||
_log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count());
|
_log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count());
|
||||||
_log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.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());
|
_log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count());
|
||||||
|
|
||||||
// Find the movies that do not yet have MovieDbIds
|
// Find the movies that do not yet have MovieDbIds
|
||||||
var needsMovieDbPlex = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet();
|
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 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 newPlexMovies = await GetMoviesWithoutId(addedPlexMovieLogIds, needsMovieDbPlex);
|
||||||
var newEmbyMovies = await GetMoviesWithoutId(addedEmbyMoviesLogIds, needsMovieDbEmby);
|
var newEmbyMovies = await GetMoviesWithoutId(addedEmbyMoviesLogIds, needsMovieDbEmby);
|
||||||
|
var newJellyfinMovies = await GetMoviesWithoutId(addedJellyfinMoviesLogIds, needsMovieDbJellyfin);
|
||||||
plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet();
|
plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet();
|
||||||
embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet();
|
embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet();
|
||||||
|
jellyfinContentMoviesToSend = jellyfinContentMoviesToSend.Union(newJellyfinMovies).ToHashSet();
|
||||||
|
|
||||||
plexContentMoviesToSend = plexContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet();
|
plexContentMoviesToSend = plexContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet();
|
||||||
embyContentMoviesToSend = embyContentMoviesToSend.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);
|
FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds);
|
||||||
var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(),
|
var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(),
|
||||||
addedEmbyEpisodesLogIds);
|
addedEmbyEpisodesLogIds);
|
||||||
|
var jellyfinEpisodesToSend = FilterJellyfinEpisodes(_jellyfin.GetAllEpisodes().Include(x => x.Series).AsNoTracking(),
|
||||||
|
addedJellyfinEpisodesLogIds);
|
||||||
|
|
||||||
_log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count());
|
_log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count());
|
||||||
_log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.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 plexSettings = await _plexSettings.GetSettingsAsync();
|
||||||
var embySettings = await _embySettings.GetSettingsAsync();
|
var embySettings = await _embySettings.GetSettingsAsync();
|
||||||
|
var jellyfinSettings = await _jellyfinSettings.GetSettingsAsync();
|
||||||
var body = string.Empty;
|
var body = string.Empty;
|
||||||
if (test)
|
if (test)
|
||||||
{
|
{
|
||||||
var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10);
|
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 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 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 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();
|
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
|
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())
|
if (body.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -285,6 +305,34 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
SeasonNumber = p.SeasonNumber
|
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);
|
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -349,6 +397,20 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
return result.ToHashSet();
|
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)
|
private async Task UpdateTheMovieDbId(IEnumerable<PlexServerContent> content)
|
||||||
{
|
{
|
||||||
foreach (var movie in content)
|
foreach (var movie in content)
|
||||||
|
@ -358,6 +420,10 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var entity = await _plex.Find(movie.Id);
|
var entity = await _plex.Find(movie.Id);
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
entity.TheMovieDbId = movie.TheMovieDbId;
|
entity.TheMovieDbId = movie.TheMovieDbId;
|
||||||
_plex.UpdateWithoutSave(entity);
|
_plex.UpdateWithoutSave(entity);
|
||||||
}
|
}
|
||||||
|
@ -379,6 +445,21 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
await _plex.SaveChangesAsync();
|
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)
|
public async Task Execute(IJobExecutionContext job)
|
||||||
{
|
{
|
||||||
var newsletterSettings = await _newsletterSettings.GetSettingsAsync();
|
var newsletterSettings = await _newsletterSettings.GetSettingsAsync();
|
||||||
|
@ -419,6 +500,23 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
return itemsToReturn;
|
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)
|
private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings)
|
||||||
{
|
{
|
||||||
var resolver = new NotificationMessageResolver();
|
var resolver = new NotificationMessageResolver();
|
||||||
|
@ -429,8 +527,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
return resolver.ParseMessage(template, curlys);
|
return resolver.ParseMessage(template, curlys);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
|
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend, IQueryable<JellyfinContent> jellyfinContentToSend,
|
||||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings, EmbySettings embySettings,
|
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<JellyfinEpisode> jellyfinEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings, EmbySettings embySettings, JellyfinSettings jellyfinSettings,
|
||||||
PlexSettings plexSettings)
|
PlexSettings plexSettings)
|
||||||
{
|
{
|
||||||
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
||||||
|
@ -438,6 +536,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
|
|
||||||
var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||||
var embyMovies = embyContentToSend.Where(x => x.Type == EmbyMediaType.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)
|
if ((plexMovies.Any() || embyMovies.Any()) && !settings.DisableMovies)
|
||||||
{
|
{
|
||||||
sb.Append("<h1 style=\"text-align: center; max-width: 1042px;\">New Movies</h1><br /><br />");
|
sb.Append("<h1 style=\"text-align: center; max-width: 1042px;\">New Movies</h1><br /><br />");
|
||||||
|
@ -457,6 +556,11 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
|
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("</tr>");
|
||||||
sb.Append("</table>");
|
sb.Append("</table>");
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
|
@ -464,7 +568,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
sb.Append("</table>");
|
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("<br /><br /><h1 style=\"text-align: center; max-width: 1042px;\">New TV</h1><br /><br />");
|
||||||
sb.Append(
|
sb.Append(
|
||||||
|
@ -483,6 +587,11 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
await ProcessEmbyTv(embyEp, sb, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
|
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("</tr>");
|
||||||
sb.Append("</table>");
|
sb.Append("</table>");
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
|
@ -638,6 +747,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)
|
private void CreateMovieHtmlContent(StringBuilder sb, MovieResponseDto info, string mediaurl)
|
||||||
{
|
{
|
||||||
AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}");
|
AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}");
|
||||||
|
@ -981,6 +1143,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)
|
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...
|
//NOTE: BR have to be in TD's as per html spec or it will be put outside of the table...
|
||||||
|
@ -1040,4 +1325,4 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ombi.Api.Emby;
|
using Ombi.Api.Emby;
|
||||||
|
using Ombi.Api.Jellyfin;
|
||||||
using Ombi.Api.TheMovieDb;
|
using Ombi.Api.TheMovieDb;
|
||||||
using Ombi.Api.TheMovieDb.Models;
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Api.TvMaze;
|
using Ombi.Api.TvMaze;
|
||||||
|
@ -14,6 +15,7 @@ using Ombi.Core.Settings.Models.External;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Hubs;
|
using Ombi.Hubs;
|
||||||
using Ombi.Schedule.Jobs.Emby;
|
using Ombi.Schedule.Jobs.Emby;
|
||||||
|
using Ombi.Schedule.Jobs.Jellyfin;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
@ -23,31 +25,41 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
public class RefreshMetadata : IRefreshMetadata
|
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,
|
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;
|
_plexRepo = plexRepo;
|
||||||
_embyRepo = embyRepo;
|
_embyRepo = embyRepo;
|
||||||
|
_jellyfinRepo = jellyfinRepo;
|
||||||
_log = log;
|
_log = log;
|
||||||
_movieApi = movieApi;
|
_movieApi = movieApi;
|
||||||
_tvApi = tvApi;
|
_tvApi = tvApi;
|
||||||
_plexSettings = plexSettings;
|
_plexSettings = plexSettings;
|
||||||
_embySettings = embySettings;
|
_embySettings = embySettings;
|
||||||
_embyApiFactory = embyApi;
|
_embyApiFactory = embyApi;
|
||||||
|
_jellyfinSettings = jellyfinSettings;
|
||||||
|
_jellyfinApiFactory = jellyfinApi;
|
||||||
_notification = notification;
|
_notification = notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexContentRepository _plexRepo;
|
private readonly IPlexContentRepository _plexRepo;
|
||||||
private readonly IEmbyContentRepository _embyRepo;
|
private readonly IEmbyContentRepository _embyRepo;
|
||||||
|
private readonly IJellyfinContentRepository _jellyfinRepo;
|
||||||
private readonly ILogger _log;
|
private readonly ILogger _log;
|
||||||
private readonly IMovieDbApi _movieApi;
|
private readonly IMovieDbApi _movieApi;
|
||||||
private readonly ITvMazeApi _tvApi;
|
private readonly ITvMazeApi _tvApi;
|
||||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||||
|
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||||
private readonly IEmbyApiFactory _embyApiFactory;
|
private readonly IEmbyApiFactory _embyApiFactory;
|
||||||
|
private readonly IJellyfinApiFactory _jellyfinApiFactory;
|
||||||
private readonly IHubContext<NotificationHub> _notification;
|
private readonly IHubContext<NotificationHub> _notification;
|
||||||
private IEmbyApi EmbyApi { get; set; }
|
private IEmbyApi EmbyApi { get; set; }
|
||||||
|
private IJellyfinApi JellyfinApi { get; set; }
|
||||||
|
|
||||||
public async Task Execute(IJobExecutionContext job)
|
public async Task Execute(IJobExecutionContext job)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +84,14 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
|
|
||||||
await OmbiQuartz.TriggerJob(nameof(IEmbyAvaliabilityChecker), "Emby");
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -107,6 +127,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
await StartEmbyTv();
|
await StartEmbyTv();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task StartJellyfin(JellyfinSettings s)
|
||||||
|
{
|
||||||
|
JellyfinApi = _jellyfinApiFactory.CreateClient(s);
|
||||||
|
await StartJellyfinMovies(s);
|
||||||
|
await StartJellyfinTv();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task StartPlexTv(List<PlexServerContent> allTv)
|
private async Task StartPlexTv(List<PlexServerContent> allTv)
|
||||||
{
|
{
|
||||||
foreach (var show in 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, RequestType.TvShow);
|
||||||
|
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)
|
private async Task StartPlexMovies(List<PlexServerContent> allMovies)
|
||||||
{
|
{
|
||||||
foreach (var movie in 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, RequestType.Movie);
|
||||||
|
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)
|
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);
|
_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);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,12 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
_log.LogInformation("Could not create Plex user since the have no username, PlexUserId: {0}", plexUser.Id);
|
_log.LogInformation("Could not create Plex user since the have no username, PlexUserId: {0}", plexUser.Id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((plexUser.Email.HasValue()) && await _userManager.FindByEmailAsync(plexUser.Email) != null)
|
||||||
|
{
|
||||||
|
_log.LogWarning($"Cannot add user {plexUser.Username} because their email address is already in Ombi, skipping this user");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Create this users
|
// Create this users
|
||||||
// We do not store a password against the user since they will authenticate via Plex
|
// We do not store a password against the user since they will authenticate via Plex
|
||||||
var newUser = new OmbiUser
|
var newUser = new OmbiUser
|
||||||
|
@ -98,7 +104,8 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
Email = plexUser?.Email ?? string.Empty,
|
Email = plexUser?.Email ?? string.Empty,
|
||||||
Alias = string.Empty,
|
Alias = string.Empty,
|
||||||
MovieRequestLimit = userManagementSettings.MovieRequestLimit,
|
MovieRequestLimit = userManagementSettings.MovieRequestLimit,
|
||||||
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit
|
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit,
|
||||||
|
StreamingCountry = userManagementSettings.DefaultStreamingCountry
|
||||||
};
|
};
|
||||||
_log.LogInformation("Creating Plex user {0}", newUser.UserName);
|
_log.LogInformation("Creating Plex user {0}", newUser.UserName);
|
||||||
var result = await _userManager.CreateAsync(newUser);
|
var result = await _userManager.CreateAsync(newUser);
|
||||||
|
@ -161,7 +168,8 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
UserName = plexAdmin.username ?? plexAdmin.id,
|
UserName = plexAdmin.username ?? plexAdmin.id,
|
||||||
ProviderUserId = plexAdmin.id,
|
ProviderUserId = plexAdmin.id,
|
||||||
Email = plexAdmin.email ?? string.Empty,
|
Email = plexAdmin.email ?? string.Empty,
|
||||||
Alias = string.Empty
|
Alias = string.Empty,
|
||||||
|
StreamingCountry = settings.DefaultStreamingCountry
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await _userManager.CreateAsync(newUser);
|
var result = await _userManager.CreateAsync(newUser);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.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.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||||
|
@ -37,4 +38,4 @@
|
||||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Ombi.Helpers;
|
||||||
using Ombi.Schedule.Jobs;
|
using Ombi.Schedule.Jobs;
|
||||||
using Ombi.Schedule.Jobs.Couchpotato;
|
using Ombi.Schedule.Jobs.Couchpotato;
|
||||||
using Ombi.Schedule.Jobs.Emby;
|
using Ombi.Schedule.Jobs.Emby;
|
||||||
|
using Ombi.Schedule.Jobs.Jellyfin;
|
||||||
using Ombi.Schedule.Jobs.Lidarr;
|
using Ombi.Schedule.Jobs.Lidarr;
|
||||||
using Ombi.Schedule.Jobs.Ombi;
|
using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
|
@ -51,6 +52,7 @@ namespace Ombi.Schedule
|
||||||
// Run configuration
|
// Run configuration
|
||||||
await AddPlex(s);
|
await AddPlex(s);
|
||||||
await AddEmby(s);
|
await AddEmby(s);
|
||||||
|
await AddJellyfin(s);
|
||||||
await AddDvrApps(s);
|
await AddDvrApps(s);
|
||||||
await AddSystem(s);
|
await AddSystem(s);
|
||||||
await AddNotifications(s);
|
await AddNotifications(s);
|
||||||
|
@ -98,9 +100,18 @@ namespace Ombi.Schedule
|
||||||
await OmbiQuartz.Instance.AddJob<IEmbyAvaliabilityChecker>(nameof(IEmbyAvaliabilityChecker), "Emby", null);
|
await OmbiQuartz.Instance.AddJob<IEmbyAvaliabilityChecker>(nameof(IEmbyAvaliabilityChecker), "Emby", null);
|
||||||
await OmbiQuartz.Instance.AddJob<IEmbyUserImporter>(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s));
|
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)
|
private static async Task AddNotifications(JobSettings s)
|
||||||
{
|
{
|
||||||
await OmbiQuartz.Instance.AddJob<INotificationService>(nameof(INotificationService), "Notifications", null);
|
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 sealed class EmbySettings : Ombi.Settings.Settings.Models.Settings
|
||||||
{
|
{
|
||||||
public bool Enable { get; set; }
|
public bool Enable { get; set; }
|
||||||
public bool IsJellyfin { get; set; }
|
|
||||||
public List<EmbyServers> Servers { get; set; } = new List<EmbyServers>();
|
public List<EmbyServers> Servers { get; set; } = new List<EmbyServers>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,4 +18,4 @@ namespace Ombi.Core.Settings.Models.External
|
||||||
public string ServerHostname { get; set; }
|
public string ServerHostname { get; set; }
|
||||||
public bool EnableEpisodeSearching { 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; }
|
||||||
|
}
|
||||||
|
}
|
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