1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -04:00

Compare commits

...

83 Commits

Author SHA1 Message Date
FuNK3Y 002f84c58b Merge 3f26dae02b into 7756b03555 2019-05-04 20:38:40 +00:00
FuNK3Y 3f26dae02b Merge branch 'develop' into AddMetadataURL 2019-05-04 22:38:37 +02:00
Leonardo Galli 7756b03555 Fixed: a after a shortened word is not removed correctly for matching. Fixes ##3487 2019-05-02 13:18:28 +02:00
Leonardo Galli 5faaffc9c6 @cosmetic updated changelog 2019-05-02 13:05:35 +02:00
Ricardo Amaral 7a43bf3f60 Added: Tags support to NetImport (Lists) (#3127)
* Add tags support to NetImport (Lists)

* Keep indentation consistent with current code

* Initialize set of empty tags

* Add tags from list to movie

* Prevent tags used by lists from being removed
2019-05-02 12:57:40 +02:00
RhinoRhys 84fab25af7 Changed: Improved templates for h264 & h265 custom formats (#3432)
* Update CustomFormatService.cs

resolves #3419

* Update CustomFormatService.cs

* Update CustomFormatService.cs
2019-05-02 12:55:48 +02:00
jwildman16 52520e356d Changed: Logo-256.png to a transparent version so it looks better in Android notifications. (#3479) 2019-05-02 12:55:01 +02:00
Logan cc44d022b3 Added: Radarr_Download_Id and Radarr_Download_Client to the environment (#3276)
Fixes #3232
2019-05-02 12:52:33 +02:00
Steven Crouchman f411903e90 New: Allow CheckForFinishedDownloadInterval to be set from the UI (#3233)
* Adding CheckForFinishedDownloadInterval to config service
Changed TaskManager to use a configurable interval for CheckForFinishedDownloadCommand

* Adding new CheckForFinishedDownloadInterval to UI

Fixes #840
2019-05-02 12:51:21 +02:00
stephanrenggli 9985554dcb New: Added support for Gotify notifications (#3474)
* Added support for Gotify notifications

* Use string interpolation to build url

* Adapt changes by markus101 over at Sonarr

* Remove blank line

Fixes #3472
2019-05-02 12:49:21 +02:00
Viserius f8c009a348 Fixed: Solve ambiguous naming (#3417) 2019-05-02 12:40:51 +02:00
Michael Poutre da012eb6b5 Fixed: Copy to clipboard not working with calendar feed (#3495)
* Replace flash clipboard implementation with document.execute("copy")

Fixes #3486

* Replace flash clipboard implementation with document.execCommand("copy")

Resolves Radarr/Radarr#3486
2019-05-02 12:38:00 +02:00
RobinQ124274 c79578e99f Added: Remote poster and fanart references to Kodi metadata file (#2837) (#3302)
* Added: Remote poster and fanart references to Kodi metadata file (#2837)

* Added: Null checks to Remote poster and fanart references to Kodi metadata file (#2837)
2019-04-27 19:33:06 -04:00
FuNK3Y 64626b8bc3 Update XbmcMetadata.cs
Code styling again
2019-04-16 20:40:09 +02:00
FuNK3Y 85c0b95a3c Update XbmcMetadata.cs
Code styling
2019-04-16 20:36:27 +02:00
FuNK3Y b48d9a6bac Merge branch 'develop' into AddMetadataURL 2019-04-12 22:11:33 +02:00
Leonardo Galli 961fe70cb8 @cosmetic Hopefully fixed nunit randomly failing. (updated nunit and mono) 2019-03-19 11:34:03 +01:00
Qstick 65267af512 Fixed: Edition Tags Not Showing in UI (#3389) 2019-03-19 11:08:15 +01:00
Qstick ac387f208a Fixed: Support new feed url format IPTorrents (#573) (#3390) 2019-03-19 11:03:40 +01:00
Qstick 80304d8804 New: Support Krypton Kodi Unique Ids (#3388) 2019-03-19 11:03:20 +01:00
Qstick cafb1a5a45 Changed: Adjust Max Slider Value for Qualities (#3393) 2019-03-19 10:59:57 +01:00
hotio feef9b1b8d Update README.md (#3409)
Update outdated suitarr info.
2019-03-19 10:56:26 +01:00
Leonardo Galli 4aec27890c Fixed: MediaCovers Race condition which leads to fanart not being downloaded
Fixes #3350
2019-02-24 19:54:02 +01:00
Qstick 58ae4417df Fixed: Cannot Add ITA or ITALIAN custom format (#3385)
Fixes #3253
2019-02-24 18:26:20 +01:00
Qstick 6287bfd9d9 New: Nzb Validation and Nzbget/SAB URLBase (#3380) 2019-02-24 18:16:12 +01:00
Leonardo Galli 5f139c0cb6 Fixed: OsInfo for real this time. 2019-02-24 18:11:42 +01:00
Qstick 6cf257ac01 Fixed: Error in unicode cleanup code removing most non-latin characters instead of just invalid ones. (#3383) 2019-02-24 18:08:28 +01:00
Qstick 5cb5faa8a3 Fixed: Forced Subs not parsed by SubtitleLanguageRegex (#3384)
Fixes #2908
2019-02-24 18:07:48 +01:00
Leonardo Galli a28f2fd21d Fixed: OsInfo being renamed to PlatformInfo 2019-02-24 18:06:48 +01:00
Qstick dbf12e1fa4 Fixed: Word Boundary on Edition (#3382) 2019-02-24 18:03:42 +01:00
Václav Slavík 264629cfa5 Fixed: Mis-classification of releases as being Czech (#3378)
After the changes from PR #2948, the regex was too eager to match any
substring contain "SK", such as in "MASK". Fix by requiring word
separation around it, as was already the case with the "CZ" token.
2019-02-24 18:00:04 +01:00
Pieter Janssens 53f49f3b07 Fixed: Match MBluray releases (#3358) 2019-02-24 17:59:25 +01:00
Leonardo Galli a26016fc08 Fixed: Mono bug causing memory leakage when http connections use gzip compression.
The bug is registered upstream, but this commit works around the problem by doing the gzip decompression separately from the http stack.

Ref #2296
2019-02-24 17:58:19 +01:00
Leonardo Galli bd969e0bc3 Fixed: Build errors due to dotnet library. 2019-02-24 00:34:23 +01:00
hatharry 3e96fe4a72 Fixed: Emby library update (#3318)
Fixes: #3158
2019-02-18 11:12:34 +01:00
tobsen987 19299ad47f Fixed: Prevent NullRef in CustomScript on Grab for Indexer (#3323)
fixes #3313
2019-02-18 11:10:22 +01:00
Leonardo Galli 8902a3ca44 @cosmetic removed deploy 2019-02-18 11:07:51 +01:00
Leonardo Galli 4015ff08a6 @cosmetic Added Parsing Library: A dotnet library that "exports" our parsing interface. 2019-02-18 00:09:41 +01:00
Leonardo Galli 9bc50749ae Update README.md 2019-01-31 11:41:07 +01:00
Leonardo Galli 34271605c3 @cosmetic Merge pull request #3339 from hotio/patch-2
Update docker link in README
2019-01-31 11:31:26 +01:00
hotio 8272a160d2 Update docker link in README 2019-01-28 10:20:42 +01:00
Leonardo Galli e7ad09cf62 Fixed: Hopefully fixed sqlite errors when finding by title. 2019-01-10 16:07:41 +01:00
Leonardo Galli 420cbf6dd0 @cosmetic updated changelog. 2019-01-10 08:39:13 +01:00
Leonardo Galli 02eb30b8ee Fixed: A lot of memory issues, such as IDisposable leakage and leaking all the media covers. (#3227)
Thanks again to @Taloth
2019-01-10 08:36:34 +01:00
Leonardo Galli 49b1f2a67d New: Use APIKey & APIUser for authenticating to PassThePopcorn. (#3264) 2018-12-30 14:17:26 +01:00
Swizzy 7c90667965 Added: Support for forcedUP status (#3277)
Also added a warning message upon hitting new unknown states so that they can be handled properly at some point
2018-12-30 14:17:03 +01:00
Leonardo Galli 6d3cff85ae Fixed: Unable to parse movies from 1800s.
Fixes #3271
2018-12-30 13:55:45 +01:00
Leonardo Galli b9aba5f424 @cosmetic Updated readme to include roadmap. 2018-12-30 13:32:56 +01:00
Leonardo Galli 47b481e797 Fixed: OSX Packages not getting correct version info
Fixes #3274
2018-12-30 13:29:44 +01:00
Taloth Saldono 3ef224ed1d Fixed: Another IDisposable leak when lazy loading properties. 2018-12-26 15:50:06 +01:00
Leonardo Galli 44b1252ecc Fixed: npm start not working with node 10
Fixes #3258
2018-12-24 15:56:53 +01:00
Leonardo Galli 44842a4e6b Fixed: Hopefully fixed issue where a null downloadId or title would cause no downloads to be tracked.
Fixes #3260
2018-12-24 14:49:33 +01:00
Leonardo Galli cd520b0341 Fixed: MediaCoverService tests and stupidly forgetting to open the database connection for logging. 2018-12-14 18:48:36 +01:00
Leonardo Galli fed4a0aebe Fixed: MediaCovers resizing potentially leaking memory when concurrently executing. 2018-12-14 09:55:14 +01:00
lps-rocks ff894d5210 New: rTorrent - Don't start download automatically (#3222) 2018-12-07 11:26:26 +01:00
Qstick 92b5822a39 New: Remove Pre, postbot, xpost suffixes from release groups (#3220) 2018-12-07 11:22:06 +01:00
Qstick cbdea30a6e New: Secure URLs for Links and Services (#3219) 2018-12-07 11:21:43 +01:00
Qstick 969ef5c515 Fixed: Bump default version to prevent update running from source (#3199) 2018-12-07 11:20:54 +01:00
Qstick 5b52115d68 Fixed: MediaInfo Unit Test Failing due to AudioAdditionalFeatures (#3221) 2018-12-06 12:59:02 +01:00
Leonardo Galli 899bd086ec Fixed: Stream leakage inside CurlHttpDispatcher 2018-12-02 19:24:16 +01:00
Leonardo Galli d02d71c336 Fixed: Apparently Microsoft thinks that you should cast to IDisposable first. 2018-12-02 19:04:28 +01:00
Leonardo Galli d4061bd13c Fixed: Invalid SQLite cache size. 2018-12-02 18:22:18 +01:00
Leonardo Galli c83995adc1 Fixed: Resource leakage inside HttpClient. 2018-12-02 18:16:05 +01:00
Leonardo Galli df18c34878 Fixed: Typo that could probably lead to an infinite loop. 2018-12-02 18:14:52 +01:00
Leonardo Galli eb077b043e Fixed: Resource leakage inside ManagedHttpDispatcher. 2018-12-02 18:14:21 +01:00
Leonardo Galli 42015d5d95 Fixed: Leaking of objects when logging something to the database. 2018-12-02 18:12:03 +01:00
Leonardo Galli 09899fcf6c Fixed: DataMapper potentially leaking stuff when being disposed. 2018-12-02 18:10:57 +01:00
Leonardo Galli 8e7b718209 Fixed: DataMapper not being disposed, leading to resource leakage. 2018-12-02 18:10:14 +01:00
Leonardo Galli d38562664d Fixed: Memory leak due to unmanaged Bitmaps leaking. 2018-12-02 17:59:15 +01:00
FuNK3Y abf738ffee Merge branch 'develop' into AddMetadataURL 2018-11-29 20:46:04 +01:00
Leonardo Galli 77950645af @cosmetic add yarn to circleci image 2018-11-25 01:03:50 +01:00
Leonardo Galli 60a9be71bc @cosmetic fix deploy 2018-11-24 19:54:36 +01:00
Leonardo Galli 49ee288dae @cosmetic forgot this 2018-11-24 19:47:37 +01:00
Leonardo Galli 443995cd93 Merge branch 'develop' of https://github.com/Radarr/Radarr into develop 2018-11-24 19:20:02 +01:00
Leonardo Galli 1456200717 @cosmetic added auto deploy for prs and branch builds 2018-11-24 19:19:53 +01:00
Leonardo Galli 28155b09a0 @cosmetic Hopefully made circle ci artifacts compatible with hotios docker 2018-11-24 16:31:53 +01:00
Leonardo Galli 9ba1d26f05 @cosmetic updated release changelog templates. 2018-11-20 11:18:55 +01:00
Leonardo Galli e439fb00b5 As requested
Co-Authored-By: FuNK3Y <FuNK3Y@users.noreply.github.com>
2018-11-10 16:20:39 +01:00
Leonardo Galli e1fa7440da Update src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadata.cs
Co-Authored-By: FuNK3Y <FuNK3Y@users.noreply.github.com>
2018-11-10 16:16:50 +01:00
Leonardo Galli e0e531c287 @cosmetic Updated changelog. 2018-11-07 20:59:37 +01:00
FuNK3Y 24cff4d6ee Update XbmcMetadata.cs
Fix typo
2018-10-14 14:24:36 +02:00
FuNK3Y 0df5cd478b Update XbmcMetadataSettings.cs
Fix typo
2018-10-14 14:24:00 +02:00
FuN_K3Y 3a55b766c3 Add imdb url, added setting, fix movie.nfo being renamed on movie rename 2018-10-14 09:59:57 +02:00
170 changed files with 4037 additions and 2777 deletions
+4 -3
View File
@@ -1,13 +1,14 @@
FROM mono:5.8 FROM mono:5.18
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y git ssh tar gzip ca-certificates wget zip wine wine32 wine64 libwine libwine:i386 RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y git ssh tar gzip ca-certificates wget zip wine wine32 wine64 libwine libwine:i386
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -E - RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -E -
RUN apt-get install -y nodejs RUN apt-get install -y nodejs
RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-5_all.deb && dpkg -i repo-mediaarea_1.0-5_all.deb && apt-get update RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-7_all.deb && dpkg -i repo-mediaarea_1.0-7_all.deb && apt-get update
RUN apt-get install -y libmediainfo-dev libmediainfo0 mediainfo RUN apt-get install -y libmediainfo-dev libmediainfo0v5 mediainfo
RUN npm i -g npm RUN npm i -g npm
RUN apt-get install -y python3-pip && pip3 install gitchangelog pystache RUN apt-get install -y python3-pip && pip3 install gitchangelog pystache
RUN curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz && tar xvf go*.tar.gz && chown -R root:root ./go && mv go /usr/local RUN curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz && tar xvf go*.tar.gz && chown -R root:root ./go && mv go /usr/local
ENV GOPATH=$HOME/work ENV GOPATH=$HOME/work
ENV PATH="${PATH}:/usr/local/go/bin:$GOPATH/bin" ENV PATH="${PATH}:/usr/local/go/bin:$GOPATH/bin"
RUN go get github.com/aktau/github-release RUN go get github.com/aktau/github-release
RUN npm install -g yarn
+7 -3
View File
@@ -2,7 +2,7 @@ version: 2
defaults: &defaults defaults: &defaults
docker: docker:
- image: gallileo/radarr-cci-primary:5.8.7 - image: gallileo/radarr-cci-primary:5.8.9
environment: environment:
BUILD_VERSION: 0.2.0 BUILD_VERSION: 0.2.0
@@ -62,6 +62,7 @@ jobs:
- _tests - _tests
- setup - setup
- .circleci - .circleci
- deploy.sh
unit_tests: unit_tests:
<<: *defaults <<: *defaults
steps: steps:
@@ -105,10 +106,10 @@ jobs:
zip -r _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.windows.zip _packages/Radarr zip -r _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.windows.zip _packages/Radarr
rm -rf _packages/Radarr rm -rf _packages/Radarr
cp -r _output_mono/ _packages/Radarr cp -r _output_mono/ _packages/Radarr
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz _packages/Radarr tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz -C _packages Radarr
rm -rf _packages/Radarr rm -rf _packages/Radarr
cp -r _output_osx/ _packages/Radarr cp -r _output_osx/ _packages/Radarr
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx.tar.gz _packages/Radarr tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx.tar.gz -C _packages Radarr
rm -rf _packages/Radarr rm -rf _packages/Radarr
cd _output_osx_app/ cd _output_osx_app/
zip -r ../_packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx-app.zip * zip -r ../_packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx-app.zip *
@@ -118,6 +119,9 @@ jobs:
- store_artifacts: - store_artifacts:
path: _packages path: _packages
destination: artifacts destination: artifacts
#- run:
# name: "Deploying"
# command: chmod +x deploy.sh && ./deploy.sh
- persist_to_workspace: - persist_to_workspace:
root: . root: .
# Must be relative path from root # Must be relative path from root
+3 -3
View File
@@ -82,13 +82,13 @@ ignore_regexps = [
## whenever you are tweaking this variable. ## whenever you are tweaking this variable.
## ##
section_regexps = [ section_regexps = [
('**New features**', [ ('**New features:**', [
r'^[aA]dded?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', r'^[aA]dded?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
r'^[uU]pdated?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', r'^[uU]pdated?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
r'^[cC]hanged?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', r'^[cC]hanged?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
r'^[nN]ew?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', r'^[nN]ew?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]), ]),
('**Fixes**', [ ('**Fixes:**', [
r'^(?![mM]erge\s*)' r'^(?![mM]erge\s*)'
] ]
), ),
@@ -151,7 +151,7 @@ subject_process = (strip |
ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r'\4') | ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r'\4') |
ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r'\4') | ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r'\4') |
ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r'\4') | ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r'\4') |
ReSub(r'#(\d{3,4})', r'<a href="https://github.com/Radarr/Radarr/issues/\1">\1</a>') | ReSub(r'#(\d{3,4})', r'Issue #\1') |
SetIfEmpty("No commit message.") | ucfirst | final_dot) SetIfEmpty("No commit message.") | ucfirst | final_dot)
+4 -1
View File
@@ -104,7 +104,7 @@ _TeamCity*
# Radarr # Radarr
Backups/ Backups/
logs/ logs/
MediaCover/ #MediaCover/
UpdateLogs/ UpdateLogs/
xdg/ xdg/
config.xml config.xml
@@ -177,3 +177,6 @@ packages.config.md5sum
**/.idea/**/*.iml **/.idea/**/*.iml
**/.idea/**/contentModel.xml **/.idea/**/contentModel.xml
**/.idea/**/modules.xml **/.idea/**/modules.xml
# ignore node_modules symlink
node_modules
node_modules.nosync
+78
View File
@@ -3,6 +3,78 @@
## (unreleased) ## (unreleased)
### **New features** ### **New features**
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Tags support to NetImport (Lists) ([#3127](https://github.com/Radarr/Radarr/issues/3127)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Improved templates for h264 & h265 custom formats ([#3432](https://github.com/Radarr/Radarr/issues/3432)) [<a href="https://github.com/RhinoRhys">RhinoRhys</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Logo-256.png to a transparent version so it looks better in Android notifications. ([#3479](https://github.com/Radarr/Radarr/issues/3479)) [<a href="https://github.com/jwildman16">jwildman16</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Radarr_Download_Id and Radarr_Download_Client to the environment ([#3276](https://github.com/Radarr/Radarr/issues/3276)) [<a href="https://github.com/Logan">Logan</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Allow CheckForFinishedDownloadInterval to be set from the UI ([#3233](https://github.com/Radarr/Radarr/issues/3233)) [<a href="https://github.com/Steven Crouchman">Steven Crouchman</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Added support for Gotify notifications ([#3474](https://github.com/Radarr/Radarr/issues/3474)) [<a href="https://github.com/stephanrenggli">stephanrenggli</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Remote poster and fanart references to Kodi metadata file ([#2837](https://github.com/Radarr/Radarr/issues/2837)) ([#3302](https://github.com/Radarr/Radarr/issues/3302)) [<a href="https://github.com/RobinQ124274">RobinQ124274</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Support Krypton Kodi Unique Ids ([#3388](https://github.com/Radarr/Radarr/issues/3388)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Adjust Max Slider Value for Qualities ([#3393](https://github.com/Radarr/Radarr/issues/3393)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Updated](https://img.shields.io/badge/--%20-Updated-blue.svg?style=flat-square) README.md ([#3409](https://github.com/Radarr/Radarr/issues/3409)) [<a href="https://github.com/hotio">hotio</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Nzb Validation and Nzbget/SAB URLBase ([#3380](https://github.com/Radarr/Radarr/issues/3380)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Updated](https://img.shields.io/badge/--%20-Updated-blue.svg?style=flat-square) README.md. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Updated](https://img.shields.io/badge/--%20-Updated-blue.svg?style=flat-square) docker link in README. [<a href="https://github.com/hotio">hotio</a>]
### **Fixes**
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Solve ambiguous naming ([#3417](https://github.com/Radarr/Radarr/issues/3417)) [<a href="https://github.com/Viserius">Viserius</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Copy to clipboard not working with calendar feed ([#3495](https://github.com/Radarr/Radarr/issues/3495)) [<a href="https://github.com/Michael Poutre">Michael Poutre</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Edition Tags Not Showing in UI ([#3389](https://github.com/Radarr/Radarr/issues/3389)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Support new feed url format IPTorrents ([#573](https://github.com/Radarr/Radarr/issues/573)) ([#3390](https://github.com/Radarr/Radarr/issues/3390)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaCovers Race condition which leads to fanart not being downloaded. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Cannot Add ITA or ITALIAN custom format ([#3385](https://github.com/Radarr/Radarr/issues/3385)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) OsInfo for real this time. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Error in unicode cleanup code removing most non-latin characters instead of just invalid ones. ([#3383](https://github.com/Radarr/Radarr/issues/3383)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Forced Subs not parsed by SubtitleLanguageRegex ([#3384](https://github.com/Radarr/Radarr/issues/3384)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) OsInfo being renamed to PlatformInfo. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Word Boundary on Edition ([#3382](https://github.com/Radarr/Radarr/issues/3382)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Mis-classification of releases as being Czech ([#3378](https://github.com/Radarr/Radarr/issues/3378)) [<a href="https://github.com/Václav Slavík">Václav Slavík</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Match MBluray releases ([#3358](https://github.com/Radarr/Radarr/issues/3358)) [<a href="https://github.com/Pieter Janssens">Pieter Janssens</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Mono bug causing memory leakage when http connections use gzip compression. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Build errors due to dotnet library. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Emby library update ([#3318](https://github.com/Radarr/Radarr/issues/3318)) [<a href="https://github.com/hatharry">hatharry</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Prevent NullRef in CustomScript on Grab for Indexer ([#3323](https://github.com/Radarr/Radarr/issues/3323)) [<a href="https://github.com/tobsen987">tobsen987</a>]
## v0.2.0.1293 (2019-01-10)
### **New features**
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Use APIKey & APIUser for authenticating to PassThePopcorn. ([#3264](https://github.com/Radarr/Radarr/issues/3264)) [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Support for forcedUP status ([#3277](https://github.com/Radarr/Radarr/issues/3277)) [<a href="https://github.com/Swizzy">Swizzy</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) rTorrent - Don't start download automatically ([#3222](https://github.com/Radarr/Radarr/issues/3222)) [<a href="https://github.com/lps-rocks">lps-rocks</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Remove Pre, postbot, xpost suffixes from release groups ([#3220](https://github.com/Radarr/Radarr/issues/3220)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Secure URLs for Links and Services ([#3219](https://github.com/Radarr/Radarr/issues/3219)) [<a href="https://github.com/Qstick">Qstick</a>]
### **Fixes**
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Hopefully fixed sqlite errors when finding by title. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Another IDisposable leak when lazy loading properties. [<a href="https://github.com/Taloth Saldono">Taloth Saldono</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaCoverService tests and stupidly forgetting to open the database connection for logging. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaCovers resizing potentially leaking memory when concurrently executing. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Stream leakage inside CurlHttpDispatcher. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Apparently Microsoft thinks that you should cast to IDisposable first. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Invalid SQLite cache size. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Resource leakage inside HttpClient. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Typo that could probably lead to an infinite loop. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Resource leakage inside ManagedHttpDispatcher. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Leaking of objects when logging something to the database. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) DataMapper potentially leaking stuff when being disposed. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) DataMapper not being disposed, leading to resource leakage. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Memory leak due to unmanaged Bitmaps leaking. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Unable to parse movies from 1800s. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) OSX Packages not getting correct version info. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) npm start not working with node 10. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Hopefully fixed issue where a null downloadId or title would cause no downloads to be tracked. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Bump default version to prevent update running from source ([#3199](https://github.com/Radarr/Radarr/issues/3199)) [<a href="https://github.com/Qstick">Qstick</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) MediaInfo Unit Test Failing due to AudioAdditionalFeatures ([#3221](https://github.com/Radarr/Radarr/issues/3221)) [<a href="https://github.com/Qstick">Qstick</a>]
## v0.2.0.1216 (2018-11-07)
### **New features**
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) 64bit mediainfo.dll to 32bit to resolve issue: https://github.com/Radarr/Radarr/issues/3138. [<a href="https://github.com/geogolem">geogolem</a>]
- ![New](https://img.shields.io/badge/--%20-New-brightgreen.svg?style=flat-square) Refactor MediaInfo tokens (fixes old tokens adds new stuff) ([#3058](https://github.com/Radarr/Radarr/issues/3058)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Don't hide custom formats behind advanced settings when editing quality. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Upped rate at which we scan the download client. Should reduce cpu and ram usage as well as decrease pressure on download clients. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>] - ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Upped rate at which we scan the download client. Should reduce cpu and ram usage as well as decrease pressure on download clients. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Improve model and UI handling for lists. Should finally fix root folder errors. ([#3133](https://github.com/Radarr/Radarr/issues/3133)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>] - ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Improve model and UI handling for lists. Should finally fix root folder errors. ([#3133](https://github.com/Radarr/Radarr/issues/3133)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Don't return unmapped folders on rootfolder API call. Massively improves loading time. ([#3116](https://github.com/Radarr/Radarr/issues/3116)) [<a href="https://github.com/Justin Kromlinger">Justin Kromlinger</a>] - ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) Don't return unmapped folders on rootfolder API call. Massively improves loading time. ([#3116](https://github.com/Radarr/Radarr/issues/3116)) [<a href="https://github.com/Justin Kromlinger">Justin Kromlinger</a>]
@@ -20,6 +92,12 @@
- ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) "importing an episode" to "importing a movie file" ([#2829](https://github.com/Radarr/Radarr/issues/2829)) [<a href="https://github.com/Travis Boss">Travis Boss</a>] - ![Changed](https://img.shields.io/badge/--%20-Changed-orange.svg?style=flat-square) "importing an episode" to "importing a movie file" ([#2829](https://github.com/Radarr/Radarr/issues/2829)) [<a href="https://github.com/Travis Boss">Travis Boss</a>]
### **Fixes** ### **Fixes**
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Fallback to 'VideoCodec' if 'VideoFormat' is unavailable ([#3142](https://github.com/Radarr/Radarr/issues/3142)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Read video 'BitRate_Nominal' if 'BitRate' is empty ([#3144](https://github.com/Radarr/Radarr/issues/3144)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) UpdateMovieQualityService Tests. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Ignore "special drives" from System » Disk Space ([#3050](https://github.com/Radarr/Radarr/issues/3050)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Tweak style of movie path template on "add movies" screen ([#3108](https://github.com/Radarr/Radarr/issues/3108)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Unable to update custom formats for releases with bad Source Titles. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Do not search movie if unmonitored ([#3131](https://github.com/Radarr/Radarr/issues/3131)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>] - ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Do not search movie if unmonitored ([#3131](https://github.com/Radarr/Radarr/issues/3131)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Quality badges not being shown on bulk import. ([#3121](https://github.com/Radarr/Radarr/issues/3121)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>] - ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Quality badges not being shown on bulk import. ([#3121](https://github.com/Radarr/Radarr/issues/3121)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
- ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Trim filename from Kodi movie path before sending library scan request. ([#3097](https://github.com/Radarr/Radarr/issues/3097)) [<a href="https://github.com/Lawrence">Lawrence</a>] - ![Fixed](https://img.shields.io/badge/--%20-Fixed-red.svg?style=flat-square) Trim filename from Kodi movie path before sending library scan request. ([#3097](https://github.com/Radarr/Radarr/issues/3097)) [<a href="https://github.com/Lawrence">Lawrence</a>]
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 28 KiB

+8 -12
View File
@@ -6,6 +6,8 @@ Radarr is an __independent__ fork of [Sonarr](https://github.com/Sonarr/Sonarr)
The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato. The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
## Getting Started ## Getting Started
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/wiki/Installation) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/wiki/Installation)
@@ -20,13 +22,12 @@ The project was inspired by other Usenet/BitTorrent movie downloaders such as Co
## Downloads ## Downloads
[![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) Branch | develop (stable) | nightly (semi-unstable) |
[![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts) ---|---|---
Binary Releases | [![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) | [![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts)
Docker (linuxserver.io, x86_64, arm64, armhf) | [![Docker release](https://img.shields.io/badge/linuxserver-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr) | [![Docker nightly](https://img.shields.io/badge/linuxserver-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
Docker (hotio, see [here](https://github.com/hotio/docker-radarr) for more information) | [![Docker release / nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker release / nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr)
[![Docker release](https://img.shields.io/badge/docker-release-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
[![Docker nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/hotio/suitarr)
[![Docker armhf](https://img.shields.io/badge/docker-armhf-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr)
[![Docker aarch64](https://img.shields.io/badge/docker-aarch64-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr-aarch64)
## Support ## Support
@@ -93,12 +94,7 @@ Radarr is currently undergoing rapid development and pull requests are actively
### Planned Features ### Planned Features
* Dynamically renaming folders with quality info, etc. (Currently working partially.) (\*) See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
* Supporting custom folder structures, such as all movie files in one folder (\*)
* Supporting multiple editions per movies (\*)
* Supporting collections of movies, such as James Bond (\*)
**Note:** All features marked with (\*) are set to be in the first release of Radarr.
#### [Feature Requests](http://feathub.com/Radarr/Radarr) #### [Feature Requests](http://feathub.com/Radarr/Radarr)
+5 -1
View File
@@ -1,6 +1,7 @@
#addin nuget:?package=Cake.Npm #addin nuget:?package=Cake.Npm
#addin nuget:?package=SharpZipLib #addin nuget:?package=SharpZipLib
#addin nuget:?package=Cake.Compression #addin nuget:?package=Cake.Compression
#addin "Cake.FileHelpers"
// Build variables // Build variables
var outputFolder = "./_output"; var outputFolder = "./_output";
@@ -178,7 +179,7 @@ Task("PackageOsx").Does(() => {
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr"); CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
}); });
Task("PackageOsxApp").Does(() => { Task("PackageOsxApp").Does((ctx) => {
// Start osx app package // Start osx app package
if (DirectoryExists(outputFolderOsxApp)) { if (DirectoryExists(outputFolderOsxApp)) {
DeleteDirectory(outputFolderOsxApp, true); DeleteDirectory(outputFolderOsxApp, true);
@@ -189,6 +190,9 @@ Task("PackageOsxApp").Does(() => {
// Copy osx package files // Copy osx package files
CopyDirectory("./osx/Radarr.app", outputFolderOsxApp + "/Radarr.app"); CopyDirectory("./osx/Radarr.app", outputFolderOsxApp + "/Radarr.app");
CopyDirectory(outputFolderOsx, outputFolderOsxApp + "/Radarr.app/Contents/MacOS"); CopyDirectory(outputFolderOsx, outputFolderOsxApp + "/Radarr.app/Contents/MacOS");
// Edit version of osx app
ctx.ReplaceTextInFiles(outputFolderOsxApp + "/Radarr.app/Contents/Info.plist", "2.0", ctx.EnvironmentVariable("APPVEYOR_BUILD_VERSION") ?? "unknown");
}); });
Task("PackageTests").Does(() => { Task("PackageTests").Does(() => {
+2 -2
View File
@@ -221,9 +221,9 @@ PackageTests()
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \; find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
if [ $runtime = "dotnet" ] ; then if [ $runtime = "dotnet" ] ; then
$nuget install NUnit.Runners -Version 3.2.1 -Output $testPackageFolder $nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
else else
mono $nuget install NUnit.Runners -Version 3.2.1 -Output $testPackageFolder mono $nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
fi fi
cp $outputFolder/*.dll $testPackageFolder cp $outputFolder/*.dll $testPackageFolder
+1 -1
View File
@@ -5,7 +5,7 @@
{{#sections}} {{#sections}}
{{{label}}} {{{label}}}
{{#commits}} {{#commits}}
- {{{subject}}} [<a href="https://github.com/{{{author}}}">{{{author}}}</a>] - {{{subject}}} [{{{author}}}]
{{/commits}} {{/commits}}
{{/sections}} {{/sections}}
Vendored Executable → Regular
View File
+7
View File
@@ -0,0 +1,7 @@
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "We are building a normal branch, deploying as such..."
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=branch&name=${CIRCLE_BRANCH}"
else
echo "We are building a pr, deploying as such..."
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=pr&name=${CIRCLE_PR_NUMBER}"
fi
+2000 -1950
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -44,6 +44,7 @@
"tar.gz": "0.1.1", "tar.gz": "0.1.1",
"url-search-params": "^0.6.1", "url-search-params": "^0.6.1",
"webpack": "1.12.0", "webpack": "1.12.0",
"webpack-stream": "2.1.0" "webpack-stream": "2.1.0",
"natives": "^1.1.3"
} }
} }
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
</component>
</project>
+1 -1
View File
@@ -2,4 +2,4 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("0.1.0.*")] [assembly: AssemblyVersion("10.0.0.*")]
+12 -12
View File
@@ -941,21 +941,21 @@ namespace Marr.Data
{ {
if (disposing) if (disposing)
{ {
if (Command.Transaction != null) if (_command != null)
{ {
Command.Transaction.Dispose(); if (_command.Transaction != null)
Command.Transaction = null; {
} _command.Transaction.Dispose();
_command.Transaction = null;
}
if (Command.Connection != null) if (_command.Connection != null)
{ {
Command.Connection.Dispose(); _command.Connection.Dispose();
Command.Connection = null; _command.Connection = null;
} }
if (Command != null) _command.Dispose();
{
Command.Dispose();
_command = null; _command = null;
} }
} }
+4 -2
View File
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Data.Common; using System.Data.Common;
@@ -91,9 +91,11 @@ namespace Marr.Data.Mapping
Type entType = ent.GetType(); Type entType = ent.GetType();
if (_repos.Relationships.ContainsKey(entType)) if (_repos.Relationships.ContainsKey(entType))
{ {
var provider = _db.ProviderFactory;
var connectionString = _db.ConnectionString;
Func<IDataMapper> dbCreate = () => Func<IDataMapper> dbCreate = () =>
{ {
var db = new DataMapper(_db.ProviderFactory, _db.ConnectionString); var db = new DataMapper(provider, connectionString);
db.SqlMode = SqlModes.Text; db.SqlMode = SqlModes.Text;
return db; return db;
}; };
@@ -1,4 +1,4 @@
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Config namespace NzbDrone.Api.Config
@@ -11,6 +11,7 @@ namespace NzbDrone.Api.Config
public bool EnableCompletedDownloadHandling { get; set; } public bool EnableCompletedDownloadHandling { get; set; }
public bool RemoveCompletedDownloads { get; set; } public bool RemoveCompletedDownloads { get; set; }
public int CheckForFinishedDownloadInterval { get; set; }
public bool AutoRedownloadFailed { get; set; } public bool AutoRedownloadFailed { get; set; }
public bool RemoveFailedDownloads { get; set; } public bool RemoveFailedDownloads { get; set; }
@@ -28,6 +29,7 @@ namespace NzbDrone.Api.Config
EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling, EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling,
RemoveCompletedDownloads = model.RemoveCompletedDownloads, RemoveCompletedDownloads = model.RemoveCompletedDownloads,
CheckForFinishedDownloadInterval = model.CheckForFinishedDownloadInterval,
AutoRedownloadFailed = model.AutoRedownloadFailed, AutoRedownloadFailed = model.AutoRedownloadFailed,
RemoveFailedDownloads = model.RemoveFailedDownloads RemoveFailedDownloads = model.RemoveFailedDownloads
@@ -5,6 +5,7 @@ using System.Linq;
using Nancy; using Nancy;
using Nancy.Bootstrapper; using Nancy.Bootstrapper;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
namespace NzbDrone.Api.Extensions.Pipelines namespace NzbDrone.Api.Extensions.Pipelines
@@ -15,9 +16,14 @@ namespace NzbDrone.Api.Extensions.Pipelines
public int Order => 0; public int Order => 0;
private readonly Action<Action<Stream>, Stream> _writeGZipStream;
public GzipCompressionPipeline(Logger logger) public GzipCompressionPipeline(Logger logger)
{ {
_logger = logger; _logger = logger;
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
_writeGZipStream = OsInfo.IsMonoRuntime ? WriteGZipStreamMono : (Action<Action<Stream>, Stream>)WriteGZipStream;
} }
public void Register(IPipelines pipelines) public void Register(IPipelines pipelines)
@@ -43,14 +49,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
var contents = response.Contents; var contents = response.Contents;
response.Headers["Content-Encoding"] = "gzip"; response.Headers["Content-Encoding"] = "gzip";
response.Contents = responseStream => response.Contents = responseStream => _writeGZipStream(contents, responseStream);
{
using (var gzip = new GZipStream(responseStream, CompressionMode.Compress, true))
using (var buffered = new BufferedStream(gzip, 8192))
{
contents.Invoke(buffered);
}
};
} }
} }
@@ -61,6 +60,25 @@ namespace NzbDrone.Api.Extensions.Pipelines
} }
} }
private static void WriteGZipStreamMono(Action<Stream> innerContent, Stream targetStream)
{
using (var membuffer = new MemoryStream())
{
WriteGZipStream(innerContent, membuffer);
membuffer.Position = 0;
membuffer.CopyTo(targetStream);
}
}
private static void WriteGZipStream(Action<Stream> innerContent, Stream targetStream)
{
using (var gzip = new GZipStream(targetStream, CompressionMode.Compress, true))
using (var buffered = new BufferedStream(gzip, 8192))
{
innerContent.Invoke(buffered);
}
}
private static bool ContentLengthIsTooSmall(Response response) private static bool ContentLengthIsTooSmall(Response response)
{ {
var contentLength = response.Headers.GetValueOrDefault("Content-Length"); var contentLength = response.Headers.GetValueOrDefault("Content-Length");
@@ -24,6 +24,7 @@ namespace NzbDrone.Api.NetImport
resource.RootFolderPath = definition.RootFolderPath; resource.RootFolderPath = definition.RootFolderPath;
resource.ShouldMonitor = definition.ShouldMonitor; resource.ShouldMonitor = definition.ShouldMonitor;
resource.MinimumAvailability = definition.MinimumAvailability; resource.MinimumAvailability = definition.MinimumAvailability;
resource.Tags = definition.Tags;
} }
protected override void MapToModel(NetImportDefinition definition, NetImportResource resource) protected override void MapToModel(NetImportDefinition definition, NetImportResource resource)
@@ -36,6 +37,7 @@ namespace NzbDrone.Api.NetImport
definition.RootFolderPath = resource.RootFolderPath; definition.RootFolderPath = resource.RootFolderPath;
definition.ShouldMonitor = resource.ShouldMonitor; definition.ShouldMonitor = resource.ShouldMonitor;
definition.MinimumAvailability = resource.MinimumAvailability; definition.MinimumAvailability = resource.MinimumAvailability;
definition.Tags = resource.Tags;
} }
protected override void Validate(NetImportDefinition definition, bool includeWarnings) protected override void Validate(NetImportDefinition definition, bool includeWarnings)
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
namespace NzbDrone.Api.NetImport namespace NzbDrone.Api.NetImport
@@ -10,5 +11,6 @@ namespace NzbDrone.Api.NetImport
public string RootFolderPath { get; set; } public string RootFolderPath { get; set; }
public int ProfileId { get; set; } public int ProfileId { get; set; }
public MovieStatusType MinimumAvailability { get; set; } public MovieStatusType MinimumAvailability { get; set; }
public HashSet<int> Tags { get; set; }
} }
} }
@@ -2,23 +2,18 @@ using NzbDrone.Common.Http;
namespace NzbDrone.Common.Cloud namespace NzbDrone.Common.Cloud
{ {
public interface ISonarrCloudRequestBuilder public interface IRadarrCloudRequestBuilder
{ {
IHttpRequestBuilderFactory Services { get; } IHttpRequestBuilderFactory Services { get; }
IHttpRequestBuilderFactory SkyHookTvdb { get; }
IHttpRequestBuilderFactory TMDB { get; } IHttpRequestBuilderFactory TMDB { get; }
IHttpRequestBuilderFactory TMDBSingle { get; } IHttpRequestBuilderFactory TMDBSingle { get; }
} }
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder public class RadarrCloudRequestBuilder : IRadarrCloudRequestBuilder
{ {
public SonarrCloudRequestBuilder() public RadarrCloudRequestBuilder()
{ {
Services = new HttpRequestBuilder("http://radarr.aeonlucid.com/v1/") Services = new HttpRequestBuilder("https://radarr.aeonlucid.com/v1/")
.CreateFactory();
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
.SetSegment("language", "en")
.CreateFactory(); .CreateFactory();
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}") TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}")
@@ -31,7 +26,6 @@ namespace NzbDrone.Common.Cloud
} }
public IHttpRequestBuilderFactory Services { get; private set; } public IHttpRequestBuilderFactory Services { get; private set; }
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
public IHttpRequestBuilderFactory TMDB { get; private set; } public IHttpRequestBuilderFactory TMDB { get; private set; }
public IHttpRequestBuilderFactory TMDBSingle { get; private set; } public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
} }
@@ -123,27 +123,6 @@ namespace NzbDrone.Common.Disk
} }
} }
public bool IsValidGDIPlusImage(string filename)
{
if (!CanUseGDIPlus())
{
return true;
}
try
{
using (var bmp = new Bitmap(filename))
{
}
return true;
}
catch (Exception ex)
{
Logger.Debug(ex, "Corrupted image found at: {0}.", filename);
return false;
}
}
public bool FolderWritable(string path) public bool FolderWritable(string path)
{ {
Ensure.That(path, () => path).IsValidPath(); Ensure.That(path, () => path).IsValidPath();
@@ -20,7 +20,6 @@ namespace NzbDrone.Common.Disk
bool FileExists(string path); bool FileExists(string path);
bool FileExists(string path, StringComparison stringComparison); bool FileExists(string path, StringComparison stringComparison);
bool CanUseGDIPlus(); bool CanUseGDIPlus();
bool IsValidGDIPlusImage(string path);
bool FolderWritable(string path); bool FolderWritable(string path);
string[] GetDirectories(string path); string[] GetDirectories(string path);
string[] GetFiles(string path, SearchOption searchOption); string[] GetFiles(string path, SearchOption searchOption);
@@ -66,95 +66,96 @@ namespace NzbDrone.Common.Http.Dispatchers
lock (CurlGlobalHandle.Instance) lock (CurlGlobalHandle.Instance)
{ {
Stream responseStream = new MemoryStream(); using (Stream responseStream = new MemoryStream())
Stream headerStream = new MemoryStream(); using (Stream headerStream = new MemoryStream())
using (var curlEasy = new CurlEasy())
{ {
curlEasy.AutoReferer = false; using (var curlEasy = new CurlEasy())
curlEasy.WriteFunction = (b, s, n, o) =>
{ {
responseStream.Write(b, 0, s * n); curlEasy.AutoReferer = false;
return s * n; curlEasy.WriteFunction = (b, s, n, o) =>
};
curlEasy.HeaderFunction = (b, s, n, o) =>
{
headerStream.Write(b, 0, s * n);
return s * n;
};
AddProxy(curlEasy, request);
curlEasy.Url = request.Url.FullUri;
switch (request.Method)
{
case HttpMethod.GET:
curlEasy.HttpGet = true;
break;
case HttpMethod.POST:
curlEasy.Post = true;
break;
case HttpMethod.PUT:
curlEasy.Put = true;
break;
default:
throw new NotSupportedException(string.Format("HttpCurl method {0} not supported", request.Method));
}
curlEasy.FollowLocation = false;
curlEasy.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent; ;
if (request.RequestTimeout != TimeSpan.Zero)
{
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
}
if (OsInfo.IsWindows)
{
curlEasy.CaInfo = _caBundleFilePath;
}
if (cookies != null)
{
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
}
if (request.ContentData != null)
{
curlEasy.PostFieldSize = request.ContentData.Length;
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
}
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
using (var httpRequestHeaders = SerializeHeaders(request))
{
curlEasy.HttpHeader = httpRequestHeaders;
var result = curlEasy.Perform();
if (result != CurlCode.Ok)
{ {
switch (result) responseStream.Write(b, 0, s * n);
return s * n;
};
curlEasy.HeaderFunction = (b, s, n, o) =>
{
headerStream.Write(b, 0, s * n);
return s * n;
};
AddProxy(curlEasy, request);
curlEasy.Url = request.Url.FullUri;
switch (request.Method)
{
case HttpMethod.GET:
curlEasy.HttpGet = true;
break;
case HttpMethod.POST:
curlEasy.Post = true;
break;
case HttpMethod.PUT:
curlEasy.Put = true;
break;
default:
throw new NotSupportedException(string.Format("HttpCurl method {0} not supported", request.Method));
}
curlEasy.FollowLocation = false;
curlEasy.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent; ;
if (request.RequestTimeout != TimeSpan.Zero)
{
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
}
if (OsInfo.IsWindows)
{
curlEasy.CaInfo = _caBundleFilePath;
}
if (cookies != null)
{
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
}
if (request.ContentData != null)
{
curlEasy.PostFieldSize = request.ContentData.Length;
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
}
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
using (var httpRequestHeaders = SerializeHeaders(request))
{
curlEasy.HttpHeader = httpRequestHeaders;
var result = curlEasy.Perform();
if (result != CurlCode.Ok)
{ {
case CurlCode.SslCaCert: switch (result)
case (CurlCode)77: {
throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url)); case CurlCode.SslCaCert:
default: case (CurlCode)77:
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url)); throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url));
default:
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
}
} }
} }
var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream);
var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection);
var httpHeader = new HttpHeader(webHeaderCollection);
return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode);
} }
var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream);
var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection);
var httpHeader = new HttpHeader(webHeaderCollection);
return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode);
} }
} }
} }
@@ -259,6 +260,7 @@ namespace NzbDrone.Common.Http.Dispatchers
private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream, WebHeaderCollection webHeaderCollection) private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream, WebHeaderCollection webHeaderCollection)
{ {
byte[] bytes = null;
responseStream.Position = 0; responseStream.Position = 0;
if (responseStream.Length != 0) if (responseStream.Length != 0)
@@ -268,21 +270,27 @@ namespace NzbDrone.Common.Http.Dispatchers
{ {
if (encoding.IndexOf("gzip") != -1) if (encoding.IndexOf("gzip") != -1)
{ {
responseStream = new GZipStream(responseStream, CompressionMode.Decompress); using (var zipStream = new GZipStream(responseStream, CompressionMode.Decompress))
{
bytes = zipStream.ToBytes();
}
webHeaderCollection.Remove("Content-Encoding"); webHeaderCollection.Remove("Content-Encoding");
} }
else if (encoding.IndexOf("deflate") != -1) else if (encoding.IndexOf("deflate") != -1)
{ {
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); using (var deflateStream = new DeflateStream(responseStream, CompressionMode.Decompress))
{
bytes = deflateStream.ToBytes();
}
webHeaderCollection.Remove("Content-Encoding"); webHeaderCollection.Remove("Content-Encoding");
} }
} }
} }
return responseStream.ToBytes(); if (bytes == null) bytes = responseStream.ToBytes();
return bytes;
} }
} }
@@ -1,4 +1,6 @@
using System; using System;
using System.IO;
using System.IO.Compression;
using System.Net; using System.Net;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@@ -20,72 +22,106 @@ namespace NzbDrone.Common.Http.Dispatchers
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies) public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
{ {
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url); HttpWebResponse httpWebResponse = null;
HttpWebRequest webRequest = null;
// Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
webRequest.Method = request.Method.ToString();
webRequest.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent;
webRequest.KeepAlive = request.ConnectionKeepAlive;
webRequest.AllowAutoRedirect = false;
webRequest.CookieContainer = cookies;
if (request.RequestTimeout != TimeSpan.Zero)
{
webRequest.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalMilliseconds);
}
AddProxy(webRequest, request);
if (request.Headers != null)
{
AddRequestHeaders(webRequest, request.Headers);
}
if (request.ContentData != null)
{
webRequest.ContentLength = request.ContentData.Length;
using (var writeStream = webRequest.GetRequestStream())
{
writeStream.Write(request.ContentData, 0, request.ContentData.Length);
}
}
HttpWebResponse httpWebResponse;
try try
{ {
httpWebResponse = (HttpWebResponse)webRequest.GetResponse(); webRequest = (HttpWebRequest) WebRequest.Create((Uri) request.Url);
if (OsInfo.IsMonoRuntime)
{
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
webRequest.AutomaticDecompression = DecompressionMethods.None;
webRequest.Headers.Add("Accept-Encoding", "gzip");
}
else
{
// Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
}
webRequest.Method = request.Method.ToString();
webRequest.UserAgent = request.UseSimplifiedUserAgent
? UserAgentBuilder.UserAgentSimplified
: UserAgentBuilder.UserAgent;
webRequest.KeepAlive = request.ConnectionKeepAlive;
webRequest.AllowAutoRedirect = false;
webRequest.CookieContainer = cookies;
if (request.RequestTimeout != TimeSpan.Zero)
{
webRequest.Timeout = (int) Math.Ceiling(request.RequestTimeout.TotalMilliseconds);
}
AddProxy(webRequest, request);
if (request.Headers != null)
{
AddRequestHeaders(webRequest, request.Headers);
}
if (request.ContentData != null)
{
webRequest.ContentLength = request.ContentData.Length;
using (var writeStream = webRequest.GetRequestStream())
{
writeStream.Write(request.ContentData, 0, request.ContentData.Length);
}
}
try
{
httpWebResponse = (HttpWebResponse) webRequest.GetResponse();
}
catch (WebException e)
{
if (e.Status == WebExceptionStatus.SecureChannelFailure && OsInfo.IsWindows)
{
SecurityProtocolPolicy.DisableTls12();
}
httpWebResponse = (HttpWebResponse) e.Response;
if (httpWebResponse == null)
{
throw;
}
}
byte[] data = null;
using (var responseStream = httpWebResponse.GetResponseStream())
{
if (responseStream != null)
{
data = responseStream.ToBytes();
if (OsInfo.IsMonoRuntime && httpWebResponse.ContentEncoding == "gzip")
{
using (var compressedStream = new MemoryStream(data))
using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var decompressedStream = new MemoryStream())
{
gzip.CopyTo(decompressedStream);
data = decompressedStream.ToArray();
}
httpWebResponse.Headers.Remove("Content-Encoding");
}
}
}
return new HttpResponse(request, new HttpHeader(httpWebResponse.Headers), data,
httpWebResponse.StatusCode);
} }
catch (WebException e) finally
{ {
if (e.Status == WebExceptionStatus.SecureChannelFailure && OsInfo.IsWindows) webRequest = null;
{ (httpWebResponse as IDisposable)?.Dispose();
SecurityProtocolPolicy.DisableTls12(); httpWebResponse = null;
}
httpWebResponse = (HttpWebResponse)e.Response;
if (httpWebResponse == null)
{
throw;
}
} }
byte[] data = null;
using (var responseStream = httpWebResponse.GetResponseStream())
{
if (responseStream != null)
{
data = responseStream.ToBytes();
}
}
return new HttpResponse(request, new HttpHeader(httpWebResponse.Headers), data, httpWebResponse.StatusCode);
} }
protected virtual void AddProxy(HttpWebRequest webRequest, HttpRequest request) protected virtual void AddProxy(HttpWebRequest webRequest, HttpRequest request)
+7 -5
View File
@@ -226,11 +226,13 @@ namespace NzbDrone.Common.Http
_logger.Debug("Downloading [{0}] to [{1}]", url, fileName); _logger.Debug("Downloading [{0}] to [{1}]", url, fileName);
var stopWatch = Stopwatch.StartNew(); var stopWatch = Stopwatch.StartNew();
var webClient = new GZipWebClient(); using (var webClient = new GZipWebClient())
webClient.Headers.Add(HttpRequestHeader.UserAgent, UserAgentBuilder.UserAgent); {
webClient.DownloadFile(url, fileName); webClient.Headers.Add(HttpRequestHeader.UserAgent, UserAgentBuilder.UserAgent);
stopWatch.Stop(); webClient.DownloadFile(url, fileName);
_logger.Debug("Downloading Completed. took {0:0}s", stopWatch.Elapsed.Seconds); stopWatch.Stop();
_logger.Debug("Downloading Completed. took {0:0}s", stopWatch.Elapsed.Seconds);
}
} }
catch (WebException e) catch (WebException e)
{ {
+2 -2
View File
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -135,7 +135,7 @@ namespace NzbDrone.Common.Http
return new HttpUri(Scheme, Host, Port, CombinePath(Path, path), Query, Fragment); return new HttpUri(Scheme, Host, Port, CombinePath(Path, path), Query, Fragment);
} }
private static string CombinePath(string basePath, string relativePath) public static string CombinePath(string basePath, string relativePath)
{ {
if (relativePath.IsNullOrWhiteSpace()) if (relativePath.IsNullOrWhiteSpace())
{ {
@@ -44,6 +44,8 @@ namespace NzbDrone.Common.Instrumentation
return; return;
} }
} }
Console.WriteLine(exception.StackTrace);
Console.WriteLine("EPIC FAIL: {0}", exception); Console.WriteLine("EPIC FAIL: {0}", exception);
Logger.Fatal(exception, "EPIC FAIL: " + exception.Message); Logger.Fatal(exception, "EPIC FAIL: " + exception.Message);
+1 -1
View File
@@ -81,7 +81,7 @@
<Compile Include="Cache\CachedDictionary.cs" /> <Compile Include="Cache\CachedDictionary.cs" />
<Compile Include="Cache\ICached.cs" /> <Compile Include="Cache\ICached.cs" />
<Compile Include="Cache\ICachedDictionary.cs" /> <Compile Include="Cache\ICachedDictionary.cs" />
<Compile Include="Cloud\SonarrCloudRequestBuilder.cs" /> <Compile Include="Cloud\RadarrCloudRequestBuilder.cs" />
<Compile Include="Composition\Container.cs" /> <Compile Include="Composition\Container.cs" />
<Compile Include="Composition\ContainerBuilderBase.cs" /> <Compile Include="Composition\ContainerBuilderBase.cs" />
<Compile Include="Composition\IContainer.cs" /> <Compile Include="Composition\IContainer.cs" />
@@ -1,10 +1,10 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("radarr.tv")] [assembly: AssemblyCompany("radarr.video")]
[assembly: AssemblyProduct("NzbDrone")] [assembly: AssemblyProduct("Radarr")]
[assembly: AssemblyVersion("0.1.0.*")] [assembly: AssemblyVersion("10.0.0.*")]
[assembly: AssemblyCopyright("GNU General Public v3")] [assembly: AssemblyCopyright("GNU General Public v3")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -1,4 +1,4 @@
using System; using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Test.CustomFormat
[TestCase("S_WEBdL", TagType.Source, Source.WEBDL)] [TestCase("S_WEBdL", TagType.Source, Source.WEBDL)]
[TestCase("S_CAM", TagType.Source, Source.CAM)] [TestCase("S_CAM", TagType.Source, Source.CAM)]
[TestCase("L_English", TagType.Language, Language.English)] [TestCase("L_English", TagType.Language, Language.English)]
[TestCase("L_Italian", TagType.Language, Language.Italian)]
[TestCase("L_iTa", TagType.Language, Language.Italian)]
[TestCase("L_germaN", TagType.Language, Language.German)] [TestCase("L_germaN", TagType.Language, Language.German)]
[TestCase("E_Director", TagType.Edition, "director")] [TestCase("E_Director", TagType.Edition, "director")]
[TestCase("E_RX_Director('?s)?", TagType.Edition, "director('?s)?", TagModifier.Regex)] [TestCase("E_RX_Director('?s)?", TagType.Edition, "director('?s)?", TagModifier.Regex)]
@@ -19,6 +19,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
private NzbgetQueueItem _queued; private NzbgetQueueItem _queued;
private NzbgetHistoryItem _failed; private NzbgetHistoryItem _failed;
private NzbgetHistoryItem _completed; private NzbgetHistoryItem _completed;
private Dictionary<string, string> _configItems;
[SetUp] [SetUp]
public void Setup() public void Setup()
@@ -80,13 +81,17 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
DownloadRate = 7000000 DownloadRate = 7000000
}); });
var configItems = new Dictionary<string, string>(); Mocker.GetMock<INzbgetProxy>()
configItems.Add("Category1.Name", "movie"); .Setup(v => v.GetVersion(It.IsAny<NzbgetSettings>()))
configItems.Add("Category1.DestDir", @"/remote/mount/movie"); .Returns("14.0");
_configItems = new Dictionary<string, string>();
_configItems.Add("Category1.Name", "movie");
_configItems.Add("Category1.DestDir", @"/remote/mount/movie");
Mocker.GetMock<INzbgetProxy>() Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>())) .Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>()))
.Returns(configItems); .Returns(_configItems);
} }
protected void GivenFailedDownload() protected void GivenFailedDownload()
@@ -386,5 +391,18 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
error.IsValid.Should().Be(expected); error.IsValid.Should().Be(expected);
} }
[TestCase("0", false)]
[TestCase("1", true)]
[TestCase(" 7", false)]
[TestCase("5000000", false)]
public void should_test_keephistory(string keephistory, bool expected)
{
_configItems["KeepHistory"] = keephistory;
var error = Subject.Test();
error.IsValid.Should().Be(expected);
}
} }
} }
@@ -54,11 +54,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
protected void GivenSuccessfulDownload() protected void GivenSuccessfulDownload()
{ {
Mocker.GetMock<IRTorrentProxy>() Mocker.GetMock<IRTorrentProxy>()
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<RTorrentSettings>())) .Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<RTorrentSettings>()))
.Callback(PrepareClientToReturnCompletedItem); .Callback(PrepareClientToReturnCompletedItem);
Mocker.GetMock<IRTorrentProxy>() Mocker.GetMock<IRTorrentProxy>()
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<RTorrentSettings>())) .Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<RTorrentSettings>()))
.Callback(PrepareClientToReturnCompletedItem); .Callback(PrepareClientToReturnCompletedItem);
@@ -123,4 +123,4 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
} }
} }
@@ -0,0 +1,43 @@
using System.IO;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
[TestFixture]
public class NzbValidationServiceFixture : CoreTest<NzbValidationService>
{
private byte[] GivenNzbFile(string name)
{
return File.ReadAllBytes(GetTestPath("Files/Nzbs/" + name + ".nzb"));
}
[Test]
public void should_throw_on_invalid_nzb()
{
var filename = "NotNzb";
var fileContent = GivenNzbFile(filename);
Assert.Throws<InvalidNzbException>(() => Subject.Validate(filename, fileContent));
}
[Test]
public void should_throw_when_no_files()
{
var filename = "NoFiles";
var fileContent = GivenNzbFile(filename);
Assert.Throws<InvalidNzbException>(() => Subject.Validate(filename, fileContent));
}
[Test]
public void should_validate_nzb()
{
var filename = "ValidNzb";
var fileContent = GivenNzbFile(filename);
Subject.Validate(filename, fileContent);
}
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nzb
PUBLIC '-//newzBin//DTD NZB 1.1//EN'
'http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd'>
<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
</nzb>
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<fail>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[01/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.nfo&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="12053" number="1">ZQ9h749E781168561i4J0Q6-01m6Q3185@2894t-767038L.Pg7769</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[02/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.par2&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="26932" number="1">405Z5Y4066010l377VP1k6$U4873W933@f32Bs90575538201.pj54</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[03/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.r00&quot; yEnc (1/66)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793068" number="1">1x9894417$M.1s25279485O1s1Fi95Z1_18Z554u440@D1k0854_134551.0794144</segment>
<segment bytes="793234" number="2">48JYp$W18B2R1s2rI24EG7$907$r89875n60@8xK3374080716.115545M</segment>
<segment bytes="793346" number="3">0U93471uI59Y781x77Q8-4286308-4aU35$07-179z@u90567568251.4zgUW968</segment>
<segment bytes="793302" number="4">5119x6417a.s06F$1k46$2q89298-C0@G7C-7811268.bK9x00B</segment>
<segment bytes="793289" number="5">B8$1_h0b64Z14-16_O6$ESw481L421n9agj7731k@414.473581-K$4.0Zd5A</segment>
<segment bytes="793380" number="6">O-4731$tn71v05623J9GT.yc22O975111dR01r58065p@Da1G9L33q74h3095.5X240</segment>
<segment bytes="793253" number="7">d9R03J$07w75945Z556197z50F0w.0-5.x9$58311S@J0-v50033110.4a440EYJ</segment>
<segment bytes="793317" number="8">05e650149.5r1Hk$E0Bko7G5B.1107mz8l17PS8F@vr816$S6T19245w.042B9</segment>
<segment bytes="793060" number="9">245Xy0w4o$tN6428321b.n1816Q1n95bE0816Y@q-qv7E12k.F3672H.16E19</segment>
<segment bytes="793266" number="10">H681i185g64H23101kP125z41101O91P384l@E9n597k05j798D94X.2ezz1K</segment>
<segment bytes="793223" number="11">T18.6136787.HLJ806.8$Si49m0459445101Z15-5@b80M7.788598D.gXu201cR</segment>
<segment bytes="793346" number="12">Vdl8H243Go28j1o865772039416v2@090a20-v365N5S7qf.G225s6</segment>
<segment bytes="793305" number="13">S9769892v956069345.0TN.i05R@I04825Gt2706N.BAj1DT1T</segment>
<segment bytes="793187" number="14">041800q6F28q44365799m5CQ4D43895@1Bf6268z_Q20F.045JXl</segment>
<segment bytes="793119" number="15">1c-e034z4l$9K45i44218ss25$X5_5R-1i76$40-71P@Xt691t8B686Fgv.VBSl</segment>
<segment bytes="793262" number="16">76l441W.R146a5368ed02cp_44171410hT.l@Z98.k.70X9c.5mZ1w49</segment>
<segment bytes="793221" number="17">12D035G5745-KO43wZ9920ttr1338@V7d871S2-t04t8520.uQ18</segment>
<segment bytes="793221" number="18">59V4O77211HA1f5T8h1-53952zV-55294K4M04v@kS878H3g4z.B5561.L330519</segment>
<segment bytes="793087" number="19">44-yi1-79$751944J7094$y7-y49994440d86cSn@5C82v-1O9N.wk8wMb6</segment>
<segment bytes="793375" number="20">oF7Wj3$Ydh7e030oD4.e81JM464O791495lJ@Pm058Qt4-G8Wv.T1i1a6O1</segment>
<segment bytes="793293" number="21">1T7_71M9d10F2.5953VP.11.4h75L@5049bBn384.14Ms</segment>
<segment bytes="793266" number="22">u8601765028G662749SD41j0m57651Zq70u1@J5281423406375.z.6PDSx57</segment>
<segment bytes="793376" number="23">XY0476$R87Y16g2n45OO335541589V140R026j@y2q9296x7f23C.sqK71b9</segment>
<segment bytes="793363" number="24">X7N3440l08B9T5940na4Ls397-T2.P5M12241525J57@r44O419p594M6G4I.d66RQ1</segment>
<segment bytes="793203" number="25">p4148978k45.t88w2K9886H4223y5553T7$7p287TN@N8e1T98b_0.mo55a14G</segment>
<segment bytes="793039" number="26">50U0a9iP07$A66010-51h55w386f@c$42$S96V57F5u0Y.6UDV35D</segment>
<segment bytes="792999" number="27">FnKN4n2749v958xa36J2570506414D293S@8H1A1X490$z3bv.ut6KQ4N</segment>
<segment bytes="793316" number="28">q4$d0$X8x6rm85m0Ewh307m255N@t2C7484zq870u.1RLndQ</segment>
<segment bytes="793238" number="29">364U4342$5I242404oH90-1W3c0t16705057m650Cq9f@K32rE5297347130W.UNs8evbH</segment>
<segment bytes="793461" number="30">M3081U097-r06Y.yy9-1A538001B27f@L2834Y80c7b1075.Dy150</segment>
<segment bytes="793326" number="31">189585554.NS66E5D840N4Yq5m07NC1n@51L0393057L528n.k1Mc3j0S</segment>
<segment bytes="793380" number="32">189048V505q89216C149I5f$53x-T@0V9i8n7o95.I.Z1lBJ5</segment>
<segment bytes="793205" number="33">5-L.555$139r45100-S23-59859@54844694q2.3EY9b</segment>
<segment bytes="793258" number="34">641655313y0.Z002L0g39AZ11716U-uX015PI5.v6y@veS44H89Js91903K8.P3MAvk4k</segment>
<segment bytes="793229" number="35">1C8f-yz-U-b20.610.0P1M-6Z5418i229160865010s1@M7l210D48Nc.nB0sPmi</segment>
<segment bytes="793169" number="36">0653$L0.58749-1U_1PS95-1h9gQ145@0117y0-1x1p-h94.za18yc5</segment>
<segment bytes="793109" number="37">77-Mo3-a6514904987865.K0W710G4HB9237@501F7910J6j50-Bh.6cHx1</segment>
<segment bytes="793350" number="38">m4I47082655rz$b7P751u9W679475F.89p@f.o.XZv5O7y.855rgXX</segment>
<segment bytes="793193" number="39">f075$y56E57d.t11787.0$6D155735M_w89-Y57q2@x0t5H91021wZ52Vh.1h7vabU</segment>
<segment bytes="793251" number="40">H7U1331Ad7718$Y69T-q3w4$l247HV49s985J@vi800i0004p.YD5oK</segment>
<segment bytes="793106" number="41">9nr786955Ker.M583315CoJ1-W65a817-704@IN-wU12$M1E0g466.5sMJ3</segment>
<segment bytes="793006" number="42">0.3R9mN.n2_V086N0-4.Z5gAgZo@ey3G316U382o537.f51Ed5B</segment>
<segment bytes="793198" number="43">l106Z1-N411r7j44197l628r.b5Uwc55@k4-Cl_n5xc.1B.xZbNm</segment>
<segment bytes="793070" number="44">A91LT1X591x81.TI4130N$555A57q0@L70-p5qa50.40GB</segment>
<segment bytes="793457" number="45">V5$765JR6503w0-K63099R615736843G$Qj0ev@mz776wM86445N0.4I56ne</segment>
<segment bytes="793109" number="46">A86H2P415S689$568152-025O45V@s079644915.Dd57p0</segment>
<segment bytes="793169" number="47">31x5o36q14y9554L42882X0Q10e360Z64W4K9Onx38D@5g1509788414q.Y8wib</segment>
<segment bytes="793219" number="48">b$6795157EX1044V964e14-Y9E68614O94C@4061937876$f5.6.19tV</segment>
<segment bytes="793349" number="49">D00v8X$b80m93181273J-g076Qj2p79867v5d9689Rb2@r0592.v900.j43E050E</segment>
<segment bytes="793228" number="50">Tf78L4e535.o86PK0S.M2R3-66012814z@q-5j89Y29J214Y902.53Ra0f</segment>
<segment bytes="793353" number="51">7i01.23411-lQW0212-Er260e9.N5e256jx243EX@91-T.15v40K5Hj.Fo1f</segment>
<segment bytes="793290" number="52">3A$H7m63$i595.4713vv0A4$A7Lk7Jsq@0cM0Tw4107f.B520.q5Z91</segment>
<segment bytes="793364" number="53">j572m$3h87LS$37167Wp10k41541.T779-Fn@V53C11045619xJ.52.0PnnX4v5</segment>
<segment bytes="793506" number="54">A.2d4599a720rk2IB32h0X523MjTL415v89706-7Z45y@R4746-B106358.t3g62r4</segment>
<segment bytes="793118" number="55">5q6100961jM-G9F7t755x366zxc102M1SdMF@7394521p651X1I.AL05545a</segment>
<segment bytes="793137" number="56">04e851111$12u2213-80VR133125B@7x8865M4hQ9$5.1N345x</segment>
<segment bytes="793180" number="57">K2476D3600-73B4W363$008s888980421f27125V$q0@0Zc0a56-m7550.1637vAr1</segment>
<segment bytes="793214" number="58">0306u425024v448ZeCE3Q9825m9th1858@5648018-H0.2k7J4.12k0B</segment>
<segment bytes="793274" number="59">220u4SK433564Cr2l004t0wP888545779g@19j360863S$55559m.70V7Ndr</segment>
<segment bytes="793339" number="60">5u1q051C5Qq8Z9Iy$Z.5.1510NY.S2565n@7m.5-09$z235p74.8kW5</segment>
<segment bytes="793297" number="61">6F472C8nh2621_X0C1093P7n39643b5p2f76s60r@1T55203qQY6.wZml1Vb</segment>
<segment bytes="793351" number="62">5qC4568844767324-o8i05983-0f.n4.y.OBZ41f@q36B50684KU66.0R1784</segment>
<segment bytes="793257" number="63">4P0g470-F59307aDf.JF070Xx959648dO3y00463J6s@71P$D961$C0.11.I096sQ</segment>
<segment bytes="793277" number="64">z5kod75077z01w11-A5h.wiG550.J5-p756$81.Db@5l01K49h3K.Ok4R5512</segment>
<segment bytes="793292" number="65">F3JX28.B8h90T0075-08001X5w611V071@D75X9263$6$9f.OT050p5Z</segment>
<segment bytes="83545" number="66">2B8sT.A650z101514671183y47977219.M4211xYp@0b0021p736BX92.B0lSm4J3</segment>
</segments>
</file>
<file date="1504571157" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[46/46] - &quot;sample-Series.Title.s04e06.720p.hdtv.x264-killers.vol31+04.par2&quot; yEnc (1/3)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793316" number="1">16ND-8I545Pq-s107t0h07g8908870711@K401476783.5.0mFs1</segment>
<segment bytes="793409" number="2">iYdZ2D11089F310711.ci-O7O4KG03@260c03388O84Kd.GCEgv</segment>
<segment bytes="6784" number="3">r63cDD59Mg1c95738Sn75085O4X7823V1@16V6-b87O21S1937O.lw17o1VS</segment>
</segments>
</file>
</fail>
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nzb
PUBLIC '-//newzBin//DTD NZB 1.1//EN'
'http://www.newzbin.com/DTD/nzb/nzb-1.1.dtd'>
<nzb xmlns="http://www.newzbin.com/DTD/2003/nzb">
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[01/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.nfo&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="12053" number="1">ZQ9h749E781168561i4J0Q6-01m6Q3185@2894t-767038L.Pg7769</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[02/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.par2&quot; yEnc (1/1)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="26932" number="1">405Z5Y4066010l377VP1k6$U4873W933@f32Bs90575538201.pj54</segment>
</segments>
</file>
<file date="1504571104" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[03/46] - &quot;Series.Title.s04e06.720p.hdtv.x264-killers.r00&quot; yEnc (1/66)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793068" number="1">1x9894417$M.1s25279485O1s1Fi95Z1_18Z554u440@D1k0854_134551.0794144</segment>
<segment bytes="793234" number="2">48JYp$W18B2R1s2rI24EG7$907$r89875n60@8xK3374080716.115545M</segment>
<segment bytes="793346" number="3">0U93471uI59Y781x77Q8-4286308-4aU35$07-179z@u90567568251.4zgUW968</segment>
<segment bytes="793302" number="4">5119x6417a.s06F$1k46$2q89298-C0@G7C-7811268.bK9x00B</segment>
<segment bytes="793289" number="5">B8$1_h0b64Z14-16_O6$ESw481L421n9agj7731k@414.473581-K$4.0Zd5A</segment>
<segment bytes="793380" number="6">O-4731$tn71v05623J9GT.yc22O975111dR01r58065p@Da1G9L33q74h3095.5X240</segment>
<segment bytes="793253" number="7">d9R03J$07w75945Z556197z50F0w.0-5.x9$58311S@J0-v50033110.4a440EYJ</segment>
<segment bytes="793317" number="8">05e650149.5r1Hk$E0Bko7G5B.1107mz8l17PS8F@vr816$S6T19245w.042B9</segment>
<segment bytes="793060" number="9">245Xy0w4o$tN6428321b.n1816Q1n95bE0816Y@q-qv7E12k.F3672H.16E19</segment>
<segment bytes="793266" number="10">H681i185g64H23101kP125z41101O91P384l@E9n597k05j798D94X.2ezz1K</segment>
<segment bytes="793223" number="11">T18.6136787.HLJ806.8$Si49m0459445101Z15-5@b80M7.788598D.gXu201cR</segment>
<segment bytes="793346" number="12">Vdl8H243Go28j1o865772039416v2@090a20-v365N5S7qf.G225s6</segment>
<segment bytes="793305" number="13">S9769892v956069345.0TN.i05R@I04825Gt2706N.BAj1DT1T</segment>
<segment bytes="793187" number="14">041800q6F28q44365799m5CQ4D43895@1Bf6268z_Q20F.045JXl</segment>
<segment bytes="793119" number="15">1c-e034z4l$9K45i44218ss25$X5_5R-1i76$40-71P@Xt691t8B686Fgv.VBSl</segment>
<segment bytes="793262" number="16">76l441W.R146a5368ed02cp_44171410hT.l@Z98.k.70X9c.5mZ1w49</segment>
<segment bytes="793221" number="17">12D035G5745-KO43wZ9920ttr1338@V7d871S2-t04t8520.uQ18</segment>
<segment bytes="793221" number="18">59V4O77211HA1f5T8h1-53952zV-55294K4M04v@kS878H3g4z.B5561.L330519</segment>
<segment bytes="793087" number="19">44-yi1-79$751944J7094$y7-y49994440d86cSn@5C82v-1O9N.wk8wMb6</segment>
<segment bytes="793375" number="20">oF7Wj3$Ydh7e030oD4.e81JM464O791495lJ@Pm058Qt4-G8Wv.T1i1a6O1</segment>
<segment bytes="793293" number="21">1T7_71M9d10F2.5953VP.11.4h75L@5049bBn384.14Ms</segment>
<segment bytes="793266" number="22">u8601765028G662749SD41j0m57651Zq70u1@J5281423406375.z.6PDSx57</segment>
<segment bytes="793376" number="23">XY0476$R87Y16g2n45OO335541589V140R026j@y2q9296x7f23C.sqK71b9</segment>
<segment bytes="793363" number="24">X7N3440l08B9T5940na4Ls397-T2.P5M12241525J57@r44O419p594M6G4I.d66RQ1</segment>
<segment bytes="793203" number="25">p4148978k45.t88w2K9886H4223y5553T7$7p287TN@N8e1T98b_0.mo55a14G</segment>
<segment bytes="793039" number="26">50U0a9iP07$A66010-51h55w386f@c$42$S96V57F5u0Y.6UDV35D</segment>
<segment bytes="792999" number="27">FnKN4n2749v958xa36J2570506414D293S@8H1A1X490$z3bv.ut6KQ4N</segment>
<segment bytes="793316" number="28">q4$d0$X8x6rm85m0Ewh307m255N@t2C7484zq870u.1RLndQ</segment>
<segment bytes="793238" number="29">364U4342$5I242404oH90-1W3c0t16705057m650Cq9f@K32rE5297347130W.UNs8evbH</segment>
<segment bytes="793461" number="30">M3081U097-r06Y.yy9-1A538001B27f@L2834Y80c7b1075.Dy150</segment>
<segment bytes="793326" number="31">189585554.NS66E5D840N4Yq5m07NC1n@51L0393057L528n.k1Mc3j0S</segment>
<segment bytes="793380" number="32">189048V505q89216C149I5f$53x-T@0V9i8n7o95.I.Z1lBJ5</segment>
<segment bytes="793205" number="33">5-L.555$139r45100-S23-59859@54844694q2.3EY9b</segment>
<segment bytes="793258" number="34">641655313y0.Z002L0g39AZ11716U-uX015PI5.v6y@veS44H89Js91903K8.P3MAvk4k</segment>
<segment bytes="793229" number="35">1C8f-yz-U-b20.610.0P1M-6Z5418i229160865010s1@M7l210D48Nc.nB0sPmi</segment>
<segment bytes="793169" number="36">0653$L0.58749-1U_1PS95-1h9gQ145@0117y0-1x1p-h94.za18yc5</segment>
<segment bytes="793109" number="37">77-Mo3-a6514904987865.K0W710G4HB9237@501F7910J6j50-Bh.6cHx1</segment>
<segment bytes="793350" number="38">m4I47082655rz$b7P751u9W679475F.89p@f.o.XZv5O7y.855rgXX</segment>
<segment bytes="793193" number="39">f075$y56E57d.t11787.0$6D155735M_w89-Y57q2@x0t5H91021wZ52Vh.1h7vabU</segment>
<segment bytes="793251" number="40">H7U1331Ad7718$Y69T-q3w4$l247HV49s985J@vi800i0004p.YD5oK</segment>
<segment bytes="793106" number="41">9nr786955Ker.M583315CoJ1-W65a817-704@IN-wU12$M1E0g466.5sMJ3</segment>
<segment bytes="793006" number="42">0.3R9mN.n2_V086N0-4.Z5gAgZo@ey3G316U382o537.f51Ed5B</segment>
<segment bytes="793198" number="43">l106Z1-N411r7j44197l628r.b5Uwc55@k4-Cl_n5xc.1B.xZbNm</segment>
<segment bytes="793070" number="44">A91LT1X591x81.TI4130N$555A57q0@L70-p5qa50.40GB</segment>
<segment bytes="793457" number="45">V5$765JR6503w0-K63099R615736843G$Qj0ev@mz776wM86445N0.4I56ne</segment>
<segment bytes="793109" number="46">A86H2P415S689$568152-025O45V@s079644915.Dd57p0</segment>
<segment bytes="793169" number="47">31x5o36q14y9554L42882X0Q10e360Z64W4K9Onx38D@5g1509788414q.Y8wib</segment>
<segment bytes="793219" number="48">b$6795157EX1044V964e14-Y9E68614O94C@4061937876$f5.6.19tV</segment>
<segment bytes="793349" number="49">D00v8X$b80m93181273J-g076Qj2p79867v5d9689Rb2@r0592.v900.j43E050E</segment>
<segment bytes="793228" number="50">Tf78L4e535.o86PK0S.M2R3-66012814z@q-5j89Y29J214Y902.53Ra0f</segment>
<segment bytes="793353" number="51">7i01.23411-lQW0212-Er260e9.N5e256jx243EX@91-T.15v40K5Hj.Fo1f</segment>
<segment bytes="793290" number="52">3A$H7m63$i595.4713vv0A4$A7Lk7Jsq@0cM0Tw4107f.B520.q5Z91</segment>
<segment bytes="793364" number="53">j572m$3h87LS$37167Wp10k41541.T779-Fn@V53C11045619xJ.52.0PnnX4v5</segment>
<segment bytes="793506" number="54">A.2d4599a720rk2IB32h0X523MjTL415v89706-7Z45y@R4746-B106358.t3g62r4</segment>
<segment bytes="793118" number="55">5q6100961jM-G9F7t755x366zxc102M1SdMF@7394521p651X1I.AL05545a</segment>
<segment bytes="793137" number="56">04e851111$12u2213-80VR133125B@7x8865M4hQ9$5.1N345x</segment>
<segment bytes="793180" number="57">K2476D3600-73B4W363$008s888980421f27125V$q0@0Zc0a56-m7550.1637vAr1</segment>
<segment bytes="793214" number="58">0306u425024v448ZeCE3Q9825m9th1858@5648018-H0.2k7J4.12k0B</segment>
<segment bytes="793274" number="59">220u4SK433564Cr2l004t0wP888545779g@19j360863S$55559m.70V7Ndr</segment>
<segment bytes="793339" number="60">5u1q051C5Qq8Z9Iy$Z.5.1510NY.S2565n@7m.5-09$z235p74.8kW5</segment>
<segment bytes="793297" number="61">6F472C8nh2621_X0C1093P7n39643b5p2f76s60r@1T55203qQY6.wZml1Vb</segment>
<segment bytes="793351" number="62">5qC4568844767324-o8i05983-0f.n4.y.OBZ41f@q36B50684KU66.0R1784</segment>
<segment bytes="793257" number="63">4P0g470-F59307aDf.JF070Xx959648dO3y00463J6s@71P$D961$C0.11.I096sQ</segment>
<segment bytes="793277" number="64">z5kod75077z01w11-A5h.wiG550.J5-p756$81.Db@5l01K49h3K.Ok4R5512</segment>
<segment bytes="793292" number="65">F3JX28.B8h90T0075-08001X5w611V071@D75X9263$6$9f.OT050p5Z</segment>
<segment bytes="83545" number="66">2B8sT.A650z101514671183y47977219.M4211xYp@0b0021p736BX92.B0lSm4J3</segment>
</segments>
</file>
<file date="1504571157" poster="autom@gical.tv (M@GiC)" subject="[432278]-[FULL]-[#a.b.teevee@EFNet]-[ Series.Title.S04E06.720p.HDTV.x264-KILLERS ]-[46/46] - &quot;sample-Series.Title.s04e06.720p.hdtv.x264-killers.vol31+04.par2&quot; yEnc (1/3)">
<groups>
<group>alt.binaries.teevee</group>
</groups>
<segments>
<segment bytes="793316" number="1">16ND-8I545Pq-s107t0h07g8908870711@K401476783.5.0mFs1</segment>
<segment bytes="793409" number="2">iYdZ2D11089F310711.ci-O7O4KG03@260c03388O84Kd.GCEgv</segment>
<segment bytes="6784" number="3">r63cDD59Mg1c95738Sn75085O4X7823V1@16V6-b87O21S1937O.lw17o1VS</segment>
</segments>
</file>
</nzb>
+1 -1
View File
@@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Framework
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<NLog.Logger>())); Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<NLog.Logger>()));
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger)); Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), TestLogger)); Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), TestLogger));
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder()); Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
} }
//Used for tests that rely on parsing working correctly. //Used for tests that rely on parsing working correctly.
@@ -1,4 +1,4 @@
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
@@ -24,6 +24,64 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
}; };
} }
private void GivenOldFeedFormat()
{
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/torrents/rss?u=snip;tp=snip;3;80;93;37;download" }
};
}
private void GivenNewFeedFormat()
{
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/t.rss?u=USERID;tp=APIKEY;3;80;93;37;download" }
};
}
private void GivenFeedNoDownloadFormat()
{
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { BaseUrl = "https://iptorrents.com/t.rss?u=USERID;tp=APIKEY;3;80;93;37" }
};
}
[Test]
public void should_validate_old_feed_format()
{
GivenOldFeedFormat();
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeTrue();
}
[Test]
public void should_validate_new_feed_format()
{
GivenNewFeedFormat();
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeTrue();
}
[Test]
public void should_not_validate_bad_format()
{
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeFalse();
}
[Test]
public void should_not_validate_no_download_format()
{
GivenFeedNoDownloadFormat();
var validationResult = Subject.Definition.Settings.Validate();
validationResult.IsValid.Should().BeFalse();
}
[Test] [Test]
public void should_parse_recent_feed_from_IPTorrents() public void should_parse_recent_feed_from_IPTorrents()
{ {
@@ -37,15 +37,6 @@ namespace NzbDrone.Core.Test.MediaCoverTests
} }
private void GivenImageFileCorrupt(bool corrupt)
{
GivenFileExistsOnDisk();
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.IsValidGDIPlusImage(It.IsAny<string>()))
.Returns(!corrupt);
}
[Test] [Test]
public void should_return_false_if_file_not_exists() public void should_return_false_if_file_not_exists()
{ {
@@ -61,21 +52,11 @@ namespace NzbDrone.Core.Test.MediaCoverTests
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse(); Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse();
} }
[Test]
public void should_return_false_if_file_exists_and_same_size_and_corrupt()
{
GivenExistingFileSize(100);
GivenImageFileCorrupt(true);
_httpResponse.Headers.ContentLength = 100;
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse();
}
[Test] [Test]
public void should_return_true_if_file_exists_and_same_size_and_not_corrupt() public void should_return_true_if_file_exists_and_same_size_and_not_corrupt()
{ {
GivenExistingFileSize(100); GivenExistingFileSize(100);
GivenImageFileCorrupt(false);
_httpResponse.Headers.ContentLength = 100; _httpResponse.Headers.ContentLength = 100;
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeTrue(); Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeTrue();
} }
@@ -8,6 +8,7 @@ using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Events; using NzbDrone.Core.Movies.Events;
@@ -28,6 +29,18 @@ namespace NzbDrone.Core.Test.MediaCoverTests
.With(v => v.Id = 2) .With(v => v.Id = 2)
.With(v => v.Images = new List<MediaCover.MediaCover> { new MediaCover.MediaCover(MediaCoverTypes.Poster, "") }) .With(v => v.Images = new List<MediaCover.MediaCover> { new MediaCover.MediaCover(MediaCoverTypes.Poster, "") })
.Build(); .Build();
Mocker.GetMock<IMovieService>().Setup(m => m.GetMovie(It.Is<int>(id => id == _movie.Id))).Returns(_movie);
}
private void ExecuteAndVerifyCommand(Movie movie)
{
Subject.HandleAsync(new MovieUpdatedEvent(movie));
Mocker.GetMock<IManageCommandQueue>()
.Verify(v => v.Push(It.Is<EnsureMediaCoversCommand>(c => c.MovieId == movie.Id), It.IsAny<CommandPriority>(), It.IsAny<CommandTrigger>()), Times.Once());
Subject.Execute(new EnsureMediaCoversCommand(movie.Id));
} }
[Test] [Test]
@@ -76,7 +89,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
.Setup(v => v.FileExists(It.IsAny<string>())) .Setup(v => v.FileExists(It.IsAny<string>()))
.Returns(true); .Returns(true);
Subject.HandleAsync(new MovieUpdatedEvent(_movie)); ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>() Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2)); .Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@@ -93,7 +106,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
.Setup(v => v.FileExists(It.IsAny<string>())) .Setup(v => v.FileExists(It.IsAny<string>()))
.Returns(false); .Returns(false);
Subject.HandleAsync(new MovieUpdatedEvent(_movie)); ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>() Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2)); .Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@@ -114,7 +127,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
.Setup(v => v.GetFileSize(It.IsAny<string>())) .Setup(v => v.GetFileSize(It.IsAny<string>()))
.Returns(1000); .Returns(1000);
Subject.HandleAsync(new MovieUpdatedEvent(_movie)); ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>() Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Never()); .Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Never());
@@ -135,7 +148,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
.Setup(v => v.GetFileSize(It.IsAny<string>())) .Setup(v => v.GetFileSize(It.IsAny<string>()))
.Returns(0); .Returns(0);
Subject.HandleAsync(new MovieUpdatedEvent(_movie)); ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>() Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2)); .Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@@ -156,7 +169,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
.Setup(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>())) .Setup(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()))
.Throws<ApplicationException>(); .Throws<ApplicationException>();
Subject.HandleAsync(new MovieUpdatedEvent(_movie)); ExecuteAndVerifyCommand(_movie);
Mocker.GetMock<IImageResizer>() Mocker.GetMock<IImageResizer>()
.Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2)); .Verify(v => v.Resize(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(2));
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
info.AudioBitrate.Should().Be(128000); info.AudioBitrate.Should().Be(128000);
info.AudioChannels.Should().Be(2); info.AudioChannels.Should().Be(2);
info.AudioLanguages.Should().Be("English"); info.AudioLanguages.Should().Be("English");
info.AudioAdditionalFeatures.Should().Be(""); info.AudioAdditionalFeatures.Should().Be("LC");
info.Height.Should().Be(320); info.Height.Should().Be(320);
info.RunTime.Seconds.Should().Be(10); info.RunTime.Seconds.Should().Be(10);
info.ScanType.Should().Be("Progressive"); info.ScanType.Should().Be("Progressive");
@@ -90,7 +90,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
info.AudioBitrate.Should().Be(128000); info.AudioBitrate.Should().Be(128000);
info.AudioChannels.Should().Be(2); info.AudioChannels.Should().Be(2);
info.AudioLanguages.Should().Be("English"); info.AudioLanguages.Should().Be("English");
info.AudioAdditionalFeatures.Should().Be(""); info.AudioAdditionalFeatures.Should().Be("LC");
info.Height.Should().Be(320); info.Height.Should().Be(320);
info.RunTime.Seconds.Should().Be(10); info.RunTime.Seconds.Should().Be(10);
info.ScanType.Should().Be("Progressive"); info.ScanType.Should().Be("Progressive");
@@ -195,6 +195,7 @@
<Compile Include="Download\DownloadClientTests\DownloadStationTests\UsenetDownloadStationFixture.cs" /> <Compile Include="Download\DownloadClientTests\DownloadStationTests\UsenetDownloadStationFixture.cs" />
<Compile Include="Download\DownloadServiceFixture.cs" /> <Compile Include="Download\DownloadServiceFixture.cs" />
<Compile Include="Download\FailedDownloadServiceFixture.cs" /> <Compile Include="Download\FailedDownloadServiceFixture.cs" />
<Compile Include="Download\NzbValidationServiceFixture.cs" />
<Compile Include="Download\Pending\PendingReleaseServiceTests\PendingReleaseServiceFixture.cs" /> <Compile Include="Download\Pending\PendingReleaseServiceTests\PendingReleaseServiceFixture.cs" />
<Compile Include="Download\Pending\PendingReleaseServiceTests\RemovePendingFixture.cs" /> <Compile Include="Download\Pending\PendingReleaseServiceTests\RemovePendingFixture.cs" />
<Compile Include="Download\Pending\PendingReleaseServiceTests\RemoveRejectedFixture.cs" /> <Compile Include="Download\Pending\PendingReleaseServiceTests\RemoveRejectedFixture.cs" />
@@ -553,6 +554,15 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Files\Nzbs\NoFiles.nzb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\Nzbs\NotNzb.nzb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\Nzbs\ValidNzb.nzb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Files\TestArchive.tar.gz"> <None Include="Files\TestArchive.tar.gz">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
@@ -584,4 +594,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>
@@ -49,6 +49,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The Danish Girl 2015", Language.English)] [TestCase("The Danish Girl 2015", Language.English)]
[TestCase("Nocturnal Animals (2016) MULTi VFQ English [1080p] BluRay x264-PopHD", Language.English, Language.French)] [TestCase("Nocturnal Animals (2016) MULTi VFQ English [1080p] BluRay x264-PopHD", Language.English, Language.French)]
[TestCase("Wonder.Woman.2017.720p.BluRay.DD5.1.x264-TayTO.CZ-FTU", Language.Czech)] [TestCase("Wonder.Woman.2017.720p.BluRay.DD5.1.x264-TayTO.CZ-FTU", Language.Czech)]
[TestCase("Fantastic.Beasts.The.Crimes.Of.Grindelwald.2018.2160p.WEBRip.x265.10bit.HDR.DD5.1-GASMASK", Language.English)]
public void should_parse_language(string postTitle, params Language[] languages) public void should_parse_language(string postTitle, params Language[] languages)
{ {
var movieInfo = Parser.Parser.ParseMovieTitle(postTitle, true); var movieInfo = Parser.Parser.ParseMovieTitle(postTitle, true);
@@ -65,6 +66,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("2 Broke Girls - S01E01 - Pilot.en.sub", Language.English)] [TestCase("2 Broke Girls - S01E01 - Pilot.en.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot.eng.sub", Language.English)] [TestCase("2 Broke Girls - S01E01 - Pilot.eng.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot.sub", Language.Unknown)] [TestCase("2 Broke Girls - S01E01 - Pilot.sub", Language.Unknown)]
[TestCase("2 Broke Girls - S01E01 - Pilot.eng.forced.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot-eng-forced.sub", Language.English)]
public void should_parse_subtitle_language(string fileName, Language language) public void should_parse_subtitle_language(string fileName, Language language)
{ {
var result = LanguageParser.ParseSubtitleLanguage(fileName); var result = LanguageParser.ParseSubtitleLanguage(fileName);
@@ -25,6 +25,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("test/test", "testtest")] [TestCase("test/test", "testtest")]
[TestCase("90210", "90210")] [TestCase("90210", "90210")]
[TestCase("24", "24")] [TestCase("24", "24")]
[TestCase("I'm a cyborg, but that's OK", "imcyborgbutthatsok")]
[TestCase("Im a cyborg, but thats ok", "imcyborgbutthatsok")]
public void should_remove_special_characters_and_casing(string dirty, string clean) public void should_remove_special_characters_and_casing(string dirty, string clean)
{ {
var result = dirty.CleanSeriesTitle(); var result = dirty.CleanSeriesTitle();
@@ -43,7 +43,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("To.Live.and.Die.in.L.A.1985.1080p.BluRay", "To Live and Die in L.A.")] [TestCase("To.Live.and.Die.in.L.A.1985.1080p.BluRay", "To Live and Die in L.A.")]
[TestCase("A.I.Artificial.Intelligence.(2001)", "A.I. Artificial Intelligence")] [TestCase("A.I.Artificial.Intelligence.(2001)", "A.I. Artificial Intelligence")]
[TestCase("A.Movie.Name.(1998)", "A Movie Name")] [TestCase("A.Movie.Name.(1998)", "A Movie Name")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")] [TestCase("www.Torrenting.com - Revenge.2008.720p.X264-DIMENSION", "Revenge")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
[TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")] [TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Soldat James")] [TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Soldat James")]
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", "Passengers")] [TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", "Passengers")]
@@ -52,7 +53,10 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Mission Impossible: Rogue Nation (2015)[XviD - Ita Ac3 - SoftSub Ita]azione, spionaggio, thriller *Prima Visione* Team mulnic Tom Cruise", "Mission Impossible Rogue Nation")] [TestCase("Mission Impossible: Rogue Nation (2015)[XviD - Ita Ac3 - SoftSub Ita]azione, spionaggio, thriller *Prima Visione* Team mulnic Tom Cruise", "Mission Impossible Rogue Nation")]
[TestCase("Scary.Movie.2000.FRENCH..BluRay.-AiRLiNE", "Scary Movie")] [TestCase("Scary.Movie.2000.FRENCH..BluRay.-AiRLiNE", "Scary Movie")]
[TestCase("My Movie 1999 German Bluray", "My Movie")] [TestCase("My Movie 1999 German Bluray", "My Movie")]
public void should_parse_movie_title(string postTitle, string title) [TestCase("Leaving Jeruselem by Railway (1897) [DVD].mp4", "Leaving Jeruselem by Railway")]
[TestCase("Climax.2018.1080p.AMZN.WEB-DL.DD5.1.H.264-NTG", "Climax")]
[TestCase("Movie.Title.Imax.2018.1080p.AMZN.WEB-DL.DD5.1.H.264-NTG", "Movie Title")]
public void should_parse_movie_title(string postTitle, string title)
{ {
Parser.Parser.ParseMovieTitle(postTitle, true).MovieTitle.Should().Be(title); Parser.Parser.ParseMovieTitle(postTitle, true).MovieTitle.Should().Be(title);
} }
@@ -66,6 +70,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)] [TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", 2016)] [TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", 2016)]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)] [TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)]
[TestCase("Leaving Jeruselem by Railway (1897) [DVD].mp4", 1897)]
public void should_parse_movie_year(string postTitle, int year) public void should_parse_movie_year(string postTitle, int year)
{ {
Parser.Parser.ParseMovieTitle(postTitle, false).Year.Should().Be(year); Parser.Parser.ParseMovieTitle(postTitle, false).Year.Should().Be(year);
@@ -108,6 +113,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("My.Movie.GERMAN.Extended.Cut.2016", "Extended Cut")] [TestCase("My.Movie.GERMAN.Extended.Cut.2016", "Extended Cut")]
[TestCase("My.Movie.GERMAN.Extended.Cut", "Extended Cut")] [TestCase("My.Movie.GERMAN.Extended.Cut", "Extended Cut")]
[TestCase("Mission Impossible: Rogue Nation 2012 Bluray", "")] [TestCase("Mission Impossible: Rogue Nation 2012 Bluray", "")]
[TestCase("Loving.Pablo.2018.TS.FRENCH.MD.x264-DROGUERiE","")]
public void should_parse_edition(string postTitle, string edition) public void should_parse_edition(string postTitle, string edition)
{ {
var parsed = Parser.Parser.ParseMovieTitle(postTitle, true); var parsed = Parser.Parser.ParseMovieTitle(postTitle, true);
@@ -207,6 +207,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[Elysium]Lucky.Star.01(BD.720p.AAC.DA)[0BB96AD8].mkv", false)] [TestCase("[Elysium]Lucky.Star.01(BD.720p.AAC.DA)[0BB96AD8].mkv", false)]
[TestCase("Battlestar.Galactica.S01E01.33.720p.HDDVD.x264-SiNNERS.mkv", false)] [TestCase("Battlestar.Galactica.S01E01.33.720p.HDDVD.x264-SiNNERS.mkv", false)]
[TestCase("The.Expanse.S01E07.RERIP.720p.BluRay.x264-DEMAND", true)] [TestCase("The.Expanse.S01E07.RERIP.720p.BluRay.x264-DEMAND", true)]
[TestCase("John.Carpenter.Live.Retrospective.2016.2018.720p.MBluRay.x264-CRUELTY.mkv", false)]
[TestCase("Heart.Live.In.Atlantic.City.2019.720p.MBLURAY.x264-MBLURAYFANS.mkv", false)]
[TestCase("Opeth.Garden.Of.The.Titans.Live.At.Red.Rocks.Amphitheatre.2017.720p.MBluRay.x264-TREBLE.mkv", false)]
public void should_parse_bluray720p_quality(string title, bool proper) public void should_parse_bluray720p_quality(string title, bool proper)
{ {
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R720P); ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R720P);
@@ -221,6 +224,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[Zurako] Log Horizon - 01 - The Apocalypse (BD 1080p AAC) [7AE12174].mkv", false)] [TestCase("[Zurako] Log Horizon - 01 - The Apocalypse (BD 1080p AAC) [7AE12174].mkv", false)]
[TestCase("WEEDS.S03E01-06.DUAL.1080p.Blu-ray.AC3.-HELLYWOOD.avi", false)] [TestCase("WEEDS.S03E01-06.DUAL.1080p.Blu-ray.AC3.-HELLYWOOD.avi", false)]
[TestCase("[Coalgirls]_Durarara!!_01_(1920x1080_Blu-ray_FLAC)_[8370CB8F].mkv", false)] [TestCase("[Coalgirls]_Durarara!!_01_(1920x1080_Blu-ray_FLAC)_[8370CB8F].mkv", false)]
[TestCase("John.Carpenter.Live.Retrospective.2016.2018.1080p.MBluRay.x264-CRUELTY.mkv", false)]
[TestCase("Heart.Live.In.Atlantic.City.2019.1080p.MBLURAY.x264-MBLURAYFANS.mkv", false)]
[TestCase("Opeth.Garden.Of.The.Titans.Live.At.Red.Rocks.Amphitheatre.2017.1080p.MBluRay.x264-TREBLE.mkv", false)]
public void should_parse_bluray1080p_quality(string title, bool proper) public void should_parse_bluray1080p_quality(string title, bool proper)
{ {
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R1080P); ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R1080P);
@@ -26,6 +26,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[ www.Torrenting.com ] - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "DIMENSION")] [TestCase("[ www.Torrenting.com ] - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "DIMENSION")]
[TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "2HD")] [TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "2HD")]
[TestCase("7s-atlantis-s02e01-720p.mkv", null)] [TestCase("7s-atlantis-s02e01-720p.mkv", null)]
[TestCase("The.Middle.720p.HEVC.x265-MeGusta-Pre", "MeGusta")]
[TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-postbot", "NTb")]
[TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-xpost", "NTb")]
//[TestCase("", "")] //[TestCase("", "")]
public void should_parse_release_group(string title, string expected) public void should_parse_release_group(string title, string expected)
{ {
@@ -20,12 +20,12 @@ namespace NzbDrone.Core.Authentication
public User FindUser(string username) public User FindUser(string username)
{ {
return Query.Where(u => u.Username == username).SingleOrDefault(); return Query(q => q.Where(u => u.Username == username).SingleOrDefault());
} }
public User FindUser(Guid identifier) public User FindUser(Guid identifier)
{ {
return Query.Where(u => u.Identifier == identifier).SingleOrDefault(); return Query(q => q.Where(u => u.Identifier == identifier).SingleOrDefault());
} }
} }
} }
@@ -26,7 +26,11 @@ namespace NzbDrone.Core.Backup
public void BackupDatabase(IDatabase database, string targetDirectory) public void BackupDatabase(IDatabase database, string targetDirectory)
{ {
var sourceConnectionString = database.GetDataMapper().ConnectionString; var sourceConnectionString = "";
using (var db = database.GetDataMapper())
{
sourceConnectionString = db.ConnectionString;
}
var backupConnectionStringBuilder = new SQLiteConnectionStringBuilder(sourceConnectionString); var backupConnectionStringBuilder = new SQLiteConnectionStringBuilder(sourceConnectionString);
backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource)); backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource));
@@ -22,19 +22,19 @@ namespace NzbDrone.Core.Blacklisting
public List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle) public List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle)
{ {
return Query.Where(e => e.MovieId == movieId) return Query(q => q.Where(e => e.MovieId == movieId)
.AndWhere(e => e.SourceTitle.Contains(sourceTitle)); .AndWhere(e => e.SourceTitle.Contains(sourceTitle)).ToList());
} }
public List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash) public List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash)
{ {
return Query.Where(e => e.MovieId == movieId) return Query(q => q.Where(e => e.MovieId == movieId)
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash)); .AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash)).ToList());
} }
public List<Blacklist> BlacklistedByMovie(int movieId) public List<Blacklist> BlacklistedByMovie(int movieId)
{ {
return Query.Where(b => b.MovieId == movieId); return Query(q => q.Where(b => b.MovieId == movieId).ToList());
} }
protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec) protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec)
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Configuration
public Config Get(string key) public Config Get(string key)
{ {
return Query.Where(c => c.Key == key).SingleOrDefault(); return Query(q => q.Where(c => c.Key == key).SingleOrDefault());
} }
public Config Upsert(string key, string value) public Config Upsert(string key, string value)
@@ -273,6 +273,13 @@ namespace NzbDrone.Core.Configuration
set { SetValue("DownloadedMoviesScanInterval", value); } set { SetValue("DownloadedMoviesScanInterval", value); }
} }
public int CheckForFinishedDownloadInterval
{
get { return GetValueInt("CheckForFinishedDownloadInterval", 1); }
set { SetValue("CheckForFinishedDownloadInterval", value); }
}
public int DownloadClientHistoryLimit public int DownloadClientHistoryLimit
{ {
get { return GetValueInt("DownloadClientHistoryLimit", 30); } get { return GetValueInt("DownloadClientHistoryLimit", 30); }
@@ -16,6 +16,7 @@ namespace NzbDrone.Core.Configuration
string DownloadClientWorkingFolders { get; set; } string DownloadClientWorkingFolders { get; set; }
int DownloadedMoviesScanInterval { get; set; } int DownloadedMoviesScanInterval { get; set; }
int DownloadClientHistoryLimit { get; set; } int DownloadClientHistoryLimit { get; set; }
int CheckForFinishedDownloadInterval { get; set; }
//Completed/Failed Download Handling (Download client) //Completed/Failed Download Handling (Download client)
bool EnableCompletedDownloadHandling { get; set; } bool EnableCompletedDownloadHandling { get; set; }
@@ -191,8 +191,8 @@ namespace NzbDrone.Core.CustomFormats
{ {
"Easy", new List<CustomFormat> "Easy", new List<CustomFormat>
{ {
new CustomFormat("x264", "C_RX_(x|h)264"), new CustomFormat("x264", @"C_RX_(x|h)\.?264"),
new CustomFormat("x265", "C_RX_(((x|h)265)|(HEVC))"), new CustomFormat("x265", @"C_RX_(((x|h)\.?265)|(HEVC))"),
new CustomFormat("Simple Hardcoded Subs", "C_RX_subs?"), new CustomFormat("Simple Hardcoded Subs", "C_RX_subs?"),
new CustomFormat("Multi Language", "L_English", "L_French") new CustomFormat("Multi Language", "L_English", "L_French")
} }
+4 -1
View File
@@ -83,7 +83,9 @@ namespace NzbDrone.Core.CustomFormats
var tuple = Value as (long, long)? ?? (0, 0); var tuple = Value as (long, long)? ?? (0, 0);
return size > tuple.Item1 && size < tuple.Item2; return size > tuple.Item1 && size < tuple.Item2;
case TagType.Indexer: case TagType.Indexer:
#if !LIBRARY
return (movieInfo.ExtraInfo.GetValueOrDefault("IndexerFlags") as IndexerFlags?)?.HasFlag((IndexerFlags) Value) == true; return (movieInfo.ExtraInfo.GetValueOrDefault("IndexerFlags") as IndexerFlags?)?.HasFlag((IndexerFlags) Value) == true;
#endif
default: default:
return false; return false;
} }
@@ -188,6 +190,7 @@ namespace NzbDrone.Core.CustomFormats
Value = Parser.LanguageParser.ParseLanguages(value).First(); Value = Parser.LanguageParser.ParseLanguages(value).First();
break; break;
case "i": case "i":
#if !LIBRARY
TagType = TagType.Indexer; TagType = TagType.Indexer;
var flagValues = Enum.GetValues(typeof(IndexerFlags)); var flagValues = Enum.GetValues(typeof(IndexerFlags));
@@ -198,7 +201,7 @@ namespace NzbDrone.Core.CustomFormats
Value = flagValue; Value = flagValue;
break; break;
} }
#endif
break; break;
case "g": case "g":
TagType = TagType.Size; TagType = TagType.Size;
+55 -23
View File
@@ -42,7 +42,10 @@ namespace NzbDrone.Core.Datastore
private readonly IDatabase _database; private readonly IDatabase _database;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
protected IDataMapper DataMapper => _database.GetDataMapper(); protected IDataMapper DataMapper()
{
return _database.GetDataMapper();
}
public BasicRepository(IDatabase database, IEventAggregator eventAggregator) public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
{ {
@@ -50,26 +53,40 @@ namespace NzbDrone.Core.Datastore
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
} }
protected QueryBuilder<TModel> Query => AddJoinQueries(DataMapper.Query<TModel>());
protected T Query<T>(Func<QueryBuilder<TModel>, T> finalizeQuery)
{
using (var mapper = DataMapper())
{
var query = AddJoinQueries(mapper.Query<TModel>());
return finalizeQuery(query);
}
}
protected void Delete(Expression<Func<TModel, bool>> filter) protected void Delete(Expression<Func<TModel, bool>> filter)
{ {
DataMapper.Delete(filter); using (var db = DataMapper())
{
db.Delete(filter);
}
} }
public IEnumerable<TModel> All() public IEnumerable<TModel> All()
{ {
return Query.ToList(); return Query((q => q.ToList()));
} }
public int Count() public int Count()
{ {
return DataMapper.Query<TModel>().GetRowCount(); using (var db = DataMapper())
{
return db.Query<TModel>().GetRowCount();
}
} }
public TModel Get(int id) public TModel Get(int id)
{ {
var model = Query.Where(c => c.Id == id).SingleOrDefault(); TModel model = Query(q => q.Where(c => c.Id == id).SingleOrDefault());
if (model == null) if (model == null)
{ {
@@ -83,7 +100,7 @@ namespace NzbDrone.Core.Datastore
{ {
var idList = ids.ToList(); var idList = ids.ToList();
var query = string.Format("Id IN ({0})", string.Join(",", idList)); var query = string.Format("Id IN ({0})", string.Join(",", idList));
var result = Query.Where(m => m.Id.In(idList)).ToList(); var result = Query(q => q.Where(m => m.Id.In(idList)).ToList());
//var result = Query.Where(query).ToList(); //var result = Query.Where(query).ToList();
if (result.Count != idList.Count()) if (result.Count != idList.Count())
@@ -111,7 +128,10 @@ namespace NzbDrone.Core.Datastore
throw new InvalidOperationException("Can't insert model with existing ID " + model.Id); throw new InvalidOperationException("Can't insert model with existing ID " + model.Id);
} }
DataMapper.Insert(model); using (var db = DataMapper())
{
db.Insert(model);
}
ModelCreated(model); ModelCreated(model);
@@ -125,7 +145,10 @@ namespace NzbDrone.Core.Datastore
throw new InvalidOperationException("Can't update model with ID 0"); throw new InvalidOperationException("Can't update model with ID 0");
} }
DataMapper.Update(model, c => c.Id == model.Id); using (var db = DataMapper())
{
db.Update(model, c => c.Id == model.Id);
}
ModelUpdated(model); ModelUpdated(model);
@@ -139,7 +162,7 @@ namespace NzbDrone.Core.Datastore
public void InsertMany(IList<TModel> models) public void InsertMany(IList<TModel> models)
{ {
using (var unitOfWork = new UnitOfWork(() => DataMapper)) using (var unitOfWork = new UnitOfWork(() => DataMapper()))
{ {
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
@@ -154,7 +177,7 @@ namespace NzbDrone.Core.Datastore
public void UpdateMany(IList<TModel> models) public void UpdateMany(IList<TModel> models)
{ {
using (var unitOfWork = new UnitOfWork(() => DataMapper)) using (var unitOfWork = new UnitOfWork(() => DataMapper()))
{ {
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
@@ -192,12 +215,15 @@ namespace NzbDrone.Core.Datastore
public void Delete(int id) public void Delete(int id)
{ {
DataMapper.Delete<TModel>(c => c.Id == id); using (var db = DataMapper())
{
db.Delete<TModel>(c => c.Id == id);
}
} }
public void DeleteMany(IEnumerable<int> ids) public void DeleteMany(IEnumerable<int> ids)
{ {
using (var unitOfWork = new UnitOfWork(() => DataMapper)) using (var unitOfWork = new UnitOfWork(() => DataMapper()))
{ {
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
@@ -214,7 +240,10 @@ namespace NzbDrone.Core.Datastore
public void Purge(bool vacuum = false) public void Purge(bool vacuum = false)
{ {
DataMapper.Delete<TModel>(c => c.Id > -1); using (var db = DataMapper())
{
db.Delete<TModel>(c => c.Id > -1);
}
if (vacuum) if (vacuum)
{ {
Vacuum(); Vacuum();
@@ -238,20 +267,23 @@ namespace NzbDrone.Core.Datastore
throw new InvalidOperationException("Attempted to updated model without ID"); throw new InvalidOperationException("Attempted to updated model without ID");
} }
DataMapper.Update<TModel>() using (var db = DataMapper())
.Where(c => c.Id == model.Id) {
.ColumnsIncluding(properties) db.Update<TModel>()
.Entity(model) .Where(c => c.Id == model.Id)
.Execute(); .ColumnsIncluding(properties)
.Entity(model)
.Execute();
}
ModelUpdated(model); ModelUpdated(model);
} }
public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec) public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec)
{ {
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).Skip(pagingSpec.PagingOffset()) pagingSpec.Records = Query(q => GetPagedQuery(q, pagingSpec).Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize).ToList(); .Take(pagingSpec.PageSize).ToList());
pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount(); pagingSpec.TotalRecords = Query(q => GetPagedQuery(q, pagingSpec).GetRowCount());
return pagingSpec; return pagingSpec;
} }
@@ -285,7 +317,7 @@ namespace NzbDrone.Core.Datastore
} }
} }
protected virtual QueryBuilder<TModel> AddJoinQueries(QueryBuilder<TModel> baseQuery) protected virtual QueryBuilder<TActual> AddJoinQueries<TActual>(QueryBuilder<TActual> baseQuery)
{ {
return baseQuery; return baseQuery;
} }
@@ -35,7 +35,7 @@ namespace NzbDrone.Core.Datastore
var connectionBuilder = new SQLiteConnectionStringBuilder(); var connectionBuilder = new SQLiteConnectionStringBuilder();
connectionBuilder.DataSource = dbPath; connectionBuilder.DataSource = dbPath;
connectionBuilder.CacheSize = (int)-10.Megabytes(); connectionBuilder.CacheSize = (int)-20000;
connectionBuilder.DateTimeKind = DateTimeKind.Utc; connectionBuilder.DateTimeKind = DateTimeKind.Utc;
connectionBuilder.JournalMode = OsInfo.IsOsx ? SQLiteJournalModeEnum.Truncate : SQLiteJournalModeEnum.Wal; connectionBuilder.JournalMode = OsInfo.IsOsx ? SQLiteJournalModeEnum.Truncate : SQLiteJournalModeEnum.Wal;
connectionBuilder.Pooling = true; connectionBuilder.Pooling = true;
+9 -3
View File
@@ -34,8 +34,11 @@ namespace NzbDrone.Core.Datastore
{ {
get get
{ {
var version = _datamapperFactory().ExecuteScalar("SELECT sqlite_version()").ToString(); using (var db = _datamapperFactory())
return new Version(version); {
var version = db.ExecuteScalar("SELECT sqlite_version()").ToString();
return new Version(version);
}
} }
} }
@@ -44,7 +47,10 @@ namespace NzbDrone.Core.Datastore
try try
{ {
_logger.Info("Vacuuming {0} database", _databaseName); _logger.Info("Vacuuming {0} database", _databaseName);
_datamapperFactory().ExecuteNonQuery("Vacuum;"); using (var db = _datamapperFactory())
{
db.ExecuteNonQuery("Vacuum;");
}
_logger.Info("{0} database compressed", _databaseName); _logger.Info("{0} database compressed", _databaseName);
} }
catch (Exception e) catch (Exception e)
@@ -0,0 +1,17 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(151)]
public class add_tags_to_net_import : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("NetImport")
.AddColumn("Tags").AsString().Nullable();
Execute.Sql("UPDATE NetImport SET Tags = '[]'");
}
}
}
@@ -25,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger) : base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{ {
_scanWatchFolder = scanWatchFolder; _scanWatchFolder = scanWatchFolder;
@@ -34,9 +34,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger Logger logger
) )
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger) : base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{ {
_dsInfoProxy = dsInfoProxy; _dsInfoProxy = dsInfoProxy;
_dsTaskProxy = dsTaskProxy; _dsTaskProxy = dsTaskProxy;
@@ -25,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger) : base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{ {
_proxy = proxy; _proxy = proxy;
} }
@@ -12,6 +12,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using System.Globalization;
namespace NzbDrone.Core.Download.Clients.Nzbget namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
@@ -27,8 +28,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger) : base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{ {
_proxy = proxy; _proxy = proxy;
} }
@@ -53,19 +55,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
private IEnumerable<DownloadClientItem> GetQueue() private IEnumerable<DownloadClientItem> GetQueue()
{ {
NzbgetGlobalStatus globalStatus; var globalStatus = _proxy.GetGlobalStatus(Settings);
List<NzbgetQueueItem> queue; var queue = _proxy.GetQueue(Settings);
try
{
globalStatus = _proxy.GetGlobalStatus(Settings);
queue = _proxy.GetQueue(Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex, ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var queueItems = new List<DownloadClientItem>(); var queueItems = new List<DownloadClientItem>();
@@ -121,17 +112,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
private IEnumerable<DownloadClientItem> GetHistory() private IEnumerable<DownloadClientItem> GetHistory()
{ {
List<NzbgetHistoryItem> history; var history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
try
{
history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
}
catch (DownloadClientException ex)
{
_logger.Error(ex, ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var historyItems = new List<DownloadClientItem>(); var historyItems = new List<DownloadClientItem>();
@@ -307,7 +288,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
return new NzbDroneValidationFailure("MovieCategory", "Category does not exist") return new NzbDroneValidationFailure("MovieCategory", "Category does not exist")
{ {
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "The category you entered doesn't exist in NZBGet. Go to NZBGet to create it." DetailedDescription = "The category you entered doesn't exist in NZBGet. Go to NZBGet to create it."
}; };
} }
@@ -319,13 +300,22 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
var config = _proxy.GetConfig(Settings); var config = _proxy.GetConfig(Settings);
var keepHistory = config.GetValueOrDefault("KeepHistory"); var keepHistory = config.GetValueOrDefault("KeepHistory", "7");
if (keepHistory == "0") int value;
if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out value) || value == 0)
{ {
return new NzbDroneValidationFailure(string.Empty, "NZBGet setting KeepHistory should be greater than 0") return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
{ {
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "NZBGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads." DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
};
}
else if (value > 25000)
{
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be less than 25000")
{
InfoLink = _proxy.GetBaseUrl(Settings),
DetailedDescription = "NzbGet setting KeepHistory is set too high."
}; };
} }
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
public interface INzbgetProxy public interface INzbgetProxy
{ {
string GetBaseUrl(NzbgetSettings settings, string relativePath = null);
string DownloadNzb(byte[] nzbData, string title, string category, int priority, bool addpaused, NzbgetSettings settings); string DownloadNzb(byte[] nzbData, string title, string category, int priority, bool addpaused, NzbgetSettings settings);
NzbgetGlobalStatus GetGlobalStatus(NzbgetSettings settings); NzbgetGlobalStatus GetGlobalStatus(NzbgetSettings settings);
List<NzbgetQueueItem> GetQueue(NzbgetSettings settings); List<NzbgetQueueItem> GetQueue(NzbgetSettings settings);
@@ -36,9 +37,17 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
_versionCache = cacheManager.GetCache<string>(GetType(), "versions"); _versionCache = cacheManager.GetCache<string>(GetType(), "versions");
} }
public string GetBaseUrl(NzbgetSettings settings, string relativePath = null)
{
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase);
baseUrl = HttpUri.CombinePath(baseUrl, relativePath);
return baseUrl;
}
private bool HasVersion(int minimumVersion, NzbgetSettings settings) private bool HasVersion(int minimumVersion, NzbgetSettings settings)
{ {
var versionString = _versionCache.Find(settings.Host + ":" + settings.Port) ?? GetVersion(settings); var versionString = _versionCache.Find(GetBaseUrl(settings)) ?? GetVersion(settings);
var version = int.Parse(versionString.Split(new[] { '.', '-' })[0]); var version = int.Parse(versionString.Split(new[] { '.', '-' })[0]);
@@ -139,7 +148,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
var response = ProcessRequest<string>(settings, "version"); var response = ProcessRequest<string>(settings, "version");
_versionCache.Set(settings.Host + ":" + settings.Port, response, TimeSpan.FromDays(1)); _versionCache.Set(GetBaseUrl(settings), response, TimeSpan.FromDays(1));
return response; return response;
} }
@@ -170,7 +179,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
queueItem = queue.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string))); queueItem = queue.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string)));
historyItem = history.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string))); historyItem = history.SingleOrDefault(h => h.Parameters.Any(p => p.Name == "drone" && id == (p.Value as string)));
} }
if (queueItem != null) if (queueItem != null)
{ {
if (!EditQueue("GroupFinalDelete", 0, "", queueItem.NzbId, settings)) if (!EditQueue("GroupFinalDelete", 0, "", queueItem.NzbId, settings))
@@ -218,7 +227,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
private T ProcessRequest<T>(NzbgetSettings settings, string method, params object[] parameters) private T ProcessRequest<T>(NzbgetSettings settings, string method, params object[] parameters)
{ {
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, "jsonrpc"); var baseUrl = GetBaseUrl(settings, "jsonrpc");
var requestBuilder = new JsonRpcRequestBuilder(baseUrl, method, parameters); var requestBuilder = new JsonRpcRequestBuilder(baseUrl, method, parameters);
requestBuilder.LogResponseContent = true; requestBuilder.LogResponseContent = true;
@@ -1,4 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@@ -11,6 +12,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535); RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace());
RuleFor(c => c.Username).NotEmpty().When(c => !string.IsNullOrWhiteSpace(c.Password)); RuleFor(c => c.Username).NotEmpty().When(c => !string.IsNullOrWhiteSpace(c.Password));
RuleFor(c => c.Password).NotEmpty().When(c => !string.IsNullOrWhiteSpace(c.Username)); RuleFor(c => c.Password).NotEmpty().When(c => !string.IsNullOrWhiteSpace(c.Username));
@@ -39,25 +42,28 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "Username", Type = FieldType.Textbox)] [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the nzbget url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")]
public string UrlBase { get; set; }
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)]
public string Username { get; set; } public string Username { get; set; }
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)] [FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] [FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)] [FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
[FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NZBGet version 16.0")] [FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NZBGet version 16.0")]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
@@ -154,6 +154,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
case "stalledUP": // torrent is being seeded, but no connection were made case "stalledUP": // torrent is being seeded, but no connection were made
case "queuedUP": // queuing is enabled and torrent is queued for upload case "queuedUP": // queuing is enabled and torrent is queued for upload
case "checkingUP": // torrent has finished downloading and is being checked case "checkingUP": // torrent has finished downloading and is being checked
case "forcedUP": // torrent is beeing seeded by force
item.Status = DownloadItemStatus.Completed; item.Status = DownloadItemStatus.Completed;
item.RemainingTime = TimeSpan.Zero; // qBittorrent sends eta=8640000 for completed torrents item.RemainingTime = TimeSpan.Zero; // qBittorrent sends eta=8640000 for completed torrents
break; break;
@@ -164,7 +165,12 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
break; break;
case "downloading": // torrent is being downloaded and data is being transfered case "downloading": // torrent is being downloaded and data is being transfered
item.Status = DownloadItemStatus.Downloading;
break;
default: // new status in API? default to downloading default: // new status in API? default to downloading
item.Message = "Unknown download state: " + torrent.State;
_logger.Warn(item.Message);
item.Status = DownloadItemStatus.Downloading; item.Status = DownloadItemStatus.Downloading;
break; break;
} }
@@ -25,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, logger) : base(httpClient, configService, namingConfigService, diskProvider, remotePathMappingService, nzbValidationService, logger)
{ {
_proxy = proxy; _proxy = proxy;
} }
@@ -115,17 +116,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
private IEnumerable<DownloadClientItem> GetHistory() private IEnumerable<DownloadClientItem> GetHistory()
{ {
SabnzbdHistory sabHistory; var sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.MovieCategory, Settings);
try
{
sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.MovieCategory, Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex, ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var historyItems = new List<DownloadClientItem>(); var historyItems = new List<DownloadClientItem>();
@@ -191,6 +182,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
} }
} }
historyItems.Add(historyItem); historyItems.Add(historyItem);
} }
@@ -327,6 +319,11 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
private Version ParseVersion(string version) private Version ParseVersion(string version)
{ {
if (version.IsNullOrWhiteSpace())
{
return null;
}
var parsed = VersionRegex.Match(version); var parsed = VersionRegex.Match(version);
int major; int major;
@@ -364,7 +361,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
if (version == null) if (version == null)
{ {
return new ValidationFailure("Version", "Unknown Version: " + version); return new ValidationFailure("Version", "Unknown Version: " + rawVersion);
} }
if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase)) if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase))
@@ -424,7 +421,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
return new NzbDroneValidationFailure("", "Disable 'Check before download' option in SABnzbd") return new NzbDroneValidationFailure("", "Disable 'Check before download' option in SABnzbd")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/switches/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings, "config/switches/"),
DetailedDescription = "Using Check before download affects Radarr ability to track new downloads. Also SABnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective." DetailedDescription = "Using Check before download affects Radarr ability to track new downloads. Also SABnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective."
}; };
} }
@@ -443,7 +440,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
return new NzbDroneValidationFailure("MovieCategory", "Enable Job folders") return new NzbDroneValidationFailure("MovieCategory", "Enable Job folders")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"),
DetailedDescription = "Radarr prefers each download to have a separate folder. With * appended to the Folder/Path SABnzbd will not create these job folders. Go to SABnzbd to fix it." DetailedDescription = "Radarr prefers each download to have a separate folder. With * appended to the Folder/Path SABnzbd will not create these job folders. Go to SABnzbd to fix it."
}; };
} }
@@ -454,7 +451,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
return new NzbDroneValidationFailure("MovieCategory", "Category does not exist") return new NzbDroneValidationFailure("MovieCategory", "Category does not exist")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"),
DetailedDescription = "The category you entered doesn't exist in SABnzbd. Go to SABnzbd to create it." DetailedDescription = "The category you entered doesn't exist in SABnzbd. Go to SABnzbd to create it."
}; };
} }
@@ -463,7 +460,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable TV Sorting") return new NzbDroneValidationFailure("MovieCategory", "Disable TV Sorting")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it." DetailedDescription = "You must disable SABnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it."
}; };
} }
@@ -471,7 +468,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable Movie Sorting") return new NzbDroneValidationFailure("MovieCategory", "Disable Movie Sorting")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it." DetailedDescription = "You must disable SABnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it."
}; };
} }
@@ -479,7 +476,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
return new NzbDroneValidationFailure("MovieCategory", "Disable Date Sorting") return new NzbDroneValidationFailure("MovieCategory", "Disable Date Sorting")
{ {
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port), InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"),
DetailedDescription = "You must disable SABnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it." DetailedDescription = "You must disable SABnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to SABnzbd to fix it."
}; };
} }
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
public interface ISabnzbdProxy public interface ISabnzbdProxy
{ {
string GetBaseUrl(SabnzbdSettings settings, string relativePath = null);
SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings); SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings);
void RemoveFrom(string source, string id,bool deleteData, SabnzbdSettings settings); void RemoveFrom(string source, string id,bool deleteData, SabnzbdSettings settings);
string GetVersion(SabnzbdSettings settings); string GetVersion(SabnzbdSettings settings);
@@ -32,6 +33,14 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
_logger = logger; _logger = logger;
} }
public string GetBaseUrl(SabnzbdSettings settings, string relativePath = null)
{
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase);
baseUrl = HttpUri.CombinePath(baseUrl, relativePath);
return baseUrl;
}
public SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings) public SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings)
{ {
var request = BuildRequest("addfile", settings).Post(); var request = BuildRequest("addfile", settings).Post();
@@ -140,10 +149,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
private HttpRequestBuilder BuildRequest(string mode, SabnzbdSettings settings) private HttpRequestBuilder BuildRequest(string mode, SabnzbdSettings settings)
{ {
var baseUrl = string.Format(@"{0}://{1}:{2}/api", var baseUrl = GetBaseUrl(settings, "api");
settings.UseSsl ? "https" : "http",
settings.Host,
settings.Port);
var requestBuilder = new HttpRequestBuilder(baseUrl) var requestBuilder = new HttpRequestBuilder(baseUrl)
.Accept(HttpAccept.Json) .Accept(HttpAccept.Json)
@@ -1,4 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@@ -11,6 +12,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535); RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.UrlBase).ValidUrlBase().When(c => c.UrlBase.IsNotNullOrWhiteSpace());
RuleFor(c => c.ApiKey).NotEmpty() RuleFor(c => c.ApiKey).NotEmpty()
.WithMessage("API Key is required when username/password are not configured") .WithMessage("API Key is required when username/password are not configured")
@@ -49,25 +51,28 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
public int Port { get; set; } public int Port { get; set; }
[FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)] [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Sabnzbd url, e.g. http://[host]:[port]/[urlBase]/api")]
public string UrlBase { get; set; }
[FieldDefinition(3, Label = "API Key", Type = FieldType.Textbox)]
public string ApiKey { get; set; } public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox)] [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox)]
public string Username { get; set; } public string Username { get; set; }
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)] [FieldDefinition(5, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; } public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string MovieCategory { get; set; } public string MovieCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")] [FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released within the last 21 days")]
public int RecentMoviePriority { get; set; } public int RecentMoviePriority { get; set; }
[FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)] [FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
{ {
var priority = (RTorrentPriority)(remoteMovie.Movie.IsRecentMovie ? Settings.RecentMoviePriority : Settings.OlderMoviePriority); var priority = (RTorrentPriority)(remoteMovie.Movie.IsRecentMovie ? Settings.RecentMoviePriority : Settings.OlderMoviePriority);
_proxy.AddTorrentFromUrl(magnetLink, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings); _proxy.AddTorrentFromUrl(magnetLink, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings.DontStartAutomatically, Settings);
var tries = 10; var tries = 10;
var retryDelay = 500; var retryDelay = 500;
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
{ {
var priority = (RTorrentPriority)(remoteMovie.Movie.IsRecentMovie ? Settings.RecentMoviePriority : Settings.OlderMoviePriority); var priority = (RTorrentPriority)(remoteMovie.Movie.IsRecentMovie ? Settings.RecentMoviePriority : Settings.OlderMoviePriority);
_proxy.AddTorrentFromFile(filename, fileContent, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings); _proxy.AddTorrentFromFile(filename, fileContent, Settings.MovieCategory, priority, Settings.MovieDirectory, Settings.DontStartAutomatically, Settings);
var tries = 10; var tries = 10;
var retryDelay = 500; var retryDelay = 500;
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -13,8 +13,8 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
string GetVersion(RTorrentSettings settings); string GetVersion(RTorrentSettings settings);
List<RTorrentTorrent> GetTorrents(RTorrentSettings settings); List<RTorrentTorrent> GetTorrents(RTorrentSettings settings);
void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings); void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings);
void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings); void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings);
void RemoveTorrent(string hash, RTorrentSettings settings); void RemoveTorrent(string hash, RTorrentSettings settings);
bool HasHashTorrent(string hash, RTorrentSettings settings); bool HasHashTorrent(string hash, RTorrentSettings settings);
} }
@@ -24,9 +24,15 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[XmlRpcMethod("d.multicall2")] [XmlRpcMethod("d.multicall2")]
object[] TorrentMulticall(params string[] parameters); object[] TorrentMulticall(params string[] parameters);
[XmlRpcMethod("load.normal")]
int Load(string target, string data, params string[] commands);
[XmlRpcMethod("load.start")] [XmlRpcMethod("load.start")]
int LoadStart(string target, string data, params string[] commands); int LoadStart(string target, string data, params string[] commands);
[XmlRpcMethod("load.raw")]
int LoadRaw(string target, byte[] data, params string[] commands);
[XmlRpcMethod("load.raw_start")] [XmlRpcMethod("load.raw_start")]
int LoadRawStart(string target, byte[] data, params string[] commands); int LoadRawStart(string target, byte[] data, params string[] commands);
@@ -102,26 +108,50 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return items; return items;
} }
public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings) public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings)
{ {
_logger.Debug("Executing remote method: load.normal"); _logger.Debug("Adding Torrent From URL");
var client = BuildClient(settings); var client = BuildClient(settings);
var response = client.LoadStart("", torrentUrl, GetCommands(label, priority, directory)); var response = -1;
if (doNotStart)
{
_logger.Debug("Executing remote method load.normal");
response = client.Load("", torrentUrl, GetCommands(label, priority, directory));
}
else
{
_logger.Debug("Executing remote method load.start");
response = client.LoadStart("", torrentUrl, GetCommands(label, priority, directory));
}
if (response != 0) if (response != 0)
{ {
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl); throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
} }
} }
public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings) public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, bool doNotStart, RTorrentSettings settings)
{ {
_logger.Debug("Executing remote method: load.raw"); _logger.Debug("Loading Torrent from File");
var client = BuildClient(settings); var client = BuildClient(settings);
var response = client.LoadRawStart("", fileContent, GetCommands(label, priority, directory)); var response = -1;
if (doNotStart)
{
_logger.Debug("Executing remote method load.raw");
response = client.LoadRaw("", fileContent, GetCommands(label, priority, directory));
}
else
{
_logger.Debug("Executing remote method load.raw_start");
response = client.LoadRawStart("", fileContent, GetCommands(label, priority, directory));
}
if (response != 0) if (response != 0)
{ {
throw new DownloadClientException("Could not add torrent: {0}.", fileName); throw new DownloadClientException("Could not add torrent: {0}.", fileName);
@@ -202,4 +232,4 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return client; return client;
} }
} }
} }
@@ -59,6 +59,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")] [FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing movies that released over 21 days ago")]
public int OlderMoviePriority { get; set; } public int OlderMoviePriority { get; set; }
[FieldDefinition(10, Label = "Don't start download automatically", Type = FieldType.Checkbox, Advanced = true, HelpText = "Add Download in a stopped state. This is useful for letting a Queue manager like pyrotorque automatically start the download.")]
public bool DontStartAutomatically { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
@@ -0,0 +1,24 @@
using System;
using NzbDrone.Common.Exceptions;
namespace NzbDrone.Core.Download
{
public class InvalidNzbException : NzbDroneException
{
public InvalidNzbException(string message, params object[] args) : base(message, args)
{
}
public InvalidNzbException(string message) : base(message)
{
}
public InvalidNzbException(string message, Exception innerException, params object[] args) : base(message, innerException, args)
{
}
public InvalidNzbException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
@@ -0,0 +1,45 @@
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Download
{
public interface IValidateNzbs
{
void Validate(string filename, byte[] fileContent);
}
public class NzbValidationService : IValidateNzbs
{
public void Validate(string filename, byte[] fileContent)
{
var reader = new StreamReader(new MemoryStream(fileContent));
using (var xmlTextReader = XmlReader.Create(reader, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, IgnoreComments = true }))
{
var xDoc = XDocument.Load(xmlTextReader);
var nzb = xDoc.Root;
if (nzb == null)
{
throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
}
if (!nzb.Name.LocalName.Equals("nzb"))
{
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
}
var ns = nzb.Name.Namespace;
var files = nzb.Elements(ns + "file").ToList();
if (files.Empty())
{
throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
}
}
}
}
}
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Download.Pending
public List<PendingRelease> AllByMovieId(int movieId) public List<PendingRelease> AllByMovieId(int movieId)
{ {
return Query.Where(p => p.MovieId == movieId); return Query(q => q.Where(p => p.MovieId == movieId).ToList());
} }
} }
} }
@@ -44,6 +44,20 @@ namespace NzbDrone.Core.Download.TrackedDownloads
public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, DownloadClientItem downloadItem) public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, DownloadClientItem downloadItem)
{ {
if (downloadItem.DownloadId.IsNullOrWhiteSpace())
{
_logger.Warn("The following download client item ({0}) has no download hash (id), so it cannot be tracked: {1}",
downloadClient.Name, downloadItem.Title);
return null;
}
if (downloadItem.Title.IsNullOrWhiteSpace())
{
_logger.Warn("The following download client item ({0}) has no title so it cannot be tracked: {1}",
downloadClient.Name, downloadItem.Title);
return null;
}
var existingItem = Find(downloadItem.DownloadId); var existingItem = Find(downloadItem.DownloadId);
if (existingItem != null && existingItem.State != TrackedDownloadStage.Downloading) if (existingItem != null && existingItem.State != TrackedDownloadStage.Downloading)
@@ -17,16 +17,19 @@ namespace NzbDrone.Core.Download
where TSettings : IProviderConfig, new() where TSettings : IProviderConfig, new()
{ {
protected readonly IHttpClient _httpClient; protected readonly IHttpClient _httpClient;
private readonly IValidateNzbs _nzbValidationService;
protected UsenetClientBase(IHttpClient httpClient, protected UsenetClientBase(IHttpClient httpClient,
IConfigService configService, IConfigService configService,
INamingConfigService namingConfigService, INamingConfigService namingConfigService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IValidateNzbs nzbValidationService,
Logger logger) Logger logger)
: base(configService, namingConfigService, diskProvider, remotePathMappingService, logger) : base(configService, namingConfigService, diskProvider, remotePathMappingService, logger)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_nzbValidationService = nzbValidationService;
} }
public override DownloadProtocol Protocol => DownloadProtocol.Usenet; public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
@@ -66,6 +69,8 @@ namespace NzbDrone.Core.Download
throw new ReleaseDownloadException(remoteMovie.Release, "Downloading nzb failed", ex); throw new ReleaseDownloadException(remoteMovie.Release, "Downloading nzb failed", ex);
} }
_nzbValidationService.Validate(filename, nzbData);
_logger.Info("Adding report [{0}] to the queue.", remoteMovie.Release.Title); _logger.Info("Adding report [{0}] to the queue.", remoteMovie.Release.Title);
return AddFromNzbFile(remoteMovie, filename, nzbData); return AddFromNzbFile(remoteMovie, filename, nzbData);
} }
@@ -34,17 +34,17 @@ namespace NzbDrone.Core.Extras.Files
public List<TExtraFile> GetFilesByMovie(int movieId) public List<TExtraFile> GetFilesByMovie(int movieId)
{ {
return Query.Where(c => c.MovieId == movieId); return Query(q => q.Where(c => c.MovieId == movieId).ToList());
} }
public List<TExtraFile> GetFilesByMovieFile(int movieFileId) public List<TExtraFile> GetFilesByMovieFile(int movieFileId)
{ {
return Query.Where(c => c.MovieFileId == movieFileId); return Query(q => q.Where(c => c.MovieFileId == movieFileId).ToList());
} }
public TExtraFile FindByPath(string path) public TExtraFile FindByPath(string path)
{ {
return Query.Where(c => c.RelativePath == path).SingleOrDefault(); return Query(q => q.Where(c => c.RelativePath == path).SingleOrDefault());
} }
} }
} }
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -102,122 +102,156 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile) public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile)
{ {
if (!Settings.MovieMetadata)
{
return null;
}
_logger.Debug("Generating Movie Metadata for: {0}", Path.Combine(movie.Path, movieFile.RelativePath));
var watched = GetExistingWatchedStatus(movie, movieFile.RelativePath);
var xmlResult = string.Empty; var xmlResult = string.Empty;
if (Settings.MovieMetadata)
var sb = new StringBuilder();
var xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = false;
using (var xw = XmlWriter.Create(sb, xws))
{ {
var doc = new XDocument(); _logger.Debug("Generating Movie Metadata for: {0}", Path.Combine(movie.Path, movieFile.RelativePath));
var image = movie.Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot); var watched = GetExistingWatchedStatus(movie, movieFile.RelativePath);
var details = new XElement("movie"); var sb = new StringBuilder();
var xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = false;
details.Add(new XElement("title", movie.Title)); using (var xw = XmlWriter.Create(sb, xws))
if (movie.Ratings != null && movie.Ratings.Votes > 0)
{ {
details.Add(new XElement("rating", movie.Ratings.Value)); var doc = new XDocument();
} var thumbnail = movie.Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
var posters = movie.Images.Where(i => i.CoverType == MediaCoverTypes.Poster);
var fanarts = movie.Images.Where(i => i.CoverType == MediaCoverTypes.Fanart);
details.Add(new XElement("plot", movie.Overview)); var details = new XElement("movie");
details.Add(new XElement("id", movie.ImdbId));
details.Add(new XElement("year", movie.Year));
if (movie.InCinemas.HasValue) details.Add(new XElement("title", movie.Title));
{
details.Add(new XElement("premiered", movie.InCinemas.Value.ToString("yyyy-MM-dd")));
}
foreach (var genre in movie.Genres) if (movie.Ratings != null && movie.Ratings.Votes > 0)
{
details.Add(new XElement("genre", genre));
}
details.Add(new XElement("studio", movie.Studio));
if (image == null)
{
details.Add(new XElement("thumb"));
}
else
{
details.Add(new XElement("thumb", image.Url));
}
details.Add(new XElement("watched", watched));
if (movieFile.MediaInfo != null)
{
var sceneName = movieFile.GetSceneOrFileName();
var fileInfo = new XElement("fileinfo");
var streamDetails = new XElement("streamdetails");
var video = new XElement("video");
video.Add(new XElement("aspect", (float)movieFile.MediaInfo.Width / (float)movieFile.MediaInfo.Height));
video.Add(new XElement("bitrate", movieFile.MediaInfo.VideoBitrate));
video.Add(new XElement("codec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName)));
video.Add(new XElement("framerate", movieFile.MediaInfo.VideoFps));
video.Add(new XElement("height", movieFile.MediaInfo.Height));
video.Add(new XElement("scantype", movieFile.MediaInfo.ScanType));
video.Add(new XElement("width", movieFile.MediaInfo.Width));
if (movieFile.MediaInfo.RunTime != null)
{ {
video.Add(new XElement("duration", movieFile.MediaInfo.RunTime.TotalMinutes)); details.Add(new XElement("rating", movie.Ratings.Value));
video.Add(new XElement("durationinseconds", movieFile.MediaInfo.RunTime.TotalSeconds));
} }
streamDetails.Add(video); details.Add(new XElement("plot", movie.Overview));
details.Add(new XElement("id", movie.ImdbId));
var audio = new XElement("audio"); if (movie.ImdbId.IsNotNullOrWhiteSpace())
audio.Add(new XElement("bitrate", movieFile.MediaInfo.AudioBitrate));
audio.Add(new XElement("channels", movieFile.MediaInfo.AudioChannels));
audio.Add(new XElement("codec", MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName)));
audio.Add(new XElement("language", movieFile.MediaInfo.AudioLanguages));
streamDetails.Add(audio);
if (movieFile.MediaInfo.Subtitles != null && movieFile.MediaInfo.Subtitles.Length > 0)
{ {
var subtitle = new XElement("subtitle"); var imdbId = new XElement("uniqueid", movie.ImdbId);
subtitle.Add(new XElement("language", movieFile.MediaInfo.Subtitles)); imdbId.SetAttributeValue("type", "imdb");
streamDetails.Add(subtitle); imdbId.SetAttributeValue("default", true);
details.Add(imdbId);
} }
fileInfo.Add(streamDetails); var uniqueId = new XElement("uniqueid", movie.TmdbId);
details.Add(fileInfo); uniqueId.SetAttributeValue("type", "tmdb");
details.Add(uniqueId);
details.Add(new XElement("year", movie.Year));
if (movie.InCinemas.HasValue)
{
details.Add(new XElement("premiered", movie.InCinemas.Value.ToString("yyyy-MM-dd")));
}
foreach (var genre in movie.Genres)
{
details.Add(new XElement("genre", genre));
}
details.Add(new XElement("studio", movie.Studio));
if (thumbnail == null)
{
details.Add(new XElement("thumb"));
}
else
{
details.Add(new XElement("thumb", thumbnail.Url));
}
foreach (var poster in posters)
{
if (poster != null && poster.Url != null)
{
details.Add(new XElement("thumb", new XAttribute("aspect", "poster"), poster.Url));
}
}
if (fanarts.Count() > 0)
{
var fanartElement = new XElement("fanart");
foreach (var fanart in fanarts)
{
if (fanart != null && fanart.Url != null)
{
fanartElement.Add(new XElement("thumb", fanart.Url));
}
}
details.Add(fanartElement);
}
details.Add(new XElement("watched", watched));
if (movieFile.MediaInfo != null)
{
var sceneName = movieFile.GetSceneOrFileName();
var fileInfo = new XElement("fileinfo");
var streamDetails = new XElement("streamdetails");
var video = new XElement("video");
video.Add(new XElement("aspect", (float)movieFile.MediaInfo.Width / (float)movieFile.MediaInfo.Height));
video.Add(new XElement("bitrate", movieFile.MediaInfo.VideoBitrate));
video.Add(new XElement("codec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName)));
video.Add(new XElement("framerate", movieFile.MediaInfo.VideoFps));
video.Add(new XElement("height", movieFile.MediaInfo.Height));
video.Add(new XElement("scantype", movieFile.MediaInfo.ScanType));
video.Add(new XElement("width", movieFile.MediaInfo.Width));
if (movieFile.MediaInfo.RunTime != null)
{
video.Add(new XElement("duration", movieFile.MediaInfo.RunTime.TotalMinutes));
video.Add(new XElement("durationinseconds", movieFile.MediaInfo.RunTime.TotalSeconds));
}
streamDetails.Add(video);
var audio = new XElement("audio");
audio.Add(new XElement("bitrate", movieFile.MediaInfo.AudioBitrate));
audio.Add(new XElement("channels", movieFile.MediaInfo.AudioChannels));
audio.Add(new XElement("codec", MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName)));
audio.Add(new XElement("language", movieFile.MediaInfo.AudioLanguages));
streamDetails.Add(audio);
if (movieFile.MediaInfo.Subtitles != null && movieFile.MediaInfo.Subtitles.Length > 0)
{
var subtitle = new XElement("subtitle");
subtitle.Add(new XElement("language", movieFile.MediaInfo.Subtitles));
streamDetails.Add(subtitle);
}
fileInfo.Add(streamDetails);
details.Add(fileInfo);
}
doc.Add(details);
doc.Save(xw);
xmlResult += doc.ToString();
xmlResult += Environment.NewLine;
} }
}
doc.Add(details); if (Settings.MovieMetadataURL)
doc.Save(xw); {
xmlResult += "https://www.themoviedb.org/movie/" + movie.TmdbId;
xmlResult += doc.ToString();
xmlResult += Environment.NewLine; xmlResult += Environment.NewLine;
xmlResult += "https://www.imdb.com/title/" + movie.ImdbId;
xmlResult += Environment.NewLine;
} }
var metadataFileName = GetMovieMetadataFilename(movieFile.RelativePath); var metadataFileName = GetMovieMetadataFilename(movieFile.RelativePath);
if (Settings.UseMovieNfo) return xmlResult == string.Empty ? null : new MetadataFileResult(metadataFileName, xmlResult.Trim(Environment.NewLine.ToCharArray()));
{
metadataFileName = "movie.nfo";
}
return new MetadataFileResult(metadataFileName, xmlResult.Trim(Environment.NewLine.ToCharArray()));
} }
public override List<ImageFileResult> MovieImages(Movie movie) public override List<ImageFileResult> MovieImages(Movie movie)
@@ -243,7 +277,14 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
private string GetMovieMetadataFilename(string movieFilePath) private string GetMovieMetadataFilename(string movieFilePath)
{ {
return Path.ChangeExtension(movieFilePath, "nfo"); if (Settings.UseMovieNfo)
{
return Path.Combine(Path.GetDirectoryName(movieFilePath), "movie.nfo");
}
else
{
return Path.ChangeExtension(movieFilePath, "nfo");
}
} }
private bool GetExistingWatchedStatus(Movie movie, string movieFilePath) private bool GetExistingWatchedStatus(Movie movie, string movieFilePath)
@@ -19,6 +19,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
public XbmcMetadataSettings() public XbmcMetadataSettings()
{ {
MovieMetadata = true; MovieMetadata = true;
MovieMetadataURL = true;
MovieImages = true; MovieImages = true;
UseMovieNfo = false; UseMovieNfo = false;
} }
@@ -26,10 +27,13 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
[FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)] [FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)]
public bool MovieMetadata { get; set; } public bool MovieMetadata { get; set; }
[FieldDefinition(1, Label = "Movie Images", Type = FieldType.Checkbox)] [FieldDefinition(1, Label = "Movie Metadata URL", Type = FieldType.Checkbox, HelpText = "Radarr will write the tmdb/imdb url in the .nfo file")]
public bool MovieMetadataURL { get; set; }
[FieldDefinition(2, Label = "Movie Images", Type = FieldType.Checkbox)]
public bool MovieImages { get; set; } public bool MovieImages { get; set; }
[FieldDefinition(2, Label = "Use Movie.nfo", Type = FieldType.Checkbox, HelpText = "Radarr will write metadata to movie.nfo instead of the default <movie-filename>.nfo")] [FieldDefinition(3, Label = "Use Movie.nfo", Type = FieldType.Checkbox, HelpText = "Radarr will write metadata to movie.nfo instead of the default <movie-filename>.nfo")]
public bool UseMovieNfo { get; set; } public bool UseMovieNfo { get; set; }
public bool IsValid => true; public bool IsValid => true;
+4 -1
View File
@@ -2,7 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
#if !LIBRARY
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
#endif
namespace NzbDrone.Core namespace NzbDrone.Core
{ {
@@ -10,8 +12,9 @@ namespace NzbDrone.Core
{ {
public static string WithDefault(this string actual, object defaultValue) public static string WithDefault(this string actual, object defaultValue)
{ {
#if !LIBRARY
Ensure.That(defaultValue, () => defaultValue).IsNotNull(); Ensure.That(defaultValue, () => defaultValue).IsNotNull();
#endif
if (string.IsNullOrWhiteSpace(actual)) if (string.IsNullOrWhiteSpace(actual))
{ {
return defaultValue.ToString(); return defaultValue.ToString();
@@ -0,0 +1,32 @@
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.PassThePopcorn;
namespace NzbDrone.Core.HealthCheck.Checks
{
public class PTPOldSettingsCheck : HealthCheckBase
{
private readonly IIndexerFactory _indexerFactory;
public PTPOldSettingsCheck(IIndexerFactory indexerFactory)
{
_indexerFactory = indexerFactory;
}
public override HealthCheck Check()
{
var ptpIndexers = _indexerFactory.All().Where(i => i.Settings.GetType() == typeof(PassThePopcornSettings));
var ptpIndexerOldSettings = ptpIndexers
.Where(i => (i.Settings as PassThePopcornSettings).APIUser.IsNullOrWhiteSpace()).Select(i => i.Name);
if (ptpIndexerOldSettings.Count() > 0)
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, $"The following PassThePopcorn indexers have deprecated settings and should be updated: {string.Join(",", ptpIndexerOldSettings)}");
}
return new HealthCheck(GetType());
}
}
}
@@ -16,7 +16,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder; private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
public ProxyCheck(ISonarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger) public ProxyCheck(IRadarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger)
{ {
_configService = configService; _configService = configService;
_client = client; _client = client;
@@ -30,37 +30,37 @@ namespace NzbDrone.Core.History
public List<QualityModel> GetBestQualityInHistory(int movieId) public List<QualityModel> GetBestQualityInHistory(int movieId)
{ {
var history = Query.Where(c => c.MovieId == movieId); var history = Query(q => q.Where(c => c.MovieId == movieId).ToList());
return history.Select(h => h.Quality).ToList(); return history.Select(h => h.Quality).ToList();
} }
public History MostRecentForDownloadId(string downloadId) public History MostRecentForDownloadId(string downloadId)
{ {
return Query.Where(h => h.DownloadId == downloadId) return Query(q => q.Where(h => h.DownloadId == downloadId)
.OrderByDescending(h => h.Date) .OrderByDescending(h => h.Date)
.FirstOrDefault(); .FirstOrDefault());
} }
public List<History> FindByDownloadId(string downloadId) public List<History> FindByDownloadId(string downloadId)
{ {
return Query.Where(h => h.DownloadId == downloadId); return Query(q => q.Where(h => h.DownloadId == downloadId).ToList());
} }
public List<History> FindDownloadHistory(int idMovieId, QualityModel quality) public List<History> FindDownloadHistory(int idMovieId, QualityModel quality)
{ {
return Query.Where(h => return Query(q => q.Where(h =>
h.MovieId == idMovieId && h.MovieId == idMovieId &&
h.Quality == quality && h.Quality == quality &&
(h.EventType == HistoryEventType.Grabbed || (h.EventType == HistoryEventType.Grabbed ||
h.EventType == HistoryEventType.DownloadFailed || h.EventType == HistoryEventType.DownloadFailed ||
h.EventType == HistoryEventType.DownloadFolderImported) h.EventType == HistoryEventType.DownloadFolderImported)
).ToList(); ).ToList());
} }
public List<History> FindByMovieId(int movieId) public List<History> FindByMovieId(int movieId)
{ {
return Query.Where(h => h.MovieId == movieId); return Query(q => q.Where(h => h.MovieId == movieId).ToList());
} }
public void DeleteForMovie(int movieId) public void DeleteForMovie(int movieId)
@@ -77,9 +77,9 @@ namespace NzbDrone.Core.History
public History MostRecentForMovie(int movieId) public History MostRecentForMovie(int movieId)
{ {
return Query.Where(h => h.MovieId == movieId) return Query(q => q.Where(h => h.MovieId == movieId)
.OrderByDescending(h => h.Date) .OrderByDescending(h => h.Date)
.FirstOrDefault(); .FirstOrDefault());
} }
} }
} }
@@ -13,9 +13,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN ( WHERE Id IN (
SELECT Id FROM MetadataFiles SELECT Id FROM MetadataFiles
WHERE RelativePath WHERE RelativePath
@@ -25,6 +25,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
OR RelativePath OR RelativePath
LIKE '/%' LIKE '/%'
)"); )");
}
} }
} }
} }
@@ -13,12 +13,14 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig
WHERE ID NOT IN ( WHERE ID NOT IN (
SELECT ID FROM NamingConfig SELECT ID FROM NamingConfig
LIMIT 1)"); LIMIT 1)");
}
} }
} }
} }
@@ -13,13 +13,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM Users mapper.ExecuteNonQuery(@"DELETE FROM Users
WHERE ID NOT IN ( WHERE ID NOT IN (
SELECT ID FROM Users SELECT ID FROM Users
LIMIT 1)"); LIMIT 1)");
} }
}
} }
} }
@@ -19,28 +19,32 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
private void DeleteDuplicateMovieMetadata() private void DeleteDuplicateMovieMetadata()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN ( WHERE Id IN (
SELECT Id FROM MetadataFiles SELECT Id FROM MetadataFiles
WHERE Type = 1 WHERE Type = 1
GROUP BY MovieId, Consumer GROUP BY MovieId, Consumer
HAVING COUNT(MovieId) > 1 HAVING COUNT(MovieId) > 1
)"); )");
}
} }
private void DeleteDuplicateMovieFileMetadata() private void DeleteDuplicateMovieFileMetadata()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN ( WHERE Id IN (
SELECT Id FROM MetadataFiles SELECT Id FROM MetadataFiles
WHERE Type = 1 WHERE Type = 1
GROUP BY MovieFileId, Consumer GROUP BY MovieFileId, Consumer
HAVING COUNT(MovieFileId) > 1 HAVING COUNT(MovieFileId) > 1
)"); )");
}
} }
} }
} }
@@ -13,14 +13,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM AlternativeTitles mapper.ExecuteNonQuery(@"DELETE FROM AlternativeTitles
WHERE Id IN ( WHERE Id IN (
SELECT AlternativeTitles.Id FROM AlternativeTitles SELECT AlternativeTitles.Id FROM AlternativeTitles
LEFT OUTER JOIN Movies LEFT OUTER JOIN Movies
ON AlternativeTitles.MovieId = Movies.Id ON AlternativeTitles.MovieId = Movies.Id
WHERE Movies.Id IS NULL)"); WHERE Movies.Id IS NULL)");
}
} }
} }
} }
@@ -13,14 +13,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM Blacklist mapper.ExecuteNonQuery(@"DELETE FROM Blacklist
WHERE Id IN ( WHERE Id IN (
SELECT Blacklist.Id FROM Blacklist SELECT Blacklist.Id FROM Blacklist
LEFT OUTER JOIN Movies LEFT OUTER JOIN Movies
ON Blacklist.MovieId = Movies.Id ON Blacklist.MovieId = Movies.Id
WHERE Movies.Id IS NULL)"); WHERE Movies.Id IS NULL)");
}
} }
} }
} }
@@ -18,14 +18,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
private void CleanupOrphanedByMovie() private void CleanupOrphanedByMovie()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM History mapper.ExecuteNonQuery(@"DELETE FROM History
WHERE Id IN ( WHERE Id IN (
SELECT History.Id FROM History SELECT History.Id FROM History
LEFT OUTER JOIN Movies LEFT OUTER JOIN Movies
ON History.MovieId = Movies.Id ON History.MovieId = Movies.Id
WHERE Movies.Id IS NULL)"); WHERE Movies.Id IS NULL)");
}
} }
} }
} }
@@ -13,14 +13,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus
WHERE Id IN ( WHERE Id IN (
SELECT IndexerStatus.Id FROM IndexerStatus SELECT IndexerStatus.Id FROM IndexerStatus
LEFT OUTER JOIN Indexers LEFT OUTER JOIN Indexers
ON IndexerStatus.IndexerId = Indexers.Id ON IndexerStatus.IndexerId = Indexers.Id
WHERE Indexers.Id IS NULL)"); WHERE Indexers.Id IS NULL)");
}
} }
} }
} }
@@ -20,38 +20,44 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
private void DeleteOrphanedByMovie() private void DeleteOrphanedByMovie()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN ( WHERE Id IN (
SELECT MetadataFiles.Id FROM MetadataFiles SELECT MetadataFiles.Id FROM MetadataFiles
LEFT OUTER JOIN Movies LEFT OUTER JOIN Movies
ON MetadataFiles.MovieId = Movies.Id ON MetadataFiles.MovieId = Movies.Id
WHERE Movies.Id IS NULL)"); WHERE Movies.Id IS NULL)");
}
} }
private void DeleteOrphanedByMovieFile() private void DeleteOrphanedByMovieFile()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN ( WHERE Id IN (
SELECT MetadataFiles.Id FROM MetadataFiles SELECT MetadataFiles.Id FROM MetadataFiles
LEFT OUTER JOIN MovieFiles LEFT OUTER JOIN MovieFiles
ON MetadataFiles.MovieFileId = MovieFiles.Id ON MetadataFiles.MovieFileId = MovieFiles.Id
WHERE MetadataFiles.MovieFileId > 0 WHERE MetadataFiles.MovieFileId > 0
AND MovieFiles.Id IS NULL)"); AND MovieFiles.Id IS NULL)");
}
} }
private void DeleteWhereMovieFileIsZero() private void DeleteWhereMovieFileIsZero()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN ( WHERE Id IN (
SELECT Id FROM MetadataFiles SELECT Id FROM MetadataFiles
WHERE Type IN (1, 2) WHERE Type IN (1, 2)
AND MovieFileId = 0)"); AND MovieFileId = 0)");
}
} }
} }
} }
@@ -13,14 +13,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM MovieFiles mapper.ExecuteNonQuery(@"DELETE FROM MovieFiles
WHERE Id IN ( WHERE Id IN (
SELECT MovieFiles.Id FROM MovieFiles SELECT MovieFiles.Id FROM MovieFiles
LEFT OUTER JOIN Movies LEFT OUTER JOIN Movies
ON MovieFiles.Id = Movies.MovieFileId ON MovieFiles.Id = Movies.MovieFileId
WHERE Movies.Id IS NULL)"); WHERE Movies.Id IS NULL)");
}
} }
} }
} }
@@ -13,14 +13,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
WHERE Id IN ( WHERE Id IN (
SELECT PendingReleases.Id FROM PendingReleases SELECT PendingReleases.Id FROM PendingReleases
LEFT OUTER JOIN Movies LEFT OUTER JOIN Movies
ON PendingReleases.MovieId = Movies.Id ON PendingReleases.MovieId = Movies.Id
WHERE Movies.Id IS NULL)"); WHERE Movies.Id IS NULL)");
}
} }
} }
} }
@@ -17,16 +17,18 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
var mapper = _database.GetDataMapper(); using (var mapper = _database.GetDataMapper())
{
var usedTags = new[] { "Movies", "Notifications", "DelayProfiles", "Restrictions" } var usedTags = new[] {"Movies", "Notifications", "DelayProfiles", "Restrictions", "NetImport"}
.SelectMany(v => GetUsedTags(v, mapper)) .SelectMany(v => GetUsedTags(v, mapper))
.Distinct() .Distinct()
.ToArray(); .ToArray();
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray()); var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})"); mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})");
}
} }
private int[] GetUsedTags(string table, IDataMapper mapper) private int[] GetUsedTags(string table, IDataMapper mapper)

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