mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 13:23:20 -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)"
|
$response = Invoke-WebRequest -Uri "https://ombireleasenote.azurewebsites.net/api/ReleaseNotesFunction?buildId=$(Build.BuildId)"
|
||||||
Write-Host "##vso[task.setvariable variable=ReleaseNotes;]$response"
|
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
|
- task: GitHubRelease@1
|
||||||
|
displayName: 'Ombi.Releases Release'
|
||||||
inputs:
|
inputs:
|
||||||
gitHubConnection: 'github.com_tidusjar'
|
gitHubConnection: 'PAT'
|
||||||
repositoryName: 'tidusjar/Ombi.Releases'
|
repositoryName: 'Ombi-app/Ombi.Releases'
|
||||||
action: 'create'
|
action: 'create'
|
||||||
target: 'c7fcbb77b58aef1076d635a9ef99e4374abc8672'
|
target: 'c7fcbb77b58aef1076d635a9ef99e4374abc8672'
|
||||||
tagSource: 'userSpecifiedTag'
|
tagSource: 'userSpecifiedTag'
|
||||||
|
@ -91,3 +80,38 @@ stages:
|
||||||
isPreRelease: true
|
isPreRelease: true
|
||||||
changeLogCompareToRelease: 'lastNonDraftRelease'
|
changeLogCompareToRelease: 'lastNonDraftRelease'
|
||||||
changeLogType: 'commitBased'
|
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)"
|
value: "4.0.$(Build.BuildId)"
|
||||||
|
|
||||||
- name: isMain
|
- 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]
|
- OS: [e.g. iOS]
|
||||||
|
|
||||||
**Ombi Version (please complete the following information):**
|
**Ombi Version (please complete the following information):**
|
||||||
- Version [e.g. 3.0.1158]
|
- Version [e.g. 4.0.958]
|
||||||
- Media Server [e.g. Plex]
|
- Media Server [e.g. Plex]
|
||||||
- Database Type: SQLite (Please change if using MySQL)
|
- 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
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Wiki
|
- name: Docs
|
||||||
url: https://github.com/tidusjar/Ombi/wiki
|
url: https://docs.ombi.app/
|
||||||
about: The Ombi wiki should help guide you through installation and setup as well as help resolve common problems and answer frequently asked questions
|
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
|
- name: Reddit support
|
||||||
url: https://www.reddit.com/r/Ombi
|
url: https://www.reddit.com/r/Ombi
|
||||||
about: Ask questions about Ombi
|
about: Ask questions about Ombi
|
||||||
|
|
|
@ -661,7 +661,7 @@
|
||||||
|
|
||||||
- Added capture of anonymous analytical data. [tidusjar]
|
- 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]
|
- 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://discord.gg/Sa7wNWb)
|
||||||
[](https://hub.docker.com/r/linuxserver/ombi/)
|
[](https://hub.docker.com/r/linuxserver/ombi/)
|
||||||
[](https://github.com/tidusjar/Ombi)
|
[](https://github.com/ombi-app/Ombi)
|
||||||
[](http://www.firsttimersonly.com/)
|
[](http://www.firsttimersonly.com/)
|
||||||
[](https://crowdin.com/project/ombi)
|
[](https://crowdin.com/project/ombi)
|
||||||
|
|
||||||
[](https://patreon.com/tidusjar/Ombi)
|
[](https://patreon.com/tidusjar/Ombi)
|
||||||
[](https://paypal.me/PlexRequestsNet)
|
[](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
|
||||||
Feature requests are handled on Feature Upvote.
|
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)
|
(If a similar request exists, please vote, or add additional comments to the request)
|
||||||
|
|
||||||
#### [](https://features.ombi.io)
|
#### [](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>
|
<br>
|
||||||
_**Note:** There is no longer an iOS app due to complications outside of our control._
|
_**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
|
# Features
|
||||||
Here are some of the features Ombi V3 has:
|
Here are some of the features Ombi has:
|
||||||
* Now working without crashes on Linux.
|
|
||||||
* Lets users request Movies, Music, and TV Shows (whether it being the entire series, an entire season, or even single episodes.)
|
* 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
|
* 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)
|
* 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.
|
* 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
|
* Automatically updates the status of requests when they are available on Plex/Emby
|
||||||
* Slick, responsive and mobile friendly UI
|
* 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
|
# Preview
|
||||||
|
|
||||||

|

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