pull/1085/head
Jamie.Rees 8 years ago
commit 4cf4b33305

@ -9,6 +9,7 @@ namespace Ombi.Api.Interfaces
EmbyItemContainer<EmbyMovieItem> GetAllMovies(string apiKey, string userId, Uri baseUri);
EmbyItemContainer<EmbySeriesItem> GetAllShows(string apiKey, string userId, Uri baseUri);
EmbyItemContainer<EmbyEpisodeItem> GetAllEpisodes(string apiKey, string userId, Uri baseUri);
EmbyItemContainer<EmbyMovieInformation> GetCollection(string mediaId, string apiKey, string userId, Uri baseUrl);
List<EmbyUser> GetUsers(Uri baseUri, string apiKey);
EmbyItemContainer<EmbyLibrary> ViewLibrary(string apiKey, string userId, Uri baseUri);
EmbyInformation GetInformation(string mediaId, EmbyMediaType type, string apiKey, string userId, Uri baseUri);

@ -49,7 +49,7 @@ namespace Ombi.Api.Models.Emby
public object[] Taglines { get; set; }
public object[] Genres { get; set; }
public string[] SeriesGenres { get; set; }
public int CommunityRating { get; set; }
public float CommunityRating { get; set; }
public int VoteCount { get; set; }
public long RunTimeTicks { get; set; }
public string PlayAccess { get; set; }

@ -100,9 +100,9 @@ namespace Ombi.Api
var obj = RetryHandler.Execute<CouchPotatoStatus>(() => Api.Execute<CouchPotatoStatus>(request, url),
(exception, timespan) => Log.Error(exception, "Exception when calling GetStatus for CP, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (2),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)});
TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)});
return obj;
}
@ -140,9 +140,9 @@ namespace Ombi.Api
{
var obj = RetryHandler.Execute(() => Api.Execute<CouchPotatoMovies>(request, baseUrl),
(exception, timespan) => Log.Error(exception, "Exception when calling GetMovies for CP, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(30)
TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(5)
});
return obj;

@ -103,6 +103,27 @@ namespace Ombi.Api
return GetAll<EmbyEpisodeItem>("Episode", apiKey, userId, baseUri);
}
public EmbyItemContainer<EmbyMovieInformation> GetCollection(string mediaId, string apiKey, string userId, Uri baseUrl)
{
var request = new RestRequest
{
Resource = "emby/users/{userId}/items?parentId={mediaId}",
Method = Method.GET
};
request.AddUrlSegment("userId", userId);
request.AddUrlSegment("mediaId", mediaId);
AddHeaders(request, apiKey);
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetCollections for Emby, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(5)
});
return policy.Execute(() => Api.ExecuteJson<EmbyItemContainer<EmbyMovieInformation>>(request, baseUrl));
}
public EmbyInformation GetInformation(string mediaId, EmbyMediaType type, string apiKey, string userId, Uri baseUri)
{
var request = new RestRequest

@ -30,6 +30,7 @@
using System.Data;
using NLog;
using Ombi.Core.SettingModels;
using Ombi.Store;
namespace Ombi.Core.Migration.Migrations
{
@ -52,8 +53,15 @@ namespace Ombi.Core.Migration.Migrations
public void Start(IDbConnection con)
{
UpdatePlexSettings();
//UpdateCustomSettings(); Turned off the migration for now until the search has been improved on.
//UpdateSchema(con, Version);
UpdateCustomSettings();
AddNewColumns(con);
UpdateSchema(con, Version);
}
private void AddNewColumns(IDbConnection con)
{
con.AlterTable("EmbyContent", "ADD", "AddedAt", true, "VARCHAR(50)");
con.AlterTable("EmbyEpisodes", "ADD", "AddedAt", true, "VARCHAR(50)");
}
private void UpdatePlexSettings()
@ -68,7 +76,7 @@ namespace Ombi.Core.Migration.Migrations
{
var settings = Customization.GetSettings();
settings.NewSearch = true; // Use the new search
settings.EnableIssues = true;
Customization.SaveSettings(settings);

@ -45,6 +45,8 @@ namespace Ombi.Core
public const string CouchPotatoQualityProfiles = nameof(CouchPotatoQualityProfiles);
public const string CouchPotatoQueued = nameof(CouchPotatoQueued);
public const string WatcherQueued = nameof(WatcherQueued);
public const string GetCustomizationSettings = nameof(GetCustomizationSettings);
public const string GetEmbySettings = nameof(GetEmbySettings);
public const string GetPlexRequestSettings = nameof(GetPlexRequestSettings);
public const string LastestProductVersion = nameof(LastestProductVersion);
public const string SonarrRootFolders = nameof(SonarrRootFolders);

@ -144,7 +144,7 @@
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td align="center">
<img src="http://i.imgur.com/ROTp8mn.png" text-align="center" />
<img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
</td>
</tr>
<tr>

@ -124,6 +124,7 @@
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\DiscordNotificationSettings.cs" />
<Compile Include="SettingModels\EmbySettings.cs" />
<Compile Include="SettingModels\MassEmailSettings.cs" />
<Compile Include="SettingModels\RadarrSettings.cs" />
<Compile Include="SettingModels\WatcherSettings.cs" />
<Compile Include="SettingModels\ExternalSettings.cs" />

@ -54,6 +54,7 @@ namespace Ombi.Core.SettingModels
public int DefaultLang { get; set; }
public bool NewSearch { get; set; }
public bool EnableIssues { get; set; }
}
}

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EmailNotificationSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace Ombi.Core.SettingModels
{
public sealed class MassEmailSettings : NotificationSettings
{
public string Users { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
}

@ -0,0 +1,12 @@
using Quartz;
namespace Ombi.Services.Jobs
{
public interface IMassEmail
{
void Execute(IJobExecutionContext context);
void MassEmailAdminTest(string html, string subject);
void SendMassEmail(string html, string subject);
}
}

@ -5,7 +5,7 @@ namespace Ombi.Services.Jobs
public interface IRecentlyAdded
{
void Execute(IJobExecutionContext context);
void Test();
void Start();
void RecentlyAddedAdminTest();
void StartNewsLetter();
}
}

@ -286,7 +286,9 @@ namespace Ombi.Services.Jobs
var ep = await EpisodeRepo.CustomAsync(async connection =>
{
connection.Open();
var result = await connection.QueryAsync<EmbyEpisodes>("select * from EmbyEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId });
var result = await connection.QueryAsync<EmbyEpisodes>(@"select ee.* from EmbyEpisodes ee inner join EmbyContent ec
on ee.ParentId = ec.EmbyId
where ec.ProviderId = @ProviderId", new { ProviderId = theTvDbId });
return result;
});

@ -46,7 +46,7 @@ namespace Ombi.Services.Jobs
public class EmbyContentCacher : IJob, IEmbyContentCacher
{
public EmbyContentCacher(ISettingsService<EmbySettings> embySettings, IRequestService request, IEmbyApi emby, ICacheProvider cache,
IJobRecord rec, IRepository<EmbyEpisodes> repo,IRepository<EmbyContent> content)
IJobRecord rec, IRepository<EmbyEpisodes> repo, IRepository<EmbyContent> content)
{
Emby = embySettings;
RequestService = request;
@ -108,35 +108,23 @@ namespace Ombi.Services.Jobs
foreach (var m in movies)
{
var movieInfo = EmbyApi.GetInformation(m.Id, EmbyMediaType.Movie, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
if (string.IsNullOrEmpty(movieInfo.ProviderIds.Imdb))
if (m.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase))
{
Log.Error("Provider Id on movie {0} is null", movieInfo.Name);
continue;
var info = EmbyApi.GetCollection(m.Id, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri);
foreach (var item in info.Items)
{
var movieInfo = EmbyApi.GetInformation(item.Id, EmbyMediaType.Movie, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
ProcessMovies(movieInfo);
}
}
// Check if it exists
var item = EmbyContent.Custom(connection =>
else
{
connection.Open();
var media = connection.QueryFirstOrDefault<EmbyContent>("select * from EmbyContent where ProviderId = @ProviderId and type = @type", new { ProviderId = movieInfo.ProviderIds.Imdb, type = 0 });
connection.Dispose();
return media;
});
var movieInfo = EmbyApi.GetInformation(m.Id, EmbyMediaType.Movie, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
if (item == null)
{
// Doesn't exist, insert it
EmbyContent.Insert(new EmbyContent
{
ProviderId = movieInfo.ProviderIds.Imdb,
PremierDate = movieInfo.PremiereDate,
Title = movieInfo.Name,
Type = Store.Models.Plex.EmbyMediaType.Movie,
EmbyId = m.Id
});
ProcessMovies(movieInfo);
}
}
@ -170,7 +158,8 @@ namespace Ombi.Services.Jobs
PremierDate = tvInfo.PremiereDate,
Title = tvInfo.Name,
Type = Store.Models.Plex.EmbyMediaType.Series,
EmbyId = t.Id
EmbyId = t.Id,
AddedAt = DateTime.UtcNow
});
}
}
@ -216,7 +205,7 @@ namespace Ombi.Services.Jobs
}
}
private bool ValidateSettings(EmbySettings emby)
{
@ -249,5 +238,36 @@ namespace Ombi.Services.Jobs
Job.SetRunning(false, JobNames.EmbyCacher);
}
}
private void ProcessMovies(EmbyMovieInformation movieInfo)
{
if (string.IsNullOrEmpty(movieInfo.ProviderIds.Imdb))
{
Log.Error("Provider Id on movie {0} is null", movieInfo.Name);
return;
}
// Check if it exists
var item = EmbyContent.Custom(connection =>
{
connection.Open();
var media = connection.QueryFirstOrDefault<EmbyContent>("select * from EmbyContent where ProviderId = @ProviderId and type = @type", new { ProviderId = movieInfo.ProviderIds.Imdb, type = 0 });
connection.Dispose();
return media;
});
if (item == null)
{
// Doesn't exist, insert it
EmbyContent.Insert(new EmbyContent
{
ProviderId = movieInfo.ProviderIds.Imdb,
PremierDate = movieInfo.PremiereDate,
Title = movieInfo.Name,
Type = Store.Models.Plex.EmbyMediaType.Movie,
EmbyId = movieInfo.Id,
AddedAt = DateTime.UtcNow
});
}
}
}
}

@ -28,6 +28,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dapper;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.Emby;
@ -65,7 +66,8 @@ namespace Ombi.Services.Jobs
private const string TableName = "EmbyEpisodes";
// Note, once an episode exists, we store it and it always exists.
// We might want to look at checking if something has been removed from the server in the future.
public void CacheEpisodes(EmbySettings settings)
{
var allEpisodes = EmbyApi.GetAllEpisodes(settings.ApiKey, settings.AdministratorId, settings.FullUri);
@ -74,21 +76,40 @@ namespace Ombi.Services.Jobs
{
var epInfo = EmbyApi.GetInformation(ep.Id, EmbyMediaType.Episode, settings.ApiKey,
settings.AdministratorId, settings.FullUri);
model.Add(new EmbyEpisodes
if (epInfo.EpisodeInformation?.ProviderIds?.Tvdb == null)
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumber,
SeasonNumber = ep.ParentIndexNumber,
EpisodeTitle = ep.Name,
ParentId = ep.SeriesId,
ShowTitle = ep.SeriesName,
ProviderId = epInfo.EpisodeInformation.ProviderIds.Tmdb
});
}
continue;
}
// Delete all of the current items
Repo.DeleteAll(TableName);
// Check it this episode exists
var item = Repo.Custom(connection =>
{
connection.Open();
var media =
connection.QueryFirstOrDefault<EmbyEpisodes>(
"select * from EmbyEpisodes where ProviderId = @ProviderId",
new {ProviderId = epInfo.EpisodeInformation?.ProviderIds?.Tvdb});
connection.Dispose();
return media;
});
if (item == null)
{
// add it
model.Add(new EmbyEpisodes
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumber,
SeasonNumber = ep.ParentIndexNumber,
EpisodeTitle = ep.Name,
ParentId = ep.SeriesId,
ShowTitle = ep.SeriesName,
ProviderId = epInfo.EpisodeInformation.ProviderIds.Tvdb,
AddedAt = DateTime.UtcNow
});
}
}
// Insert the new items
var result = Repo.BatchInsert(model, TableName, typeof(EmbyEpisodes).GetPropertyNames());
@ -108,15 +129,6 @@ namespace Ombi.Services.Jobs
return;
}
var jobs = Job.GetJobs();
var job = jobs.FirstOrDefault(x => x.Name.Equals(JobNames.EmbyEpisodeCacher, StringComparison.CurrentCultureIgnoreCase));
if (job != null)
{
if (job.LastRun > DateTime.Now.AddHours(-11)) // If it's been run in the last 11 hours
{
return;
}
}
Job.SetRunning(true, JobNames.EmbyEpisodeCacher);
CacheEpisodes(s);
}
@ -141,15 +153,6 @@ namespace Ombi.Services.Jobs
return;
}
var jobs = Job.GetJobs();
var job = jobs.FirstOrDefault(x => x.Name.Equals(JobNames.EmbyEpisodeCacher, StringComparison.CurrentCultureIgnoreCase));
if (job != null)
{
if (job.LastRun > DateTime.Now.AddHours(-11)) // If it's been run in the last 11 hours
{
return;
}
}
Job.SetRunning(true, JobNames.EmbyEpisodeCacher);
CacheEpisodes(s);
}

@ -0,0 +1,343 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: RecentlyAddedModel.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using Ombi.Api;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.Emby;
using Ombi.Core;
using Ombi.Core.SettingModels;
using Ombi.Services.Jobs.Templates;
using Ombi.Store.Models;
using Ombi.Store.Models.Emby;
using Ombi.Store.Repository;
using EmbyMediaType = Ombi.Store.Models.Plex.EmbyMediaType;
namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
public class EmbyAddedNewsletter : HtmlTemplateGenerator, IEmbyAddedNewsletter
{
public EmbyAddedNewsletter(IEmbyApi api, ISettingsService<EmbySettings> embySettings,
ISettingsService<EmailNotificationSettings> email,
ISettingsService<NewletterSettings> newsletter, IRepository<RecentlyAddedLog> log,
IRepository<EmbyContent> embyContent, IRepository<EmbyEpisodes> episodes)
{
Api = api;
EmbySettings = embySettings;
EmailSettings = email;
NewsletterSettings = newsletter;
Content = embyContent;
MovieApi = new TheMovieDbApi();
TvApi = new TvMazeApi();
Episodes = episodes;
RecentlyAddedLog = log;
}
private IEmbyApi Api { get; }
private TheMovieDbApi MovieApi { get; }
private TvMazeApi TvApi { get; }
private ISettingsService<EmbySettings> EmbySettings { get; }
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
private IRepository<EmbyContent> Content { get; }
private IRepository<EmbyEpisodes> Episodes { get; }
private IRepository<RecentlyAddedLog> RecentlyAddedLog { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public string GetNewsletterHtml(bool test)
{
try
{
return GetHtml(test);
}
catch (Exception e)
{
Log.Error(e);
return string.Empty;
}
}
private class EmbyRecentlyAddedModel
{
public EmbyInformation EmbyInformation { get; set; }
public EmbyContent EmbyContent { get; set; }
public List<EmbyEpisodeInformation> EpisodeInformation { get; set; }
}
private string GetHtml(bool test)
{
var sb = new StringBuilder();
var embySettings = EmbySettings.GetSettings();
var embyContent = Content.GetAll().ToList();
var series = embyContent.Where(x => x.Type == EmbyMediaType.Series).ToList();
var episodes = Episodes.GetAll().ToList();
var movie = embyContent.Where(x => x.Type == EmbyMediaType.Movie).ToList();
var recentlyAdded = RecentlyAddedLog.GetAll().ToList();
var firstRun = !recentlyAdded.Any();
var filteredMovies = movie.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var filteredEp = episodes.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var info = new List<EmbyRecentlyAddedModel>();
foreach (var m in filteredMovies)
{
var i = Api.GetInformation(m.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Movie,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
info.Add(new EmbyRecentlyAddedModel
{
EmbyInformation = i,
EmbyContent = m
});
}
GenerateMovieHtml(info, sb);
info.Clear();
foreach (var t in series)
{
var i = Api.GetInformation(t.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Series,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
var ep = filteredEp.Where(x => x.ParentId == t.EmbyId);
if (ep.Any())
{
var episodeList = new List<EmbyEpisodeInformation>();
foreach (var embyEpisodese in ep)
{
var epInfo = Api.GetInformation(embyEpisodese.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Episode,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
episodeList.Add(epInfo.EpisodeInformation);
}
info.Add(new EmbyRecentlyAddedModel
{
EmbyContent = t,
EmbyInformation = i,
EpisodeInformation = episodeList
});
}
}
GenerateTvHtml(info, sb);
var template = new RecentlyAddedTemplate();
var html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
if (!test || firstRun)
{
foreach (var a in filteredMovies)
{
RecentlyAddedLog.Insert(new RecentlyAddedLog
{
ProviderId = a.ProviderId,
AddedAt = DateTime.UtcNow
});
}
foreach (var a in filteredEp)
{
RecentlyAddedLog.Insert(new RecentlyAddedLog
{
ProviderId = a.ProviderId,
AddedAt = DateTime.UtcNow
});
}
}
var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml);
return escapedHtml;
}
private void GenerateMovieHtml(IEnumerable<EmbyRecentlyAddedModel> movies, StringBuilder sb)
{
if (!movies.Any())
{
return;
}
var orderedMovies = movies.OrderByDescending(x => x.EmbyContent.AddedAt).Select(x => x.EmbyInformation.MovieInformation).ToList();
sb.Append("<h1>New Movies:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
foreach (var movie in orderedMovies)
{
try
{
var imdbId = movie.ProviderIds.Imdb;
var info = MovieApi.GetMovieInformation(imdbId).Result;
if (info == null)
{
throw new Exception($"Movie with Imdb id {imdbId} returned null from the MovieApi");
}
AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}");
sb.Append("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/");
Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? string.Empty}");
EndTag(sb, "a");
if (info.Genres.Any())
{
AddParagraph(sb,
$"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}");
}
AddParagraph(sb, info.Overview);
}
catch (Exception e)
{
Log.Error(e);
Log.Error("Error for movie with IMDB Id = {0}", movie.ProviderIds.Imdb);
}
finally
{
EndLoopHtml(sb);
}
}
sb.Append("</table><br /><br />");
}
private class TvModel
{
public EmbySeriesInformation Series { get; set; }
public List<EmbyEpisodeInformation> Episodes { get; set; }
}
private void GenerateTvHtml(List<EmbyRecentlyAddedModel> tv, StringBuilder sb)
{
if (!tv.Any())
{
return;
}
var orderedTv = tv.OrderByDescending(x => x.EmbyContent.AddedAt).ToList();
// TV
sb.Append("<h1>New Episodes:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
foreach (var t in orderedTv)
{
var seriesItem = t.EmbyInformation.SeriesInformation;
var relatedEpisodes = t.EpisodeInformation;
try
{
var info = TvApi.ShowLookupByTheTvDbId(int.Parse(seriesItem.ProviderIds.Tvdb));
var banner = info.image?.original;
if (!string.IsNullOrEmpty(banner))
{
banner = banner.Replace("http", "https"); // Always use the Https banners
}
AddImageInsideTable(sb, banner);
sb.Append("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
var title = $"{seriesItem.Name} {seriesItem.PremiereDate.Year}";
Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/");
Header(sb, 3, title);
EndTag(sb, "a");
var results = relatedEpisodes.GroupBy(p => p.ParentIndexNumber,
(key, g) => new
{
ParentIndexNumber = key,
IndexNumber = g.ToList()
}
);
// Group the episodes
foreach (var embyEpisodeInformation in results.OrderBy(x => x.ParentIndexNumber))
{
var epSb = new StringBuilder();
for (var i = 0; i < embyEpisodeInformation.IndexNumber.Count; i++)
{
var ep = embyEpisodeInformation.IndexNumber[i];
if (i < embyEpisodeInformation.IndexNumber.Count)
{
epSb.Append($"{ep.IndexNumber},");
}
else
{
epSb.Append(ep);
}
}
AddParagraph(sb, $"Season: {embyEpisodeInformation.ParentIndexNumber}, Episode: {epSb}");
}
if (info.genres.Any())
{
AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}");
}
AddParagraph(sb, string.IsNullOrEmpty(seriesItem.Overview) ? info.summary : seriesItem.Overview);
}
catch (Exception e)
{
Log.Error(e);
}
finally
{
EndLoopHtml(sb);
}
}
sb.Append("</table><br /><br />");
}
private void EndLoopHtml(StringBuilder sb)
{
//NOTE: BR have to be in TD's as per html spec or it will be put outside of the table...
//Source: http://stackoverflow.com/questions/6588638/phantom-br-tag-rendered-by-browsers-prior-to-table-tag
sb.Append("<hr />");
sb.Append("<br />");
sb.Append("<br />");
sb.Append("</td>");
sb.Append("</tr>");
}
}
}

@ -0,0 +1,7 @@
namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
public interface IEmbyAddedNewsletter
{
string GetNewsletterHtml(bool test);
}
}

@ -46,14 +46,15 @@ using Ombi.Services.Interfaces;
using Ombi.Services.Jobs.Templates;
using Quartz;
namespace Ombi.Services.Jobs
namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
public class RecentlyAdded : HtmlTemplateGenerator, IJob, IRecentlyAdded
public class RecentlyAddedNewsletter : HtmlTemplateGenerator, IJob, IRecentlyAdded, IMassEmail
{
public RecentlyAdded(IPlexApi api, ISettingsService<PlexSettings> plexSettings,
public RecentlyAddedNewsletter(IPlexApi api, ISettingsService<PlexSettings> plexSettings,
ISettingsService<EmailNotificationSettings> email, IJobRecord rec,
ISettingsService<NewletterSettings> newsletter,
IPlexReadOnlyDatabase db, IUserHelper userHelper)
IPlexReadOnlyDatabase db, IUserHelper userHelper, IEmbyAddedNewsletter embyNews,
ISettingsService<EmbySettings> embyS)
{
JobRecord = rec;
Api = api;
@ -62,23 +63,25 @@ namespace Ombi.Services.Jobs
NewsletterSettings = newsletter;
PlexDb = db;
UserHelper = userHelper;
EmbyNewsletter = embyNews;
EmbySettings = embyS;
}
private IPlexApi Api { get; }
private TvMazeApi TvApi = new TvMazeApi();
private readonly TheMovieDbApi _movieApi = new TheMovieDbApi();
private const int MetadataTypeTv = 4;
private const int MetadataTypeMovie = 1;
private ISettingsService<PlexSettings> PlexSettings { get; }
private ISettingsService<EmbySettings> EmbySettings { get; }
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
private IJobRecord JobRecord { get; }
private IPlexReadOnlyDatabase PlexDb { get; }
private IUserHelper UserHelper { get; }
private IEmbyAddedNewsletter EmbyNewsletter { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public void Start()
public void StartNewsLetter()
{
try
{
@ -88,7 +91,7 @@ namespace Ombi.Services.Jobs
return;
}
JobRecord.SetRunning(true, JobNames.RecentlyAddedEmail);
Start(settings);
StartNewsLetter(settings);
}
catch (Exception e)
{
@ -102,109 +105,135 @@ namespace Ombi.Services.Jobs
}
public void Execute(IJobExecutionContext context)
{
Start();
StartNewsLetter();
}
public void Test()
public void RecentlyAddedAdminTest()
{
Log.Debug("Starting Test Newsletter");
Log.Debug("Starting Recently Added Newsletter Test");
var settings = NewsletterSettings.GetSettings();
Start(settings, true);
StartNewsLetter(settings, true);
}
private void Start(NewletterSettings newletterSettings, bool testEmail = false)
public void MassEmailAdminTest(string html, string subject)
{
var sb = new StringBuilder();
var plexSettings = PlexSettings.GetSettings();
Log.Debug("Got Plex Settings");
var libs = Api.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
Log.Debug("Getting Plex Library Sections");
Log.Debug("Starting Mass Email Test");
var template = new MassEmailTemplate();
var body = template.LoadTemplate(html);
SendMassEmail(body, subject, true);
}
var tvSections = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib
Log.Debug("Filtered sections for TV");
var movieSection = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib
Log.Debug("Filtered sections for Movies");
public void SendMassEmail(string html, string subject)
{
Log.Debug("Starting Mass Email Test");
var template = new MassEmailTemplate();
var body = template.LoadTemplate(html);
SendMassEmail(body, subject, false);
}
var plexVersion = Api.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri).Version;
private void StartNewsLetter(NewletterSettings newletterSettings, bool testEmail = false)
{
var embySettings = EmbySettings.GetSettings();
if (embySettings.Enable)
{
var html = EmbyNewsletter.GetNewsletterHtml(testEmail);
var html = string.Empty;
if (plexVersion.StartsWith("1.3"))
var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml);
SendNewsletter(newletterSettings, escapedHtml, testEmail);
}
else
{
var tvMetadata = new List<Metadata>();
var movieMetadata = new List<Metadata>();
foreach (var tvSection in tvSections)
var sb = new StringBuilder();
var plexSettings = PlexSettings.GetSettings();
Log.Debug("Got Plex Settings");
var libs = Api.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
Log.Debug("Getting Plex Library Sections");
var tvSections = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib
Log.Debug("Filtered sections for TV");
var movieSection = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib
Log.Debug("Filtered sections for Movies");
var plexVersion = Api.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri).Version;
var html = string.Empty;
if (plexVersion.StartsWith("1.3"))
{
var item = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri,
tvSection?.Key);
if (item?.MediaContainer?.Metadata != null)
var tvMetadata = new List<Metadata>();
var movieMetadata = new List<Metadata>();
foreach (var tvSection in tvSections)
{
tvMetadata.AddRange(item?.MediaContainer?.Metadata);
var item = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri,
tvSection?.Key);
if (item?.MediaContainer?.Metadata != null)
{
tvMetadata.AddRange(item?.MediaContainer?.Metadata);
}
}
}
Log.Debug("Got RecentlyAdded TV Shows");
foreach (var movie in movieSection)
{
var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key);
if (recentlyAddedMovies?.MediaContainer?.Metadata != null)
Log.Debug("Got RecentlyAdded TV Shows");
foreach (var movie in movieSection)
{
movieMetadata.AddRange(recentlyAddedMovies?.MediaContainer?.Metadata);
var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key);
if (recentlyAddedMovies?.MediaContainer?.Metadata != null)
{
movieMetadata.AddRange(recentlyAddedMovies?.MediaContainer?.Metadata);
}
}
Log.Debug("Got RecentlyAdded Movies");
Log.Debug("Started Generating Movie HTML");
GenerateMovieHtml(movieMetadata, plexSettings, sb);
Log.Debug("Finished Generating Movie HTML");
Log.Debug("Started Generating TV HTML");
GenerateTvHtml(tvMetadata, plexSettings, sb);
Log.Debug("Finished Generating TV HTML");
var template = new RecentlyAddedTemplate();
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
}
Log.Debug("Got RecentlyAdded Movies");
Log.Debug("Started Generating Movie HTML");
GenerateMovieHtml(movieMetadata, plexSettings, sb);
Log.Debug("Finished Generating Movie HTML");
Log.Debug("Started Generating TV HTML");
GenerateTvHtml(tvMetadata, plexSettings, sb);
Log.Debug("Finished Generating TV HTML");
var template = new RecentlyAddedTemplate();
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
}
else
{
// Old API
var tvChild = new List<RecentlyAddedChild>();
var movieChild = new List<RecentlyAddedChild>();
foreach (var tvSection in tvSections)
else
{
var recentlyAddedTv = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection?.Key);
if (recentlyAddedTv?._children != null)
// Old API
var tvChild = new List<RecentlyAddedChild>();
var movieChild = new List<RecentlyAddedChild>();
foreach (var tvSection in tvSections)
{
tvChild.AddRange(recentlyAddedTv?._children);
var recentlyAddedTv = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection?.Key);
if (recentlyAddedTv?._children != null)
{
tvChild.AddRange(recentlyAddedTv?._children);
}
}
}
Log.Debug("Got RecentlyAdded TV Shows");
foreach (var movie in movieSection)
{
var recentlyAddedMovies = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key);
if (recentlyAddedMovies?._children != null)
Log.Debug("Got RecentlyAdded TV Shows");
foreach (var movie in movieSection)
{
tvChild.AddRange(recentlyAddedMovies?._children);
var recentlyAddedMovies = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key);
if (recentlyAddedMovies?._children != null)
{
tvChild.AddRange(recentlyAddedMovies?._children);
}
}
Log.Debug("Got RecentlyAdded Movies");
Log.Debug("Started Generating Movie HTML");
GenerateMovieHtml(movieChild, plexSettings, sb);
Log.Debug("Finished Generating Movie HTML");
Log.Debug("Started Generating TV HTML");
GenerateTvHtml(tvChild, plexSettings, sb);
Log.Debug("Finished Generating TV HTML");
var template = new RecentlyAddedTemplate();
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
}
Log.Debug("Got RecentlyAdded Movies");
Log.Debug("Started Generating Movie HTML");
GenerateMovieHtml(movieChild, plexSettings, sb);
Log.Debug("Finished Generating Movie HTML");
Log.Debug("Started Generating TV HTML");
GenerateTvHtml(tvChild, plexSettings, sb);
Log.Debug("Finished Generating TV HTML");
var template = new RecentlyAddedTemplate();
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml);
SendNewsletter(newletterSettings, escapedHtml, testEmail);
}
string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml);
Send(newletterSettings, escapedHtml, plexSettings, testEmail);
}
private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb)
@ -439,9 +468,49 @@ namespace Ombi.Services.Jobs
sb.Append("</table><br /><br />");
}
private void Send(NewletterSettings newletterSettings, string html, PlexSettings plexSettings, bool testEmail = false)
private void SendMassEmail(string html, string subject, bool testEmail)
{
var settings = EmailSettings.GetSettings();
if (!settings.Enabled || string.IsNullOrEmpty(settings.EmailHost))
{
return;
}
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = subject
};
Log.Debug("Created Plain/HTML MIME body");
if (!testEmail)
{
var users = UserHelper.GetUsers(); // Get all users
if (users != null)
{
foreach (var user in users)
{
if (!string.IsNullOrEmpty(user.EmailAddress))
{
message.Bcc.Add(new MailboxAddress(user.Username, user.EmailAddress)); // BCC everyone
}
}
}
}
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin
message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender));
SendMail(settings, message);
}
// TODO Emby
private void SendNewsletter(NewletterSettings newletterSettings, string html, bool testEmail = false, string subject = "New Content on Plex!")
{
Log.Debug("Entering Send");
Log.Debug("Entering SendNewsletter");
var settings = EmailSettings.GetSettings();
if (!settings.Enabled || string.IsNullOrEmpty(settings.EmailHost))
@ -454,7 +523,7 @@ namespace Ombi.Services.Jobs
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = "New Content on Plex!",
Subject = subject
};
Log.Debug("Created Plain/HTML MIME body");
@ -488,6 +557,11 @@ namespace Ombi.Services.Jobs
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin
message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender));
SendMail(settings, message);
}
private void SendMail(EmailNotificationSettings settings, MimeMessage message)
{
try
{
using (var client = new SmtpClient())

@ -0,0 +1,58 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: RecentlyAddedTemplate.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using NLog;
namespace Ombi.Services.Jobs.Templates
{
public class MassEmailTemplate
{
public string TemplateLocation => Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, "Jobs", "Templates", "MassEmailTemplate.html");
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private const string RecentlyAddedKey = "{@MASSEMAIL}";
public string LoadTemplate(string html)
{
try
{
var sb = new StringBuilder(File.ReadAllText(TemplateLocation));
sb.Replace(RecentlyAddedKey, html);
return sb.ToString();
}
catch (Exception e)
{
Log.Error(e);
return string.Empty;
}
}
}
}

@ -0,0 +1,181 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Ombi</title>
<style media="all" type="text/css">
@media all {
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
@media all {
.btn-secondary a:hover {
border-color: #34495e !important;
color: #34495e !important;
}
}
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] h2 {
font-size: 22px !important;
margin-bottom: 10px !important;
}
table[class=body] h3 {
font-size: 16px !important;
margin-bottom: 10px !important;
}
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: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .header {
margin-bottom: 10px !important;
}
table[class=body] .main {
border-left-width: 0 !important;
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;
}
table[class=body] .alert td {
border-radius: 0 !important;
padding: 10px !important;
}
table[class=body] .span-2,
table[class=body] .span-3 {
max-width: none !important;
width: 100% !important;
}
table[class=body] .receipt {
width: 100% !important;
}
}
@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;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
}
</style>
</head>
<body class="" style="font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f6f6f6; margin: 0; padding: 0;">
<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: #f6f6f6;" width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto !important; max-width: 580px; padding: 10px; width: 580px;" width="580" valign="top">
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; 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; width: 100%; background: #fff; border-radius: 3px;" width="100%">
<!-- 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; width: 100%;" width="100%">
<tr>
<td align="center">
<img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
</td>
</tr>
<tr>
<td align="left">
{@MASSEMAIL}
</td>
</tr>
</table>
</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%;" 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>
</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>
</table>
</body>
</html>

@ -144,14 +144,14 @@
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td align="center">
<img src="http://i.imgur.com/ROTp8mn.png" text-align="center" />
<img src="http://i.imgur.com/qQsN78U.png" 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="font-family: sans-serif; font-size: 20px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Here is a list of Movies and TV Shows that have recently been added to Plex!</p>
<p style="font-family: sans-serif; font-size: 20px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Here is a list of Movies and TV Shows that have recently been added!</p>
</td>
</tr>

@ -87,6 +87,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Interfaces\IEmbyNotificationEngine.cs" />
<Compile Include="Interfaces\IMassEmail.cs" />
<Compile Include="Interfaces\IPlexNotificationEngine.cs" />
<Compile Include="Interfaces\IRadarrCacher.cs" />
<Compile Include="Interfaces\IWatcherCacher.cs" />
@ -107,6 +108,9 @@
<Compile Include="Jobs\EmbyEpisodeCacher.cs" />
<Compile Include="Jobs\EmbyUserChecker.cs" />
<Compile Include="Jobs\RadarrCacher.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\EmbyRecentlyAddedNewsletter.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\IEmbyAddedNewsletter.cs" />
<Compile Include="Jobs\Templates\MassEmailTemplate.cs" />
<Compile Include="Jobs\WatcherCacher.cs" />
<Compile Include="Jobs\HtmlTemplateGenerator.cs" />
<Compile Include="Interfaces\IPlexContentCacher.cs" />
@ -115,7 +119,7 @@
<Compile Include="Jobs\JobNames.cs" />
<Compile Include="Jobs\PlexContentCacher.cs" />
<Compile Include="Jobs\PlexEpisodeCacher.cs" />
<Compile Include="Jobs\RecentlyAdded.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\RecentlyAddedNewsletter.cs" />
<Compile Include="Jobs\StoreBackup.cs" />
<Compile Include="Jobs\PlexUserChecker.cs" />
<Compile Include="Jobs\StoreCleanup.cs" />
@ -180,6 +184,9 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Jobs\Templates\MassEmailTemplate.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Jobs\Templates\RecentlyAddedTemplate.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

@ -39,5 +39,6 @@ namespace Ombi.Store.Models.Emby
public DateTime PremierDate { get; set; }
public string ProviderId { get; set; }
public EmbyMediaType Type { get; set; }
public DateTime AddedAt { get; set; }
}
}

@ -25,6 +25,7 @@
// ************************************************************************/
#endregion
using System;
using Dapper.Contrib.Extensions;
namespace Ombi.Store.Models.Emby
@ -39,5 +40,6 @@ namespace Ombi.Store.Models.Emby
public int SeasonNumber { get; set; }
public string ParentId { get; set; }
public string ProviderId { get; set; }
public DateTime AddedAt { get; set; }
}
}

@ -0,0 +1,40 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: LogEntity.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using Dapper.Contrib.Extensions;
using Newtonsoft.Json;
namespace Ombi.Store.Models
{
[Table("RecentlyAddedLog")]
public class RecentlyAddedLog : Entity
{
public string ProviderId { get; set; }
public DateTime AddedAt { get; set; }
}
}

@ -68,6 +68,7 @@
<Compile Include="Models\Emby\EmbyContent.cs" />
<Compile Include="Models\Emby\EmbyEpisodes.cs" />
<Compile Include="Models\IssueBlobs.cs" />
<Compile Include="Models\RecenetlyAddedLog.cs" />
<Compile Include="Models\Plex\PlexEpisodes.cs" />
<Compile Include="Models\Emby\EmbyUsers.cs" />
<Compile Include="Models\Plex\PlexUsers.cs" />

@ -187,7 +187,8 @@ CREATE TABLE IF NOT EXISTS EmbyEpisodes
SeasonNumber INTEGER NOT NULL,
EpisodeNumber INTEGER NOT NULL,
ParentId VARCHAR(100) NOT NULL,
ProviderId VARCHAR(100) NOT NULL
ProviderId VARCHAR(100) NOT NULL,
AddedAt VARCHAR(100) NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS EmbyEpisodes_Id ON EmbyEpisodes (Id);
@ -198,9 +199,21 @@ CREATE TABLE IF NOT EXISTS EmbyContent
PremierDate VARCHAR(100) NOT NULL,
EmbyId VARCHAR(100) NOT NULL,
ProviderId VARCHAR(100) NOT NULL,
Type INTEGER NOT NULL
Type INTEGER NOT NULL,
AddedAt VARCHAR(100) NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS EmbyEpisodes_Id ON EmbyEpisodes (Id);
CREATE TABLE IF NOT EXISTS RecentlyAddedLog
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
ProviderId VARCHAR(100) NOT NULL,
AddedAt VARCHAR(100) NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS RecentlyAddedLog_Id ON RecentlyAddedLog (Id);
CREATE INDEX IF NOT EXISTS RecentlyAddedLog_ProviderId ON RecentlyAddedLog (ProviderId);
COMMIT;

@ -333,6 +333,7 @@ namespace Ombi.UI.Helpers
{
url = $"/{content}{url}";
}
var returnString = context.Request.Path == url ?
$"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>"
: $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>";
@ -347,7 +348,14 @@ namespace Ombi.UI.Helpers
{
url = $"/{content}{url}";
}
if (url.Contains("issues"))
{
var custom = GetCustomizationSettings();
if (!custom.EnableIssues)
{
return helper.Raw(string.Empty);
}
}
var returnString = context.Request.Path == url
? $"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>"
: $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>";
@ -365,6 +373,12 @@ namespace Ombi.UI.Helpers
return helper.Raw(GetCustomizationSettings().ApplicationName);
}
public static IHtmlString GetMediaServerName(this HtmlHelpers helper)
{
var s = GetEmbySettings();
return helper.Raw(s.Enable ? "Emby" : "Plex");
}
private static string GetBaseUrl()
{
return GetSettings().BaseUrl;
@ -382,7 +396,7 @@ namespace Ombi.UI.Helpers
private static CustomizationSettings GetCustomizationSettings()
{
var returnValue = Cache.GetOrSet(CacheKeys.GetPlexRequestSettings, () =>
var returnValue = Cache.GetOrSet(CacheKeys.GetCustomizationSettings, () =>
{
var settings = Locator.Resolve<ISettingsService<CustomizationSettings>>().GetSettings();
return settings;
@ -390,6 +404,16 @@ namespace Ombi.UI.Helpers
return returnValue;
}
private static EmbySettings GetEmbySettings()
{
var returnValue = Cache.GetOrSet(CacheKeys.GetEmbySettings, () =>
{
var settings = Locator.Resolve<ISettingsService<EmbySettings>>().GetSettings();
return settings;
});
return returnValue;
}
private static string GetLinkUrl(string assetLocation)
{
return string.IsNullOrEmpty(assetLocation) ? string.Empty : $"{assetLocation}";

@ -35,6 +35,7 @@ using Ombi.Core;
using Ombi.Core.SettingModels;
using Ombi.Services.Interfaces;
using Ombi.Services.Jobs;
using Ombi.Services.Jobs.RecentlyAddedNewsletter;
using Ombi.UI.Helpers;
using Quartz;
using Quartz.Impl;
@ -70,7 +71,7 @@ namespace Ombi.UI.Jobs
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build(),
JobBuilder.Create<RecentlyAddedNewsletter>().WithIdentity("RecentlyAddedModel", "Email").Build(),
JobBuilder.Create<FaultQueueHandler>().WithIdentity("FaultQueueHandler", "Fault").Build(),
JobBuilder.Create<RadarrCacher>().WithIdentity("RadarrCacher", "Cache").Build(),
@ -304,7 +305,7 @@ namespace Ombi.UI.Jobs
var embyEpisode =
TriggerBuilder.Create()
.WithIdentity("EmbyEpisodeCacher", "Emby")
//.StartAt(DateBuilder.FutureDate(10, IntervalUnit.Minute))
//.StartNow()
.StartAt(DateBuilder.FutureDate(10, IntervalUnit.Minute))
.WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyEpisodeCacher).RepeatForever())
.Build();

@ -32,6 +32,8 @@ namespace Ombi.UI.Models
public class SearchLoadViewModel
{
public PlexRequestSettings Settings { get; set; }
public bool Plex { get; set; }
public bool Emby { get; set; }
public CustomizationSettings CustomizationSettings { get; set; }
}
}

@ -42,6 +42,7 @@ using Nancy.Validation;
using NLog;
using Ombi.Api;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.Movie;
using Ombi.Core;
using Ombi.Core.Models;
using Ombi.Core.SettingModels;
@ -92,6 +93,7 @@ namespace Ombi.UI.Modules.Admin
private IJobRecord JobRecorder { get; }
private IAnalytics Analytics { get; }
private IRecentlyAdded RecentlyAdded { get; }
private IMassEmail MassEmail { get; }
private ISettingsService<NotificationSettingsV2> NotifySettings { get; }
private ISettingsService<DiscordNotificationSettings> DiscordSettings { get; }
private IDiscordApi DiscordApi { get; }
@ -123,7 +125,7 @@ namespace Ombi.UI.Modules.Admin
ICacheProvider cache, ISettingsService<SlackNotificationSettings> slackSettings,
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded,
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded, IMassEmail massEmail,
ISettingsService<WatcherSettings> watcherSettings ,
ISettingsService<DiscordNotificationSettings> discord,
IDiscordApi discordapi, ISettingsService<RadarrSettings> settings, IRadarrApi radarrApi,
@ -158,6 +160,7 @@ namespace Ombi.UI.Modules.Admin
Analytics = analytics;
NotifySettings = notifyService;
RecentlyAdded = recentlyAdded;
MassEmail = massEmail;
WatcherSettings = watcherSettings;
DiscordSettings = discord;
DiscordApi = discordapi;
@ -222,6 +225,9 @@ namespace Ombi.UI.Modules.Admin
Get["/newsletter", true] = async (x, ct) => await Newsletter();
Post["/newsletter", true] = async (x, ct) => await SaveNewsletter();
Post["/testnewsletteradminemail"] = x => TestNewsletterAdminEmail();
Post["/testmassadminemail"] = x => TestMassAdminEmail();
Post["/sendmassemail"] = x => SendMassEmail();
Post["/createapikey"] = x => CreateApiKey();
@ -246,7 +252,6 @@ namespace Ombi.UI.Modules.Admin
Get["/notificationsettings", true] = async (x, ct) => await NotificationSettings();
Post["/notificationsettings"] = x => SaveNotificationSettings();
Post["/recentlyAddedTest"] = x => RecentlyAddedTest();
}
private async Task<Negotiator> Authentication()
@ -441,11 +446,15 @@ namespace Ombi.UI.Modules.Admin
private async Task<Response> SavePlex()
{
var plexSettings = this.Bind<PlexSettings>();
var valid = this.Validate(plexSettings);
if (!valid.IsValid)
{
return Response.AsJson(valid.SendJsonError());
}
if (plexSettings.Enable)
{
var valid = this.Validate(plexSettings);
if (!valid.IsValid)
{
return Response.AsJson(valid.SendJsonError());
}
}
if (plexSettings.Enable)
@ -462,7 +471,7 @@ namespace Ombi.UI.Modules.Admin
}
}
if (string.IsNullOrEmpty(plexSettings.MachineIdentifier))
if (string.IsNullOrEmpty(plexSettings.MachineIdentifier) && plexSettings.Enable)
{
//Lookup identifier
var server = PlexApi.GetServer(plexSettings.PlexAuthToken);
@ -815,6 +824,10 @@ namespace Ombi.UI.Modules.Admin
{
return Response.AsJson(valid.SendJsonError());
}
if (!settings.Enabled)
{
return Response.AsJson(new CouchPotatoProfiles{list = new List<ProfileList>()});
}
var profiles = CpApi.GetProfiles(settings.FullUri, settings.ApiKey);
// set the cache
@ -1133,6 +1146,8 @@ namespace Ombi.UI.Modules.Admin
var emby = await EmbySettings.GetSettingsAsync();
var plex = await PlexService.GetSettingsAsync();
var dict = new Dictionary<string, DateTime>();
@ -1145,7 +1160,24 @@ namespace Ombi.UI.Modules.Admin
}
else
{
dict.Add(j.Name,j.LastRun);
if (j.Name.Contains("Plex"))
{
if (plex.Enable)
{
dict.Add(j.Name, j.LastRun);
}
}
else if (j.Name.Contains("Emby"))
{
if (emby.Enable)
{
dict.Add(j.Name, j.LastRun);
}
}
else
{
dict.Add(j.Name, j.LastRun);
}
}
}
@ -1166,7 +1198,13 @@ namespace Ombi.UI.Modules.Admin
FaultQueueHandler = s.FaultQueueHandler,
PlexEpisodeCacher = s.PlexEpisodeCacher,
PlexUserChecker = s.PlexUserChecker,
UserRequestLimitResetter = s.UserRequestLimitResetter
UserRequestLimitResetter = s.UserRequestLimitResetter,
EmbyAvailabilityChecker = s.EmbyAvailabilityChecker,
EmbyContentCacher = s.EmbyContentCacher,
EmbyEpisodeCacher = s.EmbyEpisodeCacher,
EmbyUserChecker = s.EmbyUserChecker,
RadarrCacher = s.RadarrCacher,
WatcherCacher = s.WatcherCacher
};
return View["SchedulerSettings", model];
}
@ -1229,13 +1267,35 @@ namespace Ombi.UI.Modules.Admin
var model = this.Bind<NotificationSettingsV2>();
return View["NotificationSettings", model];
}
private Response RecentlyAddedTest()
private Response TestNewsletterAdminEmail()
{
try
{
Log.Debug("Clicked Admin Newsletter Email Test");
RecentlyAdded.RecentlyAddedAdminTest();
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to administrator" });
}
catch (Exception e)
{
Log.Error(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });
}
}
private Response TestMassAdminEmail()
{
try
{
Log.Debug("Clicked TEST");
RecentlyAdded.Test();
var settings = this.Bind<MassEmailSettings>();
Log.Debug("Clicked Admin Mass Email Test");
if (settings.Subject == null) {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Subject" });
}
if (settings.Body == null)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Body" });
}
MassEmail.MassEmailAdminTest(settings.Body.Replace("\n", "<br/>"), settings.Subject);
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to administrator" });
}
catch (Exception e)
@ -1244,5 +1304,28 @@ namespace Ombi.UI.Modules.Admin
return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });
}
}
private Response SendMassEmail()
{
try
{
var settings = this.Bind<MassEmailSettings>();
Log.Debug("Clicked Admin Mass Email Test");
if (settings.Subject == null)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Subject" });
}
if (settings.Body == null)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Body" });
}
MassEmail.SendMassEmail(settings.Body.Replace("\n", "<br/>"), settings.Subject);
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to All users" });
}
catch (Exception e)
{
Log.Error(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });
}
}
}
}

@ -156,7 +156,7 @@ namespace Ombi.UI.Modules.Admin
var cp = await CpSettings.GetSettingsAsync();
if (cp.Enabled)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "CouchPotato is enabled, we cannot enable Watcher and CouchPotato" });
return Response.AsJson(new JsonResponseModel { Result = false, Message = "CouchPotato is enabled, we cannot enable Radarr and CouchPotato" });
}
var valid = this.Validate(radarrSettings);

@ -142,7 +142,7 @@ namespace Ombi.UI.Modules.Admin
}
if (key.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase))
{
RecentlyAdded.Start();
RecentlyAdded.StartNewsLetter();
}
if (key.Equals(JobNames.FaultQueueHandler, StringComparison.CurrentCultureIgnoreCase))
{

@ -69,7 +69,7 @@ namespace Ombi.UI.Modules
{
public class SearchModule : BaseAuthModule
{
public SearchModule(ICacheProvider cache,
public SearchModule(ICacheProvider cache,
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker plexChecker,
IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings,
ISettingsService<SickRageSettings> sickRageService, ISickRageApi srApi,
@ -141,7 +141,7 @@ namespace Ombi.UI.Modules
async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons);
Post["request/tvEpisodes", true] = async (x, ct) => await RequestTvShow(0, "episode");
Post["request/album", true] = async (x, ct) => await RequestAlbum((string)Request.Form.albumId);
Get["/seasons"] = x => GetSeasons();
Get["/episodes", true] = async (x, ct) => await GetEpisodes();
}
@ -187,10 +187,14 @@ namespace Ombi.UI.Modules
var settings = await PrService.GetSettingsAsync();
var custom = await CustomizationSettings.GetSettingsAsync();
var emby = await EmbySettings.GetSettingsAsync();
var plex = await PlexService.GetSettingsAsync();
var searchViewModel = new SearchLoadViewModel
{
Settings = settings,
CustomizationSettings = custom
CustomizationSettings = custom,
Emby = emby.Enable,
Plex = plex.Enable
};
@ -300,11 +304,11 @@ namespace Ombi.UI.Modules
VoteAverage = movie.VoteAverage,
VoteCount = movie.VoteCount
};
if (counter <= 5) // Let's only do it for the first 5 items
{
var movieInfo = MovieApi.GetMovieInformationWithVideos(movie.Id);
// TODO needs to be careful about this, it's adding extra time to search...
// https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2
viewMovie.ImdbId = movieInfo?.imdb_id;
@ -400,11 +404,11 @@ namespace Ombi.UI.Modules
case ShowSearchType.Popular:
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Popular", Username, CookieHelper.GetAnalyticClientId(Cookies));
var popularShows = await TraktApi.GetPopularShows();
foreach (var popularShow in popularShows)
{
var theTvDbId = int.Parse(popularShow.Ids.Tvdb.ToString());
var model = new SearchTvShowViewModel
{
FirstAired = popularShow.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
@ -577,6 +581,7 @@ namespace Ombi.UI.Modules
Analytics.TrackEventAsync(Category.Search, Action.TvShow, searchTerm, Username,
CookieHelper.GetAnalyticClientId(Cookies));
var plexSettings = await PlexService.GetSettingsAsync();
var embySettings = await EmbySettings.GetSettingsAsync();
var prSettings = await PrService.GetSettingsAsync();
var providerId = string.Empty;
@ -600,6 +605,8 @@ namespace Ombi.UI.Modules
var sickRageCache = SickRageCacher.QueuedIds(); // consider just merging sonarr/sickrage arrays
var content = PlexContentRepository.GetAll();
var plexTvShows = PlexChecker.GetPlexTvShows(content);
var embyContent = EmbyContentRepository.GetAll();
var embyCached = EmbyChecker.GetEmbyTvShows(embyContent);
var viewTv = new List<SearchTvShowViewModel>();
foreach (var t in apiTv)
@ -633,20 +640,28 @@ namespace Ombi.UI.Modules
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason)
};
providerId = viewT.Id.ToString();
if (plexSettings.AdvancedSearch)
if (embySettings.Enable)
{
providerId = viewT.Id.ToString();
var embyShow = EmbyChecker.GetTvShow(embyCached.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4), providerId);
if (embyShow != null)
{
viewT.Available = true;
}
}
var plexShow = PlexChecker.GetTvShow(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4),
providerId);
if (plexShow != null)
if (plexSettings.Enable)
{
viewT.Available = true;
viewT.PlexUrl = plexShow.Url;
var plexShow = PlexChecker.GetTvShow(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4),
providerId);
if (plexShow != null)
{
viewT.Available = true;
viewT.PlexUrl = plexShow.Url;
}
}
else if (t.show?.externals?.thetvdb != null)
if (t.show?.externals?.thetvdb != null && !viewT.Available)
{
var tvdbid = (int)t.show.externals.thetvdb;
if (dbTv.ContainsKey(tvdbid))
@ -747,7 +762,7 @@ namespace Ombi.UI.Modules
Message = "You have reached your weekly request limit for Movies! Please contact your admin."
});
}
var embySettings = await EmbySettings.GetSettingsAsync();
Analytics.TrackEventAsync(Category.Search, Action.Request, "Movie", Username,
CookieHelper.GetAnalyticClientId(Cookies));
var movieInfo = await MovieApi.GetMovieInformation(movieId);
@ -806,7 +821,7 @@ namespace Ombi.UI.Modules
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullMovieName)
Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullMovieName,GetMediaServerName())
});
}
//#endif
@ -851,7 +866,7 @@ namespace Ombi.UI.Modules
}
if (!result.MovieSendingEnabled)
{
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
@ -946,7 +961,7 @@ namespace Ombi.UI.Modules
});
}
}
var embySettings = await EmbySettings.GetSettingsAsync();
var showInfo = TvApi.ShowLookupByTheTvDbId(showId);
DateTime firstAir;
DateTime.TryParse(showInfo.premiered, out firstAir);
@ -1053,7 +1068,7 @@ namespace Ombi.UI.Modules
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
Message = $"{fullShowName} {string.Format(Resources.UI.Search_AlreadyInPlex,embySettings.Enable ? "Emby" : "Plex")}"
});
}
}
@ -1099,7 +1114,7 @@ namespace Ombi.UI.Modules
{
Result = false,
Message =
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {Resources.UI.Search_AlreadyInPlex}"
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
});
}
}
@ -1121,7 +1136,7 @@ namespace Ombi.UI.Modules
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
Message = $"{fullShowName} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
});
}
}
@ -1134,12 +1149,11 @@ namespace Ombi.UI.Modules
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
Message = $"{fullShowName} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
});
}
}
}
var embySettings = await EmbySettings.GetSettingsAsync();
if (embySettings.Enable)
{
var embyContent = EmbyContentRepository.GetAll();
@ -1162,7 +1176,7 @@ namespace Ombi.UI.Modules
{
Result = false,
Message =
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {Resources.UI.Search_AlreadyInPlex}"
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
});
}
}
@ -1200,7 +1214,7 @@ namespace Ombi.UI.Modules
Message = $"{fullShowName} is already in Emby!"
});
}
}
}
}
}
catch (Exception)
@ -1209,7 +1223,7 @@ namespace Ombi.UI.Modules
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullShowName)
Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullShowName,GetMediaServerName())
});
}
@ -1445,7 +1459,7 @@ namespace Ombi.UI.Modules
return img;
}
private Response GetSeasons()
{
var seriesId = (int)Request.Query.tvId;
@ -1780,6 +1794,12 @@ namespace Ombi.UI.Modules
return
Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_TvNotSetUp });
}
private string GetMediaServerName()
{
var e = EmbySettings.GetSettings();
return e.Enable ? "Emby" : "Plex";
}
}
}

@ -32,6 +32,7 @@ using Ombi.Helpers.Analytics;
using Ombi.Services.Interfaces;
using Ombi.Services.Jobs;
using Ombi.Services.Jobs.Interfaces;
using Ombi.Services.Jobs.RecentlyAddedNewsletter;
using Ombi.UI.Jobs;
using Quartz;
using Quartz.Impl;
@ -48,7 +49,8 @@ namespace Ombi.UI.NinjectModules
Bind<IWatcherCacher>().To<WatcherCacher>();
Bind<ISonarrCacher>().To<SonarrCacher>();
Bind<ISickRageCacher>().To<SickRageCacher>();
Bind<IRecentlyAdded>().To<RecentlyAdded>();
Bind<IRecentlyAdded>().To<RecentlyAddedNewsletter>();
Bind<IMassEmail>().To<RecentlyAddedNewsletter>();
Bind<IRadarrCacher>().To<RadarrCacher>();
Bind<IPlexContentCacher>().To<PlexContentCacher>();
Bind<IJobFactory>().To<CustomJobFactory>();
@ -64,6 +66,7 @@ namespace Ombi.UI.NinjectModules
Bind<IEmbyContentCacher>().To<EmbyContentCacher>();
Bind<IEmbyEpisodeCacher>().To<EmbyEpisodeCacher>();
Bind<IEmbyUserChecker>().To<EmbyUserChecker>();
Bind<IEmbyAddedNewsletter>().To<EmbyAddedNewsletter>();
Bind<IAnalytics>().To<Analytics>();

@ -121,13 +121,13 @@
<value>Log ind</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Ønsker du at se en film eller tv-show, men det er i øjeblikket ikke på Plex? Log nedenfor med dit Plex.tv brugernavn og password !!</value>
<value>Ønsker du at se en film eller tv-show, men det er i øjeblikket ikke på {0}? Log nedenfor med dit brugernavn og password !!</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Dine login-oplysninger bruges kun til at godkende din Plex konto.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv Brugernavn</value>
<value>Brugernavn</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Brugernavn</value>
@ -211,7 +211,7 @@
<value>Album</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Ønsker at se noget, der ikke i øjeblikket på Plex ?! Intet problem! Bare søge efter det nedenfor og anmode den !</value>
<value>Ønsker at se noget, der ikke i øjeblikket på {0}?! Intet problem! Bare søge efter det nedenfor og anmode den !</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Søg</value>
@ -409,7 +409,7 @@
<value>allerede er blevet anmodet !!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>Vi kunne ikke kontrollere, om {0} er i Plex, er du sikker på det er korrekt setup ?!</value>
<value>Vi kunne ikke kontrollere, om {0} er i {1}, er du sikker på det er korrekt setup ?!</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Noget gik galt tilføjer filmen til CouchPotato! Tjek venligst din opsætning.!</value>
@ -418,7 +418,7 @@
<value>Du har nået din ugentlige anmodning grænse for film! Kontakt din administrator.!</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>er allerede i Plex !!</value>
<value>er allerede i {0}!!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Noget gik galt tilføjer filmen til SickRage! Tjek venligst din opsætning.!</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Du har nået din ugentlige anmodning grænse for tv-shows! Kontakt din administrator.!</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Beklager, men denne funktionalitet er i øjeblikket kun for brugere med Plex konti!</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Beklager, men din administrator har endnu ikke gjort det muligt denne funktionalitet.!</value>
</data>

@ -121,13 +121,13 @@
<value>Anmelden</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Möchten Sie einen Film oder eine Serie schauen, die momentan noch nicht auf Plex ist? Dann loggen Sie sich unten ein und fordern Sie das Material an!</value>
<value>Möchten Sie einen Film oder eine Serie schauen, die momentan noch nicht auf {0}ist? Dann loggen Sie sich unten ein und fordern Sie das Material an!</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Ihre Login-Daten werden nur zur Authorisierung Ihres Plex-Konto verwendet.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv Benutzername</value>
<value>Benutzername</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Benutzername</value>
@ -211,7 +211,7 @@
<value>Alben</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Möchten Sie etwas schauen, das derzeit nicht auf Plex ist?! Kein Problem! Suchen Sie unten einfach danach und fragen Sie es an!</value>
<value>Möchten Sie etwas schauen, das derzeit nicht auf {0} ist?! Kein Problem! Suchen Sie unten einfach danach und fragen Sie es an!</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Suche</value>
@ -409,7 +409,7 @@
<value>wurde bereits angefragt!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>Wir konnten nicht prüfen ob {0} bereits auf Plex ist. Bist du sicher dass alles richtig installiert ist?</value>
<value>Wir konnten nicht prüfen ob {0} bereits auf {1}ist. Bist du sicher dass alles richtig installiert ist?</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Etwas ging etwas schief beim hinzufügen des Filmes zu CouchPotato! Bitte überprüfe deine Einstellungen.</value>
@ -418,7 +418,7 @@
<value>Du hast deine wöchentliche Maximalanfragen für neue Filme erreicht. Bitte kontaktiere den Administrator.</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>ist bereits auf Plex!</value>
<value>ist bereits auf {0}!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Etwas ging etwas schief beim hinzufügen des Filmes zu SickRage! Bitte überprüfe deine Einstellungen.</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Du hast deine wöchentliche Maximalanfragen für neue Serien erreicht. Bitte kontaktiere den Administrator.</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Entschuldige, aber diese Funktion ist momentan nur für Benutzer mit Plex-Accounts freigeschaltet.</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Entschuldige, aber dein Administrator hat diese Funktion noch nicht freigeschaltet.</value>
</data>

@ -121,13 +121,13 @@
<value>INICIAR SESIÓN</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>¿Quieres ver una película o programa de televisión, pero no es actualmente en Plex? Ingresa abajo con su nombre de usuario y contraseña Plex.tv !</value>
<value>¿Quieres ver una película o programa de televisión, pero no es actualmente en {0}? Ingresa abajo con su nombre de usuario y contraseña !</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Sus datos de acceso sólo se utilizan para autenticar su cuenta Plex.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv nombre de usuario</value>
<value>nombre de usuario</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Username</value>
@ -211,7 +211,7 @@
<value>Álbumes</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>¿Quieres ver algo que no se encuentra actualmente en Plex ?! ¡No hay problema! Sólo la búsqueda de abajo y que solicitarlo !</value>
<value>¿Quieres ver algo que no se encuentra actualmente en {0}?! ¡No hay problema! Sólo la búsqueda de abajo y que solicitarlo !</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Buscar</value>
@ -409,7 +409,7 @@
<value>ya ha sido solicitada !!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>No hemos podido comprobar si {0} está en Plex, ¿estás seguro de que es correcta la configuración ?!</value>
<value>No hemos podido comprobar si {0} está en {1}, ¿estás seguro de que es correcta la configuración ?!</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Algo salió mal la adición de la película para CouchPotato! Por favor verifica la configuracion.!</value>
@ -418,7 +418,7 @@
<value>Ha llegado a su límite de solicitudes semanales de películas! Por favor, póngase en contacto con su administrador.!</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>ya está en Plex !!</value>
<value>ya está en {0}!!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Algo salió mal la adición de la película para SickRage! Por favor verifica la configuracion.!</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Ha llegado a su límite de solicitudes semanales de programas de televisión! Por favor, póngase en contacto con su administrador.!</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Lo sentimos, pero esta funcionalidad es actualmente sólo para los usuarios con cuentas Plex!</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Lo sentimos, pero el administrador aún no ha habilitado esta funcionalidad.!</value>
</data>

@ -121,13 +121,13 @@
<value>Connexion</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Vous souhaitez avoir accès à un contenu qui n'est pas encore disponible dans Plex ? Demandez-le ici !</value>
<value>Vous souhaitez avoir accès à un contenu qui n'est pas encore disponible dans {0}? Demandez-le ici !</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Vos informations de connexion sont uniquement utilisées pour authentifier votre compte Plex.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Nom d'utilisateur Plex.tv</value>
<value>Nom d'utilisateur</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Nom dutilisateur</value>
@ -211,7 +211,7 @@
<value>Albums</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Vous souhaitez avoir accès à un contenu qui n'est pas encore disponible dans Plex ?! Aucun problème ! Il suffit d'effectuer une recherche ci-dessous et d'en faire la demande!</value>
<value>Vous souhaitez avoir accès à un contenu qui n'est pas encore disponible dans {0} ?! Aucun problème ! Il suffit d'effectuer une recherche ci-dessous et d'en faire la demande!</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Rechercher</value>
@ -409,7 +409,7 @@
<value>a déjà été demandé!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>Nous ne pouvons pas vérifier que {0} est présent dans Plex, êtes-vous sûr que la configuration est correcte?</value>
<value>Nous ne pouvons pas vérifier que {0} est présent dans {1}, êtes-vous sûr que la configuration est correcte?</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Une erreur s'est produite lors de l'ajout du film dans CouchPotato! Merci de bien vouloir vérifier vos paramètres.</value>
@ -418,7 +418,7 @@
<value>Vous avez atteint votre quota hebdomadaire de demandes pour les films! Merci de bien vouloir contacter l'administrateur.</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>est déjà présent dans Plex!</value>
<value>est déjà présent dans {0}!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Une erreur s'est produite lors de l'ajout de la série TV dans SickRage! Merci de bien vouloir vérifier vos paramètres.</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Vous avez atteint votre quota hebdomadaire de demandes pour les séries TV! Merci de bien vouloir contacter l'administrateur.</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Désolé mais cette fonctionnalité est réservée aux utilisateurs possédant un compte Plex.</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Désolé mais l'administrateur n'a pas encore activé cette fonctionnalité.</value>
</data>

@ -121,13 +121,13 @@
<value>Accesso</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Vuoi guardare un film o una serie tv ma non è attualmente in Plex? Effettua il login con il tuo username e la password Plex.tv !</value>
<value>Vuoi guardare un film o una serie tv ma non è attualmente in {0}? Effettua il login con il tuo username e la password !</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>I dati di accesso vengono utilizzati solo per autenticare l'account Plex.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv Nome utente</value>
<value>Nome utente</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Nome utente</value>
@ -214,7 +214,7 @@
<value>Msuica</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Vuoi guardare qualcosa che non è attualmente in Plex?! Non c'è problema! Basta cercarla qui sotto e richiederla!</value>
<value>Vuoi guardare qualcosa che non è attualmente in {0}?! Non c'è problema! Basta cercarla qui sotto e richiederla!</value>
</data>
<data name="Search_Suggestions" xml:space="preserve">
<value>Suggerimenti</value>
@ -409,7 +409,7 @@
<value>è già stato richiesto!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>Non siamo riusciti a controllare se {0} è in Plex, sei sicuro che sia configurato correttamente?</value>
<value>Non siamo riusciti a controllare se {0} è in {1}, sei sicuro che sia configurato correttamente?</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Qualcosa è andato storto aggiungendo il film a CouchPotato! Controlla le impostazioni</value>
@ -418,7 +418,7 @@
<value>Hai raggiunto il numero massimo di richieste settimanali per i Film! Contatta l'amministratore</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>è già disponibile in Plex!</value>
<value>è già disponibile in {0}!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Qualcosa è andato storto aggiungendo il film a SickRage! Controlla le impostazioni</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Hai raggiunto il numero massimo di richieste settimanali per le Serie TV! Contatta l'amministratore</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Spiacente, ma questa funzione è disponibile solo per gli utenti con un account Plex.</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Spiacente, ma l'amministratore non ha ancora abilitato questa funzionalità.</value>
</data>

@ -121,13 +121,13 @@
<value>Inloggen</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Wilt u een film of een tv serie kijken, maar staat deze niet op Plex? Log hieronder in met uw gebruikersnaam en wachtwoord van Plex.tv</value>
<value>Wilt u een film of een tv serie kijken, maar staat deze niet op {0}? Log hieronder in met uw gebruikersnaam en wachtwoord van</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Uw login gegevens worden alleen gebruikt om uw account te verifiëren bij Plex.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv Gebruikersnaam</value>
<value> Gebruikersnaam</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Gebruikersnaam</value>
@ -217,7 +217,7 @@
<value>Albums</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Wilt u kijken naar iets dat dat momenteel niet op Plex is?! Geen probleem! zoek hieronder en vraag het aan!</value>
<value>Wilt u kijken naar iets dat dat momenteel niet op {0} is?! Geen probleem! zoek hieronder en vraag het aan!</value>
</data>
<data name="Search_Suggestions" xml:space="preserve">
<value>Suggesties</value>
@ -409,10 +409,7 @@
<value>Is al aangevraagd!</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>Staat al op Plex!</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Sorry, deze functie is momenteel alleen voor gebruikers met een Plex account.</value>
<value>Staat al op {0}!</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Sorry, uw administrator heeft deze functie nog niet geactiveerd.</value>
@ -424,7 +421,7 @@
<value>Kon niet opslaan, probeer het later nog eens.</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>We konden niet controleren of {0} al in plex bestaat, weet je zeker dat het correct is ingesteld?</value>
<value>We konden niet controleren of {0} al in {1} bestaat, weet je zeker dat het correct is ingesteld?</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Er is iets foutgegaan tijdens het toevoegen van de film aan CouchPotato! Controleer je instellingen</value>

@ -121,13 +121,13 @@
<value>Entrar</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Quer assistir a um filme ou programa de TV, mas não está atualmente em Plex? Entre abaixo com seu nome de usuário e senha Plex.tv !!</value>
<value>Quer assistir a um filme ou programa de TV, mas não está atualmente em {0}? Entre abaixo com seu nome de usuário e senha !</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Seus dados de login são apenas usados para autenticar sua conta Plex.!</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv usuário</value>
<value>usuário</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Nome de usuário</value>
@ -211,7 +211,7 @@
<value>Álbuns</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Quer assistir algo que não está atualmente em Plex ?! Sem problemas! Basta procurá-lo abaixo e solicitá-lo !!</value>
<value>Quer assistir algo que não está atualmente em {0}?! Sem problemas! Basta procurá-lo abaixo e solicitá-lo !!</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Buscar</value>
@ -409,7 +409,7 @@
<value>já foi solicitado !!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>Nós não poderia verificar se {0} está em Plex, você tem certeza que é configurada corretamente ?!</value>
<value>Nós não poderia verificar se {0} está em {1}, você tem certeza que é configurada corretamente ?!</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Algo deu errado adicionando o filme para CouchPotato! Verifique as suas opções.!</value>
@ -418,7 +418,7 @@
<value>Atingiu seu limite semanal de solicitação para filmes! Entre em contato com seu administrador.</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>Já está no Plex!</value>
<value>Já está no {0}!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Algo deu errado adicionar o filme para SickRage! Por favor, verifique suas configurações.</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Atingiu seu limite semanal de solicitação para programas de TV! Entre em contato com seu administrador.</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Desculpe, mas essa funcionalidade é atualmente somente para os usuários com contas de Plex</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Desculpe, mas o administrador não permitiu ainda esta funcionalidade.</value>
</data>

@ -101,14 +101,14 @@
<value>Login</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Want to watch a movie or tv show but it's not currently on Plex?
Login below with your Plex.tv username and password!</value>
<value>Want to watch a movie or tv show but it's not currently on {0}?
Login below with your username and password!</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Your login details are only used to authenticate your Plex account.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv Username </value>
<value>Username </value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Username</value>
@ -192,7 +192,7 @@
<value>Albums</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Want to watch something that is not currently on Plex?! No problem! Just search for it below and request it!</value>
<value>Want to watch something that is not currently on {0}?! No problem! Just search for it below and request it!</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Search</value>
@ -390,7 +390,7 @@
<value>has already been requested!</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>We could not check if {0} is in Plex, are you sure it's correctly setup?</value>
<value>We could not check if {0} is in {1}, are you sure it's correctly setup?</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Something went wrong adding the movie to CouchPotato! Please check your settings.</value>
@ -399,7 +399,7 @@
<value>You have reached your weekly request limit for Movies! Please contact your admin.</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>is already in Plex!</value>
<value>is already in {0}!</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Something went wrong adding the movie to SickRage! Please check your settings.</value>
@ -416,9 +416,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>You have reached your weekly request limit for TV Shows! Please contact your admin.</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Sorry, but this functionality is currently only for users with Plex accounts</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Sorry, but your administrator has not yet enabled this functionality.</value>
</data>
@ -468,7 +465,7 @@
<value>TV show status</value>
</data>
<data name="Layout_CacherRunning" xml:space="preserve">
<value>Currently we are indexing all of the available tv shows and movies on the Plex server, so there might be some unexpected behavior. This shouldn't take too long.</value>
<value>Currently we are indexing all of the available tv shows and movies on the media server, so there might be some unexpected behavior. This shouldn't take too long.</value>
</data>
<data name="Layout_Usermanagement" xml:space="preserve">
<value>User Management</value>

@ -121,13 +121,13 @@
<value>Logga in</value>
</data>
<data name="UserLogin_Paragraph" xml:space="preserve">
<value>Vill du titta på en film eller TV-show, men det är inte närvarande på Plex? Logga in nedan med Plex.tv användarnamn och lösenord !!</value>
<value>Vill du titta på en film eller TV-show, men det är inte närvarande på {0}? Logga in nedan med användarnamn och lösenord !!</value>
</data>
<data name="UserLogin_Paragraph_SpanHover" xml:space="preserve">
<value>Dina inloggningsuppgifter används endast för att autentisera ditt Plex-konto.</value>
</data>
<data name="UserLogin_Username" xml:space="preserve">
<value>Plex.tv användarnamn</value>
<value>Användarnamn</value>
</data>
<data name="UserLogin_Username_Placeholder" xml:space="preserve">
<value>Användarnamn</value>
@ -214,7 +214,7 @@
<value>Album</value>
</data>
<data name="Search_Paragraph" xml:space="preserve">
<value>Vill titta på något som inte är närvarande på Plex ?! Inga problem! Bara söka efter den nedan och begär det !</value>
<value>Vill titta på något som inte är närvarande på {0}?! Inga problem! Bara söka efter den nedan och begär det !</value>
</data>
<data name="Search_Title" xml:space="preserve">
<value>Sök</value>
@ -409,7 +409,7 @@
<value>har redan begärts</value>
</data>
<data name="Search_CouldNotCheckPlex" xml:space="preserve">
<value>Vi kunde inte kontrollera om {0} är i Plex, är du säker det är korrekt installation?</value>
<value>Vi kunde inte kontrollera om {0} är i {1}, är du säker det är korrekt installation?</value>
</data>
<data name="Search_CouchPotatoError" xml:space="preserve">
<value>Något gick fel att lägga till filmen i CouchPotato! Kontrollera inställningarna.</value>
@ -418,7 +418,7 @@
<value>Du har nått din weekly begäran gräns för filmer! Kontakta din admin.</value>
</data>
<data name="Search_AlreadyInPlex" xml:space="preserve">
<value>är redan i Plex</value>
<value>är redan i {0}</value>
</data>
<data name="Search_SickrageError" xml:space="preserve">
<value>Något gick fel att lägga till filmen i SickRage! Kontrollera inställningarna.</value>
@ -435,9 +435,6 @@
<data name="Search_WeeklyRequestLimitTVShow" xml:space="preserve">
<value>Du har nått din weekly begäran gräns för TV-program! Kontakta din admin.</value>
</data>
<data name="Search_ErrorPlexAccountOnly" xml:space="preserve">
<value>Ledsen, men denna funktion är för närvarande endast för användare med Plex konton</value>
</data>
<data name="Search_ErrorNotEnabled" xml:space="preserve">
<value>Ledsen, men administratören har ännu inte aktiverat denna funktion.</value>
</data>

@ -223,7 +223,7 @@ namespace Ombi.UI.Resources {
}
/// <summary>
/// Looks up a localized string similar to Currently we are indexing all of the available tv shows and movies on the Plex server, so there might be some unexpected behavior. This shouldn&apos;t take too long..
/// Looks up a localized string similar to Currently we are indexing all of the available tv shows and movies on the media server, so there might be some unexpected behavior. This shouldn&apos;t take too long..
/// </summary>
public static string Layout_CacherRunning {
get {
@ -736,7 +736,7 @@ namespace Ombi.UI.Resources {
}
/// <summary>
/// Looks up a localized string similar to is already in Plex!.
/// Looks up a localized string similar to is already in {0}!.
/// </summary>
public static string Search_AlreadyInPlex {
get {
@ -790,7 +790,7 @@ namespace Ombi.UI.Resources {
}
/// <summary>
/// Looks up a localized string similar to We could not check if {0} is in Plex, are you sure it&apos;s correctly setup?.
/// Looks up a localized string similar to We could not check if {0} is in {1}, are you sure it&apos;s correctly setup?.
/// </summary>
public static string Search_CouldNotCheckPlex {
get {
@ -816,15 +816,6 @@ namespace Ombi.UI.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Sorry, but this functionality is currently only for users with Plex accounts.
/// </summary>
public static string Search_ErrorPlexAccountOnly {
get {
return ResourceManager.GetString("Search_ErrorPlexAccountOnly", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to First Season.
/// </summary>
@ -907,7 +898,7 @@ namespace Ombi.UI.Resources {
}
/// <summary>
/// Looks up a localized string similar to Want to watch something that is not currently on Plex?! No problem! Just search for it below and request it!.
/// Looks up a localized string similar to Want to watch something that is not currently on {0}?! No problem! Just search for it below and request it!.
/// </summary>
public static string Search_Paragraph {
get {
@ -1132,8 +1123,8 @@ namespace Ombi.UI.Resources {
}
/// <summary>
/// Looks up a localized string similar to Want to watch a movie or tv show but it&apos;s not currently on Plex?
/// Login below with your Plex.tv username and password!.
/// Looks up a localized string similar to Want to watch a movie or tv show but it&apos;s not currently on {0}?
/// Login below with your username and password!.
/// </summary>
public static string UserLogin_Paragraph {
get {
@ -1178,7 +1169,7 @@ namespace Ombi.UI.Resources {
}
/// <summary>
/// Looks up a localized string similar to Plex.tv Username .
/// Looks up a localized string similar to Username .
/// </summary>
public static string UserLogin_Username {
get {

@ -47,6 +47,8 @@
</div>
</div>
@Html.Checkbox(Model.EnableEpisodeSearching, "EnableEpisodeSearching", "Enable Episode Searching")
<div class="form-group">
<label for="SubDir" class="control-label">Emby Base Url</label>
<div>

@ -54,7 +54,7 @@
<p class="form-group">Notice Message</p>
<div class="form-group">
<div>
<textarea rows="4" type="text" class="form-control-custom form-control " id="NoticeMessage" name="NoticeMessage" placeholder="e.g. Plex will be down for maintaince (HTML is allowed)" value="@Model.NoticeMessage">@Model.NoticeMessage</textarea>
<textarea rows="4" type="text" class="form-control-custom form-control " id="NoticeMessage" name="NoticeMessage" placeholder="e.g. The server will be down for maintaince (HTML is allowed)" value="@Model.NoticeMessage">@Model.NoticeMessage</textarea>
</div>
</div>

@ -7,46 +7,48 @@
<form class="form-horizontal" method="POST" id="mainForm">
<fieldset>
<legend>Newsletter Settings</legend>
<div style="padding:10px">
<!-- Email Nofication Section -->
<div class="form-group">
<div class="checkbox">
<!-- Email Nofication Section -->
<div class="form-group">
<div class="checkbox">
<small>Note: This will require you to setup your email notifications</small>
<br />
@if (Model.SendRecentlyAddedEmail)
{
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail" checked="checked"><label for="SendRecentlyAddedEmail">Enable newsletter</label>
}
else
{
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail"><label for="SendRecentlyAddedEmail">Enable newsletter</label>
}
<small>Note: This will require you to setup your email notifications</small>
<br />
@if (Model.SendRecentlyAddedEmail)
{
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail" checked="checked"><label for="SendRecentlyAddedEmail">Enable newsletter</label>
}
else
{
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail"><label for="SendRecentlyAddedEmail">Enable newsletter</label>
}
</div>
</div>
</div>
<div class="form-group">
<br>
<label for="CustomUsers" class="control-label">Email Addresses to Send to (For users that are not in your User Management section)</label>
<small>You can add multiple email addresses by using the ; delimiter</small>
<div>
<input type="text" class="form-control form-control-custom " placeholder="first@address.com;second@address.com" id="CustomUsers" name="CustomUsers" value="@Model.CustomUsers">
<div class="form-group">
<br>
<label for="CustomUsers" class="control-label">Email Addresses to Send to (For users that are not in your User Management section)</label>
<small>You can add multiple email addresses by using the ; delimiter</small>
<div>
<input type="text" class="form-control form-control-custom " placeholder="first@address.com;second@address.com" id="CustomUsers" name="CustomUsers" value="@Model.CustomUsers">
</div>
</div>
</div>
<div class="form-group">
<div>
<button id="recentlyAddedBtn" class="btn btn-primary-outline">Send test email to Admin <div id="spinner"></div></button>
<div class="form-group">
<div>
<button id="recentlyAddedBtn" class="btn btn-primary-outline">Send test email to Admin <div id="testEmailSpinner"></div></button>
</div>
</div>
</div>
<br />
<br />
<div class="form-group">
<div>
<button type="submit" id="save" class="btn btn-primary-outline">Submit</button>
<br />
<br />
<div class="form-group">
<div>
<button type="submit" id="save" class="btn btn-primary-outline">Save</button>
</div>
</div>
</div>
<!-- Email Nofication Section -->
@ -54,6 +56,39 @@
</fieldset>
</form>
<form id="massemail" class="form-horizontal">
<fieldset>
<legend>Mass Email</legend>
<div style="padding:10px">
<div class="form-group">
<small>Note: This will require you to setup your email notifications</small>
</div>
<div class="form-group">
<label for="massEmailSubject" class="control-label">Subject</label>
<div>
<input type="text" class="form-control form-control-custom " placeholder="A Message from the Admin" id="massEmailSubject" name="massEmailSubject" value="">
</div>
</div>
<div class="form-group">
<label for="massEmailBody" class="control-label">Body</label>
<textarea id="massEmailBody" class="form-control" rows="5"></textarea>
<small>Supports HTML</small>
</div>
<div class="form-group">
<div>
<button id="testSendMassEmailBtn" class="btn btn-primary-outline">Send Test to Admin<div id="testSendMassEmailSpinner"></div></button>
</div>
</div>
<div class="form-group">
<div>
<button id="sendMassEmailBtn" class="btn btn-primary-outline">Send To All Users<div id="sendMassEmailSpinner"></div></button>
</div>
</div>
</div>
</fieldset>
</form>
</div>
@ -90,8 +125,8 @@
$('#recentlyAddedBtn').click(function (e) {
e.preventDefault();
var base = '@Html.GetBaseUrl()';
var url = createBaseUrl(base, '/admin/recentlyAddedTest');
$('#spinner').attr("class", "fa fa-spinner fa-spin");
var url = createBaseUrl(base, '/admin/testnewsletteradminemail');
$('#testEmailSpinner').attr("class", "fa fa-spinner fa-spin");
$.ajax({
type: "post",
url: url,
@ -99,17 +134,74 @@
success: function (response) {
if (response) {
generateNotify(response.message, "success");
$('#spinner').attr("class", "fa fa-check");
$('#testSendMassEmailSpinner').attr("class", "fa fa-check");
} else {
generateNotify(response.message, "danger");
$('#testSendMassEmailSpinner').attr("class", "fa fa-times");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
$('#testSendMassEmailSpinner').attr("class", "fa fa-times");
}
});
});
$('#testSendMassEmailBtn').click(function (e) {
e.preventDefault();
var base = '@Html.GetBaseUrl()';
var url = createBaseUrl(base, '/admin/testmassadminemail');
$('#testSendMassEmailSpinner').attr("class", "fa fa-spinner fa-spin");
var data = { "Users": "", "Body": $("#massEmailBody").val(), "Subject": $("#massEmailSubject").val() };
$.ajax({
type: "post",
url: url,
data: data,
dataType: "json",
success: function (response) {
if (response.result) {
generateNotify(response.message, "success");
$('#testSendMassEmailSpinner').attr("class", "fa fa-check");
} else {
generateNotify(response.message, "danger");
$('#testSendMassEmailSpinner').attr("class", "fa fa-times");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
$('#testSendMassEmailSpinner').attr("class", "fa fa-times");
}
});
});
$('#sendMassEmailBtn').click(function (e) {
e.preventDefault();
var base = '@Html.GetBaseUrl()';
var url = createBaseUrl(base, '/admin/sendmassemail');
$('#sendMassEmailSpinner').attr("class", "fa fa-spinner fa-spin");
var data = { "Users": "", "Body": $("#massEmailBody").val(), "Subject": $("#massEmailSubject").val() };
$.ajax({
type: "post",
url: url,
data: data,
dataType: "json",
success: function (response) {
if (response.result) {
generateNotify(response.message, "success");
$('#sendMassEmailSpinner').attr("class", "fa fa-check");
} else {
generateNotify(response.message, "danger");
$('#spinner').attr("class", "fa fa-times");
$('#sendMassEmailSpinner').attr("class", "fa fa-times");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
$('#spinner').attr("class", "fa fa-times");
$('#sendMassEmailSpinner').attr("class", "fa fa-times");
}
});
});

@ -34,7 +34,7 @@
<label for="select" class="control-label">Theme</label>
<div id="themes">
<select class="form-control form-control-custom" id="select">
<option @plexTheme class="form-control form-control-custom" value="@Themes.PlexTheme">Plex</option>
<option @plexTheme class="form-control form-control-custom" value="@Themes.PlexTheme">Dark</option>
<option @originalTheme class="form-control form-control-custom" value="@Themes.OriginalTheme">Original Blue</option>
</select>
</div>
@ -104,7 +104,8 @@
</div>
</div>
@Html.Checkbox(Model.Settings.NewSearch, "NewSearch", "Use New Search")
@*@Html.Checkbox(Model.Settings.NewSearch, "NewSearch", "Use New Search")*@
@Html.Checkbox(Model.Settings.EnableIssues, "EnableIssues", "Enable Issues")
<div class="form-group">
<div>
<button type="submit" id="save" class="btn btn-primary-outline">Submit</button>

@ -64,10 +64,10 @@
</div>
<div class="form-group">
<label for="RootPath" class="control-label">Root save directory for TV shows</label>
<label for="RootPath" class="control-label">Root save directory for Movies</label>
<div>
<input type="text" class="form-control form-control-custom " placeholder="C:\Media\Tv" id="RootPath" name="RootPath" value="@Model.RootPath">
<label>Enter the root folder where movies are saved. For example <strong>C:\Media\TV</strong>.</label>
<input type="text" class="form-control form-control-custom " placeholder="C:\Media\Movies" id="RootPath" name="RootPath" value="@Model.RootPath">
<label>Enter the root folder where movies are saved. For example <strong>C:\Media\Movies</strong>.</label>
</div>
</div>
@ -241,4 +241,4 @@
})
</script>
</script>

@ -33,7 +33,7 @@
</div>
<div class="media-body">
<h4 class="media-heading landing-title" id="statusTitle">Checking...</h4>
The Plex server is <strong><span id="statusText">Loading...</span></strong> (check this page for continuous status updates)
The Media server is <strong><span id="statusText">Loading...</span></strong> (check this page for continuous status updates)
</div>
</div>
</div>

@ -191,18 +191,36 @@
</a>
</div>
<br />
{{#if_eq type "tv"}}
<span>@UI.Search_TV_Show_Status: </span>
{{else}}
<span>@UI.Search_Movie_Status: </span>
{{/if_eq}}
<span class="label label-success">{{status}}</span>
{{#if denied}}
<div>
Denied: <i style="color:red;" class="fa fa-check"></i>
{{#if_eq type "tv"}}
<span>@UI.Search_TV_Show_Status: </span>
{{else}}
<span>@UI.Search_Movie_Status: </span>
{{/if_eq}}
<span class="label label-success">{{status}}</span>
</div>
<div>
<span>Request status: </span>
{{#if available}}
<span class="label label-success">@UI.Search_Available_on_plex</span>
{{else}}
{{#if approved}}
<span class="label label-info">@UI.Search_Processing_Request</span>
{{else if denied}}
<span class="label label-danger">@UI.Search_Request_denied</span>
{{#if deniedReason}}
<span class="customTooltip" title="{{deniedReason}}"><i class="fa fa-info-circle"></i></span>
{{/if}}
{{else}}
<span class="label label-warning">@UI.Search_Pending_approval</span>
{{/if}}
{{/if}}
</div>
{{#if denied}}
<div>
Denied: <i style="color:red;" class="fa fa-check"></i>
</div>
{{/if}}
@ -211,26 +229,7 @@
{{else}}
<div>@UI.Requests_ReleaseDate: {{releaseDate}}</div>
{{/if_eq}}
{{#unless denied}}
<div>
@UI.Common_Approved:
{{#if_eq approved false}}
<i id="{{requestId}}notapproved" class="fa fa-times"></i>
{{/if_eq}}
{{#if_eq approved true}}
<i class="fa fa-check"></i>
{{/if_eq}}
</div>
{{/unless}}
<div>
@UI.Requests_Available
{{#if_eq available false}}
<i id="availableIcon{{requestId}}" class="fa fa-times"></i>
{{/if_eq}}
{{#if_eq available true}}
<i id="availableIcon{{requestId}}" class="fa fa-check"></i>
{{/if_eq}}
</div>
<br/>
{{#if_eq type "tv"}}
{{#if episodes}}

@ -8,12 +8,14 @@
{
url = "/" + baseUrl.ToHtmlString();
}
}
<div>
<div hidden="hidden" id="useNewSearch">@Model.CustomizationSettings.NewSearch</div>
<h1 id="searchTitle">@UI.Search_Title</h1>
<h4>@UI.Search_Paragraph</h4>
<h4>@string.Format(UI.Search_Paragraph, Model.Emby ? "Emby" : "Plex")</h4>
<br />
<!-- Nav tabs -->
@ -583,3 +585,4 @@
</script>
@Html.LoadSearchAssets()

@ -50,9 +50,9 @@
{
<label class="control-label"><a href="@Model.Status.UpdateUri" target="_blank"><i class="fa fa-check"></i></a></label>
<br />
<input id="args" class="form-control form-control-custom " placeholder="optional launch arguments e.g. /etc/mono /opt/PlexRequests.exe">
@*<input id="args" class="form-control form-control-custom " placeholder="optional launch arguments e.g. /etc/mono /opt/PlexRequests.exe">*@
<br/>
<button id="autoUpdate" class="btn btn-success-outline">Automatic Update (beta) <i class="fa fa-download"></i></button>
@*<button id="autoUpdate" class="btn btn-success-outline">Automatic Update (beta) <i class="fa fa-download"></i></button>*@
}
else
{

@ -6,7 +6,7 @@
<h1>@UI.UserLogin_Title</h1>
<div>
<p>
@UI.UserLogin_Paragraph <span title="@UI.UserLogin_Paragraph_SpanHover"><i class="fa fa-question-circle"></i></span>
@string.Format(UI.UserLogin_Paragraph, Html.GetMediaServerName().ToHtmlString()).ToString()
</p>
</div>
<div id="contentBody">

@ -9,7 +9,7 @@
<span>Here you can manage the default permissions and features that your users get</span>
<small>
Note: This will not update your users that are currently there, this is to set the default settings to any users added outside of Ombi e.g. You share your Plex Server with a new user, they will be added into Ombi
Note: This will not update your users that are currently there, this is to set the default settings to any users added outside of Ombi e.g. You share your Server with a new user, they will be added into Ombi
automatically and will take the permissions and features you have selected below.
</small>

@ -192,7 +192,7 @@
<script id="adminArea" type="text/html">
<form method="post" action="@formAction/wizard/createuser" id="adminForm">
<h4 class="media-heading landing-title">Create the Admin account</h4>
<small>This account will be used to configure your settings and also manage all of the requests. Note: this should not be the same as your Plex.Tv account (you can change this later in the User Management Settings)</small>
<small>This account will be used to configure your settings and also manage all of the requests. Note: this should not be the same as your Plex/Emby account (you can change this later in the User Management Settings)</small>
<div class="form-group">
<div>
<label for="adminUsername">Username</label><input type="text" class="form-control form-control-custom" id="adminUsername" name="Username" placeholder="Username">

Loading…
Cancel
Save