Merge branch 'develop' of https://github.com/tidusjar/ombi into develop

pull/2728/head
TidusJar 6 years ago
commit c8d49faee5

@ -35,7 +35,10 @@ after_build:
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows-32bit.zip"

@ -47,6 +47,7 @@ var windowsArtifactsFolder = artifactsFolder + "win10-x64/published";
var windows32BitArtifactsFolder = artifactsFolder + "win10-x86/published";
var osxArtifactsFolder = artifactsFolder + "osx-x64/published";
var linuxArtifactsFolder = artifactsFolder + "linux-x64/published";
var linuxArmArtifactsFolder = artifactsFolder + "linux-arm/published";
var linuxArm64BitArtifactsFolder = artifactsFolder + "linux-arm64/published";
@ -149,6 +150,7 @@ Task("Package")
Zip(windows32BitArtifactsFolder +"/",artifactsFolder + "windows-32bit.zip");
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
});
@ -158,6 +160,7 @@ Task("Publish")
.IsDependentOn("Publish-Windows-32bit")
.IsDependentOn("Publish-OSX")
.IsDependentOn("Publish-Linux")
.IsDependentOn("Publish-Linux-ARM")
.IsDependentOn("Publish-Linux-ARM-64Bit")
.IsDependentOn("Package");
@ -214,6 +217,20 @@ Task("Publish-Linux")
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-Linux-ARM")
.Does(() =>
{
publishSettings.Runtime = "linux-arm";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(
buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml",
buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-Linux-ARM-64Bit")
.Does(() =>

@ -44,6 +44,7 @@ namespace Ombi.Api.Sonarr.Models
public DateTime added { get; set; }
public Ratings ratings { get; set; }
public int qualityProfileId { get; set; }
public int languageProfileId { get; set; }
public int id { get; set; }
public DateTime nextAiring { get; set; }
}

@ -10,13 +10,13 @@ namespace Ombi.Core
Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
Task<IEnumerable<SearchMovieViewModel>> Search(string search);
Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string languageCode);
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = "en");
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId);
}

@ -36,14 +36,16 @@ namespace Ombi.Core.Engine
private IMapper Mapper { get; }
private ILogger<MovieSearchEngine> Logger { get; }
private const int MovieLimit = 10;
/// <summary>
/// Lookups the imdb information.
/// </summary>
/// <param name="theMovieDbId">The movie database identifier.</param>
/// <returns></returns>
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId)
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = "en")
{
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId);
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode);
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
return await ProcessSingleMovie(viewMovie, true);
@ -54,13 +56,13 @@ namespace Ombi.Core.Engine
/// </summary>
/// <param name="search">The search.</param>
/// <returns></returns>
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string langaugeCode)
{
var result = await MovieApi.SearchMovie(search);
var result = await MovieApi.SearchMovie(search, year, langaugeCode);
if (result != null)
{
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@ -76,7 +78,7 @@ namespace Ombi.Core.Engine
if (result != null)
{
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@ -90,7 +92,7 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
if (result != null)
{
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@ -104,7 +106,7 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
if (result != null)
{
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@ -119,7 +121,7 @@ namespace Ombi.Core.Engine
if (result != null)
{
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@ -133,7 +135,7 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
if (result != null)
{
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}

@ -348,16 +348,16 @@ namespace Ombi.Core.Senders
if (!existingSeason.monitored)
{
// We need to monitor it, problem being is all episodes will now be monitored
// So we need to monior the series but unmonitor every episode
// Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
// So we need to monitor the series but unmonitor every episode
// Except the episodes that are already monitored before we update the series (we do not want to unmonitored episodes that are monitored beforehand)
existingSeason.monitored = true;
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
sea.monitored = true;
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
var epToUnmonitor = new List<Episode>();
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
var epToUnmonitored = new List<Episode>();
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
{
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
@ -366,10 +366,10 @@ namespace Ombi.Core.Senders
// continue;
//}
ep.monitored = false;
epToUnmonitor.Add(ep);
epToUnmonitored.Add(ep);
}
foreach (var epToUpdate in epToUnmonitor)
foreach (var epToUpdate in epToUnmonitored)
{
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
}

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,52 @@
using System;
using NUnit.Framework;
using System.Collections.Generic;
namespace Ombi.Helpers.Tests
{
[TestFixture]
public class PlexHelperTests
{
[TestCaseSource(nameof(ProviderIdGuidData))]
public string GetProviderIdFromPlexGuidTests(string guidInput, ProviderIdType type)
{
var result = PlexHelper.GetProviderIdFromPlexGuid(guidInput);
switch (type)
{
case ProviderIdType.Imdb:
Assert.That(result.ImdbId, Is.Not.Null);
return result.ImdbId;
case ProviderIdType.TvDb:
Assert.That(result.TheTvDb, Is.Not.Null);
return result.TheTvDb;
case ProviderIdType.MovieDb:
Assert.That(result.TheMovieDb, Is.Not.Null);
return result.TheMovieDb;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
public static IEnumerable<TestCaseData> ProviderIdGuidData
{
get
{
yield return new TestCaseData("com.plexapp.agents.thetvdb://269586/2/8?lang=en", ProviderIdType.TvDb).Returns("269586").SetName("Regular TvDb Id");
yield return new TestCaseData("com.plexapp.agents.themoviedb://390043?lang=en", ProviderIdType.MovieDb).Returns("390043").SetName("Regular MovieDb Id");
yield return new TestCaseData("com.plexapp.agents.imdb://tt2543164?lang=en", ProviderIdType.Imdb).Returns("tt2543164").SetName("Regular Imdb Id");
yield return new TestCaseData("com.plexapp.agents.agent47://tt2543456?lang=en", ProviderIdType.Imdb).Returns("tt2543456").SetName("Unknown IMDB agent");
yield return new TestCaseData("com.plexapp.agents.agent47://456822/1/1?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent");
yield return new TestCaseData("com.plexapp.agents.agent47://456822/999/999?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent, large episode and season");
}
}
public enum ProviderIdType
{
Imdb,
TvDb,
MovieDb
}
}
}

@ -27,12 +27,15 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Ombi.Helpers
{
public class PlexHelper
{
private const string ImdbMatchExpression = "tt([0-9]{1,10})";
private const string TvDbIdMatchExpression = "//[0-9]+/([0-9]{1,3})/([0-9]{1,3})";
public static ProviderId GetProviderIdFromPlexGuid(string guid)
{
//com.plexapp.agents.thetvdb://269586/2/8?lang=en
@ -52,7 +55,7 @@ namespace Ombi.Helpers
{
TheTvDb = guidSplit[1]
};
}
} else
if (guid.Contains("themoviedb", CompareOptions.IgnoreCase))
{
return new ProviderId
@ -60,6 +63,7 @@ namespace Ombi.Helpers
TheMovieDb = guidSplit[1]
};
}
else
if (guid.Contains("imdb", CompareOptions.IgnoreCase))
{
return new ProviderId
@ -67,6 +71,31 @@ namespace Ombi.Helpers
ImdbId = guidSplit[1]
};
}
else
{
var imdbRegex = new Regex(ImdbMatchExpression, RegexOptions.Compiled);
var tvdbRegex = new Regex(TvDbIdMatchExpression, RegexOptions.Compiled);
var imdbMatch = imdbRegex.IsMatch(guid);
if (imdbMatch)
{
return new ProviderId
{
ImdbId = guidSplit[1]
};
}
else
{
// Check if it matches the TvDb pattern
var tvdbMatch = tvdbRegex.IsMatch(guid);
if (tvdbMatch)
{
return new ProviderId
{
TheTvDb = guidSplit[1]
};
}
}
}
}
return new ProviderId();
}

@ -1,10 +1,17 @@
<!doctype html>
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Ombi</title>
<style type="text/css">
<style>
/* -------------------------------------
GLOBAL RESETS
------------------------------------- */
/*All the styling goes here*/
img {
border: none;
-ms-interpolation-mode: bicubic;
@ -12,6 +19,7 @@
}
body {
background-color: #1f1f1f;
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
@ -20,6 +28,8 @@
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
color: #FFF;
text-align: center;
}
table {
@ -31,39 +41,54 @@
table td {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
font-size: 14px;
vertical-align: top;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
.body {
color: #FFF;
background-color: #1f1f1f;
width: 100%;
text-align: center;
}
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
display: block;
margin: 0 auto !important;
max-width: 1042px;
Margin: 0 auto !important;
/* makes it centered */
max-width: 1036px;
padding: 10px;
width: 1042px;
width: 1036px;
}
/* This should also be a block element, so that it will fill 100% of the .container */
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
Margin: 0 auto;
max-width: 1037px;
padding: 10px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
color: #FFF;
background: #1f1f1f;
border-radius: 3px;
width: 100%;
text-align: center;
}
.wrapper {
box-sizing: border-box;
padding: 5px;
overflow: auto;
padding: 20px;
}
.content-block {
@ -71,58 +96,241 @@
padding-top: 10px;
}
.media-card {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
vertical-align: top;
padding: 3px;
width: 500px;
min-width: 500px;
max-width: 500px;
height: 252px;
max-height: 252px;
min-height: 252px;
}
.card-bg {
background-image: url({0});
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 500px;
background-color: #1f1f1f;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
background-clip: padding-box;
border: 2px solid rgba(255, 118, 27, .4);
height: 252px;
max-height: 252px;
}
.bg-tint {
background-color: rgba(0, 0, 0, .6);
}
.poster-container {
vertical-align: top;
width: 150px;
min-width: 150px;
height: 225px;
max-height: 225px;
min-height: 225px;
}
.poster-img {}
.poster-overlay {}
.movie-info {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
vertical-align: top;
padding-left: 4px;
text-align: left;
height: 227px;
}
.title h1 {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
font-size: 22px;
line-height: 24px;
vertical-align: top;
max-width: 320px;
white-space: normal;
display: block;
height: 50px;
min-height: 50px;
max-height: 50px;
}
.description {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
height: 130px;
max-height: 130px;
max-width: 320px;
overflow: hidden;
text-overflow: ellipsis;
display: block;
font-size: 14px !important;
text-align: justify;
}
.meta {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
max-width: 300px;
min-width: 300px;
padding: 3px 7px;
margin-top: 10px;
font-size: 14px !important;
line-height: 1;
text-align: left;
white-space: nowrap;
vertical-align: middle;
background-color: rgba(255, 118, 27, 0.5);
color: #fff;
border-radius: 2px;
overflow: hidden;
display: block;
}
.footer {
clear: both;
margin-top: 10px;
Margin-top: 10px;
text-align: center;
width: 100%;
font-size: 12px;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #fff;
color: #999999;
font-size: 12px;
text-align: center;
}
h1 {
color: #ffffff;
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3,
h4 {
color: #ff761b;
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
font-weight: 400;
margin: 0;
color: #ff761b;
font-size: 22px;
line-height: 24px;
margin: 0 auto;
text-transform: capitalize;
}
p {
p,
ul,
ol {
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
font-weight: 400;
font-weight: normal;
margin: 0;
}
p li,
ul li {
ul li,
ol li {
list-style-position: inside;
margin-left: 5px;
}
a {
color: #ff761b !important;
text-decoration: none;
font-weight: 400;
}
@media only screen and (max-width: 1040px) {
.media-card {
display: block !important;
margin-top: 0 !important;
margin-right: auto !important;
margin-bottom: 10px !important;
margin-left: auto !important;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
hr {
border: 0;
border-bottom: 1px solid #f6f6f6;
Margin: 20px 0;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 1059px) {
table[class=body] h1 {
font-size: 28px !important;
font-size: 22px;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 12px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
@ -135,9 +343,52 @@
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
.media-card {
display: flex !important;
margin-top: 0 !important;
margin-right: auto !important;
margin-bottom: 10px !important;
margin-left: auto !important;
height: 100% !important;
width: 100% !important;
width: 500px !important;
max-width: 500px !important;
}
}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
@ -146,66 +397,78 @@
line-height: inherit !important;
text-decoration: none !important;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body class="" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;font-size: 14px;line-height: 1.4;margin: 0;padding: 0;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; background-color: #1f1f1f; color: #fff;">
<tr>
<td class="container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;display: block;max-width: 1042px;padding: 10px;width: 1042px;margin: 0 auto !important;">
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 1037px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Ombi Recently Added</span>
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; border-radius: 3px;">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width: 1042px; width: 100%;">
<tr>
<td align="center">
<img src="{@LOGO}" width="400px" text-align="center"/>
</td>
</tr>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
<br />
<br />
<p style="color: #fff; font-family: sans-serif; font-size: 20px; font-weight: normal; margin: 0; Margin-bottom: 15px; text-align: center;">{@INTRO}</p>
<body class="" style="background-color: #1f1f1f; font-family: 'Open Sans', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #FFF; text-align: center;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" width="100%" bgcolor="#1f1f1f" align="center" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #FFF; background-color: #1f1f1f; width: 100%; text-align: center;">
<tbody>
<tr>
<td class="container" width="1036" valign="top" style="font-family: sans-serif; font-size: 14px; vertical-align: top; max-width: 1036px; padding: 10px; width: 1036px; Margin: 0 auto;">
<div class="content" style="box-sizing: border-box; Margin: 0 auto; max-width: 1037px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Ombi recently added</span>
<table role="presentation" class="main" width="100%" align="center" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #FFF; background: #1f1f1f; border-radius: 3px; width: 100%; text-align: center;">
<!-- START MAIN CONTENT AREA -->
<tbody>
<tr>
<td class="wrapper" valign="top" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tbody>
<tr>
<td valign="top" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top;">
<img src="{@LOGO}" style="border: none; -ms-interpolation-mode: bicubic; max-width: 100%;">
</td>
</tr>
<tr>
<td valign="top" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top;">
<p style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: normal; margin: 0; margin-bottom: 15px;">{@INTRO}</p>
</td>
</tr>
<tr>
<td valign="top" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top;">
{@RECENTLYADDED}
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</tbody>
</table>
<!-- START FOOTER -->
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tbody>
<tr>
<td class="content-block powered-by" valign="top" align="center" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;">
Powered by <a href="https://github.com/tidusjar/Ombi" style="font-weight: 400; font-size: 12px; text-align: center; text-decoration: none; color: #ff761b;">Ombi</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
{@RECENTLYADDED}
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a>
</td>
</tr>
</table>
</tbody>
</table>
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</td>
</tr>
</tbody>
</table>
</body>
</html>
</html>
</html>

@ -17,10 +17,6 @@ namespace Ombi.Notifications
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref)
{
LoadIssues(opts);
if (pref != null)
{
UserPreference = pref.Value;
}
string title;
if (req == null)
@ -41,6 +37,10 @@ namespace Ombi.Notifications
}
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
if (pref != null)
{
UserPreference = pref.Enabled ? pref.Value : Alias;
}
Title = title;
RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty())
@ -50,6 +50,7 @@ namespace Ombi.Notifications
Overview = req?.Overview;
Year = req?.ReleaseDate.Year.ToString();
DenyReason = req?.DeniedReason;
AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty;
if (req?.RequestType == RequestType.Movie)
{
PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase)
@ -66,10 +67,7 @@ namespace Ombi.Notifications
public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref)
{
LoadIssues(opts);
if (pref != null)
{
UserPreference = pref.Enabled ? pref.Value : string.Empty;
}
string title;
if (req == null)
{
@ -88,8 +86,13 @@ namespace Ombi.Notifications
UserName = req?.RequestedUser?.UserName;
}
AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty;
DenyReason = req?.DeniedReason;
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
if (pref != null)
{
UserPreference = pref.Enabled ? pref.Value : Alias;
}
Title = title;
RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty())
@ -114,10 +117,6 @@ namespace Ombi.Notifications
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref)
{
LoadIssues(opts);
if (pref != null)
{
UserPreference = pref.Enabled ? pref.Value : string.Empty;
}
string title;
if (req == null)
{
@ -136,7 +135,12 @@ namespace Ombi.Notifications
// Can be set if it's an issue
UserName = req?.RequestedUser?.UserName;
}
AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty;
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
if (pref != null)
{
UserPreference = pref.Enabled ? pref.Value : Alias;
}
Title = title;
RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty())
@ -237,6 +241,7 @@ namespace Ombi.Notifications
public string NewIssueComment { get; set; }
public string UserPreference { get; set; }
public string DenyReason { get; set; }
public string AvailableDate { get; set; }
// System Defined
private string LongDate => DateTime.Now.ToString("D");
@ -272,6 +277,7 @@ namespace Ombi.Notifications
{nameof(Alias),Alias},
{nameof(UserPreference),UserPreference},
{nameof(DenyReason),DenyReason},
{nameof(AvailableDate),AvailableDate},
};
}
}

@ -7,18 +7,18 @@ namespace Ombi.Schedule.Jobs.Ombi
{
protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url)
{
sb.Append("<td align=\"center\" valign=\"top\" class=\"media-card\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 12px; vertical-align: top; padding: 3px; width: 502px; min-width: 500px; max-width: 500px; height: 235px; \">");
sb.AppendFormat("<table class=\"card-bg\" style=\"background-image: url({0}); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 500px; background-color: #1f1f1f; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 2px solid rgba(255,118,27,.4); height: 248px; max-height: 500px; \">", url);
sb.Append("<td align=\"center\" valign=\"top\" width=\"500\" height=\"252\" class=\"media-card\" style=\"font-size: 14px; font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top; padding: 3px; width: 500px; min-width: 500px; max-width: 500px; height: 252px; max-height: 252px; \">");
sb.AppendFormat("<table class=\"card-bg\" width=\"500\" height=\"252\" background=\"url(0)\" bgcolor=\"#1f1f1f\" style=\"background-image: url(0); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 500px; background-color: #1f1f1f; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 2px solid rgba(255, 118, 27, .4); height: 252px; max-height: 252px; \">", url);
sb.Append("<tr>");
sb.Append("<td>");
sb.Append("<table class=\"bg-tint\" style=\"background-color: rgba(0, 0, 0, .6); position: absolute; width: 490px; height: 239px; \">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
sb.Append("<table class=\"bg-tint\" width=\"100%\" bgcolor=\"rgba(0, 0, 0, .6)\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: rgba(0, 0, 0, .6); \">");
}
protected virtual void AddPosterInsideTable(StringBuilder sb, string url)
{
sb.Append("<tr>");
sb.Append("<td class=\"poster-container\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; width: 150px; min-width: 15px; height: 225px; \">");
sb.AppendFormat("<table class=\"poster-img\" style=\"background-image: url({0}); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: transparent; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 1px solid rgba(255,255,255,.1); \">", url);
sb.Append("<td class=\"poster-container\" width=\"150\" height=\"225\" valign=\"top\" style=\"ont-family: sans-serif; font-size: 14px; vertical-align: top; width: 150px; min-width: 150px; height: 225px; max-height: 225px; min-height: 225px; \">");
sb.AppendFormat("<table class=\"poster-img\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">", url);
}
protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url)
@ -27,10 +27,10 @@ namespace Ombi.Schedule.Jobs.Ombi
{
sb.Append("<tr>");
sb.Append(
"<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
"<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
sb.AppendFormat(
"<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none;-ms-interpolation-mode: bicubic; max-width: 100%;display: block; visibility: hidden; \">",
"<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none; -ms-interpolation-mode: bicubic; max-width: 100%; \">",
url);
sb.Append("</a>");
sb.Append("</td>");
@ -44,16 +44,16 @@ namespace Ombi.Schedule.Jobs.Ombi
protected virtual void AddInfoTable(StringBuilder sb)
{
sb.Append(
"<td class=\"movie-info\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; padding-left: 4px; text-align: left; height: 227px; \">");
"<td class=\"movie-info\" height=\"227\" valign=\"top\" align=\"left\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; padding-left: 4px; text-align: left; height: 227px; \">");
sb.Append("<table style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; height: 100%; \">");
}
protected virtual void AddTitle(StringBuilder sb, string url, string title)
{
sb.Append("<tr>");
sb.Append("<td class=\"title\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.9rem; vertical-align: top; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; line-height: 1.2rem; padding: 5px; \">");
if(url.HasValue()) sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", url);
sb.AppendFormat("<h1 style=\"white-space: normal; line-height: 1;\" >{0}</h1>", title);
sb.Append("<tr class=\"title\" valign=\"top\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 22px;line-height: 24px;vertical-align: top;max-width: 320px;display: block;height: 50px;min-height: 50px;max-height: 50px; \">");
sb.Append("<td>");
if(url.HasValue()) sb.AppendFormat("<a href=\"{0}\" target=\"_blank\" style=\"text-decoration: none; font-weight: 400; color: #ff761b;\">", url);
sb.AppendFormat("<h1 style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 22px;line-height: 24px;vertical-align: top;max-width: 320px;display: block;height: 50px;min-height: 50px;max-height: 50px;\" >{0}</h1>", title);
if (url.HasValue()) sb.Append("</a>");
sb.Append("</td>");
sb.Append("</tr>");
@ -61,30 +61,30 @@ namespace Ombi.Schedule.Jobs.Ombi
protected virtual void AddParagraph(StringBuilder sb, string text)
{
sb.Append("<tr class=\"description\">");
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.75rem; vertical-align: top; padding: 5px; height: 100%; \">");
sb.AppendFormat("<p style=\"color: #fff; font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0; max-width: 325px; text-align: justify; \">{0}</p>", text);
sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
sb.AppendFormat("<p style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: normal; margin: 0; margin-bottom: 15px; \">{0}</p>", text);
sb.Append("</td>");
sb.Append("</tr>");
}
protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary)
{
sb.Append("<tr class=\"description\">");
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.75rem; vertical-align: top; padding: 5px; height: 100%; \">");
sb.AppendFormat("<p style=\"color: #fff; font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0; max-width: 325px; margin-bottom: 10px; \">{0}</p>", episodes);
sb.AppendFormat("<div style=\"color: #fff; font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0; max-width: 325px; overflow: hidden; text-align: justify; \">{0}</div>", summary);
sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
sb.AppendFormat("<p style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: normal; margin: 0; margin-bottom: 15px; \">{0}</p>", episodes);
sb.AppendFormat("<div>{0}</div>", summary);
sb.Append("</td>");
sb.Append("</tr>");
}
protected virtual void AddGenres(StringBuilder sb, string text)
{
sb.Append("<tr class=\"meta\">");
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; max-width: 265px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; \">");
sb.AppendFormat("<span style=\"display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 11px; line-height: 1; text-align: center; white-space: nowrap; vertical-align: middle; background-color: rgba(255, 118, 27, 0.5); color: #fff; border-radius: 2px; text-overflow: ellipsis; overflow: hidden; \">{0}</span>", text);
sb.Append("<tr class=\"meta\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; max-width: 300px; min-width: 300px; padding: 3px 7px; margin-top: 10px; line-height: 1; text-align: left; white-space: nowrap; vertical-align: middle; background-color: rgba(255, 118, 27, 0.5); color: #fff; border-radius: 2px; overflow: hidden; display: block; font-size: 0.9rem;\" align=\"left\" valign=\"middle\" bgcolor=\"rgba(255, 118, 27, 0.5)\">");
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
sb.AppendFormat("<span>{0}</span>", text);
sb.Append("</td>");
sb.Append("</tr>");
}
}
}
}

@ -4,6 +4,7 @@
{
public string BaseUrl { get; set; }
public bool CollectAnalyticData { get; set; }
public bool Set { get; set; }
public bool Wizard { get; set; }
public string ApiKey { get; set; }
public bool IgnoreCertificateErrors { get; set; }

@ -2,7 +2,6 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context;
namespace Ombi.Store.Migrations
@ -14,7 +13,7 @@ namespace Ombi.Store.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
@ -530,6 +529,24 @@ namespace Ombi.Store.Migrations
b.ToTable("RequestQueue");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("RequestId");
b.Property<int>("RequestType");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("RequestSubscription");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b =>
{
b.Property<int>("Id")
@ -800,24 +817,6 @@ namespace Ombi.Store.Migrations
b.ToTable("TvRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("RequestId");
b.Property<int>("RequestType");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("RequestSubscription");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
@ -1082,6 +1081,13 @@ namespace Ombi.Store.Migrations
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
@ -1146,13 +1152,6 @@ namespace Ombi.Store.Migrations
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")

@ -13,7 +13,7 @@ namespace Ombi.Store.Migrations.Settings
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
{

@ -8,10 +8,10 @@ namespace Ombi.Api.TheMovieDb
public interface IMovieDbApi
{
Task<MovieResponseDto> GetMovieInformation(int movieId);
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId);
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en");
Task<List<MovieSearchResult>> NowPlaying();
Task<List<MovieSearchResult>> PopularMovies();
Task<List<MovieSearchResult>> SearchMovie(string searchTerm);
Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string languageCode);
Task<List<TvSearchResult>> SearchTv(string searchTerm);
Task<List<MovieSearchResult>> TopRated();
Task<List<MovieSearchResult>> Upcoming();

@ -73,21 +73,27 @@ namespace Ombi.Api.TheMovieDb
return Mapper.Map<List<MovieSearchResult>>(result.results);
}
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId)
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en")
{
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates");
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
AddRetry(request);
var result = await Api.Request<MovieResponse>(request);
return Mapper.Map<MovieResponseDto>(result);
}
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm)
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string langageCode)
{
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
if (year.HasValue && year.Value > 0)
{
request.FullUri = request.FullUri.AddQueryParameter("year", year.Value.ToString());
}
AddRetry(request);
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);

@ -94,7 +94,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.SickRage", "Ombi.A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Notifications", "Ombi.Api.Notifications\Ombi.Api.Notifications.csproj", "{10D1FE9D-9124-42B7-B1E1-CEB99B832618}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Helpers.Tests", "Ombi.Helpers.Tests\Ombi.Helpers.Tests.csproj", "{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -250,6 +252,10 @@ Global
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.Build.0 = Release|Any CPU
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -286,6 +292,7 @@ Global
{94C9A366-2595-45EA-AABB-8E4A2E90EC5B} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{10D1FE9D-9124-42B7-B1E1-CEB99B832618} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{4FA21A20-92F4-462C-B929-2C517A88CC56} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869}

@ -34,6 +34,12 @@
background: any;
}
export interface ILanguageRefine {
code: string;
name: string;
nativeName: string;
}
export interface ISearchMovieResultContainer {
movies: ISearchMovieResult[];
}

@ -39,7 +39,7 @@ export class MusicRequestsComponent implements OnInit {
public rejectionReason: string;
public totalAlbums: number = 100;
private currentlyLoaded: number;
public currentlyLoaded: number;
private amountToLoad: number;
constructor(

@ -1,11 +1,11 @@
<!-- Movie tab -->
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
<div class="input-group">
<div class="input-group search-bar-background">
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons"
(keyup)="search($event)">
<div class="input-group-addon right-radius">
<div class="btn-group">
<div class="btn-group" role="group">
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
{{ 'Search.Suggestions' | translate }}
<i class="fa fa-chevron-down"></i>
@ -16,10 +16,43 @@
<li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li>
<li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li>
</ul>
<button class="btn btn-sm btn-primary-outline" (click)="refineOpen()">
{{ 'Search.Refine' | translate }}
<i class="fa" [ngClass]="{'fa-chevron-down': !refineSearchEnabled, 'fa-chevron-up': refineSearchEnabled}"></i>
</button>
</div>
<i class="fa fa-search"></i>
</div>
</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>
<!-- <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-9">
<button class="btn pull-right btn-success-outline" (click)="applyRefinedSearch()">Apply</button>
</div>
</div>
<remaining-requests [movie]="true" [quotaRefreshEvents]="movieRequested.asObservable()" #remainingFilms></remaining-requests>
@ -111,11 +144,15 @@
<br />
<div *ngIf="result.available">
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a>
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' | translate}}</a>
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}"
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a>
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline"
href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
translate}}</a>
</div>
<div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled">
<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>

@ -6,12 +6,15 @@ import { Subject } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
import { AuthService } from "../auth/auth.service";
import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../interfaces";
import { IIssueCategory, ILanguageRefine, IRequestEngineResult, ISearchMovieResult } from "../interfaces";
import { NotificationService, RequestService, SearchService } from "../services";
import * as languageData from "../../other/iso-lang.json";
@Component({
selector: "movie-search",
templateUrl: "./moviesearch.component.html",
styleUrls: ["./search.component.scss"],
})
export class MovieSearchComponent implements OnInit {
@ -22,6 +25,10 @@ export class MovieSearchComponent implements OnInit {
public result: IRequestEngineResult;
public searchApplied = false;
public refineSearchEnabled = false;
public searchYear?: number;
public selectedLanguage: string;
public langauges: ILanguageRefine[];
@Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean;
@ -37,23 +44,13 @@ export class MovieSearchComponent implements OnInit {
private notificationService: NotificationService, private authService: AuthService,
private readonly translate: TranslateService, private sanitizer: DomSanitizer,
private readonly platformLocation: PlatformLocation) {
this.langauges = <ILanguageRefine[]><any>languageData;
this.searchChanged.pipe(
debounceTime(600), // Wait Xms after the last event before emitting last event
distinctUntilChanged(), // only emit if value is different from previous value
).subscribe(x => {
this.searchText = x as string;
if (this.searchText === "") {
this.clearResults();
return;
}
this.searchService.searchMovie(this.searchText)
.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();
});
this.runSearch();
});
this.defaultPoster = "../../../images/default_movie_poster.png";
const base = this.platformLocation.getBaseHrefFromDOM();
@ -184,6 +181,18 @@ export class MovieSearchComponent implements OnInit {
});
}
public refineOpen() {
this.refineSearchEnabled = !this.refineSearchEnabled;
if (!this.refineSearchEnabled) {
this.searchYear = undefined;
}
}
public applyRefinedSearch() {
console.log(this.selectedLanguage);
this.runSearch();
}
private getExtraInfo() {
this.movieResults.forEach((val, index) => {
@ -194,10 +203,18 @@ export class MovieSearchComponent implements OnInit {
}
val.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
this.searchService.getMovieInformation(val.id)
if (this.applyRefinedSearch) {
this.searchService.getMovieInformationWithRefined(val.id, this.selectedLanguage)
.subscribe(m => {
this.updateItem(val, m);
});
} else {
this.searchService.getMovieInformation(val.id)
.subscribe(m => {
this.updateItem(val, m);
});
}
});
}
@ -214,4 +231,30 @@ export class MovieSearchComponent implements OnInit {
this.movieResults = [];
this.searchApplied = false;
}
private runSearch() {
if (this.searchText === "") {
this.clearResults();
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();
});
} else {
this.searchService.searchMovie(this.searchText)
.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();
});
}
}
}

@ -0,0 +1,30 @@
@media (max-width: 978px) {
.top-spacing {
padding-top: 5%
}
.form-control-search {
width: 77%;
}
}
@media (min-width: 979px) {
.top-spacing {
padding-top: 2%
}
.form-control-search {
width: 90%;
}
}
.search-bar-background {
background-color: #333333;
}
.vcenter {
display: flex;
align-items: center;
}
.refine-option {
box-shadow: inset 0 1px 5px rgba(0,0,0,1.0);
}

@ -18,8 +18,13 @@ export class SearchService extends ServiceHelpers {
// Movies
public searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/` + searchTerm);
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${searchTerm}`);
}
public searchMovieWithRefined(searchTerm: string, year: number | undefined, langCode: string): Observable<ISearchMovieResult[]> {
return this.http.post<ISearchMovieResult[]>(`${this.url}/Movie/`, { searchTerm, year, languageCode: langCode });
}
public similarMovies(theMovieDbId: number): Observable<ISearchMovieResult[]> {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${theMovieDbId}/similar`);
}
@ -40,34 +45,38 @@ export class SearchService extends ServiceHelpers {
return this.http.get<ISearchMovieResult>(`${this.url}/Movie/info/${theMovieDbId}`);
}
public getMovieInformationWithRefined(theMovieDbId: number, langCode: string): Observable<ISearchMovieResult> {
return this.http.post<ISearchMovieResult>(`${this.url}/Movie/info`, { theMovieDbId, languageCode: langCode });
}
// TV
public searchTv(searchTerm: string): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, {headers: this.headers});
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, { headers: this.headers });
}
public searchTvTreeNode(searchTerm: string): Observable<TreeNode[]> {
return this.http.get<TreeNode[]>(`${this.url}/Tv/${searchTerm}/tree`, {headers: this.headers});
return this.http.get<TreeNode[]>(`${this.url}/Tv/${searchTerm}/tree`, { headers: this.headers });
}
public getShowInformationTreeNode(theTvDbId: number): Observable<TreeNode> {
return this.http.get<TreeNode>(`${this.url}/Tv/info/${theTvDbId}/Tree`, {headers: this.headers});
return this.http.get<TreeNode>(`${this.url}/Tv/info/${theTvDbId}/Tree`, { headers: this.headers });
}
public getShowInformation(theTvDbId: number): Observable<ISearchTvResult> {
return this.http.get<ISearchTvResult>(`${this.url}/Tv/info/${theTvDbId}`, {headers: this.headers});
return this.http.get<ISearchTvResult>(`${this.url}/Tv/info/${theTvDbId}`, { headers: this.headers });
}
public popularTv(): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, {headers: this.headers});
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, { headers: this.headers });
}
public mostWatchedTv(): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/mostwatched`, {headers: this.headers});
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/mostwatched`, { headers: this.headers });
}
public anticipatedTv(): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated`, {headers: this.headers});
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated`, { headers: this.headers });
}
public trendingTv(): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, {headers: this.headers});
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, { headers: this.headers });
}
// Music
public searchArtist(searchTerm: string): Observable<ISearchArtistResult[]> {

@ -71,7 +71,7 @@
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="CollectAnalyticData" name="CollectAnalyticData" formControlName="collectAnalyticData">
<label for="CollectAnalyticData">Allow us to collect anonymous analytical data e.g. browser used</label>
<label for="CollectAnalyticData" tooltipPosition="top" pTooltip="This will allow us to have a better understanding of the userbase so we know what we should be supporting">Allow us to collect anonymous analytical data e.g. browser used</label>
</div>
</div>

@ -23,7 +23,7 @@
<div class="text-center">If you do not yet want to setup a media server you can use the
skip below. You can later setup your media servers via the settings.</div>
<div class="row">
<button routerLink="Wizard/CreateAdmin" data-test="skipbtn" class="btn btn-primary-outline wizard-img"
<button routerLink="/Wizard/CreateAdmin" data-test="skipbtn" class="btn btn-primary-outline wizard-img"
id="plexImg">
Skip
</button>

@ -0,0 +1,184 @@
[
{"code":"ab","name":"Abkhaz","nativeName":"аҧсуа"},
{"code":"aa","name":"Afar","nativeName":"Afaraf"},
{"code":"af","name":"Afrikaans","nativeName":"Afrikaans"},
{"code":"ak","name":"Akan","nativeName":"Akan"},
{"code":"sq","name":"Albanian","nativeName":"Shqip"},
{"code":"am","name":"Amharic","nativeName":"አማርኛ"},
{"code":"ar","name":"Arabic","nativeName":"العربية"},
{"code":"an","name":"Aragonese","nativeName":"Aragonés"},
{"code":"hy","name":"Armenian","nativeName":"Հայերեն"},
{"code":"as","name":"Assamese","nativeName":"অসমীয়া"},
{"code":"av","name":"Avaric","nativeName":"авар мацӀ, магӀарул мацӀ"},
{"code":"ae","name":"Avestan","nativeName":"avesta"},
{"code":"ay","name":"Aymara","nativeName":"aymar aru"},
{"code":"az","name":"Azerbaijani","nativeName":"azərbaycan dili"},
{"code":"bm","name":"Bambara","nativeName":"bamanankan"},
{"code":"ba","name":"Bashkir","nativeName":"башҡорт теле"},
{"code":"eu","name":"Basque","nativeName":"euskara, euskera"},
{"code":"be","name":"Belarusian","nativeName":"Беларуская"},
{"code":"bn","name":"Bengali","nativeName":"বাংলা"},
{"code":"bh","name":"Bihari","nativeName":"भोजपुरी"},
{"code":"bi","name":"Bislama","nativeName":"Bislama"},
{"code":"bs","name":"Bosnian","nativeName":"bosanski jezik"},
{"code":"br","name":"Breton","nativeName":"brezhoneg"},
{"code":"bg","name":"Bulgarian","nativeName":"български език"},
{"code":"my","name":"Burmese","nativeName":"ဗမာစာ"},
{"code":"ca","name":"Catalan; Valencian","nativeName":"Català"},
{"code":"ch","name":"Chamorro","nativeName":"Chamoru"},
{"code":"ce","name":"Chechen","nativeName":"нохчийн мотт"},
{"code":"ny","name":"Chichewa; Chewa; Nyanja","nativeName":"chiCheŵa, chinyanja"},
{"code":"zh","name":"Chinese","nativeName":"中文 (Zhōngwén), 汉语, 漢語"},
{"code":"cv","name":"Chuvash","nativeName":"чӑваш чӗлхи"},
{"code":"kw","name":"Cornish","nativeName":"Kernewek"},
{"code":"co","name":"Corsican","nativeName":"corsu, lingua corsa"},
{"code":"cr","name":"Cree","nativeName":"ᓀᐦᐃᔭᐍᐏᐣ"},
{"code":"hr","name":"Croatian","nativeName":"hrvatski"},
{"code":"cs","name":"Czech","nativeName":"česky, čeština"},
{"code":"da","name":"Danish","nativeName":"dansk"},
{"code":"dv","name":"Divehi; Dhivehi; Maldivian;","nativeName":"ދިވެހި"},
{"code":"nl","name":"Dutch","nativeName":"Nederlands, Vlaams"},
{"code":"en","name":"English","nativeName":"English"},
{"code":"eo","name":"Esperanto","nativeName":"Esperanto"},
{"code":"et","name":"Estonian","nativeName":"eesti, eesti keel"},
{"code":"ee","name":"Ewe","nativeName":"Eʋegbe"},
{"code":"fo","name":"Faroese","nativeName":"føroyskt"},
{"code":"fj","name":"Fijian","nativeName":"vosa Vakaviti"},
{"code":"fi","name":"Finnish","nativeName":"suomi, suomen kieli"},
{"code":"fr","name":"French","nativeName":"français, langue française"},
{"code":"ff","name":"Fula; Fulah; Pulaar; Pular","nativeName":"Fulfulde, Pulaar, Pular"},
{"code":"gl","name":"Galician","nativeName":"Galego"},
{"code":"ka","name":"Georgian","nativeName":"ქართული"},
{"code":"de","name":"German","nativeName":"Deutsch"},
{"code":"el","name":"Greek, Modern","nativeName":"Ελληνικά"},
{"code":"gn","name":"Guaraní","nativeName":"Avañeẽ"},
{"code":"gu","name":"Gujarati","nativeName":"ગુજરાતી"},
{"code":"ht","name":"Haitian; Haitian Creole","nativeName":"Kreyòl ayisyen"},
{"code":"ha","name":"Hausa","nativeName":"Hausa, هَوُسَ"},
{"code":"he","name":"Hebrew (modern)","nativeName":"עברית"},
{"code":"hz","name":"Herero","nativeName":"Otjiherero"},
{"code":"hi","name":"Hindi","nativeName":"हिन्दी, हिंदी"},
{"code":"ho","name":"Hiri Motu","nativeName":"Hiri Motu"},
{"code":"hu","name":"Hungarian","nativeName":"Magyar"},
{"code":"ia","name":"Interlingua","nativeName":"Interlingua"},
{"code":"id","name":"Indonesian","nativeName":"Bahasa Indonesia"},
{"code":"ie","name":"Interlingue","nativeName":"Originally called Occidental; then Interlingue after WWII"},
{"code":"ga","name":"Irish","nativeName":"Gaeilge"},
{"code":"ig","name":"Igbo","nativeName":"Asụsụ Igbo"},
{"code":"ik","name":"Inupiaq","nativeName":"Iñupiaq, Iñupiatun"},
{"code":"io","name":"Ido","nativeName":"Ido"},
{"code":"is","name":"Icelandic","nativeName":"Íslenska"},
{"code":"it","name":"Italian","nativeName":"Italiano"},
{"code":"iu","name":"Inuktitut","nativeName":"ᐃᓄᒃᑎᑐᑦ"},
{"code":"ja","name":"Japanese","nativeName":"日本語 (にほんご/にっぽんご)"},
{"code":"jv","name":"Javanese","nativeName":"basa Jawa"},
{"code":"kl","name":"Kalaallisut, Greenlandic","nativeName":"kalaallisut, kalaallit oqaasii"},
{"code":"kn","name":"Kannada","nativeName":"ಕನ್ನಡ"},
{"code":"kr","name":"Kanuri","nativeName":"Kanuri"},
{"code":"ks","name":"Kashmiri","nativeName":"कश्मीरी, كشميري‎"},
{"code":"kk","name":"Kazakh","nativeName":"Қазақ тілі"},
{"code":"km","name":"Khmer","nativeName":"ភាសាខ្មែរ"},
{"code":"ki","name":"Kikuyu, Gikuyu","nativeName":"Gĩkũyũ"},
{"code":"rw","name":"Kinyarwanda","nativeName":"Ikinyarwanda"},
{"code":"ky","name":"Kirghiz, Kyrgyz","nativeName":"кыргыз тили"},
{"code":"kv","name":"Komi","nativeName":"коми кыв"},
{"code":"kg","name":"Kongo","nativeName":"KiKongo"},
{"code":"ko","name":"Korean","nativeName":"한국어 (韓國語), 조선말 (朝鮮語)"},
{"code":"ku","name":"Kurdish","nativeName":"Kurdî, كوردی‎"},
{"code":"kj","name":"Kwanyama, Kuanyama","nativeName":"Kuanyama"},
{"code":"la","name":"Latin","nativeName":"latine, lingua latina"},
{"code":"lb","name":"Luxembourgish, Letzeburgesch","nativeName":"Lëtzebuergesch"},
{"code":"lg","name":"Luganda","nativeName":"Luganda"},
{"code":"li","name":"Limburgish, Limburgan, Limburger","nativeName":"Limburgs"},
{"code":"ln","name":"Lingala","nativeName":"Lingála"},
{"code":"lo","name":"Lao","nativeName":"ພາສາລາວ"},
{"code":"lt","name":"Lithuanian","nativeName":"lietuvių kalba"},
{"code":"lu","name":"Luba-Katanga","nativeName":"Luba-Katanga"},
{"code":"lv","name":"Latvian","nativeName":"latviešu valoda"},
{"code":"gv","name":"Manx","nativeName":"Gaelg, Gailck"},
{"code":"mk","name":"Macedonian","nativeName":"македонски јазик"},
{"code":"mg","name":"Malagasy","nativeName":"Malagasy fiteny"},
{"code":"ms","name":"Malay","nativeName":"bahasa Melayu, بهاس ملايو‎"},
{"code":"ml","name":"Malayalam","nativeName":"മലയാളം"},
{"code":"mt","name":"Maltese","nativeName":"Malti"},
{"code":"mi","name":"Māori","nativeName":"te reo Māori"},
{"code":"mr","name":"Marathi (Marāṭhī)","nativeName":"मराठी"},
{"code":"mh","name":"Marshallese","nativeName":"Kajin M̧ajeļ"},
{"code":"mn","name":"Mongolian","nativeName":"монгол"},
{"code":"na","name":"Nauru","nativeName":"Ekakairũ Naoero"},
{"code":"nv","name":"Navajo, Navaho","nativeName":"Diné bizaad, Dinékʼehǰí"},
{"code":"nb","name":"Norwegian Bokmål","nativeName":"Norsk bokmål"},
{"code":"nd","name":"North Ndebele","nativeName":"isiNdebele"},
{"code":"ne","name":"Nepali","nativeName":"नेपाली"},
{"code":"ng","name":"Ndonga","nativeName":"Owambo"},
{"code":"nn","name":"Norwegian Nynorsk","nativeName":"Norsk nynorsk"},
{"code":"no","name":"Norwegian","nativeName":"Norsk"},
{"code":"ii","name":"Nuosu","nativeName":"ꆈꌠ꒿ Nuosuhxop"},
{"code":"nr","name":"South Ndebele","nativeName":"isiNdebele"},
{"code":"oc","name":"Occitan","nativeName":"Occitan"},
{"code":"oj","name":"Ojibwe, Ojibwa","nativeName":"ᐊᓂᔑᓈᐯᒧᐎᓐ"},
{"code":"cu","name":"Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic","nativeName":"ѩзыкъ словѣньскъ"},
{"code":"om","name":"Oromo","nativeName":"Afaan Oromoo"},
{"code":"or","name":"Oriya","nativeName":"ଓଡ଼ିଆ"},
{"code":"os","name":"Ossetian, Ossetic","nativeName":"ирон æвзаг"},
{"code":"pa","name":"Panjabi, Punjabi","nativeName":"ਪੰਜਾਬੀ, پنجابی‎"},
{"code":"pi","name":"Pāli","nativeName":"पाऴि"},
{"code":"fa","name":"Persian","nativeName":"فارسی"},
{"code":"pl","name":"Polish","nativeName":"polski"},
{"code":"ps","name":"Pashto, Pushto","nativeName":"پښتو"},
{"code":"pt","name":"Portuguese","nativeName":"Português"},
{"code":"qu","name":"Quechua","nativeName":"Runa Simi, Kichwa"},
{"code":"rm","name":"Romansh","nativeName":"rumantsch grischun"},
{"code":"rn","name":"Kirundi","nativeName":"kiRundi"},
{"code":"ro","name":"Romanian, Moldavian, Moldovan","nativeName":"română"},
{"code":"ru","name":"Russian","nativeName":"русский язык"},
{"code":"sa","name":"Sanskrit (Saṁskṛta)","nativeName":"संस्कृतम्"},
{"code":"sc","name":"Sardinian","nativeName":"sardu"},
{"code":"sd","name":"Sindhi","nativeName":"सिन्धी, سنڌي، سندھی‎"},
{"code":"se","name":"Northern Sami","nativeName":"Davvisámegiella"},
{"code":"sm","name":"Samoan","nativeName":"gagana faa Samoa"},
{"code":"sg","name":"Sango","nativeName":"yângâ tî sängö"},
{"code":"sr","name":"Serbian","nativeName":"српски језик"},
{"code":"gd","name":"Scottish Gaelic; Gaelic","nativeName":"Gàidhlig"},
{"code":"sn","name":"Shona","nativeName":"chiShona"},
{"code":"si","name":"Sinhala, Sinhalese","nativeName":"සිංහල"},
{"code":"sk","name":"Slovak","nativeName":"slovenčina"},
{"code":"sl","name":"Slovene","nativeName":"slovenščina"},
{"code":"so","name":"Somali","nativeName":"Soomaaliga, af Soomaali"},
{"code":"st","name":"Southern Sotho","nativeName":"Sesotho"},
{"code":"es","name":"Spanish; Castilian","nativeName":"español, castellano"},
{"code":"su","name":"Sundanese","nativeName":"Basa Sunda"},
{"code":"sw","name":"Swahili","nativeName":"Kiswahili"},
{"code":"ss","name":"Swati","nativeName":"SiSwati"},
{"code":"sv","name":"Swedish","nativeName":"svenska"},
{"code":"ta","name":"Tamil","nativeName":"தமிழ்"},
{"code":"te","name":"Telugu","nativeName":"తెలుగు"},
{"code":"tg","name":"Tajik","nativeName":"тоҷикӣ, toğikī, تاجیکی‎"},
{"code":"th","name":"Thai","nativeName":"ไทย"},
{"code":"ti","name":"Tigrinya","nativeName":"ትግርኛ"},
{"code":"bo","name":"Tibetan Standard, Tibetan, Central","nativeName":"བོད་ཡིག"},
{"code":"tk","name":"Turkmen","nativeName":"Türkmen, Түркмен"},
{"code":"tl","name":"Tagalog","nativeName":"Wikang Tagalog"},
{"code":"tn","name":"Tswana","nativeName":"Setswana"},
{"code":"to","name":"Tonga (Tonga Islands)","nativeName":"faka Tonga"},
{"code":"tr","name":"Turkish","nativeName":"Türkçe"},
{"code":"ts","name":"Tsonga","nativeName":"Xitsonga"},
{"code":"tt","name":"Tatar","nativeName":"татарча, tatarça, تاتارچا‎"},
{"code":"tw","name":"Twi","nativeName":"Twi"},
{"code":"ty","name":"Tahitian","nativeName":"Reo Tahiti"},
{"code":"ug","name":"Uighur, Uyghur","nativeName":"Uyƣurqə, ئۇيغۇرچە‎"},
{"code":"uk","name":"Ukrainian","nativeName":"українська"},
{"code":"ur","name":"Urdu","nativeName":"اردو"},
{"code":"uz","name":"Uzbek","nativeName":"zbek, Ўзбек, أۇزبېك‎"},
{"code":"ve","name":"Venda","nativeName":"Tshivenḓa"},
{"code":"vi","name":"Vietnamese","nativeName":"Tiếng Việt"},
{"code":"vo","name":"Volapük","nativeName":"Volapük"},
{"code":"wa","name":"Walloon","nativeName":"Walon"},
{"code":"cy","name":"Welsh","nativeName":"Cymraeg"},
{"code":"wo","name":"Wolof","nativeName":"Wollof"},
{"code":"fy","name":"Western Frisian","nativeName":"Frysk"},
{"code":"xh","name":"Xhosa","nativeName":"isiXhosa"},
{"code":"yi","name":"Yiddish","nativeName":"ייִדיש"},
{"code":"yo","name":"Yoruba","nativeName":"Yorùbá"},
{"code":"za","name":"Zhuang, Chuang","nativeName":"Saɯ cueŋƅ, Saw cuengh"}
]

@ -28,3 +28,40 @@ $bg-colour-disabled: #252424;
.label {
margin: 3px;
}
@media (max-width: 924px) {
.navbar-header {
float: none;
}
.navbar-left,.navbar-right {
float: none !important;
}
.navbar-toggle {
display: block;
}
.navbar-collapse {
border-top: 1px solid transparent;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
}
.navbar-fixed-top {
top: 0;
border-width: 0 0 1px;
}
.navbar-collapse.collapse {
display: none!important;
}
.navbar-nav {
float: none!important;
margin-top: 7.5px;
}
.navbar-nav>li {
float: none;
}
.navbar-nav>li>a {
padding-top: 10px;
padding-bottom: 10px;
}
.collapse.in{
display:block !important;
}
}

@ -1,16 +1,15 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Ombi.Api.Lidarr.Models;
using Ombi.Core;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models;
using Ombi.Core.Models.Search;
using Ombi.Models;
using StackExchange.Profiling;
using Microsoft.AspNetCore.Http;
namespace Ombi.Controllers
{
@ -18,7 +17,7 @@ namespace Ombi.Controllers
[ApiV1]
[Produces("application/json")]
[ApiController]
public class SearchController : ControllerBase
public class SearchController : Controller
{
public SearchController(IMovieEngine movie, ITvSearchEngine tvEngine, ILogger<SearchController> logger, IMusicSearchEngine music)
{
@ -40,13 +39,40 @@ namespace Ombi.Controllers
/// <param name="searchTerm">The search term.</param>
/// <returns></returns>
[HttpGet("movie/{searchTerm}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchMovieViewModel>> SearchMovie(string searchTerm)
{
using (MiniProfiler.Current.Step("SearchingMovie"))
{
Logger.LogDebug("Searching : {searchTerm}", searchTerm);
return await MovieEngine.Search(searchTerm);
return await MovieEngine.Search(searchTerm, null, "en");
}
}
/// <summary>
/// Searches for a movie.
/// </summary>
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
/// <param name="model">The refinement model, language code and year are both optional. Language code uses ISO 639-1</param>
/// <returns></returns>
[HttpPost("movie")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesDefaultResponseType]
public async Task<IActionResult> SearchMovie([FromBody] SearchMovieRefineModel model)
{
if (model == null)
{
return BadRequest();
}
using (MiniProfiler.Current.Step("SearchingMovie"))
{
Logger.LogDebug("Searching : {0}, Year: {1}, Lang: {2}", model.SearchTerm, model.Year, model.LanguageCode);
return Json(await MovieEngine.Search(model.SearchTerm, model.Year, model.LanguageCode));
}
}
@ -59,11 +85,35 @@ namespace Ombi.Controllers
/// We use TheMovieDb as the Movie Provider
/// </remarks>
[HttpGet("movie/info/{theMovieDbId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<SearchMovieViewModel> GetExtraMovieInfo(int theMovieDbId)
{
return await MovieEngine.LookupImdbInformation(theMovieDbId);
}
/// <summary>
/// Gets extra information on the movie e.g. IMDBId
/// </summary>
/// <param name="model">TheMovieDb and Language Code, Pass in the language code (ISO 639-1) to get it back in a different lang </param>
/// <returns></returns>
/// <remarks>
/// We use TheMovieDb as the Movie Provider
/// </remarks>
[HttpPost("movie/info")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesDefaultResponseType]
public async Task<IActionResult> GetExtraMovieInfo([FromBody] SearchMovieExtraInfoRefineModel model)
{
if (model == null)
{
return BadRequest();
}
return Json(await MovieEngine.LookupImdbInformation(model.TheMovieDbId, model.LanguageCode));
}
/// <summary>
/// Returns similar movies to the movie id passed in
/// </summary>
@ -72,6 +122,8 @@ namespace Ombi.Controllers
/// We use TheMovieDb as the Movie Provider
/// </remarks>
[HttpGet("movie/{theMovieDbId}/similar")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId)
{
return await MovieEngine.SimilarMovies(theMovieDbId);
@ -83,6 +135,8 @@ namespace Ombi.Controllers
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
/// <returns></returns>
[HttpGet("movie/popular")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchMovieViewModel>> Popular()
{
return await MovieEngine.PopularMovies();
@ -93,6 +147,8 @@ namespace Ombi.Controllers
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
/// <returns></returns>
[HttpGet("movie/nowplaying")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
{
return await MovieEngine.NowPlayingMovies();
@ -103,6 +159,8 @@ namespace Ombi.Controllers
/// <returns></returns>
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
[HttpGet("movie/toprated")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
{
return await MovieEngine.TopRatedMovies();
@ -113,6 +171,8 @@ namespace Ombi.Controllers
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
/// <returns></returns>
[HttpGet("movie/upcoming")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
{
return await MovieEngine.UpcomingMovies();
@ -125,6 +185,8 @@ namespace Ombi.Controllers
/// <remarks>We use TvMaze as the Provider</remarks>
/// <returns></returns>
[HttpGet("tv/{searchTerm}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchTvShowViewModel>> SearchTv(string searchTerm)
{
return await TvEngine.Search(searchTerm);
@ -137,6 +199,8 @@ namespace Ombi.Controllers
/// <remarks>We use TvMaze as the Provider</remarks>
/// <returns></returns>
[HttpGet("tv/info/{tvdbId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<SearchTvShowViewModel> GetShowInfo(int tvdbId)
{
return await TvEngine.GetShowInformation(tvdbId);
@ -148,6 +212,8 @@ namespace Ombi.Controllers
/// <remarks>We use Trakt.tv as the Provider</remarks>
/// <returns></returns>
[HttpGet("tv/popular")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchTvShowViewModel>> PopularTv()
{
return await TvEngine.Popular();
@ -159,6 +225,8 @@ namespace Ombi.Controllers
/// <remarks>We use Trakt.tv as the Provider</remarks>
/// <returns></returns>
[HttpGet("tv/anticipated")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchTvShowViewModel>> AnticipatedTv()
{
return await TvEngine.Anticipated();
@ -171,6 +239,8 @@ namespace Ombi.Controllers
/// <remarks>We use Trakt.tv as the Provider</remarks>
/// <returns></returns>
[HttpGet("tv/mostwatched")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatched()
{
return await TvEngine.MostWatches();
@ -182,6 +252,8 @@ namespace Ombi.Controllers
/// <remarks>We use Trakt.tv as the Provider</remarks>
/// <returns></returns>
[HttpGet("tv/trending")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
{
return await TvEngine.Trending();
@ -193,6 +265,8 @@ namespace Ombi.Controllers
/// <remarks>We use Lidarr as the Provider</remarks>
/// <returns></returns>
[HttpGet("music/artist/{searchTerm}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string searchTerm)
{
return await MusicEngine.SearchArtist(searchTerm);
@ -204,6 +278,8 @@ namespace Ombi.Controllers
/// <remarks>We use Lidarr as the Provider</remarks>
/// <returns></returns>
[HttpGet("music/album/{searchTerm}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string searchTerm)
{
return await MusicEngine.SearchAlbum(searchTerm);
@ -215,6 +291,8 @@ namespace Ombi.Controllers
/// <remarks>We use Lidarr as the Provider</remarks>
/// <returns></returns>
[HttpGet("music/artist/album/{foreignArtistId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IEnumerable<SearchAlbumViewModel>> GetAlbumsByArtist(string foreignArtistId)
{
return await MusicEngine.GetArtistAlbums(foreignArtistId);

@ -0,0 +1,8 @@
namespace Ombi.Models
{
public class SearchMovieExtraInfoRefineModel
{
public int TheMovieDbId { get; set; }
public string LanguageCode { get; set; } = "en";
}
}

@ -0,0 +1,9 @@
namespace Ombi.Models
{
public class SearchMovieRefineModel
{
public string SearchTerm { get; set; }
public int? Year { get; set; }
public string LanguageCode { get; set; } = "en";
}
}

@ -1,14 +1,14 @@
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;linux-x64;linux-arm64;</RuntimeIdentifiers>
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssemblyVersion>$(SemVer)</AssemblyVersion>
<FileVersion>$(SemVer)</FileVersion>
<Version>$(FullVer)</Version>
<PackageVersion></PackageVersion>
<TypeScriptToolsVersion>3.0</TypeScriptToolsVersion>
<TypeScriptToolsVersion>3.1</TypeScriptToolsVersion>
</PropertyGroup>
<PropertyGroup>
<ServerGarbageCollection>false</ServerGarbageCollection>

@ -175,6 +175,15 @@ namespace Ombi
{
// Generate a API Key
settings.ApiKey = Guid.NewGuid().ToString("N");
settings.CollectAnalyticData = true; // Since this is a first setup, enable analytical data collection
settings.Set = true;
ombiService.SaveSettings(settings);
}
if (!settings.Set)
{
settings.Set = true;
settings.CollectAnalyticData = true;
ombiService.SaveSettings(settings);
}

@ -93,7 +93,19 @@
<meta name="theme-color" content="#df691a">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
@if (s?.CollectAnalyticData ?? true)
{
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89270627-3"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-89270627-3');
</script>
}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

@ -42,8 +42,8 @@
"angular2-template-loader": "^0.6.2",
"aspnet-webpack": "^3.0.0",
"awesome-typescript-loader": "^5.2.0",
"bootstrap": "3.3.7",
"bootswatch": "3.3.7",
"bootstrap": "3.4.0",
"bootswatch": "3.4.0",
"copy-webpack-plugin": "^4.5.2",
"core-js": "^2.5.7",
"css": "^2.2.3",
@ -60,6 +60,7 @@
"jquery": "^3.3.1",
"mini-css-extract-plugin": "^0.4.1",
"moment": "^2.22.2",
"natives": "1.1.6",
"ng2-cookies": "^1.0.12",
"ngx-clipboard": "^11.1.1",
"ngx-infinite-scroll": "^6.0.1",

@ -2,3 +2,8 @@
declare module "pace-progress";
declare var __webpack_public_path__: any;
declare module "*.json" {
const value: any;
export default value;
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Se på Emby",
"RequestAdded": "{{title}} er anmodet med succes",
"Similar": "Lignende",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Populære film",
"UpcomingMovies": "Kommende film",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Stemt",
"VotesTab": "Nødvendige stemmer"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "In Emby anschauen",
"RequestAdded": "Anfrage für {{title}} wurde erfolgreich hinzugefügt",
"Similar": "Ähnliche",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Beliebte Filme",
"UpcomingMovies": "Kommende Filme",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Bewertet",
"VotesTab": "Erforderliche Bewertungen"
}
}
}

@ -78,6 +78,7 @@
"ViewOnEmby": "View On Emby",
"RequestAdded": "Request for {{title}} has been added successfully",
"Similar":"Similar",
"Refine":"Refine",
"Movies": {
"PopularMovies": "Popular Movies",
"UpcomingMovies": "Upcoming Movies",

@ -74,6 +74,7 @@
"ViewOnEmby": "Ver en Emby",
"RequestAdded": "La solicitud de {{title}} se ha agregado con éxito",
"Similar": "Similar",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Películas populares",
"UpcomingMovies": "Próximas películas",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Regarder sur Emby",
"RequestAdded": "La demande pour {{title}} a été ajoutée avec succès",
"Similar": "Similaires",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Films populaires",
"UpcomingMovies": "Films à venir",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voté",
"VotesTab": "Votes nécessaires"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Guarda su Emby",
"RequestAdded": "La richiesta per {{title}} è stata aggiunta correttamente",
"Similar": "Similar",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Film popolari",
"UpcomingMovies": "Film in arrivo",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
}
}
}

@ -12,8 +12,8 @@
"Common": {
"ContinueButton": "Doorgaan",
"Available": "Beschikbaar",
"PartiallyAvailable": "Partially Available",
"Monitored": "Monitored",
"PartiallyAvailable": "Deels Beschikbaar",
"Monitored": "Gecontroleerd",
"NotAvailable": "Niet Beschikbaar",
"ProcessingRequest": "Verzoek wordt verwerkt",
"PendingApproval": "Wacht op goedkeuring",
@ -25,7 +25,7 @@
"Approve": "Accepteer",
"PartlyAvailable": "Deels Beschikbaar",
"Errors": {
"Validation": "Fout: Controleer de ingevulde waardes"
"Validation": "Controleer de ingevulde waardes"
}
},
"PasswordReset": {
@ -48,7 +48,7 @@
"Requests": "Verzoeklijst",
"UserManagement": "Gebruikersbeheer",
"Issues": "Problemen",
"Vote": "Vote",
"Vote": "Stem",
"Donate": "Doneer!",
"DonateLibraryMaintainer": "Doneren aan bibliotheek beheerder",
"DonateTooltip": "Zo heb ik mijn vrouw overtuigd dat ik Ombi mag ontwikkelen ;)",
@ -56,9 +56,9 @@
"Settings": "Instellingen",
"Welcome": "Welkom {{username}}",
"UpdateDetails": "Update gegevens",
"Logout": "Logout",
"Logout": "Afmelden",
"OpenMobileApp": "Open Mobiele App",
"RecentlyAdded": "Recently Added"
"RecentlyAdded": "Onlangs Toegevoegd"
},
"Search": {
"Title": "Zoeken",
@ -68,12 +68,13 @@
"MusicTab": "Muziek",
"Suggestions": "Suggesties",
"NoResults": "Sorry, we hebben geen resultaten gevonden!",
"DigitalDate": "Digital Release: {{date}}",
"TheatricalRelease": "Theatrical Release: {{date}}",
"DigitalDate": "Digitale Uitgave: {{date}}",
"TheatricalRelease": "Bioscoop Uitgave: {{date}}",
"ViewOnPlex": "Bekijk op Plex",
"ViewOnEmby": "Bekijk op Emby",
"RequestAdded": "Aanvraag voor {{title}} is succesvol toegevoegd",
"Similar": "Similar",
"Similar": "Vergelijkbaar",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Populaire films",
"UpcomingMovies": "Aankomende Films",
@ -108,10 +109,10 @@
"Status": "Status:",
"RequestStatus": "Aanvraagstatus:",
"Denied": " Geweigerd:",
"TheatricalRelease": "Theatrical Release: {{date}}",
"ReleaseDate": "Released: {{date}}",
"TheatricalReleaseSort": "Theatrical Release",
"DigitalRelease": "Digital Release: {{date}}",
"TheatricalRelease": "Cinema Uitgave: {{date}}",
"ReleaseDate": "Uitgekomen: {{date}}",
"TheatricalReleaseSort": "Bioscoop Uitgave",
"DigitalRelease": "Digitale Uitgave: {{date}}",
"RequestDate": "Aanvraag Datum:",
"QualityOverride": "Kwaliteit overschrijven:",
"RootFolderOverride": "Hoofdmap overschrijven:",
@ -127,20 +128,20 @@
"GridStatus": "Status",
"ReportIssue": "Probleem Melden",
"Filter": "Filter",
"Sort": "Sort",
"Sort": "Sorteer",
"SeasonNumberHeading": "Seizoen: {seasonNumber}",
"SortTitleAsc": "Titel ▲",
"SortTitleDesc": "Titel ▼",
"SortRequestDateAsc": "Request Date ▲",
"SortRequestDateDesc": "Request Date ▼",
"SortRequestDateAsc": "Aanvraag Datum ▲",
"SortRequestDateDesc": "Aanvraag Datum ▼",
"SortStatusAsc": "Status ▲",
"SortStatusDesc": "Status ▼",
"Remaining": {
"Quota": "{{remaining}}/{{total}} requests remaining",
"NextDays": "Another request will be added in {{time}} days",
"NextHours": "Another request will be added in {{time}} hours",
"NextMinutes": "Another request will be added in {{time}} minutes",
"NextMinute": "Another request will be added in {{time}} minute"
"Quota": "{{remaining}}/{{total}} resterende aanvragen",
"NextDays": "Een ander verzoek zal worden toegevoegd in {{time}} Dagen",
"NextHours": "Een ander verzoek zal worden toegevoegd in {{time}} Uren",
"NextMinutes": "Een ander verzoek zal worden toegevoegd in {{time}} Minuten",
"NextMinute": "Een ander verzoek zal worden toegevoegd in {{time}} Minuut"
}
},
"Issues": {
@ -167,18 +168,18 @@
"FilterHeaderAvailability": "Beschikbaarheid",
"FilterHeaderRequestStatus": "Status",
"Approved": "Goedgekeurd",
"PendingApproval": "Pending Approval"
"PendingApproval": "In afwachting van goedkeuring"
},
"UserManagment": {
"TvRemaining": "TV: {{remaining}}/{{total}} remaining",
"MovieRemaining": "Movies: {{remaining}}/{{total}} remaining",
"MusicRemaining": "Music: {{remaining}}/{{total}} remaining",
"TvDue": "TV: {{date}}",
"TvRemaining": "Tv: {{remaining}}/{{total}} Resterend",
"MovieRemaining": "Tv: {{remaining}}/{{total}} Resterend",
"MusicRemaining": "Muziek: {{remaining}}/{{total}} Resterend",
"TvDue": "Tv: {{date}}",
"MovieDue": "Film: {{date}}",
"MusicDue": "Muziek: {{date}}"
},
"Votes": {
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
"CompletedVotesTab": "Gestemd",
"VotesTab": "Stemmen nodig"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Spill av på Emby",
"RequestAdded": "Forespørsel om {{title}} er lagt til",
"Similar": "Lignende",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Populære filmer",
"UpcomingMovies": "Kommende filmer",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Obejrzyj na Emby",
"RequestAdded": "Zgłoszenie dla {{title}} zostało dodane",
"Similar": "Podobne",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Popularne filmy",
"UpcomingMovies": "Wkrótce w kinach",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Assistir no Emby",
"RequestAdded": "Pedido de {{title}} foi adicionado com sucesso",
"Similar": "Semelhante",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Filmes populares",
"UpcomingMovies": "Próximos filmes",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
}
}
}

@ -74,6 +74,7 @@
"ViewOnEmby": "Visa på Emby",
"RequestAdded": "Efterfrågan om {{title}} har lagts till",
"Similar": "Liknande",
"Refine": "Refine",
"Movies": {
"PopularMovies": "Populära filmer",
"UpcomingMovies": "Kommande filmer",
@ -181,4 +182,4 @@
"CompletedVotesTab": "Voted",
"VotesTab": "Votes Needed"
}
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save