mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-21 05:43:19 -07:00
commit
5ee25be669
164 changed files with 5028 additions and 2280 deletions
136
CHANGELOG.md
136
CHANGELOG.md
|
@ -1,15 +1,148 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
|
## (unreleased)
|
||||||
|
|
||||||
|
### **New Features**
|
||||||
|
|
||||||
|
- Added a global language flag that now applies to the search by default. [tidusjar]
|
||||||
|
|
||||||
|
- Updated the frontend packages (Using Angular 7 now) [TidusJar]
|
||||||
|
|
||||||
|
- Added capture of anonymous analytical data. [tidusjar]
|
||||||
|
|
||||||
|
- Added {AvailableDate} as a Notification Variable, this is the date the request was marked as available. See here: https://github.com/tidusjar/Ombi/wiki/Notification-Template-Variables. [tidusjar]
|
||||||
|
|
||||||
|
- Added the ability to search movies via the movie db with a different language! [tidusjar]
|
||||||
|
|
||||||
|
- Added the ability to specify a year when searching for movies. [tidusjar]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update NewsletterTemplate.html. [d1slact0r]
|
||||||
|
|
||||||
|
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||||
|
|
||||||
|
- Updated boostrap #2694. [Jamie]
|
||||||
|
|
||||||
|
- Added the ability to deny a request with a reason. [TidusJar]
|
||||||
|
|
||||||
## v3.0.4036 (2018-12-11)
|
## v3.0.4036 (2018-12-11)
|
||||||
|
|
||||||
|
|
||||||
|
- Update EmbyEpisodeSync.cs. [Jamie]
|
||||||
|
|
||||||
|
- Updated to .net core 2.2 and included a linux-arm64 build. [TidusJar]
|
||||||
|
|
||||||
|
### **Fixes**
|
||||||
|
|
||||||
|
- Made the newsletter use the default lanuage code set in the Ombi settings for movie information. [TidusJar]
|
||||||
|
|
||||||
|
- Save the language code against the request so we can use it later e.g. Sending to the DVR apps. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #2716. [tidusjar]
|
||||||
|
|
||||||
|
- Make the newsletter BCC the users rather than creating a million newsletters (Hopefully will stop SMTP providers from marking as spam). This does mean that the custom user customization in the newsletter will no longer work. [TidusJar]
|
||||||
|
|
||||||
|
- If we don't know the Plex agent, then see if it's a ImdbId, if it's not check the string for any episode and season hints #2695. [tidusjar]
|
||||||
|
|
||||||
|
- New translations en.json (Swedish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Spanish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Polish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Italian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (German) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (French) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Danish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- Made the search results the language specified in the search refinement. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #2704. [tidusjar]
|
||||||
|
|
||||||
|
- Now it is fixed :) [d1slact0r]
|
||||||
|
|
||||||
|
- Android please be nice now. [d1slact0r]
|
||||||
|
|
||||||
|
- Fixed title bit better. [d1slact0r]
|
||||||
|
|
||||||
|
- Fixed titles. [d1slact0r]
|
||||||
|
|
||||||
|
- This should fix the build for sure (stupid quotes) [d1slact0r]
|
||||||
|
|
||||||
|
- Fixes build. [d1slact0r]
|
||||||
|
|
||||||
|
- Rewritten the whole newsletter template. [d1slact0r]
|
||||||
|
|
||||||
|
- Fixed #2697. [tidusjar]
|
||||||
|
|
||||||
|
- Add linux-arm runtime identifier. [aptalca]
|
||||||
|
|
||||||
|
- Add back arm packages. [aptalca]
|
||||||
|
|
||||||
|
- Add arm32 package. [aptalca]
|
||||||
|
|
||||||
|
- Fixed #2691. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed linting. [TidusJar]
|
||||||
|
|
||||||
|
- Fixed the Plex OAuth when going through the wizard. [TidusJar]
|
||||||
|
|
||||||
|
- Fixed #2678. [TidusJar]
|
||||||
|
|
||||||
|
- Deny reason for movie requests. [TidusJar]
|
||||||
|
|
||||||
|
- Set the landing and login pages background refresh to 15 seconds rather than 10 and 7. [TidusJar]
|
||||||
|
|
||||||
|
- Fixed a bug with us thinking future dated emby episodes are not available, Consoldated the emby and plex search rules (since they have the same logic) [TidusJar]
|
||||||
|
|
||||||
|
- Fixed build. [TidusJar]
|
||||||
|
|
||||||
|
|
||||||
|
## v3.0.4036 (2018-12-11)
|
||||||
|
|
||||||
### **New Features**
|
### **New Features**
|
||||||
|
|
||||||
|
- Changelog. [Jamie]
|
||||||
|
|
||||||
- Added Sonarr v3 #2359. [TidusJar]
|
- Added Sonarr v3 #2359. [TidusJar]
|
||||||
|
|
||||||
### **Fixes**
|
### **Fixes**
|
||||||
|
|
||||||
|
- !changelog. [Jamie]
|
||||||
|
|
||||||
|
- Fixed a missing translation. [Jamie]
|
||||||
|
|
||||||
- Fixed a potential security vulnerability. [Jamie]
|
- Fixed a potential security vulnerability. [Jamie]
|
||||||
|
|
||||||
- Sorted out some of the settings pages, trying to make it consistent. [Jamie]
|
- Sorted out some of the settings pages, trying to make it consistent. [Jamie]
|
||||||
|
@ -37,8 +170,7 @@
|
||||||
|
|
||||||
### **Fixes**
|
### **Fixes**
|
||||||
|
|
||||||
|
- !changelog. [Jamie]
|
||||||
- Fixed #2601 [TidusJar]
|
|
||||||
|
|
||||||
- Made the subscribe/unsubscribe button more obvious on the UI #2309. [Jamie]
|
- Made the subscribe/unsubscribe button more obvious on the UI #2309. [Jamie]
|
||||||
|
|
||||||
|
|
12
appveyor.yml
12
appveyor.yml
|
@ -27,22 +27,22 @@ test: off
|
||||||
after_build:
|
after_build:
|
||||||
- cmd: >-
|
- cmd: >-
|
||||||
|
|
||||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip"
|
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows.zip"
|
||||||
|
|
||||||
|
|
||||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz"
|
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\osx.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.2\linux.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.2\linux-arm.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip"
|
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows-32bit.zip"
|
||||||
|
|
||||||
|
|
||||||
# appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz"
|
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm64.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
|
||||||
var solutionFile = "Ombi.sln"; // Solution file if needed
|
var solutionFile = "Ombi.sln"; // Solution file if needed
|
||||||
GitVersion versionInfo = null;
|
GitVersion versionInfo = null;
|
||||||
|
|
||||||
var frameworkVer = "netcoreapp2.1";
|
var frameworkVer = "netcoreapp2.2";
|
||||||
|
|
||||||
var buildSettings = new DotNetCoreBuildSettings
|
var buildSettings = new DotNetCoreBuildSettings
|
||||||
{
|
{
|
||||||
|
@ -151,7 +151,7 @@ Task("Package")
|
||||||
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
|
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
|
||||||
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
|
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
|
||||||
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
|
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
|
||||||
//GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
|
GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("Publish")
|
Task("Publish")
|
||||||
|
|
291
package-lock.json
generated
291
package-lock.json
generated
|
@ -1,291 +0,0 @@
|
||||||
{
|
|
||||||
"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="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace Ombi.Api.Sonarr.Models
|
||||||
public DateTime added { get; set; }
|
public DateTime added { get; set; }
|
||||||
public Ratings ratings { get; set; }
|
public Ratings ratings { get; set; }
|
||||||
public int qualityProfileId { get; set; }
|
public int qualityProfileId { get; set; }
|
||||||
|
public int languageProfileId { get; set; }
|
||||||
public int id { get; set; }
|
public int id { get; set; }
|
||||||
public DateTime nextAiring { get; set; }
|
public DateTime nextAiring { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="Polly" Version="6.1.0" />
|
<PackageReference Include="Polly" Version="6.1.0" />
|
||||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"></packagereference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -157,6 +157,24 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string defaultLangCode;
|
||||||
|
protected async Task<string> DefaultLanguageCode(string currentCode)
|
||||||
|
{
|
||||||
|
if (currentCode.HasValue())
|
||||||
|
{
|
||||||
|
return currentCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = await GetOmbiSettings();
|
||||||
|
return s.DefaultLanguageCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OmbiSettings ombiSettings;
|
||||||
|
protected async Task<OmbiSettings> GetOmbiSettings()
|
||||||
|
{
|
||||||
|
return ombiSettings ?? (ombiSettings = await OmbiSettings.GetSettingsAsync());
|
||||||
|
}
|
||||||
|
|
||||||
public class HideResult
|
public class HideResult
|
||||||
{
|
{
|
||||||
public bool Hide { get; set; }
|
public bool Hide { get; set; }
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
Task<RequestEngineResult>ApproveAlbum(AlbumRequest request);
|
Task<RequestEngineResult>ApproveAlbum(AlbumRequest request);
|
||||||
Task<RequestEngineResult> ApproveAlbumById(int requestId);
|
Task<RequestEngineResult> ApproveAlbumById(int requestId);
|
||||||
Task<RequestEngineResult> DenyAlbumById(int modelId);
|
Task<RequestEngineResult> DenyAlbumById(int modelId, string reason);
|
||||||
Task<IEnumerable<AlbumRequest>> GetRequests();
|
Task<IEnumerable<AlbumRequest>> GetRequests();
|
||||||
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, OrderFilterModel orderFilter);
|
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, OrderFilterModel orderFilter);
|
||||||
Task<int> GetTotal();
|
Task<int> GetTotal();
|
||||||
|
|
|
@ -10,14 +10,14 @@ namespace Ombi.Core
|
||||||
|
|
||||||
Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
|
Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
|
||||||
|
|
||||||
Task<IEnumerable<SearchMovieViewModel>> Search(string search);
|
Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string languageCode);
|
||||||
|
|
||||||
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();
|
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();
|
||||||
|
|
||||||
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
||||||
|
|
||||||
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
|
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = null);
|
||||||
|
|
||||||
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId);
|
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId, string langCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,6 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
|
Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
|
||||||
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
||||||
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
||||||
Task<RequestEngineResult> DenyMovieById(int modelId);
|
Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task RemoveTvRequest(int requestId);
|
Task RemoveTvRequest(int requestId);
|
||||||
Task<TvRequests> GetTvRequest(int requestId);
|
Task<TvRequests> GetTvRequest(int requestId);
|
||||||
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
|
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
|
||||||
Task<RequestEngineResult> DenyChildRequest(int requestId);
|
Task<RequestEngineResult> DenyChildRequest(int requestId, string reason);
|
||||||
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
||||||
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
||||||
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<RequestEngineResult> RequestMovie(MovieRequestViewModel model)
|
public async Task<RequestEngineResult> RequestMovie(MovieRequestViewModel model)
|
||||||
{
|
{
|
||||||
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId);
|
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId, model.LanguageCode);
|
||||||
if (movieInfo == null || movieInfo.Id == 0)
|
if (movieInfo == null || movieInfo.Id == 0)
|
||||||
{
|
{
|
||||||
return new RequestEngineResult
|
return new RequestEngineResult
|
||||||
|
@ -82,7 +82,8 @@ namespace Ombi.Core.Engine
|
||||||
RequestedDate = DateTime.UtcNow,
|
RequestedDate = DateTime.UtcNow,
|
||||||
Approved = false,
|
Approved = false,
|
||||||
RequestedUserId = userDetails.Id,
|
RequestedUserId = userDetails.Id,
|
||||||
Background = movieInfo.BackdropPath
|
Background = movieInfo.BackdropPath,
|
||||||
|
LangCode = model.LanguageCode
|
||||||
};
|
};
|
||||||
|
|
||||||
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||||
|
@ -305,7 +306,7 @@ namespace Ombi.Core.Engine
|
||||||
return await ApproveMovie(request);
|
return await ApproveMovie(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestEngineResult> DenyMovieById(int modelId)
|
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason)
|
||||||
{
|
{
|
||||||
var request = await MovieRepository.Find(modelId);
|
var request = await MovieRepository.Find(modelId);
|
||||||
if (request == null)
|
if (request == null)
|
||||||
|
@ -317,6 +318,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Denied = true;
|
request.Denied = true;
|
||||||
|
request.DeniedReason = denyReason;
|
||||||
// We are denying a request
|
// We are denying a request
|
||||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||||
await MovieRepository.Update(request);
|
await MovieRepository.Update(request);
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
using System;
|
using AutoMapper;
|
||||||
using AutoMapper;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ombi.Api.TheMovieDb;
|
using Ombi.Api.TheMovieDb;
|
||||||
using Ombi.Api.TheMovieDb.Models;
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Models.Search;
|
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 Ombi.Core.Rule.Interfaces;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using Ombi.Core.Authentication;
|
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -36,14 +35,17 @@ namespace Ombi.Core.Engine
|
||||||
private IMapper Mapper { get; }
|
private IMapper Mapper { get; }
|
||||||
private ILogger<MovieSearchEngine> Logger { get; }
|
private ILogger<MovieSearchEngine> Logger { get; }
|
||||||
|
|
||||||
|
private const int MovieLimit = 10;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lookups the imdb information.
|
/// Lookups the imdb information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="theMovieDbId">The movie database identifier.</param>
|
/// <param name="theMovieDbId">The movie database identifier.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId)
|
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = null)
|
||||||
{
|
{
|
||||||
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId);
|
langCode = await DefaultLanguageCode(langCode);
|
||||||
|
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode);
|
||||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
|
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
|
||||||
|
|
||||||
return await ProcessSingleMovie(viewMovie, true);
|
return await ProcessSingleMovie(viewMovie, true);
|
||||||
|
@ -54,13 +56,14 @@ namespace Ombi.Core.Engine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="search">The search.</param>
|
/// <param name="search">The search.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
|
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string langaugeCode)
|
||||||
{
|
{
|
||||||
var result = await MovieApi.SearchMovie(search);
|
langaugeCode = await DefaultLanguageCode(langaugeCode);
|
||||||
|
var result = await MovieApi.SearchMovie(search, year, langaugeCode);
|
||||||
|
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -70,13 +73,14 @@ namespace Ombi.Core.Engine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="theMovieDbId"></param>
|
/// <param name="theMovieDbId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId)
|
public async Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId, string langCode)
|
||||||
{
|
{
|
||||||
var result = await MovieApi.SimilarMovies(theMovieDbId);
|
langCode = await DefaultLanguageCode(langCode);
|
||||||
|
var result = await MovieApi.SimilarMovies(theMovieDbId, langCode);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Search Result: {result}", result);
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -87,10 +91,15 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
||||||
{
|
{
|
||||||
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
|
|
||||||
|
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () =>
|
||||||
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
return await MovieApi.PopularMovies(langCode);
|
||||||
|
}, DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -101,10 +110,14 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
||||||
{
|
{
|
||||||
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () =>
|
||||||
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
return await MovieApi.TopRated(langCode);
|
||||||
|
}, DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -115,11 +128,15 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
||||||
{
|
{
|
||||||
var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () => await MovieApi.Upcoming(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () =>
|
||||||
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
return await MovieApi.Upcoming(langCode);
|
||||||
|
}, DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Search Result: {result}", result);
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -130,10 +147,14 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
||||||
{
|
{
|
||||||
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () =>
|
||||||
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
return await MovieApi.NowPlaying(langCode);
|
||||||
|
}, DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -174,6 +195,10 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
// Check if this user requested it
|
// Check if this user requested it
|
||||||
var user = await GetUser();
|
var user = await GetUser();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var request = await RequestService.MovieRequestService.GetAll()
|
var request = await RequestService.MovieRequestService.GetAll()
|
||||||
.AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id);
|
.AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id);
|
||||||
if (request)
|
if (request)
|
||||||
|
|
|
@ -299,7 +299,7 @@ namespace Ombi.Core.Engine
|
||||||
return await ApproveAlbum(request);
|
return await ApproveAlbum(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestEngineResult> DenyAlbumById(int modelId)
|
public async Task<RequestEngineResult> DenyAlbumById(int modelId, string reason)
|
||||||
{
|
{
|
||||||
var request = await MusicRepository.Find(modelId);
|
var request = await MusicRepository.Find(modelId);
|
||||||
if (request == null)
|
if (request == null)
|
||||||
|
@ -311,6 +311,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Denied = true;
|
request.Denied = true;
|
||||||
|
request.DeniedReason = reason;
|
||||||
// We are denying a request
|
// We are denying a request
|
||||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||||
await MusicRepository.Update(request);
|
await MusicRepository.Update(request);
|
||||||
|
|
|
@ -403,7 +403,7 @@ namespace Ombi.Core.Engine
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestEngineResult> DenyChildRequest(int requestId)
|
public async Task<RequestEngineResult> DenyChildRequest(int requestId, string reason)
|
||||||
{
|
{
|
||||||
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
|
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||||
if (request == null)
|
if (request == null)
|
||||||
|
@ -414,6 +414,7 @@ namespace Ombi.Core.Engine
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
request.Denied = true;
|
request.Denied = true;
|
||||||
|
request.DeniedReason = reason;
|
||||||
await TvRepository.UpdateChild(request);
|
await TvRepository.UpdateChild(request);
|
||||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||||
return new RequestEngineResult
|
return new RequestEngineResult
|
||||||
|
|
|
@ -29,5 +29,6 @@ namespace Ombi.Core.Models.Requests
|
||||||
public class MovieRequestViewModel
|
public class MovieRequestViewModel
|
||||||
{
|
{
|
||||||
public int TheMovieDbId { get; set; }
|
public int TheMovieDbId { get; set; }
|
||||||
|
public string LanguageCode { get; set; } = "en";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,11 +11,11 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
|
||||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
<PackageReference Include="Hangfire" Version="1.6.21" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.1.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||||
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
97
src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs
Normal file
97
src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
|
using Ombi.Core.Models.Search;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Repository.Requests;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Rule.Rules.Search
|
||||||
|
{
|
||||||
|
public static class AvailabilityRuleHelper
|
||||||
|
{
|
||||||
|
public static void CheckForUnairedEpisodes(SearchTvShowViewModel search)
|
||||||
|
{
|
||||||
|
if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
||||||
|
{
|
||||||
|
search.FullyAvailable = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var airedButNotAvailable = search.SeasonRequests.Any(x =>
|
||||||
|
x.Episodes.Any(c => !c.Available && c.AirDate <= DateTime.Now.Date && c.AirDate != DateTime.MinValue));
|
||||||
|
if (!airedButNotAvailable)
|
||||||
|
{
|
||||||
|
var unairedEpisodes = search.SeasonRequests.Any(x =>
|
||||||
|
x.Episodes.Any(c => !c.Available && c.AirDate > DateTime.Now.Date));
|
||||||
|
if (unairedEpisodes)
|
||||||
|
{
|
||||||
|
search.FullyAvailable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<PlexEpisode> allEpisodes, EpisodeRequests episode,
|
||||||
|
SeasonRequests season, PlexServerContent item, bool useTheMovieDb, bool useTvDb)
|
||||||
|
{
|
||||||
|
PlexEpisode epExists = null;
|
||||||
|
if (useImdb)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||||
|
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||||
|
x.Series.ImdbId == item.ImdbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useTheMovieDb)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||||
|
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||||
|
x.Series.TheMovieDbId == item.TheMovieDbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useTvDb)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||||
|
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||||
|
x.Series.TvDbId == item.TvDbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (epExists != null)
|
||||||
|
{
|
||||||
|
episode.Available = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<EmbyEpisode> allEpisodes, EpisodeRequests episode,
|
||||||
|
SeasonRequests season, EmbyContent item, bool useTheMovieDb, bool useTvDb)
|
||||||
|
{
|
||||||
|
EmbyEpisode epExists = null;
|
||||||
|
if (useImdb)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||||
|
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||||
|
x.Series.ImdbId == item.ImdbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useTheMovieDb)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||||
|
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||||
|
x.Series.TheMovieDbId == item.TheMovieDbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useTvDb)
|
||||||
|
{
|
||||||
|
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||||
|
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||||
|
x.Series.TvDbId == item.TvDbId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (epExists != null)
|
||||||
|
{
|
||||||
|
episode.Available = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
|
@ -23,15 +21,27 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||||
{
|
{
|
||||||
EmbyContent item = null;
|
EmbyContent item = null;
|
||||||
|
var useImdb = false;
|
||||||
|
var useTheMovieDb = false;
|
||||||
|
var useTvDb = false;
|
||||||
|
|
||||||
if (obj.ImdbId.HasValue())
|
if (obj.ImdbId.HasValue())
|
||||||
{
|
{
|
||||||
item = await EmbyContentRepository.GetByImdbId(obj.ImdbId);
|
item = await EmbyContentRepository.GetByImdbId(obj.ImdbId);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
useImdb = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
if (obj.TheMovieDbId.HasValue())
|
if (obj.TheMovieDbId.HasValue())
|
||||||
{
|
{
|
||||||
item = await EmbyContentRepository.GetByTheMovieDbId(obj.TheMovieDbId);
|
item = await EmbyContentRepository.GetByTheMovieDbId(obj.TheMovieDbId);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
useTheMovieDb = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -39,6 +49,10 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
if (obj.TheTvDbId.HasValue())
|
if (obj.TheTvDbId.HasValue())
|
||||||
{
|
{
|
||||||
item = await EmbyContentRepository.GetByTvDbId(obj.TheTvDbId);
|
item = await EmbyContentRepository.GetByTvDbId(obj.TheTvDbId);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
useTvDb = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,29 +73,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
{
|
{
|
||||||
foreach (var episode in season.Episodes)
|
foreach (var episode in season.Episodes)
|
||||||
{
|
{
|
||||||
EmbyEpisode epExists = null;
|
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb);
|
||||||
|
|
||||||
if (item.HasImdb)
|
|
||||||
{
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
|
||||||
&& e.ImdbId == item.ImdbId);
|
|
||||||
} if (item.HasTvDb && epExists == null)
|
|
||||||
{
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
|
||||||
&& e.Series.TvDbId == item.TvDbId);
|
|
||||||
} if (item.HasTheMovieDb && epExists == null)
|
|
||||||
{
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
|
||||||
&& e.TheMovieDbId == item.TheMovieDbId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (epExists != null)
|
|
||||||
{
|
|
||||||
episode.Available = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Success();
|
return Success();
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
@ -74,56 +72,17 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
{
|
{
|
||||||
foreach (var episode in season.Episodes)
|
foreach (var episode in season.Episodes)
|
||||||
{
|
{
|
||||||
PlexEpisode epExists = null;
|
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb);
|
||||||
if (useImdb)
|
|
||||||
{
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
|
||||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
|
||||||
x.Series.ImdbId == item.ImdbId.ToString());
|
|
||||||
}
|
|
||||||
if (useTheMovieDb)
|
|
||||||
{
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
|
||||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
|
||||||
x.Series.TheMovieDbId == item.TheMovieDbId.ToString());
|
|
||||||
}
|
|
||||||
if (useTvDb)
|
|
||||||
{
|
|
||||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
|
||||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
|
||||||
x.Series.TvDbId == item.TvDbId.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (epExists != null)
|
|
||||||
{
|
|
||||||
episode.Available = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
|
||||||
{
|
|
||||||
search.FullyAvailable = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var airedButNotAvailable = search.SeasonRequests.Any(x =>
|
|
||||||
x.Episodes.Any(c => !c.Available && c.AirDate <= DateTime.Now.Date));
|
|
||||||
if (!airedButNotAvailable)
|
|
||||||
{
|
|
||||||
var unairedEpisodes = search.SeasonRequests.Any(x =>
|
|
||||||
x.Episodes.Any(c => !c.Available && c.AirDate > DateTime.Now.Date));
|
|
||||||
if (unairedEpisodes)
|
|
||||||
{
|
|
||||||
search.FullyAvailable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Success();
|
return Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -348,16 +348,16 @@ namespace Ombi.Core.Senders
|
||||||
if (!existingSeason.monitored)
|
if (!existingSeason.monitored)
|
||||||
{
|
{
|
||||||
// We need to monitor it, problem being is all episodes will now be monitored
|
// We need to monitor it, problem being is all episodes will now be monitored
|
||||||
// So we need to monior the series but unmonitor every episode
|
// So we need to monitor the series but unmonitor every episode
|
||||||
// Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
|
// Except the episodes that are already monitored before we update the series (we do not want to unmonitored episodes that are monitored beforehand)
|
||||||
existingSeason.monitored = true;
|
existingSeason.monitored = true;
|
||||||
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
||||||
sea.monitored = true;
|
sea.monitored = true;
|
||||||
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
|
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
|
||||||
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
|
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
|
||||||
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
||||||
var epToUnmonitor = new List<Episode>();
|
var epToUnmonitored = new List<Episode>();
|
||||||
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
|
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member
|
||||||
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
||||||
{
|
{
|
||||||
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
|
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
|
||||||
|
@ -366,10 +366,10 @@ namespace Ombi.Core.Senders
|
||||||
// continue;
|
// continue;
|
||||||
//}
|
//}
|
||||||
ep.monitored = false;
|
ep.monitored = false;
|
||||||
epToUnmonitor.Add(ep);
|
epToUnmonitored.Add(ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var epToUpdate in epToUnmonitor)
|
foreach (var epToUpdate in epToUnmonitored)
|
||||||
{
|
{
|
||||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<ILidarrAvailabilityChecker, LidarrAvailabilityChecker>();
|
services.AddTransient<ILidarrAvailabilityChecker, LidarrAvailabilityChecker>();
|
||||||
services.AddTransient<IIssuesPurge, IssuesPurge>();
|
services.AddTransient<IIssuesPurge, IssuesPurge>();
|
||||||
services.AddTransient<IResendFailedRequests, ResendFailedRequests>();
|
services.AddTransient<IResendFailedRequests, ResendFailedRequests>();
|
||||||
|
services.AddTransient<IMediaDatabaseRefresh, MediaDatabaseRefresh>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
19
src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj
Normal file
19
src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="nunit" Version="3.11.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
52
src/Ombi.Helpers.Tests/PlexHelperTests.cs
Normal file
52
src/Ombi.Helpers.Tests/PlexHelperTests.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Helpers.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class PlexHelperTests
|
||||||
|
{
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(ProviderIdGuidData))]
|
||||||
|
public string GetProviderIdFromPlexGuidTests(string guidInput, ProviderIdType type)
|
||||||
|
{
|
||||||
|
var result = PlexHelper.GetProviderIdFromPlexGuid(guidInput);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ProviderIdType.Imdb:
|
||||||
|
Assert.That(result.ImdbId, Is.Not.Null);
|
||||||
|
return result.ImdbId;
|
||||||
|
case ProviderIdType.TvDb:
|
||||||
|
Assert.That(result.TheTvDb, Is.Not.Null);
|
||||||
|
return result.TheTvDb;
|
||||||
|
case ProviderIdType.MovieDb:
|
||||||
|
Assert.That(result.TheMovieDb, Is.Not.Null);
|
||||||
|
return result.TheMovieDb;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TestCaseData> ProviderIdGuidData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new TestCaseData("com.plexapp.agents.thetvdb://269586/2/8?lang=en", ProviderIdType.TvDb).Returns("269586").SetName("Regular TvDb Id");
|
||||||
|
yield return new TestCaseData("com.plexapp.agents.themoviedb://390043?lang=en", ProviderIdType.MovieDb).Returns("390043").SetName("Regular MovieDb Id");
|
||||||
|
yield return new TestCaseData("com.plexapp.agents.imdb://tt2543164?lang=en", ProviderIdType.Imdb).Returns("tt2543164").SetName("Regular Imdb Id");
|
||||||
|
yield return new TestCaseData("com.plexapp.agents.agent47://tt2543456?lang=en", ProviderIdType.Imdb).Returns("tt2543456").SetName("Unknown IMDB agent");
|
||||||
|
yield return new TestCaseData("com.plexapp.agents.agent47://456822/1/1?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent");
|
||||||
|
yield return new TestCaseData("com.plexapp.agents.agent47://456822/999/999?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent, large episode and season");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ProviderIdType
|
||||||
|
{
|
||||||
|
Imdb,
|
||||||
|
TvDb,
|
||||||
|
MovieDb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ namespace Ombi.Helpers
|
||||||
public static EventId PlexContentCacher => new EventId(2008);
|
public static EventId PlexContentCacher => new EventId(2008);
|
||||||
public static EventId SickRageCacher => new EventId(2009);
|
public static EventId SickRageCacher => new EventId(2009);
|
||||||
public static EventId LidarrArtistCache => new EventId(2010);
|
public static EventId LidarrArtistCache => new EventId(2010);
|
||||||
|
public static EventId MediaReferesh => new EventId(2011);
|
||||||
|
|
||||||
public static EventId MovieSender => new EventId(3000);
|
public static EventId MovieSender => new EventId(3000);
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
|
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
|
||||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -27,11 +27,14 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Ombi.Helpers
|
namespace Ombi.Helpers
|
||||||
{
|
{
|
||||||
public class PlexHelper
|
public class PlexHelper
|
||||||
{
|
{
|
||||||
|
private const string ImdbMatchExpression = "tt([0-9]{1,10})";
|
||||||
|
private const string TvDbIdMatchExpression = "//[0-9]+/([0-9]{1,3})/([0-9]{1,3})";
|
||||||
|
|
||||||
public static ProviderId GetProviderIdFromPlexGuid(string guid)
|
public static ProviderId GetProviderIdFromPlexGuid(string guid)
|
||||||
{
|
{
|
||||||
|
@ -52,7 +55,7 @@ namespace Ombi.Helpers
|
||||||
{
|
{
|
||||||
TheTvDb = guidSplit[1]
|
TheTvDb = guidSplit[1]
|
||||||
};
|
};
|
||||||
}
|
} else
|
||||||
if (guid.Contains("themoviedb", CompareOptions.IgnoreCase))
|
if (guid.Contains("themoviedb", CompareOptions.IgnoreCase))
|
||||||
{
|
{
|
||||||
return new ProviderId
|
return new ProviderId
|
||||||
|
@ -60,6 +63,7 @@ namespace Ombi.Helpers
|
||||||
TheMovieDb = guidSplit[1]
|
TheMovieDb = guidSplit[1]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (guid.Contains("imdb", CompareOptions.IgnoreCase))
|
if (guid.Contains("imdb", CompareOptions.IgnoreCase))
|
||||||
{
|
{
|
||||||
return new ProviderId
|
return new ProviderId
|
||||||
|
@ -67,6 +71,31 @@ namespace Ombi.Helpers
|
||||||
ImdbId = guidSplit[1]
|
ImdbId = guidSplit[1]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var imdbRegex = new Regex(ImdbMatchExpression, RegexOptions.Compiled);
|
||||||
|
var tvdbRegex = new Regex(TvDbIdMatchExpression, RegexOptions.Compiled);
|
||||||
|
var imdbMatch = imdbRegex.IsMatch(guid);
|
||||||
|
if (imdbMatch)
|
||||||
|
{
|
||||||
|
return new ProviderId
|
||||||
|
{
|
||||||
|
ImdbId = guidSplit[1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if it matches the TvDb pattern
|
||||||
|
var tvdbMatch = tvdbRegex.IsMatch(guid);
|
||||||
|
if (tvdbMatch)
|
||||||
|
{
|
||||||
|
return new ProviderId
|
||||||
|
{
|
||||||
|
TheTvDb = guidSplit[1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new ProviderId();
|
return new ProviderId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<title>Ombi</title>
|
<title>Ombi</title>
|
||||||
<style type="text/css">
|
<style>
|
||||||
|
/* -------------------------------------
|
||||||
|
GLOBAL RESETS
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
|
/*All the styling goes here*/
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border: none;
|
border: none;
|
||||||
-ms-interpolation-mode: bicubic;
|
-ms-interpolation-mode: bicubic;
|
||||||
|
@ -12,6 +19,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
background-color: #1f1f1f;
|
||||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -20,6 +28,8 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
-ms-text-size-adjust: 100%;
|
-ms-text-size-adjust: 100%;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
|
color: #FFF;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
|
@ -31,39 +41,54 @@
|
||||||
|
|
||||||
table td {
|
table td {
|
||||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
BODY & CONTAINER
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
|
color: #FFF;
|
||||||
|
background-color: #1f1f1f;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: block;
|
Margin: 0 auto !important;
|
||||||
margin: 0 auto !important;
|
/* makes it centered */
|
||||||
max-width: 1042px;
|
max-width: 1036px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: 1042px;
|
width: 1036px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
Margin: 0 auto;
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 1037px;
|
max-width: 1037px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
HEADER, FOOTER, MAIN
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
|
color: #FFF;
|
||||||
|
background: #1f1f1f;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 5px;
|
padding: 20px;
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-block {
|
.content-block {
|
||||||
|
@ -71,58 +96,241 @@
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.media-card {
|
||||||
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 3px;
|
||||||
|
width: 500px;
|
||||||
|
min-width: 500px;
|
||||||
|
max-width: 500px;
|
||||||
|
height: 252px;
|
||||||
|
max-height: 252px;
|
||||||
|
min-height: 252px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bg {
|
||||||
|
background-image: url({0});
|
||||||
|
border-collapse: separate;
|
||||||
|
mso-table-lspace: 0pt;
|
||||||
|
mso-table-rspace: 0pt;
|
||||||
|
width: 500px;
|
||||||
|
background-color: #1f1f1f;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 2px solid rgba(255, 118, 27, .4);
|
||||||
|
height: 252px;
|
||||||
|
max-height: 252px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-tint {
|
||||||
|
background-color: rgba(0, 0, 0, .6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-container {
|
||||||
|
vertical-align: top;
|
||||||
|
width: 150px;
|
||||||
|
min-width: 150px;
|
||||||
|
height: 225px;
|
||||||
|
max-height: 225px;
|
||||||
|
min-height: 225px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-img {}
|
||||||
|
|
||||||
|
.poster-overlay {}
|
||||||
|
|
||||||
|
.movie-info {
|
||||||
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
|
vertical-align: top;
|
||||||
|
padding-left: 4px;
|
||||||
|
text-align: left;
|
||||||
|
height: 227px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title h1 {
|
||||||
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 24px;
|
||||||
|
vertical-align: top;
|
||||||
|
max-width: 320px;
|
||||||
|
white-space: normal;
|
||||||
|
display: block;
|
||||||
|
height: 50px;
|
||||||
|
min-height: 50px;
|
||||||
|
max-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
|
height: 130px;
|
||||||
|
max-height: 130px;
|
||||||
|
max-width: 320px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
font-size: 14px !important;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
|
max-width: 300px;
|
||||||
|
min-width: 300px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px !important;
|
||||||
|
line-height: 1;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: rgba(255, 118, 27, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-top: 10px;
|
Margin-top: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer td,
|
.footer td,
|
||||||
.footer p,
|
.footer p,
|
||||||
.footer span,
|
.footer span,
|
||||||
.footer a {
|
.footer a {
|
||||||
color: #fff;
|
color: #999999;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
/* -------------------------------------
|
||||||
color: #ffffff;
|
TYPOGRAPHY
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4 {
|
||||||
|
color: #ff761b;
|
||||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin: 0;
|
font-size: 22px;
|
||||||
color: #ff761b;
|
line-height: 24px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p,
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
font-weight: 400;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p li,
|
p li,
|
||||||
ul li {
|
ul li,
|
||||||
|
ol li {
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
color: #ff761b !important;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
OTHER STYLES THAT MIGHT BE USEFUL
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
|
.last {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt0 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb0 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preheader {
|
||||||
|
color: transparent;
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
max-height: 0;
|
||||||
|
max-width: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
mso-hide: all;
|
||||||
|
visibility: hidden;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.powered-by a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1040px) {
|
hr {
|
||||||
.media-card {
|
border: 0;
|
||||||
display: block !important;
|
border-bottom: 1px solid #f6f6f6;
|
||||||
margin-top: 0 !important;
|
Margin: 20px 0;
|
||||||
margin-right: auto !important;
|
}
|
||||||
margin-bottom: 10px !important;
|
|
||||||
margin-left: auto !important;
|
/* -------------------------------------
|
||||||
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1059px) {
|
||||||
|
table[class=body] h1 {
|
||||||
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table[class=body] h1 {
|
table[class=body] p,
|
||||||
font-size: 28px !important;
|
table[class=body] ul,
|
||||||
|
table[class=body] ol,
|
||||||
|
table[class=body] td,
|
||||||
|
table[class=body] span,
|
||||||
|
table[class=body] a {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .wrapper,
|
||||||
|
table[class=body] .article {
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .content {
|
||||||
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
table[class=body] .container {
|
table[class=body] .container {
|
||||||
|
@ -135,9 +343,52 @@
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
border-right-width: 0 !important;
|
border-right-width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table[class=body] .btn table {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .btn a {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .img-responsive {
|
||||||
|
height: auto !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-card {
|
||||||
|
display: flex !important;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
height: 100% !important;
|
||||||
|
width: 100% !important;
|
||||||
|
width: 500px !important;
|
||||||
|
max-width: 500px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
PRESERVE THESE STYLES IN THE HEAD
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
@media all {
|
@media all {
|
||||||
|
.ExternalClass {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExternalClass,
|
||||||
|
.ExternalClass p,
|
||||||
|
.ExternalClass span,
|
||||||
|
.ExternalClass font,
|
||||||
|
.ExternalClass td,
|
||||||
|
.ExternalClass div {
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.apple-link a {
|
.apple-link a {
|
||||||
color: inherit !important;
|
color: inherit !important;
|
||||||
font-family: inherit !important;
|
font-family: inherit !important;
|
||||||
|
@ -146,66 +397,78 @@
|
||||||
line-height: inherit !important;
|
line-height: inherit !important;
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary table td:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary a:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
border-color: #34495e !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;font-size: 14px;line-height: 1.4;margin: 0;padding: 0;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; background-color: #1f1f1f; color: #fff;">
|
|
||||||
<tr>
|
|
||||||
<td class="container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;display: block;max-width: 1042px;padding: 10px;width: 1042px;margin: 0 auto !important;">
|
|
||||||
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 1037px; padding: 10px;">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
<body class="" style="background-color: #1f1f1f; font-family: 'Open Sans', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #FFF; text-align: center;">
|
||||||
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Ombi Recently Added</span>
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" width="100%" bgcolor="#1f1f1f" align="center" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #FFF; background-color: #1f1f1f; width: 100%; text-align: center;">
|
||||||
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; border-radius: 3px;">
|
<tbody>
|
||||||
|
<tr>
|
||||||
<!-- START MAIN CONTENT AREA -->
|
<td class="container" width="1036" valign="top" style="font-family: sans-serif; font-size: 14px; vertical-align: top; max-width: 1036px; padding: 10px; width: 1036px; Margin: 0 auto;">
|
||||||
<tr>
|
<div class="content" style="box-sizing: border-box; Margin: 0 auto; max-width: 1037px; padding: 10px;">
|
||||||
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
|
<!-- START CENTERED WHITE CONTAINER -->
|
||||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width: 1042px; width: 100%;">
|
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Ombi recently added</span>
|
||||||
<tr>
|
<table role="presentation" class="main" width="100%" align="center" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #FFF; background: #1f1f1f; border-radius: 3px; width: 100%; text-align: center;">
|
||||||
<td align="center">
|
<!-- START MAIN CONTENT AREA -->
|
||||||
<img src="{@LOGO}" width="400px" text-align="center"/>
|
<tbody>
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td class="wrapper" valign="top" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;">
|
||||||
<tr>
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
|
<tbody>
|
||||||
<br />
|
<tr>
|
||||||
<br />
|
<td valign="top" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top;">
|
||||||
<p style="color: #fff; font-family: sans-serif; font-size: 20px; font-weight: normal; margin: 0; Margin-bottom: 15px; text-align: center;">{@INTRO}</p>
|
<img src="{@LOGO}" style="border: none; -ms-interpolation-mode: bicubic; max-width: 100%;">
|
||||||
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
</table>
|
<td valign="top" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top;">
|
||||||
|
<p style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: normal; margin: 0; margin-bottom: 15px;">{@INTRO}</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td valign="top" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top;">
|
||||||
{@RECENTLYADDED}
|
{@RECENTLYADDED}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</table>
|
||||||
<!-- END MAIN CONTENT AREA -->
|
</td>
|
||||||
</table>
|
</tr>
|
||||||
|
<!-- END MAIN CONTENT AREA -->
|
||||||
<!-- START FOOTER -->
|
</tbody>
|
||||||
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
|
||||||
<tr>
|
|
||||||
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
|
|
||||||
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
<!-- START FOOTER -->
|
||||||
|
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
|
||||||
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="content-block powered-by" valign="top" align="center" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;">
|
||||||
|
Powered by <a href="https://github.com/tidusjar/Ombi" style="font-weight: 400; font-size: 12px; text-align: center; text-decoration: none; color: #ff761b;">Ombi</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- END FOOTER -->
|
||||||
|
<!-- END CENTERED WHITE CONTAINER -->
|
||||||
</div>
|
</div>
|
||||||
|
</td>
|
||||||
<!-- END FOOTER -->
|
</tr>
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
</tbody>
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,14 +1,14 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"></packagereference>
|
||||||
<PackageReference Include="Moq" Version="4.10.0" />
|
<PackageReference Include="Moq" Version="4.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -83,22 +83,20 @@ namespace Ombi.Notifications
|
||||||
{
|
{
|
||||||
client.Authenticate(settings.Username, settings.Password);
|
client.Authenticate(settings.Username, settings.Password);
|
||||||
}
|
}
|
||||||
//Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated);
|
_log.LogDebug("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated);
|
||||||
await client.SendAsync(message);
|
await client.SendAsync(message);
|
||||||
await client.DisconnectAsync(true);
|
await client.DisconnectAsync(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
//Log.Error(e);
|
_log.LogError(e, "Exception when attempting to send an email");
|
||||||
throw new InvalidOperationException(e.Message);
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Send(NotificationMessage model, EmailNotificationSettings settings)
|
public async Task Send(NotificationMessage model, EmailNotificationSettings settings)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
EnsureArg.IsNotNullOrEmpty(settings.SenderAddress);
|
EnsureArg.IsNotNullOrEmpty(settings.SenderAddress);
|
||||||
EnsureArg.IsNotNullOrEmpty(model.To);
|
EnsureArg.IsNotNullOrEmpty(model.To);
|
||||||
EnsureArg.IsNotNullOrEmpty(model.Message);
|
EnsureArg.IsNotNullOrEmpty(model.Message);
|
||||||
|
@ -120,9 +118,18 @@ namespace Ombi.Notifications
|
||||||
Subject = model.Subject
|
Subject = model.Subject
|
||||||
};
|
};
|
||||||
|
|
||||||
message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress));
|
|
||||||
message.To.Add(new MailboxAddress(model.To, model.To));
|
message.To.Add(new MailboxAddress(model.To, model.To));
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Send(MimeMessage message, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress));
|
||||||
|
|
||||||
using (var client = new SmtpClient())
|
using (var client = new SmtpClient())
|
||||||
{
|
{
|
||||||
if (settings.DisableCertificateChecking)
|
if (settings.DisableCertificateChecking)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MimeKit;
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
using Ombi.Settings.Settings.Models.Notifications;
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
|
||||||
|
@ -8,5 +9,6 @@ namespace Ombi.Notifications
|
||||||
{
|
{
|
||||||
Task Send(NotificationMessage model, EmailNotificationSettings settings);
|
Task Send(NotificationMessage model, EmailNotificationSettings settings);
|
||||||
Task SendAdHoc(NotificationMessage model, EmailNotificationSettings settings);
|
Task SendAdHoc(NotificationMessage model, EmailNotificationSettings settings);
|
||||||
|
Task Send(MimeMessage message, EmailNotificationSettings settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,10 +17,6 @@ namespace Ombi.Notifications
|
||||||
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
if (pref != null)
|
|
||||||
{
|
|
||||||
UserPreference = pref.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
|
@ -41,6 +37,10 @@ namespace Ombi.Notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
||||||
|
if (pref != null)
|
||||||
|
{
|
||||||
|
UserPreference = pref.Value.HasValue() ? pref.Value : Alias;
|
||||||
|
}
|
||||||
Title = title;
|
Title = title;
|
||||||
RequestedDate = req?.RequestedDate.ToString("D");
|
RequestedDate = req?.RequestedDate.ToString("D");
|
||||||
if (Type.IsNullOrEmpty())
|
if (Type.IsNullOrEmpty())
|
||||||
|
@ -49,7 +49,8 @@ namespace Ombi.Notifications
|
||||||
}
|
}
|
||||||
Overview = req?.Overview;
|
Overview = req?.Overview;
|
||||||
Year = req?.ReleaseDate.Year.ToString();
|
Year = req?.ReleaseDate.Year.ToString();
|
||||||
|
DenyReason = req?.DeniedReason;
|
||||||
|
AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty;
|
||||||
if (req?.RequestType == RequestType.Movie)
|
if (req?.RequestType == RequestType.Movie)
|
||||||
{
|
{
|
||||||
PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase)
|
PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase)
|
||||||
|
@ -66,10 +67,7 @@ namespace Ombi.Notifications
|
||||||
public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
if (pref != null)
|
|
||||||
{
|
|
||||||
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
|
||||||
}
|
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
{
|
{
|
||||||
|
@ -88,7 +86,13 @@ namespace Ombi.Notifications
|
||||||
UserName = req?.RequestedUser?.UserName;
|
UserName = req?.RequestedUser?.UserName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty;
|
||||||
|
DenyReason = req?.DeniedReason;
|
||||||
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
||||||
|
if (pref != null)
|
||||||
|
{
|
||||||
|
UserPreference = pref.Value.HasValue() ? pref.Value : Alias;
|
||||||
|
}
|
||||||
Title = title;
|
Title = title;
|
||||||
RequestedDate = req?.RequestedDate.ToString("D");
|
RequestedDate = req?.RequestedDate.ToString("D");
|
||||||
if (Type.IsNullOrEmpty())
|
if (Type.IsNullOrEmpty())
|
||||||
|
@ -101,22 +105,15 @@ namespace Ombi.Notifications
|
||||||
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetupNewsletter(CustomizationSettings s, OmbiUser username)
|
public void SetupNewsletter(CustomizationSettings s)
|
||||||
{
|
{
|
||||||
ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty;
|
ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty;
|
||||||
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName;
|
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName;
|
||||||
RequestedUser = username.UserName;
|
|
||||||
UserName = username.UserName;
|
|
||||||
Alias = username.Alias.HasValue() ? username.Alias : username.UserName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref)
|
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
if (pref != null)
|
|
||||||
{
|
|
||||||
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
|
||||||
}
|
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
{
|
{
|
||||||
|
@ -126,6 +123,7 @@ namespace Ombi.Notifications
|
||||||
{
|
{
|
||||||
title = req?.ParentRequest.Title;
|
title = req?.ParentRequest.Title;
|
||||||
}
|
}
|
||||||
|
DenyReason = req?.DeniedReason;
|
||||||
ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty;
|
ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty;
|
||||||
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName;
|
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName;
|
||||||
RequestedUser = req?.RequestedUser?.UserName;
|
RequestedUser = req?.RequestedUser?.UserName;
|
||||||
|
@ -134,7 +132,12 @@ namespace Ombi.Notifications
|
||||||
// Can be set if it's an issue
|
// Can be set if it's an issue
|
||||||
UserName = req?.RequestedUser?.UserName;
|
UserName = req?.RequestedUser?.UserName;
|
||||||
}
|
}
|
||||||
|
AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty;
|
||||||
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
||||||
|
if (pref != null)
|
||||||
|
{
|
||||||
|
UserPreference = pref.Value.HasValue() ? pref.Value : Alias;
|
||||||
|
}
|
||||||
Title = title;
|
Title = title;
|
||||||
RequestedDate = req?.RequestedDate.ToString("D");
|
RequestedDate = req?.RequestedDate.ToString("D");
|
||||||
if (Type.IsNullOrEmpty())
|
if (Type.IsNullOrEmpty())
|
||||||
|
@ -217,7 +220,6 @@ namespace Ombi.Notifications
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
public string IssueUser => UserName;
|
public string IssueUser => UserName;
|
||||||
public string Alias { get; set; }
|
public string Alias { get; set; }
|
||||||
|
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string RequestedDate { get; set; }
|
public string RequestedDate { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
@ -235,6 +237,8 @@ namespace Ombi.Notifications
|
||||||
public string IssueSubject { get; set; }
|
public string IssueSubject { get; set; }
|
||||||
public string NewIssueComment { get; set; }
|
public string NewIssueComment { get; set; }
|
||||||
public string UserPreference { get; set; }
|
public string UserPreference { get; set; }
|
||||||
|
public string DenyReason { get; set; }
|
||||||
|
public string AvailableDate { get; set; }
|
||||||
|
|
||||||
// System Defined
|
// System Defined
|
||||||
private string LongDate => DateTime.Now.ToString("D");
|
private string LongDate => DateTime.Now.ToString("D");
|
||||||
|
@ -269,6 +273,8 @@ namespace Ombi.Notifications
|
||||||
{nameof(UserName),UserName},
|
{nameof(UserName),UserName},
|
||||||
{nameof(Alias),Alias},
|
{nameof(Alias),Alias},
|
||||||
{nameof(UserPreference),UserPreference},
|
{nameof(UserPreference),UserPreference},
|
||||||
|
{nameof(DenyReason),DenyReason},
|
||||||
|
{nameof(AvailableDate),AvailableDate},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.3" />
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
<PackageReference Include="Moq" Version="4.10.0" />
|
<PackageReference Include="Moq" Version="4.10.0" />
|
||||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"></packagereference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Ombi.Schedule.Jobs.Emby;
|
||||||
using Ombi.Schedule.Jobs.Lidarr;
|
using Ombi.Schedule.Jobs.Lidarr;
|
||||||
using Ombi.Schedule.Jobs.Ombi;
|
using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
|
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||||
using Ombi.Schedule.Jobs.Radarr;
|
using Ombi.Schedule.Jobs.Radarr;
|
||||||
using Ombi.Schedule.Jobs.SickRage;
|
using Ombi.Schedule.Jobs.SickRage;
|
||||||
using Ombi.Schedule.Jobs.Sonarr;
|
using Ombi.Schedule.Jobs.Sonarr;
|
||||||
|
@ -21,7 +22,7 @@ namespace Ombi.Schedule
|
||||||
IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache,
|
IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache,
|
||||||
ISettingsService<JobSettings> jobsettings, ISickRageSync srSync, IRefreshMetadata refresh,
|
ISettingsService<JobSettings> jobsettings, ISickRageSync srSync, IRefreshMetadata refresh,
|
||||||
INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist,
|
INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist,
|
||||||
IIssuesPurge purge, IResendFailedRequests resender)
|
IIssuesPurge purge, IResendFailedRequests resender, IMediaDatabaseRefresh dbRefresh)
|
||||||
{
|
{
|
||||||
_plexContentSync = plexContentSync;
|
_plexContentSync = plexContentSync;
|
||||||
_radarrSync = radarrSync;
|
_radarrSync = radarrSync;
|
||||||
|
@ -39,6 +40,7 @@ namespace Ombi.Schedule
|
||||||
_lidarrArtistSync = artist;
|
_lidarrArtistSync = artist;
|
||||||
_issuesPurge = purge;
|
_issuesPurge = purge;
|
||||||
_resender = resender;
|
_resender = resender;
|
||||||
|
_mediaDatabaseRefresh = dbRefresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexContentSync _plexContentSync;
|
private readonly IPlexContentSync _plexContentSync;
|
||||||
|
@ -57,6 +59,7 @@ namespace Ombi.Schedule
|
||||||
private readonly ILidarrArtistSync _lidarrArtistSync;
|
private readonly ILidarrArtistSync _lidarrArtistSync;
|
||||||
private readonly IIssuesPurge _issuesPurge;
|
private readonly IIssuesPurge _issuesPurge;
|
||||||
private readonly IResendFailedRequests _resender;
|
private readonly IResendFailedRequests _resender;
|
||||||
|
private readonly IMediaDatabaseRefresh _mediaDatabaseRefresh;
|
||||||
|
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
|
@ -80,9 +83,9 @@ namespace Ombi.Schedule
|
||||||
RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s));
|
RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s));
|
||||||
RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s));
|
RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s));
|
||||||
RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s));
|
RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s));
|
||||||
|
RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|
|
@ -83,7 +83,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
{
|
{
|
||||||
processed++;
|
processed++;
|
||||||
|
|
||||||
if (ep.LocationType.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase))
|
if (ep.LocationType?.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase) ?? false)
|
||||||
{
|
{
|
||||||
// For some reason Emby is not respecting the `IsVirtualItem` field.
|
// For some reason Emby is not respecting the `IsVirtualItem` field.
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -7,18 +7,18 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url)
|
protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url)
|
||||||
{
|
{
|
||||||
sb.Append("<td align=\"center\" valign=\"top\" class=\"media-card\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 12px; vertical-align: top; padding: 3px; width: 502px; min-width: 500px; max-width: 500px; height: 235px; \">");
|
sb.Append("<td align=\"center\" valign=\"top\" width=\"500\" height=\"252\" class=\"media-card\" style=\"font-size: 14px; font-family: 'Open Sans', Helvetica, Arial, sans-serif; vertical-align: top; padding: 3px; width: 500px; min-width: 500px; max-width: 500px; height: 252px; max-height: 252px; \">");
|
||||||
sb.AppendFormat("<table class=\"card-bg\" style=\"background-image: url({0}); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 500px; background-color: #1f1f1f; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 2px solid rgba(255,118,27,.4); height: 248px; max-height: 500px; \">", url);
|
sb.AppendFormat("<table class=\"card-bg\" width=\"500\" height=\"252\" background=\"url(0)\" bgcolor=\"#1f1f1f\" style=\"background-image: url(0); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 500px; background-color: #1f1f1f; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 2px solid rgba(255, 118, 27, .4); height: 252px; max-height: 252px; \">", url);
|
||||||
sb.Append("<tr>");
|
sb.Append("<tr>");
|
||||||
sb.Append("<td>");
|
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||||
sb.Append("<table class=\"bg-tint\" style=\"background-color: rgba(0, 0, 0, .6); position: absolute; width: 490px; height: 239px; \">");
|
sb.Append("<table class=\"bg-tint\" width=\"100%\" bgcolor=\"rgba(0, 0, 0, .6)\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: rgba(0, 0, 0, .6); \">");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddPosterInsideTable(StringBuilder sb, string url)
|
protected virtual void AddPosterInsideTable(StringBuilder sb, string url)
|
||||||
{
|
{
|
||||||
sb.Append("<tr>");
|
sb.Append("<tr>");
|
||||||
sb.Append("<td class=\"poster-container\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; width: 150px; min-width: 15px; height: 225px; \">");
|
sb.Append("<td class=\"poster-container\" width=\"150\" height=\"225\" valign=\"top\" style=\"ont-family: sans-serif; font-size: 14px; vertical-align: top; width: 150px; min-width: 150px; height: 225px; max-height: 225px; min-height: 225px; \">");
|
||||||
sb.AppendFormat("<table class=\"poster-img\" style=\"background-image: url({0}); border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: transparent; background-position: center; background-size: cover; background-repeat: no-repeat; background-clip: padding-box; border: 1px solid rgba(255,255,255,.1); \">", url);
|
sb.AppendFormat("<table class=\"poster-img\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url)
|
protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url)
|
||||||
|
@ -27,10 +27,10 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
sb.Append("<tr>");
|
sb.Append("<tr>");
|
||||||
sb.Append(
|
sb.Append(
|
||||||
"<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
"<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||||
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
|
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
"<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none;-ms-interpolation-mode: bicubic; max-width: 100%;display: block; visibility: hidden; \">",
|
"<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none; -ms-interpolation-mode: bicubic; max-width: 100%; \">",
|
||||||
url);
|
url);
|
||||||
sb.Append("</a>");
|
sb.Append("</a>");
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
|
@ -44,16 +44,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
protected virtual void AddInfoTable(StringBuilder sb)
|
protected virtual void AddInfoTable(StringBuilder sb)
|
||||||
{
|
{
|
||||||
sb.Append(
|
sb.Append(
|
||||||
"<td class=\"movie-info\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; padding-left: 4px; text-align: left; height: 227px; \">");
|
"<td class=\"movie-info\" height=\"227\" valign=\"top\" align=\"left\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; padding-left: 4px; text-align: left; height: 227px; \">");
|
||||||
sb.Append("<table style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; height: 100%; \">");
|
sb.Append("<table style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; height: 100%; \">");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddTitle(StringBuilder sb, string url, string title)
|
protected virtual void AddTitle(StringBuilder sb, string url, string title)
|
||||||
{
|
{
|
||||||
sb.Append("<tr>");
|
sb.Append("<tr class=\"title\" valign=\"top\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 22px;line-height: 24px;vertical-align: top;max-width: 320px;display: block;height: 50px;min-height: 50px;max-height: 50px; \">");
|
||||||
sb.Append("<td class=\"title\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.9rem; vertical-align: top; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; line-height: 1.2rem; padding: 5px; \">");
|
sb.Append("<td>");
|
||||||
if(url.HasValue()) sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", url);
|
if(url.HasValue()) sb.AppendFormat("<a href=\"{0}\" target=\"_blank\" style=\"text-decoration: none; font-weight: 400; color: #ff761b;\">", url);
|
||||||
sb.AppendFormat("<h1 style=\"white-space: normal; line-height: 1;\" >{0}</h1>", title);
|
sb.AppendFormat("<h1 style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 22px;line-height: 24px;vertical-align: top;max-width: 320px;display: block;height: 50px;min-height: 50px;max-height: 50px;\" >{0}</h1>", title);
|
||||||
if (url.HasValue()) sb.Append("</a>");
|
if (url.HasValue()) sb.Append("</a>");
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
sb.Append("</tr>");
|
sb.Append("</tr>");
|
||||||
|
@ -61,28 +61,28 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
|
|
||||||
protected virtual void AddParagraph(StringBuilder sb, string text)
|
protected virtual void AddParagraph(StringBuilder sb, string text)
|
||||||
{
|
{
|
||||||
sb.Append("<tr class=\"description\">");
|
sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">");
|
||||||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.75rem; vertical-align: top; padding: 5px; height: 100%; \">");
|
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||||
sb.AppendFormat("<p style=\"color: #fff; font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0; max-width: 325px; text-align: justify; \">{0}</p>", text);
|
sb.AppendFormat("<p style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: normal; margin: 0; margin-bottom: 15px; \">{0}</p>", text);
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
sb.Append("</tr>");
|
sb.Append("</tr>");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary)
|
protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary)
|
||||||
{
|
{
|
||||||
sb.Append("<tr class=\"description\">");
|
sb.Append("<tr class=\"description\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;height: 130px;max-height: 130px;max-width: 320px;overflow: hidden;text-overflow: ellipsis;display: block;font-size: 14px !important;text-align: justify;\" valign=\"top\">");
|
||||||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.75rem; vertical-align: top; padding: 5px; height: 100%; \">");
|
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||||
sb.AppendFormat("<p style=\"color: #fff; font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0; max-width: 325px; margin-bottom: 10px; \">{0}</p>", episodes);
|
sb.AppendFormat("<p style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: normal; margin: 0; margin-bottom: 15px; \">{0}</p>", episodes);
|
||||||
sb.AppendFormat("<div style=\"color: #fff; font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0; max-width: 325px; overflow: hidden; text-align: justify; \">{0}</div>", summary);
|
sb.AppendFormat("<div>{0}</div>", summary);
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
sb.Append("</tr>");
|
sb.Append("</tr>");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddGenres(StringBuilder sb, string text)
|
protected virtual void AddGenres(StringBuilder sb, string text)
|
||||||
{
|
{
|
||||||
sb.Append("<tr class=\"meta\">");
|
sb.Append("<tr class=\"meta\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; max-width: 300px; min-width: 300px; padding: 3px 7px; margin-top: 10px; line-height: 1; text-align: left; white-space: nowrap; vertical-align: middle; background-color: rgba(255, 118, 27, 0.5); color: #fff; border-radius: 2px; overflow: hidden; display: block; font-size: 0.9rem;\" align=\"left\" valign=\"middle\" bgcolor=\"rgba(255, 118, 27, 0.5)\">");
|
||||||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; max-width: 265px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; \">");
|
sb.Append("<td style=\"font-family: sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||||
sb.AppendFormat("<span style=\"display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 11px; line-height: 1; text-align: center; white-space: nowrap; vertical-align: middle; background-color: rgba(255, 118, 27, 0.5); color: #fff; border-radius: 2px; text-overflow: ellipsis; overflow: hidden; \">{0}</span>", text);
|
sb.AppendFormat("<span>{0}</span>", text);
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
sb.Append("</tr>");
|
sb.Append("</tr>");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Schedule.Jobs.Plex.Interfaces
|
||||||
|
{
|
||||||
|
public interface IMediaDatabaseRefresh : IBaseJob
|
||||||
|
{
|
||||||
|
Task Start();
|
||||||
|
}
|
||||||
|
}
|
120
src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs
Normal file
120
src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Hangfire;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Api.Plex;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Core.Settings.Models.External;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Schedule.Jobs.Emby;
|
||||||
|
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||||
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
|
namespace Ombi.Schedule.Jobs.Plex
|
||||||
|
{
|
||||||
|
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
|
||||||
|
{
|
||||||
|
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log, IPlexApi plexApi,
|
||||||
|
IPlexContentRepository plexRepo, IPlexContentSync c, IEmbyContentRepository embyRepo, IEmbyContentSync embySync)
|
||||||
|
{
|
||||||
|
_settings = s;
|
||||||
|
_log = log;
|
||||||
|
_api = plexApi;
|
||||||
|
_plexRepo = plexRepo;
|
||||||
|
_plexContentSync = c;
|
||||||
|
_embyRepo = embyRepo;
|
||||||
|
_embyContentSync = embySync;
|
||||||
|
_settings.ClearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ISettingsService<PlexSettings> _settings;
|
||||||
|
private readonly ILogger _log;
|
||||||
|
private readonly IPlexApi _api;
|
||||||
|
private readonly IPlexContentRepository _plexRepo;
|
||||||
|
private readonly IPlexContentSync _plexContentSync;
|
||||||
|
private readonly IEmbyContentRepository _embyRepo;
|
||||||
|
private readonly IEmbyContentSync _embyContentSync;
|
||||||
|
|
||||||
|
public async Task Start()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await RemovePlexData();
|
||||||
|
await RemoveEmbyData();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Media Data Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveEmbyData()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var s = await _settings.GetSettingsAsync();
|
||||||
|
if (!s.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string episodeSQL = "DELETE FROM EmbyEpisode";
|
||||||
|
const string mainSql = "DELETE FROM EmbyContent";
|
||||||
|
await _embyRepo.ExecuteSql(episodeSQL);
|
||||||
|
await _embyRepo.ExecuteSql(mainSql);
|
||||||
|
|
||||||
|
BackgroundJob.Enqueue(() => _embyContentSync.Start());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Emby Data Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemovePlexData()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var s = await _settings.GetSettingsAsync();
|
||||||
|
if (!s.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string episodeSQL = "DELETE FROM PlexEpisode";
|
||||||
|
const string seasonsSql = "DELETE FROM PlexSeasonsContent";
|
||||||
|
const string mainSql = "DELETE FROM PlexServerContent";
|
||||||
|
await _plexRepo.ExecuteSql(episodeSQL);
|
||||||
|
await _plexRepo.ExecuteSql(seasonsSql);
|
||||||
|
await _plexRepo.ExecuteSql(mainSql);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Plex Data Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_plexRepo?.Dispose();
|
||||||
|
_settings?.Dispose();
|
||||||
|
}
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ using MailKit;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MimeKit;
|
||||||
using Ombi.Api.Lidarr;
|
using Ombi.Api.Lidarr;
|
||||||
using Ombi.Api.Lidarr.Models;
|
using Ombi.Api.Lidarr.Models;
|
||||||
using Ombi.Api.TheMovieDb;
|
using Ombi.Api.TheMovieDb;
|
||||||
|
@ -24,6 +25,7 @@ using Ombi.Settings.Settings.Models.External;
|
||||||
using Ombi.Settings.Settings.Models.Notifications;
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
using ContentType = Ombi.Store.Entities.ContentType;
|
||||||
|
|
||||||
namespace Ombi.Schedule.Jobs.Ombi
|
namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
|
@ -33,7 +35,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
||||||
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
||||||
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
||||||
ILidarrApi lidarrApi, IRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings)
|
ILidarrApi lidarrApi, IRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings,
|
||||||
|
ISettingsService<OmbiSettings> ombiSettings)
|
||||||
{
|
{
|
||||||
_plex = plex;
|
_plex = plex;
|
||||||
_emby = emby;
|
_emby = emby;
|
||||||
|
@ -53,6 +56,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
_lidarrApi = lidarrApi;
|
_lidarrApi = lidarrApi;
|
||||||
_lidarrAlbumRepository = albumCache;
|
_lidarrAlbumRepository = albumCache;
|
||||||
_lidarrSettings = lidarrSettings;
|
_lidarrSettings = lidarrSettings;
|
||||||
|
_ombiSettings = ombiSettings;
|
||||||
|
_ombiSettings.ClearCache();
|
||||||
_lidarrSettings.ClearCache();
|
_lidarrSettings.ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +71,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
private readonly INotificationTemplatesRepository _templateRepo;
|
private readonly INotificationTemplatesRepository _templateRepo;
|
||||||
private readonly ISettingsService<EmailNotificationSettings> _emailSettings;
|
private readonly ISettingsService<EmailNotificationSettings> _emailSettings;
|
||||||
private readonly ISettingsService<NewsletterSettings> _newsletterSettings;
|
private readonly ISettingsService<NewsletterSettings> _newsletterSettings;
|
||||||
|
private readonly ISettingsService<OmbiSettings> _ombiSettings;
|
||||||
private readonly UserManager<OmbiUser> _userManager;
|
private readonly UserManager<OmbiUser> _userManager;
|
||||||
private readonly ILogger _log;
|
private readonly ILogger _log;
|
||||||
private readonly ILidarrApi _lidarrApi;
|
private readonly ILidarrApi _lidarrApi;
|
||||||
|
@ -162,7 +168,23 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
Email = emails
|
Email = emails
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var emailTasks = new List<Task>();
|
|
||||||
|
var messageContent = ParseTemplate(template, customization);
|
||||||
|
var email = new NewsletterTemplate();
|
||||||
|
|
||||||
|
var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo);
|
||||||
|
|
||||||
|
var bodyBuilder = new BodyBuilder
|
||||||
|
{
|
||||||
|
HtmlBody = html,
|
||||||
|
};
|
||||||
|
|
||||||
|
var message = new MimeMessage
|
||||||
|
{
|
||||||
|
Body = bodyBuilder.ToMessageBody(),
|
||||||
|
Subject = messageContent.Subject
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var user in users)
|
foreach (var user in users)
|
||||||
{
|
{
|
||||||
// Get the users to send it to
|
// Get the users to send it to
|
||||||
|
@ -170,17 +192,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// BCC the messages
|
||||||
var messageContent = ParseTemplate(template, customization, user);
|
message.Bcc.Add(new MailboxAddress(user.Email, user.Email));
|
||||||
var email = new NewsletterTemplate();
|
|
||||||
|
|
||||||
var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo);
|
|
||||||
|
|
||||||
emailTasks.Add(_email.Send(
|
|
||||||
new NotificationMessage { Message = html, Subject = messageContent.Subject, To = user.Email },
|
|
||||||
emailSettings));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the email
|
||||||
|
await _email.Send(message, emailSettings);
|
||||||
|
|
||||||
// Now add all of this to the Recently Added log
|
// Now add all of this to the Recently Added log
|
||||||
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||||
foreach (var p in plexContentMoviesToSend)
|
foreach (var p in plexContentMoviesToSend)
|
||||||
|
@ -234,7 +252,6 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||||
await Task.WhenAll(emailTasks.ToArray());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -245,7 +262,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var messageContent = ParseTemplate(template, customization, a);
|
var messageContent = ParseTemplate(template, customization);
|
||||||
|
|
||||||
var email = new NewsletterTemplate();
|
var email = new NewsletterTemplate();
|
||||||
|
|
||||||
|
@ -305,12 +322,12 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
return itemsToReturn;
|
return itemsToReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings, OmbiUser username)
|
private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings)
|
||||||
{
|
{
|
||||||
var resolver = new NotificationMessageResolver();
|
var resolver = new NotificationMessageResolver();
|
||||||
var curlys = new NotificationMessageCurlys();
|
var curlys = new NotificationMessageCurlys();
|
||||||
|
|
||||||
curlys.SetupNewsletter(settings, username);
|
curlys.SetupNewsletter(settings);
|
||||||
|
|
||||||
return resolver.ParseMessage(template, curlys);
|
return resolver.ParseMessage(template, curlys);
|
||||||
}
|
}
|
||||||
|
@ -318,6 +335,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
|
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
|
||||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings)
|
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings)
|
||||||
{
|
{
|
||||||
|
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||||
|
@ -331,8 +349,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||||
sb.Append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
|
sb.Append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
|
||||||
sb.Append("<tr>");
|
sb.Append("<tr>");
|
||||||
await ProcessPlexMovies(plexMovies, sb);
|
await ProcessPlexMovies(plexMovies, sb, ombiSettings.DefaultLanguageCode);
|
||||||
await ProcessEmbyMovies(embyMovies, sb);
|
await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode);
|
||||||
sb.Append("</tr>");
|
sb.Append("</tr>");
|
||||||
sb.Append("</table>");
|
sb.Append("</table>");
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
|
@ -379,7 +397,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessPlexMovies(IQueryable<PlexServerContent> plexContentToSend, StringBuilder sb)
|
private async Task ProcessPlexMovies(IQueryable<PlexServerContent> plexContentToSend, StringBuilder sb, string defaultLanguageCode)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
var ordered = plexContentToSend.OrderByDescending(x => x.AddedAt);
|
var ordered = plexContentToSend.OrderByDescending(x => x.AddedAt);
|
||||||
|
@ -390,7 +408,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId);
|
var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId, defaultLanguageCode);
|
||||||
var mediaurl = content.Url;
|
var mediaurl = content.Url;
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
|
@ -453,7 +471,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessEmbyMovies(IQueryable<EmbyContent> embyContent, StringBuilder sb)
|
private async Task ProcessEmbyMovies(IQueryable<EmbyContent> embyContent, StringBuilder sb, string defaultLangaugeCode)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
var ordered = embyContent.OrderByDescending(x => x.AddedAt);
|
var ordered = embyContent.OrderByDescending(x => x.AddedAt);
|
||||||
|
@ -474,7 +492,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
}
|
}
|
||||||
|
|
||||||
var mediaurl = content.Url;
|
var mediaurl = content.Url;
|
||||||
var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId));
|
var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId), defaultLangaugeCode);
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="1.50.2" />
|
<PackageReference Include="Dapper" Version="1.50.2" />
|
||||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
<PackageReference Include="Hangfire" Version="1.6.21" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.19" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.21" />
|
||||||
<PackageReference Include="Hangfire.Console" Version="1.3.10" />
|
<PackageReference Include="Hangfire.Console" Version="1.3.10" />
|
||||||
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
|
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
|
||||||
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />
|
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />
|
||||||
<PackageReference Include="Hangfire.SQLite" Version="1.4.2" />
|
<PackageReference Include="Hangfire.SQLite" Version="1.4.2" />
|
||||||
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" />
|
<PackageReference Include="Serilog" Version="2.7.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.18.2" />
|
<PackageReference Include="SharpCompress" Version="0.18.2" />
|
||||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.6.13" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.6.13" />
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
9
src/Ombi.Settings/Settings/Models/CustomPageSettings.cs
Normal file
9
src/Ombi.Settings/Settings/Models/CustomPageSettings.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ombi.Settings.Settings.Models
|
||||||
|
{
|
||||||
|
public class CustomPageSettings : Settings
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Html { get; set; }
|
||||||
|
public string FontAwesomeIcon { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
public string CustomDonationMessage { get; set; }
|
public string CustomDonationMessage { get; set; }
|
||||||
public string Logo { get; set; }
|
public string Logo { get; set; }
|
||||||
public bool RecentlyAddedPage { get; set; }
|
public bool RecentlyAddedPage { get; set; }
|
||||||
|
public bool UseCustomPage { get; set; }
|
||||||
|
|
||||||
public void AddToUrl(string part)
|
public void AddToUrl(string part)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,5 +16,6 @@
|
||||||
public string LidarrArtistSync { get; set; }
|
public string LidarrArtistSync { get; set; }
|
||||||
public string IssuesPurge { get; set; }
|
public string IssuesPurge { get; set; }
|
||||||
public string RetryRequests { get; set; }
|
public string RetryRequests { get; set; }
|
||||||
|
public string MediaDatabaseRefresh { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -65,7 +65,10 @@ namespace Ombi.Settings.Settings.Models
|
||||||
{
|
{
|
||||||
return Get(s.RetryRequests, Cron.Daily(6));
|
return Get(s.RetryRequests, Cron.Daily(6));
|
||||||
}
|
}
|
||||||
|
public static string MediaDatabaseRefresh(JobSettings s)
|
||||||
|
{
|
||||||
|
return Get(s.MediaDatabaseRefresh, Cron.DayInterval(5));
|
||||||
|
}
|
||||||
private static string Get(string settings, string defaultCron)
|
private static string Get(string settings, string defaultCron)
|
||||||
{
|
{
|
||||||
return settings.HasValue() ? settings : defaultCron;
|
return settings.HasValue() ? settings : defaultCron;
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
{
|
{
|
||||||
public string BaseUrl { get; set; }
|
public string BaseUrl { get; set; }
|
||||||
public bool CollectAnalyticData { get; set; }
|
public bool CollectAnalyticData { get; set; }
|
||||||
|
public bool Set { get; set; }
|
||||||
public bool Wizard { get; set; }
|
public bool Wizard { get; set; }
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
public bool IgnoreCertificateErrors { get; set; }
|
public bool IgnoreCertificateErrors { get; set; }
|
||||||
public bool DoNotSendNotificationsForAutoApprove { get; set; }
|
public bool DoNotSendNotificationsForAutoApprove { get; set; }
|
||||||
public bool HideRequestsUsers { get; set; }
|
public bool HideRequestsUsers { get; set; }
|
||||||
|
public string DefaultLanguageCode { get; set; } = "en";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using Ombi.Helpers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Ombi.Store.Entities.Requests
|
namespace Ombi.Store.Entities.Requests
|
||||||
{
|
{
|
||||||
|
@ -19,5 +20,14 @@ namespace Ombi.Store.Entities.Requests
|
||||||
|
|
||||||
public int RootPathOverride { get; set; }
|
public int RootPathOverride { get; set; }
|
||||||
public int QualityOverride { get; set; }
|
public int QualityOverride { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only Use for setting the Language Code, Use the LanguageCode property for reading
|
||||||
|
/// </summary>
|
||||||
|
public string LangCode { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
[JsonIgnore]
|
||||||
|
public string LanguageCode => LangCode.IsNullOrEmpty() ? "en" : LangCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1206
src/Ombi.Store/Migrations/20190104203305_LanguageCode.Designer.cs
generated
Normal file
1206
src/Ombi.Store/Migrations/20190104203305_LanguageCode.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
22
src/Ombi.Store/Migrations/20190104203305_LanguageCode.cs
Normal file
22
src/Ombi.Store/Migrations/20190104203305_LanguageCode.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
public partial class LanguageCode : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "LangCode",
|
||||||
|
table: "MovieRequests",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LangCode",
|
||||||
|
table: "MovieRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
{
|
{
|
||||||
|
@ -530,6 +530,24 @@ namespace Ombi.Store.Migrations
|
||||||
b.ToTable("RequestQueue");
|
b.ToTable("RequestQueue");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("RequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RequestSubscription");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
@ -713,6 +731,8 @@ namespace Ombi.Store.Migrations
|
||||||
|
|
||||||
b.Property<int?>("IssueId");
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<string>("LangCode");
|
||||||
|
|
||||||
b.Property<DateTime>("MarkedAsApproved");
|
b.Property<DateTime>("MarkedAsApproved");
|
||||||
|
|
||||||
b.Property<DateTime?>("MarkedAsAvailable");
|
b.Property<DateTime?>("MarkedAsAvailable");
|
||||||
|
@ -800,24 +820,6 @@ namespace Ombi.Store.Migrations
|
||||||
b.ToTable("TvRequests");
|
b.ToTable("TvRequests");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd();
|
|
||||||
|
|
||||||
b.Property<int>("RequestId");
|
|
||||||
|
|
||||||
b.Property<int>("RequestType");
|
|
||||||
|
|
||||||
b.Property<string>("UserId");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RequestSubscription");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
@ -1082,6 +1084,13 @@ namespace Ombi.Store.Migrations
|
||||||
.HasForeignKey("PlexServerContentId");
|
.HasForeignKey("PlexServerContentId");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||||
|
@ -1146,13 +1155,6 @@ namespace Ombi.Store.Migrations
|
||||||
.HasForeignKey("UserId");
|
.HasForeignKey("UserId");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Ombi.Store.Migrations.Settings
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
|
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
|
||||||
<PackageReference Include="Moq" Version="4.10.0" />
|
<PackageReference Include="Moq" Version="4.10.0" />
|
||||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"></packagereference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -8,14 +8,14 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public interface IMovieDbApi
|
public interface IMovieDbApi
|
||||||
{
|
{
|
||||||
Task<MovieResponseDto> GetMovieInformation(int movieId);
|
Task<MovieResponseDto> GetMovieInformation(int movieId);
|
||||||
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId);
|
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en");
|
||||||
Task<List<MovieSearchResult>> NowPlaying();
|
Task<List<MovieSearchResult>> NowPlaying(string languageCode);
|
||||||
Task<List<MovieSearchResult>> PopularMovies();
|
Task<List<MovieSearchResult>> PopularMovies(string languageCode);
|
||||||
Task<List<MovieSearchResult>> SearchMovie(string searchTerm);
|
Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string languageCode);
|
||||||
Task<List<TvSearchResult>> SearchTv(string searchTerm);
|
Task<List<TvSearchResult>> SearchTv(string searchTerm);
|
||||||
Task<List<MovieSearchResult>> TopRated();
|
Task<List<MovieSearchResult>> TopRated(string languageCode);
|
||||||
Task<List<MovieSearchResult>> Upcoming();
|
Task<List<MovieSearchResult>> Upcoming(string languageCode);
|
||||||
Task<List<MovieSearchResult>> SimilarMovies(int movieId);
|
Task<List<MovieSearchResult>> SimilarMovies(int movieId, string langCode);
|
||||||
Task<FindResult> Find(string externalId, ExternalSource source);
|
Task<FindResult> Find(string externalId, ExternalSource source);
|
||||||
Task<TvExternals> GetTvExternals(int theMovieDbId);
|
Task<TvExternals> GetTvExternals(int theMovieDbId);
|
||||||
Task<TvInfo> GetTVInfo(string themoviedbid);
|
Task<TvInfo> GetTVInfo(string themoviedbid);
|
||||||
|
|
|
@ -63,68 +63,80 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return await Api.Request<TvExternals>(request);
|
return await Api.Request<TvExternals>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId)
|
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId)
|
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en")
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates");
|
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates");
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<MovieResponse>(request);
|
var result = await Api.Request<MovieResponse>(request);
|
||||||
return Mapper.Map<MovieResponseDto>(result);
|
return Mapper.Map<MovieResponseDto>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm)
|
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string langageCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
||||||
|
if (year.HasValue && year.Value > 0)
|
||||||
|
{
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("year", year.Value.ToString());
|
||||||
|
}
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> PopularMovies()
|
public async Task<List<MovieSearchResult>> PopularMovies(string langageCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/popular", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/popular", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> TopRated()
|
public async Task<List<MovieSearchResult>> TopRated(string langageCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/top_rated", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/top_rated", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> Upcoming()
|
public async Task<List<MovieSearchResult>> Upcoming(string langageCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/upcoming", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/upcoming", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> NowPlaying()
|
public async Task<List<MovieSearchResult>> NowPlaying(string langageCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/now_playing", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/now_playing", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>3.0.0.0</FileVersion>
|
<FileVersion>3.0.0.0</FileVersion>
|
||||||
<Version></Version>
|
<Version></Version>
|
||||||
|
@ -12,17 +12,17 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" />
|
||||||
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" />
|
<PackageReference Include="Serilog" Version="2.7.1" />
|
||||||
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
|
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="3.2.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.1-dev-00771" />
|
<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.1-dev-00771" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -94,7 +94,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.SickRage", "Ombi.A
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Notifications", "Ombi.Api.Notifications\Ombi.Api.Notifications.csproj", "{10D1FE9D-9124-42B7-B1E1-CEB99B832618}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Notifications", "Ombi.Api.Notifications\Ombi.Api.Notifications.csproj", "{10D1FE9D-9124-42B7-B1E1-CEB99B832618}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Helpers.Tests", "Ombi.Helpers.Tests\Ombi.Helpers.Tests.csproj", "{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -250,6 +252,10 @@ Global
|
||||||
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -286,6 +292,7 @@ Global
|
||||||
{94C9A366-2595-45EA-AABB-8E4A2E90EC5B} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{94C9A366-2595-45EA-AABB-8E4A2E90EC5B} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
{10D1FE9D-9124-42B7-B1E1-CEB99B832618} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{10D1FE9D-9124-42B7-B1E1-CEB99B832618} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
{4FA21A20-92F4-462C-B929-2C517A88CC56} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{4FA21A20-92F4-462C-B929-2C517A88CC56} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
|
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869}
|
SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869}
|
||||||
|
|
|
@ -34,6 +34,14 @@
|
||||||
<i class="fa fa-th-list"></i> {{ 'NavigationBar.Requests' | translate }}</a>
|
<i class="fa fa-th-list"></i> {{ 'NavigationBar.Requests' | translate }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div *ngIf="customizationSettings && customizationSettings.useCustomPage && customPageSettings">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li id="RecentlyAdded" [routerLinkActive]="['active']">
|
||||||
|
<a [routerLink]="['/Custom']">
|
||||||
|
<i class="fa {{customPageSettings.fontAwesomeIcon}}"></i> {{customPageSettings.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div *ngIf="customizationSettings">
|
<div *ngIf="customizationSettings">
|
||||||
<ul *ngIf="customizationSettings.recentlyAddedPage" class="nav navbar-nav">
|
<ul *ngIf="customizationSettings.recentlyAddedPage" class="nav navbar-nav">
|
||||||
<li id="RecentlyAdded" [routerLinkActive]="['active']">
|
<li id="RecentlyAdded" [routerLinkActive]="['active']">
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ILocalUser } from "./auth/IUserLogin";
|
||||||
import { IdentityService, NotificationService } from "./services";
|
import { IdentityService, NotificationService } from "./services";
|
||||||
import { JobService, SettingsService } from "./services";
|
import { JobService, SettingsService } from "./services";
|
||||||
|
|
||||||
import { ICustomizationSettings } from "./interfaces";
|
import { ICustomizationSettings, ICustomPage } from "./interfaces";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "ombi",
|
selector: "ombi",
|
||||||
|
@ -17,6 +17,7 @@ import { ICustomizationSettings } from "./interfaces";
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
|
|
||||||
public customizationSettings: ICustomizationSettings;
|
public customizationSettings: ICustomizationSettings;
|
||||||
|
public customPageSettings: ICustomPage;
|
||||||
public issuesEnabled = false;
|
public issuesEnabled = false;
|
||||||
public user: ILocalUser;
|
public user: ILocalUser;
|
||||||
public showNav: boolean;
|
public showNav: boolean;
|
||||||
|
@ -53,7 +54,18 @@ export class AppComponent implements OnInit {
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.user = this.authService.claims();
|
this.user = this.authService.claims();
|
||||||
|
|
||||||
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
this.settingsService.getCustomization().subscribe(x => {
|
||||||
|
this.customizationSettings = x;
|
||||||
|
if(this.customizationSettings.useCustomPage) {
|
||||||
|
this.settingsService.getCustomPage().subscribe(c => {
|
||||||
|
this.customPageSettings = c;
|
||||||
|
if(!this.customPageSettings.title) {
|
||||||
|
this.customPageSettings.title = "Custom Page";
|
||||||
|
this.customPageSettings.fontAwesomeIcon = "fa-check";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
this.settingsService.issueEnabled().subscribe(x => this.issuesEnabled = x);
|
this.settingsService.issueEnabled().subscribe(x => this.issuesEnabled = x);
|
||||||
this.settingsService.voteEnabled().subscribe(x => this.voteEnabled =x);
|
this.settingsService.voteEnabled().subscribe(x => this.voteEnabled =x);
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,16 @@ import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||||
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||||
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
|
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
|
||||||
import { CookieService } from "ng2-cookies";
|
import { CookieService } from "ng2-cookies";
|
||||||
|
import { NgxEditorModule } from "ngx-editor";
|
||||||
import { GrowlModule } from "primeng/components/growl/growl";
|
import { GrowlModule } from "primeng/components/growl/growl";
|
||||||
import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, OverlayPanelModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng";
|
import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, OverlayPanelModule, SharedModule, SidebarModule,
|
||||||
|
TooltipModule } from "primeng/primeng";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { AppComponent } from "./app.component";
|
import { AppComponent } from "./app.component";
|
||||||
|
|
||||||
import { CookieComponent } from "./auth/cookie.component";
|
import { CookieComponent } from "./auth/cookie.component";
|
||||||
|
import { CustomPageComponent } from "./custompage/custompage.component";
|
||||||
import { PageNotFoundComponent } from "./errors/not-found.component";
|
import { PageNotFoundComponent } from "./errors/not-found.component";
|
||||||
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
||||||
import { LoginComponent } from "./login/login.component";
|
import { LoginComponent } from "./login/login.component";
|
||||||
|
@ -43,6 +46,7 @@ const routes: Routes = [
|
||||||
{ path: "", redirectTo: "/search", pathMatch: "full" },
|
{ path: "", redirectTo: "/search", pathMatch: "full" },
|
||||||
{ path: "login", component: LoginComponent },
|
{ path: "login", component: LoginComponent },
|
||||||
{ path: "Login/OAuth/:pin", component: LoginOAuthComponent },
|
{ path: "Login/OAuth/:pin", component: LoginOAuthComponent },
|
||||||
|
{ path: "Custom", component: CustomPageComponent },
|
||||||
{ path: "login/:landing", component: LoginComponent },
|
{ path: "login/:landing", component: LoginComponent },
|
||||||
{ path: "reset", component: ResetPasswordComponent },
|
{ path: "reset", component: ResetPasswordComponent },
|
||||||
{ path: "token", component: TokenResetPasswordComponent },
|
{ path: "token", component: TokenResetPasswordComponent },
|
||||||
|
@ -88,6 +92,7 @@ export function JwtTokenGetter() {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
DataTableModule,
|
DataTableModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
NgxEditorModule,
|
||||||
DialogModule,
|
DialogModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
NgbModule.forRoot(),
|
NgbModule.forRoot(),
|
||||||
|
@ -121,6 +126,7 @@ export function JwtTokenGetter() {
|
||||||
LandingPageComponent,
|
LandingPageComponent,
|
||||||
ResetPasswordComponent,
|
ResetPasswordComponent,
|
||||||
TokenResetPasswordComponent,
|
TokenResetPasswordComponent,
|
||||||
|
CustomPageComponent,
|
||||||
CookieComponent,
|
CookieComponent,
|
||||||
LoginOAuthComponent,
|
LoginOAuthComponent,
|
||||||
],
|
],
|
||||||
|
|
31
src/Ombi/ClientApp/app/custompage/custompage.component.html
Normal file
31
src/Ombi/ClientApp/app/custompage/custompage.component.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<div *ngIf="form">
|
||||||
|
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
|
<button type="button" *ngIf="isAdmin" class="btn btn-info-outline" (click)="isEditing = !isEditing">Edit</button>
|
||||||
|
<div *ngIf="isEditing">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="Ip" class="control-label">Page Title
|
||||||
|
|
||||||
|
<i *ngIf="form.get('title').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Title is required"></i>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" formControlName="title" [ngClass]="{'form-error': form.get('title').hasError('required')}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="Ip" class="control-label">Font Awesome Icon
|
||||||
|
|
||||||
|
<i *ngIf="form.get('fontAwesomeIcon').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Font Awesome Icon is required"></i>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input type="text" class="form-control form-control-custom " id="fontAwesomeIcon" name="fontAwesomeIcon" formControlName="fontAwesomeIcon" [ngClass]="{'form-error': form.get('fontAwesomeIcon').hasError('required')}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<app-ngx-editor [placeholder]="'Enter text here...'" [minHeight]="100" formControlName="html"></app-ngx-editor>
|
||||||
|
<button type="submit" class="btn btn-primary-outline">Save</button>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<div [innerHTML]="form.controls['html'].value"></div>
|
||||||
|
</div>
|
44
src/Ombi/ClientApp/app/custompage/custompage.component.scss
Normal file
44
src/Ombi/ClientApp/app/custompage/custompage.component.scss
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
$primary-colour: #df691a;
|
||||||
|
$primary-colour-outline: #ff761b;
|
||||||
|
$bg-colour: #333333;
|
||||||
|
$bg-colour-disabled: #252424;
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-toolbar {
|
||||||
|
background-color: $bg-colour;
|
||||||
|
border: 1px solid $primary-colour-outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-toolbar-set {
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: 1px solid $primary-colour-outline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-editor-button {
|
||||||
|
-o-transition: all 0.218s;
|
||||||
|
-moz-transition: all 0.218s;
|
||||||
|
-webkit-transition: all 0.218s;
|
||||||
|
transition: all 0.218s;
|
||||||
|
color: $primary-colour-outline;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: $primary-colour-outline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-editor-button:hover {
|
||||||
|
color: black;
|
||||||
|
background-color: $primary-colour !important;
|
||||||
|
border-color: $primary-colour-outline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-editor-grippie {
|
||||||
|
background-color: $bg-colour;
|
||||||
|
border: 1px solid $primary-colour-outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-editor-textarea {
|
||||||
|
border: 1px solid $primary-colour-outline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host app-ngx-editor /deep/ .ngx-editor-message {
|
||||||
|
display:none !important;
|
||||||
|
}
|
48
src/Ombi/ClientApp/app/custompage/custompage.component.ts
Normal file
48
src/Ombi/ClientApp/app/custompage/custompage.component.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { Component, OnInit, SecurityContext } from "@angular/core";
|
||||||
|
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||||
|
import { DomSanitizer } from "@angular/platform-browser";
|
||||||
|
import { AuthService } from "../auth/auth.service";
|
||||||
|
import { NotificationService, SettingsService } from "../services";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./custompage.component.html",
|
||||||
|
styleUrls: ["./custompage.component.scss"],
|
||||||
|
})
|
||||||
|
export class CustomPageComponent implements OnInit {
|
||||||
|
|
||||||
|
public form: FormGroup;
|
||||||
|
public isEditing: boolean;
|
||||||
|
public isAdmin: boolean;
|
||||||
|
|
||||||
|
constructor(private auth: AuthService, private settings: SettingsService, private fb: FormBuilder,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private sanitizer: DomSanitizer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.settings.getCustomPage().subscribe(x => {
|
||||||
|
x.html = this.sanitizer.sanitize(SecurityContext.HTML, this.sanitizer.bypassSecurityTrustHtml(x.html));
|
||||||
|
this.form = this.fb.group({
|
||||||
|
enabled: [x.enabled],
|
||||||
|
title: [x.title, [Validators.required]],
|
||||||
|
html: [x.html, [Validators.required]],
|
||||||
|
fontAwesomeIcon: [x.fontAwesomeIcon, [Validators.required]],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSubmit() {
|
||||||
|
if (this.form.invalid) {
|
||||||
|
this.notificationService.error("Please check your entered values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.settings.saveCustomPage(this.form.value).subscribe(x => {
|
||||||
|
if (x) {
|
||||||
|
this.notificationService.success("Successfully saved Custom Page settings");
|
||||||
|
} else {
|
||||||
|
this.notificationService.success("There was an error when saving the Custom Page settings");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,10 +47,18 @@ export interface IMovieUpdateModel {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDenyMovieModel extends IMovieUpdateModel {
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IAlbumUpdateModel {
|
export interface IAlbumUpdateModel {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDenyAlbumModel extends IAlbumUpdateModel {
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFullBaseRequest extends IBaseRequest {
|
export interface IFullBaseRequest extends IBaseRequest {
|
||||||
imdbId: string;
|
imdbId: string;
|
||||||
overview: string;
|
overview: string;
|
||||||
|
@ -113,6 +121,10 @@ export interface ITvUpdateModel {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ITvDenyModel extends ITvUpdateModel {
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrderType {
|
export enum OrderType {
|
||||||
RequestedDateAsc = 1,
|
RequestedDateAsc = 1,
|
||||||
RequestedDateDesc = 2,
|
RequestedDateDesc = 2,
|
||||||
|
@ -142,6 +154,7 @@ export interface IEpisodesRequests {
|
||||||
|
|
||||||
export interface IMovieRequestModel {
|
export interface IMovieRequestModel {
|
||||||
theMovieDbId: number;
|
theMovieDbId: number;
|
||||||
|
languageCode: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFilter {
|
export interface IFilter {
|
||||||
|
|
|
@ -34,6 +34,12 @@
|
||||||
background: any;
|
background: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILanguageRefine {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
nativeName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ISearchMovieResultContainer {
|
export interface ISearchMovieResultContainer {
|
||||||
movies: ISearchMovieResult[];
|
movies: ISearchMovieResult[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export interface IOmbiSettings extends ISettings {
|
||||||
ignoreCertificateErrors: boolean;
|
ignoreCertificateErrors: boolean;
|
||||||
doNotSendNotificationsForAutoApprove: boolean;
|
doNotSendNotificationsForAutoApprove: boolean;
|
||||||
hideRequestsUsers: boolean;
|
hideRequestsUsers: boolean;
|
||||||
|
defaultLanguageCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUpdateSettings extends ISettings {
|
export interface IUpdateSettings extends ISettings {
|
||||||
|
@ -119,6 +120,7 @@ export interface ICustomizationSettings extends ISettings {
|
||||||
customDonationUrl: string;
|
customDonationUrl: string;
|
||||||
customDonationMessage: string;
|
customDonationMessage: string;
|
||||||
recentlyAddedPage: boolean;
|
recentlyAddedPage: boolean;
|
||||||
|
useCustomPage: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IJobSettings {
|
export interface IJobSettings {
|
||||||
|
@ -136,6 +138,7 @@ export interface IJobSettings {
|
||||||
lidarrArtistSync: string;
|
lidarrArtistSync: string;
|
||||||
issuesPurge: string;
|
issuesPurge: string;
|
||||||
retryRequests: string;
|
retryRequests: string;
|
||||||
|
mediaDatabaseRefresh: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IIssueSettings extends ISettings {
|
export interface IIssueSettings extends ISettings {
|
||||||
|
@ -157,6 +160,13 @@ export interface IAuthenticationSettings extends ISettings {
|
||||||
enableOAuth: boolean;
|
enableOAuth: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ICustomPage extends ISettings {
|
||||||
|
enabled: boolean;
|
||||||
|
fontAwesomeIcon: string;
|
||||||
|
title: string;
|
||||||
|
html: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IUserManagementSettings extends ISettings {
|
export interface IUserManagementSettings extends ISettings {
|
||||||
importPlexUsers: boolean;
|
importPlexUsers: boolean;
|
||||||
importPlexAdmin: boolean;
|
importPlexAdmin: boolean;
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class LandingPageComponent implements OnDestroy, OnInit {
|
||||||
});
|
});
|
||||||
this.timer = setInterval(() => {
|
this.timer = setInterval(() => {
|
||||||
this.cycleBackground();
|
this.cycleBackground();
|
||||||
}, 10000);
|
}, 15000);
|
||||||
|
|
||||||
const base = this.location.getBaseHrefFromDOM();
|
const base = this.location.getBaseHrefFromDOM();
|
||||||
if (base.length > 1) {
|
if (base.length > 1) {
|
||||||
|
|
|
@ -87,7 +87,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
||||||
});
|
});
|
||||||
this.timer = setInterval(() => {
|
this.timer = setInterval(() => {
|
||||||
this.cycleBackground();
|
this.cycleBackground();
|
||||||
}, 7000);
|
}, 15000);
|
||||||
|
|
||||||
const base = this.location.getBaseHrefFromDOM();
|
const base = this.location.getBaseHrefFromDOM();
|
||||||
if (base.length > 1) {
|
if (base.length > 1) {
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="request.denied" id="requestDenied">
|
<div *ngIf="request.denied" id="requestDenied">
|
||||||
{{ 'Requests.Denied' | translate }}
|
{{ 'Requests.Denied' | translate }}
|
||||||
<i style="color:red;" class="fa fa-check"></i>
|
<i style="color:red;" class="fa fa-check" pTooltip="{{request.deniedReason}}"></i>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -215,6 +215,14 @@
|
||||||
<p-paginator [rows]="10" [totalRecords]="totalMovies" (onPageChange)="paginate($event)"></p-paginator>
|
<p-paginator [rows]="10" [totalRecords]="totalMovies" (onPageChange)="paginate($event)"></p-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||||
|
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||||
|
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||||
|
<p-footer>
|
||||||
|
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||||
|
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||||
|
</p-footer>
|
||||||
|
</p-dialog>
|
||||||
|
|
||||||
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequest?.title"
|
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequest?.title"
|
||||||
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId"></issue-report>
|
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId"></issue-report>
|
||||||
|
|
|
@ -37,9 +37,12 @@ export class MovieRequestsComponent implements OnInit {
|
||||||
|
|
||||||
public orderType: OrderType = OrderType.RequestedDateDesc;
|
public orderType: OrderType = OrderType.RequestedDateDesc;
|
||||||
public OrderType = OrderType;
|
public OrderType = OrderType;
|
||||||
|
public denyDisplay: boolean;
|
||||||
|
public requestToDeny: IMovieRequests;
|
||||||
|
public rejectionReason: string;
|
||||||
|
|
||||||
public totalMovies: number = 100;
|
public totalMovies: number = 100;
|
||||||
private currentlyLoaded: number;
|
public currentlyLoaded: number;
|
||||||
private amountToLoad: number;
|
private amountToLoad: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -130,8 +133,22 @@ export class MovieRequestsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public deny(request: IMovieRequests) {
|
public deny(request: IMovieRequests) {
|
||||||
request.denied = true;
|
this.requestToDeny = request;
|
||||||
this.denyRequest(request);
|
this.denyDisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public denyRequest() {
|
||||||
|
this.requestService.denyMovie({ id: this.requestToDeny.id, reason: this.rejectionReason })
|
||||||
|
.subscribe(x => {
|
||||||
|
this.denyDisplay = false;
|
||||||
|
if (x.result) {
|
||||||
|
this.notificationService.success(
|
||||||
|
`Request for ${this.requestToDeny.title} has been denied successfully`);
|
||||||
|
} else {
|
||||||
|
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
||||||
|
this.requestToDeny.denied = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectRootFolder(searchResult: IMovieRequests, rootFolderSelected: IRadarrRootFolder, event: any) {
|
public selectRootFolder(searchResult: IMovieRequests, rootFolderSelected: IRadarrRootFolder, event: any) {
|
||||||
|
@ -278,19 +295,6 @@ export class MovieRequestsComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private denyRequest(request: IMovieRequests) {
|
|
||||||
this.requestService.denyMovie({ id: request.id })
|
|
||||||
.subscribe(x => {
|
|
||||||
if (x.result) {
|
|
||||||
this.notificationService.success(
|
|
||||||
`Request for ${request.title} has been denied successfully`);
|
|
||||||
} else {
|
|
||||||
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
|
||||||
request.denied = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadInit() {
|
private loadInit() {
|
||||||
this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter)
|
this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="request.denied" id="requestDenied">
|
<div *ngIf="request.denied" id="requestDenied">
|
||||||
{{ 'Requests.Denied' | translate }}
|
{{ 'Requests.Denied' | translate }}
|
||||||
<i style="color:red;" class="fa fa-check"></i>
|
<i style="color:red;" class="fa fa-check" pTooltip="{{request.deniedReason}}"></i>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -266,3 +266,14 @@
|
||||||
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
|
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
|
||||||
<i class="fa fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}</button>
|
<i class="fa fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}</button>
|
||||||
</p-sidebar>
|
</p-sidebar>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||||
|
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||||
|
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||||
|
<p-footer>
|
||||||
|
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||||
|
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||||
|
</p-footer>
|
||||||
|
</p-dialog>
|
|
@ -34,9 +34,12 @@ export class MusicRequestsComponent implements OnInit {
|
||||||
|
|
||||||
public orderType: OrderType = OrderType.RequestedDateDesc;
|
public orderType: OrderType = OrderType.RequestedDateDesc;
|
||||||
public OrderType = OrderType;
|
public OrderType = OrderType;
|
||||||
|
public denyDisplay: boolean;
|
||||||
|
public requestToDeny: IAlbumRequest;
|
||||||
|
public rejectionReason: string;
|
||||||
|
|
||||||
public totalAlbums: number = 100;
|
public totalAlbums: number = 100;
|
||||||
private currentlyLoaded: number;
|
public currentlyLoaded: number;
|
||||||
private amountToLoad: number;
|
private amountToLoad: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -126,23 +129,22 @@ export class MusicRequestsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public deny(request: IAlbumRequest) {
|
public deny(request: IAlbumRequest) {
|
||||||
request.denied = true;
|
this.requestToDeny = request;
|
||||||
this.denyRequest(request);
|
this.denyDisplay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public selectRootFolder(searchResult: IAlbumRequest, rootFolderSelected: IRadarrRootFolder, event: any) {
|
public denyRequest() {
|
||||||
// event.preventDefault();
|
this.requestService.denyAlbum({ id: this.requestToDeny.id, reason: this.rejectionReason })
|
||||||
// // searchResult.rootPathOverride = rootFolderSelected.id;
|
.subscribe(x => {
|
||||||
// this.setOverride(searchResult);
|
if (x.result) {
|
||||||
// this.updateRequest(searchResult);
|
this.notificationService.success(
|
||||||
// }
|
`Request for ${this.requestToDeny.title} has been denied successfully`);
|
||||||
|
} else {
|
||||||
// public selectQualityProfile(searchResult: IMovieRequests, profileSelected: IRadarrProfile, event: any) {
|
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
||||||
// event.preventDefault();
|
this.requestToDeny.denied = false;
|
||||||
// searchResult.qualityOverride = profileSelected.id;
|
}
|
||||||
// this.setOverride(searchResult);
|
});
|
||||||
// this.updateRequest(searchResult);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
public reportIssue(catId: IIssueCategory, req: IAlbumRequest) {
|
public reportIssue(catId: IIssueCategory, req: IAlbumRequest) {
|
||||||
this.issueRequest = req;
|
this.issueRequest = req;
|
||||||
|
@ -266,19 +268,6 @@ export class MusicRequestsComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private denyRequest(request: IAlbumRequest) {
|
|
||||||
this.requestService.denyAlbum({ id: request.id })
|
|
||||||
.subscribe(x => {
|
|
||||||
if (x.result) {
|
|
||||||
this.notificationService.success(
|
|
||||||
`Request for ${request.title} has been denied successfully`);
|
|
||||||
} else {
|
|
||||||
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
|
||||||
request.denied = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadInit() {
|
private loadInit() {
|
||||||
this.requestService.getAlbumRequests(this.amountToLoad, 0, this.orderType, this.filter)
|
this.requestService.getAlbumRequests(this.amountToLoad, 0, this.orderType, this.filter)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}</button>
|
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}</button>
|
||||||
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}</button>
|
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}</button>
|
||||||
|
|
||||||
<button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> {{ 'Requests.Deny' | translate }}</button>
|
<button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny">
|
||||||
|
<i class="fa fa-times"></i> {{ 'Requests.Deny' | translate }}</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isAdmin || isRequestUser(child)">
|
<div *ngIf="isAdmin || isRequestUser(child)">
|
||||||
|
@ -76,7 +77,9 @@
|
||||||
{{ep.airDate | amLocal | amDateFormat: 'L' }}
|
{{ep.airDate | amLocal | amDateFormat: 'L' }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel" [translate]="'Common.Denied'"></span>
|
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel" [translate]="'Common.Denied'">
|
||||||
|
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i>
|
||||||
|
</span>
|
||||||
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||||
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info" id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
|
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info" id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
|
||||||
<div *ngIf="!child.denied && !ep.approved">
|
<div *ngIf="!child.denied && !ep.approved">
|
||||||
|
@ -98,3 +101,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||||
|
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||||
|
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||||
|
<p-footer>
|
||||||
|
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
||||||
|
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
||||||
|
</p-footer>
|
||||||
|
</p-dialog>
|
||||||
|
|
|
@ -12,6 +12,10 @@ export class TvRequestChildrenComponent {
|
||||||
@Input() public isAdmin: boolean;
|
@Input() public isAdmin: boolean;
|
||||||
@Input() public currentUser: string;
|
@Input() public currentUser: string;
|
||||||
|
|
||||||
|
public denyDisplay: boolean;
|
||||||
|
public requestToDeny: IChildRequests;
|
||||||
|
public rejectionReason: string;
|
||||||
|
|
||||||
@Output() public requestDeleted = new EventEmitter<number>();
|
@Output() public requestDeleted = new EventEmitter<number>();
|
||||||
|
|
||||||
constructor(private requestService: RequestService,
|
constructor(private requestService: RequestService,
|
||||||
|
@ -57,20 +61,26 @@ export class TvRequestChildrenComponent {
|
||||||
|
|
||||||
public deny(request: IChildRequests) {
|
public deny(request: IChildRequests) {
|
||||||
request.denied = true;
|
request.denied = true;
|
||||||
|
this.requestToDeny = request;
|
||||||
|
this.denyDisplay = true;
|
||||||
|
|
||||||
request.seasonRequests.forEach((season) => {
|
request.seasonRequests.forEach((season) => {
|
||||||
season.episodes.forEach((ep) => {
|
season.episodes.forEach((ep) => {
|
||||||
ep.approved = false;
|
ep.approved = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.requestService.denyChild({ id: request.id })
|
}
|
||||||
|
|
||||||
|
public denyRequest() {
|
||||||
|
this.requestService.denyChild({ id: this.requestToDeny.id, reason: this.rejectionReason })
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
|
this.denyDisplay = false;
|
||||||
if (x.result) {
|
if (x.result) {
|
||||||
this.notificationService.success(
|
this.notificationService.success(
|
||||||
`Request has been denied successfully`);
|
`Request has been denied successfully`);
|
||||||
} else {
|
} else {
|
||||||
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
|
||||||
request.approved = false;
|
this.requestToDeny.approved = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<!-- Movie tab -->
|
<!-- Movie tab -->
|
||||||
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group search-bar-background">
|
||||||
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons"
|
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons"
|
||||||
(keyup)="search($event)">
|
(keyup)="search($event)">
|
||||||
<div class="input-group-addon right-radius">
|
<div class="input-group-addon right-radius">
|
||||||
<div class="btn-group">
|
<div class="btn-group" role="group">
|
||||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||||
{{ 'Search.Suggestions' | translate }}
|
{{ 'Search.Suggestions' | translate }}
|
||||||
<i class="fa fa-chevron-down"></i>
|
<i class="fa fa-chevron-down"></i>
|
||||||
|
@ -16,10 +16,43 @@
|
||||||
<li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li>
|
<li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li>
|
||||||
<li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li>
|
<li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<button class="btn btn-sm btn-primary-outline" (click)="refineOpen()">
|
||||||
|
{{ 'Search.Refine' | translate }}
|
||||||
|
<i class="fa" [ngClass]="{'fa-chevron-down': !refineSearchEnabled, 'fa-chevron-up': refineSearchEnabled}"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<i class="fa fa-search"></i>
|
<i class="fa fa-search"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Refine search options -->
|
||||||
|
<div class="row top-spacing form-group vcenter" *ngIf="refineSearchEnabled">
|
||||||
|
<div class="col-md-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">Year</label>
|
||||||
|
|
||||||
|
<input [(ngModel)]="searchYear" class="form-control form-control-custom refine-option">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <label for="name" class="col-xs-2 col-md-1">Language:</label> -->
|
||||||
|
<div class="col-md-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="select" class="control-label">Language</label>
|
||||||
|
<div id="profiles">
|
||||||
|
<select [(ngModel)]="selectedLanguage" class="form-control form-control-custom refine-option"
|
||||||
|
id="select">
|
||||||
|
<option *ngFor="let lang of langauges" value="{{lang.code}}">{{lang.nativeName}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<button class="btn pull-right btn-success-outline" (click)="applyRefinedSearch()">Apply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<remaining-requests [movie]="true" [quotaRefreshEvents]="movieRequested.asObservable()" #remainingFilms></remaining-requests>
|
<remaining-requests [movie]="true" [quotaRefreshEvents]="movieRequested.asObservable()" #remainingFilms></remaining-requests>
|
||||||
|
|
||||||
|
@ -111,11 +144,15 @@
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<div *ngIf="result.available">
|
<div *ngIf="result.available">
|
||||||
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a>
|
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}"
|
||||||
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' | translate}}</a>
|
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a>
|
||||||
|
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline"
|
||||||
|
href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
|
||||||
|
translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled">
|
<div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled">
|
||||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||||
|
aria-haspopup="true" aria-expanded="true">
|
||||||
<i class="fa fa-plus"></i> {{'Requests.ReportIssue' | translate}}
|
<i class="fa fa-plus"></i> {{'Requests.ReportIssue' | translate}}
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -6,12 +6,15 @@ import { Subject } from "rxjs";
|
||||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||||
|
|
||||||
import { AuthService } from "../auth/auth.service";
|
import { AuthService } from "../auth/auth.service";
|
||||||
import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../interfaces";
|
import { IIssueCategory, ILanguageRefine, IRequestEngineResult, ISearchMovieResult } from "../interfaces";
|
||||||
import { NotificationService, RequestService, SearchService } from "../services";
|
import { NotificationService, RequestService, SearchService, SettingsService } from "../services";
|
||||||
|
|
||||||
|
import * as languageData from "../../other/iso-lang.json";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "movie-search",
|
selector: "movie-search",
|
||||||
templateUrl: "./moviesearch.component.html",
|
templateUrl: "./moviesearch.component.html",
|
||||||
|
styleUrls: ["./search.component.scss"],
|
||||||
})
|
})
|
||||||
export class MovieSearchComponent implements OnInit {
|
export class MovieSearchComponent implements OnInit {
|
||||||
|
|
||||||
|
@ -22,6 +25,10 @@ export class MovieSearchComponent implements OnInit {
|
||||||
public result: IRequestEngineResult;
|
public result: IRequestEngineResult;
|
||||||
|
|
||||||
public searchApplied = false;
|
public searchApplied = false;
|
||||||
|
public refineSearchEnabled = false;
|
||||||
|
public searchYear?: number;
|
||||||
|
public selectedLanguage: string;
|
||||||
|
public langauges: ILanguageRefine[];
|
||||||
|
|
||||||
@Input() public issueCategories: IIssueCategory[];
|
@Input() public issueCategories: IIssueCategory[];
|
||||||
@Input() public issuesEnabled: boolean;
|
@Input() public issuesEnabled: boolean;
|
||||||
|
@ -36,24 +43,14 @@ export class MovieSearchComponent implements OnInit {
|
||||||
private searchService: SearchService, private requestService: RequestService,
|
private searchService: SearchService, private requestService: RequestService,
|
||||||
private notificationService: NotificationService, private authService: AuthService,
|
private notificationService: NotificationService, private authService: AuthService,
|
||||||
private readonly translate: TranslateService, private sanitizer: DomSanitizer,
|
private readonly translate: TranslateService, private sanitizer: DomSanitizer,
|
||||||
private readonly platformLocation: PlatformLocation) {
|
private readonly platformLocation: PlatformLocation, private settingsService: SettingsService) {
|
||||||
|
this.langauges = <ILanguageRefine[]><any>languageData;
|
||||||
this.searchChanged.pipe(
|
this.searchChanged.pipe(
|
||||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||||
distinctUntilChanged(), // only emit if value is different from previous value
|
distinctUntilChanged(), // only emit if value is different from previous value
|
||||||
).subscribe(x => {
|
).subscribe(x => {
|
||||||
this.searchText = x as string;
|
this.searchText = x as string;
|
||||||
if (this.searchText === "") {
|
this.runSearch();
|
||||||
this.clearResults();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.searchService.searchMovie(this.searchText)
|
|
||||||
.subscribe(x => {
|
|
||||||
this.movieResults = x;
|
|
||||||
this.searchApplied = true;
|
|
||||||
// Now let's load some extra info including IMDB Id
|
|
||||||
// This way the search is fast at displaying results.
|
|
||||||
this.getExtraInfo();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
this.defaultPoster = "../../../images/default_movie_poster.png";
|
this.defaultPoster = "../../../images/default_movie_poster.png";
|
||||||
const base = this.platformLocation.getBaseHrefFromDOM();
|
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||||
|
@ -70,7 +67,7 @@ export class MovieSearchComponent implements OnInit {
|
||||||
result: false,
|
result: false,
|
||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
};
|
};
|
||||||
|
this.settingsService.getDefaultLanguage().subscribe(x => this.selectedLanguage = x);
|
||||||
this.popularMovies();
|
this.popularMovies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +84,8 @@ export class MovieSearchComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.requestService.requestMovie({ theMovieDbId: searchResult.id })
|
const language = this.selectedLanguage && this.selectedLanguage.length > 0 ? this.selectedLanguage : "en";
|
||||||
|
this.requestService.requestMovie({ theMovieDbId: searchResult.id, languageCode: language })
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.result = x;
|
this.result = x;
|
||||||
if (this.result.result) {
|
if (this.result.result) {
|
||||||
|
@ -161,7 +159,8 @@ export class MovieSearchComponent implements OnInit {
|
||||||
|
|
||||||
public similarMovies(theMovieDbId: number) {
|
public similarMovies(theMovieDbId: number) {
|
||||||
this.clearResults();
|
this.clearResults();
|
||||||
this.searchService.similarMovies(theMovieDbId)
|
const lang = this.selectedLanguage && this.selectedLanguage.length > 0 ? this.selectedLanguage : "";
|
||||||
|
this.searchService.similarMovies(theMovieDbId, lang)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.movieResults = x;
|
this.movieResults = x;
|
||||||
this.getExtraInfo();
|
this.getExtraInfo();
|
||||||
|
@ -184,6 +183,17 @@ export class MovieSearchComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public refineOpen() {
|
||||||
|
this.refineSearchEnabled = !this.refineSearchEnabled;
|
||||||
|
if (!this.refineSearchEnabled) {
|
||||||
|
this.searchYear = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyRefinedSearch() {
|
||||||
|
this.runSearch();
|
||||||
|
}
|
||||||
|
|
||||||
private getExtraInfo() {
|
private getExtraInfo() {
|
||||||
|
|
||||||
this.movieResults.forEach((val, index) => {
|
this.movieResults.forEach((val, index) => {
|
||||||
|
@ -194,10 +204,18 @@ export class MovieSearchComponent implements OnInit {
|
||||||
}
|
}
|
||||||
val.background = this.sanitizer.bypassSecurityTrustStyle
|
val.background = this.sanitizer.bypassSecurityTrustStyle
|
||||||
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
|
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
|
||||||
this.searchService.getMovieInformation(val.id)
|
|
||||||
|
if (this.applyRefinedSearch) {
|
||||||
|
this.searchService.getMovieInformationWithRefined(val.id, this.selectedLanguage)
|
||||||
|
.subscribe(m => {
|
||||||
|
this.updateItem(val, m);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.searchService.getMovieInformation(val.id)
|
||||||
.subscribe(m => {
|
.subscribe(m => {
|
||||||
this.updateItem(val, m);
|
this.updateItem(val, m);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,4 +232,30 @@ export class MovieSearchComponent implements OnInit {
|
||||||
this.movieResults = [];
|
this.movieResults = [];
|
||||||
this.searchApplied = false;
|
this.searchApplied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private runSearch() {
|
||||||
|
if (this.searchText === "") {
|
||||||
|
this.clearResults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.refineOpen) {
|
||||||
|
this.searchService.searchMovieWithRefined(this.searchText, this.searchYear, this.selectedLanguage)
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.searchApplied = true;
|
||||||
|
// Now let's load some extra info including IMDB Id
|
||||||
|
// This way the search is fast at displaying results.
|
||||||
|
this.getExtraInfo();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.searchService.searchMovie(this.searchText)
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.searchApplied = true;
|
||||||
|
// Now let's load some extra info including IMDB Id
|
||||||
|
// This way the search is fast at displaying results.
|
||||||
|
this.getExtraInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ export class MovieSearchGridComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.requestService.requestMovie({ theMovieDbId: searchResult.id })
|
this.requestService.requestMovie({ theMovieDbId: searchResult.id, languageCode: "en" })
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.result = x;
|
this.result = x;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import { PlatformLocation } from "@angular/common";
|
import { PlatformLocation } from "@angular/common";
|
||||||
import { Component, Input, OnInit } from "@angular/core";
|
import { Component, Input, OnInit } from "@angular/core";
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
import { DomSanitizer } from "@angular/platform-browser";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
|
||||||
|
import { IIssueCategory, IRequestEngineResult } from "../../interfaces";
|
||||||
import { AuthService } from "../../auth/auth.service";
|
|
||||||
import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../../interfaces";
|
|
||||||
import { ISearchAlbumResult, ISearchArtistResult } from "../../interfaces/ISearchMusicResult";
|
import { ISearchAlbumResult, ISearchArtistResult } from "../../interfaces/ISearchMusicResult";
|
||||||
import { NotificationService, RequestService, SearchService } from "../../services";
|
import { SearchService } from "../../services";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "music-search",
|
selector: "music-search",
|
||||||
|
@ -35,10 +32,8 @@ export class MusicSearchComponent implements OnInit {
|
||||||
public defaultPoster: string;
|
public defaultPoster: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private searchService: SearchService, private requestService: RequestService,
|
private searchService: SearchService, private sanitizer: DomSanitizer,
|
||||||
private notificationService: NotificationService, private authService: AuthService,
|
private platformLocation: PlatformLocation) {
|
||||||
private readonly translate: TranslateService, private sanitizer: DomSanitizer,
|
|
||||||
private readonly platformLocation: PlatformLocation) {
|
|
||||||
|
|
||||||
this.searchChanged.pipe(
|
this.searchChanged.pipe(
|
||||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||||
|
@ -110,45 +105,6 @@ export class MusicSearchComponent implements OnInit {
|
||||||
this.searchChanged.next(`lidarr:${artistId}`);
|
this.searchChanged.next(`lidarr:${artistId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public request(searchResult: ISearchMovieResult) {
|
|
||||||
searchResult.requested = true;
|
|
||||||
searchResult.requestProcessing = true;
|
|
||||||
searchResult.showSubscribe = false;
|
|
||||||
if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) {
|
|
||||||
searchResult.approved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.requestService.requestMovie({ theMovieDbId: searchResult.id })
|
|
||||||
.subscribe(x => {
|
|
||||||
this.result = x;
|
|
||||||
|
|
||||||
if (this.result.result) {
|
|
||||||
this.translate.get("Search.RequestAdded", { title: searchResult.title }).subscribe(x => {
|
|
||||||
this.notificationService.success(x);
|
|
||||||
searchResult.processed = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (this.result.errorMessage && this.result.message) {
|
|
||||||
this.notificationService.warning("Request Added", `${this.result.message} - ${this.result.errorMessage}`);
|
|
||||||
} else {
|
|
||||||
this.notificationService.warning("Request Added", this.result.message ? this.result.message : this.result.errorMessage);
|
|
||||||
}
|
|
||||||
searchResult.requested = false;
|
|
||||||
searchResult.approved = false;
|
|
||||||
searchResult.processed = false;
|
|
||||||
searchResult.requestProcessing = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
searchResult.processed = false;
|
|
||||||
searchResult.requestProcessing = false;
|
|
||||||
this.notificationService.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public viewAlbumsForArtist(albums: ISearchAlbumResult[]) {
|
public viewAlbumsForArtist(albums: ISearchAlbumResult[]) {
|
||||||
this.clearArtistResults();
|
this.clearArtistResults();
|
||||||
this.searchAlbum = true;
|
this.searchAlbum = true;
|
||||||
|
|
30
src/Ombi/ClientApp/app/search/search.component.scss
Normal file
30
src/Ombi/ClientApp/app/search/search.component.scss
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
@media (max-width: 978px) {
|
||||||
|
.top-spacing {
|
||||||
|
padding-top: 5%
|
||||||
|
}
|
||||||
|
.form-control-search {
|
||||||
|
width: 77%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@media (min-width: 979px) {
|
||||||
|
.top-spacing {
|
||||||
|
padding-top: 2%
|
||||||
|
}
|
||||||
|
.form-control-search {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-background {
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vcenter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refine-option {
|
||||||
|
box-shadow: inset 0 1px 5px rgba(0,0,0,1.0);
|
||||||
|
}
|
|
@ -5,8 +5,8 @@ import { HttpClient } from "@angular/common/http";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { TreeNode } from "primeng/primeng";
|
import { TreeNode } from "primeng/primeng";
|
||||||
import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IFilter, IMovieRequestModel, IMovieRequests,
|
import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IDenyAlbumModel, IDenyMovieModel, IFilter,
|
||||||
IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
|
IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvDenyModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
|
||||||
import { ITvRequestViewModel } from "../interfaces";
|
import { ITvRequestViewModel } from "../interfaces";
|
||||||
import { ServiceHelpers } from "./service.helpers";
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export class RequestService extends ServiceHelpers {
|
||||||
return this.http.post<IRequestEngineResult>(`${this.url}Movie/Approve`, JSON.stringify(movie), {headers: this.headers});
|
return this.http.post<IRequestEngineResult>(`${this.url}Movie/Approve`, JSON.stringify(movie), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public denyMovie(movie: IMovieUpdateModel): Observable<IRequestEngineResult> {
|
public denyMovie(movie: IDenyMovieModel): Observable<IRequestEngineResult> {
|
||||||
return this.http.put<IRequestEngineResult>(`${this.url}Movie/Deny`, JSON.stringify(movie), {headers: this.headers});
|
return this.http.put<IRequestEngineResult>(`${this.url}Movie/Deny`, JSON.stringify(movie), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ export class RequestService extends ServiceHelpers {
|
||||||
return this.http.put<IChildRequests>(`${this.url}tv/child`, JSON.stringify(child), {headers: this.headers});
|
return this.http.put<IChildRequests>(`${this.url}tv/child`, JSON.stringify(child), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public denyChild(child: ITvUpdateModel): Observable<IRequestEngineResult> {
|
public denyChild(child: ITvDenyModel): Observable<IRequestEngineResult> {
|
||||||
return this.http.put<IRequestEngineResult>(`${this.url}tv/deny`, JSON.stringify(child), {headers: this.headers});
|
return this.http.put<IRequestEngineResult>(`${this.url}tv/deny`, JSON.stringify(child), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ export class RequestService extends ServiceHelpers {
|
||||||
return this.http.post<IRequestEngineResult>(`${this.url}music/Approve`, JSON.stringify(Album), {headers: this.headers});
|
return this.http.post<IRequestEngineResult>(`${this.url}music/Approve`, JSON.stringify(Album), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public denyAlbum(Album: IAlbumUpdateModel): Observable<IRequestEngineResult> {
|
public denyAlbum(Album: IDenyAlbumModel): Observable<IRequestEngineResult> {
|
||||||
return this.http.put<IRequestEngineResult>(`${this.url}music/Deny`, JSON.stringify(Album), {headers: this.headers});
|
return this.http.put<IRequestEngineResult>(`${this.url}music/Deny`, JSON.stringify(Album), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,15 @@ export class SearchService extends ServiceHelpers {
|
||||||
|
|
||||||
// Movies
|
// Movies
|
||||||
public searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> {
|
public searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/` + searchTerm);
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${searchTerm}`);
|
||||||
}
|
}
|
||||||
public similarMovies(theMovieDbId: number): Observable<ISearchMovieResult[]> {
|
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${theMovieDbId}/similar`);
|
public searchMovieWithRefined(searchTerm: string, year: number | undefined, langCode: string): Observable<ISearchMovieResult[]> {
|
||||||
|
return this.http.post<ISearchMovieResult[]>(`${this.url}/Movie/`, { searchTerm, year, languageCode: langCode });
|
||||||
|
}
|
||||||
|
|
||||||
|
public similarMovies(theMovieDbId: number, langCode: string): Observable<ISearchMovieResult[]> {
|
||||||
|
return this.http.post<ISearchMovieResult[]>(`${this.url}/Movie/similar`, {theMovieDbId, languageCode: langCode});
|
||||||
}
|
}
|
||||||
|
|
||||||
public popularMovies(): Observable<ISearchMovieResult[]> {
|
public popularMovies(): Observable<ISearchMovieResult[]> {
|
||||||
|
@ -40,34 +45,38 @@ export class SearchService extends ServiceHelpers {
|
||||||
return this.http.get<ISearchMovieResult>(`${this.url}/Movie/info/${theMovieDbId}`);
|
return this.http.get<ISearchMovieResult>(`${this.url}/Movie/info/${theMovieDbId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getMovieInformationWithRefined(theMovieDbId: number, langCode: string): Observable<ISearchMovieResult> {
|
||||||
|
return this.http.post<ISearchMovieResult>(`${this.url}/Movie/info`, { theMovieDbId, languageCode: langCode });
|
||||||
|
}
|
||||||
|
|
||||||
// TV
|
// TV
|
||||||
public searchTv(searchTerm: string): Observable<ISearchTvResult[]> {
|
public searchTv(searchTerm: string): Observable<ISearchTvResult[]> {
|
||||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, {headers: this.headers});
|
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
public searchTvTreeNode(searchTerm: string): Observable<TreeNode[]> {
|
public searchTvTreeNode(searchTerm: string): Observable<TreeNode[]> {
|
||||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/${searchTerm}/tree`, {headers: this.headers});
|
return this.http.get<TreeNode[]>(`${this.url}/Tv/${searchTerm}/tree`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
public getShowInformationTreeNode(theTvDbId: number): Observable<TreeNode> {
|
public getShowInformationTreeNode(theTvDbId: number): Observable<TreeNode> {
|
||||||
return this.http.get<TreeNode>(`${this.url}/Tv/info/${theTvDbId}/Tree`, {headers: this.headers});
|
return this.http.get<TreeNode>(`${this.url}/Tv/info/${theTvDbId}/Tree`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
public getShowInformation(theTvDbId: number): Observable<ISearchTvResult> {
|
public getShowInformation(theTvDbId: number): Observable<ISearchTvResult> {
|
||||||
return this.http.get<ISearchTvResult>(`${this.url}/Tv/info/${theTvDbId}`, {headers: this.headers});
|
return this.http.get<ISearchTvResult>(`${this.url}/Tv/info/${theTvDbId}`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
public popularTv(): Observable<ISearchTvResult[]> {
|
public popularTv(): Observable<ISearchTvResult[]> {
|
||||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, {headers: this.headers});
|
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
public mostWatchedTv(): Observable<ISearchTvResult[]> {
|
public mostWatchedTv(): Observable<ISearchTvResult[]> {
|
||||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/mostwatched`, {headers: this.headers});
|
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/mostwatched`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
public anticipatedTv(): Observable<ISearchTvResult[]> {
|
public anticipatedTv(): Observable<ISearchTvResult[]> {
|
||||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated`, {headers: this.headers});
|
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
public trendingTv(): Observable<ISearchTvResult[]> {
|
public trendingTv(): Observable<ISearchTvResult[]> {
|
||||||
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, {headers: this.headers});
|
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, { headers: this.headers });
|
||||||
}
|
}
|
||||||
// Music
|
// Music
|
||||||
public searchArtist(searchTerm: string): Observable<ISearchArtistResult[]> {
|
public searchArtist(searchTerm: string): Observable<ISearchArtistResult[]> {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
ICronTestModel,
|
ICronTestModel,
|
||||||
ICronViewModelBody,
|
ICronViewModelBody,
|
||||||
ICustomizationSettings,
|
ICustomizationSettings,
|
||||||
|
ICustomPage,
|
||||||
IDiscordNotifcationSettings,
|
IDiscordNotifcationSettings,
|
||||||
IDogNzbSettings,
|
IDogNzbSettings,
|
||||||
IEmailNotificationSettings,
|
IEmailNotificationSettings,
|
||||||
|
@ -52,6 +53,10 @@ export class SettingsService extends ServiceHelpers {
|
||||||
return this.http.get<IOmbiSettings>(`${this.url}/Ombi/`, {headers: this.headers});
|
return this.http.get<IOmbiSettings>(`${this.url}/Ombi/`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDefaultLanguage(): Observable<string> {
|
||||||
|
return this.http.get<string>(`${this.url}/defaultlanguage/`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public saveOmbi(settings: IOmbiSettings): Observable<boolean> {
|
public saveOmbi(settings: IOmbiSettings): Observable<boolean> {
|
||||||
return this.http.post<boolean>(`${this.url}/Ombi/`, JSON.stringify(settings), {headers: this.headers});
|
return this.http.post<boolean>(`${this.url}/Ombi/`, JSON.stringify(settings), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
@ -108,6 +113,14 @@ export class SettingsService extends ServiceHelpers {
|
||||||
return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers});
|
return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCustomPage(): Observable<ICustomPage> {
|
||||||
|
return this.http.get<ICustomPage>(`${this.url}/CustomPage`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveCustomPage(model: ICustomPage): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}/CustomPage`, model, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public getClientId(): Observable<string> {
|
public getClientId(): Observable<string> {
|
||||||
return this.http.get<string>(`${this.url}/clientid`, {headers: this.headers});
|
return this.http.get<string>(`${this.url}/clientid`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="applicationurl" class="control-label">Application URL</label>
|
<label for="applicationurl" class="control-label">Application URL</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" [(ngModel)]="settings.applicationUrl" class="form-control form-control-custom " id="applicationurl" name="applicationurl"
|
<input type="text" [(ngModel)]="settings.applicationUrl" class="form-control form-control-custom " id="applicationurl"
|
||||||
placeholder="http://ombi.io/" value="{{settings.applicationUrl}}">
|
name="applicationurl" placeholder="http://ombi.io/" value="{{settings.applicationUrl}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="logo" class="control-label">Custom Logo</label>
|
<label for="logo" class="control-label">Custom Logo</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" [(ngModel)]="settings.logo" class="form-control form-control-custom " id="logo" name="logo" value="{{settings.logo}}"
|
<input type="text" [(ngModel)]="settings.logo" class="form-control form-control-custom " id="logo" name="logo"
|
||||||
tooltipPosition="top" pTooltip="Use a URL e.g. www.google.com/logo.png">
|
value="{{settings.logo}}" tooltipPosition="top" pTooltip="Use a URL e.g. www.google.com/logo.png">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="settings.logo" class="form-group">
|
<div *ngIf="settings.logo" class="form-group">
|
||||||
|
@ -50,14 +50,16 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="enableCustomDonations" name="enableCustomDonations" [(ngModel)]="settings.enableCustomDonations">
|
<input type="checkbox" id="enableCustomDonations" name="enableCustomDonations" [(ngModel)]="settings.enableCustomDonations">
|
||||||
<label for="enableCustomDonations" tooltipPosition="top" pTooltip="Enable to show a custom donation link in the navigation bar">Enable custom donation link</label>
|
<label for="enableCustomDonations" tooltipPosition="top" pTooltip="Enable to show a custom donation link in the navigation bar">Enable
|
||||||
|
custom donation link</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" *ngIf="settings.enableCustomDonations">
|
<div class="form-group" *ngIf="settings.enableCustomDonations">
|
||||||
<label for="customDonation" class="control-label">Custom Donation URL</label>
|
<label for="customDonation" class="control-label">Custom Donation URL</label>
|
||||||
<div>
|
<div>
|
||||||
<input [disabled]="!settings.enableCustomDonations" type="text" [(ngModel)]="settings.customDonationUrl" class="form-control form-control-custom " name="customDonation" value="{{settings.customDonationUrl}}"
|
<input [disabled]="!settings.enableCustomDonations" type="text" [(ngModel)]="settings.customDonationUrl"
|
||||||
|
class="form-control form-control-custom " name="customDonation" value="{{settings.customDonationUrl}}"
|
||||||
tooltipPosition="top" pTooltip="A link to a Paypal address, or your custom donation url.">
|
tooltipPosition="top" pTooltip="A link to a Paypal address, or your custom donation url.">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,10 +67,19 @@
|
||||||
<div class="form-group" *ngIf="settings.enableCustomDonations">
|
<div class="form-group" *ngIf="settings.enableCustomDonations">
|
||||||
<label for="customDonationMessage" class="control-label">Donation Button Message</label>
|
<label for="customDonationMessage" class="control-label">Donation Button Message</label>
|
||||||
<div>
|
<div>
|
||||||
<input [disabled]="!settings.enableCustomDonations" type="text" [(ngModel)]="settings.customDonationMessage" class="form-control form-control-custom " name="customDonationMessage" value="{{settings.customDonationMessage}}"
|
<input [disabled]="!settings.enableCustomDonations" type="text" [(ngModel)]="settings.customDonationMessage"
|
||||||
|
class="form-control form-control-custom " name="customDonationMessage" value="{{settings.customDonationMessage}}"
|
||||||
tooltipPosition="top" pTooltip="Set a custom message to be displayed in the navigation bar.">
|
tooltipPosition="top" pTooltip="Set a custom message to be displayed in the navigation bar.">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="useCustomPage" name="useCustomPage" [(ngModel)]="settings.useCustomPage">
|
||||||
|
<label for="useCustomPage" tooltipPosition="top" pTooltip="Enabled a custom page where you can fully edit">Use
|
||||||
|
Custom Page</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,9 +96,8 @@
|
||||||
<label for="customCss" class="control-label">Custom CSS</label>
|
<label for="customCss" class="control-label">Custom CSS</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group language-css" pCode>
|
<div class="form-group language-css" pCode>
|
||||||
<textarea rows="25" type="text"
|
<textarea rows="25" type="text" pTooltip="Enter your custom styles here" tooltipPosition="top" class="form-control-custom form-control "
|
||||||
pTooltip="Enter your custom styles here" tooltipPosition="top"
|
id="themeContent" name="themeContent" [(ngModel)]="settings.customCss"> {{settings.customCss}} </textarea>
|
||||||
class="form-control-custom form-control " id="themeContent" name="themeContent" [(ngModel)]="settings.customCss"> {{settings.customCss}} </textarea>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -105,6 +105,13 @@
|
||||||
<small *ngIf="form.get('issuesPurge').hasError('required')" class="error-text">The Issues Purge is required</small>
|
<small *ngIf="form.get('issuesPurge').hasError('required')" class="error-text">The Issues Purge is required</small>
|
||||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('issuesPurge')?.value)">Test</button>
|
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('issuesPurge')?.value)">Test</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="userImporter" class="control-label">Media Data Refresh</label>
|
||||||
|
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('mediaDatabaseRefresh').hasError('required')}" id="mediaDatabaseRefresh" name="mediaDatabaseRefresh" formControlName="mediaDatabaseRefresh">
|
||||||
|
<small *ngIf="form.get('mediaDatabaseRefresh').hasError('required')" class="error-text">The Media Database Refresh is required</small>
|
||||||
|
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('mediaDatabaseRefresh')?.value)">Test</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class JobsComponent implements OnInit {
|
||||||
lidarrArtistSync: [x.lidarrArtistSync, Validators.required],
|
lidarrArtistSync: [x.lidarrArtistSync, Validators.required],
|
||||||
issuesPurge: [x.issuesPurge, Validators.required],
|
issuesPurge: [x.issuesPurge, Validators.required],
|
||||||
retryRequests: [x.retryRequests, Validators.required],
|
retryRequests: [x.retryRequests, Validators.required],
|
||||||
|
mediaDatabaseRefresh: [x.mediaDatabaseRefresh, Validators.required],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,63 +23,79 @@
|
||||||
</div>-->
|
</div>-->
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="baseUrl" class="control-label">Base Url</label>
|
<label for="baseUrl" class="control-label">Base Url</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom" id="baseUrl" name="baseUrl" formControlName="baseUrl">
|
<input type="text" class="form-control form-control-custom" id="baseUrl" name="baseUrl"
|
||||||
</div>
|
formControlName="baseUrl">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="ApiKey" class="control-label">Api Key</label>
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="form-control form-control-custom" id="ApiKey" name="ApiKey" formControlName="apiKey" readonly="readonly" #apiKey>
|
|
||||||
|
|
||||||
<div class="input-group-addon">
|
|
||||||
<div (click)="refreshApiKey()" id="refreshKey" class="fa fa-refresh" title="Reset API Key" pTooltip="This will invalidate the old API key" ></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group-addon">
|
|
||||||
<div ngxClipboard [ngxClipboard]="apiKey" class="fa fa-clipboard" (cbOnSuccess)="successfullyCopied()"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<label for="ApiKey" class="control-label">Api Key</label>
|
||||||
<input type="checkbox" id="doNotSendNotificationsForAutoApprove" name="doNotSendNotificationsForAutoApprove" formControlName="doNotSendNotificationsForAutoApprove">
|
<div class="input-group">
|
||||||
<label for="doNotSendNotificationsForAutoApprove">Do not send Notifications if a User has the Auto Approve permission</label>
|
<input type="text" class="form-control form-control-custom" id="ApiKey" name="ApiKey"
|
||||||
|
formControlName="apiKey" readonly="readonly" #apiKey>
|
||||||
|
|
||||||
|
<div class="input-group-addon">
|
||||||
|
<div (click)="refreshApiKey()" id="refreshKey" class="fa fa-refresh" title="Reset API Key"
|
||||||
|
pTooltip="This will invalidate the old API key"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group-addon">
|
||||||
|
<div ngxClipboard [ngxClipboard]="apiKey" class="fa fa-clipboard" (cbOnSuccess)="successfullyCopied()"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<br />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="hideRequestsUsers" name="hideRequestsUsers" formControlName="hideRequestsUsers">
|
<input type="checkbox" id="doNotSendNotificationsForAutoApprove" name="doNotSendNotificationsForAutoApprove"
|
||||||
<label for="hideRequestsUsers">Hide requests from other users</label>
|
formControlName="doNotSendNotificationsForAutoApprove">
|
||||||
|
<label for="doNotSendNotificationsForAutoApprove">Do not send Notifications if a User has the Auto
|
||||||
|
Approve permission</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<div class="checkbox">
|
||||||
<div class="checkbox">
|
<input type="checkbox" id="hideRequestsUsers" name="hideRequestsUsers" formControlName="hideRequestsUsers">
|
||||||
<input type="checkbox" id="ignoreCertificateErrors" name="ignoreCertificateErrors" formControlName="ignoreCertificateErrors">
|
<label for="hideRequestsUsers">Hide requests from other users</label>
|
||||||
<label for="ignoreCertificateErrors" tooltipPosition="top" pTooltip="Enable if you are having connectivity problems over SSL">Ignore any certificate errors</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="checkbox">
|
|
||||||
<input type="checkbox" id="CollectAnalyticData" name="CollectAnalyticData" formControlName="collectAnalyticData">
|
|
||||||
<label for="CollectAnalyticData">Allow us to collect anonymous analytical data e.g. browser used</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div class="checkbox">
|
||||||
<button [disabled]="form.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
<input type="checkbox" id="ignoreCertificateErrors" name="ignoreCertificateErrors" formControlName="ignoreCertificateErrors">
|
||||||
|
<label for="ignoreCertificateErrors" tooltipPosition="top" pTooltip="Enable if you are having connectivity problems over SSL">Ignore
|
||||||
|
any certificate errors</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="CollectAnalyticData" name="CollectAnalyticData" formControlName="collectAnalyticData">
|
||||||
|
<label for="CollectAnalyticData" tooltipPosition="top" pTooltip="This will allow us to have a better understanding of the userbase so we know what we should be supporting">Allow
|
||||||
|
us to collect anonymous analytical data e.g. browser used</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngIf="langauges">
|
||||||
|
<label for="select" class="control-label">Language</label>
|
||||||
|
<div id="profiles">
|
||||||
|
<select formControlName="defaultLanguageCode" class="form-control form-control-custom" id="select">
|
||||||
|
<option *ngFor="let lang of langauges" value="{{lang.code}}">{{lang.nativeName}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button [disabled]="form.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
|
@ -1,16 +1,19 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, FormGroup } from "@angular/forms";
|
import { FormBuilder, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
import { IOmbiSettings } from "../../interfaces";
|
import { ILanguageRefine, IOmbiSettings } from "../../interfaces";
|
||||||
import { NotificationService } from "../../services";
|
import { NotificationService } from "../../services";
|
||||||
import { SettingsService } from "../../services";
|
import { SettingsService } from "../../services";
|
||||||
|
|
||||||
|
import * as languageData from "../../../other/iso-lang.json";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./ombi.component.html",
|
templateUrl: "./ombi.component.html",
|
||||||
})
|
})
|
||||||
export class OmbiComponent implements OnInit {
|
export class OmbiComponent implements OnInit {
|
||||||
|
|
||||||
public form: FormGroup;
|
public form: FormGroup;
|
||||||
|
public langauges: ILanguageRefine[];
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService,
|
constructor(private settingsService: SettingsService,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
|
@ -25,8 +28,10 @@ export class OmbiComponent implements OnInit {
|
||||||
baseUrl: [x.baseUrl],
|
baseUrl: [x.baseUrl],
|
||||||
doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove],
|
doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove],
|
||||||
hideRequestsUsers: [x.hideRequestsUsers],
|
hideRequestsUsers: [x.hideRequestsUsers],
|
||||||
|
defaultLanguageCode: [x.defaultLanguageCode],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
this.langauges = <ILanguageRefine[]><any>languageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshApiKey() {
|
public refreshApiKey() {
|
||||||
|
|
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