mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 12:59:39 -07:00
commit
2181be30f5
481 changed files with 35469 additions and 20715 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -243,3 +243,6 @@ _Pvt_Extensions
|
|||
# CAKE - C# Make
|
||||
/Tools/*
|
||||
*.db-journal
|
||||
|
||||
# Ignore local vscode config
|
||||
*.vscode
|
||||
|
|
4472
CHANGELOG.md
4472
CHANGELOG.md
File diff suppressed because it is too large
Load diff
14
README.md
14
README.md
|
@ -1,13 +1,19 @@
|
|||

|
||||
____
|
||||
[](https://discord.gg/KxYZ64w)
|
||||
[](https://discord.gg/Sa7wNWb)
|
||||
[](https://hub.docker.com/r/linuxserver/ombi/)
|
||||
[](https://github.com/tidusjar/Ombi)
|
||||
[](http://www.firsttimersonly.com/)
|
||||
[](http://www.firsttimersonly.com/)
|
||||
[](https://crowdin.com/project/ombi)
|
||||
|
||||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
___
|
||||
<a href='https://play.google.com/store/apps/details?id=com.tidusjar.Ombi&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img width="150" alt='Get it on Google Play' src='https://play.google.com/intl/en_gb/badges/images/generic/en_badge_web_generic.png'/></a>
|
||||
|
||||
<a href='https://itunes.apple.com/us/app/ombi/id1335260043?ls=1&mt=8'><img width="150" alt='Get it on App Store' src='https://i.imgur.com/cJFa0M4.png'/></a>
|
||||
|
||||
|
||||
___
|
||||
|
||||
|
|
12
appveyor.yml
12
appveyor.yml
|
@ -2,7 +2,7 @@ version: 3.0.{build}
|
|||
configuration: Release
|
||||
os: Visual Studio 2017
|
||||
environment:
|
||||
nodejs_version: "7.8.0"
|
||||
nodejs_version: "9.8.0"
|
||||
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
|
@ -15,19 +15,19 @@ test: off
|
|||
after_build:
|
||||
- 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"
|
||||
|
|
42
build.cake
42
build.cake
|
@ -1,10 +1,10 @@
|
|||
|
||||
#tool "nuget:?package=GitVersion.CommandLine"
|
||||
#addin "Cake.Gulp"
|
||||
#addin "Cake.Npm"
|
||||
#addin "SharpZipLib"
|
||||
#addin nuget:?package=Cake.Compression&version=0.1.4
|
||||
#addin "Cake.Incubator"
|
||||
#addin "Cake.Yarn"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ARGUMENTS
|
||||
|
@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
|
|||
var solutionFile = "Ombi.sln"; // Solution file if needed
|
||||
GitVersion versionInfo = null;
|
||||
|
||||
var frameworkVer = "netcoreapp2.0";
|
||||
var frameworkVer = "netcoreapp2.1";
|
||||
|
||||
var buildSettings = new DotNetCoreBuildSettings
|
||||
{
|
||||
|
@ -122,36 +122,19 @@ Task("SetVersionInfo")
|
|||
|
||||
Task("NPM")
|
||||
.Does(() => {
|
||||
var settings = new NpmInstallSettings {
|
||||
LogLevel = NpmLogLevel.Silent,
|
||||
WorkingDirectory = webProjDir,
|
||||
Production = true
|
||||
};
|
||||
|
||||
NpmInstall(settings);
|
||||
Yarn.FromPath(webProjDir).Install();
|
||||
});
|
||||
|
||||
Task("Gulp Publish")
|
||||
.IsDependentOn("NPM")
|
||||
.Does(() => {
|
||||
|
||||
var runScriptSettings = new NpmRunScriptSettings {
|
||||
ScriptName="publish",
|
||||
WorkingDirectory = webProjDir,
|
||||
};
|
||||
|
||||
NpmRunScript(runScriptSettings);
|
||||
Yarn.FromPath(webProjDir).RunScript("publish");
|
||||
});
|
||||
|
||||
Task("TSLint")
|
||||
.Does(() =>
|
||||
{
|
||||
var settings = new NpmRunScriptSettings {
|
||||
WorkingDirectory = webProjDir,
|
||||
ScriptName = "lint"
|
||||
};
|
||||
|
||||
NpmRunScript(settings);
|
||||
Yarn.FromPath(webProjDir).RunScript("lint");
|
||||
});
|
||||
|
||||
Task("PrePublish")
|
||||
|
@ -178,7 +161,7 @@ Task("Publish")
|
|||
.IsDependentOn("Publish-OSX")
|
||||
.IsDependentOn("Publish-Linux")
|
||||
.IsDependentOn("Publish-Linux-ARM")
|
||||
//.IsDependentOn("Publish-Linux-ARM-64Bit")
|
||||
.IsDependentOn("Publish-Linux-ARM-64Bit")
|
||||
.IsDependentOn("Package");
|
||||
|
||||
Task("Publish-Windows")
|
||||
|
@ -189,6 +172,8 @@ Task("Publish-Windows")
|
|||
|
||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -200,6 +185,9 @@ Task("Publish-Windows-32bit")
|
|||
|
||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -211,6 +199,8 @@ Task("Publish-OSX")
|
|||
|
||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -222,6 +212,8 @@ Task("Publish-Linux")
|
|||
|
||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -235,6 +227,8 @@ Task("Publish-Linux-ARM")
|
|||
CopyFile(
|
||||
buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml",
|
||||
buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml");
|
||||
|
||||
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater");
|
||||
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||
});
|
||||
|
||||
|
@ -248,6 +242,8 @@ Task("Publish-Linux-ARM-64Bit")
|
|||
CopyFile(
|
||||
buildDir + "/"+frameworkVer+"/linux-arm64/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);
|
||||
});
|
||||
|
||||
|
|
BIN
music-placeholder.psd
Normal file
BIN
music-placeholder.psd
Normal file
Binary file not shown.
291
package-lock.json
generated
Normal file
291
package-lock.json
generated
Normal 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="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Emby.Models;
|
||||
using Ombi.Api.Emby.Models.Media.Tv;
|
||||
|
@ -90,27 +91,31 @@ namespace Ombi.Api.Emby
|
|||
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);
|
||||
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);
|
||||
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)
|
||||
|
@ -129,20 +134,40 @@ namespace Ombi.Api.Emby
|
|||
private async Task<T> GetInformation<T>(string mediaId, string apiKey, string userId, string baseUrl)
|
||||
{
|
||||
var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
var response = await Api.RequestContent(request);
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(response);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri)
|
||||
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
|
||||
{
|
||||
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("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);
|
||||
|
||||
|
|
|
@ -14,12 +14,17 @@ namespace Ombi.Api.Emby
|
|||
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
|
||||
Task<EmbyConnectUser> LoginConnectUser(string username, string password);
|
||||
|
||||
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string userId, string baseUri);
|
||||
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, string userId, string baseUri);
|
||||
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, string userId, string baseUri);
|
||||
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
|
||||
string baseUri);
|
||||
|
||||
Task<EmbyItemContainer<MovieInformation>> GetCollection(string mediaId, string apiKey, string userId,
|
||||
string baseUrl);
|
||||
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
|
||||
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<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
|
||||
|
|
|
@ -28,5 +28,7 @@ namespace Ombi.Api.Emby.Models.Movie
|
|||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public int CriticRating { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public EmbyProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -39,5 +39,6 @@ namespace Ombi.Api.Emby.Models.Media.Tv
|
|||
public string LocationType { get; set; }
|
||||
public string MediaType { get; set; }
|
||||
public bool HasSubtitles { get; set; }
|
||||
public EmbyProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -26,5 +26,7 @@ namespace Ombi.Api.Emby.Models.Media.Tv
|
|||
public string[] BackdropImageTags { get; set; }
|
||||
public string LocationType { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
public EmbyProviderids ProviderIds { get; set; }
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ namespace Ombi.Api.FanartTv
|
|||
{
|
||||
var request = new Request($"tv/{tvdbId}", Endpoint, HttpMethod.Get);
|
||||
request.AddHeader("api-key", token);
|
||||
request.IgnoreErrors = true;
|
||||
try
|
||||
{
|
||||
return await Api.Request<TvResult>(request);
|
||||
|
@ -36,6 +37,7 @@ namespace Ombi.Api.FanartTv
|
|||
{
|
||||
var request = new Request($"movies/{movieOrImdbId}", Endpoint, HttpMethod.Get);
|
||||
request.AddHeader("api-key", token);
|
||||
request.IgnoreErrors = true;
|
||||
|
||||
return await Api.Request<MovieResult>(request);
|
||||
}
|
||||
|
|
27
src/Ombi.Api.Lidarr/ILidarrApi.cs
Normal file
27
src/Ombi.Api.Lidarr/ILidarrApi.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
|
||||
namespace Ombi.Api.Lidarr
|
||||
{
|
||||
public interface ILidarrApi
|
||||
{
|
||||
Task<List<AlbumLookup>> AlbumLookup(string searchTerm, string apiKey, string baseUrl);
|
||||
Task<List<ArtistLookup>> ArtistLookup(string searchTerm, string apiKey, string baseUrl);
|
||||
Task<List<LidarrProfile>> GetProfiles(string apiKey, string baseUrl);
|
||||
Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
|
||||
Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl);
|
||||
Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl);
|
||||
Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId);
|
||||
Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl);
|
||||
Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl);
|
||||
Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl);
|
||||
Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl);
|
||||
Task<AlbumResponse> MontiorAlbum(int albumId, string apiKey, string baseUrl);
|
||||
Task<List<AlbumResponse>> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl);
|
||||
Task<List<MetadataProfile>> GetMetadataProfile(string apiKey, string baseUrl);
|
||||
Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl);
|
||||
Task<LidarrStatus> Status(string apiKey, string baseUrl);
|
||||
Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl);
|
||||
}
|
||||
}
|
170
src/Ombi.Api.Lidarr/LidarrApi.cs
Normal file
170
src/Ombi.Api.Lidarr/LidarrApi.cs
Normal file
|
@ -0,0 +1,170 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
|
||||
namespace Ombi.Api.Lidarr
|
||||
{
|
||||
public class LidarrApi : ILidarrApi
|
||||
{
|
||||
public LidarrApi(ILogger<LidarrApi> logger, IApi api)
|
||||
{
|
||||
Api = api;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private IApi Api { get; }
|
||||
private ILogger Logger { get; }
|
||||
|
||||
private const string ApiVersion = "/api/v1";
|
||||
|
||||
public Task<List<LidarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/qualityprofile", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LidarrProfile>>(request);
|
||||
}
|
||||
|
||||
public Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/rootfolder", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LidarrRootFolder>>(request);
|
||||
}
|
||||
|
||||
public async Task<List<ArtistLookup>> ArtistLookup(string searchTerm, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/Artist/lookup", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("term", searchTerm);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return await Api.Request<List<ArtistLookup>>(request);
|
||||
}
|
||||
|
||||
public Task<List<AlbumLookup>> AlbumLookup(string searchTerm, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/Album/lookup", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("term", searchTerm);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<AlbumLookup>>(request);
|
||||
}
|
||||
|
||||
public Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist/{artistId}", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<ArtistResult>(request);
|
||||
}
|
||||
|
||||
public async Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist/lookup", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("term", $"lidarr:{foreignArtistId}");
|
||||
AddHeaders(request, apiKey);
|
||||
return (await Api.Request<List<ArtistResult>>(request)).FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album/lookup", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("term", $"lidarr:{foreignArtistId}");
|
||||
AddHeaders(request, apiKey);
|
||||
var albums = await Api.Request<List<AlbumLookup>>(request);
|
||||
return albums.FirstOrDefault();
|
||||
}
|
||||
|
||||
public Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId)
|
||||
{
|
||||
var request = new Request(string.Empty, $"https://api.lidarr.audio/api/v0.3/artist/{foreignArtistId}",
|
||||
HttpMethod.Get) {IgnoreBaseUrlAppend = true};
|
||||
return Api.Request<AlbumByArtistResponse>(request);
|
||||
}
|
||||
|
||||
public Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<ArtistResult>>(request);
|
||||
}
|
||||
|
||||
public Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<AlbumResponse>>(request);
|
||||
}
|
||||
|
||||
public Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post);
|
||||
request.AddJsonBody(artist);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<ArtistResult>(request);
|
||||
}
|
||||
|
||||
public async Task<AlbumResponse> MontiorAlbum(int albumId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album/monitor", baseUrl, HttpMethod.Put);
|
||||
request.AddJsonBody(new
|
||||
{
|
||||
albumIds = new[] { albumId },
|
||||
monitored = true
|
||||
});
|
||||
AddHeaders(request, apiKey);
|
||||
return (await Api.Request<List<AlbumResponse>>(request)).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Task<List<AlbumResponse>> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("artistId", artistId.ToString());
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<AlbumResponse>>(request);
|
||||
}
|
||||
|
||||
public Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/languageprofile", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LanguageProfiles>>(request);
|
||||
}
|
||||
|
||||
public Task<List<MetadataProfile>> GetMetadataProfile(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/metadataprofile", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<MetadataProfile>>(request);
|
||||
}
|
||||
|
||||
public Task<LidarrStatus> Status(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/system/status", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<LidarrStatus>(request);
|
||||
}
|
||||
|
||||
public Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/command/", baseUrl, HttpMethod.Post);
|
||||
request.AddJsonBody(new { name = "AlbumSearch", albumIds });
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<CommandResult>(request);
|
||||
}
|
||||
|
||||
private void AddHeaders(Request request, string key)
|
||||
{
|
||||
request.AddHeader("X-Api-Key", key);
|
||||
}
|
||||
}
|
||||
}
|
34
src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs
Normal file
34
src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumByArtistResponse
|
||||
{
|
||||
public Album[] Albums { get; set; }
|
||||
public string ArtistName { get; set; }
|
||||
public string Disambiguation { get; set; }
|
||||
public string Id { get; set; }
|
||||
public Image[] Images { get; set; }
|
||||
public Link[] Links { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public Rating Rating { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
|
||||
public class Rating
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
}
|
||||
|
||||
public class Album
|
||||
{
|
||||
public string Disambiguation { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string ReleaseDate { get; set; }
|
||||
public string[] ReleaseStatuses { get; set; }
|
||||
public string[] SecondaryTypes { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
25
src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
Normal file
25
src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumLookup
|
||||
{
|
||||
public string title { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string foreignAlbumId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public int duration { get; set; }
|
||||
public string albumType { get; set; }
|
||||
public string[] secondaryTypes { get; set; }
|
||||
public int mediumCount { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
//public object[] releases { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
//public object[] media { get; set; }
|
||||
public Artist artist { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string remoteCover { get; set; }
|
||||
}
|
||||
}
|
27
src/Ombi.Api.Lidarr/Models/AlbumResponse.cs
Normal file
27
src/Ombi.Api.Lidarr/Models/AlbumResponse.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumResponse
|
||||
{
|
||||
public string title { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string foreignAlbumId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public int duration { get; set; }
|
||||
public string albumType { get; set; }
|
||||
public object[] secondaryTypes { get; set; }
|
||||
public int mediumCount { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public Currentrelease currentRelease { get; set; }
|
||||
public Release[] releases { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public Medium[] media { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
25
src/Ombi.Api.Lidarr/Models/Artist.cs
Normal file
25
src/Ombi.Api.Lidarr/Models/Artist.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Artist
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public object[] links { get; set; }
|
||||
public object[] images { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
}
|
||||
}
|
49
src/Ombi.Api.Lidarr/Models/ArtistAdd.cs
Normal file
49
src/Ombi.Api.Lidarr/Models/ArtistAdd.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Net.Mime;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class ArtistAdd
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public Link[] links { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string remotePoster { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public string cleanName { get; set; }
|
||||
public string sortName { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
public Addoptions addOptions { get; set; }
|
||||
public string rootFolderPath { get; set; }
|
||||
}
|
||||
|
||||
public class Addoptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Future = 1
|
||||
/// Missing = 2
|
||||
/// Existing = 3
|
||||
/// First = 5
|
||||
/// Latest = 4
|
||||
/// None = 6
|
||||
/// </summary>
|
||||
public int selectedOption { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public bool searchForMissingAlbums { get; set; }
|
||||
public string[] AlbumsToMonitor { get; set; } // Uses the MusicBrainzAlbumId!
|
||||
}
|
||||
}
|
32
src/Ombi.Api.Lidarr/Models/ArtistLookup.cs
Normal file
32
src/Ombi.Api.Lidarr/Models/ArtistLookup.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Net.Mime;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class ArtistLookup
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string artistType { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public Link[] links { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string remotePoster { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public string cleanName { get; set; }
|
||||
public string sortName { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
}
|
||||
}
|
93
src/Ombi.Api.Lidarr/Models/ArtistResult.cs
Normal file
93
src/Ombi.Api.Lidarr/Models/ArtistResult.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
|
||||
public class ArtistResult
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public DateTime lastInfoSync { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string artistType { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public Link[] links { get; set; }
|
||||
public Nextalbum nextAlbum { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string path { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public string cleanName { get; set; }
|
||||
public string sortName { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class Nextalbum
|
||||
{
|
||||
public string foreignAlbumId { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string title { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public string cleanTitle { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public int duration { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public object[] images { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public Medium[] media { get; set; }
|
||||
public DateTime lastInfoSync { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public string albumType { get; set; }
|
||||
public object[] secondaryTypes { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Release[] releases { get; set; }
|
||||
public Currentrelease currentRelease { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class Currentrelease
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string title { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public int trackCount { get; set; }
|
||||
public int mediaCount { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public string[] country { get; set; }
|
||||
public string format { get; set; }
|
||||
public string[] label { get; set; }
|
||||
}
|
||||
|
||||
public class Medium
|
||||
{
|
||||
public int number { get; set; }
|
||||
public string name { get; set; }
|
||||
public string format { get; set; }
|
||||
}
|
||||
|
||||
public class Release
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string title { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public int trackCount { get; set; }
|
||||
public int mediaCount { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public string[] country { get; set; }
|
||||
public string format { get; set; }
|
||||
public string[] label { get; set; }
|
||||
}
|
||||
}
|
15
src/Ombi.Api.Lidarr/Models/CommandResult.cs
Normal file
15
src/Ombi.Api.Lidarr/Models/CommandResult.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
|
||||
public class CommandResult
|
||||
{
|
||||
public string name { get; set; }
|
||||
public DateTime queued { get; set; }
|
||||
public DateTime stateChangeTime { get; set; }
|
||||
public bool sendUpdatesToClient { get; set; }
|
||||
public string status { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/Image.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/Image.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Image
|
||||
{
|
||||
public string coverType { get; set; }
|
||||
public string url { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LanguageProfiles
|
||||
{
|
||||
public string name { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
23
src/Ombi.Api.Lidarr/Models/LidarrProfile.cs
Normal file
23
src/Ombi.Api.Lidarr/Models/LidarrProfile.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Quality
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class Item
|
||||
{
|
||||
public Quality quality { get; set; }
|
||||
public bool allowed { get; set; }
|
||||
}
|
||||
|
||||
public class LidarrProfile
|
||||
{
|
||||
public string name { get; set; }
|
||||
public List<Item> items { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs
Normal file
11
src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrRootFolder
|
||||
{
|
||||
public string path { get; set; }
|
||||
public long freeSpace { get; set; }
|
||||
public object[] unmappedFolders { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
}
|
31
src/Ombi.Api.Lidarr/Models/LidarrStatus.cs
Normal file
31
src/Ombi.Api.Lidarr/Models/LidarrStatus.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrStatus
|
||||
{
|
||||
public string version { get; set; }
|
||||
public DateTime buildTime { get; set; }
|
||||
public bool isDebug { get; set; }
|
||||
public bool isProduction { get; set; }
|
||||
public bool isAdmin { get; set; }
|
||||
public bool isUserInteractive { get; set; }
|
||||
public string startupPath { get; set; }
|
||||
public string appData { get; set; }
|
||||
public string osName { get; set; }
|
||||
public string osVersion { get; set; }
|
||||
public bool isMonoRuntime { get; set; }
|
||||
public bool isMono { get; set; }
|
||||
public bool isLinux { get; set; }
|
||||
public bool isOsx { get; set; }
|
||||
public bool isWindows { get; set; }
|
||||
public string mode { get; set; }
|
||||
public string branch { get; set; }
|
||||
public string authentication { get; set; }
|
||||
public string sqliteVersion { get; set; }
|
||||
public int migrationVersion { get; set; }
|
||||
public string urlBase { get; set; }
|
||||
public string runtimeVersion { get; set; }
|
||||
public string runtimeName { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/Link.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/Link.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Link
|
||||
{
|
||||
public string url { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/MetadataProfile.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/MetadataProfile.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class MetadataProfile
|
||||
{
|
||||
public string name { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/Ratings.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/Ratings.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Ratings
|
||||
{
|
||||
public int votes { get; set; }
|
||||
public decimal value { get; set; }
|
||||
}
|
||||
}
|
12
src/Ombi.Api.Lidarr/Models/Statistics.cs
Normal file
12
src/Ombi.Api.Lidarr/Models/Statistics.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Statistics
|
||||
{
|
||||
public int albumCount { get; set; }
|
||||
public int trackFileCount { get; set; }
|
||||
public int trackCount { get; set; }
|
||||
public int totalTrackCount { get; set; }
|
||||
public long sizeOnDisk { get; set; }
|
||||
public decimal percentOfEpisodes { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj
Normal file
11
src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -120,8 +120,10 @@ namespace Ombi.Api.Mattermost.Models
|
|||
var attIndex = outMessages[msgCount].Attachments.Count - 1;
|
||||
|
||||
//Get the text lines
|
||||
lines = att.Text.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
|
||||
|
||||
if (!String.IsNullOrEmpty(att.Text))
|
||||
{
|
||||
lines = att.Text.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
|
||||
}
|
||||
foreach (var line in lines)
|
||||
{
|
||||
//Get the total length of all attachments on the current outgoing message
|
||||
|
@ -153,8 +155,9 @@ namespace Ombi.Api.Mattermost.Models
|
|||
|
||||
foreach (var msg in outMessages)
|
||||
{
|
||||
var request = new Request("", _webhookUrl.ToString(), HttpMethod.Post);
|
||||
var request = new Request(_webhookUrl.ToString(), "", HttpMethod.Post);
|
||||
request.AddJsonBody(msg);
|
||||
request.AddHeader("Host", _webhookUrl.Host);
|
||||
await api.Request(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Ombi.Api.Mattermost.Models
|
|||
/// Bot/User Icon
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "icon_url")]
|
||||
public Uri IconUrl { get; set; }
|
||||
public string IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Message body. Supports Markdown
|
||||
|
@ -142,7 +142,7 @@ namespace Ombi.Api.Mattermost.Models
|
|||
/// Large images are resized to a maximum width of 400px or a maximum height of 300px, while still maintaining the original aspect ratio.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "image_url")]
|
||||
public Uri ImageUrl { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional URL to an image file(GIF, JPEG, PNG, or BMP) that is displayed as a 75x75 pixel thumbnail on the right side of an attachment.
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -6,6 +6,6 @@ namespace Ombi.Api.Notifications
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -4,18 +4,22 @@
|
|||
{
|
||||
public string app_id { 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 class Data
|
||||
{
|
||||
public string foo { get; set; }
|
||||
}
|
||||
|
||||
public class Contents
|
||||
{
|
||||
public string en { get; set; }
|
||||
}
|
||||
|
||||
public class Button
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string text { get; set; }
|
||||
//public string icon { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,10 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Ombi.Api.Notifications
|
|||
private readonly IApplicationConfigRepository _appConfig;
|
||||
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())
|
||||
{
|
||||
|
@ -39,6 +39,17 @@ namespace Ombi.Api.Notifications
|
|||
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);
|
||||
|
||||
var result = await _api.Request<OneSignalNotificationResponse>(request);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.Friends;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
using Ombi.Api.Plex.Models.Server;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
|
||||
|
@ -9,6 +11,7 @@ namespace Ombi.Api.Plex
|
|||
public interface IPlexApi
|
||||
{
|
||||
Task<PlexStatus> GetStatus(string authToken, string uri);
|
||||
Task<PlexLibrariesForMachineId> GetLibrariesForMachineId(string authToken, string machineId);
|
||||
Task<PlexAuthentication> SignIn(UserRequest user);
|
||||
Task<PlexServer> GetServer(string authToken);
|
||||
Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost);
|
||||
|
@ -19,5 +22,9 @@ namespace Ombi.Api.Plex
|
|||
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
|
||||
Task<PlexFriends> GetUsers(string authToken);
|
||||
Task<PlexAccount> GetAccount(string authToken);
|
||||
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
|
||||
Task<OAuthPin> GetPin(int pinId);
|
||||
Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl);
|
||||
Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs);
|
||||
}
|
||||
}
|
|
@ -11,20 +11,20 @@ namespace Ombi.Api.Plex.Models
|
|||
public string summary { get; set; }
|
||||
public int index { get; set; }
|
||||
public float rating { get; set; }
|
||||
public int viewCount { get; set; }
|
||||
public int lastViewedAt { get; set; }
|
||||
//public int viewCount { get; set; }
|
||||
//public int lastViewedAt { get; set; }
|
||||
public int year { get; set; }
|
||||
public string thumb { get; set; }
|
||||
public string art { get; set; }
|
||||
public string banner { get; set; }
|
||||
public string theme { get; set; }
|
||||
public string duration { get; set; }
|
||||
public string originallyAvailableAt { get; set; }
|
||||
//public string duration { get; set; }
|
||||
//public string originallyAvailableAt { get; set; }
|
||||
public int leafCount { get; set; }
|
||||
public int viewedLeafCount { get; set; }
|
||||
public int childCount { get; set; }
|
||||
public long addedAt { get; set; }
|
||||
public int updatedAt { get; set; }
|
||||
//public long addedAt { get; set; }
|
||||
//public int updatedAt { get; set; }
|
||||
public Genre[] Genre { get; set; }
|
||||
//public Role[] Role { get; set; }
|
||||
public string primaryExtraKey { get; set; }
|
||||
|
|
27
src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs
Normal file
27
src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Plex.Models.OAuth
|
||||
{
|
||||
public class OAuthPin
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string code { get; set; }
|
||||
public bool trusted { get; set; }
|
||||
public string clientIdentifier { get; set; }
|
||||
public Location location { get; set; }
|
||||
public int expiresIn { get; set; }
|
||||
public DateTime createdAt { get; set; }
|
||||
public DateTime expiresAt { get; set; }
|
||||
public string authToken { get; set; }
|
||||
}
|
||||
|
||||
public class Location
|
||||
{
|
||||
public string code { get; set; }
|
||||
public string country { get; set; }
|
||||
public string city { get; set; }
|
||||
public string subdivisions { get; set; }
|
||||
public string coordinates { get; set; }
|
||||
}
|
||||
|
||||
}
|
84
src/Ombi.Api.Plex/Models/PlexAdd.cs
Normal file
84
src/Ombi.Api.Plex/Models/PlexAdd.cs
Normal 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;
|
||||
}
|
||||
}
|
66
src/Ombi.Api.Plex/Models/PlexLibrariesForMachineId.cs
Normal file
66
src/Ombi.Api.Plex/Models/PlexLibrariesForMachineId.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -1,20 +1,66 @@
|
|||
using System.Net.Http;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.Friends;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
using Ombi.Api.Plex.Models.Server;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
||||
namespace Ombi.Api.Plex
|
||||
{
|
||||
public class PlexApi : IPlexApi
|
||||
{
|
||||
public PlexApi(IApi api)
|
||||
public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings, ISettingsService<PlexSettings> p)
|
||||
{
|
||||
Api = api;
|
||||
_custom = settings;
|
||||
_plexSettings = p;
|
||||
}
|
||||
|
||||
private IApi Api { get; }
|
||||
private readonly ISettingsService<CustomizationSettings> _custom;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
|
||||
private string _app;
|
||||
private string ApplicationName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_app))
|
||||
{
|
||||
var settings = _custom.GetSettings();
|
||||
if (settings.ApplicationName.IsNullOrEmpty())
|
||||
{
|
||||
_app = "Ombi";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
||||
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
||||
|
@ -36,7 +82,7 @@ namespace Ombi.Api.Plex
|
|||
};
|
||||
var request = new Request(SignInUri, string.Empty, HttpMethod.Post);
|
||||
|
||||
AddHeaders(request);
|
||||
await AddHeaders(request);
|
||||
request.AddJsonBody(userModel);
|
||||
|
||||
var obj = await Api.Request<PlexAuthentication>(request);
|
||||
|
@ -47,14 +93,14 @@ namespace Ombi.Api.Plex
|
|||
public async Task<PlexStatus> GetStatus(string authToken, string uri)
|
||||
{
|
||||
var request = new Request(uri, string.Empty, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
return await Api.Request<PlexStatus>(request);
|
||||
}
|
||||
|
||||
public async Task<PlexAccount> GetAccount(string authToken)
|
||||
{
|
||||
var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
return await Api.Request<PlexAccount>(request);
|
||||
}
|
||||
|
||||
|
@ -62,7 +108,7 @@ namespace Ombi.Api.Plex
|
|||
{
|
||||
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
|
||||
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
|
||||
return await Api.Request<PlexServer>(request);
|
||||
}
|
||||
|
@ -70,17 +116,24 @@ namespace Ombi.Api.Plex
|
|||
public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost)
|
||||
{
|
||||
var request = new Request("library/sections", plexFullHost, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
return await Api.Request<PlexContainer>(request);
|
||||
}
|
||||
|
||||
public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId)
|
||||
{
|
||||
var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
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>
|
||||
// 192.168.1.69:32400/library/metadata/3662/allLeaves
|
||||
// The metadata ratingkey should be in the Cache
|
||||
|
@ -95,21 +148,21 @@ namespace Ombi.Api.Plex
|
|||
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
|
||||
{
|
||||
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
return await Api.Request<PlexMetadata>(request);
|
||||
}
|
||||
|
||||
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
|
||||
{
|
||||
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
return await Api.Request<PlexMetadata>(request);
|
||||
}
|
||||
|
||||
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
|
||||
{
|
||||
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
return await Api.Request<PlexMetadata>(request);
|
||||
}
|
||||
|
||||
|
@ -128,7 +181,7 @@ namespace Ombi.Api.Plex
|
|||
|
||||
request.AddQueryString("type", "4");
|
||||
AddLimitHeaders(request, start, retCount);
|
||||
AddHeaders(request, authToken);
|
||||
await AddHeaders(request, authToken);
|
||||
|
||||
return await Api.Request<PlexContainer>(request);
|
||||
}
|
||||
|
@ -141,32 +194,110 @@ namespace Ombi.Api.Plex
|
|||
/// <returns></returns>
|
||||
public async Task<PlexFriends> GetUsers(string authToken)
|
||||
{
|
||||
var request = new Request(string.Empty,FriendsUri, HttpMethod.Get, ContentType.Xml);
|
||||
AddHeaders(request, authToken);
|
||||
var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml);
|
||||
await AddHeaders(request, authToken);
|
||||
|
||||
return await Api.Request<PlexFriends>(request);
|
||||
}
|
||||
|
||||
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
|
||||
{
|
||||
var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get);
|
||||
await AddHeaders(request, authToken);
|
||||
AddLimitHeaders(request, 0, 50);
|
||||
|
||||
return await Api.Request<PlexMetadata>(request);
|
||||
}
|
||||
|
||||
public async Task<OAuthPin> GetPin(int pinId)
|
||||
{
|
||||
var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get);
|
||||
await AddHeaders(request);
|
||||
|
||||
return await Api.Request<OAuthPin>(request);
|
||||
}
|
||||
|
||||
public async Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl)
|
||||
{
|
||||
var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get);
|
||||
await AddHeaders(request);
|
||||
|
||||
request.AddQueryString("pinID", pinId.ToString());
|
||||
request.AddQueryString("code", code);
|
||||
request.AddQueryString("context[device][product]", ApplicationName);
|
||||
request.AddQueryString("context[device][environment]", "bundled");
|
||||
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("#"))
|
||||
{
|
||||
var uri = request.FullUri.ToString();
|
||||
var withoutEnd = uri.Remove(uri.Length - 1, 1);
|
||||
var startOfQueryLocation = withoutEnd.IndexOf('?');
|
||||
var better = withoutEnd.Insert(startOfQueryLocation, "#");
|
||||
request.FullUri = new Uri(better);
|
||||
}
|
||||
|
||||
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>
|
||||
/// Adds the required headers and also the authorization header
|
||||
/// </summary>
|
||||
/// <param name="request"></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);
|
||||
AddHeaders(request);
|
||||
await AddHeaders(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the main required headers to the Plex Request
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
private void AddHeaders(Request request)
|
||||
private async Task AddHeaders(Request request)
|
||||
{
|
||||
request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3");
|
||||
request.AddHeader("X-Plex-Product", "Ombi");
|
||||
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-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.AddHeader("Accept", "application/json");
|
||||
}
|
||||
|
@ -176,5 +307,19 @@ namespace Ombi.Api.Plex
|
|||
request.AddHeader("X-Plex-Container-Start", from.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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ namespace Ombi.Api.Pushover
|
|||
{
|
||||
public interface IPushoverApi
|
||||
{
|
||||
Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken);
|
||||
Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Pushover.Models;
|
||||
|
@ -15,9 +16,13 @@ namespace Ombi.Api.Pushover
|
|||
private readonly IApi _api;
|
||||
private const string PushoverEndpoint = "https://api.pushover.net/1";
|
||||
|
||||
public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken)
|
||||
public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound)
|
||||
{
|
||||
var request = new Request($"messages.json?token={accessToken}&user={userToken}&message={message}", PushoverEndpoint, HttpMethod.Post);
|
||||
if (message.Contains("'"))
|
||||
{
|
||||
message = message.Replace("'", "'");
|
||||
}
|
||||
var request = new Request($"messages.json?token={accessToken}&user={userToken}&priority={priority}&sound={sound}&message={WebUtility.HtmlEncode(message)}", PushoverEndpoint, HttpMethod.Post);
|
||||
|
||||
var result = await _api.Request<PushoverResponse>(request);
|
||||
return result;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace Ombi.Api.Radarr
|
|||
tmdbId = tmdbId,
|
||||
qualityProfileId = qualityId,
|
||||
rootFolderPath = rootPath,
|
||||
titleSlug = title,
|
||||
titleSlug = title + year,
|
||||
monitored = true,
|
||||
year = year,
|
||||
minimumAvailability = minimumAvailability
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -6,6 +6,30 @@ namespace Ombi.Api.Sonarr.Models
|
|||
{
|
||||
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 episodeFileId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
|
@ -27,6 +51,24 @@ namespace Ombi.Api.Sonarr.Models
|
|||
|
||||
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 seasonNumber { get; set; }
|
||||
public string relativePath { get; set; }
|
||||
|
@ -41,12 +83,32 @@ namespace Ombi.Api.Sonarr.Models
|
|||
|
||||
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 Revision revision { get; set; }
|
||||
}
|
||||
|
||||
public class Revision
|
||||
{
|
||||
public Revision()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Revision(Revision r)
|
||||
{
|
||||
version = r.version;
|
||||
real = r.real;
|
||||
}
|
||||
public int version { get; set; }
|
||||
public int real { get; set; }
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Ombi.Api.Sonarr.Models
|
|||
public string cleanTitle { get; set; }
|
||||
public string imdbId { get; set; }
|
||||
public string titleSlug { get; set; }
|
||||
public string seriesType { get; set; }
|
||||
public int id { get; set; }
|
||||
public List<SonarrImage> images { get; set; }
|
||||
|
||||
|
|
|
@ -2,6 +2,16 @@ namespace Ombi.Api.Sonarr.Models
|
|||
{
|
||||
public class Quality
|
||||
{
|
||||
public Quality()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Quality(Quality q)
|
||||
{
|
||||
id = q.id;
|
||||
name = q.name;
|
||||
}
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@ namespace Ombi.Api
|
|||
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
}
|
||||
|
||||
if (request.Retry)
|
||||
{
|
||||
|
||||
|
@ -76,15 +80,20 @@ namespace Ombi.Api
|
|||
else
|
||||
{
|
||||
// XML
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(T));
|
||||
StringReader reader = new StringReader(receivedString);
|
||||
var value = (T)serializer.Deserialize(reader);
|
||||
return value;
|
||||
return DeserializeXml<T>(receivedString);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))
|
||||
|
@ -94,7 +103,10 @@ namespace Ombi.Api
|
|||
var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
}
|
||||
}
|
||||
// do something with the response
|
||||
var data = httpResponseMessage.Content;
|
||||
|
@ -112,7 +124,10 @@ namespace Ombi.Api
|
|||
var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@ namespace Ombi.Api
|
|||
Task Request(Request request);
|
||||
Task<T> Request<T>(Request request);
|
||||
Task<string> RequestContent(Request request);
|
||||
T DeserializeXml<T>(string receivedString);
|
||||
}
|
||||
}
|
|
@ -9,9 +9,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Polly" Version="5.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Polly" Version="6.1.0" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -25,9 +25,10 @@ namespace Ombi.Api
|
|||
public string Endpoint { get; }
|
||||
public string BaseUrl { get; }
|
||||
public HttpMethod HttpMethod { get; }
|
||||
|
||||
public bool IgnoreErrors { get; set; }
|
||||
public bool Retry { get; set; }
|
||||
public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>();
|
||||
public bool IgnoreBaseUrlAppend { get; set; }
|
||||
|
||||
public Action<string> OnBeforeDeserialization { get; set; }
|
||||
|
||||
|
@ -38,7 +39,7 @@ namespace Ombi.Api
|
|||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(BaseUrl))
|
||||
{
|
||||
sb.Append(!BaseUrl.EndsWith("/") ? string.Format("{0}/", BaseUrl) : BaseUrl);
|
||||
sb.Append(!BaseUrl.EndsWith("/") && !IgnoreBaseUrlAppend ? string.Format("{0}/", BaseUrl) : BaseUrl);
|
||||
}
|
||||
sb.Append(Endpoint.StartsWith("/") ? Endpoint.Remove(0, 1) : Endpoint);
|
||||
return sb.ToString();
|
||||
|
@ -105,10 +106,10 @@ namespace Ombi.Api
|
|||
hasQuery = true;
|
||||
startingTag = builder.Query.Contains("?") ? "&" : "?";
|
||||
}
|
||||
|
||||
builder.Query = hasQuery
|
||||
? $"{builder.Query}{startingTag}{key}={value}"
|
||||
: $"{startingTag}{key}={value}";
|
||||
|
||||
_modified = builder.Uri;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.7.99" />
|
||||
<PackageReference Include="Nunit" Version="3.8.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.6.1"></packagereference>
|
||||
<PackageReference Include="Moq" Version="4.9.0" />
|
||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -25,11 +25,14 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
[Test]
|
||||
public async Task Movie_ShouldBe_Available_WhenFoundInEmby()
|
||||
{
|
||||
ContextMock.Setup(x => x.Get(It.IsAny<string>())).ReturnsAsync(new EmbyContent
|
||||
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
|
||||
{
|
||||
ProviderId = "123"
|
||||
});
|
||||
var search = new SearchMovieViewModel();
|
||||
var search = new SearchMovieViewModel()
|
||||
{
|
||||
TheMovieDbId = "123",
|
||||
};
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
Assert.True(result.Success);
|
||||
|
@ -39,7 +42,7 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
[Test]
|
||||
public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInEmby()
|
||||
{
|
||||
ContextMock.Setup(x => x.Get(It.IsAny<string>())).Returns(Task.FromResult(default(EmbyContent)));
|
||||
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).Returns(Task.FromResult(default(EmbyContent)));
|
||||
var search = new SearchMovieViewModel();
|
||||
var result = await Rule.Execute(search);
|
||||
|
||||
|
|
|
@ -19,12 +19,14 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
|
||||
MovieMock = new Mock<IMovieRequestRepository>();
|
||||
TvMock = new Mock<ITvRequestRepository>();
|
||||
Rule = new ExistingRule(MovieMock.Object, TvMock.Object);
|
||||
MusicMock = new Mock<IMusicRequestRepository>();
|
||||
Rule = new ExistingRule(MovieMock.Object, TvMock.Object, MusicMock.Object);
|
||||
}
|
||||
|
||||
private ExistingRule Rule { get; set; }
|
||||
private Mock<IMovieRequestRepository> MovieMock { get; set; }
|
||||
private Mock<ITvRequestRepository> TvMock { get; set; }
|
||||
private Mock<IMusicRequestRepository> MusicMock { get; set; }
|
||||
|
||||
|
||||
[Test]
|
||||
|
|
26
src/Ombi.Core.Tests/StringHelperTests.cs
Normal file
26
src/Ombi.Core.Tests/StringHelperTests.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Core.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StringHelperTests
|
||||
{
|
||||
[TestCaseSource(nameof(StripCharsData))]
|
||||
public string StripCharacters(string str, char[] chars)
|
||||
{
|
||||
return str.StripCharacters(chars);
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> StripCharsData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new TestCaseData("this!is^a*string",new []{'!','^','*'}).Returns("thisisastring").SetName("Basic Strip Multipe Chars");
|
||||
yield return new TestCaseData("What is this madness'",new []{'\'','^','*'}).Returns("What is this madness").SetName("Basic Strip Multipe Chars");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
src/Ombi.Core/Authentication/PlexOAuthManager.cs
Normal file
53
src/Ombi.Core/Authentication/PlexOAuthManager.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
||||
namespace Ombi.Core.Authentication
|
||||
{
|
||||
public class PlexOAuthManager : IPlexOAuthManager
|
||||
{
|
||||
public PlexOAuthManager(IPlexApi api, ISettingsService<CustomizationSettings> settings)
|
||||
{
|
||||
_api = api;
|
||||
_customizationSettingsService = settings;
|
||||
}
|
||||
|
||||
private readonly IPlexApi _api;
|
||||
private readonly ISettingsService<CustomizationSettings> _customizationSettingsService;
|
||||
|
||||
public async Task<string> GetAccessTokenFromPin(int pinId)
|
||||
{
|
||||
var pin = await _api.GetPin(pinId);
|
||||
if (pin.expiresAt < DateTime.UtcNow)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return pin.authToken;
|
||||
}
|
||||
|
||||
public async Task<PlexAccount> GetAccount(string accessToken)
|
||||
{
|
||||
return await _api.GetAccount(accessToken);
|
||||
}
|
||||
|
||||
public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null)
|
||||
{
|
||||
var settings = await _customizationSettingsService.GetSettingsAsync();
|
||||
var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public async Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress)
|
||||
{
|
||||
var url = await _api.GetOAuthUrl(pinId, code, websiteAddress);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,13 +9,12 @@ using System.Threading.Tasks;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
using Ombi.Store.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -26,18 +25,21 @@ namespace Ombi.Core.Engine
|
|||
private Dictionary<int, TvRequests> _dbTv;
|
||||
|
||||
protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService,
|
||||
IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings) : base(identity, um, rules)
|
||||
IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub) : base(identity, um, rules)
|
||||
{
|
||||
RequestService = requestService;
|
||||
Cache = cache;
|
||||
OmbiSettings = ombiSettings;
|
||||
_subscriptionRepository = sub;
|
||||
}
|
||||
|
||||
protected IRequestServiceMain RequestService { get; }
|
||||
protected IMovieRequestRepository MovieRepository => RequestService.MovieRequestService;
|
||||
protected ITvRequestRepository TvRepository => RequestService.TvRequestService;
|
||||
protected IMusicRequestRepository MusicRepository => RequestService.MusicRequestRepository;
|
||||
protected readonly ICacheService Cache;
|
||||
protected readonly ISettingsService<OmbiSettings> OmbiSettings;
|
||||
protected readonly IRepository<RequestSubscription> _subscriptionRepository;
|
||||
|
||||
protected async Task<Dictionary<int, MovieRequests>> GetMovieRequests()
|
||||
{
|
||||
|
@ -78,7 +80,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
var pendingTv = 0;
|
||||
var approvedTv = 0;
|
||||
var availableTv = 0;
|
||||
var availableTv = 0;
|
||||
foreach (var tv in tvQuery)
|
||||
{
|
||||
foreach (var child in tv.ChildRequests)
|
||||
|
@ -108,23 +110,53 @@ namespace Ombi.Core.Engine
|
|||
|
||||
protected async Task<HideResult> HideFromOtherUsers()
|
||||
{
|
||||
if (await IsInRole(OmbiRoles.Admin) || await IsInRole(OmbiRoles.PowerUser))
|
||||
var user = await GetUser();
|
||||
if (await IsInRole(OmbiRoles.Admin) || await IsInRole(OmbiRoles.PowerUser) || user.IsSystemUser)
|
||||
{
|
||||
return new HideResult();
|
||||
return new HideResult
|
||||
{
|
||||
UserId = user.Id
|
||||
};
|
||||
}
|
||||
var settings = await Cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await OmbiSettings.GetSettingsAsync());
|
||||
var result = new HideResult
|
||||
{
|
||||
Hide = settings.HideRequestsUsers
|
||||
Hide = settings.HideRequestsUsers,
|
||||
UserId = user.Id
|
||||
};
|
||||
if (settings.HideRequestsUsers)
|
||||
{
|
||||
var user = await GetUser();
|
||||
result.UserId = user.Id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task SubscribeToRequest(int requestId, RequestType type)
|
||||
{
|
||||
var user = await GetUser();
|
||||
var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x =>
|
||||
x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type);
|
||||
if (existingSub != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var sub = new RequestSubscription
|
||||
{
|
||||
UserId = user.Id,
|
||||
RequestId = requestId,
|
||||
RequestType = type
|
||||
};
|
||||
|
||||
await _subscriptionRepository.Add(sub);
|
||||
}
|
||||
|
||||
public async Task UnSubscribeRequest(int requestId, RequestType type)
|
||||
{
|
||||
var user = await GetUser();
|
||||
var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x =>
|
||||
x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type);
|
||||
if (existingSub != null)
|
||||
{
|
||||
await _subscriptionRepository.Delete(existingSub);
|
||||
}
|
||||
}
|
||||
|
||||
public class HideResult
|
||||
{
|
||||
public bool Hide { get; set; }
|
||||
|
|
27
src/Ombi.Core/Engine/IMusicRequestEngine.cs
Normal file
27
src/Ombi.Core/Engine/IMusicRequestEngine.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public interface IMusicRequestEngine
|
||||
{
|
||||
Task<RequestEngineResult>ApproveAlbum(AlbumRequest request);
|
||||
Task<RequestEngineResult> ApproveAlbumById(int requestId);
|
||||
Task<RequestEngineResult> DenyAlbumById(int modelId);
|
||||
Task<IEnumerable<AlbumRequest>> GetRequests();
|
||||
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, OrderFilterModel orderFilter);
|
||||
Task<int> GetTotal();
|
||||
Task<RequestEngineResult> MarkAvailable(int modelId);
|
||||
Task<RequestEngineResult> MarkUnavailable(int modelId);
|
||||
Task RemoveAlbumRequest(int requestId);
|
||||
Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
|
||||
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
|
||||
Task<bool> UserHasRequest(string userId);
|
||||
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
|
||||
}
|
||||
}
|
|
@ -32,14 +32,7 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
private OmbiUser _user;
|
||||
protected async Task<OmbiUser> GetUser()
|
||||
{
|
||||
if (IsApiUser)
|
||||
{
|
||||
return new OmbiUser
|
||||
{
|
||||
UserName = Username,
|
||||
};
|
||||
}
|
||||
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username));
|
||||
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName.Equals(Username, StringComparison.CurrentCultureIgnoreCase)));
|
||||
}
|
||||
|
||||
protected async Task<string> UserAlias()
|
||||
|
@ -49,10 +42,6 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
|
||||
protected async Task<bool> IsInRole(string roleName)
|
||||
{
|
||||
if (IsApiUser && roleName != OmbiRoles.Disabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return await UserManager.IsInRoleAsync(await GetUser(), roleName);
|
||||
}
|
||||
|
||||
|
@ -72,7 +61,5 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
var ruleResults = await Rules.StartSpecificRules(model, rule);
|
||||
return ruleResults;
|
||||
}
|
||||
|
||||
private bool IsApiUser => Username.Equals("Api", StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
|
@ -17,6 +17,5 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
||||
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
||||
Task<RequestEngineResult> DenyMovieById(int modelId);
|
||||
Task<IEnumerable<MovieRequests>> Filter(FilterViewModel vm);
|
||||
}
|
||||
}
|
16
src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
Normal file
16
src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Core.Models.Search;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public interface IMusicSearchEngine
|
||||
{
|
||||
Task<ArtistResult> GetAlbumArtist(string foreignArtistId);
|
||||
Task<ArtistResult> GetArtist(int artistId);
|
||||
Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId);
|
||||
Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search);
|
||||
Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Engine.Interfaces
|
||||
{
|
||||
|
@ -11,11 +14,15 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
//Task<IEnumerable<T>> GetNewRequests();
|
||||
//Task<IEnumerable<T>> GetAvailableRequests();
|
||||
RequestCountModel RequestCount();
|
||||
Task<IEnumerable<T>> GetRequests(int count, int position);
|
||||
Task<RequestsViewModel<T>> GetRequests(int count, int position, OrderFilterModel model);
|
||||
Task<IEnumerable<T>> GetRequests();
|
||||
Task<bool> UserHasRequest(string userId);
|
||||
|
||||
Task<RequestEngineResult> MarkUnavailable(int modelId);
|
||||
Task<RequestEngineResult> MarkAvailable(int modelId);
|
||||
Task<int> GetTotal();
|
||||
Task UnSubscribeRequest(int requestId, RequestType type);
|
||||
Task SubscribeToRequest(int requestId, RequestType type);
|
||||
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Engine.Interfaces
|
||||
|
@ -10,15 +10,18 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
{
|
||||
|
||||
Task RemoveTvRequest(int requestId);
|
||||
Task<TvRequests> GetTvRequest(int requestId);
|
||||
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
|
||||
Task<RequestEngineResult> DenyChildRequest(int requestId);
|
||||
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
||||
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
||||
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search);
|
||||
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
||||
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position);
|
||||
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
|
||||
Task<ChildRequests> UpdateChildRequest(ChildRequests request);
|
||||
Task RemoveTvChild(int requestId);
|
||||
Task<RequestEngineResult> ApproveChildRequest(int id);
|
||||
Task<IEnumerable<TvRequests>> GetRequestsLite();
|
||||
Task UpdateQualityProfile(int requestId, int profileId);
|
||||
Task UpdateRootPath(int requestId, int rootPath);
|
||||
}
|
||||
}
|
|
@ -7,16 +7,10 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
public interface ITvSearchEngine
|
||||
{
|
||||
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<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Popular();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> MostWatches();
|
||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree();
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Trending();
|
||||
}
|
||||
}
|
9
src/Ombi.Core/Engine/Interfaces/IUserStatsEngine.cs
Normal file
9
src/Ombi.Core/Engine/Interfaces/IUserStatsEngine.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public interface IUserStatsEngine
|
||||
{
|
||||
Task<UserStatsSummary> GetSummary(SummaryRequest request);
|
||||
}
|
||||
}
|
|
@ -13,11 +13,13 @@ using Microsoft.Extensions.Logging;
|
|||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Core.Models;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -25,7 +27,9 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
|
||||
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings) : base(user, requestService, r, manager, cache, ombiSettings)
|
||||
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
|
||||
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub)
|
||||
: base(user, requestService, r, manager, cache, ombiSettings, sub)
|
||||
{
|
||||
MovieApi = movieApi;
|
||||
NotificationHelper = helper;
|
||||
|
@ -57,6 +61,7 @@ namespace Ombi.Core.Engine
|
|||
ErrorMessage = $"Please try again later"
|
||||
};
|
||||
}
|
||||
|
||||
var fullMovieName =
|
||||
$"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}";
|
||||
|
||||
|
@ -81,7 +86,8 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
|
||||
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
requestModel.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
|
||||
requestModel.DigitalReleaseDate = usDates?.ReleaseDate
|
||||
?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
|
||||
|
||||
var ruleResults = (await RunRequestRules(requestModel)).ToList();
|
||||
if (ruleResults.Any(x => !x.Success))
|
||||
|
@ -124,24 +130,106 @@ namespace Ombi.Core.Engine
|
|||
/// </summary>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="orderFilter">The order/filter type.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<MovieRequests>> GetRequests(int count, int position)
|
||||
public async Task<RequestsViewModel<MovieRequests>> GetRequests(int count, int position,
|
||||
OrderFilterModel orderFilter)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<MovieRequests> allRequests;
|
||||
IQueryable<MovieRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).Skip(position).Take(count).ToListAsync();
|
||||
allRequests =
|
||||
MovieRepository.GetWithUser(shouldHide
|
||||
.UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).ToListAsync();
|
||||
allRequests =
|
||||
MovieRepository
|
||||
.GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync();
|
||||
}
|
||||
allRequests.ForEach(x =>
|
||||
|
||||
switch (orderFilter.AvailabilityFilter)
|
||||
{
|
||||
case FilterType.None:
|
||||
break;
|
||||
case FilterType.Available:
|
||||
allRequests = allRequests.Where(x => x.Available);
|
||||
break;
|
||||
case FilterType.NotAvailable:
|
||||
allRequests = allRequests.Where(x => !x.Available);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
switch (orderFilter.StatusFilter)
|
||||
{
|
||||
case FilterType.None:
|
||||
break;
|
||||
case FilterType.Approved:
|
||||
allRequests = allRequests.Where(x => x.Approved);
|
||||
break;
|
||||
case FilterType.Processing:
|
||||
allRequests = allRequests.Where(x => x.Approved && !x.Available);
|
||||
break;
|
||||
case FilterType.PendingApproval:
|
||||
allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
var total = allRequests.Count();
|
||||
|
||||
var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count)
|
||||
.ToListAsync();
|
||||
|
||||
requests.ForEach(async x =>
|
||||
{
|
||||
x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
|
||||
await CheckForSubscription(shouldHide, x);
|
||||
});
|
||||
return allRequests;
|
||||
return new RequestsViewModel<MovieRequests>
|
||||
{
|
||||
Collection = requests,
|
||||
Total = total
|
||||
};
|
||||
}
|
||||
|
||||
private IQueryable<MovieRequests> OrderMovies(IQueryable<MovieRequests> allRequests, OrderType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OrderType.RequestedDateAsc:
|
||||
return allRequests.OrderBy(x => x.RequestedDate);
|
||||
case OrderType.RequestedDateDesc:
|
||||
return allRequests.OrderByDescending(x => x.RequestedDate);
|
||||
case OrderType.TitleAsc:
|
||||
return allRequests.OrderBy(x => x.Title);
|
||||
case OrderType.TitleDesc:
|
||||
return allRequests.OrderByDescending(x => x.Title);
|
||||
case OrderType.StatusAsc:
|
||||
return allRequests.OrderBy(x => x.Status);
|
||||
case OrderType.StatusDesc:
|
||||
return allRequests.OrderByDescending(x => x.Status);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> GetTotal()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
return await MovieRepository.GetWithUser(shouldHide.UserId).CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await MovieRepository.GetWithUser().CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -160,9 +248,30 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async x =>
|
||||
{
|
||||
x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
|
||||
await CheckForSubscription(shouldHide, x);
|
||||
});
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(HideResult shouldHide, MovieRequests x)
|
||||
{
|
||||
if (shouldHide.UserId == x.RequestedUserId)
|
||||
{
|
||||
x.ShowSubscribe = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
x.ShowSubscribe = true;
|
||||
var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s =>
|
||||
s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Movie);
|
||||
x.Subscribed = sub != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the movie request.
|
||||
/// </summary>
|
||||
|
@ -180,10 +289,12 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
||||
}
|
||||
|
||||
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
|
||||
results.ForEach(x =>
|
||||
results.ForEach(async x =>
|
||||
{
|
||||
x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
|
||||
await CheckForSubscription(shouldHide, x);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
@ -204,6 +315,7 @@ namespace Ombi.Core.Engine
|
|||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.Denied = true;
|
||||
// We are denying a request
|
||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||
|
@ -224,6 +336,8 @@ namespace Ombi.Core.Engine
|
|||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.MarkedAsApproved = DateTime.Now;
|
||||
request.Approved = true;
|
||||
request.Denied = false;
|
||||
await MovieRepository.Update(request);
|
||||
|
@ -244,6 +358,7 @@ namespace Ombi.Core.Engine
|
|||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message);
|
||||
|
@ -254,6 +369,7 @@ namespace Ombi.Core.Engine
|
|||
Result = false
|
||||
};
|
||||
}
|
||||
|
||||
// If there are no providers then it's successful but movie has not been sent
|
||||
}
|
||||
|
||||
|
@ -315,6 +431,7 @@ namespace Ombi.Core.Engine
|
|||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.Available = false;
|
||||
await MovieRepository.Update(request);
|
||||
|
||||
|
@ -335,7 +452,9 @@ namespace Ombi.Core.Engine
|
|||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
NotificationHelper.Notify(request, NotificationType.RequestAvailable);
|
||||
await MovieRepository.Update(request);
|
||||
|
||||
|
@ -364,45 +483,51 @@ namespace Ombi.Core.Engine
|
|||
RequestType = RequestType.Movie,
|
||||
});
|
||||
|
||||
return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" };
|
||||
return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MovieRequests>> Filter(FilterViewModel vm)
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
var requests = shouldHide.Hide ? MovieRepository.GetWithUser(shouldHide.UserId) : MovieRepository.GetWithUser();
|
||||
switch (vm.AvailabilityFilter)
|
||||
if (user == null)
|
||||
{
|
||||
case FilterType.None:
|
||||
break;
|
||||
case FilterType.Available:
|
||||
requests = requests.Where(x => x.Available);
|
||||
break;
|
||||
case FilterType.NotAvailable:
|
||||
requests = requests.Where(x => !x.Available);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
user = await GetUser();
|
||||
|
||||
// If user is still null after attempting to get the logged in user, return null.
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch (vm.StatusFilter)
|
||||
int limit = user.MovieRequestLimit ?? 0;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
case FilterType.None:
|
||||
break;
|
||||
case FilterType.Approved:
|
||||
requests = requests.Where(x => x.Approved);
|
||||
break;
|
||||
case FilterType.Processing:
|
||||
requests = requests.Where(x => x.Approved && !x.Available);
|
||||
break;
|
||||
case FilterType.PendingApproval:
|
||||
requests = requests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = false,
|
||||
Limit = 0,
|
||||
Remaining = 0,
|
||||
NextRequest = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
return requests;
|
||||
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Movie);
|
||||
|
||||
int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
|
||||
DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
|
||||
.OrderBy(x => x.RequestDate)
|
||||
.Select(x => x.RequestDate)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = true,
|
||||
Limit = limit,
|
||||
Remaining = count,
|
||||
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,20 +9,23 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class MovieSearchEngine : BaseMediaEngine, IMovieEngine
|
||||
{
|
||||
public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
||||
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s)
|
||||
: base(identity, service, r, um, mem, s)
|
||||
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub)
|
||||
: base(identity, service, r, um, mem, s, sub)
|
||||
{
|
||||
MovieApi = movApi;
|
||||
Mapper = mapper;
|
||||
|
@ -57,7 +60,6 @@ namespace Ombi.Core.Engine
|
|||
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
|
@ -88,7 +90,6 @@ namespace Ombi.Core.Engine
|
|||
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
|
@ -103,7 +104,6 @@ namespace Ombi.Core.Engine
|
|||
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
|
@ -133,7 +133,6 @@ namespace Ombi.Core.Engine
|
|||
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
|
@ -165,9 +164,30 @@ namespace Ombi.Core.Engine
|
|||
|
||||
await RunSearchRules(viewMovie);
|
||||
|
||||
// This requires the rules to be run first to populate the RequestId property
|
||||
await CheckForSubscription(viewMovie);
|
||||
|
||||
return viewMovie;
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(SearchMovieViewModel viewModel)
|
||||
{
|
||||
// Check if this user requested it
|
||||
var user = await GetUser();
|
||||
var request = await RequestService.MovieRequestService.GetAll()
|
||||
.AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id);
|
||||
if (request)
|
||||
{
|
||||
viewModel.ShowSubscribe = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel.ShowSubscribe = true;
|
||||
var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => s.UserId == user.Id
|
||||
&& s.RequestId == viewModel.RequestId && s.RequestType == RequestType.Movie);
|
||||
viewModel.Subscribed = sub != null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie)
|
||||
{
|
||||
|
|
503
src/Ombi.Core/Engine/MusicRequestEngine.cs
Normal file
503
src/Ombi.Core/Engine/MusicRequestEngine.cs
Normal file
|
@ -0,0 +1,503 @@
|
|||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Senders;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class MusicRequestEngine : BaseMediaEngine, IMusicRequestEngine
|
||||
{
|
||||
public MusicRequestEngine(IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationHelper helper, IRuleEvaluator r, ILogger<MusicRequestEngine> log,
|
||||
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
|
||||
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, ILidarrApi lidarr,
|
||||
ISettingsService<LidarrSettings> lidarrSettings, IMusicSender sender)
|
||||
: base(user, requestService, r, manager, cache, ombiSettings, sub)
|
||||
{
|
||||
NotificationHelper = helper;
|
||||
_musicSender = sender;
|
||||
Logger = log;
|
||||
_requestLog = rl;
|
||||
_lidarrApi = lidarr;
|
||||
_lidarrSettings = lidarrSettings;
|
||||
}
|
||||
|
||||
private INotificationHelper NotificationHelper { get; }
|
||||
//private IMovieSender Sender { get; }
|
||||
private ILogger Logger { get; }
|
||||
private readonly IRepository<RequestLog> _requestLog;
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly IMusicSender _musicSender;
|
||||
|
||||
/// <summary>
|
||||
/// Requests the Album.
|
||||
/// </summary>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model)
|
||||
{
|
||||
var s = await _lidarrSettings.GetSettingsAsync();
|
||||
var album = await _lidarrApi.GetAlbumByForeignId(model.ForeignAlbumId, s.ApiKey, s.FullUri);
|
||||
if (album == null)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = false,
|
||||
Message = "There was an issue adding this album!",
|
||||
ErrorMessage = "Please try again later"
|
||||
};
|
||||
}
|
||||
|
||||
var userDetails = await GetUser();
|
||||
|
||||
var requestModel = new AlbumRequest
|
||||
{
|
||||
ForeignAlbumId = model.ForeignAlbumId,
|
||||
ArtistName = album.artist?.artistName,
|
||||
ReleaseDate = album.releaseDate,
|
||||
RequestedDate = DateTime.Now,
|
||||
RequestType = RequestType.Album,
|
||||
Rating = album.ratings?.value ?? 0m,
|
||||
RequestedUserId = userDetails.Id,
|
||||
Title = album.title,
|
||||
Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
|
||||
Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url,
|
||||
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty
|
||||
};
|
||||
if (requestModel.Cover.IsNullOrEmpty())
|
||||
{
|
||||
requestModel.Cover = album.remoteCover;
|
||||
}
|
||||
|
||||
var ruleResults = (await RunRequestRules(requestModel)).ToList();
|
||||
if (ruleResults.Any(x => !x.Success))
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = ruleResults.FirstOrDefault(x => x.Message.HasValue()).Message
|
||||
};
|
||||
}
|
||||
|
||||
if (requestModel.Approved) // The rules have auto approved this
|
||||
{
|
||||
var requestEngineResult = await AddAlbumRequest(requestModel);
|
||||
if (requestEngineResult.Result)
|
||||
{
|
||||
var result = await ApproveAlbum(requestModel);
|
||||
if (result.IsError)
|
||||
{
|
||||
Logger.LogWarning("Tried auto sending Album but failed. Message: {0}", result.Message);
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Message = result.Message,
|
||||
ErrorMessage = result.Message,
|
||||
Result = false
|
||||
};
|
||||
}
|
||||
|
||||
return requestEngineResult;
|
||||
}
|
||||
|
||||
// If there are no providers then it's successful but album has not been sent
|
||||
}
|
||||
|
||||
return await AddAlbumRequest(requestModel);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the requests.
|
||||
/// </summary>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="orderFilter">The order/filter type.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position,
|
||||
OrderFilterModel orderFilter)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
IQueryable<AlbumRequest> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests =
|
||||
MusicRepository.GetWithUser(shouldHide
|
||||
.UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests =
|
||||
MusicRepository
|
||||
.GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync();
|
||||
}
|
||||
|
||||
switch (orderFilter.AvailabilityFilter)
|
||||
{
|
||||
case FilterType.None:
|
||||
break;
|
||||
case FilterType.Available:
|
||||
allRequests = allRequests.Where(x => x.Available);
|
||||
break;
|
||||
case FilterType.NotAvailable:
|
||||
allRequests = allRequests.Where(x => !x.Available);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
switch (orderFilter.StatusFilter)
|
||||
{
|
||||
case FilterType.None:
|
||||
break;
|
||||
case FilterType.Approved:
|
||||
allRequests = allRequests.Where(x => x.Approved);
|
||||
break;
|
||||
case FilterType.Processing:
|
||||
allRequests = allRequests.Where(x => x.Approved && !x.Available);
|
||||
break;
|
||||
case FilterType.PendingApproval:
|
||||
allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
var total = allRequests.Count();
|
||||
|
||||
var requests = await (OrderAlbums(allRequests, orderFilter.OrderType)).Skip(position).Take(count)
|
||||
.ToListAsync();
|
||||
|
||||
requests.ForEach(async x =>
|
||||
{
|
||||
await CheckForSubscription(shouldHide, x);
|
||||
});
|
||||
return new RequestsViewModel<AlbumRequest>
|
||||
{
|
||||
Collection = requests,
|
||||
Total = total
|
||||
};
|
||||
}
|
||||
|
||||
private IQueryable<AlbumRequest> OrderAlbums(IQueryable<AlbumRequest> allRequests, OrderType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OrderType.RequestedDateAsc:
|
||||
return allRequests.OrderBy(x => x.RequestedDate);
|
||||
case OrderType.RequestedDateDesc:
|
||||
return allRequests.OrderByDescending(x => x.RequestedDate);
|
||||
case OrderType.TitleAsc:
|
||||
return allRequests.OrderBy(x => x.Title);
|
||||
case OrderType.TitleDesc:
|
||||
return allRequests.OrderByDescending(x => x.Title);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> GetTotal()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
return await MusicRepository.GetWithUser(shouldHide.UserId).CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await MusicRepository.GetWithUser().CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the requests.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<AlbumRequest>> GetRequests()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<AlbumRequest> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await MusicRepository.GetWithUser(shouldHide.UserId).ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await MusicRepository.GetWithUser().ToListAsync();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async x =>
|
||||
{
|
||||
await CheckForSubscription(shouldHide, x);
|
||||
});
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(HideResult shouldHide, AlbumRequest x)
|
||||
{
|
||||
if (shouldHide.UserId == x.RequestedUserId)
|
||||
{
|
||||
x.ShowSubscribe = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
x.ShowSubscribe = true;
|
||||
var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s =>
|
||||
s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Album);
|
||||
x.Subscribed = sub != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the album request.
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<AlbumRequest> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = await MusicRepository.GetWithUser(shouldHide.UserId).ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await MusicRepository.GetWithUser().ToListAsync();
|
||||
}
|
||||
|
||||
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
|
||||
results.ForEach(async x =>
|
||||
{
|
||||
await CheckForSubscription(shouldHide, x);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> ApproveAlbumById(int requestId)
|
||||
{
|
||||
var request = await MusicRepository.Find(requestId);
|
||||
return await ApproveAlbum(request);
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> DenyAlbumById(int modelId)
|
||||
{
|
||||
var request = await MusicRepository.Find(modelId);
|
||||
if (request == null)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.Denied = true;
|
||||
// We are denying a request
|
||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||
await MusicRepository.Update(request);
|
||||
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Message = "Request successfully deleted",
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> ApproveAlbum(AlbumRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.MarkedAsApproved = DateTime.Now;
|
||||
request.Approved = true;
|
||||
request.Denied = false;
|
||||
await MusicRepository.Update(request);
|
||||
|
||||
|
||||
var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification);
|
||||
if (canNotify.Success)
|
||||
{
|
||||
NotificationHelper.Notify(request, NotificationType.RequestApproved);
|
||||
}
|
||||
|
||||
if (request.Approved)
|
||||
{
|
||||
var result = await _musicSender.Send(request);
|
||||
if (result.Success && result.Sent)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
Logger.LogWarning("Tried auto sending album but failed. Message: {0}", result.Message);
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Message = result.Message,
|
||||
ErrorMessage = result.Message,
|
||||
Result = false
|
||||
};
|
||||
}
|
||||
|
||||
// If there are no providers then it's successful but movie has not been sent
|
||||
}
|
||||
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Album request.
|
||||
/// </summary>
|
||||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
public async Task RemoveAlbumRequest(int requestId)
|
||||
{
|
||||
var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
await MusicRepository.Delete(request);
|
||||
}
|
||||
|
||||
public async Task<bool> UserHasRequest(string userId)
|
||||
{
|
||||
return await MusicRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId);
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> MarkUnavailable(int modelId)
|
||||
{
|
||||
var request = await MusicRepository.Find(modelId);
|
||||
if (request == null)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.Available = false;
|
||||
await MusicRepository.Update(request);
|
||||
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Message = "Request is now unavailable",
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
user = await GetUser();
|
||||
|
||||
// If user is still null after attempting to get the logged in user, return null.
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int limit = user.MusicRequestLimit ?? 0;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = false,
|
||||
Limit = 0,
|
||||
Remaining = 0,
|
||||
NextRequest = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Album);
|
||||
|
||||
int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
|
||||
DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
|
||||
.OrderBy(x => x.RequestDate)
|
||||
.Select(x => x.RequestDate)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = true,
|
||||
Limit = limit,
|
||||
Remaining = count,
|
||||
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> MarkAvailable(int modelId)
|
||||
{
|
||||
var request = await MusicRepository.Find(modelId);
|
||||
if (request == null)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
NotificationHelper.Notify(request, NotificationType.RequestAvailable);
|
||||
await MusicRepository.Update(request);
|
||||
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Message = "Request is now available",
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddAlbumRequest(AlbumRequest model)
|
||||
{
|
||||
await MusicRepository.Add(model);
|
||||
|
||||
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
||||
if (result.Success)
|
||||
{
|
||||
NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.Album,
|
||||
});
|
||||
|
||||
return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!" };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
219
src/Ombi.Core/Engine/MusicSearchEngine.cs
Normal file
219
src/Ombi.Core/Engine/MusicSearchEngine.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class MusicSearchEngine : BaseMediaEngine, IMusicSearchEngine
|
||||
{
|
||||
public MusicSearchEngine(IPrincipal identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper,
|
||||
ILogger<MusicSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
|
||||
ISettingsService<LidarrSettings> lidarrSettings)
|
||||
: base(identity, service, r, um, mem, s, sub)
|
||||
{
|
||||
_lidarrApi = lidarrApi;
|
||||
_lidarrSettings = lidarrSettings;
|
||||
Mapper = mapper;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private IMapper Mapper { get; }
|
||||
private ILogger Logger { get; }
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Searches the specified album.
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.AlbumLookup(search, settings.ApiKey, settings.FullUri);
|
||||
var vm = new List<SearchAlbumViewModel>();
|
||||
foreach (var r in result)
|
||||
{
|
||||
vm.Add(await MapIntoAlbumVm(r, settings));
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the specified artist
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.ArtistLookup(search, settings.ApiKey, settings.FullUri);
|
||||
|
||||
var vm = new List<SearchArtistViewModel>();
|
||||
foreach (var r in result)
|
||||
{
|
||||
vm.Add(await MapIntoArtistVm(r));
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all albums by the specified artist
|
||||
/// </summary>
|
||||
/// <param name="foreignArtistId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.GetAlbumsByArtist(foreignArtistId);
|
||||
// We do not want any Singles (This will include EP's)
|
||||
var albumsOnly =
|
||||
result.Albums.Where(x => !x.Type.Equals("Single", StringComparison.InvariantCultureIgnoreCase));
|
||||
var vm = new List<SearchAlbumViewModel>();
|
||||
foreach (var album in albumsOnly)
|
||||
{
|
||||
vm.Add(await MapIntoAlbumVm(album, result.Id, result.ArtistName, settings));
|
||||
}
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the artist that produced the album
|
||||
/// </summary>
|
||||
/// <param name="foreignArtistId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<ArtistResult> GetAlbumArtist(string foreignArtistId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
return await _lidarrApi.GetArtistByForeignId(foreignArtistId, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
public async Task<ArtistResult> GetArtist(int artistId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
return await _lidarrApi.GetArtist(artistId, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
private async Task<SearchArtistViewModel> MapIntoArtistVm(ArtistLookup a)
|
||||
{
|
||||
var vm = new SearchArtistViewModel
|
||||
{
|
||||
ArtistName = a.artistName,
|
||||
ArtistType = a.artistType,
|
||||
Banner = a.images?.FirstOrDefault(x => x.coverType.Equals("banner"))?.url,
|
||||
Logo = a.images?.FirstOrDefault(x => x.coverType.Equals("logo"))?.url,
|
||||
CleanName = a.cleanName,
|
||||
Disambiguation = a.disambiguation,
|
||||
ForignArtistId = a.foreignArtistId,
|
||||
Links = a.links,
|
||||
Overview = a.overview,
|
||||
};
|
||||
|
||||
var poster = a.images?.FirstOrDefault(x => x.coverType.Equals("poaster"));
|
||||
if (poster == null)
|
||||
{
|
||||
vm.Poster = a.remotePoster;
|
||||
}
|
||||
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrArtist);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings)
|
||||
{
|
||||
var vm = new SearchAlbumViewModel
|
||||
{
|
||||
ForeignAlbumId = a.foreignAlbumId,
|
||||
Monitored = a.monitored,
|
||||
Rating = a.ratings?.value ?? 0m,
|
||||
ReleaseDate = a.releaseDate,
|
||||
Title = a.title,
|
||||
Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url
|
||||
};
|
||||
if (a.artistId > 0)
|
||||
{
|
||||
//TODO THEY HAVE FIXED THIS IN DEV
|
||||
// The JSON is different for some stupid reason
|
||||
// Need to lookup the artist now and all the images -.-"
|
||||
var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri);
|
||||
vm.ArtistName = artist.artistName;
|
||||
vm.ForeignArtistId = artist.foreignArtistId;
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.ForeignArtistId = a.artist?.foreignArtistId;
|
||||
vm.ArtistName = a.artist?.artistName;
|
||||
}
|
||||
|
||||
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url;
|
||||
if (vm.Cover.IsNullOrEmpty())
|
||||
{
|
||||
vm.Cover = a.remoteCover;
|
||||
}
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
|
||||
|
||||
await RunSearchRules(vm);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(Album a, string artistId, string artistName, LidarrSettings settings)
|
||||
{
|
||||
var fullAlbum = await _lidarrApi.GetAlbumByForeignId(a.Id, settings.ApiKey, settings.FullUri);
|
||||
var vm = new SearchAlbumViewModel
|
||||
{
|
||||
ForeignAlbumId = a.Id,
|
||||
Monitored = fullAlbum.monitored,
|
||||
Rating = fullAlbum.ratings?.value ?? 0m,
|
||||
ReleaseDate = fullAlbum.releaseDate,
|
||||
Title = a.Title,
|
||||
Disk = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
|
||||
ForeignArtistId = artistId,
|
||||
ArtistName = artistName,
|
||||
Cover = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url
|
||||
};
|
||||
|
||||
if (vm.Cover.IsNullOrEmpty())
|
||||
{
|
||||
vm.Cover = fullAlbum.remoteCover;
|
||||
}
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
|
||||
|
||||
await RunSearchRules(vm);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private LidarrSettings _settings;
|
||||
private async Task<LidarrSettings> GetSettings()
|
||||
{
|
||||
return _settings ?? (_settings = await _lidarrSettings.GetSettingsAsync());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,13 +63,17 @@ namespace Ombi.Core.Engine
|
|||
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||
foreach (var p in plexContent)
|
||||
{
|
||||
if (!p.HasTheMovieDb)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (p.Type == PlexMediaTypeEntity.Movie)
|
||||
{
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Plex,
|
||||
ContentId = p.Id,
|
||||
ContentId = int.Parse(p.TheMovieDbId),
|
||||
ContentType = ContentType.Parent
|
||||
});
|
||||
}
|
||||
|
@ -78,12 +82,18 @@ namespace Ombi.Core.Engine
|
|||
// Add the episodes
|
||||
foreach (var ep in p.Episodes)
|
||||
{
|
||||
if (!ep.Series.HasTvDb)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Plex,
|
||||
ContentId = ep.Id,
|
||||
ContentType = ContentType.Episode
|
||||
ContentId = int.Parse(ep.Series.TvDbId),
|
||||
ContentType = ContentType.Episode,
|
||||
EpisodeNumber = ep.EpisodeNumber,
|
||||
SeasonNumber = ep.SeasonNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -91,13 +101,17 @@ namespace Ombi.Core.Engine
|
|||
|
||||
foreach (var e in embyContent)
|
||||
{
|
||||
if (e.TheMovieDbId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (e.Type == EmbyMediaType.Movie)
|
||||
{
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Emby,
|
||||
ContentId = e.Id,
|
||||
ContentId = int.Parse(e.TheMovieDbId),
|
||||
ContentType = ContentType.Parent
|
||||
});
|
||||
}
|
||||
|
@ -106,12 +120,18 @@ namespace Ombi.Core.Engine
|
|||
// Add the episodes
|
||||
foreach (var ep in e.Episodes)
|
||||
{
|
||||
if (ep.Series.TvDbId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Emby,
|
||||
ContentId = ep.Id,
|
||||
ContentType = ContentType.Episode
|
||||
ContentId = int.Parse(ep.Series.TvDbId),
|
||||
ContentType = ContentType.Episode,
|
||||
EpisodeNumber = ep.EpisodeNumber,
|
||||
SeasonNumber = ep.SeasonNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +172,9 @@ namespace Ombi.Core.Engine
|
|||
model.Add(new RecentlyAddedMovieModel
|
||||
{
|
||||
Id = emby.Id,
|
||||
ImdbId = emby.ProviderId,
|
||||
ImdbId = emby.ImdbId,
|
||||
TheMovieDbId = emby.TheMovieDbId,
|
||||
TvDbId = emby.TvDbId,
|
||||
AddedAt = emby.AddedAt,
|
||||
Title = emby.Title,
|
||||
});
|
||||
|
@ -211,7 +233,9 @@ namespace Ombi.Core.Engine
|
|||
model.Add(new RecentlyAddedTvModel
|
||||
{
|
||||
Id = emby.Id,
|
||||
ImdbId = emby.ProviderId,
|
||||
ImdbId = emby.ImdbId,
|
||||
TvDbId = emby.TvDbId,
|
||||
TheMovieDbId = emby.TheMovieDbId,
|
||||
AddedAt = emby.AddedAt,
|
||||
Title = emby.Title,
|
||||
EpisodeNumber = episode.EpisodeNumber,
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Helpers;
|
||||
|
@ -14,6 +15,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Helpers;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Core.Rule;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Senders;
|
||||
|
@ -21,16 +23,19 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Core.Models;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
|
||||
{
|
||||
public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
|
||||
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache) : base(user, requestService, rule, manager, cache, settings)
|
||||
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
||||
IRepository<RequestSubscription> sub) : base(user, requestService, rule, manager, cache, settings, sub)
|
||||
{
|
||||
TvApi = tvApi;
|
||||
MovieDbApi = movApi;
|
||||
NotificationHelper = helper;
|
||||
TvSender = sender;
|
||||
Audit = audit;
|
||||
|
@ -39,6 +44,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
private INotificationHelper NotificationHelper { get; }
|
||||
private ITvMazeApi TvApi { get; }
|
||||
private IMovieDbApi MovieDbApi { get; }
|
||||
private ITvSender TvSender { get; }
|
||||
private IAuditRepository Audit { get; }
|
||||
private readonly IRepository<RequestLog> _requestLog;
|
||||
|
@ -47,7 +53,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
var user = await GetUser();
|
||||
|
||||
var tvBuilder = new TvShowRequestBuilder(TvApi);
|
||||
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi);
|
||||
(await tvBuilder
|
||||
.GetShowInfo(tv.TvDbId))
|
||||
.CreateTvList(tv)
|
||||
|
@ -128,7 +134,7 @@ namespace Ombi.Core.Engine
|
|||
return await AddRequest(newRequest.NewRequest);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TvRequests>> GetRequests(int count, int position)
|
||||
public async Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, OrderFilterModel type)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<TvRequests> allRequests;
|
||||
|
@ -138,6 +144,7 @@ namespace Ombi.Core.Engine
|
|||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault())
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
|
||||
// Filter out children
|
||||
|
@ -150,72 +157,143 @@ namespace Ombi.Core.Engine
|
|||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault())
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
|
||||
}
|
||||
|
||||
return allRequests;
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
|
||||
return new RequestsViewModel<TvRequests>
|
||||
{
|
||||
Collection = allRequests
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
|
||||
public async Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<TvRequests> allRequests = null;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
var tv = TvRepository.GetLite(shouldHide.UserId);
|
||||
if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
|
||||
{
|
||||
allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync();
|
||||
}
|
||||
|
||||
// Filter out children
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
var tv = TvRepository.GetLite();
|
||||
if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
|
||||
{
|
||||
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); });
|
||||
|
||||
return new RequestsViewModel<TvRequests>
|
||||
{
|
||||
Collection = allRequests
|
||||
};
|
||||
}
|
||||
public async Task<IEnumerable<TvRequests>> GetRequests()
|
||||
{
|
||||
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)
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
allRequests = await TvRepository.Get(shouldHide.UserId).ToListAsync();
|
||||
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = await TvRepository.Get()
|
||||
.Include(x => x.ChildRequests)
|
||||
.ThenInclude(x => x.SeasonRequests)
|
||||
.ThenInclude(x => x.Episodes)
|
||||
.Skip(position).Take(count).ToListAsync();
|
||||
allRequests = await TvRepository.Get().ToListAsync();
|
||||
}
|
||||
return ParseIntoTreeNode(allRequests);
|
||||
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TvRequests>> GetRequests()
|
||||
|
||||
public async Task<IEnumerable<TvRequests>> GetRequestsLite()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
IQueryable<TvRequests> allRequests;
|
||||
List<TvRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||
allRequests = await TvRepository.GetLite(shouldHide.UserId).ToListAsync();
|
||||
|
||||
FilterChildren(allRequests, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = TvRepository.Get();
|
||||
allRequests = await TvRepository.GetLite().ToListAsync();
|
||||
}
|
||||
|
||||
return await allRequests.ToListAsync();
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
public async Task<TvRequests> GetTvRequest(int requestId)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
TvRequests request;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
request = await TvRepository.Get(shouldHide.UserId).Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||
|
||||
FilterChildren(request, shouldHide);
|
||||
}
|
||||
else
|
||||
{
|
||||
request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
await CheckForSubscription(shouldHide, request);
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide)
|
||||
{
|
||||
if (allRequests == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Filter out children
|
||||
foreach (var t in allRequests)
|
||||
{
|
||||
for (var j = 0; j < t.ChildRequests.Count; j++)
|
||||
{
|
||||
var child = t.ChildRequests[j];
|
||||
if (child.RequestedUserId != shouldHide.UserId)
|
||||
{
|
||||
t.ChildRequests.RemoveAt(j);
|
||||
j--;
|
||||
}
|
||||
FilterChildren(t, shouldHide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FilterChildren(TvRequests t, HideResult shouldHide)
|
||||
{
|
||||
// Filter out children
|
||||
|
||||
for (var j = 0; j < t.ChildRequests.Count; j++)
|
||||
{
|
||||
var child = t.ChildRequests[j];
|
||||
if (child.RequestedUserId != shouldHide.UserId)
|
||||
{
|
||||
t.ChildRequests.RemoveAt(j);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
|
@ -229,6 +307,8 @@ namespace Ombi.Core.Engine
|
|||
allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
||||
}
|
||||
|
||||
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
|
||||
return allRequests;
|
||||
}
|
||||
|
||||
|
@ -245,23 +325,27 @@ namespace Ombi.Core.Engine
|
|||
allRequests = TvRepository.Get();
|
||||
}
|
||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||
|
||||
results.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
|
||||
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();
|
||||
IQueryable<TvRequests> allRequests;
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
allRequests = TvRepository.Get();
|
||||
}
|
||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||
return ParseIntoTreeNode(results);
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
results.RootFolder = rootPath;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
|
||||
public async Task UpdateQualityProfile(int requestId, int profileId)
|
||||
{
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
results.QualityOverride = profileId;
|
||||
|
||||
await TvRepository.Update(results);
|
||||
}
|
||||
|
||||
public async Task<TvRequests> UpdateTvRequest(TvRequests request)
|
||||
|
@ -274,6 +358,7 @@ namespace Ombi.Core.Engine
|
|||
results.ImdbId = request.ImdbId;
|
||||
results.Overview = request.Overview;
|
||||
results.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath);
|
||||
results.Background = PosterPathHelper.FixBackgroundPath(request.Background);
|
||||
results.QualityOverride = request.QualityOverride;
|
||||
results.RootFolder = request.RootFolder;
|
||||
|
||||
|
@ -412,6 +497,7 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
foreach (var season in request.SeasonRequests)
|
||||
{
|
||||
foreach (var e in season.Episodes)
|
||||
|
@ -428,6 +514,42 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<int> GetTotal()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
return await TvRepository.Get(shouldHide.UserId).CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await TvRepository.Get().CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(HideResult shouldHide, TvRequests x)
|
||||
{
|
||||
foreach (var tv in x.ChildRequests)
|
||||
{
|
||||
await CheckForSubscription(shouldHide, tv);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckForSubscription(HideResult shouldHide, ChildRequests x)
|
||||
{
|
||||
if (shouldHide.UserId == x.RequestedUserId)
|
||||
{
|
||||
x.ShowSubscribe = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
x.ShowSubscribe = true;
|
||||
var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s =>
|
||||
s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.TvShow);
|
||||
x.Subscribed = sub != null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest)
|
||||
{
|
||||
// Add the child
|
||||
|
@ -445,29 +567,7 @@ namespace Ombi.Core.Engine
|
|||
return await AfterRequest(model.ChildRequests.FirstOrDefault());
|
||||
}
|
||||
|
||||
private static List<TreeNode<TvRequests, List<ChildRequests>>> ParseIntoTreeNode(IEnumerable<TvRequests> result)
|
||||
{
|
||||
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)
|
||||
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
|
||||
{
|
||||
foreach (var value in items)
|
||||
{
|
||||
|
@ -488,6 +588,15 @@ namespace Ombi.Core.Engine
|
|||
NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
EpisodeCount = model.SeasonRequests.Select(m => m.Episodes.Count).Sum(),
|
||||
});
|
||||
|
||||
if (model.Approved)
|
||||
{
|
||||
// Autosend
|
||||
|
@ -503,15 +612,58 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
});
|
||||
|
||||
return new RequestEngineResult { Result = true };
|
||||
}
|
||||
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
user = await GetUser();
|
||||
|
||||
// If user is still null after attempting to get the logged in user, return null.
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int limit = user.EpisodeRequestLimit ?? 0;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = false,
|
||||
Limit = 0,
|
||||
Remaining = 0,
|
||||
NextRequest = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
IQueryable<RequestLog> log = _requestLog.GetAll()
|
||||
.Where(x => x.UserId == user.Id
|
||||
&& x.RequestType == RequestType.TvShow
|
||||
&& x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
|
||||
// Needed, due to a bug which would cause all episode counts to be 0
|
||||
int zeroEpisodeCount = await log.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
|
||||
|
||||
int episodeCount = await log.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
|
||||
|
||||
int count = limit - (zeroEpisodeCount + episodeCount);
|
||||
|
||||
DateTime oldestRequestedAt = await log.OrderBy(x => x.RequestDate)
|
||||
.Select(x => x.RequestDate)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = true,
|
||||
Limit = limit,
|
||||
Remaining = count,
|
||||
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ using Microsoft.Extensions.Caching.Memory;
|
|||
using Ombi.Core.Authentication;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -27,8 +28,8 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService<PlexSettings> plexSettings,
|
||||
ISettingsService<EmbySettings> embySettings, IPlexContentRepository repo, IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um,
|
||||
ICacheService memCache, ISettingsService<OmbiSettings> s)
|
||||
: base(identity, service, r, um, memCache, s)
|
||||
ICacheService memCache, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub)
|
||||
: base(identity, service, r, um, memCache, s, sub)
|
||||
{
|
||||
TvMazeApi = tvMaze;
|
||||
Mapper = mapper;
|
||||
|
@ -53,16 +54,20 @@ namespace Ombi.Core.Engine
|
|||
|
||||
if (searchResult != null)
|
||||
{
|
||||
return await ProcessResults(searchResult);
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in searchResult)
|
||||
{
|
||||
if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(await ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
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)
|
||||
{
|
||||
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid);
|
||||
|
@ -115,19 +120,6 @@ namespace Ombi.Core.Engine
|
|||
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()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -135,12 +127,6 @@ namespace Ombi.Core.Engine
|
|||
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()
|
||||
{
|
||||
|
||||
|
@ -149,12 +135,6 @@ namespace Ombi.Core.Engine
|
|||
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()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -162,13 +142,6 @@ namespace Ombi.Core.Engine
|
|||
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()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||
|
@ -176,33 +149,21 @@ namespace Ombi.Core.Engine
|
|||
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)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in items)
|
||||
{
|
||||
var viewT = Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
retVal.Add(await ProcessResult(viewT));
|
||||
retVal.Add(await ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult<T>(T tvMazeSearch)
|
||||
{
|
||||
return Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
}
|
||||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult(SearchTvShowViewModel item)
|
||||
{
|
||||
item.TheTvDbId = item.Id.ToString();
|
||||
|
|
77
src/Ombi.Core/Engine/UserStatsEngine.cs
Normal file
77
src/Ombi.Core/Engine/UserStatsEngine.cs
Normal 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; }
|
||||
|
||||
}
|
||||
}
|
|
@ -40,6 +40,18 @@ namespace Ombi.Core
|
|||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel));
|
||||
}
|
||||
|
||||
public void NewRequest(AlbumRequest model)
|
||||
{
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
RequestId = model.Id,
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.NewRequest,
|
||||
RequestType = model.RequestType
|
||||
};
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel));
|
||||
}
|
||||
|
||||
|
||||
public void Notify(MovieRequests model, NotificationType type)
|
||||
{
|
||||
|
@ -66,5 +78,19 @@ namespace Ombi.Core
|
|||
};
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel));
|
||||
}
|
||||
|
||||
public void Notify(AlbumRequest model, NotificationType type)
|
||||
{
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
RequestId = model.Id,
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = type,
|
||||
RequestType = model.RequestType,
|
||||
Recipient = model.RequestedUser?.Email ?? string.Empty
|
||||
};
|
||||
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TvMaze.Models;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Helpers;
|
||||
|
@ -16,23 +18,37 @@ namespace Ombi.Core.Helpers
|
|||
public class TvShowRequestBuilder
|
||||
{
|
||||
|
||||
public TvShowRequestBuilder(ITvMazeApi tvApi)
|
||||
public TvShowRequestBuilder(ITvMazeApi tvApi, IMovieDbApi movApi)
|
||||
{
|
||||
TvApi = tvApi;
|
||||
MovieDbApi = movApi;
|
||||
}
|
||||
|
||||
private ITvMazeApi TvApi { get; }
|
||||
private IMovieDbApi MovieDbApi { get; }
|
||||
|
||||
public ChildRequests ChildRequest { get; set; }
|
||||
public List<SeasonsViewModel> TvRequests { get; protected set; }
|
||||
public string PosterPath { get; protected set; }
|
||||
public string BackdropPath { get; protected set; }
|
||||
public DateTime FirstAir { get; protected set; }
|
||||
public TvRequests NewRequest { get; protected set; }
|
||||
protected TvMazeShow ShowInfo { get; set; }
|
||||
protected List<TvSearchResult> Results { get; set; }
|
||||
|
||||
public async Task<TvShowRequestBuilder> GetShowInfo(int id)
|
||||
{
|
||||
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
|
||||
Results = await MovieDbApi.SearchTv(ShowInfo.name);
|
||||
foreach (TvSearchResult result in Results) {
|
||||
if (result.Name == ShowInfo.name)
|
||||
{
|
||||
var showIds = await MovieDbApi.GetTvExternals(result.Id);
|
||||
ShowInfo.externals.imdb = showIds.imdb_id;
|
||||
BackdropPath = result.BackdropPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DateTime.TryParse(ShowInfo.premiered, out var dt);
|
||||
|
||||
|
@ -55,7 +71,7 @@ namespace Ombi.Core.Helpers
|
|||
RequestedUserId = userId,
|
||||
SeasonRequests = new List<SeasonRequests>(),
|
||||
Title = ShowInfo.name,
|
||||
SeriesType = ShowInfo.type.Equals("Animation", StringComparison.CurrentCultureIgnoreCase) ? SeriesType.Anime : SeriesType.Standard
|
||||
SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.OrdinalIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
|
||||
};
|
||||
|
||||
return this;
|
||||
|
@ -226,7 +242,8 @@ namespace Ombi.Core.Helpers
|
|||
ImdbId = ShowInfo.externals?.imdb ?? string.Empty,
|
||||
TvDbId = tv.TvDbId,
|
||||
ChildRequests = new List<ChildRequests>(),
|
||||
TotalSeasons = tv.Seasons.Count()
|
||||
TotalSeasons = tv.Seasons.Count(),
|
||||
Background = BackdropPath
|
||||
};
|
||||
NewRequest.ChildRequests.Add(ChildRequest);
|
||||
|
||||
|
|
14
src/Ombi.Core/IPlexOAuthManager.cs
Normal file
14
src/Ombi.Core/IPlexOAuthManager.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex.Models;
|
||||
|
||||
namespace Ombi.Core.Authentication
|
||||
{
|
||||
public interface IPlexOAuthManager
|
||||
{
|
||||
Task<string> GetAccessTokenFromPin(int pinId);
|
||||
Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null);
|
||||
Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress);
|
||||
Task<PlexAccount> GetAccount(string accessToken);
|
||||
}
|
||||
}
|
15
src/Ombi.Core/Models/RequestQuotaCountModel.cs
Normal file
15
src/Ombi.Core/Models/RequestQuotaCountModel.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Core.Models
|
||||
{
|
||||
public class RequestQuotaCountModel
|
||||
{
|
||||
public bool HasLimit { get; set; }
|
||||
|
||||
public int Limit { get; set; }
|
||||
|
||||
public int Remaining { get; set; }
|
||||
|
||||
public DateTime NextRequest { get; set; }
|
||||
}
|
||||
}
|
10
src/Ombi.Core/Models/Requests/FilterResult.cs
Normal file
10
src/Ombi.Core/Models/Requests/FilterResult.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public class FilterResult<T>
|
||||
{
|
||||
public int Total { get; set; }
|
||||
public IEnumerable<T> Collection { get; set; }
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
{
|
||||
public FilterType AvailabilityFilter { get; set; }
|
||||
public FilterType StatusFilter { get; set; }
|
||||
public int Position { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
public enum FilterType
|
||||
|
|
|
@ -7,5 +7,6 @@ namespace Ombi.Core.Models.Requests
|
|||
{
|
||||
IMovieRequestRepository MovieRequestService { get; }
|
||||
ITvRequestRepository TvRequestService { get; }
|
||||
IMusicRequestRepository MusicRequestRepository { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public class MusicAlbumRequestViewModel
|
||||
{
|
||||
public string ForeignAlbumId { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,13 +5,15 @@ namespace Ombi.Core.Models.Requests
|
|||
{
|
||||
public class RequestService : IRequestServiceMain
|
||||
{
|
||||
public RequestService(ITvRequestRepository tv, IMovieRequestRepository movie)
|
||||
public RequestService(ITvRequestRepository tv, IMovieRequestRepository movie, IMusicRequestRepository music)
|
||||
{
|
||||
TvRequestService = tv;
|
||||
MovieRequestService = movie;
|
||||
MusicRequestRepository = music;
|
||||
}
|
||||
|
||||
public ITvRequestRepository TvRequestService { get; }
|
||||
public IMusicRequestRepository MusicRequestRepository { get; }
|
||||
public IMovieRequestRepository MovieRequestService { get; }
|
||||
}
|
||||
}
|
23
src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
Normal file
23
src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
public class SearchAlbumViewModel : SearchViewModel
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string ForeignAlbumId { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public string AlbumType { get; set; }
|
||||
public decimal Rating { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public string ArtistName { get; set; }
|
||||
public string ForeignArtistId { get; set; }
|
||||
public string Cover { get; set; }
|
||||
public string Disk { get; set; }
|
||||
public decimal PercentOfTracks { get; set; }
|
||||
public override RequestType Type => RequestType.Album;
|
||||
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;
|
||||
public bool FullyAvailable => PercentOfTracks == 100;
|
||||
}
|
||||
}
|
19
src/Ombi.Core/Models/Search/SearchArtistViewModel.cs
Normal file
19
src/Ombi.Core/Models/Search/SearchArtistViewModel.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using Ombi.Api.Lidarr.Models;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
public class SearchArtistViewModel
|
||||
{
|
||||
public string ArtistName { get; set; }
|
||||
public string ForignArtistId { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string Disambiguation { get; set; }
|
||||
public string Banner { get; set; }
|
||||
public string Poster { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public string ArtistType { get; set; }
|
||||
public string CleanName { get; set; }
|
||||
public Link[] Links { get; set; } // Couldn't be bothered to map it
|
||||
}
|
||||
}
|
|
@ -56,7 +56,6 @@ namespace Ombi.Core.Models.Search
|
|||
public bool FullyAvailable { get; set; }
|
||||
// We only have some episodes
|
||||
public bool PartlyAvailable { get; set; }
|
||||
|
||||
public override RequestType Type => RequestType.TvShow;
|
||||
}
|
||||
}
|
|
@ -8,12 +8,13 @@ namespace Ombi.Core.Models.Search
|
|||
public int Id { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
public bool Requested { get; set; }
|
||||
public int RequestId { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public string PlexUrl { get; set; }
|
||||
public string EmbyUrl { get; set; }
|
||||
public string Quality { get; set; }
|
||||
public abstract RequestType Type { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule rule
|
||||
/// </summary>
|
||||
|
@ -26,5 +27,11 @@ namespace Ombi.Core.Models.Search
|
|||
public string TheTvDbId { get; set; }
|
||||
[NotMapped]
|
||||
public string TheMovieDbId { get; set; }
|
||||
|
||||
|
||||
[NotMapped]
|
||||
public bool Subscribed { get; set; }
|
||||
[NotMapped]
|
||||
public bool ShowSubscribe { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Core/Models/UI/OrderFilterModel.cs
Normal file
11
src/Ombi.Core/Models/UI/OrderFilterModel.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Ombi.Core.Models.Requests;
|
||||
|
||||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
public class OrderFilterModel
|
||||
{
|
||||
public FilterType AvailabilityFilter { get; set; }
|
||||
public FilterType StatusFilter { get; set; }
|
||||
public OrderType OrderType { get; set; }
|
||||
}
|
||||
}
|
12
src/Ombi.Core/Models/UI/OrderType.cs
Normal file
12
src/Ombi.Core/Models/UI/OrderType.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
public enum OrderType
|
||||
{
|
||||
RequestedDateAsc =1,
|
||||
RequestedDateDesc =2,
|
||||
TitleAsc =3,
|
||||
TitleDesc=4,
|
||||
StatusAsc=5,
|
||||
StatusDesc=6
|
||||
}
|
||||
}
|
10
src/Ombi.Core/Models/UI/RequestsViewModel.cs
Normal file
10
src/Ombi.Core/Models/UI/RequestsViewModel.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
public class RequestsViewModel<T>
|
||||
{
|
||||
public IEnumerable<T> Collection { get; set; }
|
||||
public int Total { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
|
@ -16,6 +17,11 @@ namespace Ombi.Core.Models.UI
|
|||
public UserType UserType { get; set; }
|
||||
public int MovieRequestLimit { get; set; }
|
||||
public int EpisodeRequestLimit { get; set; }
|
||||
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MusicRequestQuota { get; set; }
|
||||
public int MusicRequestLimit { get; set; }
|
||||
public UserQualityProfiles UserQualityProfiles { get; set; }
|
||||
}
|
||||
|
||||
public class ClaimCheckboxes
|
||||
|
|
|
@ -10,18 +10,18 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.0.1" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.17" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.5" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
||||
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue