mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 04:49:33 -07:00
Merge branch 'develop' of https://github.com/Ombi-app/Ombi into develop-kuraki
This commit is contained in:
commit
166e2dc34f
38 changed files with 859 additions and 96 deletions
|
@ -40,8 +40,8 @@ Search the existing requests to see if your suggestion has already been submitte
|
||||||
___
|
___
|
||||||
<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://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>
|
||||||
<br>
|
<br>
|
||||||
_**Note:** There is no longer an iOS app due to complications outside of our control._
|
<a href='https://apps.apple.com/us/app/ombi/id1335260043'><img width="130" alt='Get it on the App Store' src='https://developer.apple.com/app-store/marketing/guidelines/images/badge-example-preferred.png'/></a>
|
||||||
|
<br>
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
Here are some of the features Ombi has:
|
Here are some of the features Ombi has:
|
||||||
|
|
|
@ -48,6 +48,8 @@ namespace Ombi.Core.Engine
|
||||||
protected readonly ISettingsService<OmbiSettings> OmbiSettings;
|
protected readonly ISettingsService<OmbiSettings> OmbiSettings;
|
||||||
protected readonly IRepository<RequestSubscription> _subscriptionRepository;
|
protected readonly IRepository<RequestSubscription> _subscriptionRepository;
|
||||||
|
|
||||||
|
private bool _demo = DemoSingleton.Instance.Demo;
|
||||||
|
|
||||||
protected async Task<Dictionary<int, MovieRequests>> GetMovieRequests()
|
protected async Task<Dictionary<int, MovieRequests>> GetMovieRequests()
|
||||||
{
|
{
|
||||||
var now = DateTime.Now.Ticks;
|
var now = DateTime.Now.Ticks;
|
||||||
|
@ -193,6 +195,23 @@ namespace Ombi.Core.Engine
|
||||||
return ombiSettings ?? (ombiSettings = await OmbiSettings.GetSettingsAsync());
|
return ombiSettings ?? (ombiSettings = await OmbiSettings.GetSettingsAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected bool DemoCheck(string title)
|
||||||
|
{
|
||||||
|
if (!title.HasValue())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (_demo)
|
||||||
|
{
|
||||||
|
if (ExcludedDemo.ExcludedContent.Any(x => title.Contains(x, System.Globalization.CompareOptions.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public class HideResult
|
public class HideResult
|
||||||
{
|
{
|
||||||
public bool Hide { get; set; }
|
public bool Hide { get; set; }
|
||||||
|
|
|
@ -26,19 +26,21 @@ using Ombi.Store.Entities.Requests;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using Ombi.Core.Models;
|
using Ombi.Core.Models;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
|
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
|
||||||
{
|
{
|
||||||
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
|
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
|
||||||
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
|
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
|
||||||
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
||||||
IRepository<RequestSubscription> sub) : base(user, requestService, rule, manager, cache, settings, sub)
|
IRepository<RequestSubscription> sub) : base(user, requestService, rule, manager, cache, settings, sub)
|
||||||
{
|
{
|
||||||
TvApi = tvApi;
|
TvApi = tvApi;
|
||||||
MovieDbApi = movApi;
|
MovieDbApi = movApi;
|
||||||
NotificationHelper = helper;
|
NotificationHelper = helper;
|
||||||
|
_logger = logger;
|
||||||
TvSender = sender;
|
TvSender = sender;
|
||||||
_requestLog = rl;
|
_requestLog = rl;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +49,8 @@ namespace Ombi.Core.Engine
|
||||||
private ITvMazeApi TvApi { get; }
|
private ITvMazeApi TvApi { get; }
|
||||||
private IMovieDbApi MovieDbApi { get; }
|
private IMovieDbApi MovieDbApi { get; }
|
||||||
private ITvSender TvSender { get; }
|
private ITvSender TvSender { get; }
|
||||||
|
|
||||||
|
private readonly ILogger<TvRequestEngine> _logger;
|
||||||
private readonly IRepository<RequestLog> _requestLog;
|
private readonly IRepository<RequestLog> _requestLog;
|
||||||
|
|
||||||
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
||||||
|
@ -69,7 +73,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi);
|
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi, _logger);
|
||||||
(await tvBuilder
|
(await tvBuilder
|
||||||
.GetShowInfo(tv.TvDbId))
|
.GetShowInfo(tv.TvDbId))
|
||||||
.CreateTvList(tv)
|
.CreateTvList(tv)
|
||||||
|
|
|
@ -315,6 +315,12 @@ namespace Ombi.Core.Engine.V2
|
||||||
foreach (var movie in movies)
|
foreach (var movie in movies)
|
||||||
{
|
{
|
||||||
var result = await ProcessSingleMovie(movie);
|
var result = await ProcessSingleMovie(movie);
|
||||||
|
|
||||||
|
if (DemoCheck(result.Title))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.HideAvailableFromDiscover && result.Available)
|
if (settings.HideAvailableFromDiscover && result.Available)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -35,6 +35,8 @@ namespace Ombi.Core.Engine.V2
|
||||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||||
private readonly IMusicBrainzApi _musicApi;
|
private readonly IMusicBrainzApi _musicApi;
|
||||||
|
|
||||||
|
private bool _demo = DemoSingleton.Instance.Demo;
|
||||||
|
|
||||||
|
|
||||||
public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm, MultiSearchFilter filter, CancellationToken cancellationToken)
|
public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm, MultiSearchFilter filter, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +62,12 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
foreach (var multiSearch in movieDbData)
|
foreach (var multiSearch in movieDbData)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (DemoCheck(multiSearch.title) || DemoCheck(multiSearch.name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var result = new MultiSearchResult
|
var result = new MultiSearchResult
|
||||||
{
|
{
|
||||||
MediaType = multiSearch.media_type,
|
MediaType = multiSearch.media_type,
|
||||||
|
|
|
@ -155,6 +155,10 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
foreach (var tvMazeSearch in items)
|
foreach (var tvMazeSearch in items)
|
||||||
{
|
{
|
||||||
|
if (DemoCheck(tvMazeSearch.Title))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (settings.HideAvailableFromDiscover)
|
if (settings.HideAvailableFromDiscover)
|
||||||
{
|
{
|
||||||
// To hide, we need to know if it's fully available, the only way to do this is to lookup it's episodes to check if we have every episode
|
// To hide, we need to know if it's fully available, the only way to do this is to lookup it's episodes to check if we have every episode
|
||||||
|
|
|
@ -12,16 +12,19 @@ using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
using Ombi.Store.Repository.Requests;
|
using Ombi.Store.Repository.Requests;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Ombi.Core.Helpers
|
namespace Ombi.Core.Helpers
|
||||||
{
|
{
|
||||||
public class TvShowRequestBuilder
|
public class TvShowRequestBuilder
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public TvShowRequestBuilder(ITvMazeApi tvApi, IMovieDbApi movApi)
|
public TvShowRequestBuilder(ITvMazeApi tvApi, IMovieDbApi movApi, ILogger logger)
|
||||||
{
|
{
|
||||||
TvApi = tvApi;
|
TvApi = tvApi;
|
||||||
MovieDbApi = movApi;
|
MovieDbApi = movApi;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ITvMazeApi TvApi { get; }
|
private ITvMazeApi TvApi { get; }
|
||||||
|
@ -45,6 +48,7 @@ namespace Ombi.Core.Helpers
|
||||||
{
|
{
|
||||||
if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase))
|
if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation($"Found matching MovieDb entry for show name {ShowInfo.name}");
|
||||||
TheMovieDbRecord = result;
|
TheMovieDbRecord = result;
|
||||||
var showIds = await MovieDbApi.GetTvExternals(result.Id);
|
var showIds = await MovieDbApi.GetTvExternals(result.Id);
|
||||||
ShowInfo.externals.imdb = showIds.imdb_id;
|
ShowInfo.externals.imdb = showIds.imdb_id;
|
||||||
|
@ -237,18 +241,19 @@ namespace Ombi.Core.Helpers
|
||||||
|
|
||||||
public TvShowRequestBuilder CreateNewRequest(TvRequestViewModel tv)
|
public TvShowRequestBuilder CreateNewRequest(TvRequestViewModel tv)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation($"Building Request for {ShowInfo.name} with Provider ID {TheMovieDbRecord?.Id ?? 0}");
|
||||||
NewRequest = new TvRequests
|
NewRequest = new TvRequests
|
||||||
{
|
{
|
||||||
Overview = ShowInfo.summary.RemoveHtml(),
|
Overview = ShowInfo.summary.RemoveHtml(),
|
||||||
PosterPath = PosterPath,
|
PosterPath = PosterPath,
|
||||||
Title = ShowInfo.name,
|
Title = ShowInfo.name,
|
||||||
ReleaseDate = FirstAir,
|
ReleaseDate = FirstAir,
|
||||||
ExternalProviderId = TheMovieDbRecord.Id,
|
ExternalProviderId = TheMovieDbRecord?.Id ?? 0,
|
||||||
Status = ShowInfo.status,
|
Status = ShowInfo.status,
|
||||||
ImdbId = ShowInfo.externals?.imdb ?? string.Empty,
|
ImdbId = ShowInfo.externals?.imdb ?? string.Empty,
|
||||||
TvDbId = tv.TvDbId,
|
TvDbId = tv.TvDbId,
|
||||||
ChildRequests = new List<ChildRequests>(),
|
ChildRequests = new List<ChildRequests>(),
|
||||||
TotalSeasons = tv.Seasons.Count(),
|
TotalSeasons = tv.Seasons?.Count ?? 0,
|
||||||
Background = BackdropPath
|
Background = BackdropPath
|
||||||
};
|
};
|
||||||
NewRequest.ChildRequests.Add(ChildRequest);
|
NewRequest.ChildRequests.Add(ChildRequest);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Ombi.Helpers
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Helpers
|
||||||
{
|
{
|
||||||
public class DemoSingleton
|
public class DemoSingleton
|
||||||
{
|
{
|
||||||
|
@ -10,4 +12,460 @@
|
||||||
|
|
||||||
public bool Demo { get; set; }
|
public bool Demo { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ExcludedDemo
|
||||||
|
{
|
||||||
|
public static HashSet<string> ExcludedContent => new HashSet<string>
|
||||||
|
{
|
||||||
|
"101 Dalmatians",
|
||||||
|
"102 Dalmatians",
|
||||||
|
"20,000 Leagues Under the Sea",
|
||||||
|
"A Bug's Life",
|
||||||
|
"A Far Off Place",
|
||||||
|
"A Goofy Movie",
|
||||||
|
"A Kid in King Arthur's Court",
|
||||||
|
"A Tale of Two Critters",
|
||||||
|
"A Tiger Walks",
|
||||||
|
"A Wrinkle in Time",
|
||||||
|
"ABCD 2",
|
||||||
|
"African Cats",
|
||||||
|
"Air Bud",
|
||||||
|
"Air Bud: Golden Receiver",
|
||||||
|
"Aladdin",
|
||||||
|
"Aladdin",
|
||||||
|
"Alexander and the Terrible, Horrible, No Good, Very Bad Day",
|
||||||
|
"Alice Through the Looking Glass",
|
||||||
|
"Alice in Wonderland",
|
||||||
|
"Alice in Wonderland",
|
||||||
|
"Aliens of the Deep",
|
||||||
|
"Almost Angels",
|
||||||
|
"America's Heart and Soul",
|
||||||
|
"Amy",
|
||||||
|
"Anaganaga O Dheerudu",
|
||||||
|
"Angels in the Outfield",
|
||||||
|
"Arjun: The Warrior Prince",
|
||||||
|
"Around the World in 80 Days",
|
||||||
|
"Artemis Fowl",
|
||||||
|
"Atlantis: The Lost Empire",
|
||||||
|
"Babes in Toyland",
|
||||||
|
"Bambi",
|
||||||
|
"Bears",
|
||||||
|
"Beauty and the Beast",
|
||||||
|
"Beauty and the Beast",
|
||||||
|
"Bedknobs and Broomsticks",
|
||||||
|
"Bedtime Stories",
|
||||||
|
"Benji the Hunted",
|
||||||
|
"Beverly Hills Chihuahua",
|
||||||
|
"Big Hero 6",
|
||||||
|
"Big Red",
|
||||||
|
"Blackbeard's Ghost",
|
||||||
|
"Blank Check",
|
||||||
|
"Blue",
|
||||||
|
"Bolt",
|
||||||
|
"Bon Voyage!",
|
||||||
|
"Born in China",
|
||||||
|
"Brave",
|
||||||
|
"Bridge to Terabithia",
|
||||||
|
"Brother Bear",
|
||||||
|
"Candleshoe",
|
||||||
|
"Cars",
|
||||||
|
"Cars 2",
|
||||||
|
"Cars 3",
|
||||||
|
"Charley and the Angel",
|
||||||
|
"Charlie, the Lonesome Cougar",
|
||||||
|
"Cheetah",
|
||||||
|
"Chicken Little",
|
||||||
|
"Chimpanzee",
|
||||||
|
"Christopher Robin",
|
||||||
|
"Cinderella",
|
||||||
|
"Cinderella",
|
||||||
|
"Coco",
|
||||||
|
"College Road Trip",
|
||||||
|
"Condorman",
|
||||||
|
"Confessions of a Teenage Drama Queen",
|
||||||
|
"Cool Runnings",
|
||||||
|
"D2: The Mighty Ducks",
|
||||||
|
"D3: The Mighty Ducks",
|
||||||
|
"Dangal",
|
||||||
|
"Darby O'Gill and the Little People",
|
||||||
|
"Dasavathaaram",
|
||||||
|
"Davy Crockett and the River Pirates",
|
||||||
|
"Davy Crockett, King of the Wild Frontier",
|
||||||
|
"Dinosaur",
|
||||||
|
"Disney's A Christmas Carol",
|
||||||
|
"Disney's The Kid",
|
||||||
|
"Do Dooni Chaar",
|
||||||
|
"Dolphin Reef",
|
||||||
|
"Doug's 1st Movie",
|
||||||
|
"Dragonslayer",
|
||||||
|
"DuckTales the Movie: Treasure of the Lost Lamp",
|
||||||
|
"Dumbo",
|
||||||
|
"Dumbo",
|
||||||
|
"Earth",
|
||||||
|
"Eight Below",
|
||||||
|
"Emil and the Detectives",
|
||||||
|
"Enchanted",
|
||||||
|
"Endurance",
|
||||||
|
"Escape to Witch Mountain",
|
||||||
|
"Expedition China",
|
||||||
|
"Fantasia",
|
||||||
|
"Fantasia 2000",
|
||||||
|
"Finding Dory",
|
||||||
|
"Finding Nemo",
|
||||||
|
"First Kid",
|
||||||
|
"Flight of the Navigator",
|
||||||
|
"Flubber",
|
||||||
|
"Follow Me, Boys!",
|
||||||
|
"Frank and Ollie",
|
||||||
|
"Frankenweenie",
|
||||||
|
"Freaky Friday",
|
||||||
|
"Freaky Friday",
|
||||||
|
"Frozen",
|
||||||
|
"Frozen II",
|
||||||
|
"Onward",
|
||||||
|
"Star Wars",
|
||||||
|
"Raya",
|
||||||
|
"Mandalorian",
|
||||||
|
"Fun and Fancy Free",
|
||||||
|
"G-Force",
|
||||||
|
"George of the Jungle",
|
||||||
|
"Ghost in the Shell 2: Innocence GITS2",
|
||||||
|
"Ghost of the Mountains",
|
||||||
|
"Ghosts of the Abyss",
|
||||||
|
"Glory Road",
|
||||||
|
"Greyfriars Bobby",
|
||||||
|
"Growing Up Wild",
|
||||||
|
"Gus",
|
||||||
|
"Hannah Montana and Miley Cyrus: Best of Both Worlds Concert",
|
||||||
|
"Hannah Montana: The Movie",
|
||||||
|
"Heavyweights",
|
||||||
|
"Herbie Goes Bananas",
|
||||||
|
"Herbie Goes to Monte Carlo",
|
||||||
|
"Herbie Rides Again",
|
||||||
|
"Herbie: Fully Loaded",
|
||||||
|
"Hercules",
|
||||||
|
"High School Musical 3: Senior Year",
|
||||||
|
"Hocus Pocus",
|
||||||
|
"Holes",
|
||||||
|
"Home on the Range",
|
||||||
|
"Homeward Bound II: Lost in San Francisco",
|
||||||
|
"Homeward Bound: The Incredible Journey",
|
||||||
|
"Honey, I Blew Up the Kid",
|
||||||
|
"Honey, I Shrunk the Kids",
|
||||||
|
"Hot Lead and Cold Feet",
|
||||||
|
"I'll Be Home for Christmas",
|
||||||
|
"Ice Princess",
|
||||||
|
"In Search of the Castaways",
|
||||||
|
"Incredibles 2",
|
||||||
|
"Inside Out",
|
||||||
|
"Inspector Gadget",
|
||||||
|
"Into the Woods",
|
||||||
|
"Invincible",
|
||||||
|
"Iron Will",
|
||||||
|
"Jagga Jasoos",
|
||||||
|
"James and the Giant Peach",
|
||||||
|
"John Carter",
|
||||||
|
"Johnny Tremain",
|
||||||
|
"Jonas Brothers: The 3D Concert Experience",
|
||||||
|
"Jungle 2 Jungle",
|
||||||
|
"Jungle Cat",
|
||||||
|
"Khoobsurat",
|
||||||
|
"Kidnapped",
|
||||||
|
"King of the Grizzlies",
|
||||||
|
"L'Empereur - March of the Penguins 2: The Next Step[a]",
|
||||||
|
"Lady and the Tramp",
|
||||||
|
"Lady and the Tramp",
|
||||||
|
"Lilly the Witch: The Dragon and the Magic Book",
|
||||||
|
"Lilly the Witch: The Journey to Mandolan",
|
||||||
|
"Lilo & Stitch",
|
||||||
|
"Lt. Robin Crusoe, U.S.N.",
|
||||||
|
"Make Mine Music",
|
||||||
|
"Maleficent",
|
||||||
|
"Maleficent: Mistress of Evil",
|
||||||
|
"Man of the House",
|
||||||
|
"Mars Needs Moms",
|
||||||
|
"Mary Poppins",
|
||||||
|
"Mary Poppins Returns",
|
||||||
|
"Max Keeble's Big Move",
|
||||||
|
"McFarland, USA",
|
||||||
|
"Meet the Deedles",
|
||||||
|
"Meet the Robinsons",
|
||||||
|
"Melody Time",
|
||||||
|
"Midnight Madness",
|
||||||
|
"Mighty Joe Young",
|
||||||
|
"Million Dollar Arm",
|
||||||
|
"Miracle",
|
||||||
|
"Miracle of the White Stallions",
|
||||||
|
"Moana",
|
||||||
|
"Monkey Kingdom",
|
||||||
|
"Monkeys, Go Home!",
|
||||||
|
"Monsters University",
|
||||||
|
"Monsters, Inc.",
|
||||||
|
"Moon Pilot",
|
||||||
|
"Morning Light",
|
||||||
|
"Mr. Magoo",
|
||||||
|
"Mulan",
|
||||||
|
"Muppet Treasure Island",
|
||||||
|
"Muppets Most Wanted",
|
||||||
|
"My Favorite Martian",
|
||||||
|
"Napoleon and Samantha",
|
||||||
|
"National Treasure",
|
||||||
|
"National Treasure: Book of Secrets",
|
||||||
|
"Never Cry Wolf",
|
||||||
|
"Never a Dull Moment",
|
||||||
|
"Newsies",
|
||||||
|
"Night Crossing",
|
||||||
|
"Nikki, Wild Dog of the North",
|
||||||
|
"No Deposit, No Return",
|
||||||
|
"Now You See Him, Now You Don't",
|
||||||
|
"Oceans",
|
||||||
|
"Old Dogs",
|
||||||
|
"Old Yeller",
|
||||||
|
"Oliver & Company",
|
||||||
|
"One Hundred and One Dalmatians",
|
||||||
|
"One Little Indian",
|
||||||
|
"One Magic Christmas",
|
||||||
|
"One of Our Dinosaurs Is Missing",
|
||||||
|
"Operation Dumbo Drop",
|
||||||
|
"Oz the Great and Powerful",
|
||||||
|
"Penguins",
|
||||||
|
"Perri",
|
||||||
|
"Pete's Dragon",
|
||||||
|
"Pete's Dragon",
|
||||||
|
"Peter Pan",
|
||||||
|
"Piglet's Big Movie",
|
||||||
|
"Pinocchio",
|
||||||
|
"Pirates of the Caribbean: At World's End",
|
||||||
|
"Pirates of the Caribbean: Dead Man's Chest",
|
||||||
|
"Pirates of the Caribbean: Dead Men Tell No Tales",
|
||||||
|
"Pirates of the Caribbean: On Stranger Tides",
|
||||||
|
"Pirates of the Caribbean: The Curse of the Black Pearl",
|
||||||
|
"Planes",
|
||||||
|
"Planes: Fire & Rescue",
|
||||||
|
"Pocahontas",
|
||||||
|
"Pollyanna",
|
||||||
|
"Pooh's Heffalump Movie",
|
||||||
|
"Popeye",
|
||||||
|
"Prince of Persia: The Sands of Time",
|
||||||
|
"Prom",
|
||||||
|
"Queen of Katwe",
|
||||||
|
"Race to Witch Mountain",
|
||||||
|
"Ralph Breaks the Internet",
|
||||||
|
"Rascal",
|
||||||
|
"Ratatouille",
|
||||||
|
"Recess: School's Out",
|
||||||
|
"Remember the Titans",
|
||||||
|
"Return from Witch Mountain",
|
||||||
|
"Return to Never Land",
|
||||||
|
"Return to Oz",
|
||||||
|
"Return to Snowy River",
|
||||||
|
"Ride a Wild Pony",
|
||||||
|
"Roadside Romeo",
|
||||||
|
"Rob Roy, the Highland Rogue",
|
||||||
|
"Robin Hood",
|
||||||
|
"RocketMan",
|
||||||
|
"Roving Mars",
|
||||||
|
"Run, Cougar, Run",
|
||||||
|
"Sacred Planet",
|
||||||
|
"Saludos Amigos",
|
||||||
|
"Savage Sam",
|
||||||
|
"Saving Mr. Banks",
|
||||||
|
"Scandalous John",
|
||||||
|
"Secretariat",
|
||||||
|
"Secrets of Life",
|
||||||
|
"Shipwrecked",
|
||||||
|
"Sky High",
|
||||||
|
"Sleeping Beauty",
|
||||||
|
"Smith!",
|
||||||
|
"Snow Dogs",
|
||||||
|
"Snow White and the Seven Dwarfs",
|
||||||
|
"Snowball Express",
|
||||||
|
"So Dear to My Heart",
|
||||||
|
"Something Wicked This Way Comes",
|
||||||
|
"Son of Flubber",
|
||||||
|
"Song of the South",
|
||||||
|
"Squanto: A Warrior's Tale",
|
||||||
|
"Summer Magic",
|
||||||
|
"Superdad",
|
||||||
|
"Swiss Family Robinson",
|
||||||
|
"Tall Tale",
|
||||||
|
"Tangled",
|
||||||
|
"Tarzan",
|
||||||
|
"Teacher's Pet",
|
||||||
|
"Ten Who Dared",
|
||||||
|
"Tex",
|
||||||
|
"That Darn Cat",
|
||||||
|
"That Darn Cat!",
|
||||||
|
"The Absent-Minded Professor",
|
||||||
|
"The Adventures of Bullwhip Griffin",
|
||||||
|
"The Adventures of Huck Finn",
|
||||||
|
"The Adventures of Ichabod and Mr. Toad",
|
||||||
|
"The African Lion",
|
||||||
|
"The Apple Dumpling Gang",
|
||||||
|
"The Apple Dumpling Gang Rides Again",
|
||||||
|
"The Aristocats",
|
||||||
|
"The BFG",
|
||||||
|
"The Barefoot Executive",
|
||||||
|
"The Bears and I",
|
||||||
|
"The Best of Walt Disney's True-Life Adventures",
|
||||||
|
"The Big Green",
|
||||||
|
"The Biscuit Eater",
|
||||||
|
"The Black Cauldron",
|
||||||
|
"The Black Hole",
|
||||||
|
"The Boatniks",
|
||||||
|
"The Book of Masters",
|
||||||
|
"The Boys: The Sherman Brothers' Story",
|
||||||
|
"The Castaway Cowboy",
|
||||||
|
"The Cat from Outer Space",
|
||||||
|
"The Chronicles of Narnia: Prince Caspian",
|
||||||
|
"The Chronicles of Narnia: The Lion, the Witch and the Wardrobe",
|
||||||
|
"The Computer Wore Tennis Shoes",
|
||||||
|
"The Country Bears",
|
||||||
|
"The Crimson Wing: Mystery of the Flamingos",
|
||||||
|
"The Devil and Max Devlin",
|
||||||
|
"The Emperor's New Groove",
|
||||||
|
"The Fighting Prince of Donegal",
|
||||||
|
"The Finest Hours",
|
||||||
|
"The Fox and the Hound",
|
||||||
|
"The Game Plan",
|
||||||
|
"The Gnome-Mobile",
|
||||||
|
"The Good Dinosaur",
|
||||||
|
"The Great Locomotive Chase",
|
||||||
|
"The Great Mouse Detective",
|
||||||
|
"The Greatest Game Ever Played",
|
||||||
|
"The Happiest Millionaire",
|
||||||
|
"The Haunted Mansion",
|
||||||
|
"The Horse in the Gray Flannel Suit",
|
||||||
|
"The Hunchback of Notre Dame",
|
||||||
|
"The Incredible Journey",
|
||||||
|
"The Incredibles",
|
||||||
|
"The Island at the Top of the World",
|
||||||
|
"The Journey of Natty Gann",
|
||||||
|
"The Jungle Book",
|
||||||
|
"The Jungle Book",
|
||||||
|
"The Jungle Book",
|
||||||
|
"The Jungle Book 2",
|
||||||
|
"The Last Flight of Noah's Ark",
|
||||||
|
"The Legend of Lobo",
|
||||||
|
"The Light in the Forest",
|
||||||
|
"The Lion King",
|
||||||
|
"The Lion King",
|
||||||
|
"The Little Mermaid",
|
||||||
|
"The Littlest Horse Thieves",
|
||||||
|
"The Littlest Outlaw",
|
||||||
|
"The Living Desert",
|
||||||
|
"The Lizzie McGuire Movie",
|
||||||
|
"The London Connection",
|
||||||
|
"The Lone Ranger",
|
||||||
|
"The Love Bug",
|
||||||
|
"The Many Adventures of Winnie the Pooh",
|
||||||
|
"The Mighty Ducks",
|
||||||
|
"The Million Dollar Duck",
|
||||||
|
"The Misadventures of Merlin Jones",
|
||||||
|
"The Monkey's Uncle",
|
||||||
|
"The Moon-Spinners",
|
||||||
|
"The Muppet Christmas Carol",
|
||||||
|
"The Muppets",
|
||||||
|
"The Nightmare Before Christmas 3D TNBC",
|
||||||
|
"The North Avenue Irregulars",
|
||||||
|
"The Nutcracker and the Four Realms",
|
||||||
|
"The Odd Life of Timothy Green",
|
||||||
|
"The One and Only, Genuine, Original Family Band",
|
||||||
|
"The Pacifier",
|
||||||
|
"The Parent Trap",
|
||||||
|
"The Parent Trap",
|
||||||
|
"The Pixar Story",
|
||||||
|
"The Princess Diaries",
|
||||||
|
"The Princess Diaries 2: Royal Engagement",
|
||||||
|
"The Princess and the Frog",
|
||||||
|
"The Reluctant Dragon",
|
||||||
|
"The Rescuers",
|
||||||
|
"The Rescuers Down Under",
|
||||||
|
"The Rocketeer TR",
|
||||||
|
"The Rookie",
|
||||||
|
"The Santa Clause 2",
|
||||||
|
"The Santa Clause 3: The Escape Clause",
|
||||||
|
"The Santa Clause TSC",
|
||||||
|
"The Shaggy D.A.",
|
||||||
|
"The Shaggy Dog",
|
||||||
|
"The Shaggy Dog",
|
||||||
|
"The Sign of Zorro",
|
||||||
|
"The Sorcerer's Apprentice",
|
||||||
|
"The Story of Robin Hood and His Merrie Men",
|
||||||
|
"The Straight Story",
|
||||||
|
"The Strongest Man in the World",
|
||||||
|
"The Sword and the Rose",
|
||||||
|
"The Sword in the Stone",
|
||||||
|
"The Three Caballeros",
|
||||||
|
"The Three Lives of Thomasina",
|
||||||
|
"The Three Musketeers",
|
||||||
|
"The Tigger Movie",
|
||||||
|
"The Ugly Dachshund",
|
||||||
|
"The Vanishing Prairie",
|
||||||
|
"The Watcher in the Woods",
|
||||||
|
"The Wild",
|
||||||
|
"The Wild Country",
|
||||||
|
"The World's Greatest Athlete",
|
||||||
|
"The Young Black Stallion",
|
||||||
|
"Third Man on the Mountain",
|
||||||
|
"Those Calloways",
|
||||||
|
"Toby Tyler",
|
||||||
|
"Tom and Huck",
|
||||||
|
"Tomorrowland",
|
||||||
|
"Tonka",
|
||||||
|
"Toy Story",
|
||||||
|
"Toy Story 2",
|
||||||
|
"Toy Story 3",
|
||||||
|
"Toy Story 4",
|
||||||
|
"Trail of the Panda",
|
||||||
|
"Treasure Island",
|
||||||
|
"Treasure Planet",
|
||||||
|
"Treasure of Matecumbe",
|
||||||
|
"Trenchcoat",
|
||||||
|
"Tron",
|
||||||
|
"Tron: Legacy",
|
||||||
|
"Tuck Everlasting",
|
||||||
|
"Underdog",
|
||||||
|
"Unidentified Flying Oddball",
|
||||||
|
"Up",
|
||||||
|
"Valiant",
|
||||||
|
"Victory Through Air Power",
|
||||||
|
"WALL-E",
|
||||||
|
"Waking Sleeping Beauty",
|
||||||
|
"Walt & El Grupo",
|
||||||
|
"Westward Ho the Wagons!",
|
||||||
|
"White Fang",
|
||||||
|
"White Fang 2: Myth of the White Wolf",
|
||||||
|
"White Wilderness",
|
||||||
|
"Wild Hearts Can't Be Broken",
|
||||||
|
"Wings of Life",
|
||||||
|
"Winnie the Pooh",
|
||||||
|
"Wreck-It Ralph",
|
||||||
|
"Zokkomon",
|
||||||
|
"Zootopia",
|
||||||
|
"Zorro the Avenger",
|
||||||
|
"Iron Man",
|
||||||
|
"Hulk",
|
||||||
|
"Thor",
|
||||||
|
"Avengers",
|
||||||
|
"Guardians of the Galaxy",
|
||||||
|
"Ant-Man",
|
||||||
|
"Captain America",
|
||||||
|
"Doctor Strange",
|
||||||
|
"Guardians of the Galaxy",
|
||||||
|
"Spider-Man",
|
||||||
|
"Black Panther",
|
||||||
|
"Marvel",
|
||||||
|
"Spider Man",
|
||||||
|
"SpiderMan",
|
||||||
|
"Loki",
|
||||||
|
"Winter Soldier",
|
||||||
|
"Wanda",
|
||||||
|
"Small Fry",
|
||||||
|
"Rex",
|
||||||
|
"Lamp life",
|
||||||
|
"Toy",
|
||||||
|
"Hawaiian"
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,5 +7,9 @@ namespace Ombi.Core.Settings.Models.External
|
||||||
public bool ShowAdultMovies { get; set; }
|
public bool ShowAdultMovies { get; set; }
|
||||||
|
|
||||||
public List<int> ExcludedKeywordIds { get; set; }
|
public List<int> ExcludedKeywordIds { get; set; }
|
||||||
|
|
||||||
|
public List<int> ExcludedMovieGenreIds { get; set; }
|
||||||
|
|
||||||
|
public List<int> ExcludedTvGenreIds { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ using System.Threading.Tasks;
|
||||||
using Ombi.Api.TheMovieDb.Models;
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.TheMovieDbApi.Models;
|
using Ombi.TheMovieDbApi.Models;
|
||||||
|
|
||||||
|
// Due to conflicting Genre models in
|
||||||
|
// Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models
|
||||||
|
using Genre = Ombi.TheMovieDbApi.Models.Genre;
|
||||||
|
|
||||||
namespace Ombi.Api.TheMovieDb
|
namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
public interface IMovieDbApi
|
public interface IMovieDbApi
|
||||||
|
@ -34,5 +38,6 @@ namespace Ombi.Api.TheMovieDb
|
||||||
Task<Keyword> GetKeyword(int keywordId);
|
Task<Keyword> GetKeyword(int keywordId);
|
||||||
Task<WatchProviders> GetMovieWatchProviders(int theMoviedbId, CancellationToken token);
|
Task<WatchProviders> GetMovieWatchProviders(int theMoviedbId, CancellationToken token);
|
||||||
Task<WatchProviders> GetTvWatchProviders(int theMoviedbId, CancellationToken token);
|
Task<WatchProviders> GetTvWatchProviders(int theMoviedbId, CancellationToken token);
|
||||||
|
Task<List<Genre>> GetGenres(string media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,9 @@ namespace Ombi.TheMovieDbApi.Models
|
||||||
public int total_results { get; set; }
|
public int total_results { get; set; }
|
||||||
public int total_pages { get; set; }
|
public int total_pages { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public class GenreContainer<T>
|
||||||
|
{
|
||||||
|
public List<T> genres { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,10 @@ using Ombi.Core.Settings.Models.External;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.TheMovieDbApi.Models;
|
using Ombi.TheMovieDbApi.Models;
|
||||||
|
|
||||||
|
// Due to conflicting Genre models in
|
||||||
|
// Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models
|
||||||
|
using Genre = Ombi.TheMovieDbApi.Models.Genre;
|
||||||
|
|
||||||
namespace Ombi.Api.TheMovieDb
|
namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
public class TheMovieDbApi : IMovieDbApi
|
public class TheMovieDbApi : IMovieDbApi
|
||||||
|
@ -198,6 +202,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
request.AddQueryString("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
await AddDiscoverSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
|
await AddGenreFilter(request, type);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
||||||
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
@ -233,6 +238,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
request.AddQueryString("vote_count.gte", "250");
|
request.AddQueryString("vote_count.gte", "250");
|
||||||
|
|
||||||
await AddDiscoverSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
|
await AddGenreFilter(request, type);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
@ -269,6 +275,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
request.AddQueryString("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
await AddDiscoverSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
|
await AddGenreFilter(request, type);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
@ -297,6 +304,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
}
|
}
|
||||||
|
|
||||||
await AddDiscoverSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
|
await AddGenreFilter(request, "movie");
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
@ -344,6 +352,16 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return keyword == null || keyword.Id == 0 ? null : keyword;
|
return keyword == null || keyword.Id == 0 ? null : keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<Genre>> GetGenres(string media)
|
||||||
|
{
|
||||||
|
var request = new Request($"genre/{media}/list", BaseUri, HttpMethod.Get);
|
||||||
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
AddRetry(request);
|
||||||
|
|
||||||
|
var result = await Api.Request<GenreContainer<Genre>>(request);
|
||||||
|
return result.genres ?? new List<Genre>();
|
||||||
|
}
|
||||||
|
|
||||||
public Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken)
|
public Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var request = new Request("search/multi", BaseUri, HttpMethod.Get);
|
var request = new Request("search/multi", BaseUri, HttpMethod.Get);
|
||||||
|
@ -380,6 +398,28 @@ namespace Ombi.Api.TheMovieDb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task AddGenreFilter(Request request, string media_type)
|
||||||
|
{
|
||||||
|
var settings = await Settings;
|
||||||
|
List<int> excludedGenres;
|
||||||
|
|
||||||
|
switch (media_type) {
|
||||||
|
case "tv":
|
||||||
|
excludedGenres = settings.ExcludedTvGenreIds;
|
||||||
|
break;
|
||||||
|
case "movie":
|
||||||
|
excludedGenres = settings.ExcludedMovieGenreIds;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludedGenres?.Any() == true)
|
||||||
|
{
|
||||||
|
request.AddQueryString("without_genres", string.Join(",", excludedGenres));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddRetry(Request request)
|
private static void AddRetry(Request request)
|
||||||
{
|
{
|
||||||
request.Retry = true;
|
request.Retry = true;
|
||||||
|
|
|
@ -170,7 +170,17 @@
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<app-my-nav id="main-container dark" [showNav]="showNav" [isAdmin]="isAdmin" [applicationName]="applicationName" [applicationLogo]="customizationSettings?.logo" [username]="username" [email]="user?.email " (logoutClick)="logOut();">
|
<app-my-nav id="main-container dark"
|
||||||
|
[showNav]="showNav"
|
||||||
|
[isAdmin]="isAdmin"
|
||||||
|
[applicationName]="applicationName"
|
||||||
|
[applicationLogo]="customizationSettings?.logo"
|
||||||
|
[username]="username"
|
||||||
|
[email]="user?.email"
|
||||||
|
[accessToken]="accessToken"
|
||||||
|
[applicationUrl]="customizationSettings?.applicationUrl"
|
||||||
|
(logoutClick)="logOut();"
|
||||||
|
>
|
||||||
</app-my-nav>
|
</app-my-nav>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ export class AppComponent implements OnInit {
|
||||||
public applicationName: string = "Ombi"
|
public applicationName: string = "Ombi"
|
||||||
public isAdmin: boolean;
|
public isAdmin: boolean;
|
||||||
public username: string;
|
public username: string;
|
||||||
|
public accessToken: string;
|
||||||
|
|
||||||
private hubConnected: boolean;
|
private hubConnected: boolean;
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ export class AppComponent implements OnInit {
|
||||||
if (this.authService.loggedIn()) {
|
if (this.authService.loggedIn()) {
|
||||||
this.user = this.authService.claims();
|
this.user = this.authService.claims();
|
||||||
this.username = this.user.name;
|
this.username = this.user.name;
|
||||||
|
this.identity.getAccessToken().subscribe(x => this.accessToken = x);
|
||||||
if (!this.hubConnected) {
|
if (!this.hubConnected) {
|
||||||
this.signalrNotification.initialize();
|
this.signalrNotification.initialize();
|
||||||
this.hubConnected = true;
|
this.hubConnected = true;
|
||||||
|
|
|
@ -284,6 +284,8 @@ export interface IVoteSettings extends ISettings {
|
||||||
export interface ITheMovieDbSettings extends ISettings {
|
export interface ITheMovieDbSettings extends ISettings {
|
||||||
showAdultMovies: boolean;
|
showAdultMovies: boolean;
|
||||||
excludedKeywordIds: number[];
|
excludedKeywordIds: number[];
|
||||||
|
excludedMovieGenreIds: number[];
|
||||||
|
excludedTvGenreIds: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUpdateModel
|
export interface IUpdateModel
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #notRequestedBtn>
|
<ng-template #notRequestedBtn>
|
||||||
<button id="requestBtn" mat-raised-button class="btn-spacing" color="primary" (click)="request()">
|
<button *ngIf="!movie.requested" id="requestBtn" mat-raised-button class="btn-spacing" color="primary" (click)="request()">
|
||||||
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
||||||
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
||||||
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
||||||
|
|
|
@ -69,10 +69,11 @@
|
||||||
<button *ngIf="!request.available" mat-raised-button color="warn" (click)="changeAvailability(request, true);">{{ 'Requests.MarkAvailable' | translate }}</button>
|
<button *ngIf="!request.available" mat-raised-button color="warn" (click)="changeAvailability(request, true);">{{ 'Requests.MarkAvailable' | translate }}</button>
|
||||||
<button *ngIf="request.available" mat-raised-button color="warn" (click)="changeAvailability(request, false);">{{ 'Requests.MarkUnavailable' | translate }}</button>
|
<button *ngIf="request.available" mat-raised-button color="warn" (click)="changeAvailability(request, false);">{{ 'Requests.MarkUnavailable' | translate }}</button>
|
||||||
<button *ngIf="!request.denied" mat-raised-button color="danger" (click)="deny(request);">{{ 'Requests.Deny' | translate }}</button>
|
<button *ngIf="!request.denied" mat-raised-button color="danger" (click)="deny(request);">{{ 'Requests.Deny' | translate }}</button>
|
||||||
<button mat-raised-button color="danger" (click)="delete(request);">{{ 'Requests.RequestPanel.Delete' | translate }}</button>
|
|
||||||
<button mat-raised-button color="accent" (click)="reProcessRequest(request);">{{ 'MediaDetails.ReProcessRequest' | translate }}</button>
|
<button mat-raised-button color="accent" (click)="reProcessRequest(request);">{{ 'MediaDetails.ReProcessRequest' | translate }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="isAdmin || manageOwnRequests">
|
||||||
|
<button mat-raised-button color="danger" (click)="delete(request);">{{ 'Requests.RequestPanel.Delete' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
import { IChildRequests, RequestType } from "../../../../../interfaces";
|
import { IChildRequests, RequestType } from "../../../../../interfaces";
|
||||||
import { RequestService } from "../../../../../services/request.service";
|
|
||||||
import { MessageService } from "../../../../../services";
|
|
||||||
import { MatDialog } from "@angular/material/dialog";
|
|
||||||
import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component";
|
import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component";
|
||||||
|
import { MatDialog } from "@angular/material/dialog";
|
||||||
|
import { MessageService } from "../../../../../services";
|
||||||
|
import { RequestService } from "../../../../../services/request.service";
|
||||||
import { RequestServiceV2 } from "../../../../../services/requestV2.service";
|
import { RequestServiceV2 } from "../../../../../services/requestV2.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -14,6 +15,7 @@ import { RequestServiceV2 } from "../../../../../services/requestV2.service";
|
||||||
export class TvRequestsPanelComponent {
|
export class TvRequestsPanelComponent {
|
||||||
@Input() public tvRequest: IChildRequests[];
|
@Input() public tvRequest: IChildRequests[];
|
||||||
@Input() public isAdmin: boolean;
|
@Input() public isAdmin: boolean;
|
||||||
|
@Input() public manageOwnRequests: boolean;
|
||||||
|
|
||||||
public displayedColumns: string[] = ['number', 'title', 'airDate', 'status'];
|
public displayedColumns: string[] = ['number', 'title', 'airDate', 'status'];
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
{{'Requests.Title' | translate}}
|
{{'Requests.Title' | translate}}
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<tv-requests-panel [tvRequest]="tvRequest" [isAdmin]="isAdmin"></tv-requests-panel>
|
<tv-requests-panel [tvRequest]="tvRequest" [isAdmin]="isAdmin" [manageOwnRequests]="manageOwnRequests"></tv-requests-panel>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
|
|
|
@ -27,6 +27,7 @@ export class TvDetailsComponent implements OnInit {
|
||||||
public showRequest: ITvRequests;
|
public showRequest: ITvRequests;
|
||||||
public fromSearch: boolean;
|
public fromSearch: boolean;
|
||||||
public isAdmin: boolean;
|
public isAdmin: boolean;
|
||||||
|
public manageOwnRequests: boolean;
|
||||||
public advancedOptions: IAdvancedData;
|
public advancedOptions: IAdvancedData;
|
||||||
public showAdvanced: boolean; // Set on the UI
|
public showAdvanced: boolean; // Set on the UI
|
||||||
public requestType = RequestType.tvShow;
|
public requestType = RequestType.tvShow;
|
||||||
|
@ -53,6 +54,7 @@ export class TvDetailsComponent implements OnInit {
|
||||||
|
|
||||||
this.issuesEnabled = this.settingsState.getIssue();
|
this.issuesEnabled = this.settingsState.getIssue();
|
||||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||||
|
this.manageOwnRequests = this.auth.hasRole('ManageOwnRequests');
|
||||||
|
|
||||||
if (this.isAdmin) {
|
if (this.isAdmin) {
|
||||||
this.showAdvanced = await this.sonarrService.isEnabled();
|
this.showAdvanced = await this.sonarrService.isEnabled();
|
||||||
|
|
|
@ -28,6 +28,13 @@
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span mat-list-item >
|
||||||
|
<a mat-list-item [disableRipple]="true" id="nav-openMobile" [routerLinkActive]="'active-list-item'"
|
||||||
|
aria-label="Toggle sidenav" (click)="openMobileApp($event);">
|
||||||
|
<i class="fa-lg fas fa-mobile-alt icon-spacing"></i>
|
||||||
|
{{ 'NavigationBar.OpenMobileApp' | translate }}
|
||||||
|
</a></span>
|
||||||
|
|
||||||
<a mat-list-item [disableRipple]="true" class="menu-spacing" id="nav-logout" [routerLinkActive]="'active-list-item'"
|
<a mat-list-item [disableRipple]="true" class="menu-spacing" id="nav-logout" [routerLinkActive]="'active-list-item'"
|
||||||
aria-label="Toggle sidenav" (click)="logOut();">
|
aria-label="Toggle sidenav" (click)="logOut();">
|
||||||
<i class="fa-lg fas fa-sign-out-alt icon-spacing"></i>
|
<i class="fa-lg fas fa-sign-out-alt icon-spacing"></i>
|
||||||
|
|
|
@ -107,10 +107,21 @@
|
||||||
margin-right:5px;
|
margin-right:5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-openMobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.profile-username{
|
.profile-username{
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 950px) {
|
||||||
|
#nav-openMobile {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-img img {
|
.profile-img img {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
|
||||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||||
import { Observable } from 'rxjs';
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { INavBar } from '../interfaces/ICommon';
|
|
||||||
import { StorageService } from '../shared/storage/storage-service';
|
|
||||||
import { SettingsService, SettingsStateService } from '../services';
|
|
||||||
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
|
||||||
import { SearchFilter } from './SearchFilter';
|
|
||||||
import { Md5 } from 'ts-md5/dist/md5';
|
|
||||||
import { IUser, RequestType, UserType } from '../interfaces';
|
import { IUser, RequestType, UserType } from '../interfaces';
|
||||||
|
import { SettingsService, SettingsStateService } from '../services';
|
||||||
|
|
||||||
import { FilterService } from '../discover/services/filter-service';
|
import { FilterService } from '../discover/services/filter-service';
|
||||||
import { ILocalUser } from '../auth/IUserLogin';
|
import { ILocalUser } from '../auth/IUserLogin';
|
||||||
|
import { INavBar } from '../interfaces/ICommon';
|
||||||
|
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
||||||
|
import { Md5 } from 'ts-md5/dist/md5';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { SearchFilter } from './SearchFilter';
|
||||||
|
import { StorageService } from '../shared/storage/storage-service';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
export enum SearchFilterType {
|
export enum SearchFilterType {
|
||||||
Movie = 1,
|
Movie = 1,
|
||||||
|
@ -34,6 +35,8 @@ export class MyNavComponent implements OnInit {
|
||||||
@Input() public showNav: boolean;
|
@Input() public showNav: boolean;
|
||||||
@Input() public applicationName: string;
|
@Input() public applicationName: string;
|
||||||
@Input() public applicationLogo: string;
|
@Input() public applicationLogo: string;
|
||||||
|
@Input() public applicationUrl: string;
|
||||||
|
@Input() public accessToken: string;
|
||||||
@Input() public username: string;
|
@Input() public username: string;
|
||||||
@Input() public isAdmin: string;
|
@Input() public isAdmin: string;
|
||||||
@Input() public email: string;
|
@Input() public email: string;
|
||||||
|
@ -122,4 +125,12 @@ export class MyNavComponent implements OnInit {
|
||||||
var fallback = this.applicationLogo ? this.applicationLogo : 'https://raw.githubusercontent.com/Ombi-app/Ombi/gh-pages/img/android-chrome-512x512.png';
|
var fallback = this.applicationLogo ? this.applicationLogo : 'https://raw.githubusercontent.com/Ombi-app/Ombi/gh-pages/img/android-chrome-512x512.png';
|
||||||
return `https://www.gravatar.com/avatar/${this.emailHash}?d=${fallback}`;
|
return `https://www.gravatar.com/avatar/${this.emailHash}?d=${fallback}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public openMobileApp(event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const url = `ombi://${this.applicationUrl}|${this.accessToken}`;
|
||||||
|
window.location.assign(url);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
||||||
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin"> {{ 'Requests.Options' | translate}}</button>
|
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || manageOwnRequests"> {{ 'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import { Component, AfterViewInit, ViewChild, EventEmitter, Output, ChangeDetectorRef, OnInit } from "@angular/core";
|
import { AfterViewInit, ChangeDetectorRef, Component, EventEmitter, OnInit, Output, ViewChild } from "@angular/core";
|
||||||
import { IMovieRequests, IRequestEngineResult, IRequestsViewModel } from "../../../interfaces";
|
import { IMovieRequests, IRequestEngineResult, IRequestsViewModel } from "../../../interfaces";
|
||||||
import { MatPaginator } from "@angular/material/paginator";
|
import { NotificationService, RequestService } from "../../../services";
|
||||||
import { MatSort } from "@angular/material/sort";
|
import { Observable, forkJoin, merge, of as observableOf } from 'rxjs';
|
||||||
import { merge, Observable, of as observableOf, forkJoin } from 'rxjs';
|
|
||||||
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
|
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { RequestServiceV2 } from "../../../services/requestV2.service";
|
|
||||||
import { AuthService } from "../../../auth/auth.service";
|
import { AuthService } from "../../../auth/auth.service";
|
||||||
import { StorageService } from "../../../shared/storage/storage-service";
|
import { MatPaginator } from "@angular/material/paginator";
|
||||||
import { RequestFilterType } from "../../models/RequestFilterType";
|
import { MatSort } from "@angular/material/sort";
|
||||||
import { SelectionModel } from "@angular/cdk/collections";
|
|
||||||
import { NotificationService, RequestService } from "../../../services";
|
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
|
||||||
import { MatTableDataSource } from "@angular/material/table";
|
import { MatTableDataSource } from "@angular/material/table";
|
||||||
|
import { RequestFilterType } from "../../models/RequestFilterType";
|
||||||
|
import { RequestServiceV2 } from "../../../services/requestV2.service";
|
||||||
|
import { SelectionModel } from "@angular/cdk/collections";
|
||||||
|
import { StorageService } from "../../../shared/storage/storage-service";
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./movies-grid.component.html",
|
templateUrl: "./movies-grid.component.html",
|
||||||
|
@ -26,6 +26,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
|
||||||
public displayedColumns: string[] = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate', 'actions'];
|
public displayedColumns: string[] = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate', 'actions'];
|
||||||
public gridCount: string = "15";
|
public gridCount: string = "15";
|
||||||
public isAdmin: boolean;
|
public isAdmin: boolean;
|
||||||
|
public manageOwnRequests: boolean;
|
||||||
public defaultSort: string = "requestedDate";
|
public defaultSort: string = "requestedDate";
|
||||||
public defaultOrder: string = "desc";
|
public defaultOrder: string = "desc";
|
||||||
public currentFilter: RequestFilterType = RequestFilterType.All;
|
public currentFilter: RequestFilterType = RequestFilterType.All;
|
||||||
|
@ -39,7 +40,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
|
||||||
private storageKeyGridCount = "Movie_DefaultGridCount";
|
private storageKeyGridCount = "Movie_DefaultGridCount";
|
||||||
private storageKeyCurrentFilter = "Movie_DefaultFilter";
|
private storageKeyCurrentFilter = "Movie_DefaultFilter";
|
||||||
|
|
||||||
@Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any }>();
|
@Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any, manageOwnRequests: boolean, isAdmin: boolean }>();
|
||||||
|
|
||||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
@ -53,6 +54,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||||
|
this.manageOwnRequests = this.auth.hasRole("ManageOwnRequests")
|
||||||
if (this.isAdmin) {
|
if (this.isAdmin) {
|
||||||
this.displayedColumns.unshift('select');
|
this.displayedColumns.unshift('select');
|
||||||
}
|
}
|
||||||
|
@ -135,7 +137,8 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
|
||||||
this.ref.detectChanges();
|
this.ref.detectChanges();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onOpenOptions.emit({ request: request, filter: filter, onChange: onChange });
|
const data = { request: request, filter: filter, onChange: onChange, manageOwnRequests: this.manageOwnRequests, isAdmin: this.isAdmin };
|
||||||
|
this.onOpenOptions.emit(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public switchFilter(type: RequestFilterType) {
|
public switchFilter(type: RequestFilterType) {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<mat-nav-list>
|
<mat-nav-list>
|
||||||
<a id="requestDelete" (click)="delete()" mat-list-item>
|
<a id="requestDelete" *ngIf="data.isAdmin || data.manageOwnRequests" (click)="delete()" mat-list-item>
|
||||||
<span mat-line>{{'Requests.RequestPanel.Delete' | translate}}</span>
|
<span mat-line>{{'Requests.RequestPanel.Delete' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
<a id="requestApprove" *ngIf="data.canApprove" (click)="approve()" mat-list-item>
|
<a id="requestApprove" *ngIf="data.canApprove && data.isAdmin" (click)="approve()" mat-list-item>
|
||||||
<span mat-line>{{'Requests.RequestPanel.Approve' | translate}}</span>
|
<span mat-line>{{'Requests.RequestPanel.Approve' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
<a id="requestChangeAvailability" *ngIf="data.type !== RequestType.tvShow" (click)="changeAvailability()" mat-list-item>
|
<a id="requestChangeAvailability" *ngIf="data.type !== RequestType.tvShow && data.isAdmin" (click)="changeAvailability()" mat-list-item>
|
||||||
<span mat-line>{{'Requests.RequestPanel.ChangeAvailability' | translate}}</span>
|
<span mat-line>{{'Requests.RequestPanel.ChangeAvailability' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
</mat-nav-list>
|
</mat-nav-list>
|
|
@ -1,8 +1,9 @@
|
||||||
import { Component, ViewChild } from "@angular/core";
|
import { Component, ViewChild } from "@angular/core";
|
||||||
|
|
||||||
import { MatBottomSheet } from "@angular/material/bottom-sheet";
|
import { MatBottomSheet } from "@angular/material/bottom-sheet";
|
||||||
|
import { MoviesGridComponent } from "./movies-grid/movies-grid.component";
|
||||||
import { RequestOptionsComponent } from "./options/request-options.component";
|
import { RequestOptionsComponent } from "./options/request-options.component";
|
||||||
import { UpdateType } from "../models/UpdateType";
|
import { UpdateType } from "../models/UpdateType";
|
||||||
import { MoviesGridComponent } from "./movies-grid/movies-grid.component";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./requests-list.component.html",
|
templateUrl: "./requests-list.component.html",
|
||||||
|
@ -12,8 +13,8 @@ export class RequestsListComponent {
|
||||||
|
|
||||||
constructor(private bottomSheet: MatBottomSheet) { }
|
constructor(private bottomSheet: MatBottomSheet) { }
|
||||||
|
|
||||||
public onOpenOptions(event: { request: any, filter: any, onChange: any }) {
|
public onOpenOptions(event: { request: any, filter: any, onChange: any, manageOwnRequests: boolean, isAdmin: boolean }) {
|
||||||
const ref = this.bottomSheet.open(RequestOptionsComponent, { data: { id: event.request.id, type: event.request.requestType, canApprove: event.request.canApprove } });
|
const ref = this.bottomSheet.open(RequestOptionsComponent, { data: { id: event.request.id, type: event.request.requestType, canApprove: event.request.canApprove, manageOwnRequests: event.manageOwnRequests, isAdmin: event.isAdmin } });
|
||||||
|
|
||||||
ref.afterDismissed().subscribe((result) => {
|
ref.afterDismissed().subscribe((result) => {
|
||||||
if(!result) {
|
if(!result) {
|
||||||
|
|
|
@ -22,4 +22,8 @@ export class TheMovieDbService extends ServiceHelpers {
|
||||||
return this.http.get<IMovieDbKeyword>(`${this.url}/Keywords/${keywordId}`, { headers: this.headers })
|
return this.http.get<IMovieDbKeyword>(`${this.url}/Keywords/${keywordId}`, { headers: this.headers })
|
||||||
.pipe(catchError((error: HttpErrorResponse) => error.status === 404 ? empty() : throwError(error)));
|
.pipe(catchError((error: HttpErrorResponse) => error.status === 404 ? empty() : throwError(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getGenres(media: string): Observable<IMovieDbKeyword[]> {
|
||||||
|
return this.http.get<IMovieDbKeyword[]>(`${this.url}/Genres/${media}`, { headers: this.headers })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<settings-menu></settings-menu>
|
<settings-menu></settings-menu>
|
||||||
<div class="small-middle-container">
|
<div class="small-middle-container">
|
||||||
<wiki [path]="'/settings/customization/'"></wiki>
|
<wiki [path]="'/settings/customization/'"></wiki>
|
||||||
<fieldset *ngIf="settings">
|
<fieldset *ngIf="settings">
|
||||||
|
@ -12,9 +12,8 @@
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-form-field">
|
<div class="md-form-field">
|
||||||
<mat-hint>The application url should be your Externally Accessible URL for example, your internal URL is http://192.168.1.50/ but your Externally
|
<mat-hint>The application url should be your Externally Accessible URL (the address you use to reach Ombi from outside your system). For example, 'https://example.com/requests'. <br />Please ensure this field is correct as it drives a lot of functionality, including the QR code for the
|
||||||
Accessible URL is 'https://mydomain.com/requests' Please ensure this field is correct as it drives a lot of functionality include the QR code for the
|
mobile app, and the way email notifications are sent.
|
||||||
mobile app and it affects the way email notifications are sent.
|
|
||||||
</mat-hint>
|
</mat-hint>
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Application URL</mat-label>
|
<mat-label>Application URL</mat-label>
|
||||||
|
@ -81,4 +80,4 @@
|
||||||
|
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<form [formGroup]='tagForm'>
|
<form [formGroup]='tagForm'>
|
||||||
|
|
||||||
<mat-form-field class="example-full-width">
|
<mat-form-field class="example-full-width">
|
||||||
<input type="text" placeholder="Excluded Keyword IDs for Movie Suggestions" matInput
|
<input type="text" placeholder="Excluded Keyword IDs for Movie & TV Suggestions" matInput
|
||||||
formControlName="input" [matAutocomplete]="auto"
|
formControlName="input" [matAutocomplete]="auto"
|
||||||
matTooltip="Prevent movies with certain keywords from being suggested. May require a restart to take effect.">
|
matTooltip="Prevent movies with certain keywords from being suggested. May require a restart to take effect.">
|
||||||
<mat-autocomplete (optionSelected)="optionSelected($event.option.value)" autoActiveFirstOption
|
<mat-autocomplete (optionSelected)="optionSelected($event.option.value)" autoActiveFirstOption
|
||||||
|
@ -28,7 +28,45 @@
|
||||||
|
|
||||||
<mat-chip-list #chipList>
|
<mat-chip-list #chipList>
|
||||||
<mat-chip *ngFor="let key of excludedKeywords" [selectable]="false" [removable]="true"
|
<mat-chip *ngFor="let key of excludedKeywords" [selectable]="false" [removable]="true"
|
||||||
(removed)="remove(key)">
|
(removed)="remove(key, 'keyword')">
|
||||||
|
{{key.name}}
|
||||||
|
<i matChipRemove class="fas fa-times fa-lg"></i>
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
|
||||||
|
<div class="md-form-field" style="margin-top:1em;">
|
||||||
|
<mat-form-field appearance="outline" >
|
||||||
|
<mat-label>Movie Genres</mat-label>
|
||||||
|
<mat-select formControlName="excludedMovieGenres" multiple>
|
||||||
|
<mat-option *ngFor="let genre of filteredMovieGenres" [value]="genre.id">
|
||||||
|
{{genre.name}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-chip-list #chipList>
|
||||||
|
<mat-chip *ngFor="let key of excludedMovieGenres" [selectable]="false" [removable]="true"
|
||||||
|
(removed)="remove(key, 'movieGenre')">
|
||||||
|
{{key.name}}
|
||||||
|
<i matChipRemove class="fas fa-times fa-lg"></i>
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
|
||||||
|
<div class="md-form-field" style="margin-top:1em;">
|
||||||
|
<mat-form-field appearance="outline" >
|
||||||
|
<mat-label>Tv Genres</mat-label>
|
||||||
|
<mat-select formControlName="excludedTvGenres" multiple>
|
||||||
|
<mat-option *ngFor="let genre of filteredTvGenres" [value]="genre.id">
|
||||||
|
{{genre.name}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-chip-list #chipList>
|
||||||
|
<mat-chip *ngFor="let key of excludedTvGenres" [selectable]="false" [removable]="true"
|
||||||
|
(removed)="remove(key, 'tvGenre')">
|
||||||
{{key.name}}
|
{{key.name}}
|
||||||
<i matChipRemove class="fas fa-times fa-lg"></i>
|
<i matChipRemove class="fas fa-times fa-lg"></i>
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
|
@ -52,4 +90,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,8 +23,13 @@ export class TheMovieDbComponent implements OnInit {
|
||||||
|
|
||||||
public settings: ITheMovieDbSettings;
|
public settings: ITheMovieDbSettings;
|
||||||
public excludedKeywords: IKeywordTag[];
|
public excludedKeywords: IKeywordTag[];
|
||||||
|
public excludedMovieGenres: IKeywordTag[];
|
||||||
|
public excludedTvGenres: IKeywordTag[];
|
||||||
public tagForm: FormGroup;
|
public tagForm: FormGroup;
|
||||||
public filteredTags: IMovieDbKeyword[];
|
public filteredTags: IMovieDbKeyword[];
|
||||||
|
public filteredMovieGenres: IMovieDbKeyword[];
|
||||||
|
public filteredTvGenres: IMovieDbKeyword[];
|
||||||
|
|
||||||
@ViewChild('fruitInput') public fruitInput: ElementRef<HTMLInputElement>;
|
@ViewChild('fruitInput') public fruitInput: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService,
|
constructor(private settingsService: SettingsService,
|
||||||
|
@ -35,9 +40,13 @@ export class TheMovieDbComponent implements OnInit {
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.tagForm = this.fb.group({
|
this.tagForm = this.fb.group({
|
||||||
input: null,
|
input: null,
|
||||||
|
excludedMovieGenres: null,
|
||||||
|
excludedTvGenres: null,
|
||||||
});
|
});
|
||||||
this.settingsService.getTheMovieDbSettings().subscribe(settings => {
|
this.settingsService.getTheMovieDbSettings().subscribe(settings => {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
|
// Map Keyword ids -> keyword name
|
||||||
this.excludedKeywords = settings.excludedKeywordIds
|
this.excludedKeywords = settings.excludedKeywordIds
|
||||||
? settings.excludedKeywordIds.map(id => ({
|
? settings.excludedKeywordIds.map(id => ({
|
||||||
id,
|
id,
|
||||||
|
@ -45,13 +54,56 @@ export class TheMovieDbComponent implements OnInit {
|
||||||
initial: true,
|
initial: true,
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
this.excludedKeywords.forEach(key => {
|
|
||||||
this.tmdbService.getKeyword(key.id).subscribe(keyResult => {
|
this.excludedKeywords.forEach(key => {
|
||||||
this.excludedKeywords.filter((val, idx) => {
|
this.tmdbService.getKeyword(key.id).subscribe(keyResult => {
|
||||||
val.name = keyResult.name;
|
this.excludedKeywords.filter((val, idx) => {
|
||||||
})
|
val.name = keyResult.name;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Map Movie Genre ids -> genre name
|
||||||
|
this.excludedMovieGenres = settings.excludedMovieGenreIds
|
||||||
|
? settings.excludedMovieGenreIds.map(id => ({
|
||||||
|
id,
|
||||||
|
name: "",
|
||||||
|
initial: true,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
this.tmdbService.getGenres("movie").subscribe(results => {
|
||||||
|
this.filteredMovieGenres = results;
|
||||||
|
|
||||||
|
this.excludedMovieGenres.forEach(genre => {
|
||||||
|
results.forEach(result => {
|
||||||
|
if (genre.id == result.id) {
|
||||||
|
genre.name = result.name;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Map Tv Genre ids -> genre name
|
||||||
|
this.excludedTvGenres = settings.excludedTvGenreIds
|
||||||
|
? settings.excludedTvGenreIds.map(id => ({
|
||||||
|
id,
|
||||||
|
name: "",
|
||||||
|
initial: true,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
this.tmdbService.getGenres("tv").subscribe(results => {
|
||||||
|
this.filteredTvGenres = results;
|
||||||
|
|
||||||
|
this.excludedTvGenres.forEach(genre => {
|
||||||
|
results.forEach(result => {
|
||||||
|
if (genre.id == result.id) {
|
||||||
|
genre.name = result.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tagForm
|
this.tagForm
|
||||||
|
@ -65,19 +117,48 @@ export class TheMovieDbComponent implements OnInit {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe((r) => (this.filteredTags = r));
|
.subscribe((r) => (this.filteredTags = r));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(tag: IKeywordTag): void {
|
public remove(tag: IKeywordTag, tag_type: string): void {
|
||||||
const index = this.excludedKeywords.indexOf(tag);
|
var exclusion_list;
|
||||||
|
|
||||||
|
switch (tag_type) {
|
||||||
|
case "keyword":
|
||||||
|
exclusion_list = this.excludedKeywords;
|
||||||
|
break;
|
||||||
|
case "movieGenre":
|
||||||
|
exclusion_list = this.excludedMovieGenres;
|
||||||
|
break;
|
||||||
|
case "tvGenre":
|
||||||
|
exclusion_list = this.excludedTvGenres;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = exclusion_list.indexOf(tag);
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
this.excludedKeywords.splice(index, 1);
|
exclusion_list.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public save() {
|
public save() {
|
||||||
|
|
||||||
|
var selectedMovieGenres: number[] = this.tagForm.controls.excludedMovieGenres.value ?? [];
|
||||||
|
var selectedTvGenres: number[] = this.tagForm.controls.excludedTvGenres.value ?? [];
|
||||||
|
|
||||||
|
var movieIds: number[] = this.excludedMovieGenres.map(k => k.id);
|
||||||
|
var tvIds: number[] = this.excludedTvGenres.map(k => k.id)
|
||||||
|
|
||||||
|
// Concat and dedup already excluded genres + newly selected ones
|
||||||
|
selectedMovieGenres = movieIds.concat(selectedMovieGenres.filter(item => movieIds.indexOf(item) < 0));
|
||||||
|
selectedTvGenres = tvIds.concat(selectedTvGenres.filter(item => tvIds.indexOf(item) < 0));
|
||||||
|
|
||||||
this.settings.excludedKeywordIds = this.excludedKeywords.map(k => k.id);
|
this.settings.excludedKeywordIds = this.excludedKeywords.map(k => k.id);
|
||||||
|
this.settings.excludedMovieGenreIds = selectedMovieGenres;
|
||||||
|
this.settings.excludedTvGenreIds = selectedTvGenres;
|
||||||
|
|
||||||
this.settingsService.saveTheMovieDbSettings(this.settings).subscribe(x => {
|
this.settingsService.saveTheMovieDbSettings(this.settings).subscribe(x => {
|
||||||
if (x) {
|
if (x) {
|
||||||
this.notificationService.success("Successfully saved The Movie Database settings");
|
this.notificationService.success("Successfully saved The Movie Database settings");
|
||||||
|
|
|
@ -56,17 +56,17 @@
|
||||||
<qrcode id="qrCode" *ngIf="qrCodeEnabled" [qrdata]="qrCode" [size]="256" [level]="'L'"></qrcode>
|
<qrcode id="qrCode" *ngIf="qrCodeEnabled" [qrdata]="qrCode" [size]="256" [level]="'L'"></qrcode>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<a href='https://play.google.com/store/apps/details?id=com.tidusjar.Ombi&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'
|
<a href="https://play.google.com/store/apps/details?id=com.tidusjar.Ombi&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1"
|
||||||
target="_blank"><img width="200px" alt='Get it on Google Play'
|
target="_blank"><img width="200" alt="Get it on Google Play"
|
||||||
src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png' /></a>
|
src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" /></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<a href='https://apps.apple.com/us/app/ombi/id1335260043' target="_blank"><img
|
<a href="https://apps.apple.com/us/app/ombi/id1335260043" target="_blank"><img
|
||||||
style="margin-left:13px" width="170px" alt='Get it from the App Store'
|
style="margin-left:13px" width="170" alt="Get it from the App Store"
|
||||||
src='{{baseUrl}}/images/appstore.svg' /></a>
|
src="{{baseUrl}}/images/appstore.svg" /></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button style="margin-left:13px; margin-top: 20px;" mat-raised-button color="accent" type="button" (click)="openMobileApp($event)">Open Mobile App</button>
|
<button style="margin-left:13px; margin-top: 20px;" mat-raised-button color="accent" type="button" (click)="openMobileApp($event)">Open Mobile App</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -145,13 +145,14 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-md-3 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<button *ngIf="!edit" type="button" mat-raised-button color="accent" data-test="createuserbtn" (click)="create()">Create</button>
|
<button *ngIf="!edit" type="button" mat-raised-button color="accent" data-test="createuserbtn" (click)="create()">Create</button>
|
||||||
<div *ngIf="edit">
|
<div *ngIf="edit">
|
||||||
<button type="button" data-test="updatebtn" mat-raised-button color="accent" class="btn btn-primary-outline" (click)="update()">Update</button>
|
<button type="button" data-test="updatebtn" mat-raised-button color="accent" class="btn btn-primary-outline" (click)="update()">Update</button>
|
||||||
<button type="button" data-test="deletebtn" mat-raised-button color="warn" class="btn btn-danger-outline" (click)="delete()">Delete</button>
|
<button type="button" data-test="deletebtn" mat-raised-button color="warn" class="btn btn-danger-outline" (click)="delete()">Delete</button>
|
||||||
<button type="button" style="float:right;" mat-raised-button color="primary" class="btn btn-info-outline" (click)="resetPassword()" pTooltip="You need your SMTP settings setup">Send
|
<button type="button" style="float:right;" mat-raised-button color="primary" class="btn btn-info-outline" (click)="resetPassword()" matTooltip="You need your SMTP settings setup">Send
|
||||||
Reset Password Link</button>
|
Reset Password Link</button>
|
||||||
|
<button *ngIf="customization.applicationUrl" type="button" mat-raised-button color="accent" class="btn btn-info-outline" (click)="appLink()" matTooltip="Send this link to the user and they can then open the app and directly login">Copy users App Link</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Location } from "@angular/common";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { AfterViewInit, Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ICheckbox, ICustomizationSettings, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces";
|
||||||
|
import { IdentityService, MessageService, RadarrService, SettingsService, SonarrService } from "../services";
|
||||||
|
|
||||||
import { ICheckbox, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces";
|
import { Clipboard } from '@angular/cdk/clipboard';
|
||||||
import { IdentityService, RadarrService, SonarrService, MessageService } from "../services";
|
import { Location } from "@angular/common";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./usermanagement-user.component.html",
|
templateUrl: "./usermanagement-user.component.html",
|
||||||
|
@ -27,12 +28,17 @@ export class UserManagementUserComponent implements OnInit {
|
||||||
|
|
||||||
public countries: string[];
|
public countries: string[];
|
||||||
|
|
||||||
|
private customization: ICustomizationSettings;
|
||||||
|
private accessToken: string;
|
||||||
|
|
||||||
constructor(private identityService: IdentityService,
|
constructor(private identityService: IdentityService,
|
||||||
private notificationService: MessageService,
|
private notificationService: MessageService,
|
||||||
|
private readonly settingsService: SettingsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private sonarrService: SonarrService,
|
private sonarrService: SonarrService,
|
||||||
private radarrService: RadarrService,
|
private radarrService: RadarrService,
|
||||||
|
private clipboard: Clipboard,
|
||||||
private location: Location) {
|
private location: Location) {
|
||||||
|
|
||||||
this.route.params.subscribe((params: any) => {
|
this.route.params.subscribe((params: any) => {
|
||||||
|
@ -60,6 +66,9 @@ export class UserManagementUserComponent implements OnInit {
|
||||||
this.radarrService.getQualityProfilesFromSettings().subscribe(x => this.radarrQualities = x);
|
this.radarrService.getQualityProfilesFromSettings().subscribe(x => this.radarrQualities = x);
|
||||||
this.radarrService.getRootFoldersFromSettings().subscribe(x => this.radarrRootFolders = x);
|
this.radarrService.getRootFoldersFromSettings().subscribe(x => this.radarrRootFolders = x);
|
||||||
|
|
||||||
|
this.settingsService.getCustomization().subscribe(x => this.customization = x);
|
||||||
|
this.identityService.getAccessToken().subscribe(x => this.accessToken = x);
|
||||||
|
|
||||||
if(!this.edit) {
|
if(!this.edit) {
|
||||||
this.user = {
|
this.user = {
|
||||||
alias: "",
|
alias: "",
|
||||||
|
@ -178,7 +187,12 @@ export class UserManagementUserComponent implements OnInit {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async appLink() {
|
||||||
|
this.clipboard.copy(`ombi://${this.customization.applicationUrl}|${this.accessToken}`);
|
||||||
|
this.notificationService.send("Copied!");
|
||||||
|
}
|
||||||
|
|
||||||
public back() {
|
public back() {
|
||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
import { CommonModule } from "@angular/common";
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
import { NgModule } from "@angular/core";
|
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
|
||||||
import { ConfirmDialogModule } from "primeng/confirmdialog";
|
|
||||||
import { MultiSelectModule } from "primeng/multiselect";
|
|
||||||
import { SidebarModule } from "primeng/sidebar";
|
|
||||||
import { TooltipModule } from "primeng/tooltip";
|
|
||||||
|
|
||||||
import { UserManagementUserComponent } from "./usermanagement-user.component";
|
|
||||||
import { UserManagementComponent } from "./usermanagement.component";
|
|
||||||
|
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
|
||||||
import { IdentityService, PlexService, RadarrService, SonarrService } from "../services";
|
import { IdentityService, PlexService, RadarrService, SonarrService } from "../services";
|
||||||
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from "../auth/auth.guard";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { ConfirmDialogModule } from "primeng/confirmdialog";
|
||||||
|
import { MultiSelectModule } from "primeng/multiselect";
|
||||||
|
import { NgModule } from "@angular/core";
|
||||||
import { OrderModule } from "ngx-order-pipe";
|
import { OrderModule } from "ngx-order-pipe";
|
||||||
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
import { SharedModule } from "../shared/shared.module";
|
import { SharedModule } from "../shared/shared.module";
|
||||||
|
import { SidebarModule } from "primeng/sidebar";
|
||||||
|
import { TooltipModule } from "primeng/tooltip";
|
||||||
|
import { UserManagementComponent } from "./usermanagement.component";
|
||||||
|
import { UserManagementUserComponent } from "./usermanagement-user.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", component: UserManagementComponent, canActivate: [AuthGuard] },
|
{ path: "", component: UserManagementComponent, canActivate: [AuthGuard] },
|
||||||
|
|
|
@ -5,6 +5,10 @@ using Ombi.Attributes;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
// Due to conflicting Genre models in
|
||||||
|
// Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models
|
||||||
|
using Genre = Ombi.TheMovieDbApi.Models.Genre;
|
||||||
|
|
||||||
namespace Ombi.Controllers.External
|
namespace Ombi.Controllers.External
|
||||||
{
|
{
|
||||||
[Admin]
|
[Admin]
|
||||||
|
@ -34,5 +38,13 @@ namespace Ombi.Controllers.External
|
||||||
var keyword = await TmdbApi.GetKeyword(keywordId);
|
var keyword = await TmdbApi.GetKeyword(keywordId);
|
||||||
return keyword == null ? NotFound() : (IActionResult)Ok(keyword);
|
return keyword == null ? NotFound() : (IActionResult)Ok(keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the genres for either Tv or Movies depending on media type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="media">Either `tv` or `movie`.</param>
|
||||||
|
[HttpGet("Genres/{media}")]
|
||||||
|
public async Task<IEnumerable<Genre>> GetGenres(string media) =>
|
||||||
|
await TmdbApi.GetGenres(media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,17 @@ namespace Ombi.Controllers.V1
|
||||||
{
|
{
|
||||||
var settings = await Ombi.GetSettingsAsync();
|
var settings = await Ombi.GetSettingsAsync();
|
||||||
|
|
||||||
return new { Result = settings?.Wizard ?? false};
|
return new { Result = settings?.Wizard ?? false };
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
[HttpGet("demo")]
|
||||||
|
public IActionResult Demo()
|
||||||
|
{
|
||||||
|
var instance = DemoSingleton.Instance;
|
||||||
|
|
||||||
|
instance.Demo = !instance.Demo;
|
||||||
|
return new OkResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -72,8 +72,6 @@ namespace Ombi
|
||||||
services.Configure<TokenAuthentication>(configuration.GetSection("TokenAuthentication"));
|
services.Configure<TokenAuthentication>(configuration.GetSection("TokenAuthentication"));
|
||||||
services.Configure<LandingPageBackground>(configuration.GetSection("LandingPageBackground"));
|
services.Configure<LandingPageBackground>(configuration.GetSection("LandingPageBackground"));
|
||||||
services.Configure<DemoLists>(configuration.GetSection("Demo"));
|
services.Configure<DemoLists>(configuration.GetSection("Demo"));
|
||||||
var enabledDemo = Convert.ToBoolean(configuration.GetSection("Demo:Enabled").Value);
|
|
||||||
DemoSingleton.Instance.Demo = enabledDemo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddJwtAuthentication(this IServiceCollection services)
|
public static void AddJwtAuthentication(this IServiceCollection services)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue