This commit is contained in:
TidusJar 2018-08-22 13:30:28 +01:00
commit 0a4acb314c
221 changed files with 12313 additions and 15406 deletions

View file

@ -1,5 +1,215 @@
# Changelog # Changelog
## v3.0.3587 (2018-08-19)
### **New Features**
- Added the ability to invite Plex Friends from the user management screen. [Jamie]
- Added rich notifications for mobile. [Jamie]
- Updater fixes. [Jamie]
- Added updater test mode. [Jamie Rees]
- Added a new API method to delete issue comments. [TidusJar]
- Updated @ngu/carousel to beta version to remove rxjs-compat dependency. [Matt Jeanes]
- Update to Angular 6/Webpack 4. [Matt Jeanes]
- Update CHANGELOG.md. [Jamie]
- Updated the way we create the wizard user, errors show now be fed back to the user. [Jamie]
- Added Brazillian Portuguese as a language and also Polish. [Jamie]
- Updated swagger. [Jamie]
- Updated to 2.1.1. [Jamie]
### **Fixes**
- #2408 Added the feature to delete comments on issues. [Jamie]
- New translations en.json (Swedish) [Jamie]
- New translations en.json (French) [Jamie]
- Fixed #2440. [TidusJar]
- Delete cake.config. [Chris Pritchard]
- Initial attempt at getting anime seriestype working. [Chris Pritchard]
- Add cake.config. [Chris Pritchard]
- Fixed the issue where we wouldn't correctly mark some shows as available when there was no provider id #2429. [Jamie]
- Fixed the 'loop' in the cacher #2429. [Jamie]
- Fixed #2427. [Jamie]
- Fixed #2424. [Jamie]
- Fixed #2409. [Jamie]
- More updater. [Jamie]
- Humanize the request type enum in notifications e.g. TvShow will now appear as "Tv Show" #2416. [TidusJar]
- Made the quality override and root folder override load when we load the show (It will now appear) [Jamie]
- Fixed #2415 where power users could not set the Sonarr Quality Override or Root Folder Override. [Jamie]
- #2371 Fixed the issue where certain actions would not setup the series correctly in Sonarr. [Jamie]
- Tightened up the security from an API perspecitve. [TidusJar]
- Stop the root folder and profile calls from erroring. [TidusJar]
- New translations en.json (Polish) [Jamie]
- New translations en.json (Polish) [Jamie]
- New translations en.json (Polish) [Jamie]
- New translations en.json (Portuguese, Brazilian) [Jamie]
- New translations en.json (Portuguese, Brazilian) [Jamie]
- New translations en.json (Portuguese, Brazilian) [Jamie]
- New translations en.json (Portuguese, Brazilian) [Jamie]
- New translations en.json (Portuguese, Brazilian) [Jamie]
- Fixed all linting. [TidusJar]
- Comment out envparam stuff. [Matt Jeanes]
- Fixed prod build issue. [Matt Jeanes]
- Missed a tiny bit. [Matt Jeanes]
- Fix test. [Matt Jeanes]
- Fix test build. [Matt Jeanes]
- Linting + remove debug. [Matt Jeanes]
- Switch to Yarn and disable auto publish in release mode. [Matt Jeanes]
- Fix for #2409. [TidusJar]
- New translations en.json (Swedish) [Jamie]
- New translations en.json (Spanish) [Jamie]
- New translations en.json (Portuguese, Brazilian) [Jamie]
- New translations en.json (Polish) [Jamie]
- New translations en.json (Norwegian) [Jamie]
- New translations en.json (Italian) [Jamie]
- New translations en.json (German) [Jamie]
- New translations en.json (French) [Jamie]
- New translations en.json (Dutch) [Jamie]
- New translations en.json (Danish) [Jamie]
- Possible fix for #2298. [D34DC3N73R]
- Fixed the text for #2370. [Jamie]
- Fixed where you couldn't bulk edit the limits to 0 #2318. [Jamie]
- Upgraded to .net 2.1.2 (Includes security fixes) [Jamie]
## v3.0.3477 (2018-07-18)
### **New Features**
- Updated the Emby availability checker to bring it more in line with what we do with Plex. [TidusJar]
- Added the ability to impersonate a user when using the API Key. This allows people to use the API and request as a certain user. #2363. [Jamie Rees]
- Added more background images and it will loop through the available ones. [Jamie Rees]
- Added chunk hashing to resolve #2330. [Jamie Rees]
- Added API at /api/v1/status/info to get branch and version information #2331. [Jamie Rees]
- Update to .net 2.1.1. [Jamie]
### **Fixes**
- Fix #2322 caused by continue statement inside try catch block. [Anojh]
- Fixed #2367. [TidusJar]
- Fixed the issue where you could not delete a user #2365. [TidusJar]
- Another attempt to fix #2366. [Jamie Rees]
- Fixed the Plex OAuth warning. [Jamie]
- Revert "Fixed Plex OAuth, should no longer show Insecure warning" [Jamie Rees]
- Fixed Plex OAuth, should no longer show Insecure warning. [Jamie Rees]
- Fixed the View On Emby URL since the Link changed #2368. [Jamie Rees]
- Fixed the issue where episodes were not being marked as available in the search #2367. [Jamie Rees]
- Fixed #2371. [Jamie Rees]
- Fixed collection issues in Emby #2366. [Jamie Rees]
- Do not delete the Emby Information every time we run, let's keep the content now. [Jamie Rees]
- Emby Improvements: Batch up the amount we get from the server. [Jamie Rees]
- Log errors when they are uncaught. [Jamie Rees]
- Fix unclosed table tags causing overflow #2322. [Anojh]
- This should now fix #2350. [Jamie]
- Improve the validation around the Application URL. [Jamie Rees]
- Fixed #2341. [Jamie Rees]
- Stop spamming errors when FanArt doesn't have the image. [Jamie Rees]
- Fixed #2338. [Jamie Rees]
- Removed some logging statements. [Jamie Rees]
- Fixed the api key being case sensative #2350. [Jamie Rees]
- Improved the Emby API #2230 Thanks Luke! [Jamie Rees]
- Revert. [Jamie Rees]
- Fixed a small error in the Mobile Notification Provider. [Jamie Rees]
- Minor style tweaks. [Randall Bruder]
- Downgrade to .net core 2.0. [Jamie Rees]
- Downgrade Microsoft.AspNetCore.All package back to 2.0.8. [Jamie Rees]
- Removed old code. [Jamie Rees]
- Swap out the old way of validating the API key with a real middlewear this time. [Jamie Rees]
## v3.0.3421 (2018-06-23) ## v3.0.3421 (2018-06-23)
### **New Features** ### **New Features**

View file

@ -15,19 +15,19 @@ test: off
after_build: after_build:
- cmd: >- - cmd: >-
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows.zip" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\osx.tar.gz" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux.tar.gz" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz"
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip" appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip"
# appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz" # appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz"

View file

@ -1,10 +1,10 @@
#tool "nuget:?package=GitVersion.CommandLine" #tool "nuget:?package=GitVersion.CommandLine"
#addin "Cake.Gulp" #addin "Cake.Gulp"
#addin "nuget:?package=Cake.Npm&version=0.13.0"
#addin "SharpZipLib" #addin "SharpZipLib"
#addin nuget:?package=Cake.Compression&version=0.1.4 #addin nuget:?package=Cake.Compression&version=0.1.4
#addin "Cake.Incubator" #addin "Cake.Incubator"
#addin "Cake.Yarn"
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// ARGUMENTS // ARGUMENTS
@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
var solutionFile = "Ombi.sln"; // Solution file if needed var solutionFile = "Ombi.sln"; // Solution file if needed
GitVersion versionInfo = null; GitVersion versionInfo = null;
var frameworkVer = "netcoreapp2.0"; var frameworkVer = "netcoreapp2.1";
var buildSettings = new DotNetCoreBuildSettings var buildSettings = new DotNetCoreBuildSettings
{ {
@ -122,36 +122,19 @@ Task("SetVersionInfo")
Task("NPM") Task("NPM")
.Does(() => { .Does(() => {
var settings = new NpmInstallSettings { Yarn.FromPath(webProjDir).Install();
LogLevel = NpmLogLevel.Silent,
WorkingDirectory = webProjDir,
Production = true
};
NpmInstall(settings);
}); });
Task("Gulp Publish") Task("Gulp Publish")
.IsDependentOn("NPM") .IsDependentOn("NPM")
.Does(() => { .Does(() => {
Yarn.FromPath(webProjDir).RunScript("publish");
var runScriptSettings = new NpmRunScriptSettings {
ScriptName="publish",
WorkingDirectory = webProjDir,
};
NpmRunScript(runScriptSettings);
}); });
Task("TSLint") Task("TSLint")
.Does(() => .Does(() =>
{ {
var settings = new NpmRunScriptSettings { Yarn.FromPath(webProjDir).RunScript("lint");
WorkingDirectory = webProjDir,
ScriptName = "lint"
};
NpmRunScript(settings);
}); });
Task("PrePublish") Task("PrePublish")
@ -178,7 +161,7 @@ Task("Publish")
.IsDependentOn("Publish-OSX") .IsDependentOn("Publish-OSX")
.IsDependentOn("Publish-Linux") .IsDependentOn("Publish-Linux")
.IsDependentOn("Publish-Linux-ARM") .IsDependentOn("Publish-Linux-ARM")
//.IsDependentOn("Publish-Linux-ARM-64Bit") .IsDependentOn("Publish-Linux-ARM-64Bit")
.IsDependentOn("Package"); .IsDependentOn("Package");
Task("Publish-Windows") Task("Publish-Windows")
@ -189,6 +172,8 @@ Task("Publish-Windows")
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/win10-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x64/published/Swagger.xml"); CopyFile(buildDir + "/"+frameworkVer+"/win10-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
}); });
@ -200,6 +185,9 @@ Task("Publish-Windows-32bit")
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/win10-x86/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x86/published/Swagger.xml"); CopyFile(buildDir + "/"+frameworkVer+"/win10-x86/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x86/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x86/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
}); });
@ -211,6 +199,8 @@ Task("Publish-OSX")
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/osx-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/osx-x64/published/Swagger.xml"); CopyFile(buildDir + "/"+frameworkVer+"/osx-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/osx-x64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/osx-x64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
}); });
@ -222,6 +212,8 @@ Task("Publish-Linux")
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/linux-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-x64/published/Swagger.xml"); CopyFile(buildDir + "/"+frameworkVer+"/linux-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-x64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-x64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
}); });
@ -235,6 +227,8 @@ Task("Publish-Linux-ARM")
CopyFile( CopyFile(
buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml",
buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml"); buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
}); });
@ -248,6 +242,8 @@ Task("Publish-Linux-ARM-64Bit")
CopyFile( CopyFile(
buildDir + "/"+frameworkVer+"/linux-arm64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-arm64/Swagger.xml",
buildDir + "/"+frameworkVer+"/linux-arm64/published/Swagger.xml"); buildDir + "/"+frameworkVer+"/linux-arm64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
}); });

291
package-lock.json generated Normal file
View file

@ -0,0 +1,291 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "1.0.3"
}
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"requires": {
"chalk": "1.1.3",
"esutils": "2.0.2",
"js-tokens": "3.0.2"
},
"dependencies": {
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "2.2.1",
"escape-string-regexp": "1.0.5",
"has-ansi": "2.0.0",
"strip-ansi": "3.0.1",
"supports-color": "2.0.0"
}
}
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
}
},
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
},
"chalk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
"supports-color": "5.4.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "1.9.1"
}
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"requires": {
"has-flag": "3.0.0"
}
}
}
},
"color-convert": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"commander": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esprima": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"requires": {
"ansi-regex": "2.1.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"js-yaml": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
"requires": {
"argparse": "1.0.10",
"esprima": "4.0.0"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "1.1.11"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1.0.2"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-parse": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
},
"resolve": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
"requires": {
"path-parse": "1.0.5"
}
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "2.1.1"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
},
"tslib": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
"integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw=="
},
"tslint": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz",
"integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=",
"requires": {
"babel-code-frame": "6.26.0",
"builtin-modules": "1.1.1",
"chalk": "2.4.1",
"commander": "2.15.1",
"diff": "3.5.0",
"glob": "7.1.2",
"js-yaml": "3.12.0",
"minimatch": "3.0.4",
"resolve": "1.7.1",
"semver": "5.5.0",
"tslib": "1.9.2",
"tsutils": "2.27.1"
}
},
"tsutils": {
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz",
"integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
"requires": {
"tslib": "1.9.2"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

View file

@ -91,27 +91,31 @@ namespace Ombi.Api.Emby
request.AddContentHeader("Content-Type", "application/json"); request.AddContentHeader("Content-Type", "application/json");
} }
public async Task<EmbyItemContainer<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl) public async Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
{ {
var request = new Request($"emby/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get); var request = new Request($"emby/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey); AddHeaders(request, apiKey);
return await Api.Request<EmbyItemContainer<MovieInformation>>(request); request.AddQueryString("Fields", "ProviderIds,Overview");
request.AddQueryString("VirtualItem", "False");
return await Api.Request<EmbyItemContainer<EmbyMovie>>(request);
} }
public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string userId, string baseUri) public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
{ {
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true); return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
} }
public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, string userId, string baseUri) public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
{ {
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri); return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
} }
public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, string userId, string baseUri) public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
{ {
return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri); return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
} }
public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl) public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl)
@ -145,7 +149,25 @@ namespace Ombi.Api.Emby
request.AddQueryString("IncludeItemTypes", type); request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("VirtualItem","False"); request.AddQueryString("VirtualItem", "False");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
{
var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
request.AddQueryString("VirtualItem", "False");
AddHeaders(request, apiKey); AddHeaders(request, apiKey);

View file

@ -14,12 +14,17 @@ namespace Ombi.Api.Emby
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri); Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
Task<EmbyConnectUser> LoginConnectUser(string username, string password); Task<EmbyConnectUser> LoginConnectUser(string username, string password);
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string userId, string baseUri); Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, string userId, string baseUri); string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, string userId, string baseUri);
Task<EmbyItemContainer<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId, Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
string baseUrl); string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId,
string apiKey, string userId, string baseUrl);
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl); Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);

View file

@ -6,6 +6,6 @@ namespace Ombi.Api.Notifications
{ {
public interface IOneSignalApi public interface IOneSignalApi
{ {
Task<OneSignalNotificationResponse> PushNotification(List<string> playerIds, string message); Task<OneSignalNotificationResponse> PushNotification(List<string> playerIds, string message, bool isAdminNotification, int requestId, int requestType);
} }
} }

View file

@ -4,18 +4,22 @@
{ {
public string app_id { get; set; } public string app_id { get; set; }
public string[] include_player_ids { get; set; } public string[] include_player_ids { get; set; }
public Data data { get; set; } public object data { get; set; }
public Button[] buttons { get; set; }
public Contents contents { get; set; } public Contents contents { get; set; }
} }
public class Data
{
public string foo { get; set; }
}
public class Contents public class Contents
{ {
public string en { get; set; } public string en { get; set; }
} }
public class Button
{
public string id { get; set; }
public string text { get; set; }
//public string icon { get; set; }
}
} }

View file

@ -4,6 +4,10 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.4.2" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" /> <ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
</ItemGroup> </ItemGroup>

View file

@ -20,7 +20,7 @@ namespace Ombi.Api.Notifications
private readonly IApplicationConfigRepository _appConfig; private readonly IApplicationConfigRepository _appConfig;
private const string ApiUrl = "https://onesignal.com/api/v1/notifications"; private const string ApiUrl = "https://onesignal.com/api/v1/notifications";
public async Task<OneSignalNotificationResponse> PushNotification(List<string> playerIds, string message) public async Task<OneSignalNotificationResponse> PushNotification(List<string> playerIds, string message, bool isAdminNotification, int requestId, int requestType)
{ {
if (!playerIds.Any()) if (!playerIds.Any())
{ {
@ -39,6 +39,17 @@ namespace Ombi.Api.Notifications
include_player_ids = playerIds.ToArray() include_player_ids = playerIds.ToArray()
}; };
if (isAdminNotification)
{
// Add the action buttons
body.data = new { requestid = requestId, requestType = requestType};
body.buttons = new[]
{
new Button {id = "approve", text = "Approve Request"},
new Button {id = "deny", text = "Deny Request"},
};
}
request.AddJsonBody(body); request.AddJsonBody(body);
var result = await _api.Request<OneSignalNotificationResponse>(request); var result = await _api.Request<OneSignalNotificationResponse>(request);

View file

@ -11,6 +11,7 @@ namespace Ombi.Api.Plex
public interface IPlexApi public interface IPlexApi
{ {
Task<PlexStatus> GetStatus(string authToken, string uri); Task<PlexStatus> GetStatus(string authToken, string uri);
Task<PlexLibrariesForMachineId> GetLibrariesForMachineId(string authToken, string machineId);
Task<PlexAuthentication> SignIn(UserRequest user); Task<PlexAuthentication> SignIn(UserRequest user);
Task<PlexServer> GetServer(string authToken); Task<PlexServer> GetServer(string authToken);
Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost); Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost);
@ -22,8 +23,8 @@ namespace Ombi.Api.Plex
Task<PlexFriends> GetUsers(string authToken); Task<PlexFriends> GetUsers(string authToken);
Task<PlexAccount> GetAccount(string authToken); Task<PlexAccount> GetAccount(string authToken);
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId); Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
Task<OAuthPin> CreatePin();
Task<OAuthPin> GetPin(int pinId); Task<OAuthPin> GetPin(int pinId);
Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard); Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl);
Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs);
} }
} }

View file

@ -0,0 +1,84 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Ombi.Api.Plex.Models
{
[XmlRoot(ElementName = "Section")]
public class Section
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "key")]
public string Key { get; set; }
[XmlAttribute(AttributeName = "title")]
public string Title { get; set; }
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlAttribute(AttributeName = "shared")]
public string Shared { get; set; }
}
[XmlRoot(ElementName = "SharedServer")]
public class SharedServer
{
[XmlElement(ElementName = "Section")]
public List<Section> Section { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "username")]
public string Username { get; set; }
[XmlAttribute(AttributeName = "email")]
public string Email { get; set; }
[XmlAttribute(AttributeName = "userID")]
public string UserID { get; set; }
[XmlAttribute(AttributeName = "accessToken")]
public string AccessToken { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "acceptedAt")]
public string AcceptedAt { get; set; }
[XmlAttribute(AttributeName = "invitedAt")]
public string InvitedAt { get; set; }
[XmlAttribute(AttributeName = "allowSync")]
public string AllowSync { get; set; }
[XmlAttribute(AttributeName = "allowCameraUpload")]
public string AllowCameraUpload { get; set; }
[XmlAttribute(AttributeName = "allowChannels")]
public string AllowChannels { get; set; }
[XmlAttribute(AttributeName = "allowTuners")]
public string AllowTuners { get; set; }
[XmlAttribute(AttributeName = "owned")]
public string Owned { get; set; }
}
[XmlRoot(ElementName = "MediaContainer")]
public class PlexAdd
{
[XmlElement(ElementName = "SharedServer")]
public SharedServer SharedServer { get; set; }
[XmlAttribute(AttributeName = "friendlyName")]
public string FriendlyName { get; set; }
[XmlAttribute(AttributeName = "identifier")]
public string Identifier { get; set; }
[XmlAttribute(AttributeName = "machineIdentifier")]
public string MachineIdentifier { get; set; }
[XmlAttribute(AttributeName = "size")]
public string Size { get; set; }
}
[XmlRoot(ElementName = "Response")]
public class AddUserError
{
[XmlAttribute(AttributeName = "code")]
public string Code { get; set; }
[XmlAttribute(AttributeName = "status")]
public string Status { get; set; }
}
public class PlexAddWrapper
{
public PlexAdd Add { get; set; }
public AddUserError Error { get; set; }
public bool HasError => Error != null;
}
}

View file

@ -0,0 +1,66 @@
namespace Ombi.Api.Plex.Models
{
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
[XmlRoot(ElementName = "Section")]
public class SectionLite
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "key")]
public string Key { get; set; }
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlAttribute(AttributeName = "title")]
public string Title { get; set; }
}
[XmlRoot(ElementName = "Server")]
public class ServerLib
{
[XmlElement(ElementName = "Section")]
public List<SectionLite> Section { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "address")]
public string Address { get; set; }
[XmlAttribute(AttributeName = "port")]
public string Port { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
[XmlAttribute(AttributeName = "scheme")]
public string Scheme { get; set; }
[XmlAttribute(AttributeName = "host")]
public string Host { get; set; }
[XmlAttribute(AttributeName = "localAddresses")]
public string LocalAddresses { get; set; }
[XmlAttribute(AttributeName = "machineIdentifier")]
public string MachineIdentifier { get; set; }
[XmlAttribute(AttributeName = "createdAt")]
public string CreatedAt { get; set; }
[XmlAttribute(AttributeName = "updatedAt")]
public string UpdatedAt { get; set; }
[XmlAttribute(AttributeName = "owned")]
public string Owned { get; set; }
[XmlAttribute(AttributeName = "synced")]
public string Synced { get; set; }
}
[XmlRoot(ElementName = "MediaContainer")]
public class PlexLibrariesForMachineId
{
[XmlElement(ElementName = "Server")]
public ServerLib Server { get; set; }
[XmlAttribute(AttributeName = "friendlyName")]
public string FriendlyName { get; set; }
[XmlAttribute(AttributeName = "identifier")]
public string Identifier { get; set; }
[XmlAttribute(AttributeName = "machineIdentifier")]
public string MachineIdentifier { get; set; }
[XmlAttribute(AttributeName = "size")]
public string Size { get; set; }
}
}

View file

@ -16,14 +16,16 @@ namespace Ombi.Api.Plex
{ {
public class PlexApi : IPlexApi public class PlexApi : IPlexApi
{ {
public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings) public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings, ISettingsService<PlexSettings> p)
{ {
Api = api; Api = api;
_custom = settings; _custom = settings;
_plexSettings = p;
} }
private IApi Api { get; } private IApi Api { get; }
private readonly ISettingsService<CustomizationSettings> _custom; private readonly ISettingsService<CustomizationSettings> _custom;
private readonly ISettingsService<PlexSettings> _plexSettings;
private string _app; private string _app;
private string ApplicationName private string ApplicationName
@ -39,7 +41,18 @@ namespace Ombi.Api.Plex
} }
else else
{ {
_app = settings.ApplicationName; // Check for non-ascii characters (New .Net Core HTTPLib does not allow this)
var chars = settings.ApplicationName.ToCharArray();
var hasNonAscii = false;
foreach (var c in chars)
{
if (c > 128)
{
hasNonAscii = true;
}
}
_app = hasNonAscii ? "Ombi" : settings.ApplicationName;
} }
return _app; return _app;
@ -69,7 +82,7 @@ namespace Ombi.Api.Plex
}; };
var request = new Request(SignInUri, string.Empty, HttpMethod.Post); var request = new Request(SignInUri, string.Empty, HttpMethod.Post);
AddHeaders(request); await AddHeaders(request);
request.AddJsonBody(userModel); request.AddJsonBody(userModel);
var obj = await Api.Request<PlexAuthentication>(request); var obj = await Api.Request<PlexAuthentication>(request);
@ -80,14 +93,14 @@ namespace Ombi.Api.Plex
public async Task<PlexStatus> GetStatus(string authToken, string uri) public async Task<PlexStatus> GetStatus(string authToken, string uri)
{ {
var request = new Request(uri, string.Empty, HttpMethod.Get); var request = new Request(uri, string.Empty, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexStatus>(request); return await Api.Request<PlexStatus>(request);
} }
public async Task<PlexAccount> GetAccount(string authToken) public async Task<PlexAccount> GetAccount(string authToken)
{ {
var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get); var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexAccount>(request); return await Api.Request<PlexAccount>(request);
} }
@ -95,7 +108,7 @@ namespace Ombi.Api.Plex
{ {
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml); var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexServer>(request); return await Api.Request<PlexServer>(request);
} }
@ -103,17 +116,24 @@ namespace Ombi.Api.Plex
public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost) public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost)
{ {
var request = new Request("library/sections", plexFullHost, HttpMethod.Get); var request = new Request("library/sections", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexContainer>(request); return await Api.Request<PlexContainer>(request);
} }
public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId) public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId)
{ {
var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get); var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexContainer>(request); return await Api.Request<PlexContainer>(request);
} }
public async Task<PlexLibrariesForMachineId> GetLibrariesForMachineId(string authToken, string machineId)
{
var request = new Request("", $"https://plex.tv/api/servers/{machineId}", HttpMethod.Get, ContentType.Xml);
await AddHeaders(request, authToken);
return await Api.Request<PlexLibrariesForMachineId>(request);
}
/// <summary> /// <summary>
// 192.168.1.69:32400/library/metadata/3662/allLeaves // 192.168.1.69:32400/library/metadata/3662/allLeaves
// The metadata ratingkey should be in the Cache // The metadata ratingkey should be in the Cache
@ -128,21 +148,21 @@ namespace Ombi.Api.Plex
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey) public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
{ {
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get); var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId) public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
{ {
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get); var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey) public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
{ {
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get); var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
@ -161,9 +181,9 @@ namespace Ombi.Api.Plex
request.AddQueryString("type", "4"); request.AddQueryString("type", "4");
AddLimitHeaders(request, start, retCount); AddLimitHeaders(request, start, retCount);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexContainer>(request); return await Api.Request<PlexContainer>(request);
} }
/// <summary> /// <summary>
@ -174,8 +194,8 @@ namespace Ombi.Api.Plex
/// <returns></returns> /// <returns></returns>
public async Task<PlexFriends> GetUsers(string authToken) public async Task<PlexFriends> GetUsers(string authToken)
{ {
var request = new Request(string.Empty,FriendsUri, HttpMethod.Get, ContentType.Xml); var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexFriends>(request); return await Api.Request<PlexFriends>(request);
} }
@ -183,43 +203,36 @@ namespace Ombi.Api.Plex
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId) public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
{ {
var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get); var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
AddLimitHeaders(request, 0, 50); AddLimitHeaders(request, 0, 50);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
public async Task<OAuthPin> CreatePin()
{
var request = new Request($"api/v2/pins", "https://plex.tv/", HttpMethod.Post);
request.AddQueryString("strong", "true");
AddHeaders(request);
return await Api.Request<OAuthPin>(request);
}
public async Task<OAuthPin> GetPin(int pinId) public async Task<OAuthPin> GetPin(int pinId)
{ {
var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get); var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get);
AddHeaders(request); await AddHeaders(request);
return await Api.Request<OAuthPin>(request); return await Api.Request<OAuthPin>(request);
} }
public Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard) public async Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl)
{ {
var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get); var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get);
AddHeaders(request); await AddHeaders(request);
var forwardUrl = wizard
? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get)
: new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get);
request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString());
request.AddQueryString("pinID", pinId.ToString()); request.AddQueryString("pinID", pinId.ToString());
request.AddQueryString("code", code); request.AddQueryString("code", code);
request.AddQueryString("context[device][product]", "Ombi"); request.AddQueryString("context[device][product]", ApplicationName);
request.AddQueryString("context[device][environment]", "bundled"); request.AddQueryString("context[device][environment]", "bundled");
request.AddQueryString("clientID", $"OmbiV3"); request.AddQueryString("context[device][layout]", "desktop");
request.AddQueryString("context[device][platform]", "Web");
request.AddQueryString("context[device][device]", "Ombi (Web)");
var s = await GetSettings();
await CheckInstallId(s);
request.AddQueryString("clientID", s.InstallId.ToString("N"));
if (request.FullUri.Fragment.Equals("#")) if (request.FullUri.Fragment.Equals("#"))
{ {
@ -233,26 +246,58 @@ namespace Ombi.Api.Plex
return request.FullUri; return request.FullUri;
} }
public async Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs)
{
var request = new Request(string.Empty, $"https://plex.tv/api/servers/{serverId}/shared_servers", HttpMethod.Post, ContentType.Xml);
await AddHeaders(request, authToken);
request.AddJsonBody(new
{
server_id = serverId,
shared_server = new
{
library_section_ids = libs.Length > 0 ? libs : new int[]{},
invited_email = emailAddress
},
sharing_settings = new { }
});
var result = await Api.RequestContent(request);
try
{
var add = Api.DeserializeXml<PlexAdd>(result);
return new PlexAddWrapper{Add = add};
}
catch (InvalidOperationException)
{
var error = Api.DeserializeXml<AddUserError>(result);
return new PlexAddWrapper{Error = error};
}
}
/// <summary> /// <summary>
/// Adds the required headers and also the authorization header /// Adds the required headers and also the authorization header
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="authToken"></param> /// <param name="authToken"></param>
private void AddHeaders(Request request, string authToken) private async Task AddHeaders(Request request, string authToken)
{ {
request.AddHeader("X-Plex-Token", authToken); request.AddHeader("X-Plex-Token", authToken);
AddHeaders(request); await AddHeaders(request);
} }
/// <summary> /// <summary>
/// Adds the main required headers to the Plex Request /// Adds the main required headers to the Plex Request
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
private void AddHeaders(Request request) private async Task AddHeaders(Request request)
{ {
request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3"); var s = await GetSettings();
await CheckInstallId(s);
request.AddHeader("X-Plex-Client-Identifier", s.InstallId.ToString("N"));
request.AddHeader("X-Plex-Product", ApplicationName); request.AddHeader("X-Plex-Product", ApplicationName);
request.AddHeader("X-Plex-Version", "3"); request.AddHeader("X-Plex-Version", "3");
request.AddHeader("X-Plex-Device", "Ombi (Web)");
request.AddHeader("X-Plex-Platform", "Web");
request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml"); request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml");
request.AddHeader("Accept", "application/json"); request.AddHeader("Accept", "application/json");
} }
@ -262,5 +307,19 @@ namespace Ombi.Api.Plex
request.AddHeader("X-Plex-Container-Start", from.ToString()); request.AddHeader("X-Plex-Container-Start", from.ToString());
request.AddHeader("X-Plex-Container-Size", to.ToString()); request.AddHeader("X-Plex-Container-Size", to.ToString());
} }
private async Task CheckInstallId(PlexSettings s)
{
if (s.InstallId == null || s.InstallId == Guid.Empty)
{
s.InstallId = Guid.NewGuid();
await _plexSettings.SaveSettingsAsync(s);
}
}
private PlexSettings _settings;
private async Task<PlexSettings> GetSettings()
{
return _settings ?? (_settings = await _plexSettings.GetSettingsAsync());
}
} }
} }

View file

@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -79,7 +79,7 @@ namespace Ombi.Api.Radarr
tmdbId = tmdbId, tmdbId = tmdbId,
qualityProfileId = qualityId, qualityProfileId = qualityId,
rootFolderPath = rootPath, rootFolderPath = rootPath,
titleSlug = title, titleSlug = title + year,
monitored = true, monitored = true,
year = year, year = year,
minimumAvailability = minimumAvailability minimumAvailability = minimumAvailability

View file

@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Options" Version="2.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -6,6 +6,30 @@ namespace Ombi.Api.Sonarr.Models
{ {
public class Episode public class Episode
{ {
public Episode()
{
}
public Episode(Episode ep)
{
seriesId = ep.seriesId;
episodeFileId = ep.episodeFileId;
seasonNumber = ep.seasonNumber;
episodeNumber = ep.episodeNumber;
title = ep.title;
airDate = ep.airDate;
airDateUtc = ep.airDateUtc;
overview = ep.overview;
hasFile = ep.hasFile;
monitored = ep.monitored;
unverifiedSceneNumbering = ep.unverifiedSceneNumbering;
id = ep.id;
absoluteEpisodeNumber = ep.absoluteEpisodeNumber;
sceneAbsoluteEpisodeNumber = ep.sceneAbsoluteEpisodeNumber;
sceneEpisodeNumber = ep.sceneEpisodeNumber;
sceneSeasonNumber = ep.sceneSeasonNumber;
}
public int seriesId { get; set; } public int seriesId { get; set; }
public int episodeFileId { get; set; } public int episodeFileId { get; set; }
public int seasonNumber { get; set; } public int seasonNumber { get; set; }
@ -27,6 +51,24 @@ namespace Ombi.Api.Sonarr.Models
public class Episodefile public class Episodefile
{ {
public Episodefile()
{
}
public Episodefile(Episodefile e)
{
seriesId = e.seriesId;
seasonNumber = e.seasonNumber;
relativePath = e.relativePath;
path = e.path;
size = e.size;
dateAdded = e.dateAdded;
sceneName = e.sceneName;
quality = new EpisodeQuality(e.quality);
qualityCutoffNotMet = e.qualityCutoffNotMet;
id = e.id;
}
public int seriesId { get; set; } public int seriesId { get; set; }
public int seasonNumber { get; set; } public int seasonNumber { get; set; }
public string relativePath { get; set; } public string relativePath { get; set; }
@ -41,12 +83,32 @@ namespace Ombi.Api.Sonarr.Models
public class EpisodeQuality public class EpisodeQuality
{ {
public EpisodeQuality()
{
}
public EpisodeQuality(EpisodeQuality e)
{
quality = new Quality(e.quality);
revision = new Revision(e.revision);
}
public Quality quality { get; set; } public Quality quality { get; set; }
public Revision revision { get; set; } public Revision revision { get; set; }
} }
public class Revision public class Revision
{ {
public Revision()
{
}
public Revision(Revision r)
{
version = r.version;
real = r.real;
}
public int version { get; set; } public int version { get; set; }
public int real { get; set; } public int real { get; set; }
} }

View file

@ -23,6 +23,7 @@ namespace Ombi.Api.Sonarr.Models
public string cleanTitle { get; set; } public string cleanTitle { get; set; }
public string imdbId { get; set; } public string imdbId { get; set; }
public string titleSlug { get; set; } public string titleSlug { get; set; }
public string seriesType { get; set; }
public int id { get; set; } public int id { get; set; }
public List<SonarrImage> images { get; set; } public List<SonarrImage> images { get; set; }

View file

@ -2,6 +2,16 @@ namespace Ombi.Api.Sonarr.Models
{ {
public class Quality public class Quality
{ {
public Quality()
{
}
public Quality(Quality q)
{
id = q.id;
name = q.name;
}
public int id { get; set; } public int id { get; set; }
public string name { get; set; } public string name { get; set; }
} }

View file

@ -80,15 +80,20 @@ namespace Ombi.Api
else else
{ {
// XML // XML
XmlSerializer serializer = new XmlSerializer(typeof(T)); return DeserializeXml<T>(receivedString);
StringReader reader = new StringReader(receivedString);
var value = (T)serializer.Deserialize(reader);
return value;
} }
} }
} }
public T DeserializeXml<T>(string receivedString)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
StringReader reader = new StringReader(receivedString);
var value = (T) serializer.Deserialize(reader);
return value;
}
public async Task<string> RequestContent(Request request) public async Task<string> RequestContent(Request request)
{ {
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri)) using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))

View file

@ -7,5 +7,6 @@ namespace Ombi.Api
Task Request(Request request); Task Request(Request request);
Task<T> Request<T>(Request request); Task<T> Request<T>(Request request);
Task<string> RequestContent(Request request); Task<string> RequestContent(Request request);
T DeserializeXml<T>(string receivedString);
} }
} }

View file

@ -9,9 +9,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Polly" Version="5.8.0" /> <PackageReference Include="Polly" Version="6.1.0" />
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" /> <PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
</ItemGroup> </ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -9,7 +9,7 @@
<PackageReference Include="Nunit" Version="3.8.1" /> <PackageReference Include="Nunit" Version="3.8.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -29,7 +29,10 @@ namespace Ombi.Core.Tests.Rule.Search
{ {
ProviderId = "123" ProviderId = "123"
}); });
var search = new SearchMovieViewModel(); var search = new SearchMovieViewModel()
{
TheMovieDbId = "123",
};
var result = await Rule.Execute(search); var result = await Rule.Execute(search);
Assert.True(result.Success); Assert.True(result.Success);

View file

@ -20,12 +20,6 @@ namespace Ombi.Core.Authentication
private readonly IPlexApi _api; private readonly IPlexApi _api;
private readonly ISettingsService<CustomizationSettings> _customizationSettingsService; private readonly ISettingsService<CustomizationSettings> _customizationSettingsService;
public async Task<OAuthPin> RequestPin()
{
var pin = await _api.CreatePin();
return pin;
}
public async Task<string> GetAccessTokenFromPin(int pinId) public async Task<string> GetAccessTokenFromPin(int pinId)
{ {
var pin = await _api.GetPin(pinId); var pin = await _api.GetPin(pinId);
@ -34,19 +28,6 @@ namespace Ombi.Core.Authentication
return string.Empty; return string.Empty;
} }
if (pin.authToken.IsNullOrEmpty())
{
// Looks like we do not have a pin yet, we should retry a few times.
var retryCount = 0;
var retryMax = 5;
var retryWaitMs = 1000;
while (pin.authToken.IsNullOrEmpty() && retryCount < retryMax)
{
retryCount++;
await Task.Delay(retryWaitMs);
pin = await _api.GetPin(pinId);
}
}
return pin.authToken; return pin.authToken;
} }
@ -58,14 +39,14 @@ namespace Ombi.Core.Authentication
public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null) public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null)
{ {
var settings = await _customizationSettingsService.GetSettingsAsync(); var settings = await _customizationSettingsService.GetSettingsAsync();
var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false); var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl);
return url; return url;
} }
public Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress) public async Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress)
{ {
var url = _api.GetOAuthUrl(pinId, code, websiteAddress, true); var url = await _api.GetOAuthUrl(pinId, code, websiteAddress);
return url; return url;
} }
} }

View file

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Ombi.Core.Engine
{
public interface IUserStatsEngine
{
Task<UserStatsSummary> GetSummary(SummaryRequest request);
}
}

View file

@ -15,13 +15,13 @@ namespace Ombi.Core.Engine.Interfaces
Task<RequestEngineResult> DenyChildRequest(int requestId); Task<RequestEngineResult> DenyChildRequest(int requestId);
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type); Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
Task<IEnumerable<TvRequests>> SearchTvRequest(string search); Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search);
Task<TvRequests> UpdateTvRequest(TvRequests request); Task<TvRequests> UpdateTvRequest(TvRequests request);
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position);
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId); Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
Task<ChildRequests> UpdateChildRequest(ChildRequests request); Task<ChildRequests> UpdateChildRequest(ChildRequests request);
Task RemoveTvChild(int requestId); Task RemoveTvChild(int requestId);
Task<RequestEngineResult> ApproveChildRequest(int id); Task<RequestEngineResult> ApproveChildRequest(int id);
Task<IEnumerable<TvRequests>> GetRequestsLite(); Task<IEnumerable<TvRequests>> GetRequestsLite();
Task UpdateQualityProfile(int requestId, int profileId);
Task UpdateRootPath(int requestId, int rootPath);
} }
} }

View file

@ -7,16 +7,10 @@ namespace Ombi.Core.Engine.Interfaces
public interface ITvSearchEngine public interface ITvSearchEngine
{ {
Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm); Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm);
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm);
Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid);
Task<SearchTvShowViewModel> GetShowInformation(int tvdbid); Task<SearchTvShowViewModel> GetShowInformation(int tvdbid);
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree();
Task<IEnumerable<SearchTvShowViewModel>> Popular(); Task<IEnumerable<SearchTvShowViewModel>> Popular();
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree();
Task<IEnumerable<SearchTvShowViewModel>> Anticipated(); Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree();
Task<IEnumerable<SearchTvShowViewModel>> MostWatches(); Task<IEnumerable<SearchTvShowViewModel>> MostWatches();
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree();
Task<IEnumerable<SearchTvShowViewModel>> Trending(); Task<IEnumerable<SearchTvShowViewModel>> Trending();
} }
} }

View file

@ -452,6 +452,7 @@ namespace Ombi.Core.Engine
} }
request.Available = true; request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
NotificationHelper.Notify(request, NotificationType.RequestAvailable); NotificationHelper.Notify(request, NotificationType.RequestAvailable);
await MovieRepository.Update(request); await MovieRepository.Update(request);

View file

@ -1,23 +0,0 @@
using System.Collections.Generic;
namespace Ombi.Core.Engine
{
public class TreeNode<T>
{
public string Label { get; set; }
public T Data { get; set; }
public List<TreeNode<T>> Children { get; set; }
public bool Leaf { get; set; }
public bool Expanded { get; set; }
}
public class TreeNode<T,U>
{
public string Label { get; set; }
public T Data { get; set; }
public List<TreeNode<U>> Children { get; set; }
public bool Leaf { get; set; }
public bool Expanded { get; set; }
}
}

View file

@ -143,7 +143,7 @@ namespace Ombi.Core.Engine
.Include(x => x.ChildRequests) .Include(x => x.ChildRequests)
.ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.SeasonRequests)
.ThenInclude(x => x.Episodes) .ThenInclude(x => x.Episodes)
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault())
.Skip(position).Take(count).ToListAsync(); .Skip(position).Take(count).ToListAsync();
// Filter out children // Filter out children
@ -156,8 +156,9 @@ namespace Ombi.Core.Engine
.Include(x => x.ChildRequests) .Include(x => x.ChildRequests)
.ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.SeasonRequests)
.ThenInclude(x => x.Episodes) .ThenInclude(x => x.Episodes)
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault())
.Skip(position).Take(count).ToListAsync(); .Skip(position).Take(count).ToListAsync();
} }
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
@ -171,24 +172,30 @@ namespace Ombi.Core.Engine
public async Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type) public async Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type)
{ {
var shouldHide = await HideFromOtherUsers(); var shouldHide = await HideFromOtherUsers();
List<TvRequests> allRequests; List<TvRequests> allRequests = null;
if (shouldHide.Hide) if (shouldHide.Hide)
{ {
allRequests = await TvRepository.GetLite(shouldHide.UserId) var tv = TvRepository.GetLite(shouldHide.UserId);
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
.Skip(position).Take(count).ToListAsync(); {
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync();
}
// Filter out children // Filter out children
FilterChildren(allRequests, shouldHide); FilterChildren(allRequests, shouldHide);
} }
else else
{ {
allRequests = await TvRepository.GetLite() var tv = TvRepository.GetLite();
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
.Skip(position).Take(count).ToListAsync(); {
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync();
}
}
if (allRequests == null)
{
return new RequestsViewModel<TvRequests>();
} }
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
return new RequestsViewModel<TvRequests> return new RequestsViewModel<TvRequests>
@ -196,38 +203,6 @@ namespace Ombi.Core.Engine
Collection = allRequests Collection = allRequests
}; };
} }
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
{
var shouldHide = await HideFromOtherUsers();
List<TvRequests> allRequests;
if (shouldHide.Hide)
{
allRequests = await TvRepository.Get(shouldHide.UserId)
.Include(x => x.ChildRequests)
.ThenInclude(x => x.SeasonRequests)
.ThenInclude(x => x.Episodes)
.Where(x => x.ChildRequests.Any())
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
.Skip(position).Take(count).ToListAsync();
FilterChildren(allRequests, shouldHide);
}
else
{
allRequests = await TvRepository.Get()
.Include(x => x.ChildRequests)
.ThenInclude(x => x.SeasonRequests)
.ThenInclude(x => x.Episodes)
.Where(x => x.ChildRequests.Any())
.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
.Skip(position).Take(count).ToListAsync();
}
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
return ParseIntoTreeNode(allRequests);
}
public async Task<IEnumerable<TvRequests>> GetRequests() public async Task<IEnumerable<TvRequests>> GetRequests()
{ {
var shouldHide = await HideFromOtherUsers(); var shouldHide = await HideFromOtherUsers();
@ -288,6 +263,10 @@ namespace Ombi.Core.Engine
private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide) private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide)
{ {
if (allRequests == null)
{
return;
}
// Filter out children // Filter out children
foreach (var t in allRequests) foreach (var t in allRequests)
{ {
@ -350,21 +329,22 @@ namespace Ombi.Core.Engine
return results; return results;
} }
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search) public async Task UpdateRootPath(int requestId, int rootPath)
{ {
var shouldHide = await HideFromOtherUsers(); var allRequests = TvRepository.Get();
IQueryable<TvRequests> allRequests; var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
if (shouldHide.Hide) results.RootFolder = rootPath;
{
allRequests = TvRepository.Get(shouldHide.UserId); await TvRepository.Update(results);
} }
else
{ public async Task UpdateQualityProfile(int requestId, int profileId)
allRequests = TvRepository.Get(); {
} var allRequests = TvRepository.Get();
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
results.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); results.QualityOverride = profileId;
return ParseIntoTreeNode(results);
await TvRepository.Update(results);
} }
public async Task<TvRequests> UpdateTvRequest(TvRequests request) public async Task<TvRequests> UpdateTvRequest(TvRequests request)
@ -516,6 +496,7 @@ namespace Ombi.Core.Engine
}; };
} }
request.Available = true; request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
foreach (var season in request.SeasonRequests) foreach (var season in request.SeasonRequests)
{ {
foreach (var e in season.Episodes) foreach (var e in season.Episodes)
@ -585,29 +566,7 @@ namespace Ombi.Core.Engine
return await AfterRequest(model.ChildRequests.FirstOrDefault()); return await AfterRequest(model.ChildRequests.FirstOrDefault());
} }
private static List<TreeNode<TvRequests, List<ChildRequests>>> ParseIntoTreeNode(IEnumerable<TvRequests> result) private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
{
var node = new List<TreeNode<TvRequests, List<ChildRequests>>>();
foreach (var value in result)
{
node.Add(new TreeNode<TvRequests, List<ChildRequests>>
{
Data = value,
Children = new List<TreeNode<List<ChildRequests>>>
{
new TreeNode<List<ChildRequests>>
{
Data = SortEpisodes(value.ChildRequests),
Leaf = true
}
}
});
}
return node;
}
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
{ {
foreach (var value in items) foreach (var value in items)
{ {

View file

@ -59,11 +59,6 @@ namespace Ombi.Core.Engine
return null; return null;
} }
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm)
{
var result = await Search(searchTerm);
return result.Select(ParseIntoTreeNode).ToList();
}
public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbid) public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbid)
{ {
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid); var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid);
@ -116,19 +111,6 @@ namespace Ombi.Core.Engine
return await ProcessResult(mapped); return await ProcessResult(mapped);
} }
public async Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid)
{
var result = await GetShowInformation(tvdbid);
return ParseIntoTreeNode(result);
}
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree()
{
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
return processed.Select(ParseIntoTreeNode).ToList();
}
public async Task<IEnumerable<SearchTvShowViewModel>> Popular() public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
{ {
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
@ -136,12 +118,6 @@ namespace Ombi.Core.Engine
return processed; return processed;
} }
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree()
{
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
return processed.Select(ParseIntoTreeNode).ToList();
}
public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated() public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated()
{ {
@ -150,12 +126,6 @@ namespace Ombi.Core.Engine
return processed; return processed;
} }
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree()
{
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
return processed.Select(ParseIntoTreeNode).ToList();
}
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches() public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches()
{ {
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
@ -163,13 +133,6 @@ namespace Ombi.Core.Engine
return processed; return processed;
} }
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree()
{
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
return processed.Select(ParseIntoTreeNode).ToList();
}
public async Task<IEnumerable<SearchTvShowViewModel>> Trending() public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
{ {
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
@ -177,22 +140,6 @@ namespace Ombi.Core.Engine
return processed; return processed;
} }
private static TreeNode<SearchTvShowViewModel> ParseIntoTreeNode(SearchTvShowViewModel result)
{
return new TreeNode<SearchTvShowViewModel>
{
Data = result,
Children = new List<TreeNode<SearchTvShowViewModel>>
{
new TreeNode<SearchTvShowViewModel>
{
Data = result, Leaf = true
}
},
Leaf = false
};
}
private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items) private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items)
{ {
var retVal = new List<SearchTvShowViewModel>(); var retVal = new List<SearchTvShowViewModel>();

View file

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Store.Entities;
using Ombi.Store.Repository.Requests;
namespace Ombi.Core.Engine
{
public class UserStatsEngine : IUserStatsEngine
{
public UserStatsEngine(OmbiUserManager um, IMovieRequestRepository movieRequest, ITvRequestRepository tvRequest)
{
_userManager = um;
_movieRequest = movieRequest;
_tvRequest = tvRequest;
}
private readonly OmbiUserManager _userManager;
private readonly IMovieRequestRepository _movieRequest;
private readonly ITvRequestRepository _tvRequest;
public async Task<UserStatsSummary> GetSummary(SummaryRequest request)
{
// get all movie requests
var movies = _movieRequest.GetWithUser();
var filteredMovies = movies.Where(x => x.RequestedDate >= request.From && x.RequestedDate <= request.To);
var tv = _tvRequest.GetLite();
var children = tv.SelectMany(x =>
x.ChildRequests.Where(c => c.RequestedDate >= request.From && c.RequestedDate <= request.To));
var moviesCount = filteredMovies.CountAsync();
var childrenCount = children.CountAsync();
var availableMovies =
movies.Select(x => x.MarkedAsAvailable >= request.From && x.MarkedAsAvailable <= request.To).CountAsync();
var availableChildren = tv.SelectMany(x =>
x.ChildRequests.Where(c => c.MarkedAsAvailable >= request.From && c.MarkedAsAvailable <= request.To)).CountAsync();
var userMovie = filteredMovies.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
var userTv = children.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
return new UserStatsSummary
{
TotalMovieRequests = await moviesCount,
TotalTvRequests = await childrenCount,
CompletedRequestsTv = await availableChildren,
CompletedRequestsMovies = await availableMovies,
MostRequestedUserMovie = (await userMovie).FirstOrDefault().RequestedUser,
MostRequestedUserTv = (await userTv).FirstOrDefault().RequestedUser,
};
}
}
public class SummaryRequest
{
public DateTime From { get; set; }
public DateTime To { get; set; }
}
public class UserStatsSummary
{
public int TotalRequests => TotalTvRequests + TotalMovieRequests;
public int TotalMovieRequests { get; set; }
public int TotalTvRequests { get; set; }
public int TotalIssues { get; set; }
public int CompletedRequestsMovies { get; set; }
public int CompletedRequestsTv { get; set; }
public int CompletedRequests => CompletedRequestsMovies + CompletedRequestsTv;
public OmbiUser MostRequestedUserMovie { get; set; }
public OmbiUser MostRequestedUserTv { get; set; }
}
}

View file

@ -1,16 +1,14 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models;
using Ombi.Api.Plex.Models.OAuth;
namespace Ombi.Core.Authentication namespace Ombi.Core.Authentication
{ {
public interface IPlexOAuthManager public interface IPlexOAuthManager
{ {
Task<string> GetAccessTokenFromPin(int pinId); Task<string> GetAccessTokenFromPin(int pinId);
Task<OAuthPin> RequestPin();
Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null); Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null);
Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress); Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress);
Task<PlexAccount> GetAccount(string accessToken); Task<PlexAccount> GetAccount(string accessToken);
} }
} }

View file

@ -12,9 +12,9 @@
<PackageReference Include="AutoMapper" Version="6.1.1" /> <PackageReference Include="AutoMapper" Version="6.1.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
<PackageReference Include="Hangfire" Version="1.6.19" /> <PackageReference Include="Hangfire" Version="1.6.19" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="2.0.0-preview1-final" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" /> <PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

View file

@ -1,4 +1,6 @@
using System.Linq; using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
@ -59,10 +61,19 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
EmbyEpisode epExists = null; EmbyEpisode epExists = null;
epExists = await allEpisodes.FirstOrDefaultAsync(x => if (item.HasImdb)
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && {
x.Series.ProviderId == item.ProviderId.ToString()); epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
&& e.ImdbId == item.ImdbId);
} if (item.HasTvDb && epExists == null)
{
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
&& e.Series.TvDbId == item.TvDbId);
} if (item.HasTheMovieDb && epExists == null)
{
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
&& e.TheMovieDbId == item.TheMovieDbId);
}
if (epExists != null) if (epExists != null)
{ {

View file

@ -120,6 +120,7 @@ namespace Ombi.Core.Senders
int qualityToUse; int qualityToUse;
string rootFolderPath; string rootFolderPath;
string seriesType;
if (model.SeriesType == SeriesType.Anime) if (model.SeriesType == SeriesType.Anime)
{ {
@ -128,6 +129,8 @@ namespace Ombi.Core.Senders
// TODO make this overrideable via the UI // TODO make this overrideable via the UI
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPathAnime), s); rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPathAnime), s);
int.TryParse(s.QualityProfileAnime, out qualityToUse); int.TryParse(s.QualityProfileAnime, out qualityToUse);
seriesType = "anime";
} }
else else
{ {
@ -136,6 +139,7 @@ namespace Ombi.Core.Senders
// For some reason, if we haven't got one use the first root folder in Sonarr // For some reason, if we haven't got one use the first root folder in Sonarr
// TODO make this overrideable via the UI // TODO make this overrideable via the UI
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s); rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s);
seriesType = "standard";
} }
if (model.ParentRequest.QualityOverride.HasValue) if (model.ParentRequest.QualityOverride.HasValue)
@ -163,27 +167,18 @@ namespace Ombi.Core.Senders
rootFolderPath = rootFolderPath, rootFolderPath = rootFolderPath,
qualityProfileId = qualityToUse, qualityProfileId = qualityToUse,
titleSlug = model.ParentRequest.Title, titleSlug = model.ParentRequest.Title,
seriesType = seriesType,
addOptions = new AddOptions addOptions = new AddOptions
{ {
ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season ignoreEpisodesWithFiles = false, // There shouldn't be any episodes with files, this is a new season
ignoreEpisodesWithoutFiles = true, // We want all missing ignoreEpisodesWithoutFiles = false, // We want all missing
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly. searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
} }
}; };
// Montitor the correct seasons, // Montitor the correct seasons,
// If we have that season in the model then it's monitored! // If we have that season in the model then it's monitored!
var seasonsToAdd = new List<Season>(); var seasonsToAdd = GetSeasonsToCreate(model);
for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
{
var index = i;
var season = new Season
{
seasonNumber = i,
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
};
seasonsToAdd.Add(season);
}
newSeries.seasons = seasonsToAdd; newSeries.seasons = seasonsToAdd;
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri); var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri); existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
@ -237,7 +232,7 @@ namespace Ombi.Core.Senders
{ {
var sonarrEp = sonarrEpList.FirstOrDefault(x => var sonarrEp = sonarrEpList.FirstOrDefault(x =>
x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber); x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber);
if (sonarrEp != null) if (sonarrEp != null && !sonarrEp.monitored)
{ {
sonarrEp.monitored = true; sonarrEp.monitored = true;
episodesToUpdate.Add(sonarrEp); episodesToUpdate.Add(sonarrEp);
@ -245,22 +240,64 @@ namespace Ombi.Core.Senders
} }
} }
var seriesChanges = false; var seriesChanges = false;
foreach (var season in model.SeasonRequests) foreach (var season in model.SeasonRequests)
{ {
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber); var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
var sonarrEpCount = sonarrSeason.Count(); var sonarrEpCount = sonarrSeason.Count();
var ourRequestCount = season.Episodes.Count; var ourRequestCount = season.Episodes.Count;
var existingSeason =
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
if (existingSeason == null)
{
Logger.LogError("There was no season numer {0} in Sonarr for title {1}", season.SeasonNumber, model.ParentRequest.Title);
continue;
}
if (sonarrEpCount == ourRequestCount) if (sonarrEpCount == ourRequestCount)
{ {
// We have the same amount of requests as all of the episodes in the season. // We have the same amount of requests as all of the episodes in the season.
var existingSeason =
result.seasons.First(x => x.seasonNumber == season.SeasonNumber); if (!existingSeason.monitored)
existingSeason.monitored = true; {
seriesChanges = true; existingSeason.monitored = true;
seriesChanges = true;
}
} }
else else
{ {
// Make sure this season is set to monitored
if (!existingSeason.monitored)
{
// We need to monitor it, problem being is all episodes will now be monitored
// So we need to monior the series but unmonitor every episode
// Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
existingSeason.monitored = true;
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
sea.monitored = true;
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
var epToUnmonitor = new List<Episode>();
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
{
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
//{
// // This was previously monitored.
// continue;
//}
ep.monitored = false;
epToUnmonitor.Add(ep);
}
foreach (var epToUpdate in epToUnmonitor)
{
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
}
}
// Now update the episodes that need updating // Now update the episodes that need updating
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber)) foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
{ {
@ -280,6 +317,24 @@ namespace Ombi.Core.Senders
} }
} }
private static List<Season> GetSeasonsToCreate(ChildRequests model)
{
// Let's get a list of seasons just incase we need to change it
var seasonsToUpdate = new List<Season>();
for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
{
var index = i;
var sea = new Season
{
seasonNumber = i,
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
};
seasonsToUpdate.Add(sea);
}
return seasonsToUpdate;
}
private async Task<bool> SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null) private async Task<bool> SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null)
{ {
var tvdbid = model.ParentRequest.TvDbId; var tvdbid = model.ParentRequest.TvDbId;

View file

@ -79,6 +79,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<ITvRequestEngine, TvRequestEngine>(); services.AddTransient<ITvRequestEngine, TvRequestEngine>();
services.AddTransient<ITvSearchEngine, TvSearchEngine>(); services.AddTransient<ITvSearchEngine, TvSearchEngine>();
services.AddTransient<IRuleEvaluator, RuleEvaluator>(); services.AddTransient<IRuleEvaluator, RuleEvaluator>();
services.AddTransient<IUserStatsEngine, UserStatsEngine>();
services.AddTransient<IMovieSender, MovieSender>(); services.AddTransient<IMovieSender, MovieSender>();
services.AddTransient<IRecentlyAddedEngine, RecentlyAddedEngine>(); services.AddTransient<IRecentlyAddedEngine, RecentlyAddedEngine>();
services.AddTransient<ITvSender, TvSender>(); services.AddTransient<ITvSender, TvSender>();

View file

@ -9,9 +9,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -10,7 +10,7 @@ namespace Ombi.Helpers
public static string GetEmbyMediaUrl(string mediaId) public static string GetEmbyMediaUrl(string mediaId)
{ {
var url = var url =
$"http://app.emby.media/itemdetails.html?id={mediaId}"; $"http://app.emby.media/#!/itemdetails.html?id={mediaId}";
return url; return url;
} }
} }

View file

@ -10,8 +10,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="EasyCrypto" Version="3.3.2" /> <PackageReference Include="EasyCrypto" Version="3.3.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" /> <PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" /> <PackageReference Include="System.Security.Claims" Version="4.3.0" />

View file

@ -182,14 +182,16 @@
</tr> </tr>
<tr> <tr>
<td> <td>
{@RECENTLYADDED} {@RECENTLYADDED}
</td>
</tr>
<!-- END MAIN CONTENT AREA --> <!-- END MAIN CONTENT AREA -->
</table> </table>
<!-- START FOOTER --> <!-- START FOOTER -->
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;"> <div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr> <tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center"> <td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a> Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a>

View file

@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nunit" Version="3.8.1" /> <PackageReference Include="Nunit" Version="3.8.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.7.99" />
</ItemGroup> </ItemGroup>

View file

@ -57,7 +57,7 @@ namespace Ombi.Notifications.Agents
// Get admin devices // Get admin devices
var playerIds = await GetAdmins(NotificationType.NewRequest); var playerIds = await GetAdmins(NotificationType.NewRequest);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model, true);
} }
protected override async Task NewIssue(NotificationOptions model, MobileNotificationSettings settings) protected override async Task NewIssue(NotificationOptions model, MobileNotificationSettings settings)
@ -75,7 +75,7 @@ namespace Ombi.Notifications.Agents
// Get admin devices // Get admin devices
var playerIds = await GetAdmins(NotificationType.Issue); var playerIds = await GetAdmins(NotificationType.Issue);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
protected override async Task IssueComment(NotificationOptions model, MobileNotificationSettings settings) protected override async Task IssueComment(NotificationOptions model, MobileNotificationSettings settings)
@ -97,13 +97,13 @@ namespace Ombi.Notifications.Agents
{ {
// Send to user // Send to user
var playerIds = GetUsers(model, NotificationType.IssueComment); var playerIds = GetUsers(model, NotificationType.IssueComment);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
else else
{ {
// Send to admin // Send to admin
var playerIds = await GetAdmins(NotificationType.IssueComment); var playerIds = await GetAdmins(NotificationType.IssueComment);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
} }
} }
@ -124,7 +124,7 @@ namespace Ombi.Notifications.Agents
// Send to user // Send to user
var playerIds = GetUsers(model, NotificationType.IssueResolved); var playerIds = GetUsers(model, NotificationType.IssueResolved);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
@ -149,7 +149,7 @@ namespace Ombi.Notifications.Agents
}; };
// Get admin devices // Get admin devices
var playerIds = await GetAdmins(NotificationType.Test); var playerIds = await GetAdmins(NotificationType.Test);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
protected override async Task RequestDeclined(NotificationOptions model, MobileNotificationSettings settings) protected override async Task RequestDeclined(NotificationOptions model, MobileNotificationSettings settings)
@ -168,7 +168,7 @@ namespace Ombi.Notifications.Agents
// Send to user // Send to user
var playerIds = GetUsers(model, NotificationType.RequestDeclined); var playerIds = GetUsers(model, NotificationType.RequestDeclined);
await AddSubscribedUsers(playerIds); await AddSubscribedUsers(playerIds);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
protected override async Task RequestApproved(NotificationOptions model, MobileNotificationSettings settings) protected override async Task RequestApproved(NotificationOptions model, MobileNotificationSettings settings)
@ -188,7 +188,7 @@ namespace Ombi.Notifications.Agents
var playerIds = GetUsers(model, NotificationType.RequestApproved); var playerIds = GetUsers(model, NotificationType.RequestApproved);
await AddSubscribedUsers(playerIds); await AddSubscribedUsers(playerIds);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
protected override async Task AvailableRequest(NotificationOptions model, MobileNotificationSettings settings) protected override async Task AvailableRequest(NotificationOptions model, MobileNotificationSettings settings)
@ -207,20 +207,20 @@ namespace Ombi.Notifications.Agents
var playerIds = GetUsers(model, NotificationType.RequestAvailable); var playerIds = GetUsers(model, NotificationType.RequestAvailable);
await AddSubscribedUsers(playerIds); await AddSubscribedUsers(playerIds);
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
protected override Task Send(NotificationMessage model, MobileNotificationSettings settings) protected override Task Send(NotificationMessage model, MobileNotificationSettings settings)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected async Task Send(List<string> playerIds, NotificationMessage model, MobileNotificationSettings settings) protected async Task Send(List<string> playerIds, NotificationMessage model, MobileNotificationSettings settings, NotificationOptions requestModel, bool isAdminNotification = false)
{ {
if (playerIds == null || !playerIds.Any()) if (playerIds == null || !playerIds.Any())
{ {
return; return;
} }
var response = await _api.PushNotification(playerIds, model.Message); var response = await _api.PushNotification(playerIds, model.Message, isAdminNotification, requestModel.RequestId, (int)requestModel.RequestType);
_logger.LogDebug("Sent message to {0} recipients with message id {1}", response.recipients, response.id); _logger.LogDebug("Sent message to {0} recipients with message id {1}", response.recipients, response.id);
} }
@ -239,7 +239,7 @@ namespace Ombi.Notifications.Agents
} }
var playerIds = user.NotificationUserIds.Select(x => x.PlayerId).ToList(); var playerIds = user.NotificationUserIds.Select(x => x.PlayerId).ToList();
await Send(playerIds, notification, settings); await Send(playerIds, notification, settings, model);
} }
private async Task<List<string>> GetAdmins(NotificationType type) private async Task<List<string>> GetAdmins(NotificationType type)

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Humanizer;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Notifications.Models; using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
@ -39,7 +40,7 @@ namespace Ombi.Notifications
RequestedDate = req?.RequestedDate.ToString("D"); RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty()) if (Type.IsNullOrEmpty())
{ {
Type = req?.RequestType.ToString(); Type = req?.RequestType.Humanize();
} }
Overview = req?.Overview; Overview = req?.Overview;
Year = req?.ReleaseDate.Year.ToString(); Year = req?.ReleaseDate.Year.ToString();
@ -91,7 +92,7 @@ namespace Ombi.Notifications
RequestedDate = req?.RequestedDate.ToString("D"); RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty()) if (Type.IsNullOrEmpty())
{ {
Type = req?.RequestType.ToString(); Type = req?.RequestType.Humanize();
} }
Overview = req?.ParentRequest.Overview; Overview = req?.ParentRequest.Overview;
@ -161,7 +162,7 @@ namespace Ombi.Notifications
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty; IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty; NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty; UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val : string.Empty; Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val.Humanize() : string.Empty;
} }
// User Defined // User Defined

View file

@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ensure.That" Version="7.0.0-pre32" /> <PackageReference Include="Ensure.That" Version="7.0.0-pre32" />
<PackageReference Include="MailKit" Version="2.0.3" /> <PackageReference Include="MailKit" Version="2.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.3" /> <PackageReference Include="Microsoft.AspNetCore" Version="2.1.2" />
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.7.99" />
<PackageReference Include="Nunit" Version="3.10.1" /> <PackageReference Include="Nunit" Version="3.10.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -89,6 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby
_log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty); _log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty);
movie.Available = true; movie.Available = true;
movie.MarkedAsAvailable = DateTime.Now;
if (movie.Available) if (movie.Available)
{ {
var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty; var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty;
@ -121,24 +122,53 @@ namespace Ombi.Schedule.Jobs.Emby
foreach (var child in tv) foreach (var child in tv)
{ {
IQueryable<EmbyEpisode> seriesEpisodes;
if (child.ParentRequest.TvDbId > 0) var useImdb = false;
var useTvDb = false;
if (child.ParentRequest.ImdbId.HasValue())
{ {
seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == child.ParentRequest.TvDbId.ToString()); useImdb = true;
} }
else if(child.ParentRequest.ImdbId.HasValue())
if (child.ParentRequest.TvDbId.ToString().HasValue())
{ {
seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == child.ParentRequest.ImdbId); useTvDb = true;
} }
else
var tvDbId = child.ParentRequest.TvDbId;
var imdbId = child.ParentRequest.ImdbId;
IQueryable<EmbyEpisode> seriesEpisodes = null;
if (useImdb)
{
seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
}
if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()))
{
seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
}
if (seriesEpisodes == null)
{ {
continue; continue;
} }
if (!seriesEpisodes.Any())
{
// Let's try and match the series by name
seriesEpisodes = embyEpisodes.Where(x =>
x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase));
}
foreach (var season in child.SeasonRequests) foreach (var season in child.SeasonRequests)
{ {
foreach (var episode in season.Episodes) foreach (var episode in season.Episodes)
{ {
if (episode.Available)
{
continue;
}
var foundEp = await seriesEpisodes.FirstOrDefaultAsync( var foundEp = await seriesEpisodes.FirstOrDefaultAsync(
x => x.EpisodeNumber == episode.EpisodeNumber && x => x.EpisodeNumber == episode.EpisodeNumber &&
x.SeasonNumber == episode.Season.SeasonNumber); x.SeasonNumber == episode.Season.SeasonNumber);
@ -156,13 +186,14 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
// We have fulfulled this request! // We have fulfulled this request!
child.Available = true; child.Available = true;
child.MarkedAsAvailable = DateTime.Now;
BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions
{ {
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.RequestAvailable, NotificationType = NotificationType.RequestAvailable,
RequestId = child.ParentRequestId, RequestId = child.Id,
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email, Recipient = child.RequestedUser.Email
})); }));
} }
} }

View file

@ -68,40 +68,96 @@ namespace Ombi.Schedule.Jobs.Emby
if (!ValidateSettings(server)) if (!ValidateSettings(server))
return; return;
await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); //await _repo.ExecuteSql("DELETE FROM EmbyEpisode");
await _repo.ExecuteSql("DELETE FROM EmbyContent"); //await _repo.ExecuteSql("DELETE FROM EmbyContent");
var movies = await _api.GetAllMovies(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var totalCount = movies.TotalRecordCount;
var processed = 1;
var movies = await _api.GetAllMovies(server.ApiKey, server.AdministratorId, server.FullUri);
var mediaToAdd = new HashSet<EmbyContent>(); var mediaToAdd = new HashSet<EmbyContent>();
foreach (var movie in movies.Items)
{
// Regular movie
await ProcessMovies(movie, mediaToAdd);
}
// TV Time
var tv = await _api.GetAllShows(server.ApiKey, server.AdministratorId, server.FullUri);
foreach (var tvShow in tv.Items) while (processed < totalCount)
{ {
if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) foreach (var movie in movies.Items)
{ {
Log.Error("Provider Id on tv {0} is null", tvShow.Name); if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase))
continue; {
var movieInfo =
await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri);
foreach (var item in movieInfo.Items)
{
await ProcessMovies(item, mediaToAdd);
}
processed++;
}
else
{
processed++;
// Regular movie
await ProcessMovies(movie, mediaToAdd);
}
} }
var existingTv = await _repo.GetByEmbyId(tvShow.Id); // Get the next batch
if (existingTv == null) movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
mediaToAdd.Add(new EmbyContent await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();
}
// TV Time
var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var totalTv = tv.TotalRecordCount;
processed = 1;
while (processed < totalTv)
{
foreach (var tvShow in tv.Items)
{
try
{ {
TvDbId = tvShow.ProviderIds?.Tvdb,
ImdbId = tvShow.ProviderIds?.Imdb, processed++;
TheMovieDbId = tvShow.ProviderIds?.Tmdb, if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb))
Title = tvShow.Name, {
Type = EmbyMediaType.Series, _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name);
EmbyId = tvShow.Id, continue;
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), }
AddedAt = DateTime.UtcNow
}); var existingTv = await _repo.GetByEmbyId(tvShow.Id);
if (existingTv == null)
{
_logger.LogDebug("Adding new TV Show {0}", tvShow.Name);
mediaToAdd.Add(new EmbyContent
{
TvDbId = tvShow.ProviderIds?.Tvdb,
ImdbId = tvShow.ProviderIds?.Imdb,
TheMovieDbId = tvShow.ProviderIds?.Tmdb,
Title = tvShow.Name,
Type = EmbyMediaType.Series,
EmbyId = tvShow.Id,
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id),
AddedAt = DateTime.UtcNow
});
}
else
{
_logger.LogDebug("We already have TV Show {0}", tvShow.Name);
}
}
catch (Exception)
{
throw;
}
}
// Get the next batch
tv = await _api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();
} }
if (mediaToAdd.Any()) if (mediaToAdd.Any())
@ -112,8 +168,10 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
// Check if it exists // Check if it exists
var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); var existingMovie = await _repo.GetByEmbyId(movieInfo.Id);
var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id);
if (existingMovie == null) if (existingMovie == null && !alreadyGoingToAdd)
{
_logger.LogDebug("Adding new movie {0}", movieInfo.Name);
content.Add(new EmbyContent content.Add(new EmbyContent
{ {
ImdbId = movieInfo.ProviderIds.Imdb, ImdbId = movieInfo.ProviderIds.Imdb,
@ -124,6 +182,12 @@ namespace Ombi.Schedule.Jobs.Emby
Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id), Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id),
AddedAt = DateTime.UtcNow, AddedAt = DateTime.UtcNow,
}); });
}
else
{
// we have this
_logger.LogDebug("We already have movie {0}", movieInfo.Name);
}
} }
private bool ValidateSettings(EmbyServers server) private bool ValidateSettings(EmbyServers server)

View file

@ -73,37 +73,51 @@ namespace Ombi.Schedule.Jobs.Emby
private async Task CacheEpisodes(EmbyServers server) private async Task CacheEpisodes(EmbyServers server)
{ {
var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, server.AdministratorId, server.FullUri); var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri);
var epToAdd = new List<EmbyEpisode>(); var total = allEpisodes.TotalRecordCount;
var processed = 1;
foreach (var ep in allEpisodes.Items) var epToAdd = new HashSet<EmbyEpisode>();
while (processed < total)
{ {
// Let's make sure we have the parent request, stop those pesky forign key errors, foreach (var ep in allEpisodes.Items)
// Damn me having data integrity
var parent = await _repo.GetByEmbyId(ep.SeriesId);
if (parent == null)
{ {
_logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", ep.Name); processed++;
continue; // Let's make sure we have the parent request, stop those pesky forign key errors,
// Damn me having data integrity
var parent = await _repo.GetByEmbyId(ep.SeriesId);
if (parent == null)
{
_logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this",
ep.Name);
continue;
}
var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id);
// Make sure it's not in the hashset too
var existingInList = epToAdd.Any(x => x.EmbyId == ep.Id);
if (existingEpisode == null && !existingInList)
{
_logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName);
// add it
epToAdd.Add(new EmbyEpisode
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumber,
SeasonNumber = ep.ParentIndexNumber,
ParentId = ep.SeriesId,
TvDbId = ep.ProviderIds.Tvdb,
TheMovieDbId = ep.ProviderIds.Tmdb,
ImdbId = ep.ProviderIds.Imdb,
Title = ep.Name,
AddedAt = DateTime.UtcNow
});
}
} }
var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id); await _repo.AddRange(epToAdd);
if (existingEpisode == null) epToAdd.Clear();
{ allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri);
// add it
epToAdd.Add(new EmbyEpisode
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumber,
SeasonNumber = ep.ParentIndexNumber,
ParentId = ep.SeriesId,
TvDbId = ep.ProviderIds.Tvdb,
TheMovieDbId = ep.ProviderIds.Tmdb,
ImdbId = ep.ProviderIds.Imdb,
Title = ep.Name,
AddedAt = DateTime.UtcNow
});
}
} }
if (epToAdd.Any()) if (epToAdd.Any())

View file

@ -492,41 +492,42 @@ namespace Ombi.Schedule.Jobs.Ombi
var orderedTv = series.OrderByDescending(x => x.AddedAt); var orderedTv = series.OrderByDescending(x => x.AddedAt);
foreach (var t in orderedTv) foreach (var t in orderedTv)
{ {
if (!t.HasTvDb)
{
// We may need to use themoviedb for the imdbid or their own id to get info
if (t.HasTheMovieDb)
{
int.TryParse(t.TheMovieDbId, out var movieId);
var externals = await _movieApi.GetTvExternals(movieId);
if (externals == null || externals.tvdb_id <= 0)
{
continue;
}
t.TvDbId = externals.tvdb_id.ToString();
}
// WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out.
//else if(t.HasImdb)
//{
// // Check the imdbid
// var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id);
// if (externals?.tv_results == null || externals.tv_results.Length <= 0)
// {
// continue;
// }
// t.TvDbId = externals.tv_results.FirstOrDefault()..ToString();
//}
}
int.TryParse(t.TvDbId, out var tvdbId);
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
if (info == null)
{
continue;
}
try try
{ {
if (!t.HasTvDb)
{
// We may need to use themoviedb for the imdbid or their own id to get info
if (t.HasTheMovieDb)
{
int.TryParse(t.TheMovieDbId, out var movieId);
var externals = await _movieApi.GetTvExternals(movieId);
if (externals == null || externals.tvdb_id <= 0)
{
continue;
}
t.TvDbId = externals.tvdb_id.ToString();
}
// WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out.
//else if(t.HasImdb)
//{
// // Check the imdbid
// var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id);
// if (externals?.tv_results == null || externals.tv_results.Length <= 0)
// {
// continue;
// }
// t.TvDbId = externals.tv_results.FirstOrDefault()..ToString();
//}
}
int.TryParse(t.TvDbId, out var tvdbId);
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
if (info == null)
{
continue;
}
var banner = info.image?.original; var banner = info.image?.original;
if (!string.IsNullOrEmpty(banner)) if (!string.IsNullOrEmpty(banner))
{ {
@ -588,7 +589,7 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}");
} }
} }
catch (Exception e) catch (Exception e)
{ {
@ -676,20 +677,20 @@ namespace Ombi.Schedule.Jobs.Ombi
var orderedTv = series.OrderByDescending(x => x.AddedAt); var orderedTv = series.OrderByDescending(x => x.AddedAt);
foreach (var t in orderedTv) foreach (var t in orderedTv)
{ {
if (!t.TvDbId.HasValue())
{
continue;
}
int.TryParse(t.TvDbId, out var tvdbId);
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
if (info == null)
{
continue;
}
try try
{ {
if (!t.TvDbId.HasValue())
{
continue;
}
int.TryParse(t.TvDbId, out var tvdbId);
var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId);
if (info == null)
{
continue;
}
var banner = info.image?.original; var banner = info.image?.original;
if (!string.IsNullOrEmpty(banner)) if (!string.IsNullOrEmpty(banner))
{ {
@ -752,7 +753,7 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}");
} }
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -72,7 +72,7 @@ namespace Ombi.Schedule.Jobs.Ombi
Logger.LogDebug(LoggingEvents.Updater, "Starting Update job"); Logger.LogDebug(LoggingEvents.Updater, "Starting Update job");
var settings = await Settings.GetSettingsAsync(); var settings = await Settings.GetSettingsAsync();
if (!settings.AutoUpdateEnabled) if (!settings.AutoUpdateEnabled && !settings.TestMode)
{ {
Logger.LogDebug(LoggingEvents.Updater, "Auto update is not enabled"); Logger.LogDebug(LoggingEvents.Updater, "Auto update is not enabled");
return; return;
@ -83,7 +83,7 @@ namespace Ombi.Schedule.Jobs.Ombi
var productVersion = AssemblyHelper.GetRuntimeVersion(); var productVersion = AssemblyHelper.GetRuntimeVersion();
Logger.LogDebug(LoggingEvents.Updater, "Product Version {0}", productVersion); Logger.LogDebug(LoggingEvents.Updater, "Product Version {0}", productVersion);
var serverVersion = string.Empty;
try try
{ {
var productArray = GetVersion(); var productArray = GetVersion();
@ -96,13 +96,17 @@ namespace Ombi.Schedule.Jobs.Ombi
Logger.LogDebug(LoggingEvents.Updater, "Branch {0}", branch); Logger.LogDebug(LoggingEvents.Updater, "Branch {0}", branch);
Logger.LogDebug(LoggingEvents.Updater, "Looking for updates now"); Logger.LogDebug(LoggingEvents.Updater, "Looking for updates now");
//TODO this fails because the branch = featureupdater when it should be feature/updater
var updates = await Processor.Process(branch); var updates = await Processor.Process(branch);
Logger.LogDebug(LoggingEvents.Updater, "Updates: {0}", updates); Logger.LogDebug(LoggingEvents.Updater, "Updates: {0}", updates);
var serverVersion = updates.UpdateVersionString;
serverVersion = updates.UpdateVersionString;
Logger.LogDebug(LoggingEvents.Updater, "Service Version {0}", updates.UpdateVersionString); Logger.LogDebug(LoggingEvents.Updater, "Service Version {0}", updates.UpdateVersionString);
if (!serverVersion.Equals(version, StringComparison.CurrentCultureIgnoreCase))
if (!serverVersion.Equals(version, StringComparison.CurrentCultureIgnoreCase) || settings.TestMode)
{ {
// Let's download the correct zip // Let's download the correct zip
var desc = RuntimeInformation.OSDescription; var desc = RuntimeInformation.OSDescription;
@ -135,7 +139,8 @@ namespace Ombi.Schedule.Jobs.Ombi
if (process == Architecture.Arm) if (process == Architecture.Arm)
{ {
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm.", CompareOptions.IgnoreCase)); download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm.", CompareOptions.IgnoreCase));
} else if (process == Architecture.Arm64) }
else if (process == Architecture.Arm64)
{ {
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm64.", CompareOptions.IgnoreCase)); download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm64.", CompareOptions.IgnoreCase));
} }
@ -206,33 +211,35 @@ namespace Ombi.Schedule.Jobs.Ombi
updaterExtension = ".exe"; updaterExtension = ".exe";
} }
var updaterFile = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), var updaterFile = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
"TempUpdate", $"Ombi.Updater{updaterExtension}"); "TempUpdate", "updater", $"Ombi.Updater{updaterExtension}");
// Make sure the file is an executable // Make sure the file is an executable
ExecLinuxCommand($"chmod +x {updaterFile}"); //ExecLinuxCommand($"chmod +x {updaterFile}");
// There must be an update // There must be an update
var start = new ProcessStartInfo var start = new ProcessStartInfo
{ {
UseShellExecute = true, UseShellExecute = false,
CreateNoWindow = false, // Ignored if UseShellExecute is set to true CreateNoWindow = true, // Ignored if UseShellExecute is set to true
FileName = updaterFile, FileName = updaterFile,
Arguments = GetArgs(settings), Arguments = GetArgs(settings),
WorkingDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "TempUpdate"), WorkingDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "TempUpdate"),
}; };
if (settings.Username.HasValue()) //if (settings.Username.HasValue())
//{
// start.UserName = settings.Username;
//}
//if (settings.Password.HasValue())
//{
// start.Password = settings.Password.ToSecureString();
//}
using (var proc = new Process { StartInfo = start })
{ {
start.UserName = settings.Username; proc.Start();
} }
if (settings.Password.HasValue())
{
start.Password = settings.Password.ToSecureString();
}
var proc = new Process { StartInfo = start };
proc.Start();
Logger.LogDebug(LoggingEvents.Updater, "Bye bye"); Logger.LogDebug(LoggingEvents.Updater, "Bye bye");
} }
} }
@ -254,10 +261,10 @@ namespace Ombi.Schedule.Jobs.Ombi
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append($"--applicationPath \"{currentLocation}\" --processname \"{processName}\" "); sb.Append($"--applicationPath \"{currentLocation}\" --processname \"{processName}\" ");
if (settings.WindowsService) //if (settings.WindowsService)
{ //{
sb.Append($"--windowsServiceName \"{settings.WindowsServiceName}\" "); // sb.Append($"--windowsServiceName \"{settings.WindowsServiceName}\" ");
} //}
var sb2 = new StringBuilder(); var sb2 = new StringBuilder();
if (url?.Value.HasValue() ?? false) if (url?.Value.HasValue() ?? false)
{ {

View file

@ -8,7 +8,7 @@ namespace Ombi.Schedule.Jobs.Plex.Models
public IEnumerable<int> Content { get; set; } public IEnumerable<int> Content { get; set; }
public IEnumerable<int> Episodes { get; set; } public IEnumerable<int> Episodes { get; set; }
public bool HasProcessedContent => Content.Any(); public bool HasProcessedContent => Content?.Any() ?? false;
public bool HasProcessedEpisodes => Episodes.Any(); public bool HasProcessedEpisodes => Episodes?.Any() ?? false;
} }
} }

View file

@ -79,11 +79,16 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
} }
if (useTvDb) if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()) )
{ {
seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
} }
if (seriesEpisodes == null)
{
continue;
}
if (!seriesEpisodes.Any()) if (!seriesEpisodes.Any())
{ {
// Let's try and match the series by name // Let's try and match the series by name
@ -118,6 +123,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
// We have fulfulled this request! // We have fulfulled this request!
child.Available = true; child.Available = true;
child.MarkedAsAvailable = DateTime.Now;
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
{ {
DateTime = DateTime.Now, DateTime = DateTime.Now,
@ -158,6 +164,7 @@ namespace Ombi.Schedule.Jobs.Plex
} }
movie.Available = true; movie.Available = true;
movie.MarkedAsAvailable = DateTime.Now;
if (movie.Available) if (movie.Available)
{ {
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions

View file

@ -150,9 +150,9 @@ namespace Ombi.Schedule.Jobs.Plex
var retVal = new ProcessedContent(); var retVal = new ProcessedContent();
var contentProcessed = new Dictionary<int, int>(); var contentProcessed = new Dictionary<int, int>();
var episodesProcessed = new List<int>(); var episodesProcessed = new List<int>();
Logger.LogInformation("Getting all content from server {0}", servers.Name); Logger.LogDebug("Getting all content from server {0}", servers.Name);
var allContent = await GetAllContent(servers, recentlyAddedSearch); var allContent = await GetAllContent(servers, recentlyAddedSearch);
Logger.LogInformation("We found {0} items", allContent.Count); Logger.LogDebug("We found {0} items", allContent.Count);
// Let's now process this. // Let's now process this.
var contentToAdd = new HashSet<PlexServerContent>(); var contentToAdd = new HashSet<PlexServerContent>();
@ -163,7 +163,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
if (content.viewGroup.Equals(PlexMediaType.Episode.ToString(), StringComparison.CurrentCultureIgnoreCase)) if (content.viewGroup.Equals(PlexMediaType.Episode.ToString(), StringComparison.CurrentCultureIgnoreCase))
{ {
Logger.LogInformation("Found some episodes, this must be a recently added sync"); Logger.LogDebug("Found some episodes, this must be a recently added sync");
var count = 0; var count = 0;
foreach (var epInfo in content.Metadata ?? new Metadata[]{}) foreach (var epInfo in content.Metadata ?? new Metadata[]{})
{ {
@ -208,7 +208,7 @@ namespace Ombi.Schedule.Jobs.Plex
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
{ {
// Process Shows // Process Shows
Logger.LogInformation("Processing TV Shows"); Logger.LogDebug("Processing TV Shows");
var count = 0; var count = 0;
foreach (var show in content.Metadata ?? new Metadata[] { }) foreach (var show in content.Metadata ?? new Metadata[] { })
{ {
@ -237,7 +237,7 @@ namespace Ombi.Schedule.Jobs.Plex
} }
if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)) if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase))
{ {
Logger.LogInformation("Processing Movies"); Logger.LogDebug("Processing Movies");
foreach (var movie in content?.Metadata ?? new Metadata[] { }) foreach (var movie in content?.Metadata ?? new Metadata[] { })
{ {
// Let's check if we have this movie // Let's check if we have this movie
@ -251,7 +251,7 @@ namespace Ombi.Schedule.Jobs.Plex
//var existing = await Repo.GetByKey(movie.ratingKey); //var existing = await Repo.GetByKey(movie.ratingKey);
if (existing != null) if (existing != null)
{ {
Logger.LogInformation("We already have movie {0}", movie.title); Logger.LogDebug("We already have movie {0}", movie.title);
continue; continue;
} }
@ -261,7 +261,7 @@ namespace Ombi.Schedule.Jobs.Plex
await Repo.Delete(hasSameKey); await Repo.Delete(hasSameKey);
} }
Logger.LogInformation("Adding movie {0}", movie.title); Logger.LogDebug("Adding movie {0}", movie.title);
var metaData = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, var metaData = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
movie.ratingKey); movie.ratingKey);
var providerIds = PlexHelper.GetProviderIdFromPlexGuid(metaData.MediaContainer.Metadata var providerIds = PlexHelper.GetProviderIdFromPlexGuid(metaData.MediaContainer.Metadata
@ -381,6 +381,19 @@ namespace Ombi.Schedule.Jobs.Plex
if (existingContent != null) if (existingContent != null)
{ {
// Let's make sure that we have some sort of ID e.g. Imdbid for this,
// Looks like it's possible to not have an Id for a show
// I suspect we cached that show just as it was added to Plex.
if (!existingContent.HasImdb && !existingContent.HasTheMovieDb && !existingContent.HasTvDb)
{
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
existingContent.Key);
GetProviderIds(showMetadata, existingContent);
await Repo.Update(existingContent);
}
// Just check the key // Just check the key
if (existingKey != null) if (existingKey != null)
{ {
@ -421,7 +434,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
try try
{ {
Logger.LogInformation("We already have show {0} checking for new seasons", Logger.LogDebug("We already have show {0} checking for new seasons",
existingContent.Title); existingContent.Title);
// Ok so we have it, let's check if there are any new seasons // Ok so we have it, let's check if there are any new seasons
var itemAdded = false; var itemAdded = false;
@ -472,16 +485,13 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
try try
{ {
Logger.LogInformation("New show {0}, so add it", show.title); Logger.LogDebug("New show {0}, so add it", show.title);
// Get the show metadata... This sucks since the `metadata` var contains all information about the show // Get the show metadata... This sucks since the `metadata` var contains all information about the show
// But it does not contain the `guid` property that we need to pull out thetvdb id... // But it does not contain the `guid` property that we need to pull out thetvdb id...
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
show.ratingKey); show.ratingKey);
var providerIds =
PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata.FirstOrDefault()
.guid);
var item = new PlexServerContent var item = new PlexServerContent
{ {
AddedAt = DateTime.Now, AddedAt = DateTime.Now,
@ -492,20 +502,7 @@ namespace Ombi.Schedule.Jobs.Plex
Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey), Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey),
Seasons = new List<PlexSeasonsContent>() Seasons = new List<PlexSeasonsContent>()
}; };
if (providerIds.Type == ProviderType.ImdbId) GetProviderIds(showMetadata, item);
{
item.ImdbId = providerIds.ImdbId;
}
if (providerIds.Type == ProviderType.TheMovieDbId)
{
item.TheMovieDbId = providerIds.TheMovieDb;
}
if (providerIds.Type == ProviderType.TvDbId)
{
item.TvDbId = providerIds.TheTvDb;
}
// Let's just double check to make sure we do not have it now we have some id's // Let's just double check to make sure we do not have it now we have some id's
var existingImdb = false; var existingImdb = false;
@ -547,6 +544,27 @@ namespace Ombi.Schedule.Jobs.Plex
} }
} }
private static void GetProviderIds(PlexMetadata showMetadata, PlexServerContent existingContent)
{
var providerIds =
PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata.FirstOrDefault()
.guid);
if (providerIds.Type == ProviderType.ImdbId)
{
existingContent.ImdbId = providerIds.ImdbId;
}
if (providerIds.Type == ProviderType.TheMovieDbId)
{
existingContent.TheMovieDbId = providerIds.TheMovieDb;
}
if (providerIds.Type == ProviderType.TvDbId)
{
existingContent.TvDbId = providerIds.TheTvDb;
}
}
/// <summary> /// <summary>
/// Gets all the library sections. /// Gets all the library sections.
/// If the user has specified only certain libraries then we will only look for those /// If the user has specified only certain libraries then we will only look for those
@ -573,7 +591,7 @@ namespace Ombi.Schedule.Jobs.Plex
.Select(x => x.Key.ToString()).ToList(); .Select(x => x.Key.ToString()).ToList();
if (!keys.Contains(dir.key)) if (!keys.Contains(dir.key))
{ {
Logger.LogInformation("Lib {0} is not monitored, so skipping", dir.key); Logger.LogDebug("Lib {0} is not monitored, so skipping", dir.key);
// We are not monitoring this lib // We are not monitoring this lib
continue; continue;
} }

View file

@ -57,6 +57,10 @@ namespace Ombi.Schedule.Jobs.Sonarr
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrEpisodeCache"); await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrEpisodeCache");
foreach (var s in sonarrSeries) foreach (var s in sonarrSeries)
{ {
if (!s.monitored)
{
continue;
}
_log.LogDebug("Syncing series: {0}", s.title); _log.LogDebug("Syncing series: {0}", s.title);
var episodes = await _api.GetEpisodes(s.id, settings.ApiKey, settings.FullUri); var episodes = await _api.GetEpisodes(s.id, settings.ApiKey, settings.FullUri);
var monitoredEpisodes = episodes.Where(x => x.monitored || x.hasFile); var monitoredEpisodes = episodes.Where(x => x.monitored || x.hasFile);

View file

@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup> </ItemGroup>

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
namespace Ombi.Core.Settings.Models.External namespace Ombi.Core.Settings.Models.External
@ -6,6 +7,10 @@ namespace Ombi.Core.Settings.Models.External
public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings
{ {
public bool Enable { get; set; } public bool Enable { get; set; }
/// <summary>
/// This is the ClientId for OAuth
/// </summary>
public Guid InstallId { get; set; }
public List<PlexServers> Servers { get; set; } public List<PlexServers> Servers { get; set; }
} }

View file

@ -10,5 +10,6 @@
public string ScriptLocation { get; set; } public string ScriptLocation { get; set; }
public string WindowsServiceName { get; set; } public string WindowsServiceName { get; set; }
public bool WindowsService { get; set; } public bool WindowsService { get; set; }
public bool TestMode { get; set; }
} }
} }

View file

@ -183,7 +183,7 @@ namespace Ombi.Store.Context
notificationToAdd = new NotificationTemplates notificationToAdd = new NotificationTemplates
{ {
NotificationType = notificationType, NotificationType = notificationType,
Message = "Hello! You {Title} on {ApplicationName}! This is now available! :)", Message = "Hello! Your request for {Title} on {ApplicationName}! This is now available! :)",
Subject = "{ApplicationName}: {Title} is now available!", Subject = "{ApplicationName}: {Title} is now available!",
Agent = agent, Agent = agent,
Enabled = true, Enabled = true,

View file

@ -6,7 +6,7 @@ namespace Ombi.Store.Entities
{ {
public enum RequestType public enum RequestType
{ {
TvShow, TvShow = 0,
Movie Movie = 1
} }
} }

View file

@ -8,10 +8,13 @@ namespace Ombi.Store.Entities.Requests
{ {
public string Title { get; set; } public string Title { get; set; }
public bool Approved { get; set; } public bool Approved { get; set; }
public DateTime MarkedAsApproved { get; set; }
public DateTime RequestedDate { get; set; } public DateTime RequestedDate { get; set; }
public bool Available { get; set; } public bool Available { get; set; }
public DateTime? MarkedAsAvailable { get; set; }
public string RequestedUserId { get; set; } public string RequestedUserId { get; set; }
public bool? Denied { get; set; } public bool? Denied { get; set; }
public DateTime MarkedAsDenied { get; set; }
public string DeniedReason { get; set; } public string DeniedReason { get; set; }
public RequestType RequestType { get; set; } public RequestType RequestType { get; set; }

View file

@ -0,0 +1,981 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Ombi.Helpers;
using Ombi.Store.Context;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using System;
namespace Ombi.Store.Migrations
{
[DbContext(typeof(OmbiContext))]
[Migration("20180703200952_EmbyUrlFix")]
partial class EmbyUrlFix
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Type");
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("ApplicationConfiguration");
});
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AuditArea");
b.Property<int>("AuditType");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("User");
b.HasKey("Id");
b.ToTable("Audit");
});
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("TheMovieDbId");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("EmbyId")
.IsRequired();
b.Property<string>("ImdbId");
b.Property<string>("ProviderId");
b.Property<string>("TheMovieDbId");
b.Property<string>("Title");
b.Property<string>("TvDbId");
b.Property<int>("Type");
b.Property<string>("Url");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("EmbyId");
b.Property<int>("EpisodeNumber");
b.Property<string>("ImdbId");
b.Property<string>("ParentId");
b.Property<string>("ProviderId");
b.Property<int>("SeasonNumber");
b.Property<string>("TheMovieDbId");
b.Property<string>("Title");
b.Property<string>("TvDbId");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Content");
b.Property<string>("SettingsName");
b.HasKey("Id");
b.ToTable("GlobalSettings");
});
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Agent");
b.Property<bool>("Enabled");
b.Property<string>("Message");
b.Property<int>("NotificationType");
b.Property<string>("Subject");
b.HasKey("Id");
b.ToTable("NotificationTemplates");
});
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("PlayerId");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("NotificationUserId");
});
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("Alias");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("EmbyConnectUserId");
b.Property<int?>("EpisodeRequestLimit");
b.Property<DateTime?>("LastLoggedIn");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<int?>("MovieRequestLimit");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("ProviderUserId");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserAccessToken");
b.Property<string>("UserName")
.HasMaxLength(256);
b.Property<int>("UserType");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeNumber");
b.Property<int>("GrandparentKey");
b.Property<int>("Key");
b.Property<int>("ParentKey");
b.Property<int>("SeasonNumber");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("ParentKey");
b.Property<int>("PlexContentId");
b.Property<int?>("PlexServerContentId");
b.Property<int>("SeasonKey");
b.Property<int>("SeasonNumber");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("ImdbId");
b.Property<int>("Key");
b.Property<string>("Quality");
b.Property<string>("ReleaseYear");
b.Property<string>("TheMovieDbId");
b.Property<string>("Title");
b.Property<string>("TvDbId");
b.Property<int>("Type");
b.Property<string>("Url");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("HasFile");
b.Property<int>("TheMovieDbId");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<int>("ContentId");
b.Property<int>("ContentType");
b.Property<int?>("EpisodeNumber");
b.Property<int?>("SeasonNumber");
b.Property<int>("Type");
b.HasKey("Id");
b.ToTable("RecentlyAddedLog");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Approved");
b.Property<bool>("Available");
b.Property<bool?>("Denied");
b.Property<string>("DeniedReason");
b.Property<int?>("IssueId");
b.Property<int>("ParentRequestId");
b.Property<int>("RequestType");
b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId");
b.Property<int>("SeriesType");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("ParentRequestId");
b.HasIndex("RequestedUserId");
b.ToTable("ChildRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("IssueCategory");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Comment");
b.Property<DateTime>("Date");
b.Property<int?>("IssuesId");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("IssuesId");
b.HasIndex("UserId");
b.ToTable("IssueComments");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Description");
b.Property<int>("IssueCategoryId");
b.Property<int?>("IssueId");
b.Property<string>("ProviderId");
b.Property<int?>("RequestId");
b.Property<int>("RequestType");
b.Property<DateTime?>("ResovledDate");
b.Property<int>("Status");
b.Property<string>("Subject");
b.Property<string>("Title");
b.Property<string>("UserReportedId");
b.HasKey("Id");
b.HasIndex("IssueCategoryId");
b.HasIndex("IssueId");
b.HasIndex("UserReportedId");
b.ToTable("Issues");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Approved");
b.Property<bool>("Available");
b.Property<string>("Background");
b.Property<bool?>("Denied");
b.Property<string>("DeniedReason");
b.Property<DateTime?>("DigitalReleaseDate");
b.Property<string>("ImdbId");
b.Property<int?>("IssueId");
b.Property<string>("Overview");
b.Property<string>("PosterPath");
b.Property<int>("QualityOverride");
b.Property<DateTime>("ReleaseDate");
b.Property<int>("RequestType");
b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId");
b.Property<int>("RootPathOverride");
b.Property<string>("Status");
b.Property<int>("TheMovieDbId");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("RequestedUserId");
b.ToTable("MovieRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeCount");
b.Property<DateTime>("RequestDate");
b.Property<int>("RequestId");
b.Property<int>("RequestType");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("RequestLog");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Background");
b.Property<string>("ImdbId");
b.Property<string>("Overview");
b.Property<string>("PosterPath");
b.Property<int?>("QualityOverride");
b.Property<DateTime>("ReleaseDate");
b.Property<int?>("RootFolder");
b.Property<string>("Status");
b.Property<string>("Title");
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("TvRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("RequestId");
b.Property<int>("RequestType");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("RequestSubscription");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeNumber");
b.Property<int>("SeasonNumber");
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeNumber");
b.Property<bool>("HasFile");
b.Property<int>("SeasonNumber");
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Token");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Tokens");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AirDate");
b.Property<bool>("Approved");
b.Property<bool>("Available");
b.Property<int>("EpisodeNumber");
b.Property<bool>("Requested");
b.Property<int>("SeasonId");
b.Property<string>("Title");
b.Property<string>("Url");
b.HasKey("Id");
b.HasIndex("SeasonId");
b.ToTable("EpisodeRequests");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("ChildRequestId");
b.Property<int>("SeasonNumber");
b.HasKey("Id");
b.HasIndex("ChildRequestId");
b.ToTable("SeasonRequests");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
});
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany("NotificationUserIds")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent")
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest")
.WithMany("ChildRequests")
.HasForeignKey("ParentRequestId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
.WithMany()
.HasForeignKey("RequestedUserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues")
.WithMany("Comments")
.HasForeignKey("IssuesId");
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory")
.WithMany()
.HasForeignKey("IssueCategoryId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests")
.WithMany("Issues")
.HasForeignKey("IssueId");
b.HasOne("Ombi.Store.Entities.Requests.MovieRequests")
.WithMany("Issues")
.HasForeignKey("IssueId");
b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported")
.WithMany()
.HasForeignKey("UserReportedId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
.WithMany()
.HasForeignKey("RequestedUserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
{
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
.WithMany("Episodes")
.HasForeignKey("SeasonId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest")
.WithMany("SeasonRequests")
.HasForeignKey("ChildRequestId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace Ombi.Store.Migrations
{
public partial class EmbyUrlFix : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
@"UPDATE EmbyContent SET Url = replace( Url, 'http://app.emby.media/itemdetails.html', 'http://app.emby.media/#!/itemdetails.html' ) WHERE Url LIKE 'http://app.emby.media/itemdetails.html%';");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View file

@ -0,0 +1,988 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context;
namespace Ombi.Store.Migrations
{
[DbContext(typeof(OmbiContext))]
[Migration("20180730085903_UserStats")]
partial class UserStats
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Type");
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("ApplicationConfiguration");
});
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AuditArea");
b.Property<int>("AuditType");
b.Property<DateTime>("DateTime");
b.Property<string>("Description");
b.Property<string>("User");
b.HasKey("Id");
b.ToTable("Audit");
});
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("TheMovieDbId");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("EmbyId")
.IsRequired();
b.Property<string>("ImdbId");
b.Property<string>("ProviderId");
b.Property<string>("TheMovieDbId");
b.Property<string>("Title");
b.Property<string>("TvDbId");
b.Property<int>("Type");
b.Property<string>("Url");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("EmbyId");
b.Property<int>("EpisodeNumber");
b.Property<string>("ImdbId");
b.Property<string>("ParentId");
b.Property<string>("ProviderId");
b.Property<int>("SeasonNumber");
b.Property<string>("TheMovieDbId");
b.Property<string>("Title");
b.Property<string>("TvDbId");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Content");
b.Property<string>("SettingsName");
b.HasKey("Id");
b.ToTable("GlobalSettings");
});
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Agent");
b.Property<bool>("Enabled");
b.Property<string>("Message");
b.Property<int>("NotificationType");
b.Property<string>("Subject");
b.HasKey("Id");
b.ToTable("NotificationTemplates");
});
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("PlayerId");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("NotificationUserId");
});
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("Alias");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("EmbyConnectUserId");
b.Property<int?>("EpisodeRequestLimit");
b.Property<DateTime?>("LastLoggedIn");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<int?>("MovieRequestLimit");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("ProviderUserId");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserAccessToken");
b.Property<string>("UserName")
.HasMaxLength(256);
b.Property<int>("UserType");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeNumber");
b.Property<int>("GrandparentKey");
b.Property<int>("Key");
b.Property<int>("ParentKey");
b.Property<int>("SeasonNumber");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("ParentKey");
b.Property<int>("PlexContentId");
b.Property<int?>("PlexServerContentId");
b.Property<int>("SeasonKey");
b.Property<int>("SeasonNumber");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<string>("ImdbId");
b.Property<int>("Key");
b.Property<string>("Quality");
b.Property<string>("ReleaseYear");
b.Property<string>("TheMovieDbId");
b.Property<string>("Title");
b.Property<string>("TvDbId");
b.Property<int>("Type");
b.Property<string>("Url");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("HasFile");
b.Property<int>("TheMovieDbId");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AddedAt");
b.Property<int>("ContentId");
b.Property<int>("ContentType");
b.Property<int?>("EpisodeNumber");
b.Property<int?>("SeasonNumber");
b.Property<int>("Type");
b.HasKey("Id");
b.ToTable("RecentlyAddedLog");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Approved");
b.Property<bool>("Available");
b.Property<bool?>("Denied");
b.Property<string>("DeniedReason");
b.Property<int?>("IssueId");
b.Property<DateTime>("MarkedAsApproved");
b.Property<DateTime?>("MarkedAsAvailable");
b.Property<DateTime>("MarkedAsDenied");
b.Property<int>("ParentRequestId");
b.Property<int>("RequestType");
b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId");
b.Property<int>("SeriesType");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("ParentRequestId");
b.HasIndex("RequestedUserId");
b.ToTable("ChildRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("IssueCategory");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Comment");
b.Property<DateTime>("Date");
b.Property<int?>("IssuesId");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("IssuesId");
b.HasIndex("UserId");
b.ToTable("IssueComments");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Description");
b.Property<int>("IssueCategoryId");
b.Property<int?>("IssueId");
b.Property<string>("ProviderId");
b.Property<int?>("RequestId");
b.Property<int>("RequestType");
b.Property<DateTime?>("ResovledDate");
b.Property<int>("Status");
b.Property<string>("Subject");
b.Property<string>("Title");
b.Property<string>("UserReportedId");
b.HasKey("Id");
b.HasIndex("IssueCategoryId");
b.HasIndex("IssueId");
b.HasIndex("UserReportedId");
b.ToTable("Issues");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Approved");
b.Property<bool>("Available");
b.Property<string>("Background");
b.Property<bool?>("Denied");
b.Property<string>("DeniedReason");
b.Property<DateTime?>("DigitalReleaseDate");
b.Property<string>("ImdbId");
b.Property<int?>("IssueId");
b.Property<DateTime>("MarkedAsApproved");
b.Property<DateTime?>("MarkedAsAvailable");
b.Property<DateTime>("MarkedAsDenied");
b.Property<string>("Overview");
b.Property<string>("PosterPath");
b.Property<int>("QualityOverride");
b.Property<DateTime>("ReleaseDate");
b.Property<int>("RequestType");
b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId");
b.Property<int>("RootPathOverride");
b.Property<string>("Status");
b.Property<int>("TheMovieDbId");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("RequestedUserId");
b.ToTable("MovieRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeCount");
b.Property<DateTime>("RequestDate");
b.Property<int>("RequestId");
b.Property<int>("RequestType");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("RequestLog");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Background");
b.Property<string>("ImdbId");
b.Property<string>("Overview");
b.Property<string>("PosterPath");
b.Property<int?>("QualityOverride");
b.Property<DateTime>("ReleaseDate");
b.Property<int?>("RootFolder");
b.Property<string>("Status");
b.Property<string>("Title");
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("TvRequests");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("RequestId");
b.Property<int>("RequestType");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("RequestSubscription");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeNumber");
b.Property<int>("SeasonNumber");
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("EpisodeNumber");
b.Property<bool>("HasFile");
b.Property<int>("SeasonNumber");
b.Property<int>("TvDbId");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Token");
b.Property<string>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Tokens");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("AirDate");
b.Property<bool>("Approved");
b.Property<bool>("Available");
b.Property<int>("EpisodeNumber");
b.Property<bool>("Requested");
b.Property<int>("SeasonId");
b.Property<string>("Title");
b.Property<string>("Url");
b.HasKey("Id");
b.HasIndex("SeasonId");
b.ToTable("EpisodeRequests");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("ChildRequestId");
b.Property<int>("SeasonNumber");
b.HasKey("Id");
b.HasIndex("ChildRequestId");
b.ToTable("SeasonRequests");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
});
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany("NotificationUserIds")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent")
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest")
.WithMany("ChildRequests")
.HasForeignKey("ParentRequestId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
.WithMany()
.HasForeignKey("RequestedUserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues")
.WithMany("Comments")
.HasForeignKey("IssuesId");
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory")
.WithMany()
.HasForeignKey("IssueCategoryId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests")
.WithMany("Issues")
.HasForeignKey("IssueId");
b.HasOne("Ombi.Store.Entities.Requests.MovieRequests")
.WithMany("Issues")
.HasForeignKey("IssueId");
b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported")
.WithMany()
.HasForeignKey("UserReportedId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
.WithMany()
.HasForeignKey("RequestedUserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany()
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
{
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
.WithMany("Episodes")
.HasForeignKey("SeasonId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
{
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest")
.WithMany("SeasonRequests")
.HasForeignKey("ChildRequestId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,72 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations
{
public partial class UserStats : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsApproved",
table: "MovieRequests",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsAvailable",
table: "MovieRequests",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsDenied",
table: "MovieRequests",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsApproved",
table: "ChildRequests",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsAvailable",
table: "ChildRequests",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsDenied",
table: "ChildRequests",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "MarkedAsApproved",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsAvailable",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsDenied",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsApproved",
table: "ChildRequests");
migrationBuilder.DropColumn(
name: "MarkedAsAvailable",
table: "ChildRequests");
migrationBuilder.DropColumn(
name: "MarkedAsDenied",
table: "ChildRequests");
}
}
}

View file

@ -1,15 +1,9 @@
// <auto-generated /> // <auto-generated />
using System;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Ombi.Helpers;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using System;
namespace Ombi.Store.Migrations namespace Ombi.Store.Migrations
{ {
@ -20,7 +14,7 @@ namespace Ombi.Store.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); .HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
@ -481,6 +475,12 @@ namespace Ombi.Store.Migrations
b.Property<int?>("IssueId"); b.Property<int?>("IssueId");
b.Property<DateTime>("MarkedAsApproved");
b.Property<DateTime?>("MarkedAsAvailable");
b.Property<DateTime>("MarkedAsDenied");
b.Property<int>("ParentRequestId"); b.Property<int>("ParentRequestId");
b.Property<int>("RequestType"); b.Property<int>("RequestType");
@ -595,6 +595,12 @@ namespace Ombi.Store.Migrations
b.Property<int?>("IssueId"); b.Property<int?>("IssueId");
b.Property<DateTime>("MarkedAsApproved");
b.Property<DateTime?>("MarkedAsAvailable");
b.Property<DateTime>("MarkedAsDenied");
b.Property<string>("Overview"); b.Property<string>("Overview");
b.Property<string>("PosterPath"); b.Property<string>("PosterPath");

View file

@ -10,10 +10,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />
</ItemGroup> </ItemGroup>

View file

@ -7,12 +7,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.3" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.7.99" />
<PackageReference Include="Nunit" Version="3.8.1" /> <PackageReference Include="Nunit" Version="3.8.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.7.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -11,7 +11,7 @@ namespace Ombi.Updater
ProcessInfo GetCurrentProcess(); ProcessInfo GetCurrentProcess();
int GetCurrentProcessId(); int GetCurrentProcessId();
ProcessInfo GetProcessById(int id); ProcessInfo GetProcessById(int id);
void Kill(StartupOptions opts); bool Kill(StartupOptions opts);
void KillAll(string processName); void KillAll(string processName);
void SetPriority(int processId, ProcessPriorityClass priority); void SetPriority(int processId, ProcessPriorityClass priority);
void WaitForExit(Process process); void WaitForExit(Process process);

View file

@ -22,29 +22,23 @@ namespace Ombi.Updater
{ {
// Kill Ombi Process // Kill Ombi Process
var p = new ProcessProvider(); var p = new ProcessProvider();
bool killed = false;
try try
{ {
p.Kill(opt); killed = p.Kill(opt);
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e); Console.WriteLine(e);
} }
// Make sure the process has been killed if (!killed)
while (p.FindProcessByName(opt.ProcessName).Any())
{ {
Thread.Sleep(500);
_log.LogDebug("Found another process called {0}, KILLING!", opt.ProcessName); _log.LogDebug("Couldn't kill the ombi process");
var proc = p.FindProcessByName(opt.ProcessName).FirstOrDefault(); return;
if (proc != null)
{
_log.LogDebug($"[{proc.Id}] - {proc.Name} - Path: {proc.StartPath}");
opt.OmbiProcessId = proc.Id;
p.Kill(opt);
}
} }
_log.LogDebug("Starting to move the files"); _log.LogDebug("Starting to move the files");
@ -111,21 +105,23 @@ namespace Ombi.Updater
var location = System.Reflection.Assembly.GetEntryAssembly().Location; var location = System.Reflection.Assembly.GetEntryAssembly().Location;
location = Path.GetDirectoryName(location); location = Path.GetDirectoryName(location);
_log.LogDebug("We are currently in dir {0}", location); _log.LogDebug("We are currently in dir {0}", location);
var updatedLocation = Directory.GetParent(location).FullName;
_log.LogDebug("The files are in {0}", updatedLocation); // Since the updater is a folder deeper
_log.LogDebug("Ombi is installed at {0}", options.ApplicationPath); _log.LogDebug("Ombi is installed at {0}", options.ApplicationPath);
//Now Create all of the directories //Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(location, "*", foreach (string dirPath in Directory.GetDirectories(updatedLocation, "*",
SearchOption.AllDirectories)) SearchOption.AllDirectories))
{ {
var newDir = dirPath.Replace(location, options.ApplicationPath); var newDir = dirPath.Replace(updatedLocation, options.ApplicationPath);
Directory.CreateDirectory(newDir); Directory.CreateDirectory(newDir);
_log.LogDebug("Created dir {0}", newDir); _log.LogDebug("Created dir {0}", newDir);
} }
//Copy all the files & Replaces any files with the same name //Copy all the files & Replaces any files with the same name
foreach (string currentPath in Directory.GetFiles(location, "*.*", foreach (string currentPath in Directory.GetFiles(updatedLocation, "*.*",
SearchOption.AllDirectories)) SearchOption.AllDirectories))
{ {
var newFile = currentPath.Replace(location, options.ApplicationPath); var newFile = currentPath.Replace(updatedLocation, options.ApplicationPath);
File.Copy(currentPath, newFile, true); File.Copy(currentPath, newFile, true);
_log.LogDebug("Replaced file {0}", newFile); _log.LogDebug("Replaced file {0}", newFile);
} }

View file

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -12,14 +12,14 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" /> <PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" /> <PackageReference Include="Serilog" Version="2.6.0-dev-00892" />
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" /> <PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
<PackageReference Include="Serilog.Sinks.File" Version="3.2.0" /> <PackageReference Include="Serilog.Sinks.File" Version="3.2.0" />

View file

@ -73,33 +73,32 @@ namespace Ombi.Updater
process.PriorityClass = priority; process.PriorityClass = priority;
} }
public void Kill(StartupOptions opts) public bool Kill(StartupOptions opts)
{ {
if (opts.IsWindowsService) //if (opts.IsWindowsService)
{ //{
Console.WriteLine("Stopping Service {0}", opts.WindowsServiceName); // Console.WriteLine("Stopping Service {0}", opts.WindowsServiceName);
var process = new Process(); // var process = new Process();
var startInfo = // var startInfo =
new ProcessStartInfo // new ProcessStartInfo
{ // {
WindowStyle = ProcessWindowStyle.Hidden, // WindowStyle = ProcessWindowStyle.Hidden,
FileName = "cmd.exe", // FileName = "cmd.exe",
Arguments = $"/C net stop \"{opts.WindowsServiceName}\"" // Arguments = $"/C net stop \"{opts.WindowsServiceName}\""
}; // };
process.StartInfo = startInfo; // process.StartInfo = startInfo;
process.Start(); // process.Start();
} //}
else //else
{ //{
var process = Process.GetProcesses().FirstOrDefault(p => p.ProcessName == opts.ProcessName); var process = Process.GetProcesses().FirstOrDefault(p => p.ProcessName == opts.ProcessName);
if (process == null) if (process == null)
{ {
Console.WriteLine("Cannot find process with name: {0}", opts.ProcessName); Console.WriteLine("Cannot find process with name: {0}", opts.ProcessName);
return; return false;
} }
process.Refresh();
if (process.Id > 0) if (process.Id > 0)
{ {
@ -108,8 +107,12 @@ namespace Ombi.Updater
Console.WriteLine("[{0}]: Waiting for exit", process.Id); Console.WriteLine("[{0}]: Waiting for exit", process.Id);
process.WaitForExit(); process.WaitForExit();
Console.WriteLine("[{0}]: Process terminated successfully", process.Id); Console.WriteLine("[{0}]: Process terminated successfully", process.Id);
return true;
} }
}
return false;
//}
} }
public void KillAll(string processName) public void KillAll(string processName)

View file

@ -2,7 +2,7 @@
"profiles": { "profiles": {
"Ombi.Updater": { "Ombi.Updater": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "--applicationPath \\\"C:\\\\Users\\\\Jamie\\\\Source\\\\Repos\\\\Ombi\\\\src\\\\Ombi\\\\bin\\\\Debug\\\\netcoreapp2.0\\\" --processname \\\"Ombi\\\" --startupArgs http://*:5000" "commandLineArgs": "--applicationPath \"C:\\_git\\ombi\\src\\Ombi.Updater\\bin\\Debug\\netcoreapp2.0\" --processname \"Ombi\""
} }
} }
} }

23
src/Ombi/.gitignore vendored
View file

@ -1,23 +1,10 @@
/wwwroot/css/** node_modules
/wwwroot/fonts/** bin
/wwwroot/lib/** obj
/wwwroot/maps/** wwwroot/dist
/wwwroot/dist/** *.log
/wwwroot/*.js.map
/wwwroot/*.js
# dependencies
/node_modules
/bower_components
# misc
/.sass-cache /.sass-cache
/connect.lock /connect.lock
/coverage/* /coverage/*
/libpeerconnection.log
npm-debug.log
testem.log
#/typings
/systemjs.config.js*
/Logs/** /Logs/**
**.db **.db

View file

@ -4,7 +4,7 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"taskName": "restore", "label": "restore",
"command": "npm", "command": "npm",
"type": "shell", "type": "shell",
"args": [ "args": [
@ -14,7 +14,16 @@
"problemMatcher": [] "problemMatcher": []
}, },
{ {
"taskName": "build", "label": "clean",
"command": "dotnet",
"type": "shell",
"args": [
"clean"
],
"problemMatcher": "$msCompile"
},
{
"label": "build",
"command": "dotnet", "command": "dotnet",
"type": "shell", "type": "shell",
"args": [ "args": [
@ -27,7 +36,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"taskName": "lint", "label": "lint",
"type": "shell", "type": "shell",
"command": "npm", "command": "npm",
"args": [ "args": [

View file

@ -94,9 +94,31 @@ namespace Ombi
} }
else else
{ {
var identity = new GenericIdentity("API"); // Check if we have a UserName header if so we can impersonate that user
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); if (context.Request.Headers.Keys.Contains("UserName", StringComparer.InvariantCultureIgnoreCase))
context.User = principal; {
var username = context.Request.Headers["UserName"].FirstOrDefault();
var um = context.RequestServices.GetService<OmbiUserManager>();
var user = await um.Users.FirstOrDefaultAsync(x =>
x.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase));
if (user == null)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid User");
await next.Invoke(context);
}
var roles = await um.GetRolesAsync(user);
var identity = new GenericIdentity(user.UserName);
var principal = new GenericPrincipal(identity, roles.ToArray());
context.User = principal;
}
else
{
var identity = new GenericIdentity("API");
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
context.User = principal;
}
await next.Invoke(context); await next.Invoke(context);
} }
} }

View file

@ -1,7 +1,7 @@
import { animate, style, transition, trigger } from "@angular/animations"; import { animate, style, transition, trigger } from "@angular/animations";
import { AnimationEntryMetadata } from "@angular/core"; import { AnimationTriggerMetadata } from "@angular/animations";
export const fadeInOutAnimation: AnimationEntryMetadata = trigger("fadeInOut", [ export const fadeInOutAnimation: AnimationTriggerMetadata = trigger("fadeInOut", [
transition(":enter", [ // :enter is alias to 'void => *' transition(":enter", [ // :enter is alias to 'void => *'
style({ opacity: 0 }), style({ opacity: 0 }),
animate(1000, style({ opacity: 1 })), animate(1000, style({ opacity: 1 })),

View file

@ -134,6 +134,12 @@
<li [ngClass]="{'active': 'no' === translate.currentLang}"> <li [ngClass]="{'active': 'no' === translate.currentLang}">
<a (click)="translate.use('no')" [translate]="'NavigationBar.Language.Norwegian'"></a> <a (click)="translate.use('no')" [translate]="'NavigationBar.Language.Norwegian'"></a>
</li> </li>
<li [ngClass]="{'active': 'pt' === translate.currentLang}">
<a (click)="translate.use('pt')" [translate]="'NavigationBar.Language.BrazillianPortuguese'"></a>
</li>
<li [ngClass]="{'active': 'pl' === translate.currentLang}">
<a (click)="translate.use('pl')" [translate]="'NavigationBar.Language.Polish'"></a>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>

View file

@ -33,20 +33,20 @@ export class AppComponent implements OnInit {
private readonly jobService: JobService, private readonly jobService: JobService,
public readonly translate: TranslateService, public readonly translate: TranslateService,
private readonly identityService: IdentityService, private readonly identityService: IdentityService,
private readonly platformLocation: PlatformLocation) { private readonly platformLocation: PlatformLocation) {
const base = this.platformLocation.getBaseHrefFromDOM(); const base = this.platformLocation.getBaseHrefFromDOM();
if (base.length > 1) { if (base.length > 1) {
__webpack_public_path__ = base + "/dist/"; __webpack_public_path__ = base + "/dist/";
} }
this.translate.addLangs(["en", "de", "fr","da","es","it","nl","sv","no"]); this.translate.addLangs(["en", "de", "fr", "da", "es", "it", "nl", "sv", "no", "pl", "pt"]);
// this language will be used as a fallback when a translation isn't found in the current language // this language will be used as a fallback when a translation isn't found in the current language
this.translate.setDefaultLang("en"); this.translate.setDefaultLang("en");
// See if we can match the supported langs with the current browser lang // See if we can match the supported langs with the current browser lang
const browserLang: string = translate.getBrowserLang(); const browserLang: string = translate.getBrowserLang();
this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no/) ? browserLang : "en"); this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no|pl|pt/) ? browserLang : "en");
} }
public ngOnInit() { public ngOnInit() {
@ -88,8 +88,8 @@ export class AppComponent implements OnInit {
public openMobileApp(event: any) { public openMobileApp(event: any) {
event.preventDefault(); event.preventDefault();
if(!this.customizationSettings.applicationUrl) { if (!this.customizationSettings.applicationUrl) {
this.notificationService.warning("Mobile","Please ask your admin to setup the Application URL!"); this.notificationService.warning("Mobile", "Please ask your admin to setup the Application URL!");
return; return;
} }

View file

@ -1,12 +1,12 @@
import {CommonModule, PlatformLocation} from "@angular/common"; import { CommonModule, PlatformLocation } from "@angular/common";
import {HttpClient, HttpClientModule} from "@angular/common/http"; import { HttpClient, HttpClientModule } from "@angular/common/http";
import {NgModule} from "@angular/core"; import { NgModule } from "@angular/core";
import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import {HttpModule} from "@angular/http"; import { HttpModule } from "@angular/http";
import {MatButtonModule, MatCardModule, MatInputModule, MatTabsModule} from "@angular/material"; import { MatButtonModule, MatCardModule, MatInputModule, MatTabsModule } from "@angular/material";
import {BrowserModule} from "@angular/platform-browser"; import { BrowserModule } from "@angular/platform-browser";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import {RouterModule, Routes} from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { JwtModule } from "@auth0/angular-jwt"; import { JwtModule } from "@auth0/angular-jwt";
@ -15,7 +15,7 @@ import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { CookieService } from "ng2-cookies"; import { CookieService } from "ng2-cookies";
import { GrowlModule } from "primeng/components/growl/growl"; import { GrowlModule } from "primeng/components/growl/growl";
import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule,DialogModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng"; import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng";
// Components // Components
import { AppComponent } from "./app.component"; import { AppComponent } from "./app.component";
@ -36,7 +36,7 @@ import { ImageService } from "./services";
import { LandingPageService } from "./services"; import { LandingPageService } from "./services";
import { NotificationService } from "./services"; import { NotificationService } from "./services";
import { SettingsService } from "./services"; import { SettingsService } from "./services";
import { IssuesService, JobService, StatusService } from "./services"; import { IssuesService, JobService, PlexTvService, StatusService } from "./services";
const routes: Routes = [ const routes: Routes = [
{ path: "*", component: PageNotFoundComponent }, { path: "*", component: PageNotFoundComponent },
@ -67,6 +67,14 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
return new TranslateHttpLoader(http, "/translations/", `.json?v=${version}`); return new TranslateHttpLoader(http, "/translations/", `.json?v=${version}`);
} }
export function JwtTokenGetter() {
const token = localStorage.getItem("id_token");
if (!token) {
return "";
}
return token;
}
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forRoot(routes), RouterModule.forRoot(routes),
@ -89,18 +97,12 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
CaptchaModule, CaptchaModule,
TooltipModule, TooltipModule,
ConfirmDialogModule, ConfirmDialogModule,
CommonModule, CommonModule,
JwtModule.forRoot({ JwtModule.forRoot({
config: { config: {
tokenGetter: () => { tokenGetter: JwtTokenGetter,
const token = localStorage.getItem("id_token");
if (!token) {
return "";
}
return token;
},
}, },
}), }),
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
@ -119,7 +121,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
TokenResetPasswordComponent, TokenResetPasswordComponent,
CookieComponent, CookieComponent,
LoginOAuthComponent, LoginOAuthComponent,
], ],
providers: [ providers: [
NotificationService, NotificationService,
AuthService, AuthService,
@ -133,6 +135,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
CookieService, CookieService,
JobService, JobService,
IssuesService, IssuesService,
PlexTvService,
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })

View file

@ -1,8 +1,11 @@
export interface IUserLogin { import { IPlexPin } from "../interfaces";
export interface IUserLogin {
username: string; username: string;
password: string; password: string;
rememberMe: boolean; rememberMe: boolean;
usePlexOAuth: boolean; usePlexOAuth: boolean;
plexTvPin: IPlexPin;
} }
export interface ILocalUser { export interface ILocalUser {

View file

@ -1,8 +1,8 @@
import { PlatformLocation } from "@angular/common"; import { PlatformLocation } from "@angular/common";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { JwtHelperService } from "@auth0/angular-jwt"; import { JwtHelperService } from "@auth0/angular-jwt";
import { Observable } from "rxjs/Rx"; import { Observable } from "rxjs";
import { ServiceHelpers } from "../services"; import { ServiceHelpers } from "../services";
import { ILocalUser, IUserLogin } from "./IUserLogin"; import { ILocalUser, IUserLogin } from "./IUserLogin";
@ -26,13 +26,13 @@ export class AuthService extends ServiceHelpers {
return this.http.post<boolean>(`${this.url}/requirePassword`, JSON.stringify(login), {headers: this.headers}); return this.http.post<boolean>(`${this.url}/requirePassword`, JSON.stringify(login), {headers: this.headers});
} }
public loggedIn() { public loggedIn() {
const token: string = this.jwtHelperService.tokenGetter(); const token: string = this.jwtHelperService.tokenGetter();
if (!token) { if (!token) {
return false; return false;
} }
const tokenExpired: boolean = this.jwtHelperService.isTokenExpired(token); const tokenExpired: boolean = this.jwtHelperService.isTokenExpired(token);
return !tokenExpired; return !tokenExpired;
} }
@ -53,9 +53,9 @@ export class AuthService extends ServiceHelpers {
} else { } else {
u.roles.push(roles); u.roles.push(roles);
} }
return <ILocalUser>u; return <ILocalUser> u;
} }
return <ILocalUser>{}; return <ILocalUser> { };
} }
public hasRole(role: string): boolean { public hasRole(role: string): boolean {

View file

@ -11,7 +11,7 @@ export class CookieComponent implements OnInit {
public ngOnInit() { public ngOnInit() {
const cookie = this.cookieService.getAll(); const cookie = this.cookieService.getAll();
if(cookie.Auth) { if (cookie.Auth) {
const jwtVal = cookie.Auth; const jwtVal = cookie.Auth;
localStorage.setItem("id_token", jwtVal); localStorage.setItem("id_token", jwtVal);
this.router.navigate(["search"]); this.router.navigate(["search"]);

View file

@ -46,6 +46,7 @@ export interface IIssueComments {
} }
export interface IIssuesChat { export interface IIssuesChat {
id: number;
comment: string; comment: string;
date: Date; date: Date;
username: string; username: string;

View file

@ -2,6 +2,16 @@
user: IPlexUser; user: IPlexUser;
} }
export interface IPlexPin {
id: number;
code: string;
}
export interface IPlexOAuthViewModel {
wizard: boolean;
pin: IPlexPin;
}
export interface IPlexOAuthAccessToken { export interface IPlexOAuthAccessToken {
accessToken: string; accessToken: string;
} }
@ -24,6 +34,19 @@ export interface IPlexLibResponse {
data: IPlexLibraries; data: IPlexLibraries;
} }
export interface IPlexLibSimpleResponse {
successful: boolean;
message: string;
data: IPlexSection[];
}
export interface IPlexSection {
id: string;
key: string;
type: string;
title: string;
}
export interface IMediaContainer { export interface IMediaContainer {
directory: IDirectory[]; directory: IDirectory[];
} }
@ -39,6 +62,28 @@ export interface IPlexServerViewModel {
servers: IPlexServerResult; servers: IPlexServerResult;
} }
export interface IPlexServerAddViewModel {
success: boolean;
servers: IPlexServersAdd[];
}
export interface IPlexServersAdd {
serverId: number;
machineId: string;
serverName: string;
}
export interface IPlexUserViewModel {
username: string;
machineIdentifier: string;
libsSelected: number[];
}
export interface IPlexUserAddResponse {
success: boolean;
error: string;
}
export interface IPlexServerResult { export interface IPlexServerResult {
friendlyName: string; friendlyName: string;
machineIdentifier: string; machineIdentifier: string;

View file

@ -20,7 +20,7 @@ export interface IRecentlyAddedTvShows extends IRecentlyAddedMovies {
export interface IRecentlyAddedRangeModel { export interface IRecentlyAddedRangeModel {
from: Date; from: Date;
to: Date; to: Date;
} }
export enum RecentlyAddedType { export enum RecentlyAddedType {

View file

@ -71,6 +71,10 @@ export interface ITvRequests {
status: string; status: string;
childRequests: IChildRequests[]; childRequests: IChildRequests[];
qualityOverride: number; qualityOverride: number;
background: any;
totalSeasons: number;
tvDbId: number;
open: boolean; // THIS IS FOR THE UI
// For UI display // For UI display
qualityOverrideTitle: string; qualityOverrideTitle: string;

View file

@ -28,8 +28,16 @@ export interface ISearchTvResult {
available: boolean; available: boolean;
plexUrl: string; plexUrl: string;
embyUrl: string; embyUrl: string;
quality: string;
firstSeason: boolean; firstSeason: boolean;
latestSeason: boolean; latestSeason: boolean;
theTvDbId: string;
subscribed: boolean;
showSubscribe: boolean;
fullyAvailable: boolean;
partlyAvailable: boolean;
background: any;
open: boolean; // THIS IS FOR THE UI
} }
export interface ITvRequestViewModel { export interface ITvRequestViewModel {

View file

@ -27,6 +27,7 @@ export interface IUpdateSettings extends ISettings {
windowsService: boolean; windowsService: boolean;
windowsServiceName: string; windowsServiceName: string;
isWindows: boolean; isWindows: boolean;
testMode: boolean;
} }
export interface IEmbySettings extends ISettings { export interface IEmbySettings extends ISettings {

View file

@ -23,6 +23,11 @@ export interface ICreateWizardUser {
usePlexAdminAccount: boolean; usePlexAdminAccount: boolean;
} }
export interface IWizardUserResult {
result: boolean;
errors: string[];
}
export enum UserType { export enum UserType {
LocalUser = 1, LocalUser = 1,
PlexUser = 2, PlexUser = 2,

View file

@ -51,15 +51,17 @@
<div *ngIf="comments" class="panel-body msg_container_base"> <div *ngIf="comments" class="panel-body msg_container_base">
<div *ngIf="comments.length <= 0" class="row msg_container base_receive"> <div *ngIf="comments.length <= 0" class="row msg_container base_receive">
<div class="col-md-10 col-xs-10"> <div class="col-md-10 col-xs-10">
<div class="messages msg_sent">
<p [translate]="'Issues.NoComments'"></p> <div class="messages msg_sent">
<p [translate]="'Issues.NoComments'"></p>
</div> </div>
</div> </div>
</div> </div>
<div *ngFor="let comment of comments" class="row msg_container" [ngClass]="{'base_sent': comment.adminComment, 'base_receive': !comment.adminComment}"> <div *ngFor="let comment of comments" class="row msg_container" [ngClass]="{'base_sent': comment.adminComment, 'base_receive': !comment.adminComment}">
<div class="col-md-10 col-xs-10"> <div class="col-md-10 col-xs-10">
<div class="messages msg_sent">
<div class="messages msg_sent"> <i *ngIf="isAdmin" style="float:right;" class="fa fa-times" aria-hidden="true" (click)="deleteComment(comment.id)"></i>
<p>{{comment.comment}}</p> <p>{{comment.comment}}</p>
<time>{{comment.username}} • {{comment.date | date:'short'}}</time> <time>{{comment.username}} • {{comment.date | date:'short'}}</time>
</div> </div>

View file

@ -37,10 +37,10 @@ export class IssueDetailsComponent implements OnInit {
private notificationService: NotificationService, private notificationService: NotificationService,
private imageService: ImageService, private imageService: ImageService,
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
private readonly platformLocation: PlatformLocation) { private readonly platformLocation: PlatformLocation) {
this.route.params this.route.params
.subscribe((params: any) => { .subscribe((params: any) => {
this.issueId = parseInt(params.id); this.issueId = parseInt(params.id);
}); });
this.isAdmin = this.authService.hasRole("Admin") || this.authService.hasRole("PowerUser"); this.isAdmin = this.authService.hasRole("Admin") || this.authService.hasRole("PowerUser");
@ -53,8 +53,8 @@ export class IssueDetailsComponent implements OnInit {
this.defaultPoster = "../../../images/"; this.defaultPoster = "../../../images/";
} }
} }
public ngOnInit() { public ngOnInit() {
this.issueService.getIssue(this.issueId).subscribe(x => { this.issueService.getIssue(this.issueId).subscribe(x => {
this.issue = { this.issue = {
comments: x.comments, comments: x.comments,
@ -63,8 +63,8 @@ export class IssueDetailsComponent implements OnInit {
issueCategoryId: x.issueCategoryId, issueCategoryId: x.issueCategoryId,
subject: x.subject, subject: x.subject,
description: x.description, description: x.description,
status:x.status, status: x.status,
resolvedDate:x.resolvedDate, resolvedDate: x.resolvedDate,
title: x.title, title: x.title,
requestType: x.requestType, requestType: x.requestType,
requestId: x.requestId, requestId: x.requestId,
@ -97,6 +97,13 @@ export class IssueDetailsComponent implements OnInit {
}); });
} }
public deleteComment(id: number) {
this.issueService.deleteComment(id).subscribe(x => {
this.loadComments();
this.notificationService.success("Comment Deleted");
});
}
private loadComments() { private loadComments() {
this.issueService.getComments(this.issueId).subscribe(x => this.comments = x); this.issueService.getComments(this.issueId).subscribe(x => this.comments = x);
} }
@ -117,7 +124,7 @@ export class IssueDetailsComponent implements OnInit {
} else { } else {
this.imageService.getTvBackground(Number(issue.providerId)).subscribe(x => { this.imageService.getTvBackground(Number(issue.providerId)).subscribe(x => {
if(x) { if (x) {
this.backgroundPath = this.sanitizer.bypassSecurityTrustStyle this.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")"); ("url(" + x + ")");
} }

View file

@ -22,8 +22,8 @@ export class IssuesComponent implements OnInit {
constructor(private issueService: IssuesService) { } constructor(private issueService: IssuesService) { }
public ngOnInit() { public ngOnInit() {
this.getPending(); this.getPending();
this.getInProg(); this.getInProg();
this.getResolved(); this.getResolved();
this.issueService.getIssuesCount().subscribe(x => this.count = x); this.issueService.getIssuesCount().subscribe(x => this.count = x);
@ -61,5 +61,4 @@ export class IssuesComponent implements OnInit {
this.resolvedIssues = x; this.resolvedIssues = x;
}); });
} }
} }

View file

@ -11,7 +11,7 @@ export class IssuesTableComponent {
@Input() public issues: IIssues[]; @Input() public issues: IIssues[];
@Input() public totalRecords: number; @Input() public totalRecords: number;
@Output() public changePage = new EventEmitter<IPagenator>(); @Output() public changePage = new EventEmitter<IPagenator>();
public IssueStatus = IssueStatus; public IssueStatus = IssueStatus;
@ -47,7 +47,7 @@ export class IssuesTableComponent {
//event.rows = Number of rows to display in new page //event.rows = Number of rows to display in new page
//event.page = Index of the new page //event.page = Index of the new page
//event.pageCount = Total number of pages //event.pageCount = Total number of pages
this.changePage.emit(event); this.changePage.emit(event);
} }

View file

@ -6,7 +6,7 @@ import { TranslateService } from "@ngx-translate/core";
import { PlatformLocation } from "@angular/common"; import { PlatformLocation } from "@angular/common";
import { AuthService } from "../auth/auth.service"; import { AuthService } from "../auth/auth.service";
import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces"; import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces";
import { NotificationService } from "../services"; import { NotificationService, PlexTvService } from "../services";
import { SettingsService } from "../services"; import { SettingsService } from "../services";
import { StatusService } from "../services"; import { StatusService } from "../services";
@ -30,9 +30,10 @@ export class LoginComponent implements OnDestroy, OnInit {
public landingFlag: boolean; public landingFlag: boolean;
public baseUrl: string; public baseUrl: string;
public loginWithOmbi: boolean; public loginWithOmbi: boolean;
public pinTimer: any;
public get appName(): string { public get appName(): string {
if(this.customizationSettings.applicationName) { if (this.customizationSettings.applicationName) {
return this.customizationSettings.applicationName; return this.customizationSettings.applicationName;
} else { } else {
return "Ombi"; return "Ombi";
@ -40,13 +41,14 @@ export class LoginComponent implements OnDestroy, OnInit {
} }
private timer: any; private timer: any;
private clientId: string;
private errorBody: string; private errorBody: string;
private errorValidation: string; private errorValidation: string;
constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService, constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService,
private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer, private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer,
private route: ActivatedRoute, private location: PlatformLocation, private readonly translate: TranslateService) { private route: ActivatedRoute, private location: PlatformLocation, private translate: TranslateService, private plexTv: PlexTvService) {
this.route.params this.route.params
.subscribe((params: any) => { .subscribe((params: any) => {
this.landingFlag = params.landing; this.landingFlag = params.landing;
@ -71,20 +73,21 @@ export class LoginComponent implements OnDestroy, OnInit {
} }
}); });
if(authService.loggedIn()) { if (authService.loggedIn()) {
this.router.navigate(["search"]); this.router.navigate(["search"]);
} }
} }
public ngOnInit() { public ngOnInit() {
this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x); this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x);
this.settingsService.getClientId().subscribe(x => this.clientId = x);
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
this.images.getRandomBackground().subscribe(x => { this.images.getRandomBackground().subscribe(x => {
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")"); this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")");
}); });
this.timer = setInterval(() => { this.timer = setInterval(() => {
this.cycleBackground(); this.cycleBackground();
}, 10000); }, 7000);
const base = this.location.getBaseHrefFromDOM(); const base = this.location.getBaseHrefFromDOM();
if (base.length > 1) { if (base.length > 1) {
@ -101,9 +104,9 @@ export class LoginComponent implements OnDestroy, OnInit {
return; return;
} }
const value = form.value; const value = form.value;
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false }; const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: "" } };
this.authService.requiresPassword(user).subscribe(x => { this.authService.requiresPassword(user).subscribe(x => {
if(x && this.authenticationSettings.allowNoPassword) { if (x && this.authenticationSettings.allowNoPassword) {
// Looks like this user requires a password // Looks like this user requires a password
this.authenticationSettings.allowNoPassword = false; this.authenticationSettings.allowNoPassword = false;
return; return;
@ -113,6 +116,7 @@ export class LoginComponent implements OnDestroy, OnInit {
localStorage.setItem("id_token", x.access_token); localStorage.setItem("id_token", x.access_token);
if (this.authService.loggedIn()) { if (this.authService.loggedIn()) {
this.ngOnDestroy();
this.router.navigate(["search"]); this.router.navigate(["search"]);
} else { } else {
this.notify.error(this.errorBody); this.notify.error(this.errorBody);
@ -123,28 +127,58 @@ export class LoginComponent implements OnDestroy, OnInit {
} }
public oauth() { public oauth() {
this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { this.plexTv.GetPin(this.clientId, this.appName).subscribe((pin: any) => {
if (window.frameElement) {
// in frame this.authService.login({ usePlexOAuth: true, password: "", rememberMe: true, username: "", plexTvPin: pin }).subscribe(x => {
window.open(x.url, "_blank");
} else { window.open(x.url, "_blank", `toolbar=0,
// not in frame location=0,
window.location.href = x.url; status=0,
} menubar=0,
scrollbars=1,
resizable=1,
width=500,
height=500`);
this.pinTimer = setInterval(() => {
this.notify.info("Authenticating", "Loading... Please Wait");
this.getPinResult(x.pinId);
}, 10000);
});
}); });
} }
public getPinResult(pinId: number) {
this.authService.oAuth(pinId).subscribe(x => {
if(x.access_token) {
localStorage.setItem("id_token", x.access_token);
if (this.authService.loggedIn()) {
this.ngOnDestroy();
this.router.navigate(["search"]);
return;
}
}
}, err => {
this.notify.error(err.statusText);
this.router.navigate(["login"]);
});
}
public ngOnDestroy() { public ngOnDestroy() {
clearInterval(this.timer); clearInterval(this.timer);
clearInterval(this.pinTimer);
} }
private cycleBackground() { private cycleBackground() {
this.images.getRandomBackground().subscribe(x => { this.images.getRandomBackground().subscribe(x => {
this.background = ""; this.background = "";
}); });
this.images.getRandomBackground().subscribe(x => { this.images.getRandomBackground().subscribe(x => {
this.background = this.sanitizer this.background = this.sanitizer
.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")"); .bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")");
}); });
} }
} }

View file

@ -16,7 +16,6 @@ export class LoginOAuthComponent implements OnInit {
this.route.params this.route.params
.subscribe((params: any) => { .subscribe((params: any) => {
this.pin = params.pin; this.pin = params.pin;
}); });
} }
@ -26,21 +25,20 @@ export class LoginOAuthComponent implements OnInit {
public auth() { public auth() {
this.authService.oAuth(this.pin).subscribe(x => { this.authService.oAuth(this.pin).subscribe(x => {
if(x.access_token) { if (x.access_token) {
localStorage.setItem("id_token", x.access_token); localStorage.setItem("id_token", x.access_token);
if (this.authService.loggedIn()) { if (this.authService.loggedIn()) {
this.router.navigate(["search"]); this.router.navigate(["search"]);
return; return;
} }
} }
if(x.errorMessage) { if (x.errorMessage) {
this.error = x.errorMessage; this.error = x.errorMessage;
} }
}, err => { }, err => {
this.notify.error(err.statusText); this.notify.error(err.statusText);
this.router.navigate(["login"]); this.router.navigate(["login"]);
}); });
} }

View file

@ -4,7 +4,7 @@ import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { ICustomizationSettings } from "../interfaces"; import { ICustomizationSettings } from "../interfaces";
import { IdentityService, ImageService,NotificationService, SettingsService } from "../services"; import { IdentityService, ImageService, NotificationService, SettingsService } from "../services";
@Component({ @Component({
templateUrl: "./resetpassword.component.html", templateUrl: "./resetpassword.component.html",

View file

@ -1,5 +1,5 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { NguCarousel } from "@ngu/carousel"; import { NguCarouselConfig } from "@ngu/carousel";
import { ImageService, RecentlyAddedService } from "../services"; import { ImageService, RecentlyAddedService } from "../services";
import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces"; import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "./../interfaces";
@ -41,13 +41,13 @@ export class RecentlyAddedComponent implements OnInit {
public range: Date[]; public range: Date[];
public groupTv: boolean = false; public groupTv: boolean = false;
// https://github.com/sheikalthaf/ngu-carousel // https://github.com/sheikalthaf/ngu-carousel
public carouselTile: NguCarousel; public carouselTile: NguCarouselConfig;
constructor(private recentlyAddedService: RecentlyAddedService, constructor(private recentlyAddedService: RecentlyAddedService,
private imageService: ImageService) {} private imageService: ImageService) {}
public ngOnInit() { public ngOnInit() {
this.getMovies(); this.getMovies();
this.getShows(); this.getShows();
@ -67,10 +67,10 @@ export class RecentlyAddedComponent implements OnInit {
} }
public close() { public close() {
if(this.range.length < 2) { if (this.range.length < 2) {
return; return;
} }
if(!this.range[1]) { if (!this.range[1]) {
// If we do not have a second date then just set it to now // If we do not have a second date then just set it to now
this.range[1] = new Date(); this.range[1] = new Date();
} }
@ -82,13 +82,13 @@ export class RecentlyAddedComponent implements OnInit {
} }
private getShows() { private getShows() {
if(this.groupTv) { if (this.groupTv) {
this.recentlyAddedService.getRecentlyAddedTvGrouped().subscribe(x => { this.recentlyAddedService.getRecentlyAddedTvGrouped().subscribe(x => {
this.tv = x; this.tv = x;
this.tv.forEach((t) => { this.tv.forEach((t) => {
this.imageService.getTvPoster(t.tvDbId).subscribe(p => { this.imageService.getTvPoster(t.tvDbId).subscribe(p => {
if(p) { if (p) {
t.posterPath = p; t.posterPath = p;
} }
}); });
@ -97,10 +97,10 @@ export class RecentlyAddedComponent implements OnInit {
} else { } else {
this.recentlyAddedService.getRecentlyAddedTv().subscribe(x => { this.recentlyAddedService.getRecentlyAddedTv().subscribe(x => {
this.tv = x; this.tv = x;
this.tv.forEach((t) => { this.tv.forEach((t) => {
this.imageService.getTvPoster(t.tvDbId).subscribe(p => { this.imageService.getTvPoster(t.tvDbId).subscribe(p => {
if(p) { if (p) {
t.posterPath = p; t.posterPath = p;
} }
}); });
@ -114,11 +114,11 @@ export class RecentlyAddedComponent implements OnInit {
this.movies = x; this.movies = x;
this.movies.forEach((movie) => { this.movies.forEach((movie) => {
if(movie.theMovieDbId) { if (movie.theMovieDbId) {
this.imageService.getMoviePoster(movie.theMovieDbId).subscribe(p => { this.imageService.getMoviePoster(movie.theMovieDbId).subscribe(p => {
movie.posterPath = p; movie.posterPath = p;
}); });
} else if(movie.imdbId) { } else if (movie.imdbId) {
this.imageService.getMoviePoster(movie.imdbId).subscribe(p => { this.imageService.getMoviePoster(movie.imdbId).subscribe(p => {
movie.posterPath = p; movie.posterPath = p;
}); });

Some files were not shown because too many files have changed in this diff Show more