Merge branch 'dev' into patch-1
|
@ -26,6 +26,8 @@
|
|||
#endregion
|
||||
|
||||
using Nancy.Cryptography;
|
||||
using Ombi.Store.Models.Emby;
|
||||
using Ombi.Store.Models.Plex;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.UI.Authentication
|
||||
|
@ -47,7 +49,8 @@ namespace Ombi.UI.Authentication
|
|||
/// <summary>Gets or sets the username/identifier mapper</summary>
|
||||
public IUserRepository LocalUserRepository { get; set; }
|
||||
|
||||
public IPlexUserRepository PlexUserRepository { get; set; }
|
||||
public IExternalUserRepository<PlexUsers> PlexUserRepository { get; set; }
|
||||
public IExternalUserRepository<EmbyUsers> EmbyUserRepository { get; set; }
|
||||
|
||||
/// <summary>Gets or sets RequiresSSL property</summary>
|
||||
/// <value>The flag that indicates whether SSL is required</value>
|
||||
|
|
|
@ -208,11 +208,17 @@ namespace Ombi.UI.Authentication
|
|||
|
||||
var plexUsers = configuration.PlexUserRepository.GetAll();
|
||||
var plexUser = plexUsers.FirstOrDefault(x => Guid.Parse(x.LoginId) == userGuid);
|
||||
var embyUsers = configuration.EmbyUserRepository.GetAll();
|
||||
var embyUser = embyUsers.FirstOrDefault(x => Guid.Parse(x.LoginId) == userGuid);
|
||||
|
||||
if (plexUser != null)
|
||||
{
|
||||
identity.UserName = plexUser.Username;
|
||||
}
|
||||
if (embyUser != null)
|
||||
{
|
||||
identity.UserName = embyUser.Username;
|
||||
}
|
||||
|
||||
var localUsers = configuration.LocalUserRepository.GetAll();
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ using Ombi.Core;
|
|||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models.Emby;
|
||||
using Ombi.Store.Models.Plex;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Authentication;
|
||||
using Ombi.UI.Helpers;
|
||||
|
@ -88,7 +90,8 @@ namespace Ombi.UI
|
|||
var config = new CustomAuthenticationConfiguration
|
||||
{
|
||||
RedirectUrl = redirect,
|
||||
PlexUserRepository = container.Get<IPlexUserRepository>(),
|
||||
PlexUserRepository = container.Get<IExternalUserRepository<PlexUsers>>(),
|
||||
EmbyUserRepository = container.Get<IExternalUserRepository<EmbyUsers>>(),
|
||||
LocalUserRepository = container.Get<IUserRepository>()
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,10 @@
|
|||
<strong>Email Address: </strong><span ng-bind="selectedUser.emailAddress"></span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>User Type: </strong><span ng-bind="selectedUser.type === 1 ? 'Local User' : 'Plex User'"></span>
|
||||
<strong>User Type: </strong>
|
||||
<span ng-if="selectedUser.type === 1">Local User</span>
|
||||
<span ng-if="selectedUser.type === 2">Emby User</span>
|
||||
<span ng-if="selectedUser.type === 3">Plex User</span>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
@ -49,7 +52,7 @@
|
|||
|
||||
|
||||
<button ng-click="updateUser()" class="btn btn-primary-outline">Update</button>
|
||||
<button ng-click="deleteUser()" class="btn btn-danger-outline">Delete</button>
|
||||
<button ng-show="selectedUser.type == 1" ng-click="deleteUser()" class="btn btn-danger-outline">Delete</button>
|
||||
<button ng-click="closeSidebarClick()" style="float: right; margin-right: 10px;" class="btn btn-danger-outline">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -75,7 +75,9 @@
|
|||
{{u.permissionsFormattedString}}
|
||||
</td>
|
||||
<td ng-hide="hideColumns">
|
||||
{{u.type === 1 ? 'Local User' : 'Plex User'}}
|
||||
<span ng-if="u.type === 1">Local User</span>
|
||||
<span ng-if="u.type === 3">Plex User</span>
|
||||
<span ng-if="u.type === 2">Emby User</span>
|
||||
</td>
|
||||
<td ng-hide="hideColumns" ng-bind="u.lastLoggedIn === minDate ? 'Never' : formatDate(u.lastLoggedIn)"></td>
|
||||
<td>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
$scope.searchTerm = "";
|
||||
|
||||
$scope.hideColumns = false;
|
||||
|
||||
|
||||
var ReadOnlyPermission = "Read Only User";
|
||||
var open = false;
|
||||
|
||||
|
|
37
Ombi.UI/Content/base.css
vendored
|
@ -517,3 +517,40 @@ label {
|
|||
background-color: #3e3e3e;
|
||||
border: 1px solid transparent; }
|
||||
|
||||
.wizard-heading {
|
||||
text-align: center; }
|
||||
|
||||
.wizard-img {
|
||||
width: 300px;
|
||||
display: block !important;
|
||||
margin: 0 auto !important; }
|
||||
|
||||
.pace {
|
||||
-webkit-pointer-events: none;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none; }
|
||||
|
||||
.pace-inactive {
|
||||
display: none; }
|
||||
|
||||
.pace .pace-progress {
|
||||
background: #df691a;
|
||||
position: fixed;
|
||||
z-index: 2000;
|
||||
top: 0;
|
||||
right: 100%;
|
||||
width: 100%;
|
||||
height: 5px; }
|
||||
|
||||
.navbar-brand {
|
||||
float: left;
|
||||
padding: 4.5px 15px;
|
||||
font-size: 19px;
|
||||
line-height: 21px;
|
||||
height: 40px; }
|
||||
|
||||
.gravatar {
|
||||
border-radius: 1em; }
|
||||
|
||||
|
|
2
Ombi.UI/Content/base.min.css
vendored
|
@ -641,4 +641,48 @@ $border-radius: 10px;
|
|||
margin-bottom: -1px;
|
||||
background-color: #3e3e3e;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-heading{
|
||||
text-align: center;
|
||||
}
|
||||
.wizard-img{
|
||||
width: 300px;
|
||||
display: block $i;
|
||||
margin: 0 auto $i;
|
||||
}
|
||||
|
||||
.pace {
|
||||
-webkit-pointer-events: none;
|
||||
pointer-events: none;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pace-inactive {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pace .pace-progress {
|
||||
background: $primary-colour;
|
||||
position: fixed;
|
||||
z-index: 2000;
|
||||
top: 0;
|
||||
right: 100%;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
float: left;
|
||||
padding: 4.5px 15px;
|
||||
font-size: 19px;
|
||||
line-height: 21px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.gravatar{
|
||||
border-radius:1em;
|
||||
}
|
||||
|
|
BIN
Ombi.UI/Content/favicon/android-icon-144x144.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Ombi.UI/Content/favicon/android-icon-192x192.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
Ombi.UI/Content/favicon/android-icon-36x36.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Ombi.UI/Content/favicon/android-icon-48x48.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
Ombi.UI/Content/favicon/android-icon-72x72.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Ombi.UI/Content/favicon/android-icon-96x96.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-114x114.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-120x120.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-144x144.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-152x152.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-180x180.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-57x57.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-60x60.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-72x72.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-76x76.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon-precomposed.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Ombi.UI/Content/favicon/apple-icon.png
Normal file
After Width: | Height: | Size: 16 KiB |
2
Ombi.UI/Content/favicon/browserconfig.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
BIN
Ombi.UI/Content/favicon/favicon-16x16.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Ombi.UI/Content/favicon/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Ombi.UI/Content/favicon/favicon-96x96.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
Ombi.UI/Content/favicon/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
41
Ombi.UI/Content/favicon/manifest.json
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "App",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/android-icon-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image\/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image\/png",
|
||||
"density": "1.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image\/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image\/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image\/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "\/android-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
}
|
BIN
Ombi.UI/Content/favicon/ms-icon-144x144.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Ombi.UI/Content/favicon/ms-icon-150x150.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Ombi.UI/Content/favicon/ms-icon-310x310.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
Ombi.UI/Content/favicon/ms-icon-70x70.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
269
Ombi.UI/Content/font-awesome.css
vendored
|
@ -1,13 +1,13 @@
|
|||
/*!
|
||||
* Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
|
||||
*/
|
||||
/* FONT PATH
|
||||
* -------------------------- */
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.5.0');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
@ -118,31 +118,31 @@
|
|||
}
|
||||
}
|
||||
.fa-rotate-90 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.fa-rotate-180 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
|
||||
-webkit-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.fa-rotate-270 {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
|
||||
-webkit-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.fa-flip-horizontal {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
|
||||
-webkit-transform: scale(-1, 1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
.fa-flip-vertical {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
|
||||
-webkit-transform: scale(1, -1);
|
||||
-ms-transform: scale(1, -1);
|
||||
transform: scale(1, -1);
|
||||
|
@ -1383,7 +1383,7 @@
|
|||
.fa-digg:before {
|
||||
content: "\f1a6";
|
||||
}
|
||||
.fa-pied-piper:before {
|
||||
.fa-pied-piper-pp:before {
|
||||
content: "\f1a7";
|
||||
}
|
||||
.fa-pied-piper-alt:before {
|
||||
|
@ -1509,6 +1509,7 @@
|
|||
content: "\f1ce";
|
||||
}
|
||||
.fa-ra:before,
|
||||
.fa-resistance:before,
|
||||
.fa-rebel:before {
|
||||
content: "\f1d0";
|
||||
}
|
||||
|
@ -1831,6 +1832,7 @@
|
|||
content: "\f23e";
|
||||
}
|
||||
.fa-battery-4:before,
|
||||
.fa-battery:before,
|
||||
.fa-battery-full:before {
|
||||
content: "\f240";
|
||||
}
|
||||
|
@ -2084,3 +2086,252 @@
|
|||
.fa-percent:before {
|
||||
content: "\f295";
|
||||
}
|
||||
.fa-gitlab:before {
|
||||
content: "\f296";
|
||||
}
|
||||
.fa-wpbeginner:before {
|
||||
content: "\f297";
|
||||
}
|
||||
.fa-wpforms:before {
|
||||
content: "\f298";
|
||||
}
|
||||
.fa-envira:before {
|
||||
content: "\f299";
|
||||
}
|
||||
.fa-universal-access:before {
|
||||
content: "\f29a";
|
||||
}
|
||||
.fa-wheelchair-alt:before {
|
||||
content: "\f29b";
|
||||
}
|
||||
.fa-question-circle-o:before {
|
||||
content: "\f29c";
|
||||
}
|
||||
.fa-blind:before {
|
||||
content: "\f29d";
|
||||
}
|
||||
.fa-audio-description:before {
|
||||
content: "\f29e";
|
||||
}
|
||||
.fa-volume-control-phone:before {
|
||||
content: "\f2a0";
|
||||
}
|
||||
.fa-braille:before {
|
||||
content: "\f2a1";
|
||||
}
|
||||
.fa-assistive-listening-systems:before {
|
||||
content: "\f2a2";
|
||||
}
|
||||
.fa-asl-interpreting:before,
|
||||
.fa-american-sign-language-interpreting:before {
|
||||
content: "\f2a3";
|
||||
}
|
||||
.fa-deafness:before,
|
||||
.fa-hard-of-hearing:before,
|
||||
.fa-deaf:before {
|
||||
content: "\f2a4";
|
||||
}
|
||||
.fa-glide:before {
|
||||
content: "\f2a5";
|
||||
}
|
||||
.fa-glide-g:before {
|
||||
content: "\f2a6";
|
||||
}
|
||||
.fa-signing:before,
|
||||
.fa-sign-language:before {
|
||||
content: "\f2a7";
|
||||
}
|
||||
.fa-low-vision:before {
|
||||
content: "\f2a8";
|
||||
}
|
||||
.fa-viadeo:before {
|
||||
content: "\f2a9";
|
||||
}
|
||||
.fa-viadeo-square:before {
|
||||
content: "\f2aa";
|
||||
}
|
||||
.fa-snapchat:before {
|
||||
content: "\f2ab";
|
||||
}
|
||||
.fa-snapchat-ghost:before {
|
||||
content: "\f2ac";
|
||||
}
|
||||
.fa-snapchat-square:before {
|
||||
content: "\f2ad";
|
||||
}
|
||||
.fa-pied-piper:before {
|
||||
content: "\f2ae";
|
||||
}
|
||||
.fa-first-order:before {
|
||||
content: "\f2b0";
|
||||
}
|
||||
.fa-yoast:before {
|
||||
content: "\f2b1";
|
||||
}
|
||||
.fa-themeisle:before {
|
||||
content: "\f2b2";
|
||||
}
|
||||
.fa-google-plus-circle:before,
|
||||
.fa-google-plus-official:before {
|
||||
content: "\f2b3";
|
||||
}
|
||||
.fa-fa:before,
|
||||
.fa-font-awesome:before {
|
||||
content: "\f2b4";
|
||||
}
|
||||
.fa-handshake-o:before {
|
||||
content: "\f2b5";
|
||||
}
|
||||
.fa-envelope-open:before {
|
||||
content: "\f2b6";
|
||||
}
|
||||
.fa-envelope-open-o:before {
|
||||
content: "\f2b7";
|
||||
}
|
||||
.fa-linode:before {
|
||||
content: "\f2b8";
|
||||
}
|
||||
.fa-address-book:before {
|
||||
content: "\f2b9";
|
||||
}
|
||||
.fa-address-book-o:before {
|
||||
content: "\f2ba";
|
||||
}
|
||||
.fa-vcard:before,
|
||||
.fa-address-card:before {
|
||||
content: "\f2bb";
|
||||
}
|
||||
.fa-vcard-o:before,
|
||||
.fa-address-card-o:before {
|
||||
content: "\f2bc";
|
||||
}
|
||||
.fa-user-circle:before {
|
||||
content: "\f2bd";
|
||||
}
|
||||
.fa-user-circle-o:before {
|
||||
content: "\f2be";
|
||||
}
|
||||
.fa-user-o:before {
|
||||
content: "\f2c0";
|
||||
}
|
||||
.fa-id-badge:before {
|
||||
content: "\f2c1";
|
||||
}
|
||||
.fa-drivers-license:before,
|
||||
.fa-id-card:before {
|
||||
content: "\f2c2";
|
||||
}
|
||||
.fa-drivers-license-o:before,
|
||||
.fa-id-card-o:before {
|
||||
content: "\f2c3";
|
||||
}
|
||||
.fa-quora:before {
|
||||
content: "\f2c4";
|
||||
}
|
||||
.fa-free-code-camp:before {
|
||||
content: "\f2c5";
|
||||
}
|
||||
.fa-telegram:before {
|
||||
content: "\f2c6";
|
||||
}
|
||||
.fa-thermometer-4:before,
|
||||
.fa-thermometer:before,
|
||||
.fa-thermometer-full:before {
|
||||
content: "\f2c7";
|
||||
}
|
||||
.fa-thermometer-3:before,
|
||||
.fa-thermometer-three-quarters:before {
|
||||
content: "\f2c8";
|
||||
}
|
||||
.fa-thermometer-2:before,
|
||||
.fa-thermometer-half:before {
|
||||
content: "\f2c9";
|
||||
}
|
||||
.fa-thermometer-1:before,
|
||||
.fa-thermometer-quarter:before {
|
||||
content: "\f2ca";
|
||||
}
|
||||
.fa-thermometer-0:before,
|
||||
.fa-thermometer-empty:before {
|
||||
content: "\f2cb";
|
||||
}
|
||||
.fa-shower:before {
|
||||
content: "\f2cc";
|
||||
}
|
||||
.fa-bathtub:before,
|
||||
.fa-s15:before,
|
||||
.fa-bath:before {
|
||||
content: "\f2cd";
|
||||
}
|
||||
.fa-podcast:before {
|
||||
content: "\f2ce";
|
||||
}
|
||||
.fa-window-maximize:before {
|
||||
content: "\f2d0";
|
||||
}
|
||||
.fa-window-minimize:before {
|
||||
content: "\f2d1";
|
||||
}
|
||||
.fa-window-restore:before {
|
||||
content: "\f2d2";
|
||||
}
|
||||
.fa-times-rectangle:before,
|
||||
.fa-window-close:before {
|
||||
content: "\f2d3";
|
||||
}
|
||||
.fa-times-rectangle-o:before,
|
||||
.fa-window-close-o:before {
|
||||
content: "\f2d4";
|
||||
}
|
||||
.fa-bandcamp:before {
|
||||
content: "\f2d5";
|
||||
}
|
||||
.fa-grav:before {
|
||||
content: "\f2d6";
|
||||
}
|
||||
.fa-etsy:before {
|
||||
content: "\f2d7";
|
||||
}
|
||||
.fa-imdb:before {
|
||||
content: "\f2d8";
|
||||
}
|
||||
.fa-ravelry:before {
|
||||
content: "\f2d9";
|
||||
}
|
||||
.fa-eercast:before {
|
||||
content: "\f2da";
|
||||
}
|
||||
.fa-microchip:before {
|
||||
content: "\f2db";
|
||||
}
|
||||
.fa-snowflake-o:before {
|
||||
content: "\f2dc";
|
||||
}
|
||||
.fa-superpowers:before {
|
||||
content: "\f2dd";
|
||||
}
|
||||
.fa-wpexplorer:before {
|
||||
content: "\f2de";
|
||||
}
|
||||
.fa-meetup:before {
|
||||
content: "\f2e0";
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
.sr-only-focusable:active,
|
||||
.sr-only-focusable:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
}
|
||||
|
|
4
Ombi.UI/Content/font-awesome.min.css
vendored
BIN
Ombi.UI/Content/images/emby-logo-dark.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
Ombi.UI/Content/images/emby-logo.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
Ombi.UI/Content/images/logo original.png
Normal file
After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 25 KiB |
BIN
Ombi.UI/Content/images/plex-logo-reversed.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
Ombi.UI/Content/images/plex-logo.png
Normal file
After Width: | Height: | Size: 16 KiB |
45
Ombi.UI/Content/requests.js
vendored
|
@ -95,7 +95,10 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
|||
//if ($tvl.mixItUp('isLoaded')) $tvl.mixItUp('destroy');
|
||||
//$tvl.mixItUp(mixItUpConfig(activeState)); // init or reinit
|
||||
}
|
||||
if (target === "#MoviesTab") {
|
||||
if (target === "#MoviesTab" || target === "#ActorsTab") {
|
||||
if (target === "#ActorsTab") {
|
||||
actorLoad();
|
||||
}
|
||||
$('#approveMovies,#deleteMovies').show();
|
||||
if ($tvl.mixItUp('isLoaded')) {
|
||||
activeState = $tvl.mixItUp('getState');
|
||||
|
@ -564,16 +567,21 @@ $(document).on("click", ".change-root-folder", function (e) {
|
|||
e.preventDefault();
|
||||
var $this = $(this);
|
||||
var $button = $this.parents('.btn-split').children('.change').first();
|
||||
var rootFolderId = e.target.id
|
||||
var rootFolderId = e.target.id;
|
||||
var $form = $this.parents('form').first();
|
||||
|
||||
var requestId = $button.attr('id');
|
||||
|
||||
if ($button.text() === " Loading...") {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingButton($button.attr('id'), "success");
|
||||
loadingButton(requestId, "success");
|
||||
|
||||
changeRootFolder($form, rootFolderId, function () {
|
||||
if ($('#' + requestId + "rootPathMain").length) {
|
||||
$('#' + requestId + "currentRootPath").text($this.text);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -733,6 +741,37 @@ function initLoad() {
|
|||
|
||||
}
|
||||
|
||||
|
||||
function actorLoad() {
|
||||
var $ml = $('#actorMovieList');
|
||||
if ($ml.mixItUp('isLoaded')) {
|
||||
activeState = $ml.mixItUp('getState');
|
||||
$ml.mixItUp('destroy');
|
||||
}
|
||||
$ml.html("");
|
||||
|
||||
var $newOnly = $('#searchNewOnly').val();
|
||||
var url = createBaseUrl(base, '/requests/actor' + (!!$newOnly ? '/new' : ''));
|
||||
$.ajax(url).success(function (results) {
|
||||
if (results.length > 0) {
|
||||
results.forEach(function (result) {
|
||||
var context = buildRequestContext(result, "movie");
|
||||
var html = searchTemplate(context);
|
||||
$ml.append(html);
|
||||
});
|
||||
|
||||
|
||||
$('.customTooltip').tooltipster({
|
||||
contentCloning: true
|
||||
});
|
||||
}
|
||||
else {
|
||||
$ml.html(noResultsHtml.format("movie"));
|
||||
}
|
||||
$ml.mixItUp(mixItUpConfig());
|
||||
});
|
||||
};
|
||||
|
||||
function movieLoad() {
|
||||
var $ml = $('#movieList');
|
||||
if ($ml.mixItUp('isLoaded')) {
|
||||
|
|
50
Ombi.UI/Content/search.js
vendored
|
@ -63,6 +63,26 @@ $(function () {
|
|||
|
||||
});
|
||||
|
||||
// Type in actor search
|
||||
$("#actorSearchContent").on("input", function () {
|
||||
triggerActorSearch();
|
||||
});
|
||||
|
||||
// if they toggle the checkbox, we want to refresh our search
|
||||
$("#actorsSearchNew").click(function () {
|
||||
triggerActorSearch();
|
||||
});
|
||||
|
||||
function triggerActorSearch()
|
||||
{
|
||||
if (searchTimer) {
|
||||
clearTimeout(searchTimer);
|
||||
}
|
||||
searchTimer = setTimeout(function () {
|
||||
moviesFromActor();
|
||||
}.bind(this), 800);
|
||||
}
|
||||
|
||||
$('#moviesComingSoon').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
moviesComingSoon();
|
||||
|
@ -300,7 +320,7 @@ $(function () {
|
|||
function movieSearch() {
|
||||
var query = $("#movieSearchContent").val();
|
||||
var url = createBaseUrl(base, '/search/movie/');
|
||||
query ? getMovies(url + query) : resetMovies();
|
||||
query ? getMovies(url + query) : resetMovies("#movieList");
|
||||
}
|
||||
|
||||
function moviesComingSoon() {
|
||||
|
@ -313,6 +333,13 @@ $(function () {
|
|||
getMovies(url);
|
||||
}
|
||||
|
||||
function moviesFromActor() {
|
||||
var query = $("#actorSearchContent").val();
|
||||
var $newOnly = $('#actorsSearchNew')[0].checked;
|
||||
var url = createBaseUrl(base, '/search/actor/' + (!!$newOnly ? 'new/' : ''));
|
||||
query ? getMovies(url + query, "#actorMovieList", "#actorSearchButton") : resetMovies("#actorMovieList");
|
||||
}
|
||||
|
||||
function popularShows() {
|
||||
var url = createBaseUrl(base, '/search/tv/popular');
|
||||
getTvShows(url, true);
|
||||
|
@ -330,30 +357,31 @@ $(function () {
|
|||
getTvShows(url, true);
|
||||
}
|
||||
|
||||
function getMovies(url) {
|
||||
resetMovies();
|
||||
|
||||
$('#movieSearchButton').attr("class", "fa fa-spinner fa-spin");
|
||||
function getMovies(url, target, button) {
|
||||
target = target || "#movieList";
|
||||
button = button || "#movieSearchButton";
|
||||
resetMovies(target);
|
||||
$(button).attr("class", "fa fa-spinner fa-spin");
|
||||
$.ajax(url).success(function (results) {
|
||||
if (results.length > 0) {
|
||||
results.forEach(function (result) {
|
||||
var context = buildMovieContext(result);
|
||||
|
||||
var html = searchTemplate(context);
|
||||
$("#movieList").append(html);
|
||||
$(target).append(html);
|
||||
|
||||
checkNetflix(context.title, context.id);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$("#movieList").html(noResultsHtml);
|
||||
$(target).html(noResultsHtml);
|
||||
}
|
||||
$('#movieSearchButton').attr("class", "fa fa-search");
|
||||
$(button).attr("class", "fa fa-search");
|
||||
});
|
||||
};
|
||||
|
||||
function resetMovies() {
|
||||
$("#movieList").html("");
|
||||
function resetMovies(target) {
|
||||
$(target).html("");
|
||||
}
|
||||
|
||||
function tvSearch() {
|
||||
|
@ -394,7 +422,7 @@ $(function () {
|
|||
if (results.result) {
|
||||
// It's on Netflix
|
||||
$('#' + id + 'netflixTab')
|
||||
.html("<a href='https://www.netflix.com/watch/"+results.netflixId+"' target='_blank'><span class='label label-success'>Avaialble on Netflix</span></a>");
|
||||
.html("<a href='https://www.netflix.com/watch/"+results.netflixId+"' target='_blank'><span class='label label-success'>Available on Netflix</span></a>");
|
||||
}
|
||||
|
||||
});
|
||||
|
|
41
Ombi.UI/Content/wizard.js
vendored
|
@ -3,9 +3,48 @@
|
|||
// Step 1
|
||||
$('#firstNext')
|
||||
.click(function () {
|
||||
loadArea("plexAuthArea");
|
||||
loadArea("mediaApplicationChoice");
|
||||
});
|
||||
|
||||
|
||||
// Plex click
|
||||
$('#contentBody')
|
||||
.on("click", "#plexImg", function(e) {
|
||||
e.preventDefault();
|
||||
return loadArea("plexAuthArea");
|
||||
});
|
||||
|
||||
|
||||
$('#contentBody')
|
||||
.on("click", "#embyImg", function (e) {
|
||||
e.preventDefault();
|
||||
return loadArea("embyApiKey");
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('#contentBody').on('click', '#embyApiKeySave', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var port = $('#portNumber').val();
|
||||
if (!port) {
|
||||
generateNotify("Please provide a port number", "warning");
|
||||
}
|
||||
|
||||
$('#spinner').attr("class", "fa fa-spinner fa-spin");
|
||||
|
||||
var $form = $("#embyAuthForm");
|
||||
$.post($form.prop("action"), $form.serialize(), function (response) {
|
||||
if (response.result === true) {
|
||||
loadArea("authArea");
|
||||
} else {
|
||||
|
||||
$('#spinner').attr("class", "fa fa-times");
|
||||
generateNotify(response.message, "warning");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Step 2 - Get the auth token
|
||||
$('#contentBody').on('click', '#requestToken', function (e) {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -283,11 +283,30 @@ namespace Ombi.UI.Helpers
|
|||
|
||||
var assetLocation = GetBaseUrl();
|
||||
var content = GetContentUrl(assetLocation);
|
||||
var sb = new StringBuilder();
|
||||
|
||||
var asset = $"<link rel=\"SHORTCUT ICON\" href=\"{content}/Content/favicon.ico\" />";
|
||||
asset += $"<link rel=\"icon\" href=\"{content}/Content/favicon.ico\" type=\"image/ico\" />";
|
||||
sb.Append($"<link rel=\"SHORTCUT ICON\" href=\"{content}/Content/favicon/favicon.ico\" />");
|
||||
sb.Append($"<link rel=\"icon\" href=\"{content}/Content/favicon/favicon.ico?v2\" type=\"image/ico\" />");
|
||||
|
||||
return helper.Raw(asset);
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"57x57\" href=\"{content}/Content/favicon/apple-icon-57x57.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"60x60\" href=\"{content}/Content/favicon/apple-icon-60x60.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"{content}/Content/favicon/apple-icon-72x72.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"76x76\" href=\"{content}/Content/favicon/apple-icon-76x76.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"{content}/Content/favicon/apple-icon-114x114.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"120x120\" href=\"{content}/Content/favicon/apple-icon-120x120.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"144x144\" href=\"{content}/Content/favicon/apple-icon-144x144.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"152x152\" href=\"{content}/Content/favicon/apple-icon-152x152.png?v2\">");
|
||||
sb.Append($"<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"{content}/Content/favicon/apple-icon-180x180.png?v2\">");
|
||||
sb.Append($"<link rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"{content}/Content/favicon/android-icon-192x192.png?v2\">");
|
||||
sb.Append($"<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"{content}/Content/favicon/favicon-32x32.png?v2\">");
|
||||
sb.Append($"<link rel=\"icon\" type=\"image/png\" sizes=\"96x96\" href=\"{content}/Content/favicon/favicon-96x96.png?v2\">");
|
||||
sb.Append($"<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"{content}/Content/favicon/favicon-16x16.png?v2\">");
|
||||
sb.Append($"<link rel=\"manifest\" href=\"{content}/Content/favicon/manifest.json?v2\">");
|
||||
sb.Append($"<meta name=\"msapplication-TileColor\" content=\"#ffffff\">");
|
||||
sb.Append($"<meta name=\"msapplication-TileImage\" content=\"{content}/Content/favicon/ms-icon-144x144.png?v2\">");
|
||||
sb.Append($"<meta name=\"theme-color\" content=\"#ffffff\">");
|
||||
|
||||
return helper.Raw(sb.ToString());
|
||||
}
|
||||
|
||||
public static IHtmlString GetSidebarUrl(this HtmlHelpers helper, NancyContext context, string url, string title, string icon = null)
|
||||
|
@ -314,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>";
|
||||
|
@ -328,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>";
|
||||
|
@ -336,6 +363,14 @@ namespace Ombi.UI.Helpers
|
|||
return helper.Raw(returnString);
|
||||
}
|
||||
|
||||
public static IHtmlString ToolTip(this HtmlHelpers helper, string tooltipText)
|
||||
{
|
||||
//< span class="customTooltip" title="It also requires users to have the Newsletter feature"><i class="fa fa-info-circle"></i></span>
|
||||
return
|
||||
helper.Raw(
|
||||
$"<span class=\"customTooltip\" title=\"{tooltipText}\"><i class=\"fa fa-info-circle\"></i></span>");
|
||||
}
|
||||
|
||||
public static IHtmlString GetBaseUrl(this HtmlHelpers helper)
|
||||
{
|
||||
return helper.Raw(GetBaseUrl());
|
||||
|
@ -346,6 +381,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;
|
||||
|
@ -363,7 +404,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;
|
||||
|
@ -371,6 +412,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}";
|
||||
|
|
|
@ -41,13 +41,14 @@ namespace Ombi.UI.Helpers
|
|||
return helper.Raw(htmlString);
|
||||
}
|
||||
|
||||
public static IHtmlString Checkbox(this HtmlHelpers helper, bool check, string name, string display)
|
||||
public static IHtmlString Checkbox(this HtmlHelpers helper, bool check, string name, string display, string tooltipText = null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("<div class=\"form-group\">");
|
||||
sb.AppendLine("<div class=\"checkbox\">");
|
||||
sb.AppendFormat("<input type=\"checkbox\" id=\"{0}\" name=\"{0}\" {2}><label for=\"{0}\">{1}</label>", name, display, check ? "checked=\"checked\"" : string.Empty);
|
||||
sb.AppendFormat("<input type=\"checkbox\" id=\"{0}\" name=\"{0}\" {2}><label for=\"{0}\">{1} {3}</label>", name, display, check ? "checked=\"checked\"" : string.Empty,
|
||||
string.IsNullOrEmpty(tooltipText) ? string.Empty : helper.ToolTip(tooltipText).ToHtmlString());
|
||||
sb.AppendLine("</div>");
|
||||
sb.AppendLine("</div>");
|
||||
return helper.Raw(sb.ToString());
|
||||
|
|
|
@ -72,9 +72,9 @@ namespace Ombi.UI.Helpers
|
|||
return Security.IsLoggedIn(context);
|
||||
}
|
||||
|
||||
public static bool IsPlexUser(this HtmlHelpers helper)
|
||||
public static bool IsExternalUser(this HtmlHelpers helper)
|
||||
{
|
||||
return Security.IsPlexUser(helper.CurrentUser);
|
||||
return Security.IsExternalUser(helper.CurrentUser);
|
||||
}
|
||||
public static bool IsNormalUser(this HtmlHelpers helper)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
@ -55,9 +56,6 @@ namespace Ombi.UI.Jobs
|
|||
|
||||
private IEnumerable<IJobDetail> CreateJobs()
|
||||
{
|
||||
var settingsService = Service.Resolve<ISettingsService<ScheduledJobsSettings>>();
|
||||
var s = settingsService.GetSettings();
|
||||
|
||||
var jobs = new List<IJobDetail>();
|
||||
|
||||
var jobList = new List<IJobDetail>
|
||||
|
@ -73,9 +71,15 @@ 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(),
|
||||
|
||||
|
||||
JobBuilder.Create<EmbyEpisodeCacher>().WithIdentity("EmbyEpisodeCacher", "Emby").Build(),
|
||||
JobBuilder.Create<EmbyAvailabilityChecker>().WithIdentity("EmbyAvailabilityChecker", "Emby").Build(),
|
||||
JobBuilder.Create<EmbyContentCacher>().WithIdentity("EmbyContentCacher", "Emby").Build(),
|
||||
JobBuilder.Create<EmbyUserChecker>().WithIdentity("EmbyUserChecker", "Emby").Build(),
|
||||
};
|
||||
|
||||
jobs.AddRange(jobList);
|
||||
|
@ -175,6 +179,22 @@ namespace Ombi.UI.Jobs
|
|||
{
|
||||
s.RadarrCacher = 60;
|
||||
}
|
||||
if (s.EmbyContentCacher == 0)
|
||||
{
|
||||
s.EmbyContentCacher = 60;
|
||||
}
|
||||
if (s.EmbyAvailabilityChecker == 0)
|
||||
{
|
||||
s.EmbyAvailabilityChecker = 60;
|
||||
}
|
||||
if (s.EmbyEpisodeCacher == 0)
|
||||
{
|
||||
s.EmbyEpisodeCacher = 11;
|
||||
}
|
||||
if (s.EmbyUserChecker == 0)
|
||||
{
|
||||
s.EmbyUserChecker = 24;
|
||||
}
|
||||
|
||||
var triggers = new List<ITrigger>();
|
||||
|
||||
|
@ -280,6 +300,38 @@ namespace Ombi.UI.Jobs
|
|||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.FaultQueueHandler).RepeatForever())
|
||||
.Build();
|
||||
|
||||
|
||||
//Emby
|
||||
var embyEpisode =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("EmbyEpisodeCacher", "Emby")
|
||||
//.StartNow()
|
||||
.StartAt(DateBuilder.FutureDate(10, IntervalUnit.Minute))
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyEpisodeCacher).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var embyContentCacher =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("EmbyContentCacher", "Emby")
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyContentCacher).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var embyAvailabilityChecker =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("EmbyAvailabilityChecker", "Emby")
|
||||
.StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute))
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyAvailabilityChecker).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var embyUserChecker =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("EmbyUserChecker", "Emby")
|
||||
//.StartNow()
|
||||
.StartAt(DateBuilder.FutureDate(1, IntervalUnit.Minute))
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyUserChecker).RepeatForever())
|
||||
.Build();
|
||||
|
||||
triggers.Add(rencentlyAdded);
|
||||
triggers.Add(plexAvailabilityChecker);
|
||||
triggers.Add(srCacher);
|
||||
|
@ -294,6 +346,10 @@ namespace Ombi.UI.Jobs
|
|||
triggers.Add(plexCacher);
|
||||
triggers.Add(plexUserChecker);
|
||||
triggers.Add(radarrCacher);
|
||||
triggers.Add(embyEpisode);
|
||||
triggers.Add(embyAvailabilityChecker);
|
||||
triggers.Add(embyContentCacher);
|
||||
triggers.Add(embyUserChecker);
|
||||
|
||||
return triggers;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ namespace Ombi.UI.Models
|
|||
{
|
||||
public class ScheduledJobsViewModel : ScheduledJobsSettings
|
||||
{
|
||||
public bool Emby { get; set; }
|
||||
public bool Plex { get; set; }
|
||||
public Dictionary<string,DateTime> JobRecorder { get; set; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -112,7 +112,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
vm.DbLocation = SqlConfig.CurrentPath;
|
||||
|
||||
vm.ApplicationVersion = AssemblyHelper.GetFileVersion();
|
||||
vm.Branch = EnumHelper<Branches>.GetDisplayValue(systemSettings.Branch);
|
||||
vm.Branch = EnumHelper<Branches>.GetBranchValue<BranchAttribute>(systemSettings.Branch).DisplayName;
|
||||
vm.LogLevel = LogManager.Configuration.LoggingRules.FirstOrDefault(x => x.NameMatches("database"))?.Levels?.FirstOrDefault()?.Name ?? "Unknown";
|
||||
|
||||
return vm;
|
||||
|
|
|
@ -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,11 +93,14 @@ 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; }
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private IEmbyApi EmbyApi { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
public AdminModule(ISettingsService<PlexRequestSettings> prService,
|
||||
|
@ -121,10 +125,11 @@ 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
|
||||
IDiscordApi discordapi, ISettingsService<RadarrSettings> settings, IRadarrApi radarrApi,
|
||||
ISettingsService<EmbySettings> embySettings, IEmbyApi emby
|
||||
, ISecurityExtensions security) : base("admin", prService, security)
|
||||
{
|
||||
PrService = prService;
|
||||
|
@ -155,12 +160,15 @@ namespace Ombi.UI.Modules.Admin
|
|||
Analytics = analytics;
|
||||
NotifySettings = notifyService;
|
||||
RecentlyAdded = recentlyAdded;
|
||||
MassEmail = massEmail;
|
||||
WatcherSettings = watcherSettings;
|
||||
DiscordSettings = discord;
|
||||
DiscordApi = discordapi;
|
||||
RadarrSettings = settings;
|
||||
RadarrApi = radarrApi;
|
||||
|
||||
EmbyApi = emby;
|
||||
EmbySettings = embySettings;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
Get["/"] = _ => Admin();
|
||||
|
@ -170,7 +178,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
|
||||
Post["/", true] = async (x, ct) => await SaveAdmin();
|
||||
|
||||
Post["/requestauth"] = _ => RequestAuthToken();
|
||||
Post["/requestauth", true] = async (x, ct) => await RequestAuthToken();
|
||||
|
||||
Get["/getusers"] = _ => GetUsers();
|
||||
|
||||
|
@ -180,6 +188,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
Get["/plex"] = _ => Plex();
|
||||
Post["/plex", true] = async (x, ct) => await SavePlex();
|
||||
|
||||
Get["/emby", true] = async (x, ct) => await Emby();
|
||||
Post["/emby", true] = async (x, ct) => await SaveEmby();
|
||||
|
||||
|
||||
Get["/sonarr"] = _ => Sonarr();
|
||||
Post["/sonarr"] = _ => SaveSonarr();
|
||||
Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles();
|
||||
|
@ -213,6 +225,11 @@ 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();
|
||||
|
||||
Get["/massemail"] = _ => MassEmailView();
|
||||
Post["/testmassadminemail"] = x => TestMassAdminEmail();
|
||||
Post["/sendmassemail"] = x => SendMassEmail();
|
||||
|
||||
Post["/createapikey"] = x => CreateApiKey();
|
||||
|
||||
|
@ -237,7 +254,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()
|
||||
|
@ -303,7 +319,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
: new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" });
|
||||
}
|
||||
|
||||
private Response RequestAuthToken()
|
||||
private async Task<Response> RequestAuthToken()
|
||||
{
|
||||
var user = this.Bind<PlexAuth>();
|
||||
|
||||
|
@ -319,11 +335,11 @@ namespace Ombi.UI.Modules.Admin
|
|||
return Response.AsJson(new { Result = false, Message = "Incorrect username or password!" });
|
||||
}
|
||||
|
||||
var oldSettings = PlexService.GetSettings();
|
||||
var oldSettings = await PlexService.GetSettingsAsync();
|
||||
if (oldSettings != null)
|
||||
{
|
||||
oldSettings.PlexAuthToken = model.user.authentication_token;
|
||||
PlexService.SaveSettings(oldSettings);
|
||||
await PlexService.SaveSettingsAsync(oldSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -331,10 +347,14 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
PlexAuthToken = model.user.authentication_token
|
||||
};
|
||||
PlexService.SaveSettings(newModel);
|
||||
await PlexService.SaveSettingsAsync(newModel);
|
||||
}
|
||||
|
||||
return Response.AsJson(new { Result = true, AuthToken = model.user.authentication_token });
|
||||
var server = PlexApi.GetServer(model.user.authentication_token);
|
||||
var machine =
|
||||
server.Server.FirstOrDefault(x => x.AccessToken == model.user.authentication_token)?.MachineIdentifier;
|
||||
|
||||
return Response.AsJson(new { Result = true, AuthToken = model.user.authentication_token, Identifier = machine });
|
||||
}
|
||||
|
||||
|
||||
|
@ -432,13 +452,32 @@ namespace Ombi.UI.Modules.Admin
|
|||
private async Task<Response> SavePlex()
|
||||
{
|
||||
var plexSettings = this.Bind<PlexSettings>();
|
||||
var valid = this.Validate(plexSettings);
|
||||
if (!valid.IsValid)
|
||||
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
var valid = this.Validate(plexSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Emby is enabled, we cannot enable Plex and Emby"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(plexSettings.MachineIdentifier))
|
||||
if (string.IsNullOrEmpty(plexSettings.MachineIdentifier) && plexSettings.Enable)
|
||||
{
|
||||
//Lookup identifier
|
||||
var server = PlexApi.GetServer(plexSettings.PlexAuthToken);
|
||||
|
@ -453,6 +492,49 @@ namespace Ombi.UI.Modules.Admin
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private async Task<Negotiator> Emby()
|
||||
{
|
||||
var settings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
return View["Emby", settings];
|
||||
}
|
||||
|
||||
private async Task<Response> SaveEmby()
|
||||
{
|
||||
var emby = this.Bind<EmbySettings>();
|
||||
var valid = this.Validate(emby);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
|
||||
if (emby.Enable)
|
||||
{
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Plex is enabled, we cannot enable Plex and Emby"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get the users
|
||||
var users = EmbyApi.GetUsers(emby.FullUri, emby.ApiKey);
|
||||
// Find admin
|
||||
var admin = users.FirstOrDefault(x => x.Policy.IsAdministrator);
|
||||
emby.AdministratorId = admin?.Id;
|
||||
|
||||
var result = await EmbySettings.SaveSettingsAsync(emby);
|
||||
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Emby!" }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private Negotiator Sonarr()
|
||||
{
|
||||
var settings = SonarrService.GetSettings();
|
||||
|
@ -748,6 +830,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
|
||||
|
@ -850,6 +936,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
var settings = await NewsLetterService.GetSettingsAsync();
|
||||
return View["NewsletterSettings", settings];
|
||||
}
|
||||
private Negotiator MassEmailView()
|
||||
{
|
||||
return View["MassEmail"];
|
||||
}
|
||||
|
||||
private async Task<Response> SaveNewsletter()
|
||||
{
|
||||
|
@ -1042,9 +1132,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
|
||||
Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update Landing Page", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
if (string.IsNullOrEmpty(plexSettings.Ip))
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (string.IsNullOrEmpty(plexSettings.Ip) && string.IsNullOrEmpty(embySettings.Ip))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "We cannot enable the landing page if Plex is not setup!" });
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "We cannot enable the landing page if Plex/Emby is not setup!" });
|
||||
}
|
||||
|
||||
if (settings.Enabled && settings.EnabledNoticeTime && string.IsNullOrEmpty(settings.NoticeMessage))
|
||||
|
@ -1063,6 +1154,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
var s = await ScheduledJobSettings.GetSettingsAsync();
|
||||
var allJobs = await JobRecorder.GetJobsAsync();
|
||||
var emby = await EmbySettings.GetSettingsAsync();
|
||||
var plex = await PlexService.GetSettingsAsync();
|
||||
|
||||
|
||||
|
||||
var dict = new Dictionary<string, DateTime>();
|
||||
|
||||
|
@ -1076,13 +1171,32 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var model = new ScheduledJobsViewModel
|
||||
{
|
||||
Emby = emby.Enable,
|
||||
Plex = plex.Enable,
|
||||
CouchPotatoCacher = s.CouchPotatoCacher,
|
||||
PlexAvailabilityChecker = s.PlexAvailabilityChecker,
|
||||
SickRageCacher = s.SickRageCacher,
|
||||
|
@ -1095,7 +1209,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];
|
||||
}
|
||||
|
@ -1158,13 +1278,13 @@ namespace Ombi.UI.Modules.Admin
|
|||
var model = this.Bind<NotificationSettingsV2>();
|
||||
return View["NotificationSettings", model];
|
||||
}
|
||||
|
||||
private Response RecentlyAddedTest()
|
||||
|
||||
private Response TestNewsletterAdminEmail()
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Debug("Clicked TEST");
|
||||
RecentlyAdded.Test();
|
||||
Log.Debug("Clicked Admin Newsletter Email Test");
|
||||
RecentlyAdded.RecentlyAddedAdminTest();
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to administrator" });
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -1173,5 +1293,50 @@ namespace Ombi.UI.Modules.Admin
|
|||
return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });
|
||||
}
|
||||
}
|
||||
private Response TestMassAdminEmail()
|
||||
{
|
||||
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.MassEmailAdminTest(settings.Body.Replace("\n", "<br/>"), settings.Subject);
|
||||
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 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,10 @@
|
|||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
|
@ -48,6 +51,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
Get["Index", "/faultqueue"] = x => Index();
|
||||
Get["DeleteFault", "/deleteFault", true] = async (x,ct) => await DeleteFault(Convert.ToInt32(Request.Form.id));
|
||||
}
|
||||
|
||||
private IRepository<RequestQueue> RequestQueue { get; }
|
||||
|
@ -69,5 +73,35 @@ namespace Ombi.UI.Modules.Admin
|
|||
|
||||
return View["RequestFaultQueue", model];
|
||||
}
|
||||
|
||||
public async Task<Response> DeleteFault(int faultId)
|
||||
{
|
||||
|
||||
if (faultId == 0)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Fault does not exist"
|
||||
});
|
||||
}
|
||||
|
||||
var fault = await RequestQueue.GetAsync(faultId);
|
||||
if (fault == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Fault does not exist"
|
||||
});
|
||||
}
|
||||
|
||||
await RequestQueue.DeleteAsync(fault);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
|
||||
|
||||
Post["/sonarrrootfolders"] = _ => GetSonarrRootFolders();
|
||||
Post["/radarrrootfolders"] = _ => GetSonarrRootFolders();
|
||||
|
||||
Get["/watcher", true] = async (x, ct) => await Watcher();
|
||||
Post["/watcher", true] = async (x, ct) => await SaveWatcher();
|
||||
|
@ -156,7 +157,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);
|
||||
|
@ -191,7 +192,22 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
var settings = this.Bind<SonarrSettings>();
|
||||
|
||||
var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
|
||||
// set the cache
|
||||
if (rootFolders != null)
|
||||
{
|
||||
Cache.Set(CacheKeys.SonarrRootFolders, rootFolders);
|
||||
}
|
||||
|
||||
return Response.AsJson(rootFolders);
|
||||
}
|
||||
|
||||
private Response GetRadarrRootFolders()
|
||||
{
|
||||
var settings = this.Bind<RadarrSettings>();
|
||||
|
||||
var rootFolders = RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
|
||||
// set the cache
|
||||
if (rootFolders != null)
|
||||
|
|
|
@ -33,6 +33,7 @@ using Ombi.Core.SettingModels;
|
|||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Jobs;
|
||||
using Ombi.Services.Jobs.Interfaces;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
|
@ -44,7 +45,8 @@ namespace Ombi.UI.Modules.Admin
|
|||
ISecurityExtensions security, IPlexContentCacher contentCacher, ISonarrCacher sonarrCacher, IWatcherCacher watcherCacher,
|
||||
IRadarrCacher radarrCacher, ICouchPotatoCacher cpCacher, IStoreBackup store, ISickRageCacher srCacher, IAvailabilityChecker plexChceker,
|
||||
IStoreCleanup cleanup, IUserRequestLimitResetter requestLimit, IPlexEpisodeCacher episodeCacher, IRecentlyAdded recentlyAdded,
|
||||
IFaultQueueHandler faultQueueHandler, IPlexUserChecker plexUserChecker) : base("admin", settingsService, security)
|
||||
IFaultQueueHandler faultQueueHandler, IPlexUserChecker plexUserChecker, IEmbyAvailabilityChecker embyAvailabilityChecker, IEmbyEpisodeCacher embyEpisode,
|
||||
IEmbyContentCacher embyContentCacher, IEmbyUserChecker embyUser) : base("admin", settingsService, security)
|
||||
{
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
|
@ -62,6 +64,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
RecentlyAdded = recentlyAdded;
|
||||
FaultQueueHandler = faultQueueHandler;
|
||||
PlexUserChecker = plexUserChecker;
|
||||
EmbyAvailabilityChecker = embyAvailabilityChecker;
|
||||
EmbyContentCacher = embyContentCacher;
|
||||
EmbyEpisodeCacher = embyEpisode;
|
||||
EmbyUserChecker = embyUser;
|
||||
|
||||
Post["/schedulerun", true] = async (x, ct) => await ScheduleRun((string)Request.Form.key);
|
||||
}
|
||||
|
@ -80,10 +86,16 @@ namespace Ombi.UI.Modules.Admin
|
|||
private IRecentlyAdded RecentlyAdded { get; }
|
||||
private IFaultQueueHandler FaultQueueHandler { get; }
|
||||
private IPlexUserChecker PlexUserChecker { get; }
|
||||
private IEmbyAvailabilityChecker EmbyAvailabilityChecker { get; }
|
||||
private IEmbyContentCacher EmbyContentCacher { get; }
|
||||
private IEmbyEpisodeCacher EmbyEpisodeCacher { get; }
|
||||
private IEmbyUserChecker EmbyUserChecker { get; }
|
||||
|
||||
|
||||
private async Task<Response> ScheduleRun(string key)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
if (key.Equals(JobNames.PlexCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
PlexContentCacher.CacheContent();
|
||||
|
@ -132,7 +144,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
}
|
||||
if (key.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
RecentlyAdded.Start();
|
||||
RecentlyAdded.StartNewsLetter();
|
||||
}
|
||||
if (key.Equals(JobNames.FaultQueueHandler, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
|
@ -142,6 +154,22 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
RequestLimit.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.EmbyEpisodeCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
EmbyEpisodeCacher.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.EmbyCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
EmbyContentCacher.CacheContent();
|
||||
}
|
||||
if (key.Equals(JobNames.EmbyChecker, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
EmbyAvailabilityChecker.CheckAndUpdateAll();
|
||||
}
|
||||
if (key.Equals(JobNames.EmbyUserChecker, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
EmbyUserChecker.Start();
|
||||
}
|
||||
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
|
|
|
@ -35,7 +35,10 @@ using MarkdownSharp;
|
|||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Common.Processes;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Core.StatusChecker;
|
||||
using Ombi.Helpers;
|
||||
|
@ -49,11 +52,13 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
public class SystemStatusModule : BaseModule
|
||||
{
|
||||
public SystemStatusModule(ISettingsService<PlexRequestSettings> settingsService, ICacheProvider cache, ISettingsService<SystemSettings> ss, ISecurityExtensions security, IAnalytics a) : base("admin", settingsService, security)
|
||||
public SystemStatusModule(ISettingsService<PlexRequestSettings> settingsService, ICacheProvider cache, ISettingsService<SystemSettings> ss,
|
||||
ISecurityExtensions security, IAnalytics a, IAppveyorApi appveyor) : base("admin", settingsService, security)
|
||||
{
|
||||
Cache = cache;
|
||||
SystemSettings = ss;
|
||||
Analytics = a;
|
||||
AppveyorApi = appveyor;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
|
@ -61,11 +66,13 @@ namespace Ombi.UI.Modules.Admin
|
|||
Post["/save", true] = async (x, ct) => await Save();
|
||||
|
||||
Post["/autoupdate"] = x => AutoUpdate();
|
||||
Get["/changes", true] = async (x, ct) => await GetLatestChanges();
|
||||
}
|
||||
|
||||
private ICacheProvider Cache { get; }
|
||||
private ISettingsService<SystemSettings> SystemSettings { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private IAppveyorApi AppveyorApi { get; }
|
||||
|
||||
private async Task<Negotiator> Status()
|
||||
{
|
||||
|
@ -81,19 +88,19 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
new BranchDropdown
|
||||
{
|
||||
Name = EnumHelper<Branches>.GetDisplayValue(Branches.Stable),
|
||||
Name =EnumHelper<Branches>.GetBranchValue<BranchAttribute>(Branches.Stable).DisplayName,
|
||||
Value = Branches.Stable,
|
||||
Selected = settings.Branch == Branches.Stable
|
||||
},
|
||||
new BranchDropdown
|
||||
{
|
||||
Name = EnumHelper<Branches>.GetDisplayValue(Branches.EarlyAccessPreview),
|
||||
Name = EnumHelper<Branches>.GetBranchValue<BranchAttribute>(Branches.EarlyAccessPreview).DisplayName,
|
||||
Value = Branches.EarlyAccessPreview,
|
||||
Selected = settings.Branch == Branches.EarlyAccessPreview
|
||||
},
|
||||
new BranchDropdown
|
||||
{
|
||||
Name = EnumHelper<Branches>.GetDisplayValue(Branches.Dev),
|
||||
Name = EnumHelper<Branches>.GetBranchValue<BranchAttribute>(Branches.Dev).DisplayName,
|
||||
Value = Branches.Dev,
|
||||
Selected = settings.Branch == Branches.Dev
|
||||
},
|
||||
|
@ -102,12 +109,40 @@ namespace Ombi.UI.Modules.Admin
|
|||
return View["Status", settings];
|
||||
}
|
||||
|
||||
public async Task<Response> GetLatestChanges()
|
||||
{
|
||||
var settings = await SystemSettings.GetSettingsAsync();
|
||||
var branchName = EnumHelper<Branches>.GetBranchValue<BranchAttribute>(settings.Branch).BranchName;
|
||||
var changes = AppveyorApi.GetProjectHistory(branchName);
|
||||
var currentVersion = AssemblyHelper.GetProductVersion();
|
||||
var model = new List<RecentUpdatesModel>();
|
||||
|
||||
foreach (var build in changes.builds)
|
||||
{
|
||||
model.Add(new RecentUpdatesModel
|
||||
{
|
||||
Date = build.finished,
|
||||
Message = BuildAppveyorMessage(build.message, build.messageExtended),
|
||||
Version = build.version,
|
||||
Installed = currentVersion.Equals(build.version, StringComparison.CurrentCultureIgnoreCase) ,
|
||||
Branch = branchName
|
||||
});
|
||||
}
|
||||
|
||||
return Response.AsJson(model);
|
||||
}
|
||||
|
||||
private string BuildAppveyorMessage(string message, string extended)
|
||||
{
|
||||
return extended == null ? message : $"{message} {extended}";
|
||||
}
|
||||
|
||||
private async Task<Response> Save()
|
||||
{
|
||||
|
||||
var settings = this.Bind<SystemSettings>();
|
||||
|
||||
Analytics.TrackEventAsync(Category.Admin, Action.Update, $"Updated Branch {EnumHelper<Branches>.GetDisplayValue(settings.Branch)}", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
Analytics.TrackEventAsync(Category.Admin, Action.Update, $"Updated Branch {EnumHelper<Branches>.GetBranchValue<BranchAttribute>(settings.Branch).DisplayName}", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
await SystemSettings.SaveSettingsAsync(settings);
|
||||
|
||||
// Clear the cache
|
||||
|
@ -123,7 +158,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
var url = Request.Form["url"];
|
||||
var args = (string)Request.Form["args"].ToString();
|
||||
var lowered = args.ToLower();
|
||||
var appPath = Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(typeof(SystemStatusModule)).Location ?? string.Empty) ?? string.Empty, "Ombi.Updater.exe");
|
||||
var appPath = Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(typeof(SystemStatusModule)).Location ?? string.Empty) ?? string.Empty, Path.Combine("UpdateService", "Ombi.Updater.exe"));
|
||||
|
||||
if (!string.IsNullOrEmpty(lowered))
|
||||
{
|
||||
|
@ -133,7 +168,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
}
|
||||
}
|
||||
|
||||
var startArgs = string.IsNullOrEmpty(lowered) ? appPath : $"{lowered} Ombi.Updater.exe";
|
||||
var startArgs = string.IsNullOrEmpty(lowered) || lowered == "Nancy.DynamicDictionaryValue".ToLower() ? appPath : $"{lowered} Ombi.Updater.exe";
|
||||
|
||||
var startInfo = Type.GetType("Mono.Runtime") != null
|
||||
? new ProcessStartInfo(startArgs) { Arguments = $"{url} {lowered}", }
|
||||
|
@ -141,7 +176,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
|
||||
Process.Start(startInfo);
|
||||
|
||||
Environment.Exit(0);
|
||||
//Environment.Exit(0);
|
||||
return Nancy.Response.NoBody;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
public ApplicationTesterModule(ICouchPotatoApi cpApi, ISonarrApi sonarrApi, IPlexApi plexApi,
|
||||
ISickRageApi srApi, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security,
|
||||
IWatcherApi watcherApi, IRadarrApi radarrApi) : base("test", pr, security)
|
||||
IWatcherApi watcherApi, IRadarrApi radarrApi, IEmbyApi emby) : base("test", pr, security)
|
||||
{
|
||||
this.RequiresAuthentication();
|
||||
|
||||
|
@ -57,6 +57,7 @@ namespace Ombi.UI.Modules
|
|||
HeadphonesApi = hpApi;
|
||||
WatcherApi = watcherApi;
|
||||
RadarrApi = radarrApi;
|
||||
Emby = emby;
|
||||
|
||||
Post["/cp"] = _ => CouchPotatoTest();
|
||||
Post["/sonarr"] = _ => SonarrTest();
|
||||
|
@ -66,6 +67,7 @@ namespace Ombi.UI.Modules
|
|||
Post["/headphones"] = _ => HeadphonesTest();
|
||||
Post["/plexdb"] = _ => TestPlexDb();
|
||||
Post["/watcher"] = _ => WatcherTest();
|
||||
Post["/emby"] = _ => EmbyTest();
|
||||
}
|
||||
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
@ -76,6 +78,7 @@ namespace Ombi.UI.Modules
|
|||
private IHeadphonesApi HeadphonesApi { get; }
|
||||
private IWatcherApi WatcherApi { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private IEmbyApi Emby { get; set; }
|
||||
|
||||
private Response CouchPotatoTest()
|
||||
{
|
||||
|
@ -213,7 +216,7 @@ namespace Ombi.UI.Modules
|
|||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Plex, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Plex's status: ");
|
||||
Log.Warn(e);
|
||||
|
@ -225,6 +228,35 @@ namespace Ombi.UI.Modules
|
|||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
private Response EmbyTest()
|
||||
{
|
||||
var emby = this.Bind<EmbySettings>();
|
||||
var valid = this.Validate(emby);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var status = Emby.GetUsers(emby?.FullUri, emby?.ApiKey);
|
||||
return status != null
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to Emby successfully!" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Emby, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Emby's users: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to Emby, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to Emby, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
|
||||
private Response SickRageTest()
|
||||
{
|
||||
|
|
|
@ -193,7 +193,7 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
// Approve it
|
||||
request.Approved = true;
|
||||
Log.Warn("We approved movie: {0} but could not add it to CouchPotato/Watcher because it has not been setup", request.Title);
|
||||
Log.Warn("We approved movie: {0} but could not add it to CouchPotato/Watcher/Radarr because it has not been setup", request.Title);
|
||||
|
||||
// Update the record
|
||||
var inserted = await Service.UpdateRequestAsync(request);
|
||||
|
|
|
@ -145,7 +145,7 @@ namespace Ombi.UI.Modules
|
|||
Deleted = issue.Deleted,
|
||||
Type = issue.Type,
|
||||
ProviderId = issue.ProviderId,
|
||||
PosterUrl = issue.PosterUrl,
|
||||
PosterUrl = issue.PosterUrl.Contains("https://image.tmdb.org/t/p/w150/") ? issue.PosterUrl : $"https://image.tmdb.org/t/p/w150/{issue.PosterUrl}",
|
||||
Id = issue.Id
|
||||
};
|
||||
return View["Details", m];
|
||||
|
|
|
@ -40,12 +40,15 @@ namespace Ombi.UI.Modules
|
|||
public class LandingPageModule : BaseModule
|
||||
{
|
||||
public LandingPageModule(ISettingsService<PlexRequestSettings> settingsService, ISettingsService<LandingPageSettings> landing,
|
||||
ISettingsService<PlexSettings> ps, IPlexApi pApi, IResourceLinker linker, ISecurityExtensions security) : base("landing", settingsService, security)
|
||||
ISettingsService<PlexSettings> ps, IPlexApi pApi, IResourceLinker linker, ISecurityExtensions security, ISettingsService<EmbySettings> emby,
|
||||
IEmbyApi embyApi) : base("landing", settingsService, security)
|
||||
{
|
||||
LandingSettings = landing;
|
||||
PlexSettings = ps;
|
||||
PlexApi = pApi;
|
||||
Linker = linker;
|
||||
EmbySettings = emby;
|
||||
EmbyApi = embyApi;
|
||||
|
||||
Get["LandingPageIndex","/", true] = async (x, ct) =>
|
||||
{
|
||||
|
@ -75,26 +78,49 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private ISettingsService<LandingPageSettings> LandingSettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private IEmbyApi EmbyApi { get; }
|
||||
private IResourceLinker Linker { get; }
|
||||
|
||||
private async Task<Response> CheckStatus()
|
||||
{
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (string.IsNullOrEmpty(plexSettings.PlexAuthToken) || string.IsNullOrEmpty(plexSettings.Ip))
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = PlexApi.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri);
|
||||
return Response.AsJson(status != null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
if (string.IsNullOrEmpty(plexSettings.PlexAuthToken) || string.IsNullOrEmpty(plexSettings.Ip))
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = PlexApi.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri);
|
||||
return Response.AsJson(status != null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
}
|
||||
|
||||
var emby = await EmbySettings.GetSettingsAsync();
|
||||
if (emby.Enable)
|
||||
{
|
||||
if (string.IsNullOrEmpty(emby.AdministratorId) || string.IsNullOrEmpty(emby.Ip))
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = EmbyApi.GetSystemInformation(emby.ApiKey, emby.FullUri);
|
||||
return Response.AsJson(status?.Version != null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
}
|
||||
return Response.AsJson(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,10 +29,12 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Nancy.Responses;
|
||||
using NLog;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Core.StatusChecker;
|
||||
using Ombi.Core.Users;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Jobs;
|
||||
|
@ -43,14 +45,16 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
public class LayoutModule : BaseAuthModule
|
||||
{
|
||||
public LayoutModule(ICacheProvider provider, ISettingsService<PlexRequestSettings> pr, ISettingsService<SystemSettings> settings, IJobRecord rec, ISecurityExtensions security) : base("layout", pr, security)
|
||||
public LayoutModule(ICacheProvider provider, ISettingsService<PlexRequestSettings> pr, ISettingsService<SystemSettings> settings, IJobRecord rec, ISecurityExtensions security, IUserHelper helper) : base("layout", pr, security)
|
||||
{
|
||||
Cache = provider;
|
||||
SystemSettings = settings;
|
||||
Job = rec;
|
||||
UserHelper = helper;
|
||||
|
||||
Get["/", true] = async (x,ct) => await CheckLatestVersion();
|
||||
Get["/cacher", true] = async (x,ct) => await CacherRunning();
|
||||
Get["/gravatar"] = x => GetGravatarImage();
|
||||
}
|
||||
|
||||
private ICacheProvider Cache { get; }
|
||||
|
@ -58,6 +62,7 @@ namespace Ombi.UI.Modules
|
|||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private ISettingsService<SystemSettings> SystemSettings { get; }
|
||||
private IJobRecord Job { get; }
|
||||
private IUserHelper UserHelper { get; }
|
||||
|
||||
private async Task<Response> CheckLatestVersion()
|
||||
{
|
||||
|
@ -116,5 +121,31 @@ namespace Ombi.UI.Modules
|
|||
return Response.AsJson(new { CurrentlyRunning = false, IsAdmin });
|
||||
}
|
||||
}
|
||||
|
||||
private Response GetGravatarImage()
|
||||
{
|
||||
if (LoggedIn)
|
||||
{
|
||||
var user = UserHelper.GetUser(Username);
|
||||
var hashed = StringHasher.CalcuateMd5Hash(user.EmailAddress);
|
||||
if (string.IsNullOrEmpty(hashed))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false
|
||||
});
|
||||
}
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = $"https://www.gravatar.com/avatar/{hashed}"
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel {Result = false});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ using Nancy;
|
|||
using Nancy.Responses.Negotiation;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Sonarr;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.SettingModels;
|
||||
|
@ -65,9 +66,13 @@ namespace Ombi.UI.Modules
|
|||
ISickRageApi sickRageApi,
|
||||
ICacheProvider cache,
|
||||
IAnalytics an,
|
||||
INotificationEngine engine,
|
||||
IPlexNotificationEngine engine,
|
||||
IEmbyNotificationEngine embyEngine,
|
||||
ISecurityExtensions security,
|
||||
ISettingsService<CustomizationSettings> customSettings) : base("requests", prSettings, security)
|
||||
ISettingsService<CustomizationSettings> customSettings,
|
||||
ISettingsService<EmbySettings> embyS,
|
||||
ISettingsService<RadarrSettings> radarr,
|
||||
IRadarrApi radarrApi) : base("requests", prSettings, security)
|
||||
{
|
||||
Service = service;
|
||||
PrSettings = prSettings;
|
||||
|
@ -81,8 +86,12 @@ namespace Ombi.UI.Modules
|
|||
CpApi = cpApi;
|
||||
Cache = cache;
|
||||
Analytics = an;
|
||||
NotificationEngine = engine;
|
||||
PlexNotificationEngine = engine;
|
||||
EmbyNotificationEngine = embyEngine;
|
||||
CustomizationSettings = customSettings;
|
||||
EmbySettings = embyS;
|
||||
Radarr = radarr;
|
||||
RadarrApi = radarrApi;
|
||||
|
||||
Get["/", true] = async (x, ct) => await LoadRequests();
|
||||
Get["/movies", true] = async (x, ct) => await GetMovies();
|
||||
|
@ -96,7 +105,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available);
|
||||
|
||||
Post["/changeRootFolder", true] = async (x, ct) => await ChangeRootFolder((int) Request.Form.requestId, (int) Request.Form.rootFolderId);
|
||||
Post["/changeRootFoldertv", true] = async (x, ct) => await ChangeRootFolder(RequestType.TvShow, (int)Request.Form.requestId, (int)Request.Form.rootFolderId);
|
||||
Post["/changeRootFoldermovie", true] = async (x, ct) => await ChangeRootFolder(RequestType.Movie, (int)Request.Form.requestId, (int)Request.Form.rootFolderId);
|
||||
|
||||
Get["/UpdateFilters", true] = async (x, ct) => await GetFilterAndSortSettings();
|
||||
}
|
||||
|
@ -111,11 +121,15 @@ namespace Ombi.UI.Modules
|
|||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
|
||||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||
private ISettingsService<RadarrSettings> Radarr { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private INotificationEngine NotificationEngine { get; }
|
||||
private INotificationEngine PlexNotificationEngine { get; }
|
||||
private INotificationEngine EmbyNotificationEngine { get; }
|
||||
|
||||
private async Task<Negotiator> LoadRequests()
|
||||
{
|
||||
|
@ -138,32 +152,64 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
|
||||
List<QualityModel> qualities = new List<QualityModel>();
|
||||
var rootFolders = new List<RootFolderModel>();
|
||||
|
||||
var radarr = await Radarr.GetSettingsAsync();
|
||||
if (IsAdmin)
|
||||
{
|
||||
var cpSettings = CpSettings.GetSettings();
|
||||
if (cpSettings.Enabled)
|
||||
try
|
||||
{
|
||||
try
|
||||
var cpSettings = await CpSettings.GetSettingsAsync();
|
||||
if (cpSettings.Enabled)
|
||||
{
|
||||
var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () =>
|
||||
try
|
||||
{
|
||||
return await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey)).ConfigureAwait(false);
|
||||
});
|
||||
if (result != null)
|
||||
var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () =>
|
||||
{
|
||||
return
|
||||
await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey))
|
||||
.ConfigureAwait(false);
|
||||
});
|
||||
if (result != null)
|
||||
{
|
||||
qualities =
|
||||
result.list.Select(x => new QualityModel { Id = x._id, Name = x.label }).ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
qualities = result.list.Select(x => new QualityModel { Id = x._id, Name = x.label }).ToList();
|
||||
Log.Info(e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
if (radarr.Enabled)
|
||||
{
|
||||
Log.Info(e);
|
||||
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.RadarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => RadarrApi.GetRootFolders(radarr.ApiKey, radarr.FullUri));
|
||||
});
|
||||
|
||||
rootFolders =
|
||||
rootFoldersResult.Select(
|
||||
x => new RootFolderModel { Id = x.id.ToString(), Path = x.path, FreeSpace = x.freespace })
|
||||
.ToList();
|
||||
|
||||
var result = await Cache.GetOrSetAsync(CacheKeys.RadarrQualityProfiles, async () =>
|
||||
{
|
||||
return await Task.Run(() => RadarrApi.GetProfiles(radarr.ApiKey, radarr.FullUri));
|
||||
});
|
||||
qualities = result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var allowViewUsers = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ViewUsers);
|
||||
|
||||
var viewModel = dbMovies.Select(movie => new RequestViewModel
|
||||
{
|
||||
ProviderId = movie.ProviderId,
|
||||
|
@ -180,7 +226,7 @@ namespace Ombi.UI.Modules
|
|||
Approved = movie.Available || movie.Approved,
|
||||
Title = movie.Title,
|
||||
Overview = movie.Overview,
|
||||
RequestedUsers = canManageRequest ? movie.AllUsers.ToArray() : new string[] { },
|
||||
RequestedUsers = canManageRequest || allowViewUsers ? movie.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = movie.ReleaseDate.Year.ToString(),
|
||||
Available = movie.Available,
|
||||
Admin = canManageRequest,
|
||||
|
@ -188,6 +234,9 @@ namespace Ombi.UI.Modules
|
|||
Denied = movie.Denied,
|
||||
DeniedReason = movie.DeniedReason,
|
||||
Qualities = qualities.ToArray(),
|
||||
HasRootFolders = rootFolders.Any(),
|
||||
RootFolders = rootFolders.ToArray(),
|
||||
CurrentRootPath = radarr.Enabled ? GetRootPath(movie.RootFolderSelected, radarr).Result : null
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
|
@ -220,14 +269,14 @@ namespace Ombi.UI.Modules
|
|||
});
|
||||
qualities = result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList();
|
||||
|
||||
|
||||
var rootFoldersResult =await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
|
||||
rootFolders = rootFoldersResult.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path, FreeSpace = x.freespace}).ToList();
|
||||
}
|
||||
|
||||
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
|
||||
rootFolders = rootFoldersResult.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path, FreeSpace = x.freespace }).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var sickRageSettings = await SickRageSettings.GetSettingsAsync();
|
||||
|
@ -247,6 +296,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var allowViewUsers = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ViewUsers);
|
||||
|
||||
var viewModel = dbTv.Select(tv => new RequestViewModel
|
||||
{
|
||||
ProviderId = tv.ProviderId,
|
||||
|
@ -254,7 +305,7 @@ namespace Ombi.UI.Modules
|
|||
Status = tv.Status,
|
||||
ImdbId = tv.ImdbId,
|
||||
Id = tv.Id,
|
||||
PosterPath = tv.PosterPath.Contains("http:") ? tv.PosterPath.Replace("http:", "https:") : tv.PosterPath, // We make the poster path https on request, but this is just incase
|
||||
PosterPath = tv.PosterPath?.Contains("http:") ?? false ? tv.PosterPath?.Replace("http:", "https:") : tv.PosterPath ?? string.Empty, // We make the poster path https on request, but this is just incase
|
||||
ReleaseDate = tv.ReleaseDate,
|
||||
ReleaseDateTicks = tv.ReleaseDate.Ticks,
|
||||
RequestedDate = tv.RequestedDate,
|
||||
|
@ -263,7 +314,7 @@ namespace Ombi.UI.Modules
|
|||
Approved = tv.Available || tv.Approved,
|
||||
Title = tv.Title,
|
||||
Overview = tv.Overview,
|
||||
RequestedUsers = canManageRequest ? tv.AllUsers.ToArray() : new string[] { },
|
||||
RequestedUsers = canManageRequest || allowViewUsers ? tv.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = tv.ReleaseDate.Year.ToString(),
|
||||
Available = tv.Available,
|
||||
Admin = canManageRequest,
|
||||
|
@ -273,7 +324,7 @@ namespace Ombi.UI.Modules
|
|||
TvSeriesRequestType = tv.SeasonsRequested,
|
||||
Qualities = qualities.ToArray(),
|
||||
Episodes = tv.Episodes.ToArray(),
|
||||
RootFolders = rootFolders.ToArray(),
|
||||
RootFolders = rootFolders.ToArray(),
|
||||
HasRootFolders = rootFolders.Any(),
|
||||
CurrentRootPath = sonarrSettings.Enabled ? GetRootPath(tv.RootFolderSelected, sonarrSettings).Result : null
|
||||
}).ToList();
|
||||
|
@ -293,13 +344,48 @@ namespace Ombi.UI.Modules
|
|||
return r.path;
|
||||
}
|
||||
|
||||
// Return default path
|
||||
return rootFoldersResult.FirstOrDefault(x => x.id.Equals(int.Parse(sonarrSettings.RootPath)))?.path ?? string.Empty;
|
||||
int outRoot;
|
||||
var defaultPath = int.TryParse(sonarrSettings.RootPath, out outRoot);
|
||||
|
||||
if (defaultPath)
|
||||
{
|
||||
// Return default path
|
||||
return rootFoldersResult.FirstOrDefault(x => x.id.Equals(outRoot))?.path ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rootFoldersResult.FirstOrDefault()?.path ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetRootPath(int pathId, RadarrSettings radarrSettings)
|
||||
{
|
||||
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.RadarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => RadarrApi.GetRootFolders(radarrSettings.ApiKey, radarrSettings.FullUri));
|
||||
});
|
||||
|
||||
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
|
||||
{
|
||||
return r.path;
|
||||
}
|
||||
|
||||
int outRoot;
|
||||
var defaultPath = int.TryParse(radarrSettings.RootPath, out outRoot);
|
||||
|
||||
if (defaultPath)
|
||||
{
|
||||
// Return default path
|
||||
return rootFoldersResult.FirstOrDefault(x => x.id.Equals(outRoot))?.path ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rootFoldersResult.FirstOrDefault()?.path ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> GetAlbumRequests()
|
||||
{
|
||||
var settings = PrSettings.GetSettings();
|
||||
var dbAlbum = await Service.GetAllAsync();
|
||||
dbAlbum = dbAlbum.Where(x => x.Type == RequestType.Album);
|
||||
if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests) && !IsAdmin)
|
||||
|
@ -438,8 +524,21 @@ namespace Ombi.UI.Modules
|
|||
originalRequest.Available = available;
|
||||
|
||||
var result = await Service.UpdateRequestAsync(originalRequest);
|
||||
var plexService = await PlexSettings.GetSettingsAsync();
|
||||
await NotificationEngine.NotifyUsers(originalRequest, plexService.PlexAuthToken, available ? NotificationType.RequestAvailable : NotificationType.RequestDeclined);
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
await
|
||||
PlexNotificationEngine.NotifyUsers(originalRequest,
|
||||
available ? NotificationType.RequestAvailable : NotificationType.RequestDeclined);
|
||||
}
|
||||
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
await EmbyNotificationEngine.NotifyUsers(originalRequest,
|
||||
available ? NotificationType.RequestAvailable : NotificationType.RequestDeclined);
|
||||
}
|
||||
return Response.AsJson(result
|
||||
? new { Result = true, Available = available, Message = string.Empty }
|
||||
: new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" });
|
||||
|
@ -461,11 +560,21 @@ namespace Ombi.UI.Modules
|
|||
return Response.AsJson(vm);
|
||||
}
|
||||
|
||||
private async Task<Response> ChangeRootFolder(int id, int rootFolderId)
|
||||
private async Task<Response> ChangeRootFolder(RequestType type, int id, int rootFolderId)
|
||||
{
|
||||
// Get all root folders
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
var rootFolders = new List<SonarrRootFolder>();
|
||||
if (type == RequestType.TvShow)
|
||||
{
|
||||
// Get all root folders
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var settings = await Radarr.GetSettingsAsync();
|
||||
rootFolders = RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
// Get Request
|
||||
var allRequests = await Service.GetAllAsync();
|
||||
|
@ -473,7 +582,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
if (request == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel {Result = false});
|
||||
return Response.AsJson(new JsonResponseModel { Result = false });
|
||||
}
|
||||
|
||||
foreach (var folder in rootFolders)
|
||||
|
@ -487,7 +596,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
await Service.UpdateRequestAsync(request);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel {Result = true});
|
||||
}
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
public async Task<Response> Netflix(string title)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var result = NetflixApi.CheckNetflix(title);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Message))
|
||||
|
|
|
@ -50,9 +50,11 @@ using Ombi.Helpers;
|
|||
using Ombi.Helpers.Analytics;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Jobs;
|
||||
using Ombi.Services.Notification;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models;
|
||||
using Ombi.Store.Models.Emby;
|
||||
using Ombi.Store.Models.Plex;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Helpers;
|
||||
|
@ -67,8 +69,8 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
public class SearchModule : BaseAuthModule
|
||||
{
|
||||
public SearchModule(ICacheProvider cache,
|
||||
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker checker,
|
||||
public SearchModule(ICacheProvider cache,
|
||||
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker plexChecker,
|
||||
IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<SickRageSettings> sickRageService, ISickRageApi srApi,
|
||||
INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi,
|
||||
|
@ -77,7 +79,8 @@ namespace Ombi.UI.Modules
|
|||
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth,
|
||||
IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
|
||||
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue, IRepository<PlexContent> content,
|
||||
ISecurityExtensions security, IMovieSender movieSender, IRadarrCacher radarrCacher, ITraktApi traktApi, ISettingsService<CustomizationSettings> cus)
|
||||
ISecurityExtensions security, IMovieSender movieSender, IRadarrCacher radarrCacher, ITraktApi traktApi, ISettingsService<CustomizationSettings> cus,
|
||||
IEmbyAvailabilityChecker embyChecker, IRepository<EmbyContent> embyContent, ISettingsService<EmbySettings> embySettings)
|
||||
: base("search", prSettings, security)
|
||||
{
|
||||
Auth = auth;
|
||||
|
@ -86,7 +89,7 @@ namespace Ombi.UI.Modules
|
|||
PrService = prSettings;
|
||||
MovieApi = new TheMovieDbApi();
|
||||
Cache = cache;
|
||||
Checker = checker;
|
||||
PlexChecker = plexChecker;
|
||||
CpCacher = cpCacher;
|
||||
SonarrCacher = sonarrCacher;
|
||||
SickRageCacher = sickRageCacher;
|
||||
|
@ -112,9 +115,14 @@ namespace Ombi.UI.Modules
|
|||
RadarrCacher = radarrCacher;
|
||||
TraktApi = traktApi;
|
||||
CustomizationSettings = cus;
|
||||
EmbyChecker = embyChecker;
|
||||
EmbyContentRepository = embyContent;
|
||||
EmbySettings = embySettings;
|
||||
|
||||
Get["SearchIndex", "/", true] = async (x, ct) => await RequestLoad();
|
||||
|
||||
Get["actor/{searchTerm}", true] = async (x, ct) => await SearchPerson((string)x.searchTerm);
|
||||
Get["actor/new/{searchTerm}", true] = async (x, ct) => await SearchPerson((string)x.searchTerm, true);
|
||||
Get["movie/{searchTerm}", true] = async (x, ct) => await SearchMovie((string)x.searchTerm);
|
||||
Get["tv/{searchTerm}", true] = async (x, ct) => await SearchTvShow((string)x.searchTerm);
|
||||
Get["music/{searchTerm}", true] = async (x, ct) => await SearchAlbum((string)x.searchTerm);
|
||||
|
@ -135,7 +143,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();
|
||||
}
|
||||
|
@ -143,6 +151,7 @@ namespace Ombi.UI.Modules
|
|||
private IWatcherCacher WatcherCacher { get; }
|
||||
private IMovieSender MovieSender { get; }
|
||||
private IRepository<PlexContent> PlexContentRepository { get; }
|
||||
private IRepository<EmbyContent> EmbyContentRepository { get; }
|
||||
private TvMazeApi TvApi { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private TheMovieDbApi MovieApi { get; }
|
||||
|
@ -152,13 +161,15 @@ namespace Ombi.UI.Modules
|
|||
private IRequestService RequestService { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private ISettingsService<AuthenticationSettings> Auth { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexService { get; }
|
||||
private ISettingsService<PlexRequestSettings> PrService { get; }
|
||||
private ISettingsService<SonarrSettings> SonarrService { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageService { get; }
|
||||
private ISettingsService<HeadphonesSettings> HeadphonesService { get; }
|
||||
private ISettingsService<EmailNotificationSettings> EmailNotificationSettings { get; }
|
||||
private IAvailabilityChecker Checker { get; }
|
||||
private IAvailabilityChecker PlexChecker { get; }
|
||||
private IEmbyAvailabilityChecker EmbyChecker { get; }
|
||||
private ICouchPotatoCacher CpCacher { get; }
|
||||
private ISonarrCacher SonarrCacher { get; }
|
||||
private ISickRageCacher SickRageCacher { get; }
|
||||
|
@ -173,15 +184,27 @@ namespace Ombi.UI.Modules
|
|||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private long _plexMovieCacheTime = 0;
|
||||
private IEnumerable<PlexContent> _plexMovies;
|
||||
|
||||
private long _embyMovieCacheTime = 0;
|
||||
private IEnumerable<EmbyContent> _embyMovies;
|
||||
|
||||
private long _dbMovieCacheTime = 0;
|
||||
private Dictionary<int, RequestedModel> _dbMovies;
|
||||
|
||||
private async Task<Negotiator> RequestLoad()
|
||||
{
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
|
@ -209,6 +232,53 @@ namespace Ombi.UI.Modules
|
|||
return await ProcessMovies(MovieSearchType.Search, searchTerm);
|
||||
}
|
||||
|
||||
private async Task<Response> SearchPerson(string searchTerm)
|
||||
{
|
||||
var movies = TransformMovieListToMovieResultList(await MovieApi.SearchPerson(searchTerm));
|
||||
return await TransformMovieResultsToResponse(movies);
|
||||
}
|
||||
|
||||
private async Task<Response> SearchPerson(string searchTerm, bool filterExisting)
|
||||
{
|
||||
var movies = TransformMovieListToMovieResultList(await MovieApi.SearchPerson(searchTerm, AlreadyAvailable));
|
||||
return await TransformMovieResultsToResponse(movies);
|
||||
}
|
||||
|
||||
private async Task<bool> AlreadyAvailable(int id, string title, string year)
|
||||
{
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
return IsMovieInCache(id, String.Empty) ||
|
||||
(plexSettings.Enable && PlexChecker.IsMovieAvailable(PlexMovies(), title, year)) ||
|
||||
(embySettings.Enable && EmbyChecker.IsMovieAvailable(EmbyMovies(), title, year, String.Empty));
|
||||
}
|
||||
|
||||
private IEnumerable<PlexContent> PlexMovies()
|
||||
{ long now = DateTime.Now.Ticks;
|
||||
if(_plexMovies == null || (now - _plexMovieCacheTime) > 10000)
|
||||
{
|
||||
var content = PlexContentRepository.GetAll();
|
||||
_plexMovies = PlexChecker.GetPlexMovies(content);
|
||||
_plexMovieCacheTime = now;
|
||||
}
|
||||
|
||||
return _plexMovies;
|
||||
}
|
||||
|
||||
private IEnumerable<EmbyContent> EmbyMovies()
|
||||
{
|
||||
long now = DateTime.Now.Ticks;
|
||||
if (_embyMovies == null || (now - _embyMovieCacheTime) > 10000)
|
||||
{
|
||||
var content = EmbyContentRepository.GetAll();
|
||||
_embyMovies = EmbyChecker.GetEmbyMovies(content);
|
||||
_embyMovieCacheTime = now;
|
||||
}
|
||||
|
||||
return _embyMovies;
|
||||
}
|
||||
|
||||
private Response GetTvPoster(int theTvDbId)
|
||||
{
|
||||
var result = TvApi.ShowLookupByTheTvDbId(theTvDbId);
|
||||
|
@ -220,15 +290,10 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
return banner;
|
||||
}
|
||||
private async Task<Response> ProcessMovies(MovieSearchType searchType, string searchTerm)
|
||||
{
|
||||
List<MovieResult> apiMovies;
|
||||
|
||||
switch (searchType)
|
||||
{
|
||||
case MovieSearchType.Search:
|
||||
var movies = await MovieApi.SearchMovie(searchTerm).ConfigureAwait(false);
|
||||
apiMovies = movies.Select(x =>
|
||||
private List<MovieResult> TransformSearchMovieListToMovieResultList(List<TMDbLib.Objects.Search.SearchMovie> searchMovies)
|
||||
{
|
||||
return searchMovies.Select(x =>
|
||||
new MovieResult
|
||||
{
|
||||
Adult = x.Adult,
|
||||
|
@ -247,6 +312,39 @@ namespace Ombi.UI.Modules
|
|||
VoteCount = x.VoteCount
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<MovieResult> TransformMovieListToMovieResultList(List<TMDbLib.Objects.Movies.Movie> movies)
|
||||
{
|
||||
return movies.Select(x =>
|
||||
new MovieResult
|
||||
{
|
||||
Adult = x.Adult,
|
||||
BackdropPath = x.BackdropPath,
|
||||
GenreIds = x.Genres.Select(y => y.Id).ToList(),
|
||||
Id = x.Id,
|
||||
OriginalLanguage = x.OriginalLanguage,
|
||||
OriginalTitle = x.OriginalTitle,
|
||||
Overview = x.Overview,
|
||||
Popularity = x.Popularity,
|
||||
PosterPath = x.PosterPath,
|
||||
ReleaseDate = x.ReleaseDate,
|
||||
Title = x.Title,
|
||||
Video = x.Video,
|
||||
VoteAverage = x.VoteAverage,
|
||||
VoteCount = x.VoteCount
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
private async Task<Response> ProcessMovies(MovieSearchType searchType, string searchTerm)
|
||||
{
|
||||
List<MovieResult> apiMovies;
|
||||
|
||||
switch (searchType)
|
||||
{
|
||||
case MovieSearchType.Search:
|
||||
var movies = await MovieApi.SearchMovie(searchTerm).ConfigureAwait(false);
|
||||
apiMovies = TransformSearchMovieListToMovieResultList(movies);
|
||||
break;
|
||||
case MovieSearchType.CurrentlyPlaying:
|
||||
apiMovies = await MovieApi.GetCurrentPlayingMovies();
|
||||
|
@ -259,21 +357,31 @@ namespace Ombi.UI.Modules
|
|||
break;
|
||||
}
|
||||
|
||||
var allResults = await RequestService.GetAllAsync();
|
||||
allResults = allResults.Where(x => x.Type == RequestType.Movie);
|
||||
return await TransformMovieResultsToResponse(apiMovies);
|
||||
}
|
||||
|
||||
var distinctResults = allResults.DistinctBy(x => x.ProviderId);
|
||||
var dbMovies = distinctResults.ToDictionary(x => x.ProviderId);
|
||||
private async Task<Dictionary<int, RequestedModel>> RequestedMovies()
|
||||
{
|
||||
long now = DateTime.Now.Ticks;
|
||||
if (_dbMovies == null || (now - _dbMovieCacheTime) > 10000)
|
||||
{
|
||||
var allResults = await RequestService.GetAllAsync();
|
||||
allResults = allResults.Where(x => x.Type == RequestType.Movie);
|
||||
|
||||
var distinctResults = allResults.DistinctBy(x => x.ProviderId);
|
||||
_dbMovies = distinctResults.ToDictionary(x => x.ProviderId);
|
||||
_dbMovieCacheTime = now;
|
||||
}
|
||||
return _dbMovies;
|
||||
}
|
||||
|
||||
var cpCached = CpCacher.QueuedIds();
|
||||
var watcherCached = WatcherCacher.QueuedIds();
|
||||
var radarrCached = RadarrCacher.QueuedIds();
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexMovies = Checker.GetPlexMovies(content);
|
||||
private async Task<Response> TransformMovieResultsToResponse(List<MovieResult> movies)
|
||||
{
|
||||
await Task.Yield();
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
var counter = 0;
|
||||
foreach (var movie in apiMovies)
|
||||
Dictionary<int, RequestedModel> dbMovies = await RequestedMovies();
|
||||
foreach (var movie in movies)
|
||||
{
|
||||
var viewMovie = new SearchMovieViewModel
|
||||
{
|
||||
|
@ -293,11 +401,10 @@ namespace Ombi.UI.Modules
|
|||
VoteCount = movie.VoteCount
|
||||
};
|
||||
|
||||
var imdbId = string.Empty;
|
||||
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;
|
||||
|
@ -313,16 +420,37 @@ namespace Ombi.UI.Modules
|
|||
counter++;
|
||||
}
|
||||
|
||||
|
||||
var canSee = CanUserSeeThisRequest(viewMovie.Id, Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests), dbMovies);
|
||||
var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString(),
|
||||
imdbId);
|
||||
if (plexMovie != null)
|
||||
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
viewMovie.Available = true;
|
||||
viewMovie.PlexUrl = plexMovie.Url;
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexMovies = PlexChecker.GetPlexMovies(content);
|
||||
|
||||
var plexMovie = PlexChecker.GetMovie(plexMovies.ToArray(), movie.Title,
|
||||
movie.ReleaseDate?.Year.ToString(),
|
||||
viewMovie.ImdbId);
|
||||
if (plexMovie != null)
|
||||
{
|
||||
viewMovie.Available = true;
|
||||
viewMovie.PlexUrl = plexMovie.Url;
|
||||
}
|
||||
}
|
||||
else if (dbMovies.ContainsKey(movie.Id) && canSee) // compare to the requests db
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
var embyContent = EmbyContentRepository.GetAll();
|
||||
var embyMovies = EmbyChecker.GetEmbyMovies(embyContent);
|
||||
|
||||
var embyMovie = EmbyChecker.GetMovie(embyMovies.ToArray(), movie.Title,
|
||||
movie.ReleaseDate?.Year.ToString(), viewMovie.ImdbId);
|
||||
if (embyMovie != null)
|
||||
{
|
||||
viewMovie.Available = true;
|
||||
}
|
||||
}
|
||||
if (dbMovies.ContainsKey(movie.Id) && canSee) // compare to the requests db
|
||||
{
|
||||
var dbm = dbMovies[movie.Id];
|
||||
|
||||
|
@ -330,20 +458,11 @@ namespace Ombi.UI.Modules
|
|||
viewMovie.Approved = dbm.Approved;
|
||||
viewMovie.Available = dbm.Available;
|
||||
}
|
||||
else if (cpCached.Contains(movie.Id) && canSee) // compare to the couchpotato db
|
||||
else if (canSee)
|
||||
{
|
||||
viewMovie.Approved = true;
|
||||
viewMovie.Requested = true;
|
||||
}
|
||||
else if(watcherCached.Contains(imdbId) && canSee) // compare to the watcher db
|
||||
{
|
||||
viewMovie.Approved = true;
|
||||
viewMovie.Requested = true;
|
||||
}
|
||||
else if (radarrCached.Contains(movie.Id) && canSee)
|
||||
{
|
||||
viewMovie.Approved = true;
|
||||
viewMovie.Requested = true;
|
||||
bool exists = IsMovieInCache(movie, viewMovie.ImdbId);
|
||||
viewMovie.Approved = exists;
|
||||
viewMovie.Requested = exists;
|
||||
}
|
||||
viewMovies.Add(viewMovie);
|
||||
}
|
||||
|
@ -351,6 +470,19 @@ namespace Ombi.UI.Modules
|
|||
return Response.AsJson(viewMovies);
|
||||
}
|
||||
|
||||
private bool IsMovieInCache(MovieResult movie, string imdbId)
|
||||
{ int id = movie.Id;
|
||||
return IsMovieInCache(id, imdbId);
|
||||
}
|
||||
|
||||
private bool IsMovieInCache(int id, string imdbId)
|
||||
{ var cpCached = CpCacher.QueuedIds();
|
||||
var watcherCached = WatcherCacher.QueuedIds();
|
||||
var radarrCached = RadarrCacher.QueuedIds();
|
||||
|
||||
return cpCached.Contains(id) || watcherCached.Contains(imdbId) || radarrCached.Contains(id);
|
||||
}
|
||||
|
||||
private bool CanUserSeeThisRequest(int movieId, bool usersCanViewOnlyOwnRequests,
|
||||
Dictionary<int, RequestedModel> moviesInDb)
|
||||
{
|
||||
|
@ -372,11 +504,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"),
|
||||
|
@ -405,6 +537,11 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
var show = anticipatedShow.Show;
|
||||
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
|
||||
var result = TvApi.ShowLookupByTheTvDbId(theTvDbId);
|
||||
if (result == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
|
@ -434,6 +571,12 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
var show = watched.Show;
|
||||
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
|
||||
var result = TvApi.ShowLookupByTheTvDbId(theTvDbId);
|
||||
if (result == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
|
@ -462,6 +605,12 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
var show = watched.Show;
|
||||
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
|
||||
var result = TvApi.ShowLookupByTheTvDbId(theTvDbId);
|
||||
if (result == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
|
@ -493,51 +642,55 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private async Task<List<SearchTvShowViewModel>> MapToTvModel(List<SearchTvShowViewModel> shows, PlexRequestSettings prSettings)
|
||||
{
|
||||
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
var providerId = string.Empty;
|
||||
// Get the requests
|
||||
var allResults = await RequestService.GetAllAsync();
|
||||
allResults = allResults.Where(x => x.Type == RequestType.TvShow);
|
||||
var distinctResults = allResults.DistinctBy(x => x.ProviderId);
|
||||
var dbTv = distinctResults.ToDictionary(x => x.ProviderId);
|
||||
|
||||
// Check the external applications
|
||||
var sonarrCached = SonarrCacher.QueuedIds().ToList();
|
||||
var sickRageCache = SickRageCacher.QueuedIds(); // consider just merging sonarr/sickrage arrays
|
||||
var dbTv = distinctResults.ToDictionary(x => x.ImdbId);
|
||||
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexTvShows = Checker.GetPlexTvShows(content).ToList();
|
||||
var plexTvShows = PlexChecker.GetPlexTvShows(content);
|
||||
var embyContent = EmbyContentRepository.GetAll();
|
||||
var embyCached = EmbyChecker.GetEmbyTvShows(embyContent).ToList();
|
||||
|
||||
foreach (var show in shows)
|
||||
{
|
||||
if (plexSettings.AdvancedSearch)
|
||||
|
||||
var providerId = show.Id.ToString();
|
||||
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
providerId = show.Id.ToString();
|
||||
var embyShow = EmbyChecker.GetTvShow(embyCached.ToArray(), show.SeriesName, show.FirstAired?.Substring(0, 4), providerId);
|
||||
if (embyShow != null)
|
||||
{
|
||||
show.Available = true;
|
||||
}
|
||||
}
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
var plexShow = PlexChecker.GetTvShow(plexTvShows.ToArray(), show.SeriesName, show.FirstAired?.Substring(0, 4),
|
||||
providerId);
|
||||
if (plexShow != null)
|
||||
{
|
||||
show.Available = true;
|
||||
show.PlexUrl = plexShow.Url;
|
||||
}
|
||||
}
|
||||
|
||||
var plexShow = Checker.GetTvShow(plexTvShows.ToArray(), show.SeriesName, show.FirstAired?.Substring(0, 4),
|
||||
providerId);
|
||||
if (plexShow != null)
|
||||
if (show.ImdbId != null && !show.Available)
|
||||
{
|
||||
show.Available = true;
|
||||
show.PlexUrl = plexShow.Url;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dbTv.ContainsKey(show.Id))
|
||||
var imdbId = show.ImdbId;
|
||||
if (dbTv.ContainsKey(imdbId))
|
||||
{
|
||||
var dbt = dbTv[show.Id];
|
||||
var dbt = dbTv[imdbId];
|
||||
|
||||
show.Requested = true;
|
||||
show.Episodes = dbt.Episodes.ToList();
|
||||
show.Approved = dbt.Approved;
|
||||
}
|
||||
if (sonarrCached.Select(x => x.TvdbId).Contains(show.Id) || sickRageCache.Contains(show.Id))
|
||||
// compare to the sonarr/sickrage db
|
||||
{
|
||||
show.Requested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return shows;
|
||||
|
@ -549,6 +702,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;
|
||||
|
||||
|
@ -571,7 +725,9 @@ namespace Ombi.UI.Modules
|
|||
var sonarrCached = SonarrCacher.QueuedIds();
|
||||
var sickRageCache = SickRageCacher.QueuedIds(); // consider just merging sonarr/sickrage arrays
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexTvShows = Checker.GetPlexTvShows(content);
|
||||
var plexTvShows = PlexChecker.GetPlexTvShows(content);
|
||||
var embyContent = EmbyContentRepository.GetAll();
|
||||
var embyCached = EmbyChecker.GetEmbyTvShows(embyContent);
|
||||
|
||||
var viewTv = new List<SearchTvShowViewModel>();
|
||||
foreach (var t in apiTv)
|
||||
|
@ -605,20 +761,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 = Checker.GetTvShow(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4),
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
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;
|
||||
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))
|
||||
|
@ -658,7 +822,7 @@ namespace Ombi.UI.Modules
|
|||
var dbAlbum = allResults.ToDictionary(x => x.MusicBrainzId);
|
||||
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexAlbums = Checker.GetPlexAlbums(content);
|
||||
var plexAlbums = PlexChecker.GetPlexAlbums(content);
|
||||
|
||||
var viewAlbum = new List<SearchMusicViewModel>();
|
||||
foreach (var a in apiAlbums)
|
||||
|
@ -678,7 +842,7 @@ namespace Ombi.UI.Modules
|
|||
DateTime release;
|
||||
DateTimeHelper.CustomParse(a.ReleaseEvents?.FirstOrDefault()?.date, out release);
|
||||
var artist = a.ArtistCredit?.FirstOrDefault()?.artist;
|
||||
var plexAlbum = Checker.GetAlbum(plexAlbums.ToArray(), a.title, release.ToString("yyyy"), artist?.name);
|
||||
var plexAlbum = PlexChecker.GetAlbum(plexAlbums.ToArray(), a.title, release.ToString("yyyy"), artist?.name);
|
||||
if (plexAlbum != null)
|
||||
{
|
||||
viewA.Available = true;
|
||||
|
@ -719,7 +883,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);
|
||||
|
@ -760,8 +924,8 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var movies = Checker.GetPlexMovies(content);
|
||||
if (Checker.IsMovieAvailable(movies.ToArray(), movieInfo.Title, movieInfo.ReleaseDate?.Year.ToString()))
|
||||
var movies = PlexChecker.GetPlexMovies(content);
|
||||
if (PlexChecker.IsMovieAvailable(movies.ToArray(), movieInfo.Title, movieInfo.ReleaseDate?.Year.ToString()))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
|
@ -778,7 +942,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
|
||||
|
@ -817,13 +981,13 @@ namespace Ombi.UI.Modules
|
|||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Message = "Could not add movie, please contract your administrator",
|
||||
Message = "Could not add movie, please contact your administrator",
|
||||
Result = false
|
||||
});
|
||||
}
|
||||
if (!result.MovieSendingEnabled)
|
||||
{
|
||||
|
||||
|
||||
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
|
||||
|
@ -918,7 +1082,7 @@ namespace Ombi.UI.Modules
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
var showInfo = TvApi.ShowLookupByTheTvDbId(showId);
|
||||
DateTime firstAir;
|
||||
DateTime.TryParse(showInfo.premiered, out firstAir);
|
||||
|
@ -1025,7 +1189,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")}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1043,66 +1207,134 @@ namespace Ombi.UI.Modules
|
|||
try
|
||||
{
|
||||
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var shows = Checker.GetPlexTvShows(content);
|
||||
var providerId = string.Empty;
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
if (plexSettings.AdvancedSearch)
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
providerId = showId.ToString();
|
||||
}
|
||||
if (episodeRequest)
|
||||
{
|
||||
var cachedEpisodesTask = await Checker.GetEpisodes();
|
||||
var cachedEpisodes = cachedEpisodesTask.ToList();
|
||||
foreach (var d in difference) // difference is from an existing request
|
||||
{
|
||||
if (
|
||||
cachedEpisodes.Any(
|
||||
x =>
|
||||
x.SeasonNumber == d.SeasonNumber && x.EpisodeNumber == d.EpisodeNumber &&
|
||||
x.ProviderId == providerId))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message =
|
||||
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {Resources.UI.Search_AlreadyInPlex}"
|
||||
});
|
||||
}
|
||||
}
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var shows = PlexChecker.GetPlexTvShows(content);
|
||||
|
||||
var diff = await GetEpisodeRequestDifference(showId, model);
|
||||
model.Episodes = diff.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (plexSettings.EnableTvEpisodeSearching)
|
||||
var providerId = string.Empty;
|
||||
if (plexSettings.AdvancedSearch)
|
||||
{
|
||||
foreach (var s in showInfo.Season)
|
||||
providerId = showId.ToString();
|
||||
}
|
||||
if (episodeRequest)
|
||||
{
|
||||
var cachedEpisodesTask = await PlexChecker.GetEpisodes();
|
||||
var cachedEpisodes = cachedEpisodesTask.ToList();
|
||||
foreach (var d in difference) // difference is from an existing request
|
||||
{
|
||||
var result = Checker.IsEpisodeAvailable(showId.ToString(), s.SeasonNumber, s.EpisodeNumber);
|
||||
if (result)
|
||||
if (
|
||||
cachedEpisodes.Any(
|
||||
x =>
|
||||
x.SeasonNumber == d.SeasonNumber && x.EpisodeNumber == d.EpisodeNumber &&
|
||||
x.ProviderId == providerId))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
|
||||
Message =
|
||||
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var diff = await GetEpisodeRequestDifference(showId, model);
|
||||
model.Episodes = diff.ToList();
|
||||
}
|
||||
else if (Checker.IsTvShowAvailable(shows.ToArray(), showInfo.name, showInfo.premiered?.Substring(0, 4),
|
||||
providerId, model.SeasonList))
|
||||
else
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
if (plexSettings.EnableTvEpisodeSearching)
|
||||
{
|
||||
foreach (var s in showInfo.Season)
|
||||
{
|
||||
Result = false,
|
||||
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
|
||||
});
|
||||
var result = PlexChecker.IsEpisodeAvailable(showId.ToString(), s.SeasonNumber,
|
||||
s.EpisodeNumber);
|
||||
if (result)
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"{fullShowName} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PlexChecker.IsTvShowAvailable(shows.ToArray(), showInfo.name,
|
||||
showInfo.premiered?.Substring(0, 4),
|
||||
providerId, model.SeasonList))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"{fullShowName} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
var embyContent = EmbyContentRepository.GetAll();
|
||||
var embyMovies = EmbyChecker.GetEmbyTvShows(embyContent);
|
||||
var providerId = showId.ToString();
|
||||
if (episodeRequest)
|
||||
{
|
||||
var cachedEpisodesTask = await EmbyChecker.GetEpisodes();
|
||||
var cachedEpisodes = cachedEpisodesTask.ToList();
|
||||
foreach (var d in difference) // difference is from an existing request
|
||||
{
|
||||
if (
|
||||
cachedEpisodes.Any(
|
||||
x =>
|
||||
x.SeasonNumber == d.SeasonNumber && x.EpisodeNumber == d.EpisodeNumber &&
|
||||
x.ProviderId == providerId))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message =
|
||||
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {string.Format(Resources.UI.Search_AlreadyInPlex,GetMediaServerName())}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var diff = await GetEpisodeRequestDifference(showId, model);
|
||||
model.Episodes = diff.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (embySettings.EnableEpisodeSearching)
|
||||
{
|
||||
foreach (var s in showInfo.Season)
|
||||
{
|
||||
var result = EmbyChecker.IsEpisodeAvailable(showId.ToString(), s.SeasonNumber,
|
||||
s.EpisodeNumber);
|
||||
if (result)
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"{fullShowName} is already in Emby!"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (EmbyChecker.IsTvShowAvailable(embyMovies.ToArray(), showInfo.name,
|
||||
showInfo.premiered?.Substring(0, 4),
|
||||
providerId, model.SeasonList))
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"{fullShowName} is already in Emby!"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1112,7 +1344,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())
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1187,9 +1419,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings)
|
||||
{
|
||||
var sendNotification = ShouldAutoApprove(type)
|
||||
? !prSettings.IgnoreNotifyForAutoApprovedRequests
|
||||
: true;
|
||||
var sendNotification = !ShouldAutoApprove(type) || !prSettings.IgnoreNotifyForAutoApprovedRequests;
|
||||
|
||||
if (IsAdmin)
|
||||
{
|
||||
|
@ -1261,8 +1491,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var albums = Checker.GetPlexAlbums(content);
|
||||
var alreadyInPlex = Checker.IsAlbumAvailable(albums.ToArray(), albumInfo.title, release.ToString("yyyy"),
|
||||
var albums = PlexChecker.GetPlexAlbums(content);
|
||||
var alreadyInPlex = PlexChecker.IsAlbumAvailable(albums.ToArray(), albumInfo.title, release.ToString("yyyy"),
|
||||
artist.name);
|
||||
|
||||
if (alreadyInPlex)
|
||||
|
@ -1280,6 +1510,7 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
Title = albumInfo.title,
|
||||
MusicBrainzId = albumInfo.id,
|
||||
ReleaseId = releaseId,
|
||||
Overview = albumInfo.disambiguation,
|
||||
PosterPath = img,
|
||||
Type = RequestType.Album,
|
||||
|
@ -1348,7 +1579,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
private Response GetSeasons()
|
||||
{
|
||||
var seriesId = (int)Request.Query.tvId;
|
||||
|
@ -1390,7 +1621,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
var existingRequest = requests.FirstOrDefault(x => x.Type == RequestType.TvShow && x.TvDbId == providerId.ToString());
|
||||
var show = await Task.Run(() => TvApi.ShowLookupByTheTvDbId(providerId));
|
||||
var tvMaxeEpisodes = await Task.Run(() => TvApi.EpisodeLookup(show.id));
|
||||
var tvMazeEpisodesTask = await Task.Run(() => TvApi.EpisodeLookup(show.id));
|
||||
var tvMazeEpisodes = tvMazeEpisodesTask.ToList();
|
||||
|
||||
var sonarrEpisodes = new List<SonarrEpisodes>();
|
||||
if (sonarrEnabled)
|
||||
|
@ -1400,26 +1632,59 @@ namespace Ombi.UI.Modules
|
|||
sonarrEpisodes = sonarrEp?.ToList() ?? new List<SonarrEpisodes>();
|
||||
}
|
||||
|
||||
var plexCacheTask = await Checker.GetEpisodes(providerId);
|
||||
var plexCache = plexCacheTask.ToList();
|
||||
foreach (var ep in tvMaxeEpisodes)
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
var requested = existingRequest?.Episodes
|
||||
.Any(episodesModel =>
|
||||
ep.number == episodesModel.EpisodeNumber && ep.season == episodesModel.SeasonNumber) ?? false;
|
||||
|
||||
var alreadyInPlex = plexCache.Any(x => x.EpisodeNumber == ep.number && x.SeasonNumber == ep.season);
|
||||
var inSonarr = sonarrEpisodes.Any(x => x.seasonNumber == ep.season && x.episodeNumber == ep.number && x.hasFile);
|
||||
|
||||
model.Add(new EpisodeListViewModel
|
||||
var plexCacheTask = await PlexChecker.GetEpisodes(providerId);
|
||||
var plexCache = plexCacheTask.ToList();
|
||||
foreach (var ep in tvMazeEpisodes)
|
||||
{
|
||||
Id = show.id,
|
||||
SeasonNumber = ep.season,
|
||||
EpisodeNumber = ep.number,
|
||||
Requested = requested || alreadyInPlex || inSonarr,
|
||||
Name = ep.name,
|
||||
EpisodeId = ep.id
|
||||
});
|
||||
var requested = existingRequest?.Episodes
|
||||
.Any(episodesModel =>
|
||||
ep.number == episodesModel.EpisodeNumber &&
|
||||
ep.season == episodesModel.SeasonNumber) ?? false;
|
||||
|
||||
var alreadyInPlex = plexCache.Any(x => x.EpisodeNumber == ep.number && x.SeasonNumber == ep.season);
|
||||
var inSonarr =
|
||||
sonarrEpisodes.Any(x => x.seasonNumber == ep.season && x.episodeNumber == ep.number && x.hasFile);
|
||||
|
||||
model.Add(new EpisodeListViewModel
|
||||
{
|
||||
Id = show.id,
|
||||
SeasonNumber = ep.season,
|
||||
EpisodeNumber = ep.number,
|
||||
Requested = requested || alreadyInPlex || inSonarr,
|
||||
Name = ep.name,
|
||||
EpisodeId = ep.id
|
||||
});
|
||||
}
|
||||
}
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
var embyCacheTask = await EmbyChecker.GetEpisodes(providerId);
|
||||
var cache = embyCacheTask.ToList();
|
||||
foreach (var ep in tvMazeEpisodes)
|
||||
{
|
||||
var requested = existingRequest?.Episodes
|
||||
.Any(episodesModel =>
|
||||
ep.number == episodesModel.EpisodeNumber &&
|
||||
ep.season == episodesModel.SeasonNumber) ?? false;
|
||||
|
||||
var alreadyInEmby = cache.Any(x => x.EpisodeNumber == ep.number && x.SeasonNumber == ep.season);
|
||||
var inSonarr =
|
||||
sonarrEpisodes.Any(x => x.seasonNumber == ep.season && x.episodeNumber == ep.number && x.hasFile);
|
||||
|
||||
model.Add(new EpisodeListViewModel
|
||||
{
|
||||
Id = show.id,
|
||||
SeasonNumber = ep.season,
|
||||
EpisodeNumber = ep.number,
|
||||
Requested = requested || alreadyInEmby || inSonarr,
|
||||
Name = ep.name,
|
||||
EpisodeId = ep.id
|
||||
});
|
||||
}
|
||||
}
|
||||
return model;
|
||||
|
||||
|
@ -1649,5 +1914,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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,9 @@ using Nancy;
|
|||
using Nancy.Extensions;
|
||||
using Nancy.Linker;
|
||||
using NLog;
|
||||
using Ombi.Api;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Emby;
|
||||
using Ombi.Api.Models.Plex;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
|
@ -44,6 +46,8 @@ using Ombi.Helpers.Analytics;
|
|||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models;
|
||||
using Ombi.Store.Models.Emby;
|
||||
using Ombi.Store.Models.Plex;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Authentication;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
@ -54,59 +58,23 @@ namespace Ombi.UI.Modules
|
|||
public class UserLoginModule : BaseModule
|
||||
{
|
||||
public UserLoginModule(ISettingsService<AuthenticationSettings> auth, IPlexApi api, ISettingsService<PlexSettings> plexSettings, ISettingsService<PlexRequestSettings> pr,
|
||||
ISettingsService<LandingPageSettings> lp, IAnalytics a, IResourceLinker linker, IRepository<UserLogins> userLogins, IPlexUserRepository plexUsers, ICustomUserMapper custom,
|
||||
ISecurityExtensions security, ISettingsService<UserManagementSettings> userManagementSettings)
|
||||
ISettingsService<LandingPageSettings> lp, IAnalytics a, IResourceLinker linker, IRepository<UserLogins> userLogins, IExternalUserRepository<PlexUsers> plexUsers, ICustomUserMapper custom,
|
||||
ISecurityExtensions security, ISettingsService<UserManagementSettings> userManagementSettings, IEmbyApi embyApi, ISettingsService<EmbySettings> emby, IExternalUserRepository<EmbyUsers> embyU)
|
||||
: base("userlogin", pr, security)
|
||||
{
|
||||
AuthService = auth;
|
||||
LandingPageSettings = lp;
|
||||
Analytics = a;
|
||||
Api = api;
|
||||
PlexApi = api;
|
||||
PlexSettings = plexSettings;
|
||||
Linker = linker;
|
||||
UserLogins = userLogins;
|
||||
PlexUserRepository = plexUsers;
|
||||
CustomUserMapper = custom;
|
||||
UserManagementSettings = userManagementSettings;
|
||||
|
||||
//Get["UserLoginIndex", "/", true] = async (x, ct) =>
|
||||
//{
|
||||
// if (Request.Query["landing"] == null)
|
||||
// {
|
||||
// var s = await LandingPageSettings.GetSettingsAsync();
|
||||
// if (s.Enabled)
|
||||
// {
|
||||
// if (s.BeforeLogin) // Before login
|
||||
// {
|
||||
// if (string.IsNullOrEmpty(Username))
|
||||
// {
|
||||
// // They are not logged in
|
||||
// return
|
||||
// Context.GetRedirect(Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString());
|
||||
// }
|
||||
// return Context.GetRedirect(Linker.BuildRelativeUri(Context, "SearchIndex").ToString());
|
||||
// }
|
||||
|
||||
// // After login
|
||||
// if (string.IsNullOrEmpty(Username))
|
||||
// {
|
||||
// // Not logged in yet
|
||||
// return Context.GetRedirect(Linker.BuildRelativeUri(Context, "UserLoginIndex").ToString() + "?landing");
|
||||
// }
|
||||
// // Send them to landing
|
||||
// var landingUrl = Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString();
|
||||
// return Context.GetRedirect(landingUrl);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!string.IsNullOrEmpty(Username) || IsAdmin)
|
||||
// {
|
||||
// var url = Linker.BuildRelativeUri(Context, "SearchIndex").ToString();
|
||||
// return Response.AsRedirect(url);
|
||||
// }
|
||||
// var settings = await AuthService.GetSettingsAsync();
|
||||
// return View["Index", settings];
|
||||
//};
|
||||
EmbySettings = emby;
|
||||
EmbyApi = embyApi;
|
||||
EmbyUserRepository = embyU;
|
||||
|
||||
Post["/", true] = async (x, ct) => await LoginUser();
|
||||
Get["/logout"] = x => Logout();
|
||||
|
@ -157,11 +125,14 @@ namespace Ombi.UI.Modules
|
|||
private ISettingsService<AuthenticationSettings> AuthService { get; }
|
||||
private ISettingsService<LandingPageSettings> LandingPageSettings { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IPlexApi Api { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private IPlexApi PlexApi { get; }
|
||||
private IEmbyApi EmbyApi { get; }
|
||||
private IResourceLinker Linker { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private IRepository<UserLogins> UserLogins { get; }
|
||||
private IPlexUserRepository PlexUserRepository { get; }
|
||||
private IExternalUserRepository<PlexUsers> PlexUserRepository { get; }
|
||||
private IExternalUserRepository<EmbyUsers> EmbyUserRepository { get; }
|
||||
private ICustomUserMapper CustomUserMapper { get; }
|
||||
private ISettingsService<UserManagementSettings> UserManagementSettings { get; }
|
||||
|
||||
|
@ -180,39 +151,69 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
var authenticated = false;
|
||||
var isOwner = false;
|
||||
var userId = string.Empty;
|
||||
EmbyUser embyUser = null;
|
||||
|
||||
if (settings.UserAuthentication) // Check against the users in Plex
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
Log.Debug("Need to auth");
|
||||
authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
if (authenticated)
|
||||
if (settings.UserAuthentication) // Check against the users in Plex
|
||||
{
|
||||
userId = GetUserIdIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
Log.Debug("Need to auth");
|
||||
authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
if (authenticated)
|
||||
{
|
||||
userId = GetUserIdIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
}
|
||||
if (CheckIfUserIsOwner(plexSettings.PlexAuthToken, username))
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
userId = GetOwnerId(plexSettings.PlexAuthToken, username);
|
||||
}
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
if (CheckIfUserIsOwner(plexSettings.PlexAuthToken, username))
|
||||
else if (!settings.UserAuthentication) // No auth, let them pass!
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
userId = GetOwnerId(plexSettings.PlexAuthToken, username);
|
||||
}
|
||||
UsersModel dbUser = await IsDbuser(username);
|
||||
if (dbUser != null) // in the db?
|
||||
{
|
||||
var perms = (Permissions)dbUser.Permissions;
|
||||
authenticated = true;
|
||||
isOwner = perms.HasFlag(Permissions.Administrator);
|
||||
userId = dbUser.UserGuid;
|
||||
}
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
else if (!settings.UserAuthentication) // No auth, let them pass!
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
if (settings.UserAuthentication) // Check against the users in Plex
|
||||
{
|
||||
Log.Debug("Need to auth");
|
||||
authenticated = CheckIfEmbyUser(username, embySettings);
|
||||
if (authenticated)
|
||||
{
|
||||
embyUser = GetEmbyUser(username, embySettings);
|
||||
userId = embyUser?.Id;
|
||||
}
|
||||
if (embyUser?.Policy?.IsAdministrator ?? false)
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
}
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
else if (!settings.UserAuthentication) // No auth, let them pass!
|
||||
{
|
||||
authenticated = true;
|
||||
}
|
||||
}
|
||||
|
||||
UsersModel dbUser = await IsDbuser(username);
|
||||
if (dbUser != null) // in the db?
|
||||
{
|
||||
var perms = (Permissions)dbUser.Permissions;
|
||||
authenticated = true;
|
||||
isOwner = perms.HasFlag(Permissions.Administrator);
|
||||
userId = dbUser.UserGuid;
|
||||
}
|
||||
|
||||
if (settings.UsePassword || isOwner || Security.HasPermissions(username, Permissions.Administrator))
|
||||
|
@ -230,7 +231,7 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
return Response.AsJson(new { result = false, message = Resources.UI.UserLogin_IncorrectUserPass });
|
||||
}
|
||||
var result = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner);
|
||||
var result = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner, plexSettings.Enable, embySettings.Enable);
|
||||
|
||||
|
||||
var landingSettings = await LandingPageSettings.GetSettingsAsync();
|
||||
|
@ -292,36 +293,68 @@ namespace Ombi.UI.Modules
|
|||
var userId = string.Empty;
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
if (settings.UserAuthentication) // Authenticate with Plex
|
||||
// attempt local login first as it has the least amount of overhead
|
||||
userId = CustomUserMapper.ValidateUser(username, password)?.ToString();
|
||||
if (userId != null)
|
||||
{
|
||||
Log.Debug("Need to auth and also provide pass");
|
||||
var signedIn = (PlexAuthentication)Api.SignIn(username, password);
|
||||
if (signedIn.user?.authentication_token != null)
|
||||
authenticated = true;
|
||||
}
|
||||
else if (userId == null && plexSettings.Enable)
|
||||
{
|
||||
if (settings.UserAuthentication) // Authenticate with Plex
|
||||
{
|
||||
Log.Debug("Correct credentials, checking if the user is account owner or in the friends list");
|
||||
if (CheckIfUserIsOwner(plexSettings.PlexAuthToken, signedIn.user?.username))
|
||||
Log.Debug("Need to auth and also provide pass");
|
||||
var signedIn = (PlexAuthentication) PlexApi.SignIn(username, password);
|
||||
if (signedIn.user?.authentication_token != null)
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
Log.Debug("Correct credentials, checking if the user is account owner or in the friends list");
|
||||
if (CheckIfUserIsOwner(plexSettings.PlexAuthToken, signedIn.user?.username))
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
userId = signedIn.user.uuid;
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken);
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
userId = signedIn.user.uuid;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
else if (userId == null && embySettings.Enable)
|
||||
{
|
||||
// Local user?
|
||||
userId = CustomUserMapper.ValidateUser(username, password)?.ToString();
|
||||
if (userId != null)
|
||||
if (settings.UserAuthentication) // Authenticate with Emby
|
||||
{
|
||||
authenticated = true;
|
||||
Log.Debug("Need to auth and also provide pass");
|
||||
EmbyUser signedIn = null;
|
||||
try
|
||||
{
|
||||
signedIn = (EmbyUser)EmbyApi.LogIn(username, password, embySettings.ApiKey, embySettings.FullUri);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
if (signedIn != null)
|
||||
{
|
||||
Log.Debug("Correct credentials, checking if the user is account owner or in the friends list");
|
||||
if (signedIn?.Policy?.IsAdministrator ?? false)
|
||||
{
|
||||
Log.Debug("User is the account owner");
|
||||
authenticated = true;
|
||||
isOwner = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticated = CheckIfEmbyUser(username, embySettings);
|
||||
Log.Debug("Friends list result = {0}", authenticated);
|
||||
}
|
||||
userId = signedIn?.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,8 +363,7 @@ namespace Ombi.UI.Modules
|
|||
return Response.AsJson(new { result = false, message = Resources.UI.UserLogin_IncorrectUserPass });
|
||||
}
|
||||
|
||||
|
||||
var m = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner);
|
||||
var m = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner, plexSettings.Enable, embySettings.Enable);
|
||||
|
||||
var landingSettings = await LandingPageSettings.GetSettingsAsync();
|
||||
|
||||
|
@ -354,7 +386,6 @@ namespace Ombi.UI.Modules
|
|||
return CustomModuleExtensions.LoginAndRedirect(this, m.LoginGuid, null, retVal.ToString());
|
||||
}
|
||||
return Response.AsJson(new { result = true, url = retVal.ToString() });
|
||||
|
||||
}
|
||||
|
||||
private async Task<Response> LoginUser()
|
||||
|
@ -399,7 +430,7 @@ namespace Ombi.UI.Modules
|
|||
if (settings.UserAuthentication && settings.UsePassword) // Authenticate with Plex
|
||||
{
|
||||
Log.Debug("Need to auth and also provide pass");
|
||||
var signedIn = (PlexAuthentication)Api.SignIn(username, password);
|
||||
var signedIn = (PlexAuthentication)PlexApi.SignIn(username, password);
|
||||
if (signedIn.user?.authentication_token != null)
|
||||
{
|
||||
Log.Debug("Correct credentials, checking if the user is account owner or in the friends list");
|
||||
|
@ -553,59 +584,47 @@ namespace Ombi.UI.Modules
|
|||
public string UserId { get; set; }
|
||||
}
|
||||
|
||||
private async Task<LoginModel> AuthenticationSetup(string userId, string username, int dateTimeOffset, Guid loginGuid, bool isOwner)
|
||||
private async Task<LoginModel> AuthenticationSetup(string userId, string username, int dateTimeOffset, Guid loginGuid, bool isOwner, bool plex, bool emby)
|
||||
{
|
||||
var m = new LoginModel();
|
||||
var settings = await AuthService.GetSettingsAsync();
|
||||
|
||||
var localUsers = await CustomUserMapper.GetUsersAsync();
|
||||
var plexLocalUsers = await PlexUserRepository.GetAllAsync();
|
||||
var embyLocalUsers = await EmbyUserRepository.GetAllAsync();
|
||||
|
||||
var localUser = false;
|
||||
|
||||
|
||||
UserLogins.Insert(new UserLogins { UserId = userId, Type = UserType.PlexUser, LastLoggedIn = DateTime.UtcNow });
|
||||
Log.Debug("We are authenticated! Setting session.");
|
||||
// Add to the session (Used in the BaseModules)
|
||||
Session[SessionKeys.UsernameKey] = username;
|
||||
Session[SessionKeys.ClientDateTimeOffsetKey] = dateTimeOffset;
|
||||
|
||||
var plexLocal = plexLocalUsers.FirstOrDefault(x => x.Username == username);
|
||||
if (plexLocal != null)
|
||||
if (plex)
|
||||
{
|
||||
loginGuid = Guid.Parse(plexLocal.LoginId);
|
||||
var plexLocal = plexLocalUsers.FirstOrDefault(x => x.Username == username);
|
||||
if (plexLocal != null)
|
||||
{
|
||||
loginGuid = Guid.Parse(plexLocal.LoginId);
|
||||
}
|
||||
}
|
||||
if (emby)
|
||||
{
|
||||
var embyLocal = embyLocalUsers.FirstOrDefault(x => x.Username == username);
|
||||
if (embyLocal != null)
|
||||
{
|
||||
loginGuid = Guid.Parse(embyLocal.LoginId);
|
||||
}
|
||||
}
|
||||
|
||||
var dbUser = localUsers.FirstOrDefault(x => x.UserName == username);
|
||||
if (dbUser != null)
|
||||
{
|
||||
loginGuid = Guid.Parse(dbUser.UserGuid);
|
||||
localUser = true;
|
||||
}
|
||||
|
||||
//if (loginGuid != Guid.Empty)
|
||||
//{
|
||||
// if (!settings.UserAuthentication)// Do not need to auth make admin use login screen for now TODO remove this
|
||||
// {
|
||||
// if (dbUser != null)
|
||||
// {
|
||||
// var perms = (Permissions)dbUser.Permissions;
|
||||
// if (perms.HasFlag(Permissions.Administrator))
|
||||
// {
|
||||
// var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
// Session["TempMessage"] = Resources.UI.UserLogin_AdminUsePassword;
|
||||
// //return Response.AsRedirect(uri.ToString());
|
||||
// }
|
||||
// }
|
||||
// if (plexLocal != null)
|
||||
// {
|
||||
// var perms = (Permissions)plexLocal.Permissions;
|
||||
// if (perms.HasFlag(Permissions.Administrator))
|
||||
// {
|
||||
// var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
|
||||
// Session["TempMessage"] = Resources.UI.UserLogin_AdminUsePassword;
|
||||
// //return Response.AsRedirect(uri.ToString());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
if (loginGuid == Guid.Empty && settings.UserAuthentication)
|
||||
{
|
||||
var defaultSettings = UserManagementSettings.GetSettings();
|
||||
|
@ -620,21 +639,52 @@ namespace Ombi.UI.Modules
|
|||
defaultPermissions += (int)Permissions.Administrator;
|
||||
}
|
||||
}
|
||||
|
||||
// Looks like we still don't have an entry, so this user does not exist
|
||||
await PlexUserRepository.InsertAsync(new PlexUsers
|
||||
if (plex)
|
||||
{
|
||||
PlexUserId = userId,
|
||||
UserAlias = string.Empty,
|
||||
Permissions = (int)defaultPermissions,
|
||||
Features = UserManagementHelper.GetPermissions(defaultSettings),
|
||||
Username = username,
|
||||
EmailAddress = string.Empty, // We don't have it, we will get it on the next scheduled job run (in 30 mins)
|
||||
LoginId = loginGuid.ToString()
|
||||
});
|
||||
// Looks like we still don't have an entry, so this user does not exist
|
||||
await PlexUserRepository.InsertAsync(new PlexUsers
|
||||
{
|
||||
PlexUserId = userId,
|
||||
UserAlias = string.Empty,
|
||||
Permissions = (int) defaultPermissions,
|
||||
Features = UserManagementHelper.GetPermissions(defaultSettings),
|
||||
Username = username,
|
||||
EmailAddress = string.Empty,
|
||||
// We don't have it, we will get it on the next scheduled job run (in 30 mins)
|
||||
LoginId = loginGuid.ToString()
|
||||
});
|
||||
}
|
||||
if (emby)
|
||||
{
|
||||
await EmbyUserRepository.InsertAsync(new EmbyUsers
|
||||
{
|
||||
EmbyUserId = userId,
|
||||
UserAlias = string.Empty,
|
||||
Permissions = (int)defaultPermissions,
|
||||
Features = UserManagementHelper.GetPermissions(defaultSettings),
|
||||
Username = username,
|
||||
EmailAddress = string.Empty,
|
||||
LoginId = loginGuid.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
m.LoginGuid = loginGuid;
|
||||
m.UserId = userId;
|
||||
var type = UserType.LocalUser;
|
||||
if (localUser)
|
||||
{
|
||||
type = UserType.LocalUser;
|
||||
}
|
||||
else if (plex)
|
||||
{
|
||||
type = UserType.PlexUser;
|
||||
}
|
||||
else if (emby)
|
||||
{
|
||||
type = UserType.EmbyUser;;
|
||||
}
|
||||
UserLogins.Insert(new UserLogins { UserId = userId, Type = type, LastLoggedIn = DateTime.UtcNow });
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
@ -651,7 +701,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private bool CheckIfUserIsOwner(string authToken, string userName)
|
||||
{
|
||||
var userAccount = Api.GetAccount(authToken);
|
||||
var userAccount = PlexApi.GetAccount(authToken);
|
||||
if (userAccount == null)
|
||||
{
|
||||
return false;
|
||||
|
@ -661,7 +711,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private string GetOwnerId(string authToken, string userName)
|
||||
{
|
||||
var userAccount = Api.GetAccount(authToken);
|
||||
var userAccount = PlexApi.GetAccount(authToken);
|
||||
if (userAccount == null)
|
||||
{
|
||||
return string.Empty;
|
||||
|
@ -671,15 +721,45 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private bool CheckIfUserIsInPlexFriends(string username, string authToken)
|
||||
{
|
||||
var users = Api.GetUsers(authToken);
|
||||
var users = PlexApi.GetUsers(authToken);
|
||||
var allUsers = users?.User?.Where(x => !string.IsNullOrEmpty(x.Title));
|
||||
return allUsers != null && allUsers.Any(x => x.Title.Equals(username, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
|
||||
private bool CheckIfEmbyUser(string username, EmbySettings s)
|
||||
{
|
||||
try
|
||||
{
|
||||
var users = EmbyApi.GetUsers(s.FullUri, s.ApiKey);
|
||||
var allUsers = users?.Where(x => !string.IsNullOrEmpty(x.Name));
|
||||
return allUsers != null && allUsers.Any(x => x.Name.Equals(username, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private EmbyUser GetEmbyUser(string username, EmbySettings s)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var users = EmbyApi.GetUsers(s.FullUri, s.ApiKey);
|
||||
var allUsers = users?.Where(x => !string.IsNullOrEmpty(x.Name));
|
||||
return allUsers?.FirstOrDefault(x => x.Name.Equals(username, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string GetUserIdIsInPlexFriends(string username, string authToken)
|
||||
{
|
||||
var users = Api.GetUsers(authToken);
|
||||
var users = PlexApi.GetUsers(authToken);
|
||||
var allUsers = users?.User?.Where(x => !string.IsNullOrEmpty(x.Title));
|
||||
return allUsers?.Where(x => x.Title.Equals(username, StringComparison.CurrentCultureIgnoreCase)).Select(x => x.Id).FirstOrDefault();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using Nancy.Extensions;
|
|||
using Nancy.Responses.Negotiation;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Emby;
|
||||
using Ombi.Api.Models.Plex;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
|
@ -16,6 +17,8 @@ using Ombi.Helpers.Analytics;
|
|||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Models;
|
||||
using Ombi.Store.Models.Emby;
|
||||
using Ombi.Store.Models.Plex;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Models;
|
||||
using Ombi.UI.Models.UserManagement;
|
||||
|
@ -26,8 +29,8 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
public class UserManagementModule : BaseModule
|
||||
{
|
||||
public UserManagementModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService<PlexSettings> plex, IRepository<UserLogins> userLogins, IPlexUserRepository plexRepo
|
||||
, ISecurityExtensions security, IRequestService req, IAnalytics ana) : base("usermanagement", pr, security)
|
||||
public UserManagementModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService<PlexSettings> plex, IRepository<UserLogins> userLogins, IExternalUserRepository<PlexUsers> plexRepo
|
||||
, ISecurityExtensions security, IRequestService req, IAnalytics ana, ISettingsService<EmbySettings> embyService, IEmbyApi embyApi, IExternalUserRepository<EmbyUsers> embyRepo) : base("usermanagement", pr, security)
|
||||
{
|
||||
#if !DEBUG
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
@ -40,6 +43,9 @@ namespace Ombi.UI.Modules
|
|||
PlexRequestSettings = pr;
|
||||
RequestService = req;
|
||||
Analytics = ana;
|
||||
EmbySettings = embyService;
|
||||
EmbyApi = embyApi;
|
||||
EmbyRepository = embyRepo;
|
||||
|
||||
Get["/"] = x => Load();
|
||||
|
||||
|
@ -57,10 +63,13 @@ namespace Ombi.UI.Modules
|
|||
private IPlexApi PlexApi { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IRepository<UserLogins> UserLoginsRepo { get; }
|
||||
private IPlexUserRepository PlexUsersRepository { get; }
|
||||
private IExternalUserRepository<PlexUsers> PlexUsersRepository { get; }
|
||||
private IExternalUserRepository<EmbyUsers> EmbyRepository { get; }
|
||||
private ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private IRequestService RequestService { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private IEmbyApi EmbyApi { get; }
|
||||
|
||||
private Negotiator Load()
|
||||
{
|
||||
|
@ -69,47 +78,20 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private async Task<Response> LoadUsers()
|
||||
{
|
||||
var localUsers = await UserMapper.GetUsersAsync();
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var model = new List<UserManagementUsersViewModel>();
|
||||
|
||||
var userLogins = UserLoginsRepo.GetAll().ToList();
|
||||
|
||||
foreach (var user in localUsers)
|
||||
{
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
model.Add(MapLocalUser(user, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (!string.IsNullOrEmpty(plexSettings.PlexAuthToken))
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
//Get Plex Users
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
if (plexUsers != null && plexUsers.User != null) {
|
||||
foreach (var u in plexUsers.User) {
|
||||
var dbUser = plexDbUsers.FirstOrDefault (x => x.PlexUserId == u.Id);
|
||||
var userDb = userLogins.FirstOrDefault (x => x.UserId == u.Id);
|
||||
|
||||
// We don't have the user in the database yet
|
||||
if (dbUser == null) {
|
||||
model.Add (MapPlexUser (u, null, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
} else {
|
||||
// The Plex User is in the database
|
||||
model.Add (MapPlexUser (u, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also get the server admin
|
||||
var account = PlexApi.GetAccount(plexSettings.PlexAuthToken);
|
||||
if (account != null)
|
||||
{
|
||||
var dbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == account.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == account.Id);
|
||||
model.Add(MapPlexAdmin(account, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
model.AddRange(await LoadPlexUsers());
|
||||
}
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
model.AddRange(await LoadEmbyUsers());
|
||||
}
|
||||
|
||||
model.AddRange(await LoadLocalUsers());
|
||||
|
||||
return Response.AsJson(model);
|
||||
}
|
||||
|
||||
|
@ -217,64 +199,95 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
var plexDbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == model.Id);
|
||||
var plexUser = plexUsers.User.FirstOrDefault(x => x.Id == model.Id);
|
||||
var userLogin = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == model.Id);
|
||||
if (plexDbUser != null && plexUser != null)
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
await UpdateRequests(plexDbUser.Username, plexDbUser.UserAlias, model.Alias);
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
plexDbUser.EmailAddress = model.EmailAddress;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// So it could actually be the admin
|
||||
var account = PlexApi.GetAccount(plexSettings.PlexAuthToken);
|
||||
if (plexDbUser != null && account != null)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
await UpdateRequests(plexDbUser.Username, plexDbUser.UserAlias, model.Alias);
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexAdmin(account, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// We have a Plex Account but he's not in the DB
|
||||
if (plexUser != null)
|
||||
{
|
||||
var user = new PlexUsers
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
var plexDbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == model.Id);
|
||||
var plexUser = plexUsers.User.FirstOrDefault(x => x.Id == model.Id);
|
||||
var userLogin = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == model.Id);
|
||||
if (plexDbUser != null && plexUser != null)
|
||||
{
|
||||
Permissions = permissionsValue,
|
||||
Features = featuresValue,
|
||||
UserAlias = model.Alias,
|
||||
PlexUserId = plexUser.Id,
|
||||
EmailAddress = plexUser.Email,
|
||||
Username = plexUser.Title,
|
||||
LoginId = Guid.NewGuid().ToString()
|
||||
};
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
await PlexUsersRepository.InsertAsync(user);
|
||||
await UpdateRequests(plexDbUser.Username, plexDbUser.UserAlias, model.Alias);
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
plexDbUser.EmailAddress = model.EmailAddress;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// So it could actually be the admin
|
||||
var account = PlexApi.GetAccount(plexSettings.PlexAuthToken);
|
||||
if (plexDbUser != null && account != null)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
await UpdateRequests(plexDbUser.Username, plexDbUser.UserAlias, model.Alias);
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexAdmin(account, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// We have a Plex Account but he's not in the DB
|
||||
if (plexUser != null)
|
||||
{
|
||||
var user = new PlexUsers
|
||||
{
|
||||
Permissions = permissionsValue,
|
||||
Features = featuresValue,
|
||||
UserAlias = model.Alias,
|
||||
PlexUserId = plexUser.Id,
|
||||
EmailAddress = plexUser.Email,
|
||||
Username = plexUser.Title,
|
||||
LoginId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
await PlexUsersRepository.InsertAsync(user);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, user, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
}
|
||||
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
var embyDbUsers = await EmbyRepository.GetAllAsync();
|
||||
var embyUsers = EmbyApi.GetUsers(embySettings.FullUri, embySettings.ApiKey);
|
||||
var selectedDbUser = embyDbUsers.FirstOrDefault(x => x.EmbyUserId == model.Id);
|
||||
var embyUser = embyUsers.FirstOrDefault(x => x.Id == model.Id);
|
||||
|
||||
var userLogin = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == model.Id);
|
||||
if (selectedDbUser != null && embyUser != null)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
selectedDbUser.Permissions = permissionsValue;
|
||||
selectedDbUser.Features = featuresValue;
|
||||
|
||||
await UpdateRequests(selectedDbUser.Username, selectedDbUser.UserAlias, model.Alias);
|
||||
|
||||
selectedDbUser.UserAlias = model.Alias;
|
||||
selectedDbUser.EmailAddress = model.EmailAddress;
|
||||
|
||||
await EmbyRepository.UpdateAsync(selectedDbUser);
|
||||
|
||||
var retUser = MapEmbyUser(embyUser, selectedDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
var retUser = MapPlexUser(plexUser, user, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
return null; // We should never end up here.
|
||||
}
|
||||
|
@ -416,7 +429,7 @@ namespace Ombi.UI.Modules
|
|||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = plexInfo.Id,
|
||||
PermissionsFormattedString = newUser ? "Processing..." :( permissions == 0 ? "None" : permissions.ToString()),
|
||||
PermissionsFormattedString = newUser ? "Processing..." : (permissions == 0 ? "None" : permissions.ToString()),
|
||||
FeaturesFormattedString = newUser ? "Processing..." : features.ToString(),
|
||||
Username = plexInfo.Title,
|
||||
Type = UserType.PlexUser,
|
||||
|
@ -436,6 +449,36 @@ namespace Ombi.UI.Modules
|
|||
return m;
|
||||
}
|
||||
|
||||
private UserManagementUsersViewModel MapEmbyUser(EmbyUser embyInfo, EmbyUsers dbUser, DateTime lastLoggedIn)
|
||||
{
|
||||
var newUser = false;
|
||||
if (dbUser == null)
|
||||
{
|
||||
newUser = true;
|
||||
dbUser = new EmbyUsers();
|
||||
}
|
||||
var features = (Features)dbUser?.Features;
|
||||
var permissions = (Permissions)dbUser?.Permissions;
|
||||
|
||||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = embyInfo.Id,
|
||||
PermissionsFormattedString = newUser ? "Processing..." : (permissions == 0 ? "None" : permissions.ToString()),
|
||||
FeaturesFormattedString = newUser ? "Processing..." : features.ToString(),
|
||||
Username = embyInfo.Name,
|
||||
Type = UserType.EmbyUser,
|
||||
EmailAddress =dbUser.EmailAddress,
|
||||
Alias = dbUser?.UserAlias ?? string.Empty,
|
||||
LastLoggedIn = lastLoggedIn,
|
||||
ManagedUser = false
|
||||
};
|
||||
|
||||
m.Permissions.AddRange(GetPermissions(permissions));
|
||||
m.Features.AddRange(GetFeatures(features));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private UserManagementUsersViewModel MapPlexAdmin(PlexAccount plexInfo, PlexUsers dbUser, DateTime lastLoggedIn)
|
||||
{
|
||||
var newUser = false;
|
||||
|
@ -505,6 +548,93 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<UserManagementUsersViewModel>> LoadLocalUsers()
|
||||
{
|
||||
|
||||
var localUsers = await UserMapper.GetUsersAsync(); var userLogins = UserLoginsRepo.GetAll().ToList();
|
||||
|
||||
var model = new List<UserManagementUsersViewModel>();
|
||||
foreach (var user in localUsers)
|
||||
{
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
model.Add(MapLocalUser(user, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<UserManagementUsersViewModel>> LoadPlexUsers()
|
||||
{
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var model = new List<UserManagementUsersViewModel>();
|
||||
|
||||
var userLogins = UserLoginsRepo.GetAll().ToList();
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
if (!string.IsNullOrEmpty(plexSettings.PlexAuthToken))
|
||||
{
|
||||
//Get Plex Users
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
if (plexUsers?.User != null)
|
||||
{
|
||||
foreach (var u in plexUsers.User)
|
||||
{
|
||||
var dbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == u.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == u.Id);
|
||||
|
||||
// We don't have the user in the database yet
|
||||
if (dbUser == null)
|
||||
{
|
||||
model.Add(MapPlexUser(u, null, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The Plex User is in the database
|
||||
model.Add(MapPlexUser(u, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also get the server admin
|
||||
var account = PlexApi.GetAccount(plexSettings.PlexAuthToken);
|
||||
if (account != null)
|
||||
{
|
||||
var dbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == account.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == account.Id);
|
||||
model.Add(MapPlexAdmin(account, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<UserManagementUsersViewModel>> LoadEmbyUsers()
|
||||
{
|
||||
var embyDbUsers = await EmbyRepository.GetAllAsync();
|
||||
var model = new List<UserManagementUsersViewModel>();
|
||||
|
||||
var userLogins = UserLoginsRepo.GetAll().ToList();
|
||||
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
if (!string.IsNullOrEmpty(embySettings.ApiKey))
|
||||
{
|
||||
//Get Plex Users
|
||||
var embyUsers = EmbyApi.GetUsers(embySettings.FullUri, embySettings.ApiKey);
|
||||
if (embyUsers != null)
|
||||
{
|
||||
foreach (var u in embyUsers)
|
||||
{
|
||||
var dbUser = embyDbUsers.FirstOrDefault(x => x.EmbyUserId == u.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == u.Id);
|
||||
|
||||
// We don't have the user in the database yet
|
||||
model.Add(dbUser == null
|
||||
? MapEmbyUser(u, null, userDb?.LastLoggedIn ?? DateTime.MinValue)
|
||||
: MapEmbyUser(u, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#region Copyright
|
||||
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: UserWizardModule.cs
|
||||
|
@ -23,6 +24,7 @@
|
|||
// 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;
|
||||
|
@ -50,8 +52,11 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
public class UserWizardModule : BaseModule
|
||||
{
|
||||
public UserWizardModule(ISettingsService<PlexRequestSettings> pr, ISettingsService<PlexSettings> plex, IPlexApi plexApi,
|
||||
ISettingsService<AuthenticationSettings> auth, ICustomUserMapper m, IAnalytics a, ISecurityExtensions security) : base("wizard", pr, security)
|
||||
public UserWizardModule(ISettingsService<PlexRequestSettings> pr, ISettingsService<PlexSettings> plex,
|
||||
IPlexApi plexApi,
|
||||
ISettingsService<AuthenticationSettings> auth, ICustomUserMapper m, IAnalytics a,
|
||||
ISecurityExtensions security, IEmbyApi embyApi,
|
||||
ISettingsService<EmbySettings> embySettings) : base("wizard", pr, security)
|
||||
{
|
||||
PlexSettings = plex;
|
||||
PlexApi = plexApi;
|
||||
|
@ -59,10 +64,13 @@ namespace Ombi.UI.Modules
|
|||
Auth = auth;
|
||||
Mapper = m;
|
||||
Analytics = a;
|
||||
EmbySettings = embySettings;
|
||||
EmbyApi = embyApi;
|
||||
|
||||
Get["/", true] = async (x, ct) =>
|
||||
{
|
||||
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();
|
||||
|
||||
|
@ -76,7 +84,10 @@ namespace Ombi.UI.Modules
|
|||
Post["/plex", true] = async (x, ct) => await Plex();
|
||||
Post["/plexrequest", true] = async (x, ct) => await PlexRequest();
|
||||
Post["/auth", true] = async (x, ct) => await Authentication();
|
||||
Post["/createuser",true] = async (x,ct) => await CreateUser();
|
||||
Post["/createuser", true] = async (x, ct) => await CreateUser();
|
||||
|
||||
|
||||
Post["/embyauth", true] = async (x, ct) => await EmbyAuth();
|
||||
}
|
||||
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
|
@ -85,6 +96,8 @@ namespace Ombi.UI.Modules
|
|||
private ISettingsService<AuthenticationSettings> Auth { get; }
|
||||
private ICustomUserMapper Mapper { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private IEmbyApi EmbyApi { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
@ -95,23 +108,31 @@ namespace Ombi.UI.Modules
|
|||
|
||||
if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please provide a valid username and password" });
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Please provide a valid username and password"
|
||||
});
|
||||
}
|
||||
|
||||
var model = PlexApi.SignIn(user.username, user.password);
|
||||
|
||||
if (model?.user == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect username or password!" });
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect username or password!" });
|
||||
}
|
||||
|
||||
// Set the auth token in the session so we can use it in the next form
|
||||
Session[SessionKeys.UserWizardPlexAuth] = model.user.authentication_token;
|
||||
|
||||
|
||||
var servers = PlexApi.GetServer(model.user.authentication_token);
|
||||
var firstServer = servers.Server.FirstOrDefault();
|
||||
|
||||
return Response.AsJson(new { Result = true, firstServer?.Port, Ip = firstServer?.LocalAddresses, firstServer?.Scheme });
|
||||
|
||||
return
|
||||
Response.AsJson(
|
||||
new { Result = true, firstServer?.Port, Ip = firstServer?.LocalAddresses, firstServer?.Scheme });
|
||||
}
|
||||
|
||||
private async Task<Response> Plex()
|
||||
|
@ -122,7 +143,8 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
form.PlexAuthToken = Session[SessionKeys.UserWizardPlexAuth].ToString(); // Set the auth token from the previous form
|
||||
form.PlexAuthToken = Session[SessionKeys.UserWizardPlexAuth].ToString();
|
||||
// Set the auth token from the previous form
|
||||
|
||||
// Get the machine ID from the settings (This could have changed)
|
||||
try
|
||||
|
@ -131,6 +153,7 @@ namespace Ombi.UI.Modules
|
|||
var firstServer = servers.Server.FirstOrDefault(x => x.AccessToken == form.PlexAuthToken);
|
||||
|
||||
Session[SessionKeys.UserWizardMachineId] = firstServer?.MachineIdentifier;
|
||||
form.MachineIdentifier = firstServer?.MachineIdentifier;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -143,7 +166,12 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save the settings to the database, please try again." });
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Could not save the settings to the database, please try again."
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<Response> PlexRequest()
|
||||
|
@ -158,14 +186,19 @@ namespace Ombi.UI.Modules
|
|||
currentSettings.SearchForMovies = form.SearchForMovies;
|
||||
currentSettings.SearchForTvShows = form.SearchForTvShows;
|
||||
currentSettings.SearchForMusic = form.SearchForMusic;
|
||||
|
||||
|
||||
var result = await PlexRequestSettings.SaveSettingsAsync(currentSettings);
|
||||
if (result)
|
||||
{
|
||||
return Response.AsJson(new { Result = true });
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save the settings to the database, please try again." });
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Could not save the settings to the database, please try again."
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<Response> Authentication()
|
||||
|
@ -177,14 +210,21 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save the settings to the database, please try again." });
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Could not save the settings to the database, please try again."
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<Response> CreateUser()
|
||||
{
|
||||
var username = (string)Request.Form.Username;
|
||||
var userId = Mapper.CreateUser(username, Request.Form.Password, EnumHelper<Permissions>.All() - (int)Permissions.ReadOnlyUser, 0);
|
||||
Analytics.TrackEventAsync(Category.Wizard, Action.Finish, "Finished the wizard", username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var userId = Mapper.CreateUser(username, Request.Form.Password,
|
||||
EnumHelper<Permissions>.All() - (int)Permissions.ReadOnlyUser, 0);
|
||||
Analytics.TrackEventAsync(Category.Wizard, Action.Finish, "Finished the wizard", username,
|
||||
CookieHelper.GetAnalyticClientId(Cookies));
|
||||
Session[SessionKeys.UsernameKey] = username;
|
||||
|
||||
// Destroy the Plex Auth Token
|
||||
|
@ -197,7 +237,55 @@ namespace Ombi.UI.Modules
|
|||
|
||||
var baseUrl = string.IsNullOrEmpty(settings.BaseUrl) ? string.Empty : $"/{settings.BaseUrl}";
|
||||
|
||||
return CustomModuleExtensions.LoginAndRedirect(this,(Guid)userId, fallbackRedirectUrl: $"{baseUrl}/search");
|
||||
return CustomModuleExtensions.LoginAndRedirect(this, (Guid)userId, fallbackRedirectUrl: $"{baseUrl}/search");
|
||||
}
|
||||
|
||||
private async Task<Response> EmbyAuth()
|
||||
{
|
||||
var ip = (string)Request.Form.Ip;
|
||||
var port = (int)Request.Form.Port;
|
||||
var apiKey = (string)Request.Form.ApiKey;
|
||||
var ssl = (bool)Request.Form.Ssl;
|
||||
|
||||
var settings = new EmbySettings
|
||||
{
|
||||
ApiKey = apiKey,
|
||||
Enable = true,
|
||||
Ip = ip,
|
||||
Port = port,
|
||||
Ssl = ssl,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// Test that we can connect
|
||||
var result = EmbyApi.GetUsers(settings.FullUri, apiKey);
|
||||
|
||||
if (result != null && result.Any())
|
||||
{
|
||||
settings.AdministratorId = result.FirstOrDefault(x => x.Policy.IsAdministrator)?.Id ?? string.Empty;
|
||||
await EmbySettings.SaveSettingsAsync(settings);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = $"Could not connect to Emby, please check your settings. Error: {e.Message}"
|
||||
});
|
||||
}
|
||||
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Could not connect to Emby, please check your settings."
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,6 +50,8 @@ namespace Ombi.UI.NinjectModules
|
|||
Bind<IDiscordApi>().To<DiscordApi>();
|
||||
Bind<IRadarrApi>().To<RadarrApi>();
|
||||
Bind<ITraktApi>().To<TraktApi>();
|
||||
Bind<IEmbyApi>().To<EmbyApi>();
|
||||
Bind<IAppveyorApi>().To<AppveyorApi>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,7 +56,8 @@ namespace Ombi.UI.NinjectModules
|
|||
Bind<ICustomUserMapper>().To<UserMapper>();
|
||||
|
||||
Bind<INotificationService>().To<NotificationService>().InSingletonScope();
|
||||
Bind<INotificationEngine>().To<NotificationEngine>();
|
||||
Bind<IPlexNotificationEngine>().To<PlexNotificationEngine>();
|
||||
Bind<IEmbyNotificationEngine>().To<EmbyNotificationEngine>();
|
||||
|
||||
Bind<IStatusChecker>().To<StatusChecker>();
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace Ombi.UI.NinjectModules
|
|||
{
|
||||
Bind<IRepository<UsersModel>>().To<UserRepository<UsersModel>>();
|
||||
Bind(typeof(IRepository<>)).To(typeof(GenericRepository<>));
|
||||
Bind(typeof(IExternalUserRepository<>)).To(typeof(BaseExternalUserRepository<>));
|
||||
|
||||
Bind<IRequestService>().To<JsonRequestModelRequestService>();
|
||||
Bind<IRequestRepository>().To<RequestJsonRepository>();
|
||||
|
@ -48,7 +49,6 @@ namespace Ombi.UI.NinjectModules
|
|||
Bind<IJobRecord>().To<JobRecord>();
|
||||
|
||||
Bind<IUserRepository>().To<UserRepository>();
|
||||
Bind<IPlexUserRepository>().To<PlexUserRepository>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ using Ombi.Core.Queue;
|
|||
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;
|
||||
|
@ -47,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>();
|
||||
|
@ -58,6 +61,14 @@ namespace Ombi.UI.NinjectModules
|
|||
Bind<IPlexEpisodeCacher>().To<PlexEpisodeCacher>();
|
||||
Bind<IFaultQueueHandler>().To<FaultQueueHandler>();
|
||||
Bind<IPlexUserChecker>().To<PlexUserChecker>();
|
||||
|
||||
Bind<IEmbyAvailabilityChecker>().To<EmbyAvailabilityChecker>();
|
||||
Bind<IEmbyContentCacher>().To<EmbyContentCacher>();
|
||||
Bind<IEmbyEpisodeCacher>().To<EmbyEpisodeCacher>();
|
||||
Bind<IEmbyUserChecker>().To<EmbyUserChecker>();
|
||||
Bind<IEmbyAddedNewsletter>().To<EmbyAddedNewsletter>();
|
||||
Bind<IPlexNewsletter>().To<PlexRecentlyAddedNewsletter>();
|
||||
|
||||
|
||||
Bind<IAnalytics>().To<Analytics>();
|
||||
Bind<ISchedulerFactory>().To<StdSchedulerFactory>();
|
||||
|
|
|
@ -266,13 +266,13 @@
|
|||
<Compile Include="Modules\Admin\UserManagementSettingsModule.cs" />
|
||||
<Compile Include="Modules\Admin\FaultQueueModule.cs" />
|
||||
<Compile Include="Modules\Admin\SystemStatusModule.cs" />
|
||||
<Compile Include="Modules\ApiDocsModule.cs" />
|
||||
<Compile Include="Modules\ApiSettingsMetadataModule.cs" />
|
||||
<Compile Include="Modules\ApiUserMetadataModule.cs" />
|
||||
<Compile Include="Modules\ApiRequestMetadataModule.cs" />
|
||||
<Compile Include="Modules\ApiSettingsModule.cs" />
|
||||
<Compile Include="Modules\ApiUserModule.cs" />
|
||||
<Compile Include="Modules\BaseApiModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiDocsModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiSettingsMetadataModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiUserMetadataModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiRequestMetadataModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiSettingsModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiUserModule.cs" />
|
||||
<Compile Include="Modules\Api\BaseApiModule.cs" />
|
||||
<Compile Include="Modules\BaseModule.cs" />
|
||||
<Compile Include="Modules\BetaModule.cs" />
|
||||
<Compile Include="Modules\CultureModule.cs" />
|
||||
|
@ -295,6 +295,7 @@
|
|||
</Compile>
|
||||
<Compile Include="Start\StartupOptions.cs" />
|
||||
<Compile Include="Start\UpdateValue.cs" />
|
||||
<Compile Include="Validators\EmbyValidator.cs" />
|
||||
<Compile Include="Validators\RadarrValidator.cs" />
|
||||
<Compile Include="Validators\WatcherValidator.cs" />
|
||||
<Compile Include="Validators\SlackSettingsValidator.cs" />
|
||||
|
@ -389,9 +390,105 @@
|
|||
<DependentUpon>datepicker.css</DependentUpon>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\android-icon-144x144.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\android-icon-192x192.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\android-icon-36x36.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\android-icon-48x48.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\android-icon-72x72.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\android-icon-96x96.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-114x114.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-120x120.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-144x144.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-152x152.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-180x180.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-57x57.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-60x60.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-72x72.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-76x76.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon-precomposed.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\apple-icon.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\browserconfig.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\favicon-16x16.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\favicon-32x32.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\favicon-96x96.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\favicon.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\ms-icon-144x144.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\ms-icon-150x150.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\ms-icon-310x310.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\favicon\ms-icon-70x70.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\helpers\bootbox.min.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\emby-logo-dark.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\emby-logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\logo original.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\plex-logo-reversed.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\plex-logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\issue-details.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -483,9 +580,6 @@
|
|||
<Content Include="Content\bootstrap-datetimepicker.min.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\images\logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\tooltip\plugins\tooltipster\sideTip\themes\tooltipster-sideTip-borderless.min.css">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -547,7 +641,7 @@
|
|||
<Content Include="Views\UserManagement\Index.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Compile Include="Modules\ApiRequestModule.cs" />
|
||||
<Compile Include="Modules\Api\ApiRequestModule.cs" />
|
||||
<Compile Include="Models\ApiModel.cs" />
|
||||
<Compile Include="Models\UserManagement\UserManagementUsersViewModel.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -623,6 +717,9 @@
|
|||
</None>
|
||||
<None Include="Content\base.scss" />
|
||||
<None Include="Content\bootstrap-datetimepicker-build.less" />
|
||||
<None Include="Content\favicon\manifest.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\Themes\original.scss" />
|
||||
<None Include="Content\Themes\plex.scss" />
|
||||
<Content Include="Content\pace.min.js">
|
||||
|
@ -779,6 +876,9 @@
|
|||
<Content Include="Views\Integration\Watcher.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="Views\Admin\MassEmail.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Views\Admin\NewsletterSettings.cshtml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
@ -803,6 +903,9 @@
|
|||
<Content Include="Views\Integration\Radarr.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Admin\Emby.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="Web.Debug.config">
|
||||
<DependentUpon>web.config</DependentUpon>
|
||||
</None>
|
||||
|
@ -838,6 +941,10 @@
|
|||
<Project>{8CB8D235-2674-442D-9C6A-35FCAEEB160D}</Project>
|
||||
<Name>Ombi.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Ombi.Common\Ombi.Common.csproj">
|
||||
<Project>{BFD45569-90CF-47CA-B575-C7B0FF97F67B}</Project>
|
||||
<Name>Ombi.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Ombi.Core.Migration\Ombi.Core.Migration.csproj">
|
||||
<Project>{8406EE57-D533-47C0-9302-C6B5F8C31E55}</Project>
|
||||
<Name>Ombi.Core.Migration</Name>
|
||||
|
|
|
@ -29,6 +29,7 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using CommandLine;
|
||||
using Microsoft.Owin.Hosting;
|
||||
|
@ -41,6 +42,7 @@ using Ombi.Core.SettingModels;
|
|||
using Ombi.Helpers;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.UI.Modules.Admin;
|
||||
using Ombi.UI.Start;
|
||||
|
||||
namespace Ombi.UI
|
||||
|
@ -50,7 +52,6 @@ namespace Ombi.UI
|
|||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
var result = Parser.Default.ParseArguments<StartupOptions>(args);
|
||||
var baseUrl = result.MapResult(
|
||||
o => o.BaseUrl,
|
||||
|
@ -75,7 +76,7 @@ namespace Ombi.UI
|
|||
var s = new Setup();
|
||||
var cn = s.SetupDb(baseUrl);
|
||||
s.CacheQualityProfiles();
|
||||
ConfigureTargets(cn);
|
||||
ConfigureTargets(cn);
|
||||
SetupLogging();
|
||||
|
||||
if (port == -1 || port == 3579)
|
||||
|
|
|
@ -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öchtest Du einen Film oder eine Serie schauen, die momentan noch nicht auf Plex ist? Dann logge dich unten ein und fordere es 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>Deine Login-Daten werden nur zur Authorisierung deines 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öchtest Du etwas schauen, das derzeit nicht auf Plex ist?! Kein Problem! Suche einfach unten danach und frage es an!</value>
|
||||
<value>Möchtest Du etwas schauen, das derzeit nicht auf {0} ist?! Kein Problem! Suche einfach unten danach und frage es an!</value>
|
||||
</data>
|
||||
<data name="Search_Title" xml:space="preserve">
|
||||
<value>Suche</value>
|
||||
|
@ -409,7 +409,7 @@
|
|||
<value>wurde schon 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 dein wöchentliches Anfragekontingent 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 dein wöchentliches Anfragekontingent 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 d’utilisateur</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>
|
||||
|
|
|
@ -1,76 +1,96 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
Version 1.3
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">1.3</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1">this is my long string</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
[base64 mime encoded serialized .NET Framework object]
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
|
@ -89,26 +109,26 @@
|
|||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="UserLogin_Title" xml:space="preserve">
|
||||
<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>
|
||||
|
@ -191,8 +211,11 @@
|
|||
<data name="Search_Albums" xml:space="preserve">
|
||||
<value>Albums</value>
|
||||
</data>
|
||||
<data name="Search_NewOnly" xml:space="preserve">
|
||||
<value>Don't include titles that are already requested/available</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 +413,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 +422,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 +439,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 +488,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>
|
||||
|
@ -476,4 +496,7 @@
|
|||
<data name="UserLogin_AdminUsePassword" xml:space="preserve">
|
||||
<value>If you are an administrator, please use the other login page</value>
|
||||
</data>
|
||||
<data name="Search_Actors" xml:space="preserve">
|
||||
<value>Actors</value>
|
||||
</data>
|
||||
</root>
|
|
@ -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>
|
||||
|
|
41
Ombi.UI/Resources/UI1.Designer.cs
generated
|
@ -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'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't take too long..
|
||||
/// </summary>
|
||||
public static string Layout_CacherRunning {
|
||||
get {
|
||||
|
@ -717,6 +717,15 @@ namespace Ombi.UI.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Actors.
|
||||
/// </summary>
|
||||
public static string Search_Actors {
|
||||
get {
|
||||
return ResourceManager.GetString("Search_Actors", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Albums.
|
||||
/// </summary>
|
||||
|
@ -736,7 +745,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 +799,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's correctly setup?.
|
||||
/// Looks up a localized string similar to We could not check if {0} is in {1}, are you sure it's correctly setup?.
|
||||
/// </summary>
|
||||
public static string Search_CouldNotCheckPlex {
|
||||
get {
|
||||
|
@ -816,15 +825,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>
|
||||
|
@ -888,6 +888,15 @@ namespace Ombi.UI.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Don't include titles that are already requested/available.
|
||||
/// </summary>
|
||||
public static string Search_NewOnly {
|
||||
get {
|
||||
return ResourceManager.GetString("Search_NewOnly", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not Requested yet.
|
||||
/// </summary>
|
||||
|
@ -907,7 +916,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 +1141,8 @@ namespace Ombi.UI.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Want to watch a movie or tv show but it'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's not currently on {0}?
|
||||
/// Login below with your username and password!.
|
||||
/// </summary>
|
||||
public static string UserLogin_Paragraph {
|
||||
get {
|
||||
|
@ -1178,7 +1187,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 {
|
||||
|
|
|
@ -31,6 +31,7 @@ using Ninject;
|
|||
using Ninject.Planning.Bindings.Resolvers;
|
||||
using Ninject.Syntax;
|
||||
using NLog;
|
||||
using Ombi.Api;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Migration;
|
||||
|
@ -83,6 +84,7 @@ namespace Ombi.UI
|
|||
var scheduler = new Scheduler();
|
||||
|
||||
|
||||
|
||||
// Reset any jobs running
|
||||
var jobSettings = kernel.Get<IRepository<ScheduledJobs>>();
|
||||
var all = jobSettings.GetAll();
|
||||
|
|
42
Ombi.UI/Validators/EmbyValidator.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: SonarrValidator.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 FluentValidation;
|
||||
using Ombi.Core.SettingModels;
|
||||
|
||||
namespace Ombi.UI.Validators
|
||||
{
|
||||
public class EmbyValidator : AbstractValidator<EmbySettings>
|
||||
{
|
||||
public EmbyValidator()
|
||||
{
|
||||
RuleFor(request => request.Ip).NotNull().WithMessage("You must specify a IP/Host name.");
|
||||
RuleFor(request => request.Port).NotNull().WithMessage("You must specify a Port.");
|
||||
RuleFor(request => request.ApiKey).NotNull().WithMessage("You must specify a Api Key.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ namespace Ombi.UI.Validators
|
|||
RuleFor(request => request.ApiKey).NotEmpty().WithMessage("You must specify a Api Key.");
|
||||
RuleFor(request => request.Ip).NotEmpty().WithMessage("You must specify a IP/Host name.");
|
||||
RuleFor(request => request.Port).NotEmpty().WithMessage("You must specify a Port.");
|
||||
RuleFor(request => request.QualityProfile).NotEmpty().WithMessage("You must specify a Quality Profile.");
|
||||
RuleFor(request => request.QualityProfile).NotEmpty().NotNull().WithMessage("You must specify a Quality Profile.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@using Ombi.UI.Helpers
|
||||
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<Ombi.Core.SettingModels.AuthenticationSettings>
|
||||
@Html.Partial("Shared/Partial/_Sidebar")
|
||||
|
||||
@{
|
||||
|
@ -17,40 +18,11 @@
|
|||
<form class="form-horizontal" method="POST" action="@formAction" id="mainForm">
|
||||
<fieldset>
|
||||
<legend>Authentication Settings</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
||||
@if (Model.UserAuthentication)
|
||||
{
|
||||
<input type="checkbox" id="userAuth" name="UserAuthentication" checked="checked">
|
||||
<label for="userAuth">Enable User Authentication</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" id="userAuth" name="UserAuthentication">
|
||||
<label for="userAuth">Enable User Authentication</label>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
||||
@if (Model.UsePassword)
|
||||
{
|
||||
<input type="checkbox" id="UsePassword" name="UsePassword" checked="checked">
|
||||
<label id="passLabel" for="UsePassword">Require users to login with their passwords</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" id="UsePassword" name="UsePassword">
|
||||
<label id="passLabel" for="UsePassword">Require users to login with their passwords</label>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@Html.Checkbox(Model.UserAuthentication, "UserAuthentication", "Enable User Authentication", "If enabled we will check the login name against a user in your local users list or Plex/Emby users.")
|
||||
|
||||
|
||||
@Html.Checkbox(Model.UsePassword, "UsePassword", "Require users to login with their passwords", "If enabled, users must provide a valid password to log into Ombi")
|
||||
|
||||
|
||||
<br />
|
||||
|
@ -59,7 +31,9 @@
|
|||
<br />
|
||||
|
||||
|
||||
<p class="form-group">A comma separated list of users that you do not want to login.</p>
|
||||
<p class="form-group">A comma separated list of users that you do not want to login.
|
||||
@Html.ToolTip("This is a 'blacklist', if you have users that you do not want to log in, enter them here!")</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="DeniedUsers" class="control-label">Denied Users</label>
|
||||
<div >
|
||||
|
@ -83,20 +57,13 @@
|
|||
$(function () {
|
||||
|
||||
var base = '@Html.GetBaseUrl()';
|
||||
|
||||
$('.customTooltip').tooltipster({
|
||||
contentCloning: true
|
||||
});
|
||||
|
||||
|
||||
changeDisabledStatus($('#UsePassword'), @Model.UserAuthentication.ToString().ToLower(), $('#passLabel'));
|
||||
if ($('#PlexAuthToken')) {
|
||||
loadUserList();
|
||||
}
|
||||
|
||||
$('#refreshUsers').click(function (e) {
|
||||
e.preventDefault();
|
||||
loadUserList();
|
||||
});
|
||||
|
||||
|
||||
$('#mainForm').on('click', '#userAuth', function () {
|
||||
var checked = this.checked;
|
||||
changeDisabledStatus($('#UsePassword'), checked, $('#passLabel'));
|
||||
|
@ -112,41 +79,5 @@
|
|||
$label.css("color", "grey");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadUserList() {
|
||||
$('#users').html("");
|
||||
var url = "getusers";
|
||||
$.ajax({
|
||||
type: "Get",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
|
||||
$('#users').html("");
|
||||
if(!response.result){
|
||||
generateNotify(response.message,"danger");
|
||||
$('#users').append("<option>Error!</option>");
|
||||
return;
|
||||
}
|
||||
if (response.users.length > 0) {
|
||||
$(response.users).each(function () {
|
||||
$('#users').append("<option>" + this + "</option>");
|
||||
});
|
||||
} else {
|
||||
$('#users').append("<option>No Users, Please refresh!</option>");
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
$('#users').html("");
|
||||
$('#users').append("<option>Error!</option>");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
</script>
|
150
Ombi.UI/Views/Admin/Emby.cshtml
Normal file
|
@ -0,0 +1,150 @@
|
|||
@using Ombi.UI.Helpers
|
||||
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<Ombi.Core.SettingModels.EmbySettings>
|
||||
@Html.Partial("Shared/Partial/_Sidebar")
|
||||
@{
|
||||
int port;
|
||||
if (Model.Port == 0)
|
||||
{
|
||||
port = 8096;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = Model.Port;
|
||||
}
|
||||
}
|
||||
<div class="col-sm-8 col-sm-push-1">
|
||||
<form class="form-horizontal" method="POST" id="mainForm">
|
||||
<fieldset>
|
||||
<legend>Emby Settings</legend>
|
||||
|
||||
@Html.Checkbox(Model.Enable, "Enable", "Enabled")
|
||||
<div class="form-group">
|
||||
<label for="Ip" class="control-label">Emby Hostname or IP</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" value="@Model.Ip">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">Port</label>
|
||||
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="@port">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
||||
@if (Model.Ssl)
|
||||
{
|
||||
<input type="checkbox" id="Ssl" name="Ssl" checked="checked"><label for="Ssl">SSL</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" id="Ssl" name="Ssl"><label for="Ssl">SSL</label>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@Html.Checkbox(Model.EnableEpisodeSearching, "EnableEpisodeSearching", "Enable Episode Searching")
|
||||
@Html.ToolTip("This will allow Ombi to search through all of the episodes stored on Emby")
|
||||
|
||||
<div class="form-group">
|
||||
<label for="SubDir" class="control-label">Emby Base Url</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="SubDir" name="SubDir" value="@Model.SubDir">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ApiKey" class="control-label">Emby Api Key</label>
|
||||
<div class="">
|
||||
<input type="text" class="form-control-custom form-control" id="ApiKey" name="ApiKey" placeholder="Api Key" value="@Model.ApiKey">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="testEmby" type="submit" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
var base = '@Html.GetBaseUrl()';
|
||||
|
||||
$('#testEmby').click(function (e) {
|
||||
e.preventDefault();
|
||||
var url = createBaseUrl(base, '/test/emby');
|
||||
var $form = $("#mainForm");
|
||||
|
||||
$('#spinner').attr("class", "fa fa-spinner fa-spin");
|
||||
$.ajax({
|
||||
type: $form.prop("method"),
|
||||
url: url,
|
||||
data: $form.serialize(),
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
$('#spinner').attr("class", "");
|
||||
console.log(response);
|
||||
if (response.result === true) {
|
||||
generateNotify(response.message, "success");
|
||||
|
||||
$('#spinner').attr("class", "fa fa-check");
|
||||
} else {
|
||||
generateNotify(response.message, "warning");
|
||||
$('#spinner').attr("class", "fa fa-times");
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
|
||||
$('#spinner').attr("class", "fa fa-times");
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#save').click(function (e) {
|
||||
e.preventDefault();
|
||||
var port = $('#portNumber').val();
|
||||
if (isNaN(port)) {
|
||||
generateNotify("You must specify a Port.", "warning");
|
||||
return;
|
||||
}
|
||||
|
||||
var $form = $("#mainForm");
|
||||
$.ajax({
|
||||
type: $form.prop("method"),
|
||||
data: $form.serialize(),
|
||||
url: $form.prop("action"),
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
if (response.result === true) {
|
||||
generateNotify(response.message, "success");
|
||||
} else {
|
||||
generateNotify(response.message, "warning");
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
|
@ -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>
|
||||
|
||||
|
|