mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-06 05:01:13 -07:00
Merge branch 'master' of https://github.com/tidusjar/ombi
This commit is contained in:
commit
0fb6e31bfb
128 changed files with 17359 additions and 1616 deletions
211
CHANGELOG.md
211
CHANGELOG.md
|
@ -1,17 +1,115 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
## v3.0.4119 (2019-1-09)
|
||||
## v3.0.4256 (2019-02-18)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Added a new Custom Page, this will allow you to completely change the page via a WYSIWYG editor! [TidusJar]
|
||||
- Update discord link to follow the scheme of other links. [Tom McClellan]
|
||||
|
||||
- Added the ability to search movies via the movie db with a different language! [tidusjar]
|
||||
- Update issue templates. [Jamie]
|
||||
|
||||
- Added the ability to specify a year when searching for movies. [tidusjar]
|
||||
- Update README.md. [Jamie]
|
||||
|
||||
- Made the newsletter use the default lanuage code set in the Ombi settings for movie information. [TidusJar]
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
- Added the functionality to remove a user from getting notifications to their mobile device #2780. [tidusjar]
|
||||
|
||||
- Added a demo mode, this will only show movies and shows that are in the public domain. Dam that stupid fruit company. [tidusjar]
|
||||
|
||||
- Added Actor Searching for Movies! [TidusJar]
|
||||
|
||||
- Added the ability to change where the View on Emby link goes to #2730. [TidusJar]
|
||||
|
||||
- Added the request queue to the notifications UI so you can turn it off per notification agent #2747. [TidusJar]
|
||||
|
||||
- Added new classes to the posters #2732. [TidusJar]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- Fix: src/Ombi/package.json to reduce vulnerabilities. [snyk-bot]
|
||||
|
||||
- Fixed #2801 this is when a season is not correctly monitored in sonarr when approved by an admin. [tidusjar]
|
||||
|
||||
- Small improvements to try and mitigate #2750. [tidusjar]
|
||||
|
||||
- Removed some areas where we clear out the cache. This should help with DB locking #2750. [tidusjar]
|
||||
|
||||
- Fixed #2810. [tidusjar]
|
||||
|
||||
- Cannot create an issue comment with the API #2811. [tidusjar]
|
||||
|
||||
- Set the local domain if the Application URL is set for the HELO or EHLO commands. #2636. [tidusjar]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- Delete ISSUE_TEMPLATE.md. [Jamie]
|
||||
|
||||
- More minor grammatical edits. [Andrew Metzger]
|
||||
|
||||
- Minor grammatical edits. [Andrew Metzger]
|
||||
|
||||
- Fixed #2802 the issue where "Issues" were not being deleted correctly. [tidusjar]
|
||||
|
||||
- Fixed #2797. [tidusjar]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- Fixed #2786. [tidusjar]
|
||||
|
||||
- Fixed #2756. [tidusjar]
|
||||
|
||||
- Ignore the UserName header as part of the Api is the value is an empty string. [tidusjar]
|
||||
|
||||
- Fixed #2759. [tidusjar]
|
||||
|
||||
- Did #2756. [TidusJar]
|
||||
|
||||
- Fixed the exception that sometimes makes ombi fallover. [TidusJar]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- Log the error to the ui to figure out what's going on with #2755. [tidusjar]
|
||||
|
||||
- Small fix when denying a request with a reason, we wasn't updating the ui. [TidusJar]
|
||||
|
||||
- Make sure we can only set the ApiAlias when using the API Key. [tidusjar]
|
||||
|
||||
- #2363 Added the ability to pass any username into the API using the ApiAlias header. [tidusjar]
|
||||
|
||||
- Removed the Add user to Plex from Ombi. [tidusjar]
|
||||
|
||||
|
||||
## v3.0.4119 (2019-01-09)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
- Added a page where the admin can write/style/basically do whatever they want with e.g. FAQ for the users #2715 This needs to be enabled in the Customization Settings and then it's all configured on the page. [TidusJar]
|
||||
|
||||
- Updated the AspnetCore.App package to remove the CVE-2019-0564 vulnerability. [TidusJar]
|
||||
|
||||
- Added a global language flag that now applies to the search by default. [tidusjar]
|
||||
|
||||
|
@ -21,28 +119,114 @@
|
|||
|
||||
- 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]
|
||||
|
||||
- Updated the Newsletter template! Better mail client support [d1slact0r]
|
||||
- Added the ability to search movies via the movie db with a different language! [tidusjar]
|
||||
|
||||
- Updated boostrap #2694. [TidusJar]
|
||||
- Added the ability to specify a year when searching for movies. [tidusjar]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||
|
||||
- Updated boostrap #2694. [Jamie]
|
||||
|
||||
- Added the ability to deny a request with a reason. [TidusJar]
|
||||
|
||||
- Updated to .net core 2.2 and included a linux-arm64 build. [aptalca]
|
||||
- Update EmbyEpisodeSync.cs. [Jamie]
|
||||
|
||||
- Make the newsletter BCC the users rather than creating a million newsletters (Hopefully will stop SMTP providers from marking as spam). This does mean that the custom user customization in the newsletter will no longer work. [TidusJar]
|
||||
|
||||
- New translations [TidusJar]
|
||||
- Updated to .net core 2.2 and included a linux-arm64 build. [TidusJar]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- There is now a new Job in ombi that will clear out the Plex/Emby data and recache. This will prevent the issues going forward that we have when Ombi and the Media server fall out of sync with deletions/updates #2641 #2362 #1566. [TidusJar]
|
||||
|
||||
- Potentially fix #2726. [TidusJar]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- Fixed #2725 and #2721. [TidusJar]
|
||||
|
||||
- Made the newsletter use the default lanuage code set in the Ombi settings for movie information. [TidusJar]
|
||||
|
||||
- Save the language code against the request so we can use it later e.g. Sending to the DVR apps. [tidusjar]
|
||||
|
||||
- Fixed #2716. [tidusjar]
|
||||
|
||||
- Make the newsletter BCC the users rather than creating a million newsletters (Hopefully will stop SMTP providers from marking as spam). This does mean that the custom user customization in the newsletter will no longer work. [TidusJar]
|
||||
|
||||
- If we don't know the Plex agent, then see if it's a ImdbId, if it's not check the string for any episode and season hints #2695. [tidusjar]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- New translations en.json (Polish) [Jamie]
|
||||
|
||||
- New translations en.json (Norwegian) [Jamie]
|
||||
|
||||
- New translations en.json (Italian) [Jamie]
|
||||
|
||||
- New translations en.json (German) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Danish) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- Made the search results the language specified in the search refinement. [tidusjar]
|
||||
|
||||
- Fixed #2704. [tidusjar]
|
||||
|
||||
- Now it is fixed :) [d1slact0r]
|
||||
|
||||
- Android please be nice now. [d1slact0r]
|
||||
|
||||
- Fixed title bit better. [d1slact0r]
|
||||
|
||||
- Fixed titles. [d1slact0r]
|
||||
|
||||
- This should fix the build for sure (stupid quotes) [d1slact0r]
|
||||
|
||||
- Fixes build. [d1slact0r]
|
||||
|
||||
- Rewritten the whole newsletter template. [d1slact0r]
|
||||
|
||||
- Fixed #2697. [tidusjar]
|
||||
|
||||
- Add linux-arm runtime identifier. [aptalca]
|
||||
|
||||
- Add back arm packages. [aptalca]
|
||||
|
||||
- Add arm32 package. [aptalca]
|
||||
|
||||
- Fixed #2691. [tidusjar]
|
||||
|
||||
- Fixed linting. [TidusJar]
|
||||
|
@ -51,10 +235,13 @@
|
|||
|
||||
- Fixed #2678. [TidusJar]
|
||||
|
||||
- Deny reason for movie requests. [TidusJar]
|
||||
|
||||
- Set the landing and login pages background refresh to 15 seconds rather than 10 and 7. [TidusJar]
|
||||
|
||||
- Fixed a bug with us thinking future dated emby episodes are not available, Consoldated the emby and plex search rules (since they have the same logic) [TidusJar]
|
||||
|
||||
- Fixed build. [TidusJar]
|
||||
|
||||
|
||||
## v3.0.4036 (2018-12-11)
|
||||
|
|
17
README.md
17
README.md
|
@ -9,11 +9,19 @@ ____
|
|||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
___
|
||||
|
||||
[](https://twitter.com/intent/follow?screen_name=tidusjar)
|
||||
|
||||
Follow me developing Ombi!
|
||||
|
||||
[](https://twitch.tv/tiusjar)
|
||||
|
||||
|
||||
___
|
||||
<a href='https://play.google.com/store/apps/details?id=com.tidusjar.Ombi&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img width="150" alt='Get it on Google Play' src='https://play.google.com/intl/en_gb/badges/images/generic/en_badge_web_generic.png'/></a>
|
||||
|
||||
<a href='https://itunes.apple.com/us/app/ombi/id1335260043?ls=1&mt=8'><img width="150" alt='Get it on App Store' src='https://i.imgur.com/cJFa0M4.png'/></a>
|
||||
|
||||
<br>
|
||||
_**Note:** There is no longer an iOS app due to complications outside of our control._
|
||||
|
||||
___
|
||||
|
||||
|
@ -34,6 +42,7 @@ Here are some of the features Ombi V3 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.)
|
||||
* 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)
|
||||
* User management system (supports plex.tv, Emby and local accounts)
|
||||
* A landing page that will give you the availability of your Plex/Emby server and also add custom notification text to inform your users of downtime.
|
||||
* Allows your users to get custom notifications!
|
||||
|
@ -41,7 +50,7 @@ Here are some of the features Ombi V3 has:
|
|||
* Will show if the request is already on plex or even if it's already monitored.
|
||||
* Automatically updates the status of requests when they are available on Plex/Emby
|
||||
* Slick, responsive and mobile friendly UI
|
||||
* Ombi will automatically update itself :)
|
||||
* Ombi will automatically update itself :) (YMMV)
|
||||
* Very fast!
|
||||
|
||||
### Integration
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
await LogError(request, httpResponseMessage);
|
||||
}
|
||||
|
||||
if (request.Retry)
|
||||
|
@ -105,7 +105,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
await LogError(request, httpResponseMessage);
|
||||
}
|
||||
}
|
||||
// do something with the response
|
||||
|
@ -126,7 +126,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
await LogError(request, httpResponseMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,10 +149,15 @@ namespace Ombi.Api
|
|||
}
|
||||
}
|
||||
|
||||
private void LogError(Request request, HttpResponseMessage httpResponseMessage)
|
||||
private async Task LogError(Request request, HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
Logger.LogError(LoggingEvents.Api,
|
||||
$"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}, RequestUri: {request.FullUri}");
|
||||
if (Logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
var content = await httpResponseMessage.Content.ReadAsStringAsync();
|
||||
Logger.LogDebug(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
106
src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs
Normal file
106
src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Config;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine.Demo
|
||||
{
|
||||
public class DemoMovieSearchEngine : MovieSearchEngine, IDemoMovieSearchEngine
|
||||
{
|
||||
public DemoMovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
||||
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s,
|
||||
IRepository<RequestSubscription> sub, IOptions<DemoLists> lists)
|
||||
: base(identity, service, movApi, mapper, logger, r, um, mem, s, sub)
|
||||
{
|
||||
_demoLists = lists.Value;
|
||||
}
|
||||
|
||||
private readonly DemoLists _demoLists;
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
|
||||
{
|
||||
var result = await MovieApi.SearchMovie(search, null, "en");
|
||||
|
||||
for (var i = 0; i < result.Count; i++)
|
||||
{
|
||||
if (!_demoLists.Movies.Contains(result[i].Id))
|
||||
{
|
||||
result.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
if(result.Count > 0)
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
||||
{
|
||||
var rand = new Random();
|
||||
var responses = new List<SearchMovieViewModel>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var item = rand.Next(_demoLists.Movies.Length);
|
||||
var movie = _demoLists.Movies[item];
|
||||
if (responses.Any(x => x.Id == movie))
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
var movieResult = await MovieApi.GetMovieInformationWithExtraInfo(movie);
|
||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieResult);
|
||||
|
||||
responses.Add(await ProcessSingleMovie(viewMovie));
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
||||
{
|
||||
return await NowPlayingMovies();
|
||||
}
|
||||
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
||||
{
|
||||
return await NowPlayingMovies();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
||||
{
|
||||
|
||||
return await NowPlayingMovies();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDemoMovieSearchEngine
|
||||
{
|
||||
Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> Search(string search);
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
||||
|
||||
}
|
||||
}
|
96
src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs
Normal file
96
src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using AutoMapper;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.Trakt;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Config;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Models.Requests;
|
||||
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.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Core.Engine.Demo
|
||||
{
|
||||
public class DemoTvSearchEngine : TvSearchEngine, IDemoTvSearchEngine
|
||||
{
|
||||
|
||||
public DemoTvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
||||
ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings, IPlexContentRepository repo,
|
||||
IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
|
||||
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists)
|
||||
: base(identity, service, tvMaze, mapper, plexSettings, embySettings, repo, embyRepo, trakt, r, um, memCache, s, sub)
|
||||
{
|
||||
_demoLists = lists.Value;
|
||||
}
|
||||
|
||||
private readonly DemoLists _demoLists;
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string search)
|
||||
{
|
||||
var searchResult = await TvMazeApi.Search(search);
|
||||
|
||||
for (var i = 0; i < searchResult.Count; i++)
|
||||
{
|
||||
if (!_demoLists.TvShows.Contains(searchResult[i].show?.externals?.thetvdb ?? 0))
|
||||
{
|
||||
searchResult.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (searchResult != null)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in searchResult)
|
||||
{
|
||||
if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> NowPlayingMovies()
|
||||
{
|
||||
var rand = new Random();
|
||||
var responses = new List<SearchTvShowViewModel>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var item = rand.Next(_demoLists.TvShows.Length);
|
||||
var tv = _demoLists.TvShows[item];
|
||||
if (responses.Any(x => x.Id == tv))
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
var movieResult = await TvMazeApi.ShowLookup(tv);
|
||||
responses.Add(ProcessResult(movieResult));
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public interface IDemoTvSearchEngine
|
||||
{
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Search(string search);
|
||||
Task<IEnumerable<SearchTvShowViewModel>> NowPlayingMovies();
|
||||
}
|
||||
}
|
|
@ -19,5 +19,6 @@ namespace Ombi.Core
|
|||
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = null);
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId, string langCode);
|
||||
Task<IEnumerable<SearchMovieViewModel>> SearchActor(string search, string langaugeCode);
|
||||
}
|
||||
}
|
|
@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
|
|||
Approved = false,
|
||||
RequestedUserId = userDetails.Id,
|
||||
Background = movieInfo.BackdropPath,
|
||||
LangCode = model.LanguageCode
|
||||
LangCode = model.LanguageCode,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
};
|
||||
|
||||
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
|
@ -325,6 +326,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = true,
|
||||
Message = "Request successfully deleted",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -31,11 +31,11 @@ namespace Ombi.Core.Engine
|
|||
Logger = logger;
|
||||
}
|
||||
|
||||
private IMovieDbApi MovieApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
protected IMovieDbApi MovieApi { get; }
|
||||
protected IMapper Mapper { get; }
|
||||
private ILogger<MovieSearchEngine> Logger { get; }
|
||||
|
||||
private const int MovieLimit = 10;
|
||||
protected const int MovieLimit = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Lookups the imdb information.
|
||||
|
@ -54,8 +54,6 @@ namespace Ombi.Core.Engine
|
|||
/// <summary>
|
||||
/// Searches the specified movie.
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string langaugeCode)
|
||||
{
|
||||
langaugeCode = await DefaultLanguageCode(langaugeCode);
|
||||
|
@ -68,6 +66,33 @@ namespace Ombi.Core.Engine
|
|||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> SearchActor(string search, string langaugeCode)
|
||||
{
|
||||
langaugeCode = await DefaultLanguageCode(langaugeCode);
|
||||
var people = await MovieApi.SearchByActor(search, langaugeCode);
|
||||
var person = people?.results?.Count > 0 ? people.results.FirstOrDefault() : null;
|
||||
|
||||
var resultSet = new List<SearchMovieViewModel>();
|
||||
if (person == null)
|
||||
{
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
// Get this person movie credits
|
||||
var credits = await MovieApi.GetActorMovieCredits(person.id, langaugeCode);
|
||||
// Grab results from both cast and crew, prefer items in cast. we can handle directors like this.
|
||||
var movieResults = (from role in credits.cast select new { Id = role.id, Title = role.title, ReleaseDate = role.release_date }).ToList();
|
||||
movieResults.AddRange((from job in credits.crew select new { Id = job.id, Title = job.title, ReleaseDate = job.release_date }).ToList());
|
||||
|
||||
movieResults = movieResults.Take(10).ToList();
|
||||
foreach (var movieResult in movieResults)
|
||||
{
|
||||
resultSet.Add(await LookupImdbInformation(movieResult.Id, langaugeCode));
|
||||
}
|
||||
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get similar movies to the id passed in
|
||||
/// </summary>
|
||||
|
@ -159,7 +184,7 @@ namespace Ombi.Core.Engine
|
|||
return null;
|
||||
}
|
||||
|
||||
private async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||
IEnumerable<MovieSearchResult> movies)
|
||||
{
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
|
@ -170,24 +195,25 @@ namespace Ombi.Core.Engine
|
|||
return viewMovies;
|
||||
}
|
||||
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
|
||||
protected async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
|
||||
{
|
||||
if (lookupExtraInfo)
|
||||
if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty())
|
||||
{
|
||||
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
|
||||
viewMovie.Id = showInfo.Id; // TheMovieDbId
|
||||
viewMovie.ImdbId = showInfo.ImdbId;
|
||||
var usDates = viewMovie.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
viewMovie.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
|
||||
}
|
||||
|
||||
var usDates = viewMovie.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
viewMovie.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
|
||||
|
||||
viewMovie.TheMovieDbId = viewMovie.Id.ToString();
|
||||
|
||||
await RunSearchRules(viewMovie);
|
||||
|
||||
// This requires the rules to be run first to populate the RequestId property
|
||||
await CheckForSubscription(viewMovie);
|
||||
|
||||
|
||||
return viewMovie;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
|
|||
Title = album.title,
|
||||
Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
|
||||
Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url,
|
||||
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty
|
||||
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
};
|
||||
if (requestModel.Cover.IsNullOrEmpty())
|
||||
{
|
||||
|
|
|
@ -385,6 +385,7 @@ namespace Ombi.Core.Engine
|
|||
foreach (var ep in s.Episodes)
|
||||
{
|
||||
ep.Approved = true;
|
||||
ep.Requested = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace Ombi.Core.Engine
|
|||
EmbyContentRepo = embyRepo;
|
||||
}
|
||||
|
||||
private ITvMazeApi TvMazeApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
protected ITvMazeApi TvMazeApi { get; }
|
||||
protected IMapper Mapper { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private IPlexContentRepository PlexContentRepo { get; }
|
||||
|
@ -99,7 +99,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
Url = e.url,
|
||||
Title = e.name,
|
||||
AirDate = DateTime.Parse(e.airstamp ?? DateTime.MinValue.ToString()),
|
||||
AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
|
||||
EpisodeNumber = e.number,
|
||||
|
||||
});
|
||||
|
@ -112,7 +112,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
Url = e.url,
|
||||
Title = e.name,
|
||||
AirDate = DateTime.Parse(e.airstamp ?? DateTime.MinValue.ToString()),
|
||||
AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
|
||||
EpisodeNumber = e.number,
|
||||
});
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ namespace Ombi.Core.Engine
|
|||
return processed;
|
||||
}
|
||||
|
||||
private IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
|
||||
protected IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in items)
|
||||
|
@ -159,7 +159,7 @@ namespace Ombi.Core.Engine
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private SearchTvShowViewModel ProcessResult<T>(T tvMazeSearch)
|
||||
protected SearchTvShowViewModel ProcessResult<T>(T tvMazeSearch)
|
||||
{
|
||||
return Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace Ombi.Core.Helpers
|
|||
SeasonRequests = new List<SeasonRequests>(),
|
||||
Title = ShowInfo.name,
|
||||
ReleaseYear = FirstAir,
|
||||
RequestedByAlias = model.RequestedByAlias,
|
||||
SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
|
||||
};
|
||||
|
||||
|
|
|
@ -24,11 +24,20 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public class MovieRequestViewModel
|
||||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
public string LanguageCode { get; set; } = "en";
|
||||
|
||||
/// <summary>
|
||||
/// This is only set from a HTTP Header
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
}
|
|
@ -3,5 +3,6 @@
|
|||
public class MusicAlbumRequestViewModel
|
||||
{
|
||||
public string ForeignAlbumId { get; set; }
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
|
@ -9,6 +10,8 @@ namespace Ombi.Core.Models.Requests
|
|||
public bool FirstSeason { get; set; }
|
||||
public int TvDbId { get; set; }
|
||||
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsViewModel
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
|
@ -87,11 +88,11 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
}
|
||||
}
|
||||
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Available && e.AirDate > DateTime.MinValue)))
|
||||
{
|
||||
request.FullyAvailable = true;
|
||||
}
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available)))
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available && e.AirDate > DateTime.MinValue)))
|
||||
{
|
||||
request.PartlyAvailable = true;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
try
|
||||
{
|
||||
|
||||
var cpSettings = await CouchPotatoSettings.GetSettingsAsync();
|
||||
//var watcherSettings = await WatcherSettings.GetSettingsAsync();
|
||||
var radarrSettings = await RadarrSettings.GetSettingsAsync();
|
||||
|
@ -76,7 +75,7 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Error when seing movie to DVR app, added to the request queue");
|
||||
Log.LogError(e, "Error when sending movie to DVR app, added to the request queue");
|
||||
|
||||
// Check if already in request quee
|
||||
var existingQueue = await _requestQueuRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id);
|
||||
|
|
|
@ -16,6 +16,7 @@ using Ombi.Settings.Settings.Models.External;
|
|||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Remotion.Linq.Parsing.Structure.IntermediateModel;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
|
@ -57,7 +58,7 @@ namespace Ombi.Core.Senders
|
|||
var sonarr = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarr.Enabled)
|
||||
{
|
||||
var result = await SendToSonarr(model);
|
||||
var result = await SendToSonarr(model, sonarr);
|
||||
if (result != null)
|
||||
{
|
||||
return new SenderResult
|
||||
|
@ -109,7 +110,7 @@ namespace Ombi.Core.Senders
|
|||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Exception thrown when sending a movie to DVR app, added to the request queue");
|
||||
// Check if already in request quee
|
||||
// Check if already in request queue
|
||||
var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id);
|
||||
if (existingQueue != null)
|
||||
{
|
||||
|
@ -134,7 +135,7 @@ namespace Ombi.Core.Senders
|
|||
return new SenderResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Something wen't wrong!"
|
||||
Message = "Something went wrong!"
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -150,13 +151,8 @@ namespace Ombi.Core.Senders
|
|||
/// <param name="s"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model)
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
|
||||
{
|
||||
var s = await SonarrSettings.GetSettingsAsync();
|
||||
if (!s.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(s.ApiKey))
|
||||
{
|
||||
return null;
|
||||
|
@ -319,10 +315,19 @@ namespace Ombi.Core.Senders
|
|||
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var sonarrEpisodeList = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber).ToList();
|
||||
var sonarrEpCount = sonarrEpisodeList.Count;
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
var ourEpisodes = season.Episodes.Select(x => x.EpisodeNumber).ToList();
|
||||
var unairedEpisodes = sonarrEpisodeList.Where(x => x.airDateUtc > DateTime.UtcNow).Select(x => x.episodeNumber).ToList();
|
||||
|
||||
//// Check if we have requested all the latest episodes, if we have then monitor
|
||||
//// NOTE, not sure if needed since ombi ui displays future episodes anyway...
|
||||
//ourEpisodes.AddRange(unairedEpisodes);
|
||||
//var distinctEpisodes = ourEpisodes.Distinct().ToList();
|
||||
//var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes);
|
||||
|
||||
var existingSeason =
|
||||
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
|
@ -332,7 +337,7 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
if (sonarrEpCount == ourRequestCount /*|| !missingEpisodes.Any()*/)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ using Ombi.Updater;
|
|||
using PlexContentCacher = Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Api.Telegram;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Demo;
|
||||
using Ombi.Core.Processor;
|
||||
using Ombi.Schedule.Jobs.Lidarr;
|
||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||
|
@ -92,6 +93,8 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
|
||||
services.AddTransient<IVoteEngine, VoteEngine>();
|
||||
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
|
||||
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
|
||||
}
|
||||
public static void RegisterHttp(this IServiceCollection services)
|
||||
{
|
||||
|
|
11
src/Ombi.Helpers/DemoLists.cs
Normal file
11
src/Ombi.Helpers/DemoLists.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ombi.Config
|
||||
{
|
||||
public class DemoLists
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public int[] Movies { get; set; }
|
||||
public int[] TvShows { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
13
src/Ombi.Helpers/DemoSingleton.cs
Normal file
13
src/Ombi.Helpers/DemoSingleton.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ombi.Helpers
|
||||
{
|
||||
public class DemoSingleton
|
||||
{
|
||||
private static DemoSingleton instance;
|
||||
|
||||
private DemoSingleton() { }
|
||||
|
||||
public static DemoSingleton Instance => instance ?? (instance = new DemoSingleton());
|
||||
|
||||
public bool Demo { get; set; }
|
||||
}
|
||||
}
|
|
@ -7,11 +7,16 @@ namespace Ombi.Helpers
|
|||
{
|
||||
public class EmbyHelper
|
||||
{
|
||||
public static string GetEmbyMediaUrl(string mediaId)
|
||||
public static string GetEmbyMediaUrl(string mediaId, string customerServerUrl = null)
|
||||
{
|
||||
var url =
|
||||
$"http://app.emby.media/#!/itemdetails.html?id={mediaId}";
|
||||
return url;
|
||||
if (customerServerUrl.HasValue())
|
||||
{
|
||||
return $"{customerServerUrl}#!/itemdetails.html?id={mediaId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"https://app.emby.media/#!/itemdetails.html?id={mediaId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,5 +15,6 @@
|
|||
public const string Disabled = nameof(Disabled);
|
||||
public const string ReceivesNewsletter = nameof(ReceivesNewsletter);
|
||||
public const string ManageOwnRequests = nameof(ManageOwnRequests);
|
||||
public const string EditCustomPage = nameof(EditCustomPage);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates
|
|||
if (string.IsNullOrEmpty(_templateLocation))
|
||||
{
|
||||
#if DEBUG
|
||||
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.0", "Templates",
|
||||
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.2", "Templates",
|
||||
"BasicTemplate.html");
|
||||
#else
|
||||
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html");
|
||||
|
|
|
@ -56,148 +56,42 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewRequest(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.NewRequest, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.NewRequest);
|
||||
}
|
||||
|
||||
protected override async Task NewIssue(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.Issue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.Issue);
|
||||
}
|
||||
|
||||
protected override async Task IssueComment(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.IssueComment, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueComment);
|
||||
}
|
||||
|
||||
protected override async Task IssueResolved(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.IssueResolved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueResolved);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var user = string.Empty;
|
||||
var title = string.Empty;
|
||||
var image = string.Empty;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
image = MovieRequest.PosterPath;
|
||||
}
|
||||
else if (model.RequestType == RequestType.TvShow)
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
image = TvRequest.ParentRequest.PosterPath;
|
||||
}
|
||||
else if (model.RequestType == RequestType.Album)
|
||||
{
|
||||
user = AlbumRequest.RequestedUser.UserAlias;
|
||||
title = AlbumRequest.Title;
|
||||
image = AlbumRequest.Cover;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
notification.Other.Add("image", image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestDeclined, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestDeclined);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestApproved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestAvailable, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, DiscordNotificationSettings settings)
|
||||
|
@ -242,5 +136,21 @@ namespace Ombi.Notifications.Agents
|
|||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
private async Task Run(NotificationOptions model, DiscordNotificationSettings settings, NotificationType type)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Discord, type, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Discord}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,6 @@ namespace Ombi.Notifications.Agents
|
|||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Send to admin
|
||||
message.To = settings.AdminEmail;
|
||||
}
|
||||
|
@ -183,37 +182,21 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var email = new EmailBasicTemplate();
|
||||
var user = string.Empty;
|
||||
var title = string.Empty;
|
||||
var img = string.Empty;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
if (!model.Recipient.HasValue())
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
img = $"https://image.tmdb.org/t/p/w300/{MovieRequest.PosterPath}";
|
||||
return;
|
||||
}
|
||||
else
|
||||
var message = await LoadTemplate(NotificationType.ItemAddedToFaultQueue, model, settings);
|
||||
if (message == null)
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
img = TvRequest.ParentRequest.PosterPath;
|
||||
return;
|
||||
}
|
||||
|
||||
var html = email.LoadTemplate(
|
||||
$"{Customization.ApplicationName}: A request could not be added.",
|
||||
$"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying", img, Customization.Logo);
|
||||
|
||||
var message = new NotificationMessage
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"{Customization.ApplicationName}: A request could not be added",
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
|
||||
var plaintext = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var plaintext = await LoadPlainTextMessage(NotificationType.ItemAddedToFaultQueue, model, settings);
|
||||
message.Other.Add("PlainTextBody", plaintext);
|
||||
|
||||
// Issues resolved should be sent to the user
|
||||
message.To = settings.AdminEmail;
|
||||
await Send(message, settings);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,20 +46,7 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewRequest(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.NewRequest, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
//notification.Other.Add("overview", model.RequestType == RequestType.Movie ? base.MovieRequest.Overview : TvRequest.);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.NewRequest);
|
||||
}
|
||||
|
||||
private void AddOtherInformation(NotificationOptions model, NotificationMessage notification,
|
||||
|
@ -71,125 +58,37 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewIssue(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.Issue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.Issue);
|
||||
}
|
||||
|
||||
protected override async Task IssueComment(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.IssueComment, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueComment);
|
||||
}
|
||||
|
||||
protected override async Task IssueResolved(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.IssueResolved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueResolved);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var user = string.Empty;
|
||||
var title = string.Empty;
|
||||
var image = string.Empty;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
image = MovieRequest.PosterPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
image = TvRequest.ParentRequest.PosterPath;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
notification.Other.Add("image", image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.RequestDeclined, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestDeclined);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.RequestApproved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationOptions model, MattermostNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.RequestAvailable, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, MattermostNotificationSettings settings)
|
||||
|
@ -228,5 +127,21 @@ namespace Ombi.Notifications.Agents
|
|||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
private async Task Run(NotificationOptions model, MattermostNotificationSettings settings, NotificationType type)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mattermost, type, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Mattermost}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
AddOtherInformation(model, notification, parsed);
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,23 +130,18 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, MobileNotificationSettings settings)
|
||||
{
|
||||
string user;
|
||||
string title;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
|
||||
var parsed = await LoadTemplate(NotificationAgent.Mobile, NotificationType.ItemAddedToFaultQueue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
_logger.LogInformation($"Template {NotificationType.ItemAddedToFaultQueue} is disabled for {NotificationAgent.Mobile}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
// Get admin devices
|
||||
var playerIds = await GetAdmins(NotificationType.Test);
|
||||
await Send(playerIds, notification, settings, model);
|
||||
|
@ -294,6 +289,5 @@ namespace Ombi.Notifications.Agents
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -44,131 +44,43 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewRequest(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.NewRequest, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.NewRequest);
|
||||
}
|
||||
|
||||
|
||||
protected override async Task NewIssue(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.Issue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.Issue);
|
||||
}
|
||||
|
||||
protected override async Task IssueComment(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.IssueComment, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueComment);
|
||||
}
|
||||
|
||||
protected override async Task IssueResolved(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.IssueResolved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueResolved);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
string user;
|
||||
string title;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.RequestDeclined, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestDeclined);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.RequestApproved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationOptions model, PushbulletSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.RequestAvailable, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, PushbulletSettings settings)
|
||||
|
@ -192,5 +104,22 @@ namespace Ombi.Notifications.Agents
|
|||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
private async Task Run(NotificationOptions model, PushbulletSettings settings, NotificationType type)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushbullet, type, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Pushbullet}");
|
||||
return;
|
||||
}
|
||||
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,132 +45,42 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewRequest(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.NewRequest, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.NewRequest);
|
||||
}
|
||||
|
||||
protected override async Task NewIssue(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.Issue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.Issue);
|
||||
}
|
||||
|
||||
protected override async Task IssueComment(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.IssueComment, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueComment);
|
||||
}
|
||||
|
||||
protected override async Task IssueResolved(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.IssueResolved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueResolved);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
string user;
|
||||
string title;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.RequestDeclined, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestDeclined);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.RequestApproved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationOptions model, PushoverSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.RequestAvailable, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, PushoverSettings settings)
|
||||
|
@ -195,5 +105,21 @@ namespace Ombi.Notifications.Agents
|
|||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
private async Task Run(NotificationOptions model, PushoverSettings settings, NotificationType type)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Pushover, type, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Pushover}");
|
||||
return;
|
||||
}
|
||||
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,138 +54,42 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewRequest(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.NewRequest, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.NewRequest);
|
||||
}
|
||||
|
||||
protected override async Task NewIssue(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.Issue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.Issue);
|
||||
}
|
||||
|
||||
protected override async Task IssueComment(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.IssueComment, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueComment);
|
||||
}
|
||||
|
||||
protected override async Task IssueResolved(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.IssueResolved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueResolved);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var user = string.Empty;
|
||||
var title = string.Empty;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.RequestDeclined, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.RequestApproved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationOptions model, SlackNotificationSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.RequestAvailable, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, SlackNotificationSettings settings)
|
||||
|
@ -218,5 +122,21 @@ namespace Ombi.Notifications.Agents
|
|||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
private async Task Run(NotificationOptions model, SlackNotificationSettings settings, NotificationType type)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Slack, type, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Slack}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
notification.Other.Add("image", parsed.Image);
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,134 +41,42 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task NewRequest(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.NewRequest, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.NewRequest);
|
||||
}
|
||||
|
||||
protected override async Task NewIssue(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.Issue, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.Issue);
|
||||
}
|
||||
|
||||
protected override async Task IssueComment(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.IssueComment, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueComment);
|
||||
}
|
||||
|
||||
protected override async Task IssueResolved(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.IssueResolved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.IssueResolved);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var user = string.Empty;
|
||||
var title = string.Empty;
|
||||
var image = string.Empty;
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
user = MovieRequest.RequestedUser.UserAlias;
|
||||
title = MovieRequest.Title;
|
||||
image = MovieRequest.PosterPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
image = TvRequest.ParentRequest.PosterPath;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.RequestDeclined, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestDeclined);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.RequestApproved, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message ?? string.Empty,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationOptions model, TelegramSettings settings)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.RequestAvailable, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
await Run(model, settings, NotificationType.RequestAvailable);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, TelegramSettings settings)
|
||||
|
@ -192,5 +100,20 @@ namespace Ombi.Notifications.Agents
|
|||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
private async Task Run(NotificationOptions model, TelegramSettings settings, NotificationType type)
|
||||
{
|
||||
var parsed = await LoadTemplate(NotificationAgent.Telegram, type, model);
|
||||
if (parsed.Disabled)
|
||||
{
|
||||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ namespace Ombi.Notifications
|
|||
MovieRepository = movie;
|
||||
TvRepository = tv;
|
||||
CustomizationSettings = customization;
|
||||
Settings.ClearCache();
|
||||
CustomizationSettings.ClearCache();
|
||||
RequestSubscription = sub;
|
||||
_log = log;
|
||||
AlbumRepository = album;
|
||||
|
@ -55,14 +53,12 @@ namespace Ombi.Notifications
|
|||
|
||||
public async Task NotifyAsync(NotificationOptions model)
|
||||
{
|
||||
Settings.ClearCache();
|
||||
var configuration = await GetConfiguration();
|
||||
await NotifyAsync(model, configuration);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync(NotificationOptions model, Settings.Settings.Models.Settings settings)
|
||||
{
|
||||
Settings.ClearCache();
|
||||
if (settings == null) await NotifyAsync(model);
|
||||
|
||||
var notificationSettings = (T)settings;
|
||||
|
|
|
@ -5,6 +5,7 @@ using MailKit.Net.Smtp;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using MimeKit;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Notifications.Templates;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
|
|
@ -28,7 +28,6 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
_repo = repo;
|
||||
_episodeSync = epSync;
|
||||
_metadata = metadata;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ILogger<EmbyContentSync> _logger;
|
||||
|
@ -87,7 +86,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri);
|
||||
foreach (var item in movieInfo.Items)
|
||||
{
|
||||
await ProcessMovies(item, mediaToAdd);
|
||||
await ProcessMovies(item, mediaToAdd, server);
|
||||
}
|
||||
|
||||
processed++;
|
||||
|
@ -96,7 +95,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
{
|
||||
processed++;
|
||||
// Regular movie
|
||||
await ProcessMovies(movie, mediaToAdd);
|
||||
await ProcessMovies(movie, mediaToAdd, server);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +137,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
Title = tvShow.Name,
|
||||
Type = EmbyMediaType.Series,
|
||||
EmbyId = tvShow.Id,
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id),
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server.ServerHostname),
|
||||
AddedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
@ -164,7 +163,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
await _repo.AddRange(mediaToAdd);
|
||||
}
|
||||
|
||||
private async Task ProcessMovies(EmbyMovie movieInfo, ICollection<EmbyContent> content)
|
||||
private async Task ProcessMovies(EmbyMovie movieInfo, ICollection<EmbyContent> content, EmbyServers server)
|
||||
{
|
||||
// Check if it exists
|
||||
var existingMovie = await _repo.GetByEmbyId(movieInfo.Id);
|
||||
|
@ -179,7 +178,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
Title = movieInfo.Name,
|
||||
Type = EmbyMediaType.Movie,
|
||||
EmbyId = movieInfo.Id,
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id),
|
||||
Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server.ServerHostname),
|
||||
AddedAt = DateTime.UtcNow,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
_settings = s;
|
||||
_repo = repo;
|
||||
_avaliabilityChecker = checker;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<EmbySettings> _settings;
|
||||
|
|
|
@ -50,8 +50,6 @@ namespace Ombi.Schedule.Jobs.Emby
|
|||
_log = log;
|
||||
_embySettings = embySettings;
|
||||
_userManagementSettings = ums;
|
||||
_userManagementSettings.ClearCache();
|
||||
_embySettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly IEmbyApi _api;
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
|
@ -29,7 +26,6 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
|||
_ctx = ctx;
|
||||
_job = job;
|
||||
_availability = availability;
|
||||
_lidarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
|
@ -29,7 +26,6 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
|||
_ctx = ctx;
|
||||
_job = background;
|
||||
_albumSync = album;
|
||||
_lidarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
|
|
|
@ -28,9 +28,9 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
return;
|
||||
}
|
||||
|
||||
var now = DateTime.Now.AddDays(-settings.DaysAfterResolvedToDelete).Date;
|
||||
var deletionDate = DateTime.Now.AddDays(settings.DaysAfterResolvedToDelete).Date;
|
||||
var resolved = _issuesRepository.GetAll().Where(x => x.Status == IssueStatus.Resolved);
|
||||
var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date <= now);
|
||||
var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date >= deletionDate);
|
||||
|
||||
foreach (var d in toDelete)
|
||||
{
|
||||
|
|
|
@ -10,28 +10,23 @@ using Ombi.Schedule.Jobs.Emby;
|
|||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Plex
|
||||
namespace Ombi.Schedule.Jobs.Ombi
|
||||
{
|
||||
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
|
||||
{
|
||||
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log, IPlexApi plexApi,
|
||||
IPlexContentRepository plexRepo, IPlexContentSync c, IEmbyContentRepository embyRepo, IEmbyContentSync embySync)
|
||||
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
|
||||
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IEmbyContentSync embySync)
|
||||
{
|
||||
_settings = s;
|
||||
_log = log;
|
||||
_api = plexApi;
|
||||
_plexRepo = plexRepo;
|
||||
_plexContentSync = c;
|
||||
_embyRepo = embyRepo;
|
||||
_embyContentSync = embySync;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<PlexSettings> _settings;
|
||||
private readonly ILogger _log;
|
||||
private readonly IPlexApi _api;
|
||||
private readonly IPlexContentRepository _plexRepo;
|
||||
private readonly IPlexContentSync _plexContentSync;
|
||||
private readonly IEmbyContentRepository _embyRepo;
|
||||
private readonly IEmbyContentSync _embyContentSync;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ using Ombi.Api.TheMovieDb;
|
|||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications;
|
||||
using Ombi.Notifications.Models;
|
||||
|
@ -36,7 +37,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
||||
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
||||
ILidarrApi lidarrApi, IRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings,
|
||||
ISettingsService<OmbiSettings> ombiSettings)
|
||||
ISettingsService<OmbiSettings> ombiSettings, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings)
|
||||
{
|
||||
_plex = plex;
|
||||
_emby = emby;
|
||||
|
@ -49,16 +50,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_emailSettings = emailSettings;
|
||||
_newsletterSettings = newsletter;
|
||||
_userManager = um;
|
||||
_emailSettings.ClearCache();
|
||||
_customizationSettings.ClearCache();
|
||||
_newsletterSettings.ClearCache();
|
||||
_log = log;
|
||||
_lidarrApi = lidarrApi;
|
||||
_lidarrAlbumRepository = albumCache;
|
||||
_lidarrSettings = lidarrSettings;
|
||||
_ombiSettings = ombiSettings;
|
||||
_ombiSettings.ClearCache();
|
||||
_lidarrSettings.ClearCache();
|
||||
_plexSettings = plexSettings;
|
||||
_embySettings = embySettings;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plex;
|
||||
|
@ -77,6 +75,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly IRepository<LidarrAlbumCache> _lidarrAlbumRepository;
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
|
||||
public async Task Start(NewsletterSettings settings, bool test)
|
||||
{
|
||||
|
@ -132,6 +132,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
|
||||
_log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count());
|
||||
_log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.Count());
|
||||
var plexSettings = await _plexSettings.GetSettingsAsync();
|
||||
var embySettings = await _embySettings.GetSettingsAsync();
|
||||
var body = string.Empty;
|
||||
if (test)
|
||||
{
|
||||
|
@ -140,11 +142,11 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet();
|
||||
var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings);
|
||||
body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings, embySettings, plexSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings);
|
||||
body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings, embySettings, plexSettings);
|
||||
if (body.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
|
@ -333,7 +335,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
|
||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
|
||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings)
|
||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings, EmbySettings embySettings,
|
||||
PlexSettings plexSettings)
|
||||
{
|
||||
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
||||
var sb = new StringBuilder();
|
||||
|
@ -349,8 +352,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||
sb.Append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
|
||||
sb.Append("<tr>");
|
||||
await ProcessPlexMovies(plexMovies, sb, ombiSettings.DefaultLanguageCode);
|
||||
await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode);
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
await ProcessPlexMovies(plexMovies, sb, ombiSettings.DefaultLanguageCode);
|
||||
}
|
||||
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode);
|
||||
}
|
||||
|
||||
sb.Append("</tr>");
|
||||
sb.Append("</table>");
|
||||
sb.Append("</td>");
|
||||
|
@ -367,8 +378,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||
sb.Append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
|
||||
sb.Append("<tr>");
|
||||
await ProcessPlexTv(plexEpisodes, sb);
|
||||
await ProcessEmbyTv(embyEp, sb);
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
await ProcessPlexTv(plexEpisodes, sb);
|
||||
}
|
||||
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
await ProcessEmbyTv(embyEp, sb);
|
||||
}
|
||||
|
||||
sb.Append("</tr>");
|
||||
sb.Append("</table>");
|
||||
sb.Append("</td>");
|
||||
|
|
|
@ -5,18 +5,13 @@ using System.IO;
|
|||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Hangfire.Console;
|
||||
using Hangfire.Server;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Ombi.Api.Service;
|
||||
using Ombi.Api.Service.Models;
|
||||
using Ombi.Core.Processor;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
|
@ -40,7 +35,6 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
Settings = s;
|
||||
_processProvider = proc;
|
||||
_appConfig = appConfig;
|
||||
Settings.ClearCache();
|
||||
}
|
||||
|
||||
private ILogger<OmbiAutomaticUpdater> Logger { get; }
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Api.TvMaze;
|
||||
|
@ -21,7 +22,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo,
|
||||
ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings,
|
||||
IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability)
|
||||
IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability,
|
||||
IEmbyApi embyApi)
|
||||
{
|
||||
_plexRepo = plexRepo;
|
||||
_embyRepo = embyRepo;
|
||||
|
@ -32,6 +34,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_embySettings = embySettings;
|
||||
_plexAvailabilityChecker = plexAvailability;
|
||||
_embyAvaliabilityChecker = embyAvaliability;
|
||||
_embyApi = embyApi;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plexRepo;
|
||||
|
@ -43,6 +46,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
private readonly ITvMazeApi _tvApi;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly IEmbyApi _embyApi;
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
|
@ -54,11 +58,11 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
await StartPlex();
|
||||
}
|
||||
|
||||
|
||||
var embySettings = await _embySettings.GetSettingsAsync();
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
await StartEmby();
|
||||
await StartEmby(embySettings);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -123,9 +127,9 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
await StartPlexTv(allTv);
|
||||
}
|
||||
|
||||
private async Task StartEmby()
|
||||
private async Task StartEmby(EmbySettings s)
|
||||
{
|
||||
await StartEmbyMovies();
|
||||
await StartEmbyMovies(s);
|
||||
await StartEmbyTv();
|
||||
}
|
||||
|
||||
|
@ -158,7 +162,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_plexRepo.UpdateWithoutSave(show);
|
||||
}
|
||||
tvCount++;
|
||||
if (tvCount >= 20)
|
||||
if (tvCount >= 75)
|
||||
{
|
||||
await _plexRepo.SaveChangesAsync();
|
||||
tvCount = 0;
|
||||
|
@ -198,7 +202,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_embyRepo.UpdateWithoutSave(show);
|
||||
}
|
||||
tvCount++;
|
||||
if (tvCount >= 20)
|
||||
if (tvCount >= 75)
|
||||
{
|
||||
await _embyRepo.SaveChangesAsync();
|
||||
tvCount = 0;
|
||||
|
@ -229,7 +233,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_plexRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
movieCount++;
|
||||
if (movieCount >= 20)
|
||||
if (movieCount >= 75)
|
||||
{
|
||||
await _plexRepo.SaveChangesAsync();
|
||||
movieCount = 0;
|
||||
|
@ -239,31 +243,56 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
await _plexRepo.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task StartEmbyMovies()
|
||||
private async Task StartEmbyMovies(EmbySettings settings)
|
||||
{
|
||||
var allMovies = _embyRepo.GetAll().Where(x =>
|
||||
x.Type == EmbyMediaType.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue()));
|
||||
int movieCount = 0;
|
||||
foreach (var movie in allMovies)
|
||||
{
|
||||
var hasImdb = movie.ImdbId.HasValue();
|
||||
var hasTheMovieDb = movie.TheMovieDbId.HasValue();
|
||||
movie.ImdbId.HasValue();
|
||||
movie.TheMovieDbId.HasValue();
|
||||
// Movies don't really use TheTvDb
|
||||
|
||||
if (!hasImdb)
|
||||
// Check if it even has 1 ID
|
||||
if (!movie.HasImdb && !movie.HasTheMovieDb)
|
||||
{
|
||||
var imdbId = await GetImdbId(hasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty);
|
||||
// Ok this sucks,
|
||||
// The only think I can think that has happened is that we scanned Emby before Emby has got the metadata
|
||||
// So let's recheck emby to see if they have got the metadata now
|
||||
_log.LogInformation($"Movie {movie.Title} does not have a ImdbId or TheMovieDbId, so rechecking emby");
|
||||
foreach (var server in settings.Servers)
|
||||
{
|
||||
_log.LogInformation($"Checking server {server.Name} for upto date metadata");
|
||||
var movieInfo = await _embyApi.GetMovieInformation(movie.EmbyId, server.ApiKey, server.AdministratorId,
|
||||
server.FullUri);
|
||||
|
||||
if (movieInfo.ProviderIds?.Imdb.HasValue() ?? false)
|
||||
{
|
||||
movie.ImdbId = movieInfo.ProviderIds.Imdb;
|
||||
}
|
||||
|
||||
if (movieInfo.ProviderIds?.Tmdb.HasValue() ?? false)
|
||||
{
|
||||
movie.TheMovieDbId = movieInfo.ProviderIds.Tmdb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!movie.HasImdb)
|
||||
{
|
||||
var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty);
|
||||
movie.ImdbId = imdbId;
|
||||
_embyRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
if (!hasTheMovieDb)
|
||||
if (!movie.HasTheMovieDb)
|
||||
{
|
||||
var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title, true);
|
||||
var id = await GetTheMovieDbId(false, movie.HasImdb, string.Empty, movie.ImdbId, movie.Title, true);
|
||||
movie.TheMovieDbId = id;
|
||||
_embyRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
movieCount++;
|
||||
if (movieCount >= 20)
|
||||
if (movieCount >= 75)
|
||||
{
|
||||
await _embyRepo.SaveChangesAsync();
|
||||
movieCount = 0;
|
||||
|
|
|
@ -20,8 +20,6 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_email = provider;
|
||||
_templates = template;
|
||||
_customizationSettings = c;
|
||||
email.ClearCache();
|
||||
_customizationSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<EmailNotificationSettings> _emailSettings;
|
||||
|
|
|
@ -57,7 +57,6 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
EpisodeSync = epsiodeSync;
|
||||
Metadata = metadataRefresh;
|
||||
Checker = checker;
|
||||
plex.ClearCache();
|
||||
}
|
||||
|
||||
private ISettingsService<PlexSettings> Plex { get; }
|
||||
|
|
|
@ -26,7 +26,6 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
_api = plexApi;
|
||||
_repo = repo;
|
||||
_availabilityChecker = a;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<PlexSettings> _settings;
|
||||
|
|
|
@ -24,8 +24,6 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
_log = log;
|
||||
_plexSettings = plexSettings;
|
||||
_userManagementSettings = ums;
|
||||
_userManagementSettings.ClearCache();
|
||||
_plexSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly IPlexApi _api;
|
||||
|
|
|
@ -22,7 +22,6 @@ namespace Ombi.Schedule.Jobs.Radarr
|
|||
RadarrApi = radarrApi;
|
||||
Logger = log;
|
||||
_ctx = ctx;
|
||||
RadarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
|
|
|
@ -22,7 +22,6 @@ namespace Ombi.Schedule.Jobs.SickRage
|
|||
_api = api;
|
||||
_log = l;
|
||||
_ctx = ctx;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<SickRageSettings> _settings;
|
||||
|
|
|
@ -25,7 +25,6 @@ namespace Ombi.Schedule.Jobs.Sonarr
|
|||
_api = api;
|
||||
_log = l;
|
||||
_ctx = ctx;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<SonarrSettings> _settings;
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Ombi.Core.Settings.Models.External
|
|||
public string Name { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
public string AdministratorId { get; set; }
|
||||
public string ServerHostname { get; set; }
|
||||
public bool EnableEpisodeSearching { get; set; }
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ namespace Ombi.Settings.Settings
|
|||
var model = obj;
|
||||
|
||||
return model;
|
||||
}, DateTime.Now.AddHours(2));
|
||||
}, DateTime.Now.AddHours(5));
|
||||
}
|
||||
|
||||
public bool SaveSettings(T model)
|
||||
|
|
|
@ -87,43 +87,6 @@ namespace Ombi.Store.Context
|
|||
|
||||
public void Seed()
|
||||
{
|
||||
// VACUUM;
|
||||
Database.ExecuteSqlCommand("VACUUM;");
|
||||
|
||||
// Make sure we have the roles
|
||||
var newsletterRole = Roles.Where(x => x.Name == OmbiRoles.ReceivesNewsletter);
|
||||
if (!newsletterRole.Any())
|
||||
{
|
||||
Roles.Add(new IdentityRole(OmbiRoles.ReceivesNewsletter)
|
||||
{
|
||||
NormalizedName = OmbiRoles.ReceivesNewsletter.ToUpper()
|
||||
});
|
||||
SaveChanges();
|
||||
}
|
||||
var requestMusicRole = Roles.Where(x => x.Name == OmbiRoles.RequestMusic);
|
||||
if (!requestMusicRole.Any())
|
||||
{
|
||||
Roles.Add(new IdentityRole(OmbiRoles.RequestMusic)
|
||||
{
|
||||
NormalizedName = OmbiRoles.RequestMusic.ToUpper()
|
||||
});
|
||||
Roles.Add(new IdentityRole(OmbiRoles.AutoApproveMusic)
|
||||
{
|
||||
NormalizedName = OmbiRoles.AutoApproveMusic.ToUpper()
|
||||
});
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
var manageOwnRequestsRole = Roles.Where(x => x.Name == OmbiRoles.ManageOwnRequests);
|
||||
if (!manageOwnRequestsRole.Any())
|
||||
{
|
||||
Roles.Add(new IdentityRole(OmbiRoles.ManageOwnRequests)
|
||||
{
|
||||
NormalizedName = OmbiRoles.ManageOwnRequests.ToUpper()
|
||||
});
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
// Make sure we have the API User
|
||||
var apiUserExists = Users.Any(x => x.UserName.Equals("Api", StringComparison.CurrentCultureIgnoreCase));
|
||||
if (!apiUserExists)
|
||||
|
@ -209,7 +172,15 @@ namespace Ombi.Store.Context
|
|||
};
|
||||
break;
|
||||
case NotificationType.ItemAddedToFaultQueue:
|
||||
continue;
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
NotificationType = notificationType,
|
||||
Message = "Hello! The user '{UserName}' has requested {Title} but it could not be added. This has been added into the requests queue and will keep retrying",
|
||||
Subject = "Item Added To Retry Queue",
|
||||
Agent = agent,
|
||||
Enabled = true,
|
||||
};
|
||||
break;
|
||||
case NotificationType.WelcomeEmail:
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
|
|
|
@ -66,5 +66,10 @@ namespace Ombi.Store.Context
|
|||
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
~SettingsContext()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ namespace Ombi.Store.Entities
|
|||
public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue();
|
||||
|
||||
[NotMapped]
|
||||
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
||||
public virtual string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
||||
|
||||
[NotMapped]
|
||||
public bool EmailLogin { get; set; }
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Ombi.Store.Entities.Requests
|
|||
public DateTime MarkedAsDenied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
public RequestType RequestType { get; set; }
|
||||
public string RequestedByAlias { get; set; }
|
||||
|
||||
[ForeignKey(nameof(RequestedUserId))]
|
||||
public OmbiUser RequestedUser { get; set; }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Globalization;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
|
@ -27,9 +28,10 @@ namespace Ombi.Store.Repository.Requests
|
|||
public bool Approved { get; set; }
|
||||
public bool Requested { get; set; }
|
||||
|
||||
|
||||
public int SeasonId { get; set; }
|
||||
[ForeignKey(nameof(SeasonId))]
|
||||
public SeasonRequests Season { get; set; }
|
||||
|
||||
[NotMapped] public string AirDateDisplay => AirDate == DateTime.MinValue ? "Unknown" : AirDate.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
|
@ -17,10 +17,6 @@ namespace Ombi.Store.Entities.Requests
|
|||
public DateTime ReleaseDate { get; set; }
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is so we can correctly send the right amount of seasons to Sonarr
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public int TotalSeasons { get; set; }
|
||||
|
||||
public List<ChildRequests> ChildRequests { get; set; }
|
||||
|
|
1212
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs
generated
Normal file
1212
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
40
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs
Normal file
40
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
public partial class RequestedByAlias : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RequestedByAlias",
|
||||
table: "MovieRequests",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RequestedByAlias",
|
||||
table: "ChildRequests",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RequestedByAlias",
|
||||
table: "AlbumRequests",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequestedByAlias",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequestedByAlias",
|
||||
table: "ChildRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequestedByAlias",
|
||||
table: "AlbumRequests");
|
||||
}
|
||||
}
|
||||
}
|
1212
src/Ombi.Store/Migrations/20190216224539_Roles.Designer.cs
generated
Normal file
1212
src/Ombi.Store/Migrations/20190216224539_Roles.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
32
src/Ombi.Store/Migrations/20190216224539_Roles.cs
Normal file
32
src/Ombi.Store/Migrations/20190216224539_Roles.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
public partial class Roles : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder mb)
|
||||
{
|
||||
// Make sure we have the roles
|
||||
InsertRole(mb, OmbiRoles.ReceivesNewsletter);
|
||||
InsertRole(mb, OmbiRoles.RequestMusic);
|
||||
InsertRole(mb, OmbiRoles.AutoApproveMusic);
|
||||
InsertRole(mb, OmbiRoles.ManageOwnRequests);
|
||||
InsertRole(mb, OmbiRoles.EditCustomPage);
|
||||
}
|
||||
|
||||
private void InsertRole(MigrationBuilder mb, string role)
|
||||
{
|
||||
mb.Sql($@"
|
||||
INSERT INTO AspnetRoles(Id, ConcurrencyStamp, Name, NormalizedName)
|
||||
SELECT '{Guid.NewGuid().ToString()}','{Guid.NewGuid().ToString()}','{role}', '{role.ToUpper()}'
|
||||
WHERE NOT EXISTS(SELECT 1 FROM AspnetRoles WHERE Name = '{role}');");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
1214
src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.Designer.cs
generated
Normal file
1214
src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
public partial class TvRequestsTotalSeasons : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "TotalSeasons",
|
||||
table: "TvRequests",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TotalSeasons",
|
||||
table: "TvRequests");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
|
||||
.HasAnnotation("ProductVersion", "2.2.1-servicing-10028");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
|
@ -583,6 +583,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("RequestedByAlias");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
@ -621,6 +623,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("RequestedByAlias");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
@ -749,6 +753,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("RequestedByAlias");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
@ -813,6 +819,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<int>("TotalSeasons");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
|
|
@ -1,71 +1,71 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Models.Identity;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
//using System;
|
||||
//using Microsoft.AspNetCore.Builder;
|
||||
//using Microsoft.AspNetCore.Hosting;
|
||||
//using Microsoft.AspNetCore.Http;
|
||||
//using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||
//using Microsoft.AspNetCore.Identity;
|
||||
//using Microsoft.Extensions.DependencyInjection;
|
||||
//using Microsoft.Extensions.Options;
|
||||
//using Moq;
|
||||
//using Ombi.Api.Emby;
|
||||
//using Ombi.Api.Plex;
|
||||
//using Ombi.Core.Authentication;
|
||||
//using Ombi.Core.Settings;
|
||||
//using Ombi.Core.Settings.Models.External;
|
||||
//using Ombi.Models.Identity;
|
||||
//using Ombi.Store.Context;
|
||||
//using Ombi.Store.Entities;
|
||||
//using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Tests
|
||||
{
|
||||
public class TestStartup
|
||||
{
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var _plexApi = new Mock<IPlexApi>();
|
||||
var _embyApi = new Mock<IEmbyApi>();
|
||||
var _tokenSettings = new Mock<IOptions<TokenAuthentication>>();
|
||||
var _embySettings = new Mock<ISettingsService<EmbySettings>>();
|
||||
var _plexSettings = new Mock<ISettingsService<PlexSettings>>();
|
||||
var audit = new Mock<IAuditRepository>();
|
||||
var tokenRepo = new Mock<ITokenRepository>();
|
||||
//namespace Ombi.Tests
|
||||
//{
|
||||
// public class TestStartup
|
||||
// {
|
||||
// public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
// {
|
||||
// var _plexApi = new Mock<IPlexApi>();
|
||||
// var _embyApi = new Mock<IEmbyApi>();
|
||||
// var _tokenSettings = new Mock<IOptions<TokenAuthentication>>();
|
||||
// var _embySettings = new Mock<ISettingsService<EmbySettings>>();
|
||||
// var _plexSettings = new Mock<ISettingsService<PlexSettings>>();
|
||||
// var audit = new Mock<IAuditRepository>();
|
||||
// var tokenRepo = new Mock<ITokenRepository>();
|
||||
|
||||
services.AddEntityFrameworkInMemoryDatabase()
|
||||
.AddDbContext<OmbiContext>();
|
||||
services.AddIdentity<OmbiUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<OmbiContext>().AddUserManager<OmbiUserManager>();
|
||||
// services.AddEntityFrameworkInMemoryDatabase()
|
||||
// .AddDbContext<OmbiContext>();
|
||||
// services.AddIdentity<OmbiUser, IdentityRole>()
|
||||
// .AddEntityFrameworkStores<OmbiContext>().AddUserManager<OmbiUserManager>();
|
||||
|
||||
services.AddTransient(x => _plexApi.Object);
|
||||
services.AddTransient(x => _embyApi.Object);
|
||||
services.AddTransient(x => _tokenSettings.Object);
|
||||
services.AddTransient(x => _embySettings.Object);
|
||||
services.AddTransient(x => _plexSettings.Object);
|
||||
services.AddTransient(x => audit.Object);
|
||||
services.AddTransient(x => tokenRepo.Object);
|
||||
// Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified)
|
||||
var context = new DefaultHttpContext();
|
||||
context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());
|
||||
services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context });
|
||||
// services.AddTransient(x => _plexApi.Object);
|
||||
// services.AddTransient(x => _embyApi.Object);
|
||||
// services.AddTransient(x => _tokenSettings.Object);
|
||||
// services.AddTransient(x => _embySettings.Object);
|
||||
// services.AddTransient(x => _plexSettings.Object);
|
||||
// services.AddTransient(x => audit.Object);
|
||||
// services.AddTransient(x => tokenRepo.Object);
|
||||
// // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified)
|
||||
// var context = new DefaultHttpContext();
|
||||
// context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());
|
||||
// services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context });
|
||||
|
||||
|
||||
services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequiredLength = 1;
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.User.AllowedUserNameCharacters = string.Empty;
|
||||
});
|
||||
// services.Configure<IdentityOptions>(options =>
|
||||
// {
|
||||
// options.Password.RequireDigit = false;
|
||||
// options.Password.RequiredLength = 1;
|
||||
// options.Password.RequireLowercase = false;
|
||||
// options.Password.RequireNonAlphanumeric = false;
|
||||
// options.Password.RequireUppercase = false;
|
||||
// options.User.AllowedUserNameCharacters = string.Empty;
|
||||
// });
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
// return services.BuildServiceProvider();
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
// public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
// {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
//}
|
|
@ -1,60 +1,60 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Controllers;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Models.Identity;
|
||||
using Ombi.Notifications;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Models;
|
||||
//using System.Net.Http;
|
||||
//using System.Threading.Tasks;
|
||||
//using AutoMapper;
|
||||
//using Microsoft.AspNetCore.Hosting;
|
||||
//using Microsoft.AspNetCore.Http;
|
||||
//using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||
//using Microsoft.AspNetCore.Identity;
|
||||
//using Microsoft.Extensions.DependencyInjection;
|
||||
//using Microsoft.Extensions.Options;
|
||||
//using Moq;
|
||||
//using NUnit.Framework;
|
||||
//using Ombi.Api.Emby;
|
||||
//using Ombi.Api.Plex;
|
||||
//using Ombi.Controllers;
|
||||
//using Ombi.Core.Authentication;
|
||||
//using Ombi.Core.Settings;
|
||||
//using Ombi.Core.Settings.Models.External;
|
||||
//using Ombi.Models.Identity;
|
||||
//using Ombi.Notifications;
|
||||
//using Ombi.Schedule.Jobs.Ombi;
|
||||
//using Ombi.Settings.Settings.Models;
|
||||
//using Ombi.Settings.Settings.Models.Notifications;
|
||||
//using Ombi.Store.Context;
|
||||
//using Ombi.Store.Entities;
|
||||
//using Ombi.Store.Repository;
|
||||
//using Microsoft.AspNetCore.Hosting.Server;
|
||||
//using Microsoft.AspNetCore.TestHost;
|
||||
//using Newtonsoft.Json;
|
||||
//using Ombi.Models;
|
||||
|
||||
namespace Ombi.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("TODO")]
|
||||
public class TokenControllerTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_testServer = new TestServer(new WebHostBuilder()
|
||||
.UseStartup<TestStartup>());
|
||||
_client = _testServer.CreateClient();
|
||||
}
|
||||
//namespace Ombi.Tests
|
||||
//{
|
||||
// [TestFixture]
|
||||
// [Ignore("TODO")]
|
||||
// public class TokenControllerTests
|
||||
// {
|
||||
// [SetUp]
|
||||
// public void Setup()
|
||||
// {
|
||||
// _testServer = new TestServer(new WebHostBuilder()
|
||||
// .UseStartup<TestStartup>());
|
||||
// _client = _testServer.CreateClient();
|
||||
// }
|
||||
|
||||
private TestServer _testServer;
|
||||
private HttpClient _client;
|
||||
// private TestServer _testServer;
|
||||
// private HttpClient _client;
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task GetToken_FromValid_LocalUser()
|
||||
{
|
||||
var model = new UserAuthModel
|
||||
{
|
||||
Password = "a",
|
||||
Username = "a"
|
||||
};
|
||||
HttpResponseMessage response = await _client.PostAsync("/api/v1/token", new StringContent(JsonConvert.SerializeObject(model)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
// [Test]
|
||||
// public async Task GetToken_FromValid_LocalUser()
|
||||
// {
|
||||
// var model = new UserAuthModel
|
||||
// {
|
||||
// Password = "a",
|
||||
// Username = "a"
|
||||
// };
|
||||
// HttpResponseMessage response = await _client.PostAsync("/api/v1/token", new StringContent(JsonConvert.SerializeObject(model)) );
|
||||
// }
|
||||
// }
|
||||
//}
|
|
@ -19,5 +19,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
Task<FindResult> Find(string externalId, ExternalSource source);
|
||||
Task<TvExternals> GetTvExternals(int theMovieDbId);
|
||||
Task<TvInfo> GetTVInfo(string themoviedbid);
|
||||
Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode);
|
||||
Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode);
|
||||
}
|
||||
}
|
51
src/Ombi.TheMovieDbApi/Models/ActorCredits.cs
Normal file
51
src/Ombi.TheMovieDbApi/Models/ActorCredits.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
namespace Ombi.Api.TheMovieDb.Models
|
||||
{
|
||||
public class ActorCredits
|
||||
{
|
||||
public Cast[] cast { get; set; }
|
||||
public Crew[] crew { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class Cast
|
||||
{
|
||||
public string character { get; set; }
|
||||
public string credit_id { get; set; }
|
||||
public string poster_path { get; set; }
|
||||
public int id { get; set; }
|
||||
public bool video { get; set; }
|
||||
public int vote_count { get; set; }
|
||||
public bool adult { get; set; }
|
||||
public string backdrop_path { get; set; }
|
||||
public int?[] genre_ids { get; set; }
|
||||
public string original_language { get; set; }
|
||||
public string original_title { get; set; }
|
||||
public float popularity { get; set; }
|
||||
public string title { get; set; }
|
||||
public float vote_average { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string release_date { get; set; }
|
||||
}
|
||||
|
||||
public class Crew
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string department { get; set; }
|
||||
public string original_language { get; set; }
|
||||
public string original_title { get; set; }
|
||||
public string job { get; set; }
|
||||
public string overview { get; set; }
|
||||
public int vote_count { get; set; }
|
||||
public bool video { get; set; }
|
||||
public string release_date { get; set; }
|
||||
public float vote_average { get; set; }
|
||||
public string title { get; set; }
|
||||
public float popularity { get; set; }
|
||||
public int?[] genre_ids { get; set; }
|
||||
public string backdrop_path { get; set; }
|
||||
public bool adult { get; set; }
|
||||
public string poster_path { get; set; }
|
||||
public string credit_id { get; set; }
|
||||
}
|
||||
|
||||
}
|
33
src/Ombi.TheMovieDbApi/Models/ActorSearchResult.cs
Normal file
33
src/Ombi.TheMovieDbApi/Models/ActorSearchResult.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
namespace Ombi.Api.TheMovieDb.Models
|
||||
{
|
||||
|
||||
public class ActorResult
|
||||
{
|
||||
public float popularity { get; set; }
|
||||
public int id { get; set; }
|
||||
public string profile_path { get; set; }
|
||||
public string name { get; set; }
|
||||
public Known_For[] known_for { get; set; }
|
||||
public bool adult { get; set; }
|
||||
}
|
||||
|
||||
public class Known_For
|
||||
{
|
||||
public float vote_average { get; set; }
|
||||
public int vote_count { get; set; }
|
||||
public int id { get; set; }
|
||||
public bool video { get; set; }
|
||||
public string media_type { get; set; }
|
||||
public string title { get; set; }
|
||||
public float popularity { get; set; }
|
||||
public string poster_path { get; set; }
|
||||
public string original_language { get; set; }
|
||||
public string original_title { get; set; }
|
||||
public int[] genre_ids { get; set; }
|
||||
public string backdrop_path { get; set; }
|
||||
public bool adult { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string release_date { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -43,6 +43,27 @@ namespace Ombi.Api.TheMovieDb
|
|||
return await Api.Request<FindResult>(request);
|
||||
}
|
||||
|
||||
public async Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode)
|
||||
{
|
||||
var request = new Request($"search/person", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
||||
|
||||
var result = await Api.Request<TheMovieDbContainer<ActorResult>>(request);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode)
|
||||
{
|
||||
var request = new Request($"person/{actorId}/movie_credits", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
||||
|
||||
var result = await Api.Request<ActorCredits>(request);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<List<TvSearchResult>> SearchTv(string searchTerm)
|
||||
{
|
||||
var request = new Request($"search/tv", BaseUri, HttpMethod.Get);
|
||||
|
|
|
@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
||||
namespace Ombi
|
||||
|
@ -98,6 +99,10 @@ namespace Ombi
|
|||
if (context.Request.Headers.Keys.Contains("UserName", StringComparer.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var username = context.Request.Headers["UserName"].FirstOrDefault();
|
||||
if (username.IsNullOrEmpty())
|
||||
{
|
||||
UseApiUser(context);
|
||||
}
|
||||
var um = context.RequestServices.GetService<OmbiUserManager>();
|
||||
var user = await um.Users.FirstOrDefaultAsync(x =>
|
||||
x.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
@ -114,13 +119,18 @@ namespace Ombi
|
|||
}
|
||||
else
|
||||
{
|
||||
var identity = new GenericIdentity("API");
|
||||
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
|
||||
context.User = principal;
|
||||
UseApiUser(context);
|
||||
}
|
||||
|
||||
await next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void UseApiUser(HttpContext context)
|
||||
{
|
||||
var identity = new GenericIdentity("API");
|
||||
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
|
||||
context.User = principal;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import { NavigationStart, Router } from "@angular/router";
|
|||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { AuthService } from "./auth/auth.service";
|
||||
import { ILocalUser } from "./auth/IUserLogin";
|
||||
import { IdentityService, NotificationService } from "./services";
|
||||
import { CustomPageService, IdentityService, NotificationService } from "./services";
|
||||
import { JobService, SettingsService } from "./services";
|
||||
|
||||
import { ICustomizationSettings, ICustomPage } from "./interfaces";
|
||||
|
@ -35,7 +35,8 @@ export class AppComponent implements OnInit {
|
|||
private readonly jobService: JobService,
|
||||
public readonly translate: TranslateService,
|
||||
private readonly identityService: IdentityService,
|
||||
private readonly platformLocation: PlatformLocation) {
|
||||
private readonly platformLocation: PlatformLocation,
|
||||
private readonly customPageService: CustomPageService) {
|
||||
|
||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||
if (base.length > 1) {
|
||||
|
@ -57,7 +58,7 @@ export class AppComponent implements OnInit {
|
|||
this.settingsService.getCustomization().subscribe(x => {
|
||||
this.customizationSettings = x;
|
||||
if(this.customizationSettings.useCustomPage) {
|
||||
this.settingsService.getCustomPage().subscribe(c => {
|
||||
this.customPageService.getCustomPage().subscribe(c => {
|
||||
this.customPageSettings = c;
|
||||
if(!this.customPageSettings.title) {
|
||||
this.customPageSettings.title = "Custom Page";
|
||||
|
|
|
@ -39,7 +39,7 @@ import { ImageService } from "./services";
|
|||
import { LandingPageService } from "./services";
|
||||
import { NotificationService } from "./services";
|
||||
import { SettingsService } from "./services";
|
||||
import { IssuesService, JobService, PlexTvService, StatusService } from "./services";
|
||||
import { CustomPageService, IssuesService, JobService, PlexTvService, StatusService } from "./services";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "*", component: PageNotFoundComponent },
|
||||
|
@ -144,6 +144,7 @@ export function JwtTokenGetter() {
|
|||
JobService,
|
||||
IssuesService,
|
||||
PlexTvService,
|
||||
CustomPageService,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService, SettingsService } from "../services";
|
||||
import { CustomPageService, NotificationService } from "../services";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./custompage.component.html",
|
||||
|
@ -14,7 +14,7 @@ export class CustomPageComponent implements OnInit {
|
|||
public isEditing: boolean;
|
||||
public isAdmin: boolean;
|
||||
|
||||
constructor(private auth: AuthService, private settings: SettingsService, private fb: FormBuilder,
|
||||
constructor(private auth: AuthService, private settings: CustomPageService, private fb: FormBuilder,
|
||||
private notificationService: NotificationService,
|
||||
private sanitizer: DomSanitizer) {
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export class CustomPageComponent implements OnInit {
|
|||
fontAwesomeIcon: [x.fontAwesomeIcon, [Validators.required]],
|
||||
});
|
||||
});
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
this.isAdmin = this.auth.hasRole("EditCustomPage");
|
||||
}
|
||||
|
||||
public onSubmit() {
|
||||
|
|
|
@ -87,6 +87,7 @@ export interface IBaseRequest {
|
|||
requestedUser: IUser;
|
||||
canApprove: boolean;
|
||||
title: string;
|
||||
requestedByAlias: string;
|
||||
}
|
||||
|
||||
export interface ITvRequests {
|
||||
|
@ -145,6 +146,7 @@ export interface IEpisodesRequests {
|
|||
episodeNumber: number;
|
||||
title: string;
|
||||
airDate: Date;
|
||||
airDateDisplay: string;
|
||||
url: string;
|
||||
available: boolean;
|
||||
requested: boolean;
|
||||
|
|
|
@ -41,6 +41,7 @@ export interface IEmbyServer extends IExternalSettings {
|
|||
apiKey: string;
|
||||
administratorId: string;
|
||||
enableEpisodeSearching: boolean;
|
||||
serverHostname: string;
|
||||
}
|
||||
|
||||
export interface IPlexSettings extends ISettings {
|
||||
|
|
|
@ -161,7 +161,8 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
}
|
||||
|
||||
}, err => {
|
||||
this.notify.error(err.statusText);
|
||||
console.log(err);
|
||||
this.notify.error(err.body);
|
||||
|
||||
this.router.navigate(["login"]);
|
||||
});
|
||||
|
|
|
@ -1,36 +1,39 @@
|
|||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" (keyup)="search($event)">
|
||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search"
|
||||
(keyup)="search($event)">
|
||||
<span class="input-group-btn">
|
||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
||||
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||
</button>
|
||||
|
||||
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||
<li>
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }}
|
||||
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
|
||||
translate }}
|
||||
|
||||
</a>
|
||||
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{ 'Requests.SortRequestDateDesc' | translate }}
|
||||
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{
|
||||
'Requests.SortRequestDateDesc' | translate }}
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
@ -58,16 +61,20 @@
|
|||
<div class="col-sm-5 small-padding">
|
||||
<div>
|
||||
<a href="http://www.imdb.com/title/{{request.imdbId}}/" target="_blank">
|
||||
<h4 class="request-title">{{request.title}} ({{request.releaseDate | amLocal | amDateFormat: 'YYYY'}})</h4>
|
||||
<h4 class="request-title">{{request.title}} ({{request.releaseDate | amLocal | amDateFormat:
|
||||
'YYYY'}})</h4>
|
||||
</a>
|
||||
</div>
|
||||
<br />
|
||||
<div class="request-info">
|
||||
<div class="request-by">
|
||||
<span>{{ 'Requests.RequestedBy' | translate }} </span>
|
||||
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
|
||||
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
|
||||
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
|
||||
<span *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
|
||||
<span *ngIf="!request.requestedByAlias">
|
||||
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
|
||||
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
|
||||
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="request-status">
|
||||
<span>{{ 'Requests.Status' | translate }} </span>
|
||||
|
@ -77,13 +84,11 @@
|
|||
<div class="requested-status">
|
||||
<span>{{ 'Requests.RequestStatus' | translate }} </span>
|
||||
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info" [translate]="'Common.ProcessingRequest'"></span>
|
||||
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info"
|
||||
[translate]="'Common.ProcessingRequest'"></span>
|
||||
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
|
||||
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel" class="label label-warning"
|
||||
[translate]="'Common.PendingApproval'"></span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
|
||||
class="label label-warning" [translate]="'Common.PendingApproval'"></span>
|
||||
|
||||
</div>
|
||||
<div *ngIf="request.denied" id="requestDenied">
|
||||
|
@ -93,16 +98,21 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}</div>
|
||||
<div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">{{ 'Requests.DigitalRelease' | translate: {date: request.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }}</div>
|
||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}</div>
|
||||
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate |
|
||||
amLocal | amDateFormat: 'LL'} }}</div>
|
||||
<div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">{{ 'Requests.DigitalRelease' |
|
||||
translate: {date: request.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }}</div>
|
||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
|
||||
| amDateFormat: 'LL'}}</div>
|
||||
<br />
|
||||
</div>
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||
<div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' |
|
||||
translate }}
|
||||
<span>{{request.qualityOverrideTitle}} </span>
|
||||
</div>
|
||||
<div *ngIf="request.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
||||
<div *ngIf="request.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' |
|
||||
translate }}
|
||||
<span>{{request.rootPathOverrideTitle}} </span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -112,10 +122,12 @@
|
|||
<div class="row">
|
||||
<div class="col-md-2 col-md-push-6">
|
||||
|
||||
<a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)" pTooltip="Subscribe for notifications">
|
||||
<a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)"
|
||||
pTooltip="Subscribe for notifications">
|
||||
<i class="fa fa-rss"></i>
|
||||
</a>
|
||||
<a *ngIf="request.showSubscribe && request.subscribed" style="color:red" (click)="unSubscribe(request)" pTooltip="Unsubscribe notification">
|
||||
<a *ngIf="request.showSubscribe && request.subscribed" style="color:red" (click)="unSubscribe(request)"
|
||||
pTooltip="Unsubscribe notification">
|
||||
<i class="fa fa-rss"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -123,7 +135,8 @@
|
|||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="!request.approved" id="approveBtn">
|
||||
<form>
|
||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit">
|
||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve"
|
||||
type="submit">
|
||||
<i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -133,7 +146,8 @@
|
|||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
@ -149,7 +163,8 @@
|
|||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
@ -166,15 +181,15 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<form id="markBtnGroup">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right"
|
||||
value="false" class="btn btn-sm btn-info-outline change">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)"
|
||||
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change">
|
||||
<i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
||||
</button>
|
||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)" style="text-align: right"
|
||||
value="true" class="btn btn-sm btn-success-outline change">
|
||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)"
|
||||
style="text-align: right" value="true" class="btn btn-sm btn-success-outline change">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -190,8 +205,8 @@
|
|||
</form>
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
|
@ -204,8 +219,8 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
@ -216,11 +231,11 @@
|
|||
</div>
|
||||
|
||||
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<p-footer>
|
||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||
<button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||
</p-footer>
|
||||
</p-dialog>
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ export class MovieRequestsComponent implements OnInit {
|
|||
public deny(request: IMovieRequests) {
|
||||
this.requestToDeny = request;
|
||||
this.denyDisplay = true;
|
||||
}
|
||||
}
|
||||
|
||||
public denyRequest() {
|
||||
this.requestService.denyMovie({ id: this.requestToDeny.id, reason: this.rejectionReason })
|
||||
|
@ -144,6 +144,10 @@ export class MovieRequestsComponent implements OnInit {
|
|||
if (x.result) {
|
||||
this.notificationService.success(
|
||||
`Request for ${this.requestToDeny.title} has been denied successfully`);
|
||||
const index = this.movieRequests.indexOf(this.requestToDeny, 0);
|
||||
if (index > -1) {
|
||||
this.movieRequests[index].denied = true;
|
||||
}
|
||||
} else {
|
||||
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
||||
this.requestToDeny.denied = false;
|
||||
|
|
|
@ -1,36 +1,39 @@
|
|||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" (keyup)="search($event)">
|
||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search"
|
||||
(keyup)="search($event)">
|
||||
<span class="input-group-btn">
|
||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
||||
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||
</button>
|
||||
|
||||
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||
<li>
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }}
|
||||
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
|
||||
translate }}
|
||||
|
||||
</a>
|
||||
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{ 'Requests.SortRequestDateDesc' | translate }}
|
||||
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{
|
||||
'Requests.SortRequestDateDesc' | translate }}
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
@ -45,7 +48,7 @@
|
|||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div *ngFor="let request of albumRequests" class="col-md-4">
|
||||
<div *ngFor="let request of albumRequests" class="col-md-4">
|
||||
<div class="row">
|
||||
<div class="album-bg backdrop" [style.background-image]="request.background"></div>
|
||||
<div class="album-tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
|
@ -53,15 +56,15 @@
|
|||
<div class="col-sm-12 small-padding">
|
||||
<img *ngIf="request.disk" class="img-responsive poster album-cover" src="{{request.disk}}" alt="poster">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="col-sm-12 small-padding">
|
||||
<div>
|
||||
<h4>
|
||||
<a href="" target="_blank">
|
||||
{{request.title | truncate: 36}}
|
||||
{{request.title | truncate: 36}}
|
||||
</a>
|
||||
|
||||
|
||||
</h4>
|
||||
<h5>
|
||||
<a href="">
|
||||
|
@ -69,25 +72,29 @@
|
|||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="request-info">
|
||||
<div class="request-by">
|
||||
<span>{{ 'Requests.RequestedBy' | translate }} </span>
|
||||
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
|
||||
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
|
||||
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
|
||||
<span *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
|
||||
<span *ngIf="!request.requestedByAlias">
|
||||
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
|
||||
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
|
||||
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="requested-status">
|
||||
<span>{{ 'Requests.RequestStatus' | translate }} </span>
|
||||
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info" [translate]="'Common.ProcessingRequest'"></span>
|
||||
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info"
|
||||
[translate]="'Common.ProcessingRequest'"></span>
|
||||
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
|
||||
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel" class="label label-warning"
|
||||
[translate]="'Common.PendingApproval'"></span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
|
||||
class="label label-warning" [translate]="'Common.PendingApproval'"></span>
|
||||
|
||||
</div>
|
||||
<div *ngIf="request.denied" id="requestDenied">
|
||||
|
@ -97,8 +104,10 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}</div>
|
||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}</div>
|
||||
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal |
|
||||
amDateFormat: 'LL'} }}</div>
|
||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
|
||||
| amDateFormat: 'LL'}}</div>
|
||||
<br />
|
||||
</div>
|
||||
<!-- <div *ngIf="isAdmin">
|
||||
|
@ -125,8 +134,9 @@
|
|||
</div> -->
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="!request.approved" id="approveBtn">
|
||||
<form class="col-md-6">
|
||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit">
|
||||
<form class="col-md-6">
|
||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve"
|
||||
type="submit">
|
||||
<i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -169,15 +179,15 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<form id="markBtnGroup">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right"
|
||||
value="false" class="btn btn-sm btn-info-outline change">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)"
|
||||
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change">
|
||||
<i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
||||
</button>
|
||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)" style="text-align: right"
|
||||
value="true" class="btn btn-sm btn-success-outline change">
|
||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)"
|
||||
style="text-align: right" value="true" class="btn btn-sm btn-success-outline change">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -193,8 +203,8 @@
|
|||
</form>
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
|
@ -207,8 +217,8 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
@ -272,8 +282,8 @@
|
|||
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<p-footer>
|
||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||
</p-footer>
|
||||
</p-dialog>
|
||||
<p-footer>
|
||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||
<button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||
</p-footer>
|
||||
</p-dialog>
|
|
@ -23,17 +23,11 @@ export class RemainingRequestsComponent implements OnInit {
|
|||
}
|
||||
|
||||
public ngOnInit() {
|
||||
const self = this;
|
||||
|
||||
this.update();
|
||||
|
||||
this.quotaRefreshEvents.subscribe(() => {
|
||||
this.quotaRefreshEvents.subscribe(() => {
|
||||
this.update();
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
self.update();
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
|
@ -43,7 +37,6 @@ export class RemainingRequestsComponent implements OnInit {
|
|||
this.calculateTime();
|
||||
}
|
||||
});
|
||||
|
||||
if (this.movie) {
|
||||
this.requestService.getRemainingMovieRequests().subscribe(callback);
|
||||
}
|
||||
|
|
|
@ -5,31 +5,43 @@
|
|||
|
||||
<div class="col-md-2">
|
||||
<span [translate]="'Requests.RequestedBy'"></span>
|
||||
|
||||
<span *ngIf="!isAdmin">{{child.requestedUser.userName}}</span>
|
||||
<span *ngIf="isAdmin && child.requestedUser.alias">{{child.requestedUser.alias}}</span>
|
||||
<span *ngIf="isAdmin && !child.requestedUser.alias">{{child.requestedUser.userName}}</span>
|
||||
<span *ngIf="child.requestedByAlias">{{child.requestedByAlias}}</span>
|
||||
<span *ngIf="!child.requestedByAlias">
|
||||
<span *ngIf="!isAdmin">{{child.requestedUser.userName}}</span>
|
||||
<span *ngIf="isAdmin && child.requestedUser.alias">{{child.requestedUser.alias}}</span>
|
||||
<span *ngIf="isAdmin && !child.requestedUser.alias">{{child.requestedUser.userName}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 col-md-push-9">
|
||||
<button id="subscribeBtn" *ngIf="child.showSubscribe && !child.subscribed" (click)="subscribe(child)" class="btn btn-sm btn-primary-outline" pTooltip="Subscribe for notifications" type="submit"><i class="fa fa-rss"></i> Subscribe</button>
|
||||
<button id="subscribeBtn" *ngIf="child.showSubscribe && child.subscribed" (click)="unSubscribe(child)" class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i class="fa fa-rss"></i> UnSubscribe</button>
|
||||
|
||||
|
||||
<div *ngIf="isAdmin">
|
||||
<button id="approveBtn" *ngIf="child.canApprove && !child.approved" (click)="approve(child)" class="btn btn-sm btn-success-outline" type="submit"><i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}</button>
|
||||
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}</button>
|
||||
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}</button>
|
||||
<button id="subscribeBtn" *ngIf="child.showSubscribe && !child.subscribed" (click)="subscribe(child)"
|
||||
class="btn btn-sm btn-primary-outline" pTooltip="Subscribe for notifications" type="submit"><i
|
||||
class="fa fa-rss"></i> Subscribe</button>
|
||||
<button id="subscribeBtn" *ngIf="child.showSubscribe && child.subscribed" (click)="unSubscribe(child)"
|
||||
class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i
|
||||
class="fa fa-rss"></i> UnSubscribe</button>
|
||||
|
||||
|
||||
<div *ngIf="isAdmin">
|
||||
<button id="approveBtn" *ngIf="child.canApprove && !child.approved" (click)="approve(child)" class="btn btn-sm btn-success-outline"
|
||||
type="submit"><i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}</button>
|
||||
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)"
|
||||
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i>
|
||||
{{ 'Requests.MarkUnavailable' | translate }}</button>
|
||||
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right"
|
||||
value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> {{
|
||||
'Requests.MarkAvailable' | translate }}</button>
|
||||
|
||||
<button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny">
|
||||
<i class="fa fa-times"></i> {{ 'Requests.Deny' | translate }}</button>
|
||||
|
||||
|
||||
</div>
|
||||
<div *ngIf="isAdmin || isRequestUser(child)">
|
||||
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> {{ 'Requests.Remove' | translate }}</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="isAdmin || isRequestUser(child)">
|
||||
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny"><i
|
||||
class="fa fa-times"></i> {{ 'Requests.Remove' | translate }}</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
|
@ -77,15 +89,19 @@
|
|||
{{ep.airDate | amLocal | amDateFormat: 'L' }}
|
||||
</td>
|
||||
<td>
|
||||
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel" [translate]="'Common.Denied'">
|
||||
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i>
|
||||
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel"
|
||||
[translate]="'Common.Denied'">
|
||||
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i>
|
||||
</span>
|
||||
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info" id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
|
||||
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel"
|
||||
[translate]="'Common.Available'"></span>
|
||||
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info"
|
||||
id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
|
||||
<div *ngIf="!child.denied && !ep.approved">
|
||||
<div *ngIf="!ep.available"><span class="label label-warning" id="pendingApprovalLabel" [translate]="'Common.PendingApproval'"></span></div>
|
||||
<div *ngIf="!ep.available"><span class="label label-warning" id="pendingApprovalLabel"
|
||||
[translate]="'Common.PendingApproval'"></span></div>
|
||||
</div>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -105,8 +121,8 @@
|
|||
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<p-footer>
|
||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||
</p-footer>
|
||||
</p-dialog>
|
||||
<p-footer>
|
||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||
<button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||
</p-footer>
|
||||
</p-dialog>
|
|
@ -27,28 +27,35 @@
|
|||
</div>
|
||||
<!-- Refine search options -->
|
||||
<div class="row top-spacing form-group vcenter" *ngIf="refineSearchEnabled">
|
||||
<div class="col-md-1">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Year</label>
|
||||
|
||||
<input [(ngModel)]="searchYear" class="form-control form-control-custom refine-option">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Year</label>
|
||||
|
||||
<!-- <label for="name" class="col-xs-2 col-md-1">Language:</label> -->
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">Language</label>
|
||||
<div id="profiles">
|
||||
<select [(ngModel)]="selectedLanguage" class="form-control form-control-custom refine-option"
|
||||
id="select">
|
||||
<option *ngFor="let lang of langauges" value="{{lang.code}}">{{lang.nativeName}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input [(ngModel)]="searchYear" class="form-control form-control-custom refine-option">
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
</div>
|
||||
|
||||
<!-- <label for="name" class="col-xs-2 col-md-1">Language:</label> -->
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">Language</label>
|
||||
<div id="profiles">
|
||||
<select [(ngModel)]="selectedLanguage" class="form-control form-control-custom refine-option" id="select">
|
||||
<option *ngFor="let lang of langauges" value="{{lang.code}}">{{lang.nativeName}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="actorSearch" name="actorSearch" [(ngModel)]="actorSearch">
|
||||
<label for="actorSearch" tooltipPosition="top" pTooltip="Search for movies by actor">Actor Search</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<button class="btn pull-right btn-success-outline" (click)="applyRefinedSearch()">Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -70,7 +77,8 @@
|
|||
<div class="myBg backdrop" [style.background-image]="result.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
<div class="col-sm-2 small-padding">
|
||||
<img *ngIf="result.posterPath" class="img-responsive poster" src="{{result.posterPath}}" alt="poster">
|
||||
<img *ngIf="result.posterPath" class="img-responsive poster movie-poster" src="{{result.posterPath}}"
|
||||
alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-8 small-padding">
|
||||
|
|
|
@ -27,6 +27,7 @@ export class MovieSearchComponent implements OnInit {
|
|||
public searchApplied = false;
|
||||
public refineSearchEnabled = false;
|
||||
public searchYear?: number;
|
||||
public actorSearch: boolean;
|
||||
public selectedLanguage: string;
|
||||
public langauges: ILanguageRefine[];
|
||||
|
||||
|
@ -204,7 +205,7 @@ export class MovieSearchComponent implements OnInit {
|
|||
}
|
||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
|
||||
|
||||
|
||||
if (this.applyRefinedSearch) {
|
||||
this.searchService.getMovieInformationWithRefined(val.id, this.selectedLanguage)
|
||||
.subscribe(m => {
|
||||
|
@ -212,9 +213,9 @@ export class MovieSearchComponent implements OnInit {
|
|||
});
|
||||
} else {
|
||||
this.searchService.getMovieInformation(val.id)
|
||||
.subscribe(m => {
|
||||
this.updateItem(val, m);
|
||||
});
|
||||
.subscribe(m => {
|
||||
this.updateItem(val, m);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -239,14 +240,25 @@ export class MovieSearchComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
if (this.refineOpen) {
|
||||
this.searchService.searchMovieWithRefined(this.searchText, this.searchYear, this.selectedLanguage)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some extra info including IMDB Id
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtraInfo();
|
||||
});
|
||||
if (!this.actorSearch) {
|
||||
this.searchService.searchMovieWithRefined(this.searchText, this.searchYear, this.selectedLanguage)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some extra info including IMDB Id
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtraInfo();
|
||||
});
|
||||
} else {
|
||||
this.searchService.searchMovieByActor(this.searchText, this.selectedLanguage)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
// Now let's load some extra info including IMDB Id
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtraInfo();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.searchService.searchMovie(this.searchText)
|
||||
.subscribe(x => {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="myBg backdrop" [style.background-image]="result.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
<div class="col-sm-3 small-padding">
|
||||
<img *ngIf="result.poster" class="img-responsive poster" src="{{result.poster}}" alt="poster">
|
||||
<img *ngIf="result.poster" class="img-responsive poster artist-cover" src="{{result.poster}}" alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-7 small-padding">
|
||||
|
|
|
@ -42,8 +42,11 @@
|
|||
<td>
|
||||
{{ep.title}}
|
||||
</td>
|
||||
<td>
|
||||
<td *ngIf="ep.airDateDisplay != 'Unknown'">
|
||||
{{ep.airDate | amLocal | amDateFormat: 'L' }}
|
||||
</td>
|
||||
<td *ngIf="ep.airDateDisplay == 'Unknown'">
|
||||
{{ep.airDateDisplay }}
|
||||
</td>
|
||||
<td>
|
||||
<ng-template [ngIf]="ep.available"><span class="label label-success" id="availableLabel">Available</span></ng-template>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
<div class="col-sm-2 small-padding">
|
||||
|
||||
<img *ngIf="node.banner" class="img-responsive poster" width="150" [src]="node.banner" alt="poster">
|
||||
<img *ngIf="node.banner" class="img-responsive poster tv-poster" width="150" [src]="node.banner" alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-8 small-padding">
|
||||
|
|
25
src/Ombi/ClientApp/app/services/custompage.service.ts
Normal file
25
src/Ombi/ClientApp/app/services/custompage.service.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import {
|
||||
ICustomPage,
|
||||
} from "../interfaces";
|
||||
|
||||
import { ServiceHelpers } from "./service.helpers";
|
||||
|
||||
@Injectable()
|
||||
export class CustomPageService extends ServiceHelpers {
|
||||
constructor(public http: HttpClient, public platformLocation: PlatformLocation) {
|
||||
super(http, "/api/v1/CustomPage", platformLocation);
|
||||
}
|
||||
|
||||
public getCustomPage(): Observable<ICustomPage> {
|
||||
return this.http.get<ICustomPage>(this.url, {headers: this.headers});
|
||||
}
|
||||
|
||||
public saveCustomPage(model: ICustomPage): Observable<boolean> {
|
||||
return this.http.post<boolean>(this.url, model, {headers: this.headers});
|
||||
}
|
||||
}
|
|
@ -16,3 +16,4 @@ export * from "./notificationMessage.service";
|
|||
export * from "./recentlyAdded.service";
|
||||
export * from "./vote.service";
|
||||
export * from "./requestretry.service";
|
||||
export * from "./custompage.service";
|
||||
|
|
|
@ -15,4 +15,8 @@ export class MobileService extends ServiceHelpers {
|
|||
public getUserDeviceList(): Observable<IMobileUsersViewModel[]> {
|
||||
return this.http.get<IMobileUsersViewModel[]>(`${this.url}notification/`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public deleteUser(userId: string): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}remove/`, userId, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,10 @@ export class SearchService extends ServiceHelpers {
|
|||
return this.http.post<ISearchMovieResult>(`${this.url}/Movie/info`, { theMovieDbId, languageCode: langCode });
|
||||
}
|
||||
|
||||
public searchMovieByActor(searchTerm: string, langCode: string): Observable<ISearchMovieResult[]> {
|
||||
return this.http.post<ISearchMovieResult[]>(`${this.url}/Movie/Actor`, { searchTerm, languageCode: langCode });
|
||||
}
|
||||
|
||||
// TV
|
||||
public searchTv(searchTerm: string): Observable<ISearchTvResult[]> {
|
||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, { headers: this.headers });
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
ICronTestModel,
|
||||
ICronViewModelBody,
|
||||
ICustomizationSettings,
|
||||
ICustomPage,
|
||||
IDiscordNotifcationSettings,
|
||||
IDogNzbSettings,
|
||||
IEmailNotificationSettings,
|
||||
|
@ -113,14 +112,6 @@ export class SettingsService extends ServiceHelpers {
|
|||
return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getCustomPage(): Observable<ICustomPage> {
|
||||
return this.http.get<ICustomPage>(`${this.url}/CustomPage`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public saveCustomPage(model: ICustomPage): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}/CustomPage`, model, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getClientId(): Observable<string> {
|
||||
return this.http.get<string>(`${this.url}/clientid`, {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<span>Discord</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://discord.gg/Sa7wNWb" target="_blank">https://discord.gg/</a>
|
||||
<a href="https://discord.gg/Sa7wNWb" target="_blank">https://discord.gg/Sa7wNWb</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="useCustomPage" name="useCustomPage" [(ngModel)]="settings.useCustomPage">
|
||||
<label for="useCustomPage" tooltipPosition="top" pTooltip="Enabled a custom page where you can fully edit">Use
|
||||
<label for="useCustomPage" tooltipPosition="top" pTooltip="Enabled a custom page where you can fully edit. You will need the Edit Custom Page role.">Use
|
||||
Custom Page</label>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -63,6 +63,18 @@
|
|||
<input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="server.apiKey" placeholder="Emby Api Key" value="{{server.apiKey}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="authToken" class="control-label">Externally Facing Hostname
|
||||
<i class="fa fa-question-circle"
|
||||
pTooltip="This will be the external address that users will naviagte to when they press the 'View On Emby' button"></i>
|
||||
</label>
|
||||
<div>
|
||||
<input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="server.serverHostname" placeholder="e.g. https://jellyfin.server.com/" value="{{server.serverHostname}}">
|
||||
<small><span *ngIf="server.serverHostname">Current URL: "{{server.serverHostname}}/#!/itemdetails.html?id=1"</span>
|
||||
<span *ngIf="!server.serverHostname">Current URL: "https://app.emby.media/#!/itemdetails.html?id=1</span></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="testEmby" type="button" (click)="test(server)" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">User to send test notification to</label>
|
||||
<label for="select" class="control-label">Users</label>
|
||||
<div>
|
||||
<select class="form-control form-control-custom" id="select" [(ngModel)]="testUserId" [ngModelOptions]="{standalone: true}">
|
||||
<option value="">Please select</option>
|
||||
|
@ -46,7 +46,12 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button [disabled]="form.invalid" type="button" (click)="test(form)" class="btn btn-danger-outline">Test</button>
|
||||
<button [disabled]="form.invalid" type="button" (click)="test(form)" class="btn btn-danger-outline">Send Test Notification</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button [disabled]="form.invalid" type="button" (click)="remove(form)" class="btn btn-danger-outline">Remove User</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -79,4 +79,24 @@ export class MobileComponent implements OnInit {
|
|||
});
|
||||
|
||||
}
|
||||
|
||||
public remove() {
|
||||
if (!this.testUserId) {
|
||||
this.notificationService.warning("Warning", "Please select a user to remove");
|
||||
return;
|
||||
}
|
||||
|
||||
this.mobileService.deleteUser(this.testUserId).subscribe(x => {
|
||||
if (x) {
|
||||
this.notificationService.success("Removed users notification");
|
||||
const userToRemove = this.userList.filter(u => {
|
||||
return u.userId === this.testUserId;
|
||||
})[1];
|
||||
this.userList.splice(this.userList.indexOf(userToRemove),1);
|
||||
} else {
|
||||
this.notificationService.error("There was an error when removing the notification. Please check your logs");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
<div class="modal-header">
|
||||
<h3>Add A Friend!</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>You can invite a user to share your Plex Library here. The invited user will be asked to confirm friendship.</p>
|
||||
<p>Please note that this user will not appear in your Ombi Users since they have not accepted the Plex Invite, as soon as they accept
|
||||
the Plex invite then the User Importer job will run (if enabled) and add the user into Ombi.
|
||||
</p>
|
||||
|
||||
|
||||
<div *ngIf="plexServers">
|
||||
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:5%;">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username" class="control-label">Username/Email</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " id="username" name="username" p formControlName="username" [ngClass]="{'form-error': form.get('username').hasError('required')}">
|
||||
<small *ngIf="form.get('username').hasError('required')" class="error-text">The Username/Email is required</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">Select a Server</label>
|
||||
<div id="profiles">
|
||||
<select formControlName="selectedServer" (change)="selected()" class="form-control form-control-custom" id="select" [ngClass]="{'form-error': form.get('selectedServer').hasError('required')}">
|
||||
<option *ngFor="let server of plexServers" value="{{server.machineId}}">{{server.serverName}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<small *ngIf="form.get('selectedServer').hasError('required')" class="error-text">You need to select a server!</small>
|
||||
</div>
|
||||
|
||||
<div *ngIf="plexLibs" class="form-group">
|
||||
<label for="select" class="control-label">Libraries to share</label>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="selectAll" formControlName="allLibsSelected">
|
||||
<label for="selectAll">All</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="!form.value.allLibsSelected">
|
||||
<div *ngFor="let lib of plexLibs">
|
||||
<div class="col-md-4">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="{{lib.id}}" value={{lib.id}} (change)="checkedLib($event.target.checked, $event.target.value)">
|
||||
<label for="{{lib.id}}">{{lib.title}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary-outline" (click)="onSubmit(form)" [disabled]="form.invalid">Add</button>
|
||||
<button type="button" class="btn btn-danger-outline" (click)="activeModal.close('Close click')">Close</button>
|
||||
</div>
|
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