mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-20 21:33:15 -07:00
commit
962e663fcd
25 changed files with 404 additions and 70 deletions
|
@ -151,7 +151,7 @@ namespace PlexRequests.Core
|
||||||
var passwordMatch = PasswordHasher.VerifyPassword(oldPassword, userToChange.Salt, userToChange.Hash);
|
var passwordMatch = PasswordHasher.VerifyPassword(oldPassword, userToChange.Salt, userToChange.Hash);
|
||||||
if (!passwordMatch)
|
if (!passwordMatch)
|
||||||
{
|
{
|
||||||
throw new SecurityException("Password does not match");
|
throw new SecurityException("Incorrect password.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSalt = PasswordHasher.GenerateSalt();
|
var newSalt = PasswordHasher.GenerateSalt();
|
||||||
|
|
|
@ -46,6 +46,7 @@ namespace PlexRequests.Helpers.Tests
|
||||||
{
|
{
|
||||||
yield return new TestCaseData("hello!").Returns("5a8dd3ad0756a93ded72b823b19dd877").SetName("Hello");
|
yield return new TestCaseData("hello!").Returns("5a8dd3ad0756a93ded72b823b19dd877").SetName("Hello");
|
||||||
yield return new TestCaseData("0111111").Returns("9549d400a68633435918290085f06293").SetName("Number");
|
yield return new TestCaseData("0111111").Returns("9549d400a68633435918290085f06293").SetName("Number");
|
||||||
|
yield return new TestCaseData("tidusjar").Returns("f47201e0103fa37ca82ce5243cebb9c7").SetName("tidusjar");
|
||||||
yield return new TestCaseData("hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!").Returns("26d0d126918fbecfc0fa4a63070d314c")
|
yield return new TestCaseData("hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!hello!").Returns("26d0d126918fbecfc0fa4a63070d314c")
|
||||||
.SetName("Long string");
|
.SetName("Long string");
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace PlexRequests.Helpers.Tests
|
||||||
"ProviderId", "ImdbId", "TvDbId", "Overview", "Title", "PosterPath", "ReleaseDate", "Type",
|
"ProviderId", "ImdbId", "TvDbId", "Overview", "Title", "PosterPath", "ReleaseDate", "Type",
|
||||||
"Status", "Approved", "RequestedBy", "RequestedDate", "Available", "Issues", "OtherMessage", "AdminNote",
|
"Status", "Approved", "RequestedBy", "RequestedDate", "Available", "Issues", "OtherMessage", "AdminNote",
|
||||||
"SeasonList", "SeasonCount", "SeasonsRequested", "MusicBrainzId", "RequestedUsers","ArtistName",
|
"SeasonList", "SeasonCount", "SeasonsRequested", "MusicBrainzId", "RequestedUsers","ArtistName",
|
||||||
"ArtistId","IssueId","Episodes","AllUsers","CanApprove","Id"
|
"ArtistId","IssueId","Episodes", "Denied", "DeniedReason", "AllUsers","CanApprove","Id",
|
||||||
}).SetName("Requested Model");
|
}).SetName("Requested Model");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace PlexRequests.Helpers
|
||||||
{
|
{
|
||||||
using (var md5 = MD5.Create())
|
using (var md5 = MD5.Create())
|
||||||
{
|
{
|
||||||
var inputBytes = Encoding.ASCII.GetBytes(input);
|
var inputBytes = Encoding.UTF8.GetBytes(input);
|
||||||
var hash = md5.ComputeHash(inputBytes);
|
var hash = md5.ComputeHash(inputBytes);
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
|
@ -92,8 +92,12 @@ namespace PlexRequests.Services.Jobs
|
||||||
{
|
{
|
||||||
var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued);
|
var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued);
|
||||||
|
|
||||||
var items = movies?.movies?.Select(x => x.info?.tmdb_id).Cast<int>().ToArray();
|
var items = movies?.movies?.Select(x => x.info?.tmdb_id);
|
||||||
return items ?? new int[] { };
|
if(items != null)
|
||||||
|
{
|
||||||
|
return items.Cast<int>().ToArray();
|
||||||
|
}
|
||||||
|
return new int[] { };
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -195,9 +195,18 @@ namespace PlexRequests.Services.Jobs
|
||||||
|
|
||||||
public PlexMovie GetMovie(PlexMovie[] plexMovies, string title, string year, string providerId = null)
|
public PlexMovie GetMovie(PlexMovie[] plexMovies, string title, string year, string providerId = null)
|
||||||
{
|
{
|
||||||
|
if (plexMovies.Length == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var advanced = !string.IsNullOrEmpty(providerId);
|
var advanced = !string.IsNullOrEmpty(providerId);
|
||||||
foreach (var movie in plexMovies)
|
foreach (var movie in plexMovies)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(movie.Title) || string.IsNullOrEmpty(movie.ReleaseYear))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (advanced)
|
if (advanced)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(movie.ProviderId) &&
|
if (!string.IsNullOrEmpty(movie.ProviderId) &&
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace PlexRequests.Store
|
||||||
public string Status { get; set; }
|
public string Status { get; set; }
|
||||||
public bool Approved { get; set; }
|
public bool Approved { get; set; }
|
||||||
|
|
||||||
[Obsolete("Use RequestedUsers")]
|
[Obsolete("Use RequestedUsers")] //TODO remove this obsolete property
|
||||||
public string RequestedBy { get; set; }
|
public string RequestedBy { get; set; }
|
||||||
|
|
||||||
public DateTime RequestedDate { get; set; }
|
public DateTime RequestedDate { get; set; }
|
||||||
|
@ -44,6 +44,8 @@ namespace PlexRequests.Store
|
||||||
public string ArtistId { get; set; }
|
public string ArtistId { get; set; }
|
||||||
public int IssueId { get; set; }
|
public int IssueId { get; set; }
|
||||||
public List<EpisodesModel> Episodes { get; set; }
|
public List<EpisodesModel> Episodes { get; set; }
|
||||||
|
public bool Denied { get; set; }
|
||||||
|
public string DeniedReason { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public List<string> AllUsers
|
public List<string> AllUsers
|
||||||
|
|
118
PlexRequests.UI/Content/requests.js
vendored
118
PlexRequests.UI/Content/requests.js
vendored
|
@ -373,6 +373,17 @@ $('#noteModal').on('show.bs.modal', function (event) {
|
||||||
requestField.val(id); // Add ID to the hidden field
|
requestField.val(id); // Add ID to the hidden field
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update deny reason modal
|
||||||
|
$('#denyReasonModal').on('show.bs.modal', function (event) {
|
||||||
|
var button = $(event.relatedTarget); // Button that triggered the modal
|
||||||
|
var id = button.data('identifier'); // Extract info from data-* attributes
|
||||||
|
|
||||||
|
var modal = $(this);
|
||||||
|
modal.find('.denySaveReason').val(id); // Add ID to the button
|
||||||
|
var requestField = modal.find('input');
|
||||||
|
requestField.val(id); // Add ID to the hidden field
|
||||||
|
});
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
$(document).on("click", ".delete", function (e) {
|
$(document).on("click", ".delete", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -403,28 +414,85 @@ $(document).on("click", ".delete", function (e) {
|
||||||
// Approve single request
|
// Approve single request
|
||||||
$(document).on("click", ".approve", function (e) {
|
$(document).on("click", ".approve", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var $this = $(this);
|
var $self = $(this);
|
||||||
var $form = $this.parents('form').first();
|
var $form = $self.parents('form').first();
|
||||||
|
|
||||||
if ($this.text() === " Loading...") {
|
if ($self.text() === " Loading...") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingButton($this.attr('id'), "success");
|
loadingButton($self.attr('id'), "success");
|
||||||
|
|
||||||
approveRequest($form, null, function () {
|
approveRequest($form, null, function () {
|
||||||
$("#" + $this.attr('id') + "notapproved").prop("class", "fa fa-check");
|
$("#" + $self.attr('id') + "notapproved").prop("class", "fa fa-check");
|
||||||
|
|
||||||
var $group = $this.parent('.btn-split');
|
|
||||||
|
var $group = $self.parent('.btn-split');
|
||||||
if ($group.length > 0) {
|
if ($group.length > 0) {
|
||||||
$group.remove();
|
$group.remove();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this.remove();
|
$self.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Deny single request
|
||||||
|
$(document).on("click", ".deny", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $self = $(this);
|
||||||
|
var $form = $self.parents('form').first();
|
||||||
|
|
||||||
|
if ($self.text() === " Loading...") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadingButton($self.attr('id')+"deny", "success");
|
||||||
|
|
||||||
|
denyRequest($form, function () {
|
||||||
|
// Remove the form
|
||||||
|
$("#" + "deny" + $self.attr('id')).remove();
|
||||||
|
// remove the approve button
|
||||||
|
var id = $self.attr("custom-button");
|
||||||
|
$("#" + id).remove();
|
||||||
|
|
||||||
|
var $group = $self.parent('.btn-split');
|
||||||
|
if ($group.length > 0) {
|
||||||
|
$group.remove();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deny single request with reason (modal)
|
||||||
|
$(document).on("click", ".denySaveReason", function (e) {
|
||||||
|
var comment = $("#denyReason").val();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var $form = $("#denyReasonForm");
|
||||||
|
var data = $form.serialize();
|
||||||
|
data = data + "&reason=" + comment;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: $form.prop("method"),
|
||||||
|
url: $form.prop("action"),
|
||||||
|
data: data,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
if (checkJsonResponse(response)) {
|
||||||
|
generateNotify(response.message, "success");
|
||||||
|
$("#denyReasonModal").modal("hide");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$(document).on("click", ".approve-with-quality", function (e) {
|
$(document).on("click", ".approve-with-quality", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
|
@ -524,6 +592,35 @@ function approveRequest($form, qualityId, successCallback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function denyRequest($form, successCallback) {
|
||||||
|
|
||||||
|
var formData = $form.serialize();
|
||||||
|
$.ajax({
|
||||||
|
type: $form.prop('method'),
|
||||||
|
url: $form.prop('action'),
|
||||||
|
data: formData,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
|
||||||
|
if (checkJsonResponse(response)) {
|
||||||
|
if (response.message) {
|
||||||
|
generateNotify(response.message, "success");
|
||||||
|
} else {
|
||||||
|
generateNotify("Success! Request Approved.", "success");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successCallback) {
|
||||||
|
successCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function mixItUpConfig(activeState) {
|
function mixItUpConfig(activeState) {
|
||||||
var conf = mixItUpDefault;
|
var conf = mixItUpDefault;
|
||||||
|
|
||||||
|
@ -556,6 +653,11 @@ function movieLoad() {
|
||||||
var html = searchTemplate(context);
|
var html = searchTemplate(context);
|
||||||
$ml.append(html);
|
$ml.append(html);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('.customTooltip').tooltipster({
|
||||||
|
contentCloning: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$ml.html(noResultsHtml.format("movie"));
|
$ml.html(noResultsHtml.format("movie"));
|
||||||
|
@ -658,6 +760,8 @@ function buildRequestContext(result, type) {
|
||||||
hasQualities: result.qualities && result.qualities.length > 0,
|
hasQualities: result.qualities && result.qualities.length > 0,
|
||||||
artist: result.artistName,
|
artist: result.artistName,
|
||||||
musicBrainzId: result.musicBrainzId,
|
musicBrainzId: result.musicBrainzId,
|
||||||
|
denied: result.denied,
|
||||||
|
deniedReason: result.deniedReason,
|
||||||
};
|
};
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
3
PlexRequests.UI/Content/search.js
vendored
3
PlexRequests.UI/Content/search.js
vendored
|
@ -467,7 +467,8 @@ $(function () {
|
||||||
available: result.available,
|
available: result.available,
|
||||||
episodes: result.episodes,
|
episodes: result.episodes,
|
||||||
tvFullyAvailable: result.tvFullyAvailable,
|
tvFullyAvailable: result.tvFullyAvailable,
|
||||||
url: result.plexUrl
|
url: result.plexUrl,
|
||||||
|
tvPartialAvailable : result.tvPartialAvailable
|
||||||
};
|
};
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ using PlexRequests.Api.Models.Sonarr;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Store;
|
using PlexRequests.Store;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using PlexRequests.Helpers.Exceptions;
|
using PlexRequests.Helpers.Exceptions;
|
||||||
|
@ -93,7 +94,7 @@ namespace PlexRequests.UI.Helpers
|
||||||
|
|
||||||
// Get the series that was just added
|
// Get the series that was just added
|
||||||
series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
|
series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
|
||||||
series.monitored = false; // Un-monitor the series
|
series.monitored = true; // We want to make sure we are monitoring the series
|
||||||
|
|
||||||
// Un-monitor all seasons
|
// Un-monitor all seasons
|
||||||
foreach (var season in series.seasons)
|
foreach (var season in series.seasons)
|
||||||
|
@ -112,6 +113,12 @@ namespace PlexRequests.UI.Helpers
|
||||||
}
|
}
|
||||||
|
|
||||||
if (series != null)
|
if (series != null)
|
||||||
|
{
|
||||||
|
var requestAll = model.SeasonsRequested.Equals("All", StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
var first = model.SeasonsRequested.Equals("First", StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
var latest = model.SeasonsRequested.Equals("Latest", StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
|
if (model.SeasonList.Any())
|
||||||
{
|
{
|
||||||
// Monitor the seasons that we have chosen
|
// Monitor the seasons that we have chosen
|
||||||
foreach (var season in series.seasons)
|
foreach (var season in series.seasons)
|
||||||
|
@ -121,6 +128,29 @@ namespace PlexRequests.UI.Helpers
|
||||||
season.monitored = true;
|
season.monitored = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestAll)
|
||||||
|
{
|
||||||
|
// Monitor all seasons
|
||||||
|
foreach (var season in series.seasons)
|
||||||
|
{
|
||||||
|
season.monitored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
var firstSeries = series?.seasons?.OrderBy(x => x.seasonNumber)?.FirstOrDefault() ?? new Season();
|
||||||
|
firstSeries.monitored = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latest)
|
||||||
|
{
|
||||||
|
var lastSeries = series?.seasons?.OrderByDescending(x => x.seasonNumber)?.FirstOrDefault() ?? new Season();
|
||||||
|
lastSeries.monitored = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update the series in sonarr with the new monitored status
|
// Update the series in sonarr with the new monitored status
|
||||||
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
|
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
|
||||||
|
@ -230,9 +260,22 @@ namespace PlexRequests.UI.Helpers
|
||||||
|
|
||||||
var internalEpisodeIds = new List<int>();
|
var internalEpisodeIds = new List<int>();
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
|
var requestedEpisodes = model.Episodes;
|
||||||
|
|
||||||
foreach (var r in episodes)
|
foreach (var r in episodes)
|
||||||
{
|
{
|
||||||
if (r.hasFile || !model.SeasonList.Contains(r.seasonNumber)) // If it already has the file, there is no point in updating it
|
if (r.hasFile) // If it already has the file, there is no point in updating it
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var epComparison = new EpisodesModel
|
||||||
|
{
|
||||||
|
EpisodeNumber = r.episodeNumber,
|
||||||
|
SeasonNumber = r.seasonNumber
|
||||||
|
};
|
||||||
|
// Make sure we are looking for the right episode and season
|
||||||
|
if (!requestedEpisodes.Contains(epComparison))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,5 +55,7 @@ namespace PlexRequests.UI.Models
|
||||||
public QualityModel[] Qualities { get; set; }
|
public QualityModel[] Qualities { get; set; }
|
||||||
public string ArtistName { get; set; }
|
public string ArtistName { get; set; }
|
||||||
public Store.EpisodesModel[] Episodes { get; set; }
|
public Store.EpisodesModel[] Episodes { get; set; }
|
||||||
|
public bool Denied { get; set; }
|
||||||
|
public string DeniedReason { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,16 +175,16 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
Get["/emailnotification"] = _ => EmailNotifications();
|
Get["/emailnotification"] = _ => EmailNotifications();
|
||||||
Post["/emailnotification"] = _ => SaveEmailNotifications();
|
Post["/emailnotification"] = _ => SaveEmailNotifications();
|
||||||
Post["/testemailnotification"] = _ => TestEmailNotifications();
|
Post["/testemailnotification", true] = async (x, ct) => await TestEmailNotifications();
|
||||||
Get["/status", true] = async (x, ct) => await Status();
|
Get["/status", true] = async (x, ct) => await Status();
|
||||||
|
|
||||||
Get["/pushbulletnotification"] = _ => PushbulletNotifications();
|
Get["/pushbulletnotification"] = _ => PushbulletNotifications();
|
||||||
Post["/pushbulletnotification"] = _ => SavePushbulletNotifications();
|
Post["/pushbulletnotification"] = _ => SavePushbulletNotifications();
|
||||||
Post["/testpushbulletnotification"] = _ => TestPushbulletNotifications();
|
Post["/testpushbulletnotification", true] = async (x, ct) => await TestPushbulletNotifications();
|
||||||
|
|
||||||
Get["/pushovernotification"] = _ => PushoverNotifications();
|
Get["/pushovernotification"] = _ => PushoverNotifications();
|
||||||
Post["/pushovernotification"] = _ => SavePushoverNotifications();
|
Post["/pushovernotification"] = _ => SavePushoverNotifications();
|
||||||
Post["/testpushovernotification"] = _ => TestPushoverNotifications();
|
Post["/testpushovernotification", true] = async (x, ct) => await TestPushoverNotifications();
|
||||||
|
|
||||||
Get["/logs"] = _ => Logs();
|
Get["/logs"] = _ => Logs();
|
||||||
Get["/loglevel"] = _ => GetLogLevels();
|
Get["/loglevel"] = _ => GetLogLevels();
|
||||||
|
@ -198,7 +198,7 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
Post["/autoupdate"] = x => AutoUpdate();
|
Post["/autoupdate"] = x => AutoUpdate();
|
||||||
|
|
||||||
Post["/testslacknotification"] = _ => TestSlackNotification();
|
Post["/testslacknotification", true] = async (x,ct) => await TestSlackNotification();
|
||||||
|
|
||||||
Get["/slacknotification"] = _ => SlackNotifications();
|
Get["/slacknotification"] = _ => SlackNotifications();
|
||||||
Post["/slacknotification"] = _ => SaveSlackNotifications();
|
Post["/slacknotification"] = _ => SaveSlackNotifications();
|
||||||
|
@ -477,7 +477,7 @@ namespace PlexRequests.UI.Modules
|
||||||
return View["EmailNotifications", settings];
|
return View["EmailNotifications", settings];
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response TestEmailNotifications()
|
private async Task<Response> TestEmailNotifications()
|
||||||
{
|
{
|
||||||
var settings = this.Bind<EmailNotificationSettings>();
|
var settings = this.Bind<EmailNotificationSettings>();
|
||||||
var valid = this.Validate(settings);
|
var valid = this.Validate(settings);
|
||||||
|
@ -485,6 +485,7 @@ namespace PlexRequests.UI.Modules
|
||||||
{
|
{
|
||||||
return Response.AsJson(valid.SendJsonError());
|
return Response.AsJson(valid.SendJsonError());
|
||||||
}
|
}
|
||||||
|
var currentSettings = await EmailService.GetSettingsAsync();
|
||||||
var notificationModel = new NotificationModel
|
var notificationModel = new NotificationModel
|
||||||
{
|
{
|
||||||
NotificationType = NotificationType.Test,
|
NotificationType = NotificationType.Test,
|
||||||
|
@ -502,9 +503,12 @@ namespace PlexRequests.UI.Modules
|
||||||
Log.Error("Failed to subscribe and publish test Email Notification");
|
Log.Error("Failed to subscribe and publish test Email Notification");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (!currentSettings.Enabled)
|
||||||
{
|
{
|
||||||
NotificationService.UnSubscribe(new EmailMessageNotification(EmailService));
|
NotificationService.UnSubscribe(new EmailMessageNotification(EmailService));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Email Notification!" });
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Email Notification!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +599,7 @@ namespace PlexRequests.UI.Modules
|
||||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response TestPushbulletNotifications()
|
private async Task<Response> TestPushbulletNotifications()
|
||||||
{
|
{
|
||||||
var settings = this.Bind<PushbulletNotificationSettings>();
|
var settings = this.Bind<PushbulletNotificationSettings>();
|
||||||
var valid = this.Validate(settings);
|
var valid = this.Validate(settings);
|
||||||
|
@ -608,6 +612,7 @@ namespace PlexRequests.UI.Modules
|
||||||
NotificationType = NotificationType.Test,
|
NotificationType = NotificationType.Test,
|
||||||
DateTime = DateTime.Now
|
DateTime = DateTime.Now
|
||||||
};
|
};
|
||||||
|
var currentSettings = await PushbulletService.GetSettingsAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
|
NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
|
||||||
|
@ -620,9 +625,12 @@ namespace PlexRequests.UI.Modules
|
||||||
Log.Error("Failed to subscribe and publish test Pushbullet Notification");
|
Log.Error("Failed to subscribe and publish test Pushbullet Notification");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (!currentSettings.Enabled)
|
||||||
{
|
{
|
||||||
NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
|
NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushbullet Notification!" });
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushbullet Notification!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,7 +665,7 @@ namespace PlexRequests.UI.Modules
|
||||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response TestPushoverNotifications()
|
private async Task<Response> TestPushoverNotifications()
|
||||||
{
|
{
|
||||||
var settings = this.Bind<PushoverNotificationSettings>();
|
var settings = this.Bind<PushoverNotificationSettings>();
|
||||||
var valid = this.Validate(settings);
|
var valid = this.Validate(settings);
|
||||||
|
@ -670,6 +678,7 @@ namespace PlexRequests.UI.Modules
|
||||||
NotificationType = NotificationType.Test,
|
NotificationType = NotificationType.Test,
|
||||||
DateTime = DateTime.Now
|
DateTime = DateTime.Now
|
||||||
};
|
};
|
||||||
|
var currentSettings = await PushbulletService.GetSettingsAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService));
|
NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService));
|
||||||
|
@ -682,9 +691,12 @@ namespace PlexRequests.UI.Modules
|
||||||
Log.Error("Failed to subscribe and publish test Pushover Notification");
|
Log.Error("Failed to subscribe and publish test Pushover Notification");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (!currentSettings.Enabled)
|
||||||
{
|
{
|
||||||
NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService));
|
NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushover Notification!" });
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushover Notification!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,7 +815,7 @@ namespace PlexRequests.UI.Modules
|
||||||
return Response.AsJson(apiKey);
|
return Response.AsJson(apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response TestSlackNotification()
|
private async Task<Response> TestSlackNotification()
|
||||||
{
|
{
|
||||||
var settings = this.BindAndValidate<SlackNotificationSettings>();
|
var settings = this.BindAndValidate<SlackNotificationSettings>();
|
||||||
if (!ModelValidationResult.IsValid)
|
if (!ModelValidationResult.IsValid)
|
||||||
|
@ -815,11 +827,13 @@ namespace PlexRequests.UI.Modules
|
||||||
NotificationType = NotificationType.Test,
|
NotificationType = NotificationType.Test,
|
||||||
DateTime = DateTime.Now
|
DateTime = DateTime.Now
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var currentSlackSettings = await SlackSettings.GetSettingsAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings));
|
NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings));
|
||||||
settings.Enabled = true;
|
settings.Enabled = true;
|
||||||
NotificationService.Publish(notificationModel, settings);
|
await NotificationService.Publish(notificationModel, settings);
|
||||||
Log.Info("Sent slack notification test");
|
Log.Info("Sent slack notification test");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -827,9 +841,12 @@ namespace PlexRequests.UI.Modules
|
||||||
Log.Error(e, "Failed to subscribe and publish test Slack Notification");
|
Log.Error(e, "Failed to subscribe and publish test Slack Notification");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (!currentSlackSettings.Enabled)
|
||||||
{
|
{
|
||||||
NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings));
|
NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Slack Notification! If you do not receive it please check the logs." });
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Slack Notification! If you do not receive it please check the logs." });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
using PlexRequests.Core;
|
using PlexRequests.Core;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
|
using PlexRequests.Helpers;
|
||||||
|
|
||||||
namespace PlexRequests.UI.Modules
|
namespace PlexRequests.UI.Modules
|
||||||
{
|
{
|
||||||
|
@ -45,6 +46,9 @@ namespace PlexRequests.UI.Modules
|
||||||
ISettingsService<PlexSettings> plexSettings, ISettingsService<CouchPotatoSettings> cp,
|
ISettingsService<PlexSettings> plexSettings, ISettingsService<CouchPotatoSettings> cp,
|
||||||
ISettingsService<SonarrSettings> sonarr, ISettingsService<SickRageSettings> sr, ISettingsService<HeadphonesSettings> hp) : base("api", pr)
|
ISettingsService<SonarrSettings> sonarr, ISettingsService<SickRageSettings> sr, ISettingsService<HeadphonesSettings> hp) : base("api", pr)
|
||||||
{
|
{
|
||||||
|
Get["GetVersion", "/version"] = x => GetVersion();
|
||||||
|
|
||||||
|
|
||||||
Get["GetAuthSettings", "/settings/authentication"] = x => GetAuthSettings();
|
Get["GetAuthSettings", "/settings/authentication"] = x => GetAuthSettings();
|
||||||
Post["PostAuthSettings", "/settings/authentication"] = x => PostAuthSettings();
|
Post["PostAuthSettings", "/settings/authentication"] = x => PostAuthSettings();
|
||||||
|
|
||||||
|
@ -83,6 +87,12 @@ namespace PlexRequests.UI.Modules
|
||||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||||
private ISettingsService<HeadphonesSettings> HeadphonesSettings { get; }
|
private ISettingsService<HeadphonesSettings> HeadphonesSettings { get; }
|
||||||
|
|
||||||
|
private Response GetVersion()
|
||||||
|
{
|
||||||
|
return ReturnReponse(AssemblyHelper.GetProductVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private Response GetPrSettings()
|
private Response GetPrSettings()
|
||||||
{
|
{
|
||||||
var model = new ApiModel<PlexRequestSettings>();
|
var model = new ApiModel<PlexRequestSettings>();
|
||||||
|
|
|
@ -65,6 +65,7 @@ namespace PlexRequests.UI.Modules
|
||||||
HeadphoneApi = hpApi;
|
HeadphoneApi = hpApi;
|
||||||
|
|
||||||
Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
|
Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
|
||||||
|
Post["/deny", true] = async (x, ct) => await DenyRequest((int)Request.Form.requestid, (string)Request.Form.reason);
|
||||||
Post["/approveall", true] = async (x, ct) => await ApproveAll();
|
Post["/approveall", true] = async (x, ct) => await ApproveAll();
|
||||||
Post["/approveallmovies", true] = async (x, ct) => await ApproveAllMovies();
|
Post["/approveallmovies", true] = async (x, ct) => await ApproveAllMovies();
|
||||||
Post["/approvealltvshows", true] = async (x, ct) => await ApproveAllTVShows();
|
Post["/approvealltvshows", true] = async (x, ct) => await ApproveAllTVShows();
|
||||||
|
@ -491,6 +492,24 @@ namespace PlexRequests.UI.Modules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Response> DenyRequest(int requestId, string reason)
|
||||||
|
{
|
||||||
|
// Get the request from the DB
|
||||||
|
var request = await Service.GetAsync(requestId);
|
||||||
|
|
||||||
|
// Deny it
|
||||||
|
request.Denied = true;
|
||||||
|
request.DeniedReason = reason;
|
||||||
|
|
||||||
|
// Update the new value
|
||||||
|
var result = await Service.UpdateRequestAsync(request);
|
||||||
|
|
||||||
|
return result
|
||||||
|
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Request has been denied" })
|
||||||
|
: Response.AsJson(new JsonResponseModel { Result = false, Message = "An error happened, could not update the DB" });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
||||||
{
|
{
|
||||||
Log.Info("Adding movie to CP : {0}", r.Title);
|
Log.Info("Adding movie to CP : {0}", r.Title);
|
||||||
|
|
|
@ -53,14 +53,15 @@ namespace PlexRequests.UI.Modules
|
||||||
private Response CheckAuth()
|
private Response CheckAuth()
|
||||||
{
|
{
|
||||||
var settings = PlexRequestSettings.GetSettings();
|
var settings = PlexRequestSettings.GetSettings();
|
||||||
|
|
||||||
|
var baseUrl = settings.BaseUrl;
|
||||||
|
|
||||||
// Have we been through the wizard?
|
// Have we been through the wizard?
|
||||||
if (!settings.Wizard)
|
if (!settings.Wizard)
|
||||||
{
|
{
|
||||||
return Context.GetRedirect("~/wizard");
|
return Context.GetRedirect(string.IsNullOrEmpty(baseUrl) ? "~/wizard" : $"~/{baseUrl}/wizard");
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseUrl = settings.BaseUrl;
|
|
||||||
|
|
||||||
var redirectPath = string.IsNullOrEmpty(baseUrl) ? "~/userlogin" : $"~/{baseUrl}/userlogin";
|
var redirectPath = string.IsNullOrEmpty(baseUrl) ? "~/userlogin" : $"~/{baseUrl}/userlogin";
|
||||||
|
|
||||||
return Session[SessionKeys.UsernameKey] == null
|
return Session[SessionKeys.UsernameKey] == null
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
|
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: LoginModule.cs
|
// File: LoginModule.cs
|
||||||
|
@ -23,10 +24,12 @@
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
|
using System.Security;
|
||||||
using Nancy;
|
using Nancy;
|
||||||
using Nancy.Authentication.Forms;
|
using Nancy.Authentication.Forms;
|
||||||
using Nancy.Extensions;
|
using Nancy.Extensions;
|
||||||
|
@ -43,7 +46,8 @@ namespace PlexRequests.UI.Modules
|
||||||
{
|
{
|
||||||
public class LoginModule : BaseModule
|
public class LoginModule : BaseModule
|
||||||
{
|
{
|
||||||
public LoginModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IResourceLinker linker) : base(pr)
|
public LoginModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IResourceLinker linker)
|
||||||
|
: base(pr)
|
||||||
{
|
{
|
||||||
UserMapper = m;
|
UserMapper = m;
|
||||||
Get["/login"] = _ =>
|
Get["/login"] = _ =>
|
||||||
|
@ -61,7 +65,14 @@ namespace PlexRequests.UI.Modules
|
||||||
return View["Index", model];
|
return View["Index", model];
|
||||||
};
|
};
|
||||||
|
|
||||||
Get["/logout"] = x => this.LogoutAndRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/" : "~/");
|
Get["/logout"] = x =>
|
||||||
|
{
|
||||||
|
if (Session[SessionKeys.UsernameKey] != null)
|
||||||
|
{
|
||||||
|
Session.Delete(SessionKeys.UsernameKey);
|
||||||
|
}
|
||||||
|
return this.LogoutAndRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/" : "~/");
|
||||||
|
};
|
||||||
|
|
||||||
Post["/login"] = x =>
|
Post["/login"] = x =>
|
||||||
{
|
{
|
||||||
|
@ -74,7 +85,10 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
{
|
{
|
||||||
return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/login?error=true&username=" + username : "~/login?error=true&username=" + username);
|
return
|
||||||
|
Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl)
|
||||||
|
? $"~/{BaseUrl}/login?error=true&username=" + username
|
||||||
|
: "~/login?error=true&username=" + username);
|
||||||
}
|
}
|
||||||
DateTime? expiry = null;
|
DateTime? expiry = null;
|
||||||
if (Request.Form.RememberMe.HasValue)
|
if (Request.Form.RememberMe.HasValue)
|
||||||
|
@ -106,7 +120,10 @@ namespace PlexRequests.UI.Modules
|
||||||
var exists = UserMapper.DoUsersExist();
|
var exists = UserMapper.DoUsersExist();
|
||||||
if (exists)
|
if (exists)
|
||||||
{
|
{
|
||||||
return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/register?error=true" : "~/register?error=true");
|
return
|
||||||
|
Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl)
|
||||||
|
? $"~/{BaseUrl}/register?error=true"
|
||||||
|
: "~/register?error=true");
|
||||||
}
|
}
|
||||||
var userId = UserMapper.CreateAdmin(username, Request.Form.Password);
|
var userId = UserMapper.CreateAdmin(username, Request.Form.Password);
|
||||||
Session[SessionKeys.UsernameKey] = username;
|
Session[SessionKeys.UsernameKey] = username;
|
||||||
|
@ -116,6 +133,7 @@ namespace PlexRequests.UI.Modules
|
||||||
Get["/changepassword"] = _ => ChangePassword();
|
Get["/changepassword"] = _ => ChangePassword();
|
||||||
Post["/changepassword"] = _ => ChangePasswordPost();
|
Post["/changepassword"] = _ => ChangePasswordPost();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ICustomUserMapper UserMapper { get; }
|
private ICustomUserMapper UserMapper { get; }
|
||||||
|
|
||||||
private Negotiator ChangePassword()
|
private Negotiator ChangePassword()
|
||||||
|
@ -141,7 +159,8 @@ namespace PlexRequests.UI.Modules
|
||||||
{
|
{
|
||||||
return Response.AsJson(new JsonResponseModel { Message = "The passwords do not match", Result = false });
|
return Response.AsJson(new JsonResponseModel { Message = "The passwords do not match", Result = false });
|
||||||
}
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
var result = UserMapper.UpdatePassword(username, oldPass, newPassword);
|
var result = UserMapper.UpdatePassword(username, oldPass, newPassword);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
@ -150,5 +169,10 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
return Response.AsJson(new JsonResponseModel { Message = "Could not update the password in the database", Result = false });
|
return Response.AsJson(new JsonResponseModel { Message = "Could not update the password in the database", Result = false });
|
||||||
}
|
}
|
||||||
|
catch (SecurityException e)
|
||||||
|
{
|
||||||
|
return Response.AsJson(new JsonResponseModel { Message = e.ToString(), Result = false });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -178,6 +178,8 @@ namespace PlexRequests.UI.Modules
|
||||||
Available = movie.Available,
|
Available = movie.Available,
|
||||||
Admin = IsAdmin,
|
Admin = IsAdmin,
|
||||||
IssueId = movie.IssueId,
|
IssueId = movie.IssueId,
|
||||||
|
Denied = movie.Denied,
|
||||||
|
DeniedReason = movie.DeniedReason,
|
||||||
Qualities = qualities.ToArray()
|
Qualities = qualities.ToArray()
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
@ -249,6 +251,8 @@ namespace PlexRequests.UI.Modules
|
||||||
Available = tv.Available,
|
Available = tv.Available,
|
||||||
Admin = IsAdmin,
|
Admin = IsAdmin,
|
||||||
IssueId = tv.IssueId,
|
IssueId = tv.IssueId,
|
||||||
|
Denied = tv.Denied,
|
||||||
|
DeniedReason = tv.DeniedReason,
|
||||||
TvSeriesRequestType = tv.SeasonsRequested,
|
TvSeriesRequestType = tv.SeasonsRequested,
|
||||||
Qualities = qualities.ToArray(),
|
Qualities = qualities.ToArray(),
|
||||||
Episodes = tv.Episodes.ToArray(),
|
Episodes = tv.Episodes.ToArray(),
|
||||||
|
@ -290,6 +294,8 @@ namespace PlexRequests.UI.Modules
|
||||||
Available = album.Available,
|
Available = album.Available,
|
||||||
Admin = IsAdmin,
|
Admin = IsAdmin,
|
||||||
IssueId = album.IssueId,
|
IssueId = album.IssueId,
|
||||||
|
Denied = album.Denied,
|
||||||
|
DeniedReason = album.DeniedReason,
|
||||||
TvSeriesRequestType = album.SeasonsRequested,
|
TvSeriesRequestType = album.SeasonsRequested,
|
||||||
MusicBrainzId = album.MusicBrainzId,
|
MusicBrainzId = album.MusicBrainzId,
|
||||||
ArtistName = album.ArtistName
|
ArtistName = album.ArtistName
|
||||||
|
|
|
@ -657,7 +657,11 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
return await AddUserToRequest(existingRequest, settings, fullShowName, true);
|
return await AddUserToRequest(existingRequest, settings, fullShowName, true);
|
||||||
}
|
}
|
||||||
// We have an episode that has not yet been requested, let's continue
|
else
|
||||||
|
{
|
||||||
|
// We no episodes to approve
|
||||||
|
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (model.SeasonList.Except(existingRequest.SeasonList).Any())
|
else if (model.SeasonList.Except(existingRequest.SeasonList).Any())
|
||||||
{
|
{
|
||||||
|
@ -1033,7 +1037,8 @@ namespace PlexRequests.UI.Modules
|
||||||
Name = ep.name,
|
Name = ep.name,
|
||||||
EpisodeId = ep.id
|
EpisodeId = ep.id
|
||||||
});
|
});
|
||||||
}return model;
|
}
|
||||||
|
return model;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,7 +216,7 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
private bool IsUserInDeniedList(string username, AuthenticationSettings settings)
|
private bool IsUserInDeniedList(string username, AuthenticationSettings settings)
|
||||||
{
|
{
|
||||||
return settings.DeniedUserList.Any(x => x.Equals(username));
|
return settings.DeniedUserList.Any(x => x.Equals(username, StringComparison.CurrentCultureIgnoreCase));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -64,6 +64,7 @@ namespace PlexRequests.UI.Modules
|
||||||
a.TrackEventAsync(Category.Wizard, Action.Start, "Started the wizard", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
a.TrackEventAsync(Category.Wizard, Action.Start, "Started the wizard", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||||
|
|
||||||
var settings = await PlexRequestSettings.GetSettingsAsync();
|
var settings = await PlexRequestSettings.GetSettingsAsync();
|
||||||
|
|
||||||
if (settings.Wizard)
|
if (settings.Wizard)
|
||||||
{
|
{
|
||||||
return Context.GetRedirect("~/search");
|
return Context.GetRedirect("~/search");
|
||||||
|
|
|
@ -171,7 +171,7 @@
|
||||||
generateNotify("Success!", "success");
|
generateNotify("Success!", "success");
|
||||||
$('#ApiKey').val(response.apiKey);
|
$('#ApiKey').val(response.apiKey);
|
||||||
} else {
|
} else {
|
||||||
generateNotify(response.message, "warning");
|
generateNotify("Could not automatically get the API key", "warning");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (e) {
|
error: function (e) {
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<p class="form-group">Notice Message</p>
|
<p class="form-group">Notice Message</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control-custom form-control " id="NoticeMessage" name="NoticeMessage" placeholder="e.g. Plex will be down for maintaince" value="@Model.NoticeMessage">
|
<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"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@using Nancy.Security
|
@using Nancy.Security
|
||||||
|
@using Nancy.Security
|
||||||
@using PlexRequests.UI.Helpers
|
@using PlexRequests.UI.Helpers
|
||||||
@using PlexRequests.UI.Resources
|
@using PlexRequests.UI.Resources
|
||||||
@{
|
@{
|
||||||
|
@ -170,12 +171,21 @@
|
||||||
<span class="label label-success">{{status}}</span>
|
<span class="label label-success">{{status}}</span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
{{#if denied}}
|
||||||
|
<div>
|
||||||
|
Denied: <i style="color:red;" class="fa fa-check"></i>
|
||||||
|
{{#if deniedReason}}
|
||||||
|
<span class="customTooltip" title="{{deniedReason}}"><i class="fa fa-info-circle"></i></span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
{{#if_eq releaseDate "01/01/0001 00:00:00"}} <!--TheTVDB didn't provide any premier info-->
|
{{#if_eq releaseDate "01/01/0001 00:00:00"}} <!--TheTVDB didn't provide any premier info-->
|
||||||
<div>@UI.Requests_ReleaseDate: {{releaseDate}}</div>
|
<div>@UI.Requests_ReleaseDate: {{releaseDate}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div>@UI.Requests_ReleaseDate: {{releaseDate}}</div>
|
<div>@UI.Requests_ReleaseDate: {{releaseDate}}</div>
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
|
{{#unless denied}}
|
||||||
<div>
|
<div>
|
||||||
@UI.Common_Approved:
|
@UI.Common_Approved:
|
||||||
{{#if_eq approved false}}
|
{{#if_eq approved false}}
|
||||||
|
@ -185,6 +195,7 @@
|
||||||
<i class="fa fa-check"></i>
|
<i class="fa fa-check"></i>
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
</div>
|
</div>
|
||||||
|
{{/unless}}
|
||||||
<div>
|
<div>
|
||||||
@UI.Requests_Available
|
@UI.Requests_Available
|
||||||
{{#if_eq available false}}
|
{{#if_eq available false}}
|
||||||
|
@ -216,6 +227,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-3 col-sm-push-3">
|
<div class="col-sm-3 col-sm-push-3">
|
||||||
{{#if_eq admin true}}
|
{{#if_eq admin true}}
|
||||||
|
|
||||||
{{#if_eq approved false}}
|
{{#if_eq approved false}}
|
||||||
<form method="POST" action="@formAction/approval/approve" id="approve{{requestId}}">
|
<form method="POST" action="@formAction/approval/approve" id="approve{{requestId}}">
|
||||||
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
||||||
|
@ -236,6 +248,22 @@
|
||||||
<button id="{{requestId}}" custom-button="{{requestId}}" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> @UI.Common_Approve</button>
|
<button id="{{requestId}}" custom-button="{{requestId}}" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> @UI.Common_Approve</button>
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
</form>
|
</form>
|
||||||
|
{{#unless denied}}
|
||||||
|
<form method="POST" action="@formAction/approval/deny" id="deny{{requestId}}">
|
||||||
|
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
||||||
|
<input name="reason" type="text" hidden="hidden" />
|
||||||
|
<div class="btn-group btn-split">
|
||||||
|
<button type="button" class="btn btn-sm btn-danger-outline deny" id="{{requestId}}deny" custom-button="{{requestId}}"><i class="fa fa-times"></i> Deny</button>
|
||||||
|
<button type="button" class="btn btn-danger-outline dropdown-toggle" id="{{requestId}}denyToggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">@UI.Requests_ToggleDropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="deny-with-reason" id="denyReason{{requestId}}" href="#" data-toggle="modal" data-identifier="{{requestId}}" data-target="#denyReasonModal">Deny with a reason</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{/unless}}
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
<form method="POST" action="@formAction/requests/delete" id="delete{{requestId}}">
|
<form method="POST" action="@formAction/requests/delete" id="delete{{requestId}}">
|
||||||
<input name="Id" type="text" value="{{requestId}}" hidden="hidden" />
|
<input name="Id" type="text" value="{{requestId}}" hidden="hidden" />
|
||||||
|
@ -324,6 +352,12 @@
|
||||||
<div>@UI.Requests_RequestedBy: {{requestedUsers}}</div>
|
<div>@UI.Requests_RequestedBy: {{requestedUsers}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div>@UI.Requests_RequestedDate: {{requestedDate}}</div>
|
<div>@UI.Requests_RequestedDate: {{requestedDate}}</div>
|
||||||
|
{{#if denied}}
|
||||||
|
<div>Denied: <i class="fa fa-times"></i></div>
|
||||||
|
{{#if deniedReason}}
|
||||||
|
<div>Reason: {{deniedReason}}</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2 col-sm-push-3">
|
<div class="col-sm-2 col-sm-push-3">
|
||||||
{{#if_eq admin true}}
|
{{#if_eq admin true}}
|
||||||
|
@ -332,6 +366,20 @@
|
||||||
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
||||||
<button id="{{requestId}}" custom-button="{{requestId}}" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> @UI.Common_Approve</button>
|
<button id="{{requestId}}" custom-button="{{requestId}}" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> @UI.Common_Approve</button>
|
||||||
</form>
|
</form>
|
||||||
|
<form method="POST" action="@formAction/approval/deny" id="deny{{requestId}}">
|
||||||
|
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
||||||
|
<input name="reason" type="text" hidden="hidden" />
|
||||||
|
<div class="btn-group btn-split">
|
||||||
|
<button type="button" class="btn btn-sm btn-danger-outline deny" id="{{requestId}}deny" custom-button="{{requestId}}"><i class="fa fa-times"></i> Deny</button>
|
||||||
|
<button type="button" class="btn btn-danger-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">@UI.Requests_ToggleDropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="deny-with-reason" id="denyReason{{requestId}}" href="#" data-toggle="modal" data-identifier="{{requestId}}" data-target="#denyReasonModal">Deny with a reason</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
<form method="POST" action="@formAction/requests/delete" id="delete{{requestId}}">
|
<form method="POST" action="@formAction/requests/delete" id="delete{{requestId}}">
|
||||||
<input name="Id" type="text" value="{{requestId}}" hidden="hidden" />
|
<input name="Id" type="text" value="{{requestId}}" hidden="hidden" />
|
||||||
|
@ -393,6 +441,27 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="denyReasonModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times"></i></button>
|
||||||
|
<h4 class="modal-title">Deny with a reason</h4>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="@formAction/approval/deny" id="denyReasonForm">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input name="requestId" class="requestId" type="text" hidden="hidden" value="" />
|
||||||
|
<textarea class="form-control form-control-custom" rows="3" id="denyReason" name="denyReason"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-danger-outline" data-dismiss="modal">@UI.Common_Close</button>
|
||||||
|
<button type="button" class="btn btn-primary-outline denySaveReason" data-dismiss="modal">@UI.Common_Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@Html.LoadRequestAssets()
|
@Html.LoadRequestAssets()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,18 @@
|
||||||
<h4>{{title}} ({{year}})</h4>
|
<h4>{{title}} ({{year}})</h4>
|
||||||
</a>
|
</a>
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
|
{{#if_eq type "tv"}}
|
||||||
|
{{#if available}}
|
||||||
|
<span class="label label-success">Available</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="label label-danger">Not Available</span>
|
||||||
|
{{/if}}
|
||||||
|
{{#if requested}}
|
||||||
|
<span class="label label-success">Requested</span>
|
||||||
|
{{/if}}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{{/if_eq}}
|
||||||
</div>
|
</div>
|
||||||
<p>{{overview}}</p>
|
<p>{{overview}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -176,6 +188,7 @@
|
||||||
{{#if_eq available true}}
|
{{#if_eq available true}}
|
||||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button>
|
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button>
|
||||||
<br />
|
<br />
|
||||||
|
<br />
|
||||||
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
|
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if_eq requested true}}
|
{{#if_eq requested true}}
|
||||||
|
@ -189,7 +202,6 @@
|
||||||
{{#if_eq tvFullyAvailable true}}
|
{{#if_eq tvFullyAvailable true}}
|
||||||
@*//TODO Not used yet*@
|
@*//TODO Not used yet*@
|
||||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button><br />
|
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button><br />
|
||||||
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button id="{{id}}" class="btn btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
<button id="{{id}}" class="btn btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
@ -204,6 +216,10 @@
|
||||||
<li><a id="EpisodeSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#episodesModal" href="#">@UI.Search_SelectEpisode...</a></li>
|
<li><a id="EpisodeSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#episodesModal" href="#">@UI.Search_SelectEpisode...</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{{#if available}}
|
||||||
|
<br />
|
||||||
|
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
|
||||||
|
{{/if}}
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ configuration: Release
|
||||||
assembly_info:
|
assembly_info:
|
||||||
patch: true
|
patch: true
|
||||||
file: '**\AssemblyInfo.*'
|
file: '**\AssemblyInfo.*'
|
||||||
assembly_version: '1.9.1'
|
assembly_version: '1.9.2'
|
||||||
assembly_file_version: '{version}'
|
assembly_file_version: '{version}'
|
||||||
assembly_informational_version: '1.9.1'
|
assembly_informational_version: '1.9.2'
|
||||||
before_build:
|
before_build:
|
||||||
- cmd: appveyor-retry nuget restore
|
- cmd: appveyor-retry nuget restore
|
||||||
build:
|
build:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue