mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 05:13:18 -07:00
Merge with develop
This commit is contained in:
commit
fb59f232e0
388 changed files with 13462 additions and 2532 deletions
|
@ -63,22 +63,11 @@ stages:
|
|||
$response = Invoke-WebRequest -Uri "https://ombireleasenote.azurewebsites.net/api/ReleaseNotesFunction?buildId=$(Build.BuildId)"
|
||||
Write-Host "##vso[task.setvariable variable=ReleaseNotes;]$response"
|
||||
|
||||
# - task: GitHubRelease@1
|
||||
# inputs:
|
||||
# gitHubConnection: 'github.com_tidusjar'
|
||||
# repositoryName: 'tidusjar/Ombi'
|
||||
# action: 'create'
|
||||
# target: '$(Build.SourceVersion)'
|
||||
# tagSource: 'userSpecifiedTag'
|
||||
# tag: '$(gitTag)'
|
||||
# isDraft: true
|
||||
# changeLogCompareToRelease: 'lastNonDraftRelease'
|
||||
# changeLogType: 'commitBased'
|
||||
|
||||
- task: GitHubRelease@1
|
||||
displayName: 'Ombi.Releases Release'
|
||||
inputs:
|
||||
gitHubConnection: 'github.com_tidusjar'
|
||||
repositoryName: 'tidusjar/Ombi.Releases'
|
||||
gitHubConnection: 'PAT'
|
||||
repositoryName: 'Ombi-app/Ombi.Releases'
|
||||
action: 'create'
|
||||
target: 'c7fcbb77b58aef1076d635a9ef99e4374abc8672'
|
||||
tagSource: 'userSpecifiedTag'
|
||||
|
@ -91,3 +80,38 @@ stages:
|
|||
isPreRelease: true
|
||||
changeLogCompareToRelease: 'lastNonDraftRelease'
|
||||
changeLogType: 'commitBased'
|
||||
|
||||
- task: GitHubRelease@1
|
||||
displayName: 'Ombi Release'
|
||||
inputs:
|
||||
gitHubConnection: 'PAT'
|
||||
repositoryName: 'Ombi-app/Ombi'
|
||||
action: 'create'
|
||||
target: '$(Build.SourceVersion)'
|
||||
tagSource: 'userSpecifiedTag'
|
||||
tag: '$(gitTag)'
|
||||
releaseNotesSource: 'inline'
|
||||
releaseNotesInline: '$(ReleaseNotes)'
|
||||
assets: |
|
||||
$(System.ArtifactsDirectory)/**/*.zip
|
||||
$(System.ArtifactsDirectory)/**/*.tar.gz
|
||||
isPreRelease: true
|
||||
changeLogCompareToRelease: 'lastNonDraftRelease'
|
||||
changeLogType: 'commitBased'
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: "Trigger APT build"
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
$body = @{
|
||||
"ref"="main"
|
||||
"inputs"= @{"version"= "$(gitTag)"}
|
||||
} | ConvertTo-Json
|
||||
|
||||
$header = @{
|
||||
"Accept"="application/vnd.github.v3+json"
|
||||
"Authorization"="Bearer ${env:APTPAT}"
|
||||
}
|
||||
|
||||
Invoke-RestMethod -Uri "https://api.github.com/repos/Ombi-app/Ombi.Apt/actions/workflows/build-deb.yml/dispatches" -Method 'Post' -Body $body -Headers $header
|
|
@ -27,4 +27,4 @@ variables:
|
|||
value: "4.0.$(Build.BuildId)"
|
||||
|
||||
- name: isMain
|
||||
value: $[eq(variables['Build.SourceBranch'], 'refs/heads/feature/v4')]
|
||||
value: $[or(eq(variables['Build.SourceBranch'], 'refs/heads/develop'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))]
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -27,7 +27,7 @@ If applicable, a snippet of the logs that seems relevant to the bug if present.
|
|||
- OS: [e.g. iOS]
|
||||
|
||||
**Ombi Version (please complete the following information):**
|
||||
- Version [e.g. 3.0.1158]
|
||||
- Version [e.g. 4.0.958]
|
||||
- Media Server [e.g. Plex]
|
||||
- Database Type: SQLite (Please change if using MySQL)
|
||||
|
||||
|
|
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,8 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Wiki
|
||||
url: https://github.com/tidusjar/Ombi/wiki
|
||||
about: The Ombi wiki should help guide you through installation and setup as well as help resolve common problems and answer frequently asked questions
|
||||
- name: Docs
|
||||
url: https://docs.ombi.app/
|
||||
about: The Ombi documentation should help guide you through installation and setup as well as help resolve common problems and answer frequently asked questions
|
||||
- name: Reddit support
|
||||
url: https://www.reddit.com/r/Ombi
|
||||
about: Ask questions about Ombi
|
||||
|
|
|
@ -661,7 +661,7 @@
|
|||
|
||||
- Added capture of anonymous analytical data. [tidusjar]
|
||||
|
||||
- Added {AvailableDate} as a Notification Variable, this is the date the request was marked as available. See here: https://github.com/tidusjar/Ombi/wiki/Notification-Template-Variables. [tidusjar]
|
||||
- Added {AvailableDate} as a Notification Variable, this is the date the request was marked as available. See here: https://docs.ombi.app/info/notification-template-variables/. [tidusjar]
|
||||
|
||||
- Added the ability to search movies via the movie db with a different language! [tidusjar]
|
||||
|
||||
|
|
104
README.md
104
README.md
|
@ -1,14 +1,25 @@
|
|||
|
||||

|
||||
____
|
||||
[](https://discord.gg/Sa7wNWb)
|
||||
[](https://hub.docker.com/r/linuxserver/ombi/)
|
||||
[](https://github.com/tidusjar/Ombi)
|
||||
[](https://github.com/ombi-app/Ombi)
|
||||
[](http://www.firsttimersonly.com/)
|
||||
[](https://crowdin.com/project/ombi)
|
||||
|
||||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
# Welcome
|
||||
|
||||
Ombi is your friendly media request tool, automatically syncs with your media with your media servers!
|
||||
Don't worry, it's grandma friendly, and more importantly; has wife approval certification 😂
|
||||
|
||||
| Service | Stable | Develop
|
||||
|----------|:---------------------------:|:----------------------------:|
|
||||
| Build Status | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&repoName=Ombi-app%2FOmbi&branchName=develop) | [](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4)
|
||||
| Download |[](https://github.com/Ombi-app/Ombi/releases) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [](https://github.com/ombi-app/ombi/releases) |
|
||||
|
||||
# Feature Requests
|
||||
Feature requests are handled on Feature Upvote.
|
||||
|
||||
|
@ -16,14 +27,13 @@ Search the existing requests to see if your suggestion has already been submitte
|
|||
(If a similar request exists, please vote, or add additional comments to the request)
|
||||
|
||||
#### [](https://features.ombi.io)
|
||||
___
|
||||
|
||||
|
||||
[](https://twitter.com/intent/follow?screen_name=tidusjar)
|
||||
<!---[](https://twitter.com/intent/follow?screen_name=tidusjar)--->
|
||||
|
||||
Follow me developing Ombi!
|
||||
<!---Follow me developing Ombi!--->
|
||||
|
||||
[](https://www.twitch.tv/tidusjar)
|
||||
<!---[](https://www.twitch.tv/tidusjar) --->
|
||||
|
||||
|
||||
___
|
||||
|
@ -31,23 +41,9 @@ ___
|
|||
<br>
|
||||
_**Note:** There is no longer an iOS app due to complications outside of our control._
|
||||
|
||||
___
|
||||
|
||||
We also now have merch up on Teespring!
|
||||
|
||||
[EU Store](https://teespring.com/stores/ombi-eu)
|
||||
[US Store](https://teespring.com/stores/ombi-us)
|
||||
|
||||
___
|
||||
|
||||
|
||||
| Service | Stable | Develop | V4 |
|
||||
|----------|:---------------------------:|:----------------------------:|:----------------------------:|
|
||||
| Build Status | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) | [](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4)
|
||||
| Download |[](https://github.com/tidusjar/Ombi/releases) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [](https://github.com/tidusjar/ombi.releases/releases) |
|
||||
# Features
|
||||
Here are some of the features Ombi V3 has:
|
||||
* Now working without crashes on Linux.
|
||||
Here are some of the features Ombi has:
|
||||
* Lets users request Movies, Music, and TV Shows (whether it being the entire series, an entire season, or even single episodes.)
|
||||
* Easily manage your requests
|
||||
* Allows you to set specific users to automatically have requests approved and added to the relevant service (Sonarr/Radarr/Lidarr/Couchpotato etc)
|
||||
|
@ -58,72 +54,16 @@ Here are some of the features Ombi V3 has:
|
|||
* Will show if the request is already on plex or even if it's already monitored.
|
||||
* Automatically updates the status of requests when they are available on Plex/Emby
|
||||
* Slick, responsive and mobile friendly UI
|
||||
* Ombi will automatically update itself :) (YMMV)
|
||||
* Very fast!
|
||||
|
||||
### Integration
|
||||
We integrate with the following applications:
|
||||
* Plex Media Server
|
||||
* Emby
|
||||
* Jellyfin
|
||||
* Sonarr V2 and V3
|
||||
* Radarr V2
|
||||
* Lidarr
|
||||
* DogNzb
|
||||
* Couch Potato
|
||||
|
||||
|
||||
### Notifications
|
||||
Supported notifications:
|
||||
* Mobile
|
||||
* SMTP Notifications (Email)
|
||||
* Discord
|
||||
* Slack
|
||||
* Pushbullet
|
||||
* Pushover
|
||||
* Mattermost
|
||||
* Telegram
|
||||
* Gotify
|
||||
* Twilio
|
||||
* Webhook
|
||||
|
||||
### The difference between Version 4 and 3
|
||||
|
||||
Over the last year, we focused on the main functions on Ombi, a complete rewrite while making it better, faster and more stable.
|
||||
We have already done most of the work, but some features are still be missing in this first version.
|
||||
We are planning to bring back these features in V3 but for now you can find a list below with a quick comparison of features between v4 and v3.
|
||||
|
||||
|
||||
| Service | Version 4 (Beta) | Version 3 (Stable)|
|
||||
|----------|:----------:|:----------:|
|
||||
| Multiple Plex/Emby/Jellyfin Servers | Yes | Yes |
|
||||
| Emby/Jellyfin & Plex support | Yes | Yes |
|
||||
| Mono dependency | No | No |
|
||||
| Plex OAuth support | Yes | Yes |
|
||||
| Login page | Yes (brand new) | Yes |
|
||||
| Discovery page | Yes (brand new) | No |
|
||||
| Request a movie collection | Yes (brand new) | No |
|
||||
| Auto Delete Available Requests | Yes (brand new) | No |
|
||||
| Report issues | Yes | Yes |
|
||||
| Notifications support | Yes | Yes |
|
||||
| Custom Notification Messages | Yes | Yes |
|
||||
| Sending newsletters | Yes | Yes |
|
||||
| Send a Mass Email | Yes | Yes |
|
||||
| SickRage | Yes | Yes |
|
||||
| CouchPotato | Yes | Yes |
|
||||
| DogNzb | Yes | Yes |
|
||||
| Headphones | No | Yes |
|
||||
| Lidarr | Yes | Yes |
|
||||
|
||||
# Preview
|
||||
|
||||

|
||||

|
||||
|
||||
# Installation
|
||||
|
||||
[Installation Guide](https://github.com/tidusjar/Ombi/wiki/Installation)
|
||||
[Here for Reverse Proxy Config Examples](https://github.com/tidusjar/Ombi/wiki/Reverse-Proxy-Examples)
|
||||
[PlexGuide.com - Ombi Deployment & 101 Demonstration!](https://www.youtube.com/watch?v=QPNlqqkjNJw&feature=youtu.be)
|
||||
[Installation Guide](https://docs.ombi.app/installation/)
|
||||
[Here for Reverse Proxy Config Examples](https://docs.ombi.app/info/reverse-proxy/)
|
||||
|
||||
# Contributors
|
||||
|
||||
|
@ -139,9 +79,3 @@ If you feel like donating you can donate with the below buttons!
|
|||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
### A massive thanks to everyone for all their help!
|
||||
|
||||
|
||||
### Sponsors ###
|
||||
- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
|
||||
- [ReSharper](http://www.jetbrains.com/resharper/)
|
||||
- [BrowserStack](https://www.browserstack.com) for allowing us to use their platform for testing
|
||||
|
|
|
@ -25,10 +25,6 @@ namespace Ombi.Api.Emby
|
|||
|
||||
public IEmbyApi CreateClient(EmbySettings settings)
|
||||
{
|
||||
if (settings.IsJellyfin)
|
||||
{
|
||||
return new JellyfinApi(_api);
|
||||
}
|
||||
return new EmbyApi(_api);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,8 @@
|
|||
public string LocalAddress { get; set; }
|
||||
public string ServerName { get; set; }
|
||||
public string Version { get; set; }
|
||||
/// <summary>
|
||||
/// Only populated for Jellyfin
|
||||
/// </summary>
|
||||
public string ProductName { get; set; }
|
||||
|
||||
public bool IsJellyfin => !string.IsNullOrEmpty(ProductName) && ProductName.Contains("Jellyfin");
|
||||
|
||||
public string OperatingSystem { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
33
src/Ombi.Api.Jellyfin/IBaseJellyfinApi.cs
Normal file
33
src/Ombi.Api.Jellyfin/IBaseJellyfinApi.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Jellyfin.Models.Media.Tv;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public interface IBaseJellyfinApi
|
||||
{
|
||||
Task<JellyfinSystemInfo> GetSystemInformation(string apiKey, string baseUrl);
|
||||
Task<List<JellyfinUser>> GetUsers(string baseUri, string apiKey);
|
||||
Task<JellyfinUser> LogIn(string username, string password, string apiKey, string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinSeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<JellyfinItemContainer<JellyfinMovie>> GetCollection(string mediaId,
|
||||
string apiKey, string userId, string baseUrl);
|
||||
|
||||
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||
Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||
Task<PublicInfo> GetPublicInformation(string baseUrl);
|
||||
}
|
||||
}
|
10
src/Ombi.Api.Jellyfin/IJellyfinApi.cs
Normal file
10
src/Ombi.Api.Jellyfin/IJellyfinApi.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public interface IJellyfinApi : IBaseJellyfinApi
|
||||
{
|
||||
Task<JellyfinConnectUser> LoginConnectUser(string username, string password);
|
||||
}
|
||||
}
|
|
@ -3,14 +3,14 @@ using System.Net.Http;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Emby.Models.Media.Tv;
|
||||
using Ombi.Api.Emby.Models.Movie;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Jellyfin.Models.Media.Tv;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Api.Emby
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public class JellyfinApi : IEmbyApi
|
||||
public class JellyfinApi : IJellyfinApi
|
||||
{
|
||||
public JellyfinApi(IApi api)
|
||||
{
|
||||
|
@ -20,27 +20,27 @@ namespace Ombi.Api.Emby
|
|||
private IApi Api { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns all users from the Emby Instance
|
||||
/// Returns all users from the Jellyfin Instance
|
||||
/// </summary>
|
||||
/// <param name="baseUri"></param>
|
||||
/// <param name="apiKey"></param>
|
||||
public async Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey)
|
||||
public async Task<List<JellyfinUser>> GetUsers(string baseUri, string apiKey)
|
||||
{
|
||||
var request = new Request("users", baseUri, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
var obj = await Api.Request<List<EmbyUser>>(request);
|
||||
var obj = await Api.Request<List<JellyfinUser>>(request);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl)
|
||||
public async Task<JellyfinSystemInfo> GetSystemInformation(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("System/Info", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
var obj = await Api.Request<EmbySystemInfo>(request);
|
||||
var obj = await Api.Request<JellyfinSystemInfo>(request);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ namespace Ombi.Api.Emby
|
|||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
|
||||
public async Task<JellyfinUser> LogIn(string username, string password, string apiKey, string baseUri)
|
||||
{
|
||||
var request = new Request("users/authenticatebyname", baseUri, HttpMethod.Post);
|
||||
var body = new
|
||||
|
@ -71,11 +71,11 @@ namespace Ombi.Api.Emby
|
|||
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
var obj = await Api.Request<EmbyUser>(request);
|
||||
var obj = await Api.Request<JellyfinUser>(request);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
public async Task<JellyfinItemContainer<JellyfinMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
{
|
||||
var request = new Request($"users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
|
@ -84,22 +84,22 @@ namespace Ombi.Api.Emby
|
|||
|
||||
request.AddQueryString("IsVirtualItem", "False");
|
||||
|
||||
return await Api.Request<EmbyItemContainer<EmbyMovie>>(request);
|
||||
return await Api.Request<JellyfinItemContainer<JellyfinMovie>>(request);
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
public async Task<JellyfinItemContainer<JellyfinMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
{
|
||||
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
|
||||
return await GetAll<JellyfinMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
public async Task<JellyfinItemContainer<JellyfinEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
{
|
||||
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
|
||||
return await GetAll<JellyfinEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
|
||||
}
|
||||
|
||||
public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
public async Task<JellyfinItemContainer<JellyfinSeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
|
||||
{
|
||||
return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
|
||||
return await GetAll<JellyfinSeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
|
||||
}
|
||||
|
||||
public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
|
@ -126,7 +126,7 @@ namespace Ombi.Api.Emby
|
|||
return JsonConvert.DeserializeObject<T>(response);
|
||||
}
|
||||
|
||||
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
|
||||
private async Task<JellyfinItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
|
||||
{
|
||||
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
|
||||
|
||||
|
@ -139,10 +139,10 @@ namespace Ombi.Api.Emby
|
|||
AddHeaders(request, apiKey);
|
||||
|
||||
|
||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
||||
var obj = await Api.Request<JellyfinItemContainer<T>>(request);
|
||||
return obj;
|
||||
}
|
||||
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
|
||||
private async Task<JellyfinItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
|
||||
{
|
||||
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
|
||||
|
||||
|
@ -157,7 +157,7 @@ namespace Ombi.Api.Emby
|
|||
AddHeaders(request, apiKey);
|
||||
|
||||
|
||||
var obj = await Api.Request<EmbyItemContainer<T>>(request);
|
||||
var obj = await Api.Request<JellyfinItemContainer<T>>(request);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ namespace Ombi.Api.Emby
|
|||
req.AddHeader("Device", "Ombi");
|
||||
}
|
||||
|
||||
public Task<EmbyConnectUser> LoginConnectUser(string username, string password)
|
||||
public Task<JellyfinConnectUser> LoginConnectUser(string username, string password)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
37
src/Ombi.Api.Jellyfin/JellyfinApiFactory.cs
Normal file
37
src/Ombi.Api.Jellyfin/JellyfinApiFactory.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using Ombi.Api;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Jellyfin
|
||||
{
|
||||
public class JellyfinApiFactory : IJellyfinApiFactory
|
||||
{
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly IApi _api;
|
||||
|
||||
// TODO, if we need to derive futher, need to rework
|
||||
public JellyfinApiFactory(ISettingsService<JellyfinSettings> jellyfinSettings, IApi api)
|
||||
{
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_api = api;
|
||||
}
|
||||
|
||||
public async Task<IJellyfinApi> CreateClient()
|
||||
{
|
||||
var settings = await _jellyfinSettings.GetSettingsAsync();
|
||||
return CreateClient(settings);
|
||||
}
|
||||
|
||||
public IJellyfinApi CreateClient(JellyfinSettings settings)
|
||||
{
|
||||
return new JellyfinApi(_api);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IJellyfinApiFactory
|
||||
{
|
||||
Task<IJellyfinApi> CreateClient();
|
||||
IJellyfinApi CreateClient(JellyfinSettings settings);
|
||||
}
|
||||
}
|
45
src/Ombi.Api.Jellyfin/Models/JellyfinConfiguration.cs
Normal file
45
src/Ombi.Api.Jellyfin/Models/JellyfinConfiguration.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinConfiguration.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinConfiguration
|
||||
{
|
||||
public bool PlayDefaultAudioTrack { get; set; }
|
||||
public bool DisplayMissingEpisodes { get; set; }
|
||||
public bool DisplayUnairedEpisodes { get; set; }
|
||||
public object[] GroupedFolders { get; set; }
|
||||
public string SubtitleMode { get; set; }
|
||||
public bool DisplayCollectionsView { get; set; }
|
||||
public bool EnableLocalPassword { get; set; }
|
||||
public object[] OrderedViews { get; set; }
|
||||
public object[] LatestItemsExcludes { get; set; }
|
||||
public bool HidePlayedInLatest { get; set; }
|
||||
public bool RememberAudioSelections { get; set; }
|
||||
public bool RememberSubtitleSelections { get; set; }
|
||||
public bool EnableNextEpisodeAutoPlay { get; set; }
|
||||
}
|
||||
}
|
47
src/Ombi.Api.Jellyfin/Models/JellyfinConnectUser.cs
Normal file
47
src/Ombi.Api.Jellyfin/Models/JellyfinConnectUser.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinConnectUser.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinConnectUser
|
||||
{
|
||||
public string AccessToken { get; set; }
|
||||
public User User { get; set; }
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string IsActive { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public object IsSupporter { get; set; }
|
||||
public object ExpDate { get; set; }
|
||||
}
|
||||
|
||||
}
|
10
src/Ombi.Api.Jellyfin/Models/JellyfinItemContainer.cs
Normal file
10
src/Ombi.Api.Jellyfin/Models/JellyfinItemContainer.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinItemContainer<T>
|
||||
{
|
||||
public List<T> Items { get; set; }
|
||||
public int TotalRecordCount { get; set; }
|
||||
}
|
||||
}
|
10
src/Ombi.Api.Jellyfin/Models/JellyfinMediaType.cs
Normal file
10
src/Ombi.Api.Jellyfin/Models/JellyfinMediaType.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public enum JellyfinMediaType
|
||||
{
|
||||
Movie = 0,
|
||||
Series = 1,
|
||||
Music = 2,
|
||||
Episode = 3
|
||||
}
|
||||
}
|
59
src/Ombi.Api.Jellyfin/Models/JellyfinPolicy.cs
Normal file
59
src/Ombi.Api.Jellyfin/Models/JellyfinPolicy.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinPolicy.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinPolicy
|
||||
{
|
||||
public bool IsAdministrator { get; set; }
|
||||
public bool IsHidden { get; set; }
|
||||
public bool IsDisabled { get; set; }
|
||||
public object[] BlockedTags { get; set; }
|
||||
public bool EnableUserPreferenceAccess { get; set; }
|
||||
public object[] AccessSchedules { get; set; }
|
||||
public object[] BlockUnratedItems { get; set; }
|
||||
public bool EnableRemoteControlOfOtherUsers { get; set; }
|
||||
public bool EnableSharedDeviceControl { get; set; }
|
||||
public bool EnableLiveTvManagement { get; set; }
|
||||
public bool EnableLiveTvAccess { get; set; }
|
||||
public bool EnableMediaPlayback { get; set; }
|
||||
public bool EnableAudioPlaybackTranscoding { get; set; }
|
||||
public bool EnableVideoPlaybackTranscoding { get; set; }
|
||||
public bool EnablePlaybackRemuxing { get; set; }
|
||||
public bool EnableContentDeletion { get; set; }
|
||||
public bool EnableContentDownloading { get; set; }
|
||||
public bool EnableSync { get; set; }
|
||||
public bool EnableSyncTranscoding { get; set; }
|
||||
public object[] EnabledDevices { get; set; }
|
||||
public bool EnableAllDevices { get; set; }
|
||||
public object[] EnabledChannels { get; set; }
|
||||
public bool EnableAllChannels { get; set; }
|
||||
public object[] EnabledFolders { get; set; }
|
||||
public bool EnableAllFolders { get; set; }
|
||||
public int InvalidLoginAttemptCount { get; set; }
|
||||
public bool EnablePublicSharing { get; set; }
|
||||
}
|
||||
}
|
63
src/Ombi.Api.Jellyfin/Models/JellyfinSystemInfo.cs
Normal file
63
src/Ombi.Api.Jellyfin/Models/JellyfinSystemInfo.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinSystemInfo.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinSystemInfo
|
||||
{
|
||||
public string SystemUpdateLevel { get; set; }
|
||||
public string OperatingSystemDisplayName { get; set; }
|
||||
public bool SupportsRunningAsService { get; set; }
|
||||
public string MacAddress { get; set; }
|
||||
public bool HasPendingRestart { get; set; }
|
||||
public bool SupportsLibraryMonitor { get; set; }
|
||||
public object[] InProgressInstallations { get; set; }
|
||||
public int WebSocketPortNumber { get; set; }
|
||||
public object[] CompletedInstallations { get; set; }
|
||||
public bool CanSelfRestart { get; set; }
|
||||
public bool CanSelfUpdate { get; set; }
|
||||
public object[] FailedPluginAssemblies { get; set; }
|
||||
public string ProgramDataPath { get; set; }
|
||||
public string ItemsByNamePath { get; set; }
|
||||
public string CachePath { get; set; }
|
||||
public string LogPath { get; set; }
|
||||
public string InternalMetadataPath { get; set; }
|
||||
public string TranscodingTempPath { get; set; }
|
||||
public int HttpServerPortNumber { get; set; }
|
||||
public bool SupportsHttps { get; set; }
|
||||
public int HttpsPortNumber { get; set; }
|
||||
public bool HasUpdateAvailable { get; set; }
|
||||
public bool SupportsAutoRunAtStartup { get; set; }
|
||||
public string EncoderLocationType { get; set; }
|
||||
public string SystemArchitecture { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
public string WanAddress { get; set; }
|
||||
public string ServerName { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string OperatingSystem { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
47
src/Ombi.Api.Jellyfin/Models/JellyfinUser.cs
Normal file
47
src/Ombi.Api.Jellyfin/Models/JellyfinUser.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: JellyfinUser.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinUser
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string ConnectUserName { get; set; }
|
||||
public string ConnectLinkType { get; set; }
|
||||
public string Id { get; set; }
|
||||
public bool HasPassword { get; set; }
|
||||
public bool HasConfiguredPassword { get; set; }
|
||||
public bool HasConfiguredEasyPassword { get; set; }
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
public DateTime LastActivityDate { get; set; }
|
||||
public JellyfinConfiguration Configuration { get; set; }
|
||||
public JellyfinPolicy Policy { get; set; }
|
||||
}
|
||||
}
|
7
src/Ombi.Api.Jellyfin/Models/JellyfinUserLogin.cs
Normal file
7
src/Ombi.Api.Jellyfin/Models/JellyfinUserLogin.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class JellyfinUserLogin
|
||||
{
|
||||
public JellyfinUser User { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinChapter.cs
Normal file
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinChapter.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinChapter
|
||||
{
|
||||
public long StartPositionTicks { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinExternalurl
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
10
src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs
Normal file
10
src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinImagetags
|
||||
{
|
||||
public string Primary { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public string Thumb { get; set; }
|
||||
public string Banner { get; set; }
|
||||
}
|
||||
}
|
30
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediasource.cs
Normal file
30
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediasource.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinMediasource
|
||||
{
|
||||
public string Protocol { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool IsRemote { get; set; }
|
||||
public string ETag { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public bool ReadAtNativeFramerate { get; set; }
|
||||
public bool SupportsTranscoding { get; set; }
|
||||
public bool SupportsDirectStream { get; set; }
|
||||
public bool SupportsDirectPlay { get; set; }
|
||||
public bool IsInfiniteStream { get; set; }
|
||||
public bool RequiresOpening { get; set; }
|
||||
public bool RequiresClosing { get; set; }
|
||||
public bool SupportsProbing { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
public object[] PlayableStreamFileNames { get; set; }
|
||||
public object[] Formats { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public int DefaultAudioStreamIndex { get; set; }
|
||||
|
||||
}
|
||||
}
|
36
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediastream.cs
Normal file
36
src/Ombi.Api.Jellyfin/Models/Media/JellyfinMediastream.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinMediastream
|
||||
{
|
||||
public string Codec { get; set; }
|
||||
public string Language { get; set; }
|
||||
public string TimeBase { get; set; }
|
||||
public string CodecTimeBase { get; set; }
|
||||
public string NalLengthSize { get; set; }
|
||||
public bool IsInterlaced { get; set; }
|
||||
public bool IsAVC { get; set; }
|
||||
public int BitRate { get; set; }
|
||||
public int BitDepth { get; set; }
|
||||
public int RefFrames { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
public bool IsForced { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int Width { get; set; }
|
||||
public float AverageFrameRate { get; set; }
|
||||
public float RealFrameRate { get; set; }
|
||||
public string Profile { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string AspectRatio { get; set; }
|
||||
public int Index { get; set; }
|
||||
public bool IsExternal { get; set; }
|
||||
public bool IsTextSubtitleStream { get; set; }
|
||||
public bool SupportsExternalStream { get; set; }
|
||||
public string PixelFormat { get; set; }
|
||||
public int Level { get; set; }
|
||||
public bool IsAnamorphic { get; set; }
|
||||
public string DisplayTitle { get; set; }
|
||||
public string ChannelLayout { get; set; }
|
||||
public int Channels { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Jellyfin/Models/Media/JellyfinPerson.cs
Normal file
11
src/Ombi.Api.Jellyfin/Models/Media/JellyfinPerson.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinPerson
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string PrimaryImageTag { get; set; }
|
||||
}
|
||||
}
|
13
src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs
Normal file
13
src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinProviderids
|
||||
{
|
||||
public string Tmdb { get; set; }
|
||||
public string Imdb { get; set; }
|
||||
public string TmdbCollection { get; set; }
|
||||
|
||||
public string Tvdb { get; set; }
|
||||
public string Zap2It { get; set; }
|
||||
public string TvRage { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinRemotetrailer
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinStudio.cs
Normal file
8
src/Ombi.Api.Jellyfin/Models/Media/JellyfinStudio.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinStudio
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
15
src/Ombi.Api.Jellyfin/Models/Media/JellyfinUserdata.cs
Normal file
15
src/Ombi.Api.Jellyfin/Models/Media/JellyfinUserdata.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinUserdata
|
||||
{
|
||||
public double PlaybackPositionTicks { get; set; }
|
||||
public int PlayCount { get; set; }
|
||||
public bool IsFavorite { get; set; }
|
||||
public bool Played { get; set; }
|
||||
public string Key { get; set; }
|
||||
public DateTime LastPlayedDate { get; set; }
|
||||
public int UnplayedItemCount { get; set; }
|
||||
}
|
||||
}
|
34
src/Ombi.Api.Jellyfin/Models/Media/Movie/JellyfinMovie.cs
Normal file
34
src/Ombi.Api.Jellyfin/Models/Media/Movie/JellyfinMovie.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class JellyfinMovie
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public object[] ProductionLocations { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public int CriticRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
60
src/Ombi.Api.Jellyfin/Models/Media/Movie/MovieInformation.cs
Normal file
60
src/Ombi.Api.Jellyfin/Models/Media/Movie/MovieInformation.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Movie
|
||||
{
|
||||
public class MovieInformation
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string OriginalTitle { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Etag { get; set; }
|
||||
public DateTime DateCreated { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
public bool CanDownload { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public JellyfinExternalurl[] ExternalUrls { get; set; }
|
||||
public JellyfinMediasource[] MediaSources { get; set; }
|
||||
public string[] ProductionLocations { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string[] Taglines { get; set; }
|
||||
public string[] Genres { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public JellyfinRemotetrailer[] RemoteTrailers { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public JellyfinPerson[] People { get; set; }
|
||||
public JellyfinStudio[] Studios { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string DisplayPreferencesId { get; set; }
|
||||
public object[] Tags { get; set; }
|
||||
public string[] Keywords { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public object[] ScreenshotImageTags { get; set; }
|
||||
public JellyfinChapter[] Chapters { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public string HomePageUrl { get; set; }
|
||||
public int Budget { get; set; }
|
||||
public float Revenue { get; set; }
|
||||
public object[] LockedFields { get; set; }
|
||||
public bool LockData { get; set; }
|
||||
}
|
||||
}
|
71
src/Ombi.Api.Jellyfin/Models/Media/Tv/EpisodeInformation.cs
Normal file
71
src/Ombi.Api.Jellyfin/Models/Media/Tv/EpisodeInformation.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class EpisodeInformation
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Etag { get; set; }
|
||||
public DateTime DateCreated { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
public bool CanDownload { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public JellyfinExternalurl[] ExternalUrls { get; set; }
|
||||
public JellyfinMediasource[] MediaSources { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public object[] Taglines { get; set; }
|
||||
public object[] Genres { get; set; }
|
||||
public string[] SeriesGenres { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public int IndexNumber { get; set; }
|
||||
public int ParentIndexNumber { get; set; }
|
||||
public object[] RemoteTrailers { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public object[] People { get; set; }
|
||||
public object[] Studios { get; set; }
|
||||
public string ParentLogoItemId { get; set; }
|
||||
public string ParentBackdropItemId { get; set; }
|
||||
public string[] ParentBackdropImageTags { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesId { get; set; }
|
||||
public string SeasonId { get; set; }
|
||||
public string DisplayPreferencesId { get; set; }
|
||||
public object[] Tags { get; set; }
|
||||
public object[] Keywords { get; set; }
|
||||
public string SeriesPrimaryImageTag { get; set; }
|
||||
public string SeasonName { get; set; }
|
||||
public JellyfinMediastream[] MediaStreams { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public object[] BackdropImageTags { get; set; }
|
||||
public object[] ScreenshotImageTags { get; set; }
|
||||
public string ParentLogoImageTag { get; set; }
|
||||
public string SeriesStudio { get; set; }
|
||||
public JellyfinSeriesstudioinfo SeriesStudioInfo { get; set; }
|
||||
public string ParentThumbItemId { get; set; }
|
||||
public string ParentThumbImageTag { get; set; }
|
||||
public JellyfinChapter[] Chapters { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public object[] LockedFields { get; set; }
|
||||
public bool LockData { get; set; }
|
||||
}
|
||||
}
|
45
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinEpisodes.cs
Normal file
45
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinEpisodes.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinEpisodes
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsPlaceHolder { get; set; }
|
||||
public int IndexNumber { get; set; }
|
||||
public int? IndexNumberEnd { get; set; }
|
||||
public int ParentIndexNumber { get; set; }
|
||||
public bool IsHD { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string ParentLogoItemId { get; set; }
|
||||
public string ParentBackdropItemId { get; set; }
|
||||
public string[] ParentBackdropImageTags { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesId { get; set; }
|
||||
public string SeasonId { get; set; }
|
||||
public string SeriesPrimaryImageTag { get; set; }
|
||||
public string SeasonName { get; set; }
|
||||
public string VideoType { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public object[] BackdropImageTags { get; set; }
|
||||
public string ParentLogoImageTag { get; set; }
|
||||
public string ParentThumbItemId { get; set; }
|
||||
public string ParentThumbImageTag { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinRemotetrailer
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
32
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinSeries.cs
Normal file
32
src/Ombi.Api.Jellyfin/Models/Media/Tv/JellyfinSeries.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinSeries
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public int ChildCount { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string AirTime { get; set; }
|
||||
public string[] AirDays { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class JellyfinSeriesstudioinfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
59
src/Ombi.Api.Jellyfin/Models/Media/Tv/SeriesInformation.cs
Normal file
59
src/Ombi.Api.Jellyfin/Models/Media/Tv/SeriesInformation.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using Ombi.Api.Jellyfin.Models.Movie;
|
||||
|
||||
namespace Ombi.Api.Jellyfin.Models.Media.Tv
|
||||
{
|
||||
public class SeriesInformation
|
||||
{
|
||||
|
||||
public string Name { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Etag { get; set; }
|
||||
public DateTime DateCreated { get; set; }
|
||||
public DateTime DateLastMediaAdded { get; set; }
|
||||
public bool CanDelete { get; set; }
|
||||
public bool CanDownload { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public DateTime PremiereDate { get; set; }
|
||||
public JellyfinExternalurl[] ExternalUrls { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string OfficialRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string ShortOverview { get; set; }
|
||||
public object[] Taglines { get; set; }
|
||||
public string[] Genres { get; set; }
|
||||
public float CommunityRating { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public long CumulativeRunTimeTicks { get; set; }
|
||||
public long RunTimeTicks { get; set; }
|
||||
public string PlayAccess { get; set; }
|
||||
public int ProductionYear { get; set; }
|
||||
public JellyfinRemotetrailer[] RemoteTrailers { get; set; }
|
||||
public JellyfinProviderids ProviderIds { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public JellyfinPerson[] People { get; set; }
|
||||
public JellyfinStudio[] Studios { get; set; }
|
||||
public int LocalTrailerCount { get; set; }
|
||||
public JellyfinUserdata UserData { get; set; }
|
||||
public int RecursiveItemCount { get; set; }
|
||||
public int ChildCount { get; set; }
|
||||
public string DisplayPreferencesId { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string AirTime { get; set; }
|
||||
public string[] AirDays { get; set; }
|
||||
public object[] Tags { get; set; }
|
||||
public object[] Keywords { get; set; }
|
||||
public JellyfinImagetags ImageTags { get; set; }
|
||||
public string[] BackdropImageTags { get; set; }
|
||||
public object[] ScreenshotImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public string HomePageUrl { get; set; }
|
||||
public object[] LockedFields { get; set; }
|
||||
public bool LockData { get; set; }
|
||||
|
||||
}
|
||||
}
|
12
src/Ombi.Api.Jellyfin/Models/PublicInfo.cs
Normal file
12
src/Ombi.Api.Jellyfin/Models/PublicInfo.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ombi.Api.Jellyfin.Models
|
||||
{
|
||||
public class PublicInfo
|
||||
{
|
||||
public string LocalAddress { get; set; }
|
||||
public string ServerName { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string OperatingSystem { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
}
|
17
src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj
Normal file
17
src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj
Normal file
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||
<FileVersion>3.0.0.0</FileVersion>
|
||||
<Version></Version>
|
||||
<PackageVersion></PackageVersion>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -2,6 +2,7 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hqub.MusicBrainz.API.Entities;
|
||||
using Hqub.MusicBrainz.API.Entities.Collections;
|
||||
using Ombi.Api.MusicBrainz.Models;
|
||||
|
||||
namespace Ombi.Api.MusicBrainz
|
||||
|
@ -11,6 +12,7 @@ namespace Ombi.Api.MusicBrainz
|
|||
Task<IEnumerable<Artist>> SearchArtist(string artistQuery);
|
||||
Task<IEnumerable<Release>> GetReleaseForArtist(string artistId);
|
||||
Task<Artist> GetArtistInformation(string artistId);
|
||||
Task<Release> GetAlbumInformation(string albumId);
|
||||
Task<ReleaseGroupArt> GetCoverArtForReleaseGroup(string musicBrainzId, CancellationToken token);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Hqub.MusicBrainz.API;
|
||||
using Hqub.MusicBrainz.API.Entities;
|
||||
using Hqub.MusicBrainz.API.Entities.Collections;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.MusicBrainz.Models;
|
||||
|
||||
|
@ -20,6 +21,12 @@ namespace Ombi.Api.MusicBrainz
|
|||
_api = api;
|
||||
}
|
||||
|
||||
public Task<Release> GetAlbumInformation(string albumId)
|
||||
{
|
||||
var album = Release.GetAsync(albumId);
|
||||
return album;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Artist>> SearchArtist(string artistQuery)
|
||||
{
|
||||
var artist = await Artist.SearchAsync(artistQuery, 10);
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
public class SystemStatus
|
||||
{
|
||||
public string version { get; set; }
|
||||
public string urlBase { get; set; }
|
||||
}
|
||||
}
|
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())
|
||||
.ReturnsAsync(new AuthenticationSettings());
|
||||
_um = new OmbiUserManager(UserStore.Object, null, null, null, null, null, null, null, null,
|
||||
PlexApi.Object, null, null, AuthenticationSettings.Object);
|
||||
PlexApi.Object, null, null, null, null, AuthenticationSettings.Object);
|
||||
}
|
||||
|
||||
public OmbiUserManager _um { get; set; }
|
||||
|
|
|
@ -115,4 +115,4 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
Assert.False(search.Available);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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.Options;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Core.Settings;
|
||||
|
@ -49,18 +50,24 @@ namespace Ombi.Core.Authentication
|
|||
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
||||
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
||||
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
|
||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth)
|
||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings,
|
||||
IJellyfinApiFactory jellyfinApi, ISettingsService<JellyfinSettings> jellyfinSettings,
|
||||
ISettingsService<AuthenticationSettings> auth)
|
||||
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
||||
{
|
||||
_plexApi = plexApi;
|
||||
_embyApi = embyApi;
|
||||
_jellyfinApi = jellyfinApi;
|
||||
_embySettings = embySettings;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_authSettings = auth;
|
||||
}
|
||||
|
||||
private readonly IPlexApi _plexApi;
|
||||
private readonly IEmbyApiFactory _embyApi;
|
||||
private readonly IJellyfinApiFactory _jellyfinApi;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||
|
||||
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
||||
|
@ -83,6 +90,10 @@ namespace Ombi.Core.Authentication
|
|||
{
|
||||
return await CheckEmbyPasswordAsync(user, password);
|
||||
}
|
||||
if (user.UserType == UserType.JellyfinUser)
|
||||
{
|
||||
return await CheckJellyfinPasswordAsync(user, password);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -185,5 +196,36 @@ namespace Ombi.Core.Authentication
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sign the user into Jellyfin
|
||||
/// <remarks>We do not check if the user is in the owners "friends" since they must have a local user account to get this far.
|
||||
/// We also have to try and authenticate them with every server, the first server that work we just say it was a success</remarks>
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> CheckJellyfinPasswordAsync(OmbiUser user, string password)
|
||||
{
|
||||
var jellyfinSettings = await _jellyfinSettings.GetSettingsAsync();
|
||||
var client = _jellyfinApi.CreateClient(jellyfinSettings);
|
||||
|
||||
foreach (var server in jellyfinSettings.Servers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
|
||||
if (result != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Jellyfin Login Failed");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Helpers;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -179,6 +181,12 @@ namespace Ombi.Core.Engine
|
|||
return user.Language;
|
||||
}
|
||||
|
||||
protected async Task<List<StreamData>> GetUserWatchProvider(WatchProviders providers)
|
||||
{
|
||||
var user = await GetUser();
|
||||
return WatchProviderParser.GetUserWatchProviders(providers, user);
|
||||
}
|
||||
|
||||
private OmbiSettings ombiSettings;
|
||||
protected async Task<OmbiSettings> GetOmbiSettings()
|
||||
{
|
||||
|
|
|
@ -26,5 +26,6 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
int ResultLimit { get; set; }
|
||||
|
||||
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> GetArtistInformationByRequestId(int requestId);
|
||||
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;
|
||||
|
||||
namespace Ombi.Core
|
||||
|
@ -7,5 +9,6 @@ namespace Ombi.Core
|
|||
{
|
||||
Task<SearchFullInfoTvShowViewModel> GetShowInformation(int tvdbid);
|
||||
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)}";
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -82,7 +98,7 @@ namespace Ombi.Core.Engine
|
|||
Status = movieInfo.Status,
|
||||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
RequestedUserId = userDetails.Id,
|
||||
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
|
||||
Background = movieInfo.BackdropPath,
|
||||
LangCode = model.LanguageCode,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
|
@ -103,7 +119,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
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)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
|
@ -429,7 +445,7 @@ namespace Ombi.Core.Engine
|
|||
public async Task<MovieRequests> GetRequest(int requestId)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
||||
if (result.Success)
|
||||
{
|
||||
{
|
||||
await NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.Movie,
|
||||
|
|
|
@ -13,38 +13,45 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public class RecentlyAddedEngine : IRecentlyAddedEngine
|
||||
{
|
||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IJellyfinContentRepository jellyfin, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||
{
|
||||
_plex = plex;
|
||||
_emby = emby;
|
||||
_jellyfin = jellyfin;
|
||||
_recentlyAddedLog = recentlyAdded;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plex;
|
||||
private readonly IEmbyContentRepository _emby;
|
||||
private readonly IJellyfinContentRepository _jellyfin;
|
||||
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||
|
||||
|
||||
|
||||
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to)
|
||||
{
|
||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies).Take(30);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies).Take(30);
|
||||
}
|
||||
|
||||
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies()
|
||||
{
|
||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies);
|
||||
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies);
|
||||
}
|
||||
|
||||
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason)
|
||||
{
|
||||
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show && x.AddedAt > from && x.AddedAt < to);
|
||||
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series && x.AddedAt > from && x.AddedAt < to);
|
||||
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series && x.AddedAt > from && x.AddedAt < to);
|
||||
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason).Take(30);
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason).Take(30);
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,14 +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 embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series);
|
||||
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series);
|
||||
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason);
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateRecentlyAddedDatabase()
|
||||
{
|
||||
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
||||
var embyContent = _emby.GetAll().Include(x => x.Episodes);
|
||||
var jellyfinContent = _jellyfin.GetAll().Include(x => x.Episodes);
|
||||
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||
foreach (var p in plexContent)
|
||||
{
|
||||
|
@ -136,17 +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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv,
|
||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv, IQueryable<JellyfinContent> jellyfinTv,
|
||||
bool groupBySeason)
|
||||
{
|
||||
var model = new HashSet<RecentlyAddedTvModel>();
|
||||
TransformPlexShows(plexTv, model);
|
||||
TransformEmbyShows(embyTv, model);
|
||||
TransformJellyfinShows(jellyfinTv, model);
|
||||
|
||||
if (groupBySeason)
|
||||
{
|
||||
|
@ -156,11 +204,12 @@ namespace Ombi.Core.Engine
|
|||
return model;
|
||||
}
|
||||
|
||||
private IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(IQueryable<PlexServerContent> plexMovies, IQueryable<EmbyContent> embyMovies)
|
||||
private IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(IQueryable<PlexServerContent> plexMovies, IQueryable<EmbyContent> embyMovies, IQueryable<JellyfinContent> jellyfinMovies)
|
||||
{
|
||||
var model = new HashSet<RecentlyAddedMovieModel>();
|
||||
TransformPlexMovies(plexMovies, model);
|
||||
TransformEmbyMovies(embyMovies, model);
|
||||
TransformJellyfinMovies(jellyfinMovies, model);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
@ -181,6 +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)
|
||||
{
|
||||
foreach (var plex in plexMovies)
|
||||
|
@ -244,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)
|
||||
{
|
||||
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);
|
||||
(await tvBuilder
|
||||
.GetShowInfo(tv.TvDbId))
|
||||
.CreateTvList(tv)
|
||||
.CreateChild(tv, user.Id);
|
||||
.CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id);
|
||||
|
||||
await tvBuilder.BuildEpisodes(tv);
|
||||
|
||||
|
@ -124,12 +140,12 @@ namespace Ombi.Core.Engine
|
|||
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
|
||||
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)
|
||||
|
@ -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
|
||||
existingRequest.ChildRequests.Add(newRequest);
|
||||
|
||||
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);
|
||||
// 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)
|
||||
|
@ -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);
|
||||
if (sendRuleResult.Success)
|
||||
|
@ -776,7 +792,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
|
|
|
@ -59,7 +59,12 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(await ProcessResult(tvMazeSearch, false));
|
||||
var mappedResult = await ProcessResult(tvMazeSearch, false);
|
||||
if (mappedResult == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(mappedResult);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
@ -194,7 +199,7 @@ namespace Ombi.Core.Engine
|
|||
foreach (var tvMazeSearch in items)
|
||||
{
|
||||
var result = await ProcessResult(tvMazeSearch, includeImages);
|
||||
if(settings.HideAvailableFromDiscover && result.Available)
|
||||
if (result == null || settings.HideAvailableFromDiscover && result.Available)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -211,15 +216,17 @@ namespace Ombi.Core.Engine
|
|||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult(SearchTvShowViewModel item, bool includeImages)
|
||||
{
|
||||
if (item.Id == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.TheTvDbId = item.Id.ToString();
|
||||
if (includeImages)
|
||||
{
|
||||
|
||||
if (item.TheTvDbId.HasValue())
|
||||
{
|
||||
item.BackdropPath = await _imageService.GetTvBackground(item.TheTvDbId);
|
||||
}
|
||||
|
||||
if (item.TheTvDbId.HasValue())
|
||||
{
|
||||
item.BackdropPath = await _imageService.GetTvBackground(item.TheTvDbId);
|
||||
}
|
||||
}
|
||||
|
||||
await RunSearchRules(item);
|
||||
|
|
|
@ -249,6 +249,26 @@ namespace Ombi.Core.Engine.V2
|
|||
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(
|
||||
IEnumerable<MovieSearchResult> movies)
|
||||
{
|
||||
|
@ -287,6 +307,7 @@ namespace Ombi.Core.Engine.V2
|
|||
mapped.Requested = viewMovie.Requested;
|
||||
mapped.PlexUrl = viewMovie.PlexUrl;
|
||||
mapped.EmbyUrl = viewMovie.EmbyUrl;
|
||||
mapped.JellyfinUrl = viewMovie.JellyfinUrl;
|
||||
mapped.Subscribed = viewMovie.Subscribed;
|
||||
mapped.ShowSubscribe = viewMovie.ShowSubscribe;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.MusicBrainz;
|
||||
|
@ -41,6 +42,21 @@ namespace Ombi.Core.Engine.V2
|
|||
_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)
|
||||
{
|
||||
var artist = await _musicBrainzApi.GetArtistInformation(artistId);
|
||||
|
@ -84,12 +100,19 @@ namespace Ombi.Core.Engine.V2
|
|||
|
||||
if (lidarrArtistTask != null)
|
||||
{
|
||||
var artistResult = await lidarrArtistTask;
|
||||
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();
|
||||
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;
|
||||
try
|
||||
{
|
||||
var artistResult = await lidarrArtistTask;
|
||||
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();
|
||||
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;
|
||||
|
@ -118,7 +141,7 @@ namespace Ombi.Core.Engine.V2
|
|||
|
||||
return new AlbumArt();
|
||||
}
|
||||
|
||||
|
||||
public async Task<ArtistInformation> GetArtistInformationByRequestId(int requestId)
|
||||
{
|
||||
var request = await RequestService.MusicRequestRepository.Find(requestId);
|
||||
|
|
|
@ -19,6 +19,8 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Store.Repository;
|
||||
using TraktSharp.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
|
||||
namespace Ombi.Core.Engine.V2
|
||||
{
|
||||
|
@ -27,15 +29,17 @@ namespace Ombi.Core.Engine.V2
|
|||
private readonly ITvMazeApi _tvMaze;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ITraktApi _traktApi;
|
||||
private readonly IMovieDbApi _movieApi;
|
||||
|
||||
public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
||||
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)
|
||||
{
|
||||
_tvMaze = tvMaze;
|
||||
_mapper = mapper;
|
||||
_traktApi = trakt;
|
||||
_movieApi = movieApi;
|
||||
}
|
||||
|
||||
|
||||
|
@ -106,6 +110,39 @@ namespace Ombi.Core.Engine.V2
|
|||
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)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
|
@ -141,7 +178,7 @@ namespace Ombi.Core.Engine.V2
|
|||
{
|
||||
item.Images.Medium = item.Images.Medium.ToHttpsUrl();
|
||||
}
|
||||
|
||||
|
||||
if (item.Cast?.Any() ?? false)
|
||||
{
|
||||
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
|
||||
{
|
||||
Plex,
|
||||
Emby
|
||||
Emby,
|
||||
Jellyfin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Ombi.Core.Models.Requests
|
|||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
public string LanguageCode { get; set; } = "en";
|
||||
public string RequestOnBehalf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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>();
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
|
||||
public string RequestOnBehalf { get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsViewModel
|
||||
|
|
|
@ -14,11 +14,12 @@ namespace Ombi.Core.Models.Search
|
|||
public bool Available { get; set; }
|
||||
public string PlexUrl { get; set; }
|
||||
public string EmbyUrl { get; set; }
|
||||
public string JellyfinUrl { get; set; }
|
||||
public string Quality { get; set; }
|
||||
public abstract RequestType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule rule
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule/JellyfinAvailabilityRule rule
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The custom identifier.
|
||||
|
@ -35,4 +36,4 @@ namespace Ombi.Core.Models.Search
|
|||
[NotMapped]
|
||||
public bool ShowSubscribe { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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; }
|
||||
}
|
||||
}
|
8
src/Ombi.Core/Models/TesterResultModel.cs
Normal file
8
src/Ombi.Core/Models/TesterResultModel.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Core.Models
|
||||
{
|
||||
public class TesterResultModel
|
||||
{
|
||||
public bool IsValid { get; set; }
|
||||
public string ExpectedSubDir { get; set; }
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models.UI
|
|||
public UserType UserType { get; set; }
|
||||
public int MovieRequestLimit { get; set; }
|
||||
public int EpisodeRequestLimit { get; set; }
|
||||
public string StreamingCountry { get; set; }
|
||||
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MusicRequestQuota { get; set; }
|
||||
|
@ -30,4 +31,10 @@ namespace Ombi.Core.Models.UI
|
|||
public string Value { 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,
|
||||
PlexUser = 2,
|
||||
EmbyUser = 3
|
||||
EmbyUser = 3,
|
||||
JellyfinUser = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj" />
|
||||
|
@ -40,4 +41,4 @@
|
|||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -108,10 +108,40 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<JellyfinEpisode> allEpisodes, EpisodeRequests episode,
|
||||
SeasonRequests season, JellyfinContent item, bool useTheMovieDb, bool useTvDb)
|
||||
{
|
||||
JellyfinEpisode epExists = null;
|
||||
if (useImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ImdbId == item.ImdbId);
|
||||
}
|
||||
|
||||
if (useTheMovieDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TheMovieDbId == item.TheMovieDbId);
|
||||
}
|
||||
|
||||
if (useTvDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,11 +70,11 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||
{
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname, s.IsJellyfin);
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null, s.IsJellyfin);
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,4 +100,4 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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.Emby;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Api.Sonarr;
|
||||
|
@ -47,6 +48,7 @@ using Ombi.Core.Senders;
|
|||
using Ombi.Helpers;
|
||||
using Ombi.Schedule.Jobs.Couchpotato;
|
||||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Jellyfin;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Schedule.Jobs.Sonarr;
|
||||
|
@ -65,6 +67,7 @@ using Quartz.Spi;
|
|||
using Ombi.Api.MusicBrainz;
|
||||
using Ombi.Api.Twilio;
|
||||
using Ombi.Api.CloudService;
|
||||
using Ombi.Api.RottenTomatoes;
|
||||
|
||||
namespace Ombi.DependencyInjection
|
||||
{
|
||||
|
@ -126,6 +129,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
||||
services.AddTransient<IPlexApi, PlexApi>();
|
||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||
services.AddTransient<IJellyfinApi, JellyfinApi>();
|
||||
services.AddTransient<ISonarrApi, SonarrApi>();
|
||||
services.AddTransient<ISonarrV3Api, SonarrV3Api>();
|
||||
services.AddTransient<ISlackApi, SlackApi>();
|
||||
|
@ -153,8 +157,9 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMusicBrainzApi, MusicBrainzApi>();
|
||||
services.AddTransient<IWhatsAppApi, WhatsAppApi>();
|
||||
services.AddTransient<ICloudMobileNotification, CloudMobileNotification>();
|
||||
services.AddTransient<IBaseEmbyApi, JellyfinApi>();
|
||||
services.AddTransient<IEmbyApiFactory, EmbyApiFactory>();
|
||||
services.AddTransient<IJellyfinApiFactory, JellyfinApiFactory>();
|
||||
services.AddTransient<IRottenTomatoesApi, RottenTomatoesApi>();
|
||||
}
|
||||
|
||||
public static void RegisterStore(this IServiceCollection services) {
|
||||
|
@ -169,6 +174,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddScoped<ISettingsResolver, SettingsResolver>();
|
||||
services.AddScoped<IPlexContentRepository, PlexServerContentRepository>();
|
||||
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
|
||||
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
|
||||
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
|
||||
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
||||
|
@ -213,6 +219,9 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IEmbyContentSync, EmbyContentSync>();
|
||||
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
|
||||
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
||||
services.AddTransient<IJellyfinContentSync, JellyfinContentSync>();
|
||||
services.AddTransient<IJellyfinEpisodeSync, JellyfinEpisodeSync>();
|
||||
services.AddTransient<IJellyfinAvaliabilityChecker, JellyfinAvaliabilityChecker>();
|
||||
services.AddTransient<IPlexEpisodeSync, PlexEpisodeSync>();
|
||||
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
||||
services.AddTransient<IRadarrSync, RadarrSync>();
|
||||
|
@ -220,6 +229,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
||||
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
||||
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
||||
services.AddTransient<IJellyfinUserImporter, JellyfinUserImporter>();
|
||||
services.AddTransient<IWelcomeEmail, WelcomeEmail>();
|
||||
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
||||
services.AddTransient<IProcessProvider, ProcessProvider>();
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Pushover\Ombi.Api.Pushover.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.SickRage\Ombi.Api.SickRage.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.Emby;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Core.Settings;
|
||||
|
|
54
src/Ombi.HealthChecks/Checks/JellyfinHealthCheck.cs
Normal file
54
src/Ombi.HealthChecks/Checks/JellyfinHealthCheck.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.HealthChecks.Checks
|
||||
{
|
||||
public class JellyfinHealthCheck : BaseHealthCheck
|
||||
{
|
||||
public JellyfinHealthCheck(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
|
||||
{
|
||||
}
|
||||
public override async Task<HealthCheckResult> CheckHealthAsync(
|
||||
HealthCheckContext context,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using (var scope = CreateScope())
|
||||
{
|
||||
var settingsProvider = scope.ServiceProvider.GetRequiredService<ISettingsService<JellyfinSettings>>();
|
||||
var api = scope.ServiceProvider.GetRequiredService<IJellyfinApiFactory>();
|
||||
var settings = await settingsProvider.GetSettingsAsync();
|
||||
if (settings == null)
|
||||
{
|
||||
return HealthCheckResult.Healthy("Jellyfin is not configured.");
|
||||
}
|
||||
|
||||
var client = api.CreateClient(settings);
|
||||
var taskResult = new List<Task<JellyfinSystemInfo>>();
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
taskResult.Add(client.GetSystemInformation(server.ApiKey, server.FullUri));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await Task.WhenAll(taskResult.ToArray());
|
||||
return HealthCheckResult.Healthy();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy("Could not communicate with Jellyfin", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|||
using Ombi.Api.CouchPotato;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Jellyfin.Models;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Api.SickRage;
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Ombi.HealthChecks
|
|||
{
|
||||
builder.AddCheck<PlexHealthCheck>("Plex", tags: new string[] { "MediaServer" });
|
||||
builder.AddCheck<EmbyHealthCheck>("Emby", tags: new string[] { "MediaServer" });
|
||||
builder.AddCheck<JellyfinHealthCheck>("Jellyfin", tags: new string[] { "MediaServer" });
|
||||
builder.AddCheck<LidarrHealthCheck>("Lidarr", tags: new string[] { "DVR" });
|
||||
builder.AddCheck<SonarrHealthCheck>("Sonarr", tags: new string[] { "DVR" });
|
||||
builder.AddCheck<RadarrHealthCheck>("Radarr", tags: new string[] { "DVR" });
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
|
|
|
@ -15,13 +15,6 @@ namespace Ombi.Helpers.Tests
|
|||
return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(JellyfinUrlData))]
|
||||
public string TestJellyfinUrl(string mediaId, string url, string serverId)
|
||||
{
|
||||
// http://192.168.68.X:8097/web/index.html#!/details?id=7ffe222498445d5ebfddb31bc4fa9a6d&serverId=50cce67f0baa425093d189b3017331fb
|
||||
return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url, true);
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> UrlData
|
||||
{
|
||||
get
|
||||
|
@ -33,16 +26,5 @@ namespace Ombi.Helpers.Tests
|
|||
yield return new TestCaseData(mediaId.ToString(), string.Empty, "1").Returns($"https://app.emby.media/web/index.html#!/item?id={mediaId}&serverId=1").SetName("EmbyHelper_GetMediaUrl_WithOutCustomDomain");
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> JellyfinUrlData
|
||||
{
|
||||
get
|
||||
{
|
||||
var mediaId = 1;
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash");
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain");
|
||||
yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_Https");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
29
src/Ombi.Helpers.Tests/JellyfinHelperTests.cs
Normal file
29
src/Ombi.Helpers.Tests/JellyfinHelperTests.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Helpers.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class JellyfinHelperTests
|
||||
{
|
||||
[TestCaseSource(nameof(UrlData))]
|
||||
public string TestUrl(string mediaId, string url, string serverId)
|
||||
{
|
||||
// http://192.168.68.X:8097/web/index.html#!/details?id=7ffe222498445d5ebfddb31bc4fa9a6d&serverId=50cce67f0baa425093d189b3017331fb
|
||||
return JellyfinHelper.GetJellyfinMediaUrl(mediaId, serverId, url);
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> UrlData
|
||||
{
|
||||
get
|
||||
{
|
||||
var mediaId = 1;
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash");
|
||||
yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain");
|
||||
yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_Https");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,14 +2,10 @@
|
|||
{
|
||||
public static class EmbyHelper
|
||||
{
|
||||
public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null, bool isJellyfin = false)
|
||||
public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null)
|
||||
{
|
||||
//web/index.html#!/details|item
|
||||
string path = "item";
|
||||
if (isJellyfin)
|
||||
{
|
||||
path = "details";
|
||||
}
|
||||
if (customerServerUrl.HasValue())
|
||||
{
|
||||
if (!customerServerUrl.EndsWith("/"))
|
||||
|
|
23
src/Ombi.Helpers/JellyfinHelper.cs
Normal file
23
src/Ombi.Helpers/JellyfinHelper.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace Ombi.Helpers
|
||||
{
|
||||
public static class JellyfinHelper
|
||||
{
|
||||
public static string GetJellyfinMediaUrl(string mediaId, string serverId, string customerServerUrl = null)
|
||||
{
|
||||
//web/index.html#!/details|item
|
||||
string path = "details";
|
||||
if (customerServerUrl.HasValue())
|
||||
{
|
||||
if (!customerServerUrl.EndsWith("/"))
|
||||
{
|
||||
return $"{customerServerUrl}/web/index.html#!/{path}?id={mediaId}&serverId={serverId}";
|
||||
}
|
||||
return $"{customerServerUrl}web/index.html#!/{path}?id={mediaId}&serverId={serverId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"http://localhost:8096/web/index.html#!/{path}?id={mediaId}&serverId={serverId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,8 +14,10 @@ namespace Ombi.Helpers
|
|||
public static EventId RadarrCacher => new EventId(2001);
|
||||
public static EventId PlexEpisodeCacher => new EventId(2002);
|
||||
public static EventId EmbyContentCacher => new EventId(2003);
|
||||
public static EventId JellyfinContentCacher => new EventId(2012);
|
||||
public static EventId PlexUserImporter => new EventId(2004);
|
||||
public static EventId EmbyUserImporter => new EventId(2005);
|
||||
public static EventId JellyfinUserImporter => new EventId(2013);
|
||||
public static EventId SonarrCacher => new EventId(2006);
|
||||
public static EventId CouchPotatoCacher => new EventId(2007);
|
||||
public static EventId PlexContentCacher => new EventId(2008);
|
||||
|
@ -43,4 +45,4 @@ namespace Ombi.Helpers
|
|||
public static EventId Updater => new EventId(6000);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Ombi.Helpers
|
|||
//com.plexapp.agents.themoviedb://390043?lang=en
|
||||
//com.plexapp.agents.imdb://tt2543164?lang=en
|
||||
//plex://movie/5e1632df2d4d84003e48e54e
|
||||
// https://github.com/tidusjar/Ombi/issues/3277
|
||||
// https://github.com/Ombi-app/Ombi/issues/3277
|
||||
if (string.IsNullOrEmpty(guid))
|
||||
{
|
||||
return new ProviderId();
|
||||
|
|
|
@ -11,5 +11,8 @@
|
|||
public string StoragePath { get; set; }
|
||||
|
||||
public string SecurityKey { get; set; }
|
||||
#if DEBUG
|
||||
= "test";
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ namespace Ombi.Mapping.Profiles
|
|||
|
||||
|
||||
CreateMap<TraktShow, SearchTvShowViewModel>()
|
||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => Convert.ToInt32(src.Ids.Tvdb.ToString())))
|
||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Ids.Tvdb.HasValue ? Convert.ToInt32(src.Ids.Tvdb.ToString()) : 0))
|
||||
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.FirstAired.HasValue ? src.FirstAired.Value.ToString("yyyy-MM-ddTHH:mm:ss") : string.Empty))
|
||||
.ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.Ids.Imdb))
|
||||
.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.Network))
|
||||
|
@ -57,9 +57,9 @@ namespace Ombi.Mapping.Profiles
|
|||
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title))
|
||||
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status)))
|
||||
.ForMember(dest => dest.Trailer,
|
||||
opts => opts.MapFrom(src => src.Trailer.ToString().ToHttpsUrl()))
|
||||
opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty))
|
||||
.ForMember(dest => dest.Homepage,
|
||||
opts => opts.MapFrom(src => src.Homepage.ToString().ToHttpsUrl()));
|
||||
opts => opts.MapFrom(src => src.Homepage != null ? src.Homepage.ToString().ToHttpsUrl() : string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -166,7 +166,7 @@
|
|||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
|
||||
<tr>
|
||||
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
|
||||
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a> {@DATENOW}
|
||||
Powered by <a href="https://github.com/Ombi-app/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a> {@DATENOW}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!doctype html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
@ -453,7 +453,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td class="content-block powered-by" valign="top" align="center" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;">
|
||||
Powered by <a href="https://github.com/tidusjar/Ombi" style="font-weight: 400; font-size: 12px; text-align: center; text-decoration: none; color: #ff761b;">Ombi</a>
|
||||
Powered by <a href="https://github.com/Ombi-app/Ombi" style="font-weight: 400; font-size: 12px; text-align: center; text-decoration: none; color: #ff761b;">Ombi</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -77,12 +77,16 @@ namespace Ombi.Schedule.Jobs.Couchpotato
|
|||
var movies = await _api.GetMovies(settings.FullUri, settings.ApiKey, new[] {"active"});
|
||||
if (movies != null)
|
||||
{
|
||||
// Let's remove the old cached data
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
var strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM CouchPotatoCache");
|
||||
tran.Commit();
|
||||
}
|
||||
// Let's remove the old cached data
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM CouchPotatoCache");
|
||||
tran.Commit();
|
||||
}
|
||||
});
|
||||
|
||||
// Save
|
||||
var movieIds = new List<CouchPotatoCache>();
|
||||
|
@ -102,14 +106,17 @@ namespace Ombi.Schedule.Jobs.Couchpotato
|
|||
_log.LogError("TMDBId is not > 0 for movie {0}", m.title);
|
||||
}
|
||||
}
|
||||
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
strat = _ctx.Database.CreateExecutionStrategy();
|
||||
await strat.ExecuteAsync(async () =>
|
||||
{
|
||||
await _ctx.CouchPotatoCache.AddRangeAsync(movieIds);
|
||||
using (var tran = await _ctx.Database.BeginTransactionAsync())
|
||||
{
|
||||
await _ctx.CouchPotatoCache.AddRangeAsync(movieIds);
|
||||
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
}
|
||||
await _ctx.SaveChangesAsync();
|
||||
tran.Commit();
|
||||
}
|
||||
});
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Couch Potato Sync Finished");
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Failed");
|
||||
_logger.LogError(e, "Exception when caching {1} for server {0}", server.Name, embySettings.IsJellyfin ? "Jellyfin" : "Emby");
|
||||
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
Title = tvShow.Name,
|
||||
Type = EmbyMediaType.Series,
|
||||
EmbyId = tvShow.Id,
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname, settings.IsJellyfin),
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
|
||||
AddedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
@ -228,4 +228,4 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
Api = _apiFactory.CreateClient(settings);
|
||||
|
||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||
.SendAsync(NotificationHub.NotificationEvent, $"{(settings.IsJellyfin ? "Jellyfin" : "Emby")} User Importer Started");
|
||||
.SendAsync(NotificationHub.NotificationEvent, $"Emby User Importer Started");
|
||||
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
|
@ -117,7 +117,8 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
ProviderUserId = embyUser.Id,
|
||||
Alias = isConnectUser ? embyUser.Name : string.Empty,
|
||||
MovieRequestLimit = userManagementSettings.MovieRequestLimit,
|
||||
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit
|
||||
EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit,
|
||||
StreamingCountry = userManagementSettings.DefaultStreamingCountry
|
||||
};
|
||||
var result = await _userManager.CreateAsync(newUser);
|
||||
if (!result.Succeeded)
|
||||
|
@ -180,4 +181,4 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public interface IJellyfinAvaliabilityChecker : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinContentSync.cs
Normal file
8
src/Ombi.Schedule/Jobs/Jellyfin/IJellyfinContentSync.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Jellyfin
|
||||
{
|
||||
public interface IJellyfinContentSync : IBaseJob
|
||||
{
|
||||
}
|
||||
}
|
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