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

Compare commits

...

182 Commits

Author SHA1 Message Date
Taloth Saldono
7818f0c59b Fixed: Don't purge xem scene mapping cache when new series gets added. 2016-03-11 17:31:06 +01:00
Taloth Saldono
03e2adc332 Sort episodes in calendar by ep nr if airdate is the same. 2016-03-11 16:54:41 +01:00
Taloth Saldono
e6ab4196de Revert "Fixed: Sort episodes on the api by episode number when they air at the same time."
This reverts commit 9ffc0ec521.
2016-03-11 16:53:22 +01:00
Mark McDowall
f2784d3ec2 New: Trakt links on series details 2016-03-10 20:48:24 -08:00
Mark McDowall
ef3d508b31 Fixed: Anime season search won't search for missing episodes 2016-03-10 20:24:07 -08:00
Taloth Saldono
9ffc0ec521 Fixed: Sort episodes on the api by episode number when they air at the same time. 2016-03-06 00:43:04 +01:00
Mark McDowall
b598add64e Updated FluentValidation 2016-03-04 00:39:17 -08:00
Mark McDowall
47446515d1 Update CONTRIBUTING.md 2016-03-02 10:03:37 -08:00
Taloth Saldono
e5de7fb8cf Fixed: Don't trigger SceneMapping update and Housekeeping right on the startup event. 2016-03-01 23:50:09 +01:00
Taloth Saldono
55e870f295 Added version to logged exceptions. 2016-03-01 22:05:34 +01:00
Taloth Saldono
1485c83ab6 Don't hammer thexem, kthxbai. 2016-03-01 22:05:23 +01:00
Mark McDowall
e8d1623e96 Default Plex Media Server "Update Library" to true 2016-02-25 19:19:31 -08:00
Taloth Saldono
3bc6bf9e99 Don't die in MonoTorrent if nodes is an empty string. 2016-02-25 21:40:55 +01:00
Taloth Saldono
949d8bf49b Fixed: Warn if user has movie/date sorting enabled in Sabnzbd for the Sonarr category. 2016-02-25 20:39:42 +01:00
Taloth Saldono
c29e49da95 Clarified error message in MatchesFolderSpecification. 2016-02-25 19:44:29 +01:00
Taloth Saldono
79c565911c New: Changed the default of 'Use Hardlinks instead of Copy' to true. Most ppl seem to want that anyway. 2016-02-25 19:19:02 +01:00
Taloth Saldono
bb9a0371c5 Added TorrentRss test for ExtraTorrents, no other changes. 2016-02-25 19:18:46 +01:00
Taloth Saldono
e945231ab3 Fixed: Newznab should reject a Torznab feed. 2016-02-21 11:37:01 +01:00
Mark McDowall
c1298d162e Womble's has size parsing now 2016-02-18 18:39:23 -08:00
Taloth Saldono
f005edfcf0 Don't use Sonarr as ReleaseGroup if the pattern contains an advanced prefix/suffix. 2016-02-18 23:32:38 +01:00
Taloth Saldono
59c68ec6cc Write debug/trace log files separately to prevent trace from quickly rolling over debug. 2016-02-18 23:32:37 +01:00
Taloth Saldono
a5077b0b1b Replaced <removed> with (removed) for the log cleanser so it doesn't mess with forums. 2016-02-18 23:32:36 +01:00
Mark McDowall
a22c0499d5 Couple more anime version test cases 2016-02-17 23:06:44 -08:00
Mark McDowall
c38608e3cf Fixed Protocol returned for release/push endpoint 2016-02-17 20:26:37 -08:00
Taloth Saldono
3b57194d47 Fixed: Parses size in Wombles Description field so min/maxsize checks works on Wombles feed. 2016-02-17 23:22:22 +01:00
Taloth Saldono
21c901eab4 fixed failing torznab test 2016-02-17 21:52:55 +01:00
Taloth Saldono
9ad8311dd6 New: Use PageSize reported by newznab/torznab caps instead of hardcoded 100.
ref Jackett/Jackett#27
2016-02-17 21:23:27 +01:00
Taloth Saldono
df84028c90 Added DrunkenSlug and SimplyNZBs as Newznab presets. 2016-02-17 21:23:26 +01:00
Mark McDowall
7cb1e91ba1 Handle 1.1x version from Sabnzbd 2016-02-16 23:49:13 -08:00
Mark McDowall
974a7276c3 New: Explicitly enforce SABnzbd minimum version of 0.7.0 2016-02-16 22:40:12 -08:00
Mark McDowall
f0ca2bc11e Fixed: Don't collapse episode titles when episode titles contain Part x only 2016-02-16 21:28:32 -08:00
Mark McDowall
cb43888496 Fixed: Use Protocol over DownloadProtocol for ReleasePushModule
DownloadProtocol is still supported for backwards compatibility
2016-02-16 20:21:20 -08:00
Taloth Saldono
34d5fb1aa0 Fiddled with the Back to the Top button a bit so it's better visible on the white background, also only on widescreen now. 2016-02-15 22:56:10 +01:00
Taloth Saldono
72f0085ef7 Fixed: DownloadedEpisodesScan API command couldn't be used to process individual files. 2016-02-15 21:55:53 +01:00
Taloth Saldono
f25f5abced Cleaned up 2160p changes and added migration and tests.
Also reserved the quality ids for WEBRip etc.
2016-02-14 00:08:42 +01:00
Björn Dahlgren
94323f79e7 New: Added support for UltraHD (2160p) quality 2016-02-14 00:08:15 +01:00
Taloth Saldono
bdb1076100 Updated db migration testing framework so we only run migrations up to the one we're testing.
fixes #902
2016-02-13 23:21:32 +01:00
Mark McDowall
8818e39c63 Fixed: Daily + Standard with 3 digit episode numbers
Closes #1145
2016-02-11 18:46:32 -08:00
Taloth Saldono
6a90035a4c Updated NLog to v4.2.3. 2016-02-11 22:15:12 +01:00
Taloth Saldono
e01b2ef25c Fixed some compile warnings. 2016-02-11 22:11:53 +01:00
Taloth Saldono
91d91bc673 Fixed: Sample files of daily episodes should also be deleted after import. 2016-02-11 21:27:41 +01:00
Taloth Saldono
1c92ea58da Fixed: Replaced mono symlink resolve logic to better handle errors. 2016-02-11 21:12:42 +01:00
Taloth Saldono
bd6a38173e Add another nn preset. 2016-02-11 20:11:59 +01:00
Mark McDowall
5d05a85411 Delete the subfolder not the parent folder 2016-02-11 00:54:52 -08:00
Mark McDowall
1a5eafd2b1 New: Remove empty subfolders after renaming FileSetLastWriteTime
Closes #490
2016-02-11 00:38:44 -08:00
Mark McDowall
1603b06431 New: Prefer regular episodes over specials when absolute numbers conflict
Closes #676
2016-02-11 00:09:29 -08:00
Mark McDowall
2396af4589 New: Pushover Silent and Emergency priorities
Closes #878
2016-02-10 23:43:40 -08:00
Taloth Saldono
262b8daec1 Revert "Fixed regression, mono should resolve symlinks while trying to find out the available/total space."
This reverts commit d33efe59fc.
2016-02-11 01:52:47 +01:00
Taloth Saldono
d33efe59fc Fixed regression, mono should resolve symlinks while trying to find out the available/total space. 2016-02-11 01:14:16 +01:00
Taloth Saldono
5550565d6a Fixed: Manual Import didn't revert to parent folder when trying to parse series leading to issues with obfuscated releases. 2016-02-10 00:15:01 +01:00
Taloth Saldono
68540cb479 Certain log messages didn't include the exception. 2016-02-07 00:23:13 +01:00
Taloth Saldono
4038fa6907 Ignore -Obfuscated while parsing. 2016-02-06 10:43:56 +01:00
Taloth Saldono
f96f997506 Fixed: Handling xml responses containing invalid html entities.
fixes #1123
2016-02-05 20:08:54 +01:00
Taloth Saldono
4e84d1a17c Fixed: Throw more specific error when there's an issue with the curl root certificate bundle. 2016-02-02 22:11:39 +01:00
Taloth Saldono
97cdb6a4a5 Fixed: ZFS and other mounts now listed in the System page.
Will now also automatically revert to a fully transactional move/copy if the move is in our out of a cifs mount. (assuming the cifs mount can be detected)
2016-02-02 22:11:37 +01:00
Mark McDowall
f5b3d70641 Merge pull request #1112 from cturra/nzbplanet-api
updating nzbplanet.net api url to reflect recent change
2016-01-30 23:33:06 -08:00
cturra
db66d3da9e updating nzbplanet.net api url to reflect recent change 2016-01-30 21:27:18 -08:00
Taloth Saldono
c20e0667d4 Added additional tier to search for daily series type on BTN to find faux-daily series with SxxExx groups instead of date groups.
fixes #1111
2016-01-30 21:16:45 +01:00
Mark McDowall
4771b1c1b2 New: Parsing of XofY mini series format
Closes #1091
2016-01-30 11:16:59 -08:00
Mark McDowall
8e07a39ec0 Merge pull request #1095 from ta264/fix-build-sh
xbuild doesn't support /m parameter
2016-01-28 14:52:19 -08:00
Mark McDowall
9895b5bc1d Merge pull request #1100 from IvanBrazza/develop
Pushbullet: Source device support
2016-01-28 14:49:47 -08:00
Mark McDowall
e89a1bc0fb Fixed: Delete confirmation message for Restriction
Closes #1102
2016-01-28 14:04:10 -08:00
Ivan Brazza
9b0654c7f2 New: Setting Pushbullet source device 2016-01-28 21:33:11 +00:00
Mark McDowall
56da824e98 AutoComplete and file browser will show files when appropriate
Fixed: File browser for Custom Script shows files
Closes #1084
2016-01-27 19:24:41 -08:00
ta264
f84b7904e6 Fix: xbuild doesn't support /m parameter 2016-01-27 14:11:29 +00:00
Mark McDowall
9f523bb167 New: Prevent automatic update if UI folder is not writable 2016-01-24 19:22:07 -08:00
Taloth Saldono
f38d5de946 Fixed: Regression in parser incorrectly parsing S2015Exx.2015-01-01 notation.
fixes #1080
2016-01-21 22:43:48 +01:00
Taloth Saldono
de379b2e47 Do or do not, there is no try. 2016-01-21 21:10:22 +01:00
Taloth Saldono
64e90f35c8 Ensure rTorrent download is started even if the user doesn't have schedule=...,start_tied= in their rtorrent.rc. 2016-01-21 08:40:10 +01:00
Taloth Saldono
bbfe8c27c6 Fixed: Misleading error message when Kickass/Torrent Rss indexer returned invalid xml.
fixes #1058
2016-01-20 21:58:23 +01:00
Taloth Saldono
2f50074123 Fixed: Incorrect api error when calling /api/episode without seriesId queryparam.
fixes #1070
2016-01-20 21:58:02 +01:00
Taloth Saldono
5cfaed7b26 Fixed: Added support for Sabnzbd 0.8 history category queryparam.
fixes #1077
2016-01-20 21:57:40 +01:00
Taloth Saldono
0d19f645e8 Fixed: Don't apply indexer backoff on DNS and connection issues.
Fixes #751
2016-01-20 21:57:39 +01:00
Taloth Saldono
45d4371328 Fixed: Additional log cleanse Regex to keep even more sensitive information out of the logs.
fixes #1051
2016-01-20 21:57:10 +01:00
Taloth Saldono
889933cb41 New: Set full Download Directory in Transmission instead of just a Category.
fixes #744
2016-01-20 21:26:59 +01:00
Taloth Saldono
958b294152 Updated URL rewriter to handle torcache Referer weirdness. 2016-01-20 21:26:58 +01:00
Taloth Saldono
cf25097cd1 Fixed: Magnet downloads weren't being started on RTorrent. 2016-01-20 21:26:57 +01:00
Taloth Saldono
902e0dd5d6 Updated SharpZipLib to include patches made since the last official release. 2016-01-20 21:16:02 +01:00
Keivan Beigi
d943551a7f fixed build.sh 2016-01-16 16:39:45 -08:00
Keivan Beigi
46304b8a71 use build config to exclude xml doc rather than deleting them later 2016-01-16 16:36:40 -08:00
Keivan Beigi
6e2fc186ca added nuget.exe to tools 2016-01-16 16:19:28 -08:00
Keivan Beigi
845689401d removed msbuild integerated nuget restore 2016-01-16 16:17:36 -08:00
Keivan Beigi
4fb9cc5e8d cleanup app.manifest for Service helpers, upgraded compat to windows 8.1 2016-01-16 15:52:00 -08:00
Keivan Beigi
487581a01a apparently new compilers alraedy embed the app.manifest into the app, no need for mt.exe anymore 2016-01-16 15:51:26 -08:00
Keivan Beigi
2bc771d91e Merge pull request #1066 from pra85/2016
Update year range to 2016
2016-01-16 00:37:43 -08:00
Keivan Beigi
8bd7969328 cleanup 2016-01-16 00:36:43 -08:00
Keivan Beigi
5876ab487c fixed gulp build 2016-01-15 22:13:00 -08:00
Keivan Beigi
9c14ca0f39 Replaced build.ps1 with warning 2016-01-15 22:06:33 -08:00
Keivan Beigi
81ca352b2f smarted mdb generation 2016-01-15 21:50:09 -08:00
Keivan Beigi
0edfed5b95 upgraded pdb2mdb.exe to mono 4.2 Stable (4.2.1.102) 2016-01-15 21:50:09 -08:00
Prayag Verma
0ff053415c Update year range to 2016 2016-01-16 09:40:24 +05:30
Mark McDowall
a0ee607ae6 Remove double slash in NZBVortex add URL 2016-01-15 09:04:18 -08:00
Keivan Beigi
2723e2a7b8 faster test packaging in build.sh 2016-01-15 00:27:59 -08:00
Keivan Beigi
6e105ce2c6 upgraded nuget packages 2016-01-14 23:07:39 -08:00
Keivan Beigi
9fc7fceda4 updated npm packages 2016-01-14 18:56:14 -08:00
Mark McDowall
2b72c0e328 Fixed: Manual Import Series selection
Closes #996
2016-01-09 13:16:34 -08:00
Mark McDowall
536aa350f0 Merge pull request #960 from Sonarr/nzb-vortex
NZBVortex Download Client
2016-01-07 15:53:57 -08:00
Mark McDowall
7c382c0e0c NZBVortex Download Client
New: NZBVortex Download Client
Closes #360
2016-01-07 15:50:52 -08:00
Mark McDowall
dda0d3259f Fixed broken test 2016-01-07 15:49:51 -08:00
Mark McDowall
a96718f7b3 Fixed Twitter notifications
New: Twitter notifications now require a Twitter (see settings for details)

Closes #1049
2016-01-06 22:32:12 -08:00
Mark McDowall
7ca67fe57a New: Special searching on RARBG 2016-01-05 23:07:49 -08:00
Mark McDowall
8373024f9d Fixed: Parsing of queued specials from download client queue 2016-01-05 22:56:34 -08:00
Mark McDowall
b62ef0c40c Fixed: Use folder quality when better than file quality (regression)
Closes #1022
2016-01-04 23:12:14 -08:00
Mark McDowall
376481eda5 Merge pull request #981 from Sonarr/ical-parameters
iCal parameters
2016-01-02 10:31:12 -08:00
Mark McDowall
800fa42982 Selectable range for iCal
New: Support for pastDays and fututeDays query parameters on iCal requests
Closes #974
2016-01-02 10:21:06 -08:00
Taloth Saldono
4e728c3a02 Increased timeout for Deluge to prevent timeout errors when posting large torrent files. 2015-12-30 22:50:17 +01:00
Taloth Saldono
663d254ced Fixed Ospath incorrectly detecting arbitrary colon as windows path. 2015-12-30 21:01:29 +01:00
vawen
8753c232c7 Fix: Paths with colons prevent Sonarr from communicating with Transmission
Issue #954
2015-12-29 13:25:36 +01:00
Taloth Saldono
221f3ef08c Safety net to handle MaxSize=0. Some users still have 0 = unlimited in their db and migration failed for them. 2015-12-28 20:24:24 +01:00
Taloth Saldono
b59175a87c Fixed: Indexer sites returning date as Retry-After header.
fixes #994
2015-12-28 20:24:23 +01:00
Taloth Saldono
37c621dcdb Fixed: Health Check produced warning if Sonarr binaries folder was not writable even when the external script update mechanism was selected.
fixes #964
2015-12-28 20:24:22 +01:00
Taloth Saldono
eaf3228bb7 Fixed Regex mistake in CleanLogMessage. 2015-12-28 20:24:21 +01:00
Jake Pusateri
c8debbf470 New: Better resolution posters on retina screens. 2015-12-28 11:12:12 -08:00
Mark McDowall
8dcd8d17b5 Fixed: Username must not be null or empty when logging in 2015-12-27 00:59:28 -08:00
Mark McDowall
cfe121c777 Don't return series as subtype for /api/episode
Fixed: Sped up loading episodes from server
2015-12-27 00:45:20 -08:00
Mark McDowall
b4f83d8a4e New: Media file extension .webm 2015-12-25 18:35:42 -08:00
Mark McDowall
9039d7e694 Fixed: Show a better error message when no episodes are parsed in a release 2015-12-25 18:32:03 -08:00
Taloth Saldono
7a25717da6 Fixed donate button. 2015-12-24 19:51:36 +01:00
Taloth Saldono
7e1c444c02 Fixed: Curl Fallback should ignore invalid cookies. 2015-12-24 19:30:16 +01:00
Mark McDowall
dc3f7c9bda Fixed: Improved parsing for single digit multi-episode titles
Closes #965
2015-12-20 00:11:41 -08:00
Mark McDowall
de754169fb Fixed: RSS Sync Interval validation 2015-12-19 18:02:38 -08:00
Mark McDowall
2d3c3bbb0c Changed torrent blackhole message 2015-12-19 17:49:54 -08:00
Mark McDowall
15cefe4a43 New: Option to Hardlink or Copy instead of move for Torrent Blackhole
Closes #1011
2015-12-19 17:18:22 -08:00
Mark McDowall
95da301975 A few UI Fixes
Closes #1009
Closes #1010
2015-12-18 23:41:30 -08:00
Mark McDowall
e03906b294 Fixed: Hardlink/Copy files from QBittorrent 2015-12-17 22:16:38 -08:00
Mark McDowall
7921dd3f96 Fixed: Removed Titans of TV tracker
Closes #992
2015-12-17 00:46:02 -08:00
Mark McDowall
9f066f7a6b New: Newznab preset for Usenet Crawler 2015-12-17 00:24:51 -08:00
Mark McDowall
81d131e732 Merge pull request #779 from cbodley/qbittorrent
Download client for qBittorrent
2015-12-15 23:10:20 -08:00
Casey Bodley
0552b56b71 qbittorrent: client plugin based heavily on uTorrent
supports a minimum qBittorrent version of 3.2.4, and uses labels for
v3.3.0 and later

Signed-off-by: Casey Bodley <cbodley@gmail.com>
2015-12-14 00:20:14 -05:00
Keivan Beigi
3952ee402b Update readme.md 2015-12-12 11:24:46 -08:00
Mark McDowall
0b3e27cb44 Don't keep dylibs for WIndows and Linux builds 2015-12-09 23:24:50 -08:00
Mark McDowall
4fa4b3507e Fixed: Force grabbing some delayed releases
Closes #984
2015-12-08 22:50:34 -08:00
Mark McDowall
8c211364e2 Fixed: Improved parsing of some multi-episode filenames 2015-12-08 15:26:52 -08:00
Mark McDowall
2d9917d074 Re-order regex to prefer [1x01] over 101 2015-12-06 11:03:11 -08:00
Mark McDowall
d514699ab7 Fixed: Prevent series from being added with an invalid Profile ID
Closes #977
2015-12-05 02:22:22 -08:00
Mark McDowall
dc176a83b3 Update CONTRIBUTING.md 2015-12-01 08:48:41 -08:00
Mark McDowall
69e3516a89 New: Allow Uppercase in Transmission category
Closes #934
2015-11-29 22:01:20 -08:00
Mark McDowall
c8a0f9fa7a Fixed: Saving settings changes 2015-11-26 12:05:37 -08:00
Mark McDowall
c2b9504b15 Merge pull request #931 from Dahlgren/osx-development
Include mediainfo and sqlite3 libraries for Mac
2015-11-24 18:35:07 -08:00
Mark McDowall
2693a3df2e Merge pull request #959 from roguecode/develop
Fixed: Indexer failure log message with local time
2015-11-24 15:41:57 -08:00
Matt
8062466ab8 Changing Indexer failure log message to local from UTC. 2015-11-24 23:42:20 +02:00
Björn Dahlgren
6cde1dd5ae Include mediainfo and sqlite3 libraries for Mac
Enables usage within MonoDevelop and Xamarin Studio including NUnit
2015-11-24 10:21:42 +01:00
Mark McDowall
b6c4a97675 Merge pull request #889 from Sonarr/quality-source
Folder quality when file quality determined by its extension
2015-11-23 23:01:10 -08:00
Mark McDowall
a9444cef30 Fixed: Folder quality when file quality determined by its extension
Closes #603
2015-11-23 23:00:51 -08:00
Mark McDowall
bf217a7093 Merge pull request #754 from Sonarr/real-releases
Support for REAL releases
2015-11-23 22:59:41 -08:00
Mark McDowall
b6b5355261 New: support for REAL releases
Closes #453

New: Added `Quality Real` naming Token
New: Quality Full will add real to file name when applicable
2015-11-23 22:58:53 -08:00
Mark McDowall
bc37084ec4 Merge pull request #928 from Dahlgren/mono-tests
Fixed tests for Mono
2015-11-23 22:50:34 -08:00
Mark McDowall
0a1a30f2af Merge pull request #953 from zetas/nn_preset_nzbcat
New: Newznab Preset for NZBCat
2015-11-23 22:50:15 -08:00
Keivan Beigi
7e023a7944 ConfigServiceFixture shouldn't be touching the DB. 2015-11-23 21:57:01 -08:00
zetas
91f68de8a7 Adding new newznab preset for NZBCat 2015-11-22 07:09:57 -05:00
Björn Dahlgren
994e2a6c57 Fixed failing tests on Mono
Test case unicode characters in escaped format
2015-11-22 01:11:43 +01:00
Mark McDowall
04da2d845a Merge pull request #941 from uzegonemad/hotfix/calendar-legend-width
Give calendar legend ul max width of 100%. Fixes #922
2015-11-18 22:12:39 -08:00
Benjamin Uzelac
d3b87bc3e8 give legend ul max width of 100%
give legend ul max width of 100%
2015-11-18 22:17:05 -06:00
Mark McDowall
554c81f251 Merge pull request #901 from brgaulin/jquery_upgrade
Update jQuery to 1.11.3
2015-11-16 13:44:42 -08:00
Mark McDowall
6de3f9dd0b Merge pull request #942 from uzegonemad/hotfix/calendar-tooltip
Fixed: Hidden calendar tooltips
2015-11-16 13:38:30 -08:00
Mark McDowall
e9692d5b9c Merge pull request #940 from uzegonemad/hotfix/fix-long-path-label
Fixed: Add wrapping to fix long paths in labels
2015-11-16 13:37:26 -08:00
Benjamin Uzelac
1a74990e9b alter calendar tooltip container. fixes #857 2015-11-15 20:50:17 -06:00
Benjamin Uzelac
3b9ac8699d Add wrapping to fix long paths in labels. Fixes #875 2015-11-15 18:25:30 -06:00
Mark McDowall
ea6ae85f7a Fixed: Logging invalid version when failing to connect to Kodi
Closes #927
2015-11-14 22:35:45 -08:00
Mark McDowall
b02b9f026f Fixed: Failing missing episode search when one search fails
Closes #917
2015-11-14 22:35:28 -08:00
Mark McDowall
c419e7b710 Merge pull request #915 from uzegonemad/hotfix/scroll-up-zindex
Fix z-index on scroll to top button
2015-11-14 21:43:37 -08:00
Benjamin Uzelac
cd9132520d fix z-index on scroll to top button 2015-11-05 19:43:53 -06:00
Mark McDowall
08d19df3f7 Fixed: Manual import when quality was not available after failed parsing
Closes #911
2015-11-03 15:12:56 -08:00
Mark McDowall
b34879b4f6 New: Manual search shows error when download fails 2015-11-02 22:46:06 -08:00
Mark McDowall
6b9c4af591 Fixed: Magnet links with torrent blackhole 2015-11-02 22:21:27 -08:00
Mark McDowall
c00c207517 Prevent regrab for all grabs
Fixed: Prevent incorrectly grabbing a similar or identical release for 12 hours
2015-11-01 21:54:49 -08:00
Mark McDowall
6f7fea3591 PFMonkey Newznab preset 2015-11-01 21:35:44 -08:00
Mark McDowall
83eebfe153 Fixed: Stricter parsing of some release filenames 2015-10-29 23:58:56 -07:00
Gaulin, Brendan
fce3f86be7 UI: Update jQuery to 1.11.3 2015-10-29 08:38:21 -04:00
Mark McDowall
2d42c59d70 Fixed: Log download client name when communication fails 2015-10-27 22:19:24 -07:00
Mark McDowall
f0933b9786 Fixed: Test messaging when indexer API returns an error with a message 2015-10-27 22:04:20 -07:00
Mark McDowall
e4e687c2a4 Fixed: Parsing anime series with number in title
Closes #898
2015-10-26 14:56:28 -07:00
Taloth Saldono
44de353b8b Sanitize dognzb apikey in nzb fetch url. 2015-10-25 23:15:21 +01:00
Taloth Saldono
aac4938598 Fixed handling cookies in different system languages.
fixes #896... hopefully
2015-10-25 22:57:37 +01:00
Mark McDowall
d37b24cd0b Better UI messaging when searching for all specials in a series
Fixed: Specials season search UI messaging
Closes #881
2015-10-25 10:10:57 -07:00
Taloth Saldono
c9a36fe4b2 Fixed sorting on Progress in Queue.
Fixes #882
2015-10-22 23:19:24 +02:00
Taloth Saldono
f01a21ce43 Degraded 101 regex to favour S01 regex to match prevent matching 3 digit series title.
Fixes #885
2015-10-22 23:19:22 +02:00
Taloth Saldono
cc72699b8a Fixed: Added verified file transfer mode that doesn't revert to copy. 2015-10-22 23:19:21 +02:00
Mark McDowall
04de0049fe Don't try to process a download client item with an invalid path for the OS 2015-10-21 15:06:54 -07:00
Taloth Saldono
330554edb0 Fixed: External links again open in new windows. 2015-10-20 20:58:56 +02:00
Taloth Saldono
a06a3fa5d6 Fixed: Removal of common suffixes such as [ettv] while parsing.
Fixes #874
2015-10-20 20:12:35 +02:00
Taloth Saldono
e8d6d62fba Warning message when BTN API throw internal server error 2015-10-20 19:31:40 +02:00
434 changed files with 16700 additions and 18693 deletions

View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="JavaScriptLibraryMappings"> <component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{Sonarr node_modules}" /> <file url="file://$PROJECT_DIR$" libraries="{Sonarr node_modules}" />
<includedPredefinedLibrary name="ECMAScript 6" />
</component> </component>
</project> </project>

View File

@@ -1,6 +1,6 @@
# How to Contribute # # How to Contribute #
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute. To get started, <a href="http://www.clahub.com/agreements/NzbDrone/NzbDrone">sign the Contributor License Agreement</a>. We're always looking for people to help make Sonarr even better, there are a number of ways to contribute.
## Documentation ## ## Documentation ##
Setup guides, FAQ, the more information we have on the wiki the better. Setup guides, FAQ, the more information we have on the wiki the better.
@@ -16,7 +16,7 @@ Setup guides, FAQ, the more information we have on the wiki the better.
### Getting started ### ### Getting started ###
1. Fork Sonarr 1. Fork Sonarr
2. Clone (develop branch) 2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
3. Run `npm install` 3. Run `npm install`
4. Run `gulp watch` - Used to compile the UI components and copy them (leave this window open) 4. Run `gulp watch` - Used to compile the UI components and copy them (leave this window open)
5. Compile in Visual Studio 5. Compile in Visual Studio

264
build.ps1
View File

@@ -1,263 +1 @@
$msBuild = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe' Write-Warning "DEPRECATED -- Please use build.sh instead."
$outputFolder = '.\_output'
$outputFolderMono = '.\_output_mono'
$outputFolderOsx = '.\_output_osx'
$outputFolderOsxApp = '.\_output_osx_app'
$testPackageFolder = '.\_tests\'
$testSearchPattern = '*.Test\bin\x86\Release'
$sourceFolder = '.\src'
$updateFolder = $outputFolder + '\NzbDrone.Update'
$updateFolderMono = $outputFolderMono + '\NzbDrone.Update'
Function Build()
{
Write-Host "##teamcity[progressStart 'Build']"
$clean = $msbuild + " src\nzbdrone.sln /t:Clean /m"
$build = $msbuild + " src\nzbdrone.sln /p:Configuration=Release /p:Platform=x86 /t:Build /m"
if(Test-Path $outputFolder)
{
Remove-Item -Recurse -Force $outputFolder -ErrorAction Continue
}
Invoke-Expression $clean
CheckExitCode
Invoke-Expression $build
CheckExitCode
CleanFolder $outputFolder
AddJsonNet
Write-Host "Removing Mono.Posix.dll"
Remove-Item "$outputFolder\Mono.Posix.dll"
Write-Host "##teamcity[progressFinish 'Build']"
}
Function CleanFolder($path, $keepConfigFiles)
{
Write-Host Removing XMLDoc files
get-childitem $path -File -Filter *.xml -Recurse | foreach ($_) {
$filename = $_.FullName
$exeFilename = $filename -replace "xml", "exe"
$dllFilename = $filename -replace "xml", "dll"
if (Test-Path $exeFilename) {
remove-item $_.fullname
}
if (Test-Path $dllFilename) {
remove-item $_.fullname
}
}
get-childitem $path -File -Filter *.transform -Recurse | foreach ($_) {remove-item $_.fullname}
if($keepConfigFiles -ne $true)
{
get-childitem $path -File -Filter *.dll.config -Recurse | foreach ($_) {remove-item $_.fullname}
}
Write-Host Removing FluentValidation.Resources files
get-childitem $path -File -Filter FluentValidation.resources.dll -recurse | foreach ($_) {remove-item $_.fullname}
get-childitem $path -File -Filter app.config -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host Removing .less files
get-childitem $path -File -Filter *.less -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host Removing vshost files
get-childitem $path -File -Filter *.vshost.exe -Recurse | foreach ($_) {remove-item $_.fullname}
if(Test-Path $$path\NuGet)
{
Write-Host Removing NuGet
Remove-Item -Recurse -Force "$path\NuGet"
}
Write-Host Removing Empty folders
while (Get-ChildItem $path -recurse | where {!@(Get-ChildItem -force $_.fullname)} | Test-Path)
{
Get-ChildItem $path -Directory -recurse | where {!@(Get-ChildItem -force $_.fullname)} | Remove-Item
}
}
Function PackageMono()
{
Write-Host "##teamcity[progressStart 'Creating Mono Package']"
if(Test-Path $outputFolderMono)
{
Remove-Item -Recurse -Force $outputFolderMono -ErrorAction Continue
}
Copy-Item $outputFolder $outputFolderMono -recurse
Write-Host Creating MDBs
get-childitem $outputFolderMono -File -Include @("*.exe", "*.dll") -Exclude @("MediaInfo.dll", "sqlite3.dll") -Recurse | foreach ($_) {
Write-Host "Creating .mdb for $_"
& "tools\pdb2mdb\pdb2mdb.exe" $_.fullname
}
Write-Host Removing PDBs
get-childitem $outputFolderMono -File -Filter *.pdb -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host Removing Service helpers
get-childitem $outputFolderMono -File -Filter ServiceUninstall.* -Recurse | foreach ($_) {remove-item $_.fullname}
get-childitem $outputFolderMono -File -Filter ServiceInstall.* -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host Removing native windows binaries Sqlite, MediaInfo
get-childitem $outputFolderMono -File -Filter sqlite3.* -Recurse | foreach ($_) {remove-item $_.fullname}
get-childitem $outputFolderMono -File -Filter MediaInfo.* -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)"
Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" $outputFolderMono
Write-Host "Adding CurlSharp.dll.config (for dllmap)"
Copy-Item "$sourceFolder\NzbDrone.Common\CurlSharp.dll.config" $outputFolderMono
Write-Host Renaming NzbDrone.Console.exe to NzbDrone.exe
Get-ChildItem $outputFolderMono -File -Filter "NzbDrone.exe*" -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host Removing NzbDrone.Windows
get-childitem $outputFolderMono -File -Filter NzbDrone.Windows.* -Recurse | foreach ($_) {remove-item $_.fullname}
Get-ChildItem $outputFolderMono -File -Filter "NzbDrone.Console.exe*" -Recurse | foreach ($_) {
$newName = $_.fullname -Replace ".Console",""
Rename-Item $_.fullname $newName
}
Write-Host Adding NzbDrone.Mono to UpdatePackage
Copy-Item $outputFolderMono\* $updateFolderMono -Filter NzbDrone.Mono.*
Write-Host "##teamcity[progressFinish 'Creating Mono Package']"
}
Function PackageOsx()
{
Write-Host "##teamcity[progressStart 'Creating OS X Package']"
if(Test-Path $outputFolderOsx)
{
Remove-Item -Recurse -Force $outputFolderOsx -ErrorAction Continue
}
Copy-Item $outputFolderMono $outputFolderOsx -recurse
Write-Host "Adding sqlite dylibs"
Copy-Item "$sourceFolder\Libraries\sqlite\*.dylib" "$outputFolderOsx"
Write-Host "Adding MediaInfo dylib"
Copy-Item "$sourceFolder\Libraries\MediaInfo\*.dylib" "$outputFolderOsx"
Write-Host "Adding Startup script"
Copy-Item .\osx\Sonarr "$outputFolderOsx"
Write-Host "##teamcity[progressFinish 'Creating OS X Package']"
}
Function PackageOsxApp()
{
Write-Host "##teamcity[progressStart 'Creating OS X App Package']"
if(Test-Path $outputFolderOsxApp)
{
Remove-Item -Recurse -Force $outputFolderOsxApp -ErrorAction Continue
}
Copy-Item .\osx\Sonarr.app $outputFolderOsxApp\Sonarr.app -recurse
Copy-Item $outputFolderOsx $outputFolderOsxApp\Sonarr.app\Contents\MacOS -recurse
Write-Host "##teamcity[progressFinish 'Creating OS X App Package']"
}
Function AddJsonNet()
{
get-childitem $outputFolder -File -Filter Newtonsoft.Json.* -Recurse | foreach ($_) {remove-item $_.fullname}
Copy-Item .\src\packages\Newtonsoft.Json.*.*\lib\net35\*.dll -Destination $outputFolder
Copy-Item .\src\packages\Newtonsoft.Json.*.*\lib\net35\*.dll -Destination $outputFolder\NzbDrone.Update
}
Function PackageTests()
{
Write-Host Packaging Tests
Write-Host "##teamcity[progressStart 'Creating Test Package']"
if(Test-Path $testPackageFolder)
{
Remove-Item -Recurse -Force $testPackageFolder -ErrorAction Continue
}
Get-ChildItem -Recurse -Directory | Where-Object {$_.FullName -like $testSearchPattern} | foreach($_){
Copy-Item -Recurse ($_.FullName + "\*") $testPackageFolder -ErrorAction Ignore
}
.\src\.nuget\NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
Copy-Item $outputFolder\*.dll -Destination $testPackageFolder -Force
Copy-Item $outputFolder\*.pdb -Destination $testPackageFolder -Force
Copy-Item .\*.sh -Destination $testPackageFolder -Force
Write-Host Creating MDBs for tests
get-childitem $testPackageFolder -File -Include @("*.exe", "*.dll") -Exclude @("MediaInfo.dll", "sqlite3.dll") -Recurse | foreach ($_) {
Write-Host "Creating .mdb for $_"
& "tools\pdb2mdb\pdb2mdb.exe" $_.fullname
}
get-childitem $testPackageFolder -File -Filter *log.config | foreach ($_) {remove-item $_.fullname}
CleanFolder $testPackageFolder $true
Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)"
Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" -Destination $testPackageFolder -Force
Write-Host "Copying CurlSharp libraries"
Copy-Item $sourceFolder\ExternalModules\CurlSharp\libs\i386\* $testPackageFolder
Write-Host "##teamcity[progressFinish 'Creating Test Package']"
}
Function RunGulp()
{
Write-Host "##teamcity[progressStart 'Running Gulp']"
Invoke-Expression 'npm install'
CheckExitCode
Invoke-Expression 'gulp build' -ErrorAction Continue -Verbose
CheckExitCode
Write-Host "##teamcity[progressFinish 'Running Gulp']"
}
Function CheckExitCode()
{
if ($lastexitcode -ne 0)
{
Write-Host $errorMessage
exit 1
}
}
Function CleanupWindowsPackage()
{
Write-Host Removing NzbDrone.Mono
get-childitem $outputFolder -File -Filter NzbDrone.Mono.* -Recurse | foreach ($_) {remove-item $_.fullname}
Write-Host Adding NzbDrone.Windows to UpdatePackage
Copy-Item $outputFolder\* $updateFolder -Filter NzbDrone.Windows.*
}
Build
RunGulp
PackageMono
PackageOsx
PackageOsxApp
PackageTests
CleanupWindowsPackage

View File

@@ -5,11 +5,13 @@ outputFolderMono='./_output_mono'
outputFolderOsx='./_output_osx' outputFolderOsx='./_output_osx'
outputFolderOsxApp='./_output_osx_app' outputFolderOsxApp='./_output_osx_app'
testPackageFolder='./_tests/' testPackageFolder='./_tests/'
testSearchPattern='*.Test/bin/x86/Release/*' testSearchPattern='*.Test/bin/x86/Release'
sourceFolder='./src' sourceFolder='./src'
slnFile=$sourceFolder/NzbDrone.sln
updateFolder=$outputFolder/NzbDrone.Update updateFolder=$outputFolder/NzbDrone.Update
updateFolderMono=$outputFolderMono/NzbDrone.Update updateFolderMono=$outputFolderMono/NzbDrone.Update
nuget='tools/nuget/nuget.exe';
CheckExitCode() CheckExitCode()
{ {
"$@" "$@"
@@ -26,14 +28,6 @@ CleanFolder()
local path=$1 local path=$1
local keepConfigFiles=$2 local keepConfigFiles=$2
echo "Removing XMLDoc files"
local xmlfiles=( $(find $path -name "*.xml") )
for filename in "${xmlfiles[@]}"
do
if [ -e ${filename%.xml}.dll ] || [ -e ${filename%.xml}.exe ] ; then
rm $filename
fi
done
find $path -name "*.transform" -exec rm "{}" \; find $path -name "*.transform" -exec rm "{}" \;
@@ -51,10 +45,8 @@ CleanFolder()
echo "Removing vshost files" echo "Removing vshost files"
find $path -name "*.vshost.exe" -exec rm "{}" \; find $path -name "*.vshost.exe" -exec rm "{}" \;
if [ -d $path/NuGet ] ; then echo "Removing dylib files"
echo "Removing NuGet" find $path -name "*.dylib" -exec rm "{}" \;
rm -rf $path/NuGet
fi
echo "Removing Empty folders" echo "Removing Empty folders"
find $path -depth -empty -type d -exec rm -r "{}" \; find $path -depth -empty -type d -exec rm -r "{}" \;
@@ -72,15 +64,17 @@ AddJsonNet()
BuildWithMSBuild() BuildWithMSBuild()
{ {
export PATH=$msBuild:$PATH export PATH=$msBuild:$PATH
CheckExitCode MSBuild.exe $sourceFolder/NzbDrone.sln //t:Clean //m CheckExitCode MSBuild.exe $slnFile //t:Clean //m
CheckExitCode MSBuild.exe $sourceFolder/NzbDrone.sln //p:Configuration=Release //p:Platform=x86 //t:Build //m $nuget restore $slnFile
CheckExitCode MSBuild.exe $slnFile //p:Configuration=Release //p:Platform=x86 //t:Build //m //p:AllowedReferenceRelatedFileExtensions=.pdb
} }
BuildWithXbuild() BuildWithXbuild()
{ {
export MONO_IOMAP=case export MONO_IOMAP=case
CheckExitCode xbuild /t:Clean $sourceFolder/NzbDrone.sln CheckExitCode xbuild /t:Clean $slnFile
CheckExitCode xbuild /p:Configuration=Release /p:Platform=x86 /t:Build $sourceFolder/NzbDrone.sln mono $nuget restore $slnFile
CheckExitCode xbuild /p:Configuration=Release /p:Platform=x86 /t:Build /p:AllowedReferenceRelatedFileExtensions=.pdb $slnFile
} }
Build() Build()
@@ -94,9 +88,9 @@ Build()
else else
BuildWithXbuild BuildWithXbuild
fi fi
CleanFolder $outputFolder false CleanFolder $outputFolder false
AddJsonNet AddJsonNet
echo "Removing Mono.Posix.dll" echo "Removing Mono.Posix.dll"
@@ -107,11 +101,12 @@ Build()
RunGulp() RunGulp()
{ {
echo "##teamcity[progressStart 'Running Gulp']" echo "##teamcity[progressStart 'npm install']"
CheckExitCode npm install CheckExitCode npm install
CheckExitCode gulp build echo "##teamcity[progressFinish 'npm install']"
echo "##teamcity[progressStart 'Running Gulp']"
CheckExitCode gulp build
echo "##teamcity[progressFinish 'Running Gulp']" echo "##teamcity[progressFinish 'Running Gulp']"
} }
@@ -119,7 +114,16 @@ CreateMdbs()
{ {
local path=$1 local path=$1
if [ $runtime = "dotnet" ] ; then if [ $runtime = "dotnet" ] ; then
find $path \( -name "*.exe" -o -name "*.dll" \) -not -name "MediaInfo.dll" -not -name "sqlite3.dll" -exec tools/pdb2mdb/pdb2mdb.exe "{}" \; local pdbFiles=( $(find $path -name "*.pdb") )
for filename in "${pdbFiles[@]}"
do
if [ -e ${filename%.pdb}.dll ] ; then
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.dll
fi
if [ -e ${filename%.pdb}.exe ] ; then
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.exe
fi
done
fi fi
} }
@@ -160,6 +164,8 @@ PackageMono()
echo "Adding NzbDrone.Mono to UpdatePackage" echo "Adding NzbDrone.Mono to UpdatePackage"
cp $outputFolderMono/NzbDrone.Mono.* $updateFolderMono cp $outputFolderMono/NzbDrone.Mono.* $updateFolderMono
echo "##teamcity[progressFinish 'Creating Mono Package']"
} }
PackageOsx() PackageOsx()
@@ -199,13 +205,12 @@ PackageTests()
rm -rf $testPackageFolder rm -rf $testPackageFolder
mkdir $testPackageFolder mkdir $testPackageFolder
find . -maxdepth 6 -path $testSearchPattern -exec cp -r "{}" $testPackageFolder \; find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
if [ $runtime = "dotnet" ] ; then if [ $runtime = "dotnet" ] ; then
$sourceFolder/.nuget/NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder $nuget install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
cp $outputFolder/*.pdb $testPackageFolder
else else
mono $sourceFolder/.nuget/NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder mono $nuget install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
fi fi
cp $outputFolder/*.dll $testPackageFolder cp $outputFolder/*.dll $testPackageFolder

2
debian/copyright vendored
View File

@@ -3,7 +3,7 @@ Upstream-Name: nzbdrone
Source: https://github.com/Sonarr/Sonarr Source: https://github.com/Sonarr/Sonarr
Files: * Files: *
Copyright: 2010-2014 Sonarr <hello@sonarr.tv> Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
License: GPL-3.0+ License: GPL-3.0+

View File

@@ -7,7 +7,6 @@ var autoprefixer = require('autoprefixer-core');
var livereload = require('gulp-livereload'); var livereload = require('gulp-livereload');
var print = require('gulp-print'); var print = require('gulp-print');
var phantom = require('./phantom');
var paths = require('./paths'); var paths = require('./paths');
var errorHandler = require('./errorHandler'); var errorHandler = require('./errorHandler');
@@ -29,14 +28,6 @@ gulp.task('less', function() {
paths.src.root + 'System/Info/info.less' paths.src.root + 'System/Info/info.less'
]; ];
if (phantom) {
src = [
paths.src.content + 'Bootstrap/bootstrap.less',
paths.src.content + 'Vendor/vendor.less',
paths.src.content + 'sonarr.less'
];
}
return gulp.src(src) return gulp.src(src)
.pipe(print()) .pipe(print())
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())

View File

@@ -1,5 +1,3 @@
var phantom = require('./phantom');
var paths = { var paths = {
src : { src : {
root : './src/UI/', root : './src/UI/',
@@ -20,26 +18,4 @@ var paths = {
} }
}; };
if (phantom) {
paths = {
src : {
root : './UI.Phantom/',
templates : './UI.Phantom/**/*.hbs',
html : './UI.Phantom/*.html',
partials : './UI.Phantom/**/*Partial.hbs',
scripts : './UI.Phantom/**/*.js',
less : ['./UI.Phantom/**/*.less'],
content : './UI.Phantom/Content/',
images : './UI.Phantom/Content/Images/**/*',
exclude : {
libs : '!./UI.Phantom/JsLibraries/**'
}
},
dest : {
root : './_output/UI.Phantom/',
content : './_output/UI.Phantom/Content/'
}
};
}
module.exports = paths; module.exports = paths;

View File

@@ -1,14 +0,0 @@
// Switch to phantom.
// Example:
// gulp --phantom
var phantom = false;
process.argv.forEach(function(val, index, array) {
if (val === '--phantom') {
phantom = true;
}
});
console.log('Phantom:', phantom);
module.exports = phantom;

View File

@@ -21,7 +21,7 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
## Configuring Development Environment: ## ## Configuring Development Environment: ##
### Requirements ### ### Requirements ###
- Visual Studio 2013 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) - Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
- [Git](http://git-scm.com/downloads) - [Git](http://git-scm.com/downloads)
- [NodeJS](http://nodejs.org/download/) - [NodeJS](http://nodejs.org/download/)
- [Gulp](http://gulpjs.com) - [Gulp](http://gulpjs.com)
@@ -45,7 +45,7 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
### License ### ### License ###
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) * [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
Copyright 2010-2015 Copyright 2010-2016
### Sponsors ### ### Sponsors ###

View File

@@ -1,17 +0,0 @@
Param(
[Parameter(Mandatory=$true, Position=0, HelpMessage="A branch name is #requires required")]
[string]$branch,
[Parameter(Mandatory=$true, Position=1, HelpMessage="A version is required")]
[string]$version
)
if ($branch -eq "<default>")
{
$branch = "teamcity";
}
Write-Host $branch;
Write-Host $version;
Write-Host "NzbDrone.$branch.$version.zip";
Rename-Item "nzbdrone.zip" "NzbDrone.$branch.$version.zip"

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>

Binary file not shown.

View File

@@ -1,136 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
<PackagesConfig>packages.config</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

Binary file not shown.

View File

@@ -1,9 +0,0 @@
<?xml version ="1.0"?>
<!-- This allows mt.exe to run on machines with the CLR v4 installed but not 1.1 or 2.0 -->
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
<supportedRuntime version="v1.1.4322"/>
</startup>
</configuration>

View File

@@ -296,7 +296,7 @@ namespace LogentriesCore
WriteDebugMessages("HostName parameter is not defined - trying to get it from System.Environment.MachineName"); WriteDebugMessages("HostName parameter is not defined - trying to get it from System.Environment.MachineName");
m_HostName = "HostName=" + System.Environment.MachineName + " "; m_HostName = "HostName=" + System.Environment.MachineName + " ";
} }
catch (InvalidOperationException ex) catch (InvalidOperationException)
{ {
// Cannot get host name automatically, so assume that HostName is not used // Cannot get host name automatically, so assume that HostName is not used
// and log message is sent without it. // and log message is sent without it.

View File

@@ -73,13 +73,6 @@
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -51,8 +51,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="NLog"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath> <HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@@ -79,19 +80,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="fastJSON\license.txt" /> <None Include="fastJSON\license.txt" />
<None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
</packages> </packages>

View File

@@ -144,7 +144,6 @@
<PreBuildEvent> <PreBuildEvent>
</PreBuildEvent> </PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -276,10 +276,9 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@@ -102,7 +102,6 @@
</ItemGroup> </ItemGroup>
<Import Project="..\Common\Microsoft.AspNet.SignalR.targets" /> <Import Project="..\Common\Microsoft.AspNet.SignalR.targets" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@@ -110,4 +109,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@@ -774,7 +774,8 @@ namespace MonoTorrent
break; break;
case ("nodes"): case ("nodes"):
this.nodes = (BEncodedList)keypair.Value; if (keypair.Value.ToString().Length != 0)
this.nodes = (BEncodedList)keypair.Value;
break; break;
case ("comment.utf-8"): case ("comment.utf-8"):

View File

@@ -38,12 +38,12 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
@@ -104,7 +104,6 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@@ -112,4 +111,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" /> <package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
<package id="Moq" version="4.0.10827" /> <package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" targetFramework="net40" /> <package id="NBuilder" version="3.0.1.1" targetFramework="net40" />
<package id="NUnit" version="2.6.3" targetFramework="net40" /> <package id="NUnit" version="2.6.3" targetFramework="net40" />
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" /> <package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
</packages> </packages>

View File

@@ -3,6 +3,7 @@ using Nancy;
using Nancy.Authentication.Forms; using Nancy.Authentication.Forms;
using Nancy.Extensions; using Nancy.Extensions;
using Nancy.ModelBinding; using Nancy.ModelBinding;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.Authentication; using NzbDrone.Core.Authentication;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@@ -23,6 +24,11 @@ namespace NzbDrone.Api.Authentication
private Response Login(LoginResource resource) private Response Login(LoginResource resource)
{ {
Ensure.That(resource.Username, () => resource.Username).IsNotNullOrWhiteSpace();
// TODO: A null or empty password should not be allowed, uncomment in v3
//Ensure.That(resource.Password, () => resource.Password).IsNotNullOrWhiteSpace();
var user = _userService.FindUser(resource.Username, resource.Password); var user = _userService.FindUser(resource.Username, resource.Password);
if (user == null) if (user == null)

View File

@@ -22,15 +22,32 @@ namespace NzbDrone.Api.Calendar
private Response GetCalendarFeed() private Response GetCalendarFeed()
{ {
var start = DateTime.Today.AddDays(-7); var pastDays = 7;
var end = DateTime.Today.AddDays(28); var futureDays = 28;
var start = DateTime.Today.AddDays(-pastDays);
var end = DateTime.Today.AddDays(futureDays);
// TODO: Remove start/end parameters in v3, they don't work well for iCal
var queryStart = Request.Query.Start; var queryStart = Request.Query.Start;
var queryEnd = Request.Query.End; var queryEnd = Request.Query.End;
var queryPastDays = Request.Query.PastDays;
var queryFutureDays = Request.Query.FutureDays;
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value); if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value); if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
if (queryPastDays.HasValue)
{
pastDays = int.Parse(queryPastDays.Value);
start = DateTime.Today.AddDays(-pastDays);
}
if (queryFutureDays.HasValue)
{
futureDays = int.Parse(queryFutureDays.Value);
end = DateTime.Today.AddDays(futureDays);
}
var episodes = _episodeService.EpisodesBetweenDates(start, end, false); var episodes = _episodeService.EpisodesBetweenDates(start, end, false);
var icalCalendar = new iCalendar(); var icalCalendar = new iCalendar();

View File

@@ -1,4 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Api.Validation;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Config namespace NzbDrone.Api.Config
@@ -16,8 +17,7 @@ namespace NzbDrone.Api.Config
.GreaterThanOrEqualTo(0); .GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.RssSyncInterval) SharedValidator.RuleFor(c => c.RssSyncInterval)
.InclusiveBetween(10, 120) .IsValidRssSyncInterval();
.When(c => c.RssSyncInterval > 0);
} }
} }
} }

View File

@@ -52,16 +52,16 @@ namespace NzbDrone.Api.EpisodeFiles
private List<EpisodeFileResource> GetEpisodeFiles() private List<EpisodeFileResource> GetEpisodeFiles()
{ {
var seriesId = (int?)Request.Query.SeriesId; if (!Request.Query.SeriesId.HasValue)
if (seriesId == null)
{ {
throw new BadRequestException("seriesId is missing"); throw new BadRequestException("seriesId is missing");
} }
var series = _seriesService.GetSeries(seriesId.Value); var seriesId = (int)Request.Query.SeriesId;
return _mediaFileService.GetFilesBySeries(seriesId.Value) var series = _seriesService.GetSeries(seriesId);
return _mediaFileService.GetFilesBySeries(seriesId)
.Select(f => MapToResource(series, f)).ToList(); .Select(f => MapToResource(series, f)).ToList();
} }

View File

@@ -20,14 +20,14 @@ namespace NzbDrone.Api.Episodes
private List<EpisodeResource> GetEpisodes() private List<EpisodeResource> GetEpisodes()
{ {
var seriesId = (int?)Request.Query.SeriesId; if (!Request.Query.SeriesId.HasValue)
if (seriesId == null)
{ {
throw new BadRequestException("seriesId is missing"); throw new BadRequestException("seriesId is missing");
} }
var resources = ToListResource(_episodeService.GetEpisodeBySeries(seriesId.Value)); var seriesId = (int)Request.Query.SeriesId;
var resources = ToListResource(_episodeService.GetEpisodeBySeries(seriesId));
return resources; return resources;
} }
@@ -36,5 +36,10 @@ namespace NzbDrone.Api.Episodes
{ {
_episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored); _episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored);
} }
protected override List<EpisodeResource> LoadSeries(List<EpisodeResource> resources)
{
return resources;
}
} }
} }

View File

@@ -79,7 +79,8 @@ namespace NzbDrone.Api.Episodes
{ {
var resources = base.ToListResource(modelList); var resources = base.ToListResource(modelList);
return resources.LoadSubtype<EpisodeResource, SeriesResource, Core.Tv.Series>(e => e.SeriesId, _seriesService.GetSeries).ToList(); return LoadSeries(resources);
} }
public void Handle(EpisodeGrabbedEvent message) public void Handle(EpisodeGrabbedEvent message)
@@ -100,5 +101,10 @@ namespace NzbDrone.Api.Episodes
BroadcastResourceChange(ModelAction.Updated, episode.Id); BroadcastResourceChange(ModelAction.Updated, episode.Id);
} }
} }
protected virtual List<EpisodeResource> LoadSeries(List<EpisodeResource> resources)
{
return resources.LoadSubtype<EpisodeResource, SeriesResource, Core.Tv.Series>(e => e.SeriesId, _seriesService.GetSeries).ToList();
}
} }
} }

View File

@@ -18,18 +18,13 @@ namespace NzbDrone.Api.Episodes
private List<RenameEpisodeResource> GetEpisodes() private List<RenameEpisodeResource> GetEpisodes()
{ {
int seriesId; if (!Request.Query.SeriesId.HasValue)
if (Request.Query.SeriesId.HasValue)
{
seriesId = (int)Request.Query.SeriesId;
}
else
{ {
throw new BadRequestException("seriesId is missing"); throw new BadRequestException("seriesId is missing");
} }
var seriesId = (int)Request.Query.SeriesId;
if (Request.Query.SeasonNumber.HasValue) if (Request.Query.SeasonNumber.HasValue)
{ {
var seasonNumber = (int)Request.Query.SeasonNumber; var seasonNumber = (int)Request.Query.SeasonNumber;

View File

@@ -26,7 +26,7 @@ namespace NzbDrone.Api.ErrorManagement
if (apiException != null) if (apiException != null)
{ {
_logger.WarnException("API Error", apiException); _logger.Warn(apiException, "API Error");
return apiException.ToErrorResponse(); return apiException.ToErrorResponse();
} }
@@ -65,10 +65,10 @@ namespace NzbDrone.Api.ErrorManagement
var sqlErrorMessage = string.Format("[{0} {1}]", context.Request.Method, context.Request.Path); var sqlErrorMessage = string.Format("[{0} {1}]", context.Request.Method, context.Request.Path);
_logger.ErrorException(sqlErrorMessage, sqLiteException); _logger.Error(sqLiteException, sqlErrorMessage);
} }
_logger.FatalException("Request Failed", exception); _logger.Fatal(exception, "Request Failed");
return new ErrorModel return new ErrorModel
{ {

View File

@@ -60,7 +60,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Unable to gzip response", ex); _logger.Error(ex, "Unable to gzip response");
throw; throw;
} }
} }

View File

@@ -68,7 +68,7 @@ namespace NzbDrone.Api.Indexers
} }
catch (ReleaseDownloadException ex) catch (ReleaseDownloadException ex)
{ {
_logger.ErrorException(ex.Message, ex); _logger.Error(ex, ex.Message);
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed"); throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
} }
@@ -96,7 +96,7 @@ namespace NzbDrone.Api.Indexers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Episode search failed: " + ex.Message, ex); _logger.Error(ex, "Episode search failed: " + ex.Message);
} }
return new List<ReleaseResource>(); return new List<ReleaseResource>();

View File

@@ -9,6 +9,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions;
using NLog; using NLog;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Api.Indexers namespace NzbDrone.Api.Indexers
{ {
@@ -30,7 +31,7 @@ namespace NzbDrone.Api.Indexers
PostValidator.RuleFor(s => s.Title).NotEmpty(); PostValidator.RuleFor(s => s.Title).NotEmpty();
PostValidator.RuleFor(s => s.DownloadUrl).NotEmpty(); PostValidator.RuleFor(s => s.DownloadUrl).NotEmpty();
PostValidator.RuleFor(s => s.DownloadProtocol).NotEmpty(); PostValidator.RuleFor(s => s.Protocol).NotEmpty();
PostValidator.RuleFor(s => s.PublishDate).NotEmpty(); PostValidator.RuleFor(s => s.PublishDate).NotEmpty();
} }
@@ -38,11 +39,14 @@ namespace NzbDrone.Api.Indexers
{ {
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl); _logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
var info = release.InjectTo<ReleaseInfo>(); var info = release.Protocol == DownloadProtocol.Usenet ?
release.InjectTo<ReleaseInfo>() :
release.InjectTo<TorrentInfo>();
info.Guid = "PUSH-" + info.DownloadUrl; info.Guid = "PUSH-" + info.DownloadUrl;
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info }); var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
var processed = _downloadDecisionProcessor.ProcessDecisions(decisions); _downloadDecisionProcessor.ProcessDecisions(decisions);
return MapDecisions(decisions).First().AsResponse(); return MapDecisions(decisions).First().AsResponse();
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@@ -48,8 +49,25 @@ namespace NzbDrone.Api.Indexers
public int? Leechers { get; set; } public int? Leechers { get; set; }
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
//TODO: besides a test I don't think this is used...
public DownloadProtocol DownloadProtocol { get; set; } // TODO: Remove in v3
// Used to support the original Release Push implementation
// JsonIgnore so we don't serialize it, but can still parse it
[JsonIgnore]
public DownloadProtocol DownloadProtocol
{
get
{
return Protocol;
}
set
{
if (value > 0 && Protocol == 0)
{
Protocol = value;
}
}
}
public bool IsDaily { get; set; } public bool IsDaily { get; set; }
public bool IsAbsoluteNumbering { get; set; } public bool IsAbsoluteNumbering { get; set; }

View File

@@ -30,10 +30,13 @@ namespace NzbDrone.Api.ManualImport
private ManualImportResource AddQualityWeight(ManualImportResource item) private ManualImportResource AddQualityWeight(ManualImportResource item)
{ {
item.QualityWeight = Quality.DefaultQualityDefinitions.Single(q => q.Quality == item.Quality.Quality).Weight; if (item.Quality != null)
item.QualityWeight += item.Quality.Revision.Real * 10; {
item.QualityWeight += item.Quality.Revision.Version; item.QualityWeight = Quality.DefaultQualityDefinitions.Single(q => q.Quality == item.Quality.Quality).Weight;
item.QualityWeight += item.Quality.Revision.Real * 10;
item.QualityWeight += item.Quality.Revision.Version;
}
return item; return item;
} }
} }

View File

@@ -40,9 +40,9 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FluentValidation, Version=5.5.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <HintPath>..\packages\FluentValidation.6.2.1.0\lib\portable-net40+sl50+wp80+win8+wpa81\FluentValidation.dll</HintPath>
<HintPath>..\packages\FluentValidation.5.5.0.0\lib\Net40\FluentValidation.dll</HintPath> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Nancy, Version=0.23.2.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Nancy, Version=0.23.2.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
@@ -59,6 +59,10 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
@@ -66,9 +70,6 @@
<Reference Include="DDay.iCal"> <Reference Include="DDay.iCal">
<HintPath>..\packages\DDay.iCal.1.0.2.575\lib\DDay.iCal.dll</HintPath> <HintPath>..\packages\DDay.iCal.1.0.2.575\lib\DDay.iCal.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Omu.ValueInjecter"> <Reference Include="Omu.ValueInjecter">
<HintPath>..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll</HintPath> <HintPath>..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll</HintPath>
</Reference> </Reference>
@@ -238,6 +239,7 @@
<Compile Include="TinyIoCNancyBootstrapper.cs" /> <Compile Include="TinyIoCNancyBootstrapper.cs" />
<Compile Include="Update\UpdateModule.cs" /> <Compile Include="Update\UpdateModule.cs" />
<Compile Include="Update\UpdateResource.cs" /> <Compile Include="Update\UpdateResource.cs" />
<Compile Include="Validation\RssSyncIntervalValidator.cs" />
<Compile Include="Validation\EmptyCollectionValidator.cs" /> <Compile Include="Validation\EmptyCollectionValidator.cs" />
<Compile Include="Validation\RuleBuilderExtensions.cs" /> <Compile Include="Validation\RuleBuilderExtensions.cs" />
<Compile Include="Wanted\CutoffModule.cs" /> <Compile Include="Wanted\CutoffModule.cs" />
@@ -270,7 +272,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -10,11 +10,11 @@ using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.SeriesStats; using NzbDrone.Core.SeriesStats;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Api.Validation;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using NzbDrone.Core.Tv.Events; using NzbDrone.Core.Tv.Events;
using NzbDrone.Core.Validation.Paths; using NzbDrone.Core.Validation.Paths;
using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Validation;
using NzbDrone.SignalR; using NzbDrone.SignalR;
namespace NzbDrone.Api.Series namespace NzbDrone.Api.Series
@@ -43,7 +43,8 @@ namespace NzbDrone.Api.Series
SeriesPathValidator seriesPathValidator, SeriesPathValidator seriesPathValidator,
SeriesExistsValidator seriesExistsValidator, SeriesExistsValidator seriesExistsValidator,
DroneFactoryValidator droneFactoryValidator, DroneFactoryValidator droneFactoryValidator,
SeriesAncestorValidator seriesAncestorValidator SeriesAncestorValidator seriesAncestorValidator,
ProfileExistsValidator profileExistsValidator
) )
: base(signalRBroadcaster) : base(signalRBroadcaster)
{ {
@@ -59,7 +60,7 @@ namespace NzbDrone.Api.Series
UpdateResource = UpdateSeries; UpdateResource = UpdateSeries;
DeleteResource = DeleteSeries; DeleteResource = DeleteSeries;
SharedValidator.RuleFor(s => s.ProfileId).ValidId(); Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.ProfileId));
SharedValidator.RuleFor(s => s.Path) SharedValidator.RuleFor(s => s.Path)
.Cascade(CascadeMode.StopOnFirstFailure) .Cascade(CascadeMode.StopOnFirstFailure)
@@ -70,6 +71,8 @@ namespace NzbDrone.Api.Series
.SetValidator(seriesAncestorValidator) .SetValidator(seriesAncestorValidator)
.When(s => !s.Path.IsNullOrWhiteSpace()); .When(s => !s.Path.IsNullOrWhiteSpace());
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Title).NotEmpty(); PostValidator.RuleFor(s => s.Title).NotEmpty();

View File

@@ -93,7 +93,6 @@ namespace NzbDrone.Api
break; break;
case Lifetime.PerRequest: case Lifetime.PerRequest:
throw new InvalidOperationException("Unable to directly register a per request lifetime."); throw new InvalidOperationException("Unable to directly register a per request lifetime.");
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
@@ -120,7 +119,6 @@ namespace NzbDrone.Api
break; break;
case Lifetime.PerRequest: case Lifetime.PerRequest:
throw new InvalidOperationException("Unable to directly register a per request lifetime."); throw new InvalidOperationException("Unable to directly register a per request lifetime.");
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }

View File

@@ -0,0 +1,34 @@
using FluentValidation.Validators;
namespace NzbDrone.Api.Validation
{
public class RssSyncIntervalValidator : PropertyValidator
{
public RssSyncIntervalValidator()
: base("Must be between 10 and 120 or 0 to disable")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null)
{
return true;
}
var value = (int)context.PropertyValue;
if (value == 0)
{
return true;
}
if (value >= 10 && value <= 120)
{
return true;
}
return false;
}
}
}

View File

@@ -31,5 +31,10 @@ namespace NzbDrone.Api.Validation
{ {
return ruleBuilder.SetValidator(new EmptyCollectionValidator<TProp>()); return ruleBuilder.SetValidator(new EmptyCollectionValidator<TProp>());
} }
public static IRuleBuilderOptions<T, int> IsValidRssSyncInterval<T>(this IRuleBuilder<T, int> ruleBuilder)
{
return ruleBuilder.SetValidator(new RssSyncIntervalValidator());
}
} }
} }

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="DDay.iCal" version="1.0.2.575" targetFramework="net40" /> <package id="DDay.iCal" version="1.0.2.575" targetFramework="net40" />
<package id="FluentValidation" version="5.5.0.0" targetFramework="net40" /> <package id="FluentValidation" version="6.2.1.0" targetFramework="net40" />
<package id="Nancy" version="0.23.2" targetFramework="net40" /> <package id="Nancy" version="0.23.2" targetFramework="net40" />
<package id="Nancy.Authentication.Basic" version="0.23.2" targetFramework="net40" /> <package id="Nancy.Authentication.Basic" version="0.23.2" targetFramework="net40" />
<package id="Nancy.Authentication.Forms" version="0.23.2" targetFramework="net40" /> <package id="Nancy.Authentication.Forms" version="0.23.2" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" /> <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" /> <package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
</packages> </packages>

View File

@@ -37,12 +37,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
@@ -60,9 +64,6 @@
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ContainerFixture.cs" /> <Compile Include="ContainerFixture.cs" />
@@ -104,7 +105,6 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent Condition="('$(OS)' == 'Windows_NT')"> <PostBuildEvent Condition="('$(OS)' == 'Windows_NT')">
xcopy /s /y "$(SolutionDir)\..\_output\NzbDrone.Mono.*" "$(TargetDir)" xcopy /s /y "$(SolutionDir)\..\_output\NzbDrone.Mono.*" "$(TargetDir)"

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" /> <package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
<package id="Moq" version="4.0.10827" /> <package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" /> <package id="NBuilder" version="3.0.1.1" />
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
<package id="NUnit" version="2.6.3" targetFramework="net40" /> <package id="NUnit" version="2.6.3" targetFramework="net40" />
</packages> </packages>

View File

@@ -38,12 +38,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
@@ -58,9 +62,6 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="WebDriver, Version=2.48.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="WebDriver, Version=2.48.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Selenium.WebDriver.2.48.0\lib\net40\WebDriver.dll</HintPath> <HintPath>..\packages\Selenium.WebDriver.2.48.0\lib\net40\WebDriver.dll</HintPath>
@@ -95,7 +96,6 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" /> <package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
<package id="NUnit" version="2.6.3" targetFramework="net40" /> <package id="NUnit" version="2.6.3" targetFramework="net40" />
<package id="Selenium.Support" version="2.48.0" targetFramework="net40" /> <package id="Selenium.Support" version="2.48.0" targetFramework="net40" />
<package id="Selenium.WebDriver" version="2.48.0" targetFramework="net40" /> <package id="Selenium.WebDriver" version="2.48.0" targetFramework="net40" />

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Threading;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Cache;
namespace NzbDrone.Common.Test.CacheTests
{
[TestFixture]
public class CachedDictionaryFixture
{
private CachedDictionary<string> _cachedString;
private DictionaryWorker _worker;
[SetUp]
public void SetUp()
{
_worker = new DictionaryWorker();
_cachedString = new CachedDictionary<string>(_worker.GetDict, TimeSpan.FromMilliseconds(100));
}
[Test]
public void should_not_fetch_on_create()
{
_worker.HitCount.Should().Be(0);
}
[Test]
public void should_fetch_on_first_call()
{
var result = _cachedString.Get("Hi");
_worker.HitCount.Should().Be(1);
result.Should().Be("Value");
}
[Test]
public void should_fetch_once()
{
var result1 = _cachedString.Get("Hi");
var result2 = _cachedString.Get("HitCount");
_worker.HitCount.Should().Be(1);
}
[Test]
public void should_auto_refresh_after_lifetime()
{
var result1 = _cachedString.Get("Hi");
Thread.Sleep(200);
var result2 = _cachedString.Get("Hi");
_worker.HitCount.Should().Be(2);
}
[Test]
public void should_refresh_early_if_requested()
{
var result1 = _cachedString.Get("Hi");
Thread.Sleep(10);
_cachedString.RefreshIfExpired(TimeSpan.FromMilliseconds(1));
var result2 = _cachedString.Get("Hi");
_worker.HitCount.Should().Be(2);
}
[Test]
public void should_not_refresh_early_if_not_expired()
{
var result1 = _cachedString.Get("Hi");
_cachedString.RefreshIfExpired(TimeSpan.FromMilliseconds(50));
var result2 = _cachedString.Get("Hi");
_worker.HitCount.Should().Be(1);
}
}
public class DictionaryWorker
{
public int HitCount { get; private set; }
public Dictionary<string, string> GetDict()
{
HitCount++;
var result = new Dictionary<string, string>();
result["Hi"] = "Value";
result["HitCount"] = "Hit count is " + HitCount;
return result;
}
}
}

View File

@@ -24,6 +24,10 @@ namespace NzbDrone.Common.Test.DiskTests
{ {
Mocker.GetMock<IDiskProvider>(MockBehavior.Strict); Mocker.GetMock<IDiskProvider>(MockBehavior.Strict);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetMount(It.IsAny<string>()))
.Returns((IMount)null);
WithEmulatedDiskProvider(); WithEmulatedDiskProvider();
WithExistingFile(_sourcePath); WithExistingFile(_sourcePath);
@@ -34,7 +38,7 @@ namespace NzbDrone.Common.Test.DiskTests
{ {
MonoOnly(); MonoOnly();
Subject.VerificationMode.Should().Be(DiskTransferVerificationMode.Transactional); Subject.VerificationMode.Should().Be(DiskTransferVerificationMode.TryTransactional);
} }
[Test] [Test]
@@ -199,9 +203,6 @@ namespace NzbDrone.Common.Test.DiskTests
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Verify(v => v.CopyFile(_sourcePath, _targetPath, false), Times.Once()); .Verify(v => v.CopyFile(_sourcePath, _targetPath, false), Times.Once());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetFileSize(It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
@@ -213,9 +214,6 @@ namespace NzbDrone.Common.Test.DiskTests
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Verify(v => v.MoveFile(_sourcePath, _targetPath, false), Times.Once()); .Verify(v => v.MoveFile(_sourcePath, _targetPath, false), Times.Once());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetFileSize(It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
@@ -377,6 +375,52 @@ namespace NzbDrone.Common.Test.DiskTests
ExceptionVerification.ExpectedErrors(1); ExceptionVerification.ExpectedErrors(1);
} }
[Test]
public void mode_transactional_should_move_and_verify_if_same_folder()
{
Subject.VerificationMode = DiskTransferVerificationMode.Transactional;
var targetPath = _sourcePath + ".test";
var result = Subject.TransferFile(_sourcePath, targetPath, TransferMode.Move);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.TryCreateHardLink(_sourcePath, _backupPath), Times.Never());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.MoveFile(_sourcePath, targetPath, false), Times.Once());
}
[Test]
public void mode_trytransactional_should_revert_to_verifyonly_if_hardlink_fails()
{
Subject.VerificationMode = DiskTransferVerificationMode.TryTransactional;
WithFailedHardlink();
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.MoveFile(_sourcePath, _targetPath, false))
.Callback(() =>
{
WithExistingFile(_sourcePath, false);
WithExistingFile(_targetPath, true);
});
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.TryCreateHardLink(_sourcePath, _backupPath), Times.Once());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.MoveFile(_sourcePath, _targetPath, false), Times.Once());
}
[Test] [Test]
public void mode_transactional_should_delete_old_backup_on_move() public void mode_transactional_should_delete_old_backup_on_move()
{ {

View File

@@ -1,17 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading;
using FluentAssertions; using FluentAssertions;
using Moq;
using NLog;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Common.TPL;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories; using NzbDrone.Test.Common.Categories;
using NLog;
using NzbDrone.Common.TPL;
using Moq;
using NzbDrone.Common.Http.Dispatchers;
namespace NzbDrone.Common.Test.Http namespace NzbDrone.Common.Test.Http
{ {
@@ -309,30 +311,70 @@ namespace NzbDrone.Common.Test.Http
.Verify(v => v.PostResponse(It.IsAny<HttpResponse>()), Times.Once()); .Verify(v => v.PostResponse(It.IsAny<HttpResponse>()), Times.Once());
} }
public void should_parse_malformed_cloudflare_cookie() [TestCase("en-US")]
[TestCase("es-ES")]
public void should_parse_malformed_cloudflare_cookie(string culture)
{ {
// the date is bad in the below - should be 13-Jul-2016 var origCulture = Thread.CurrentThread.CurrentCulture;
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Wed, 13-Jul-16 16:19:50 GMT; path=/; HttpOnly"; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
string url = "http://eu.httpbin.org/response-headers?Set-Cookie=" + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
System.Uri.EscapeUriString(malformedCookie); try
{
// the date is bad in the below - should be 13-Jul-2016
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Wed, 13-Jul-16 16:19:50 GMT; path=/; HttpOnly";
string url = "http://eu.httpbin.org/response-headers?Set-Cookie=" +
System.Uri.EscapeUriString(malformedCookie);
var requestSet = new HttpRequest(url); var requestSet = new HttpRequest(url);
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreResponseCookie = true; requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet); var responseSet = Subject.Get(requestSet);
var request = new HttpRequest("http://eu.httpbin.org/get"); var request = new HttpRequest("http://eu.httpbin.org/get");
var response = Subject.Get<HttpBinResource>(request); var response = Subject.Get<HttpBinResource>(request);
response.Resource.Headers.Should().ContainKey("Cookie"); response.Resource.Headers.Should().ContainKey("Cookie");
var cookie = response.Resource.Headers["Cookie"].ToString(); var cookie = response.Resource.Headers["Cookie"].ToString();
cookie.Should().Contain("__cfduid=d29e686a9d65800021c66faca0a29b4261436890790"); cookie.Should().Contain("__cfduid=d29e686a9d65800021c66faca0a29b4261436890790");
ExceptionVerification.IgnoreErrors(); ExceptionVerification.IgnoreErrors();
}
finally
{
Thread.CurrentThread.CurrentCulture = origCulture;
Thread.CurrentThread.CurrentUICulture = origCulture;
}
}
[TestCase("lang_code=en; expires=Fri, 23-Dec-2016 18:09:14 GMT; Max-Age=31536000; path=/; domain=.abc.com")]
public void should_reject_malformed_domain_cookie(string malformedCookie)
{
try
{
// the date is bad in the below - should be 13-Jul-2016
string url = "http://eu.httpbin.org/response-headers?Set-Cookie=" + Uri.EscapeUriString(malformedCookie);
var requestSet = new HttpRequest(url);
requestSet.AllowAutoRedirect = false;
requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet);
var request = new HttpRequest("http://eu.httpbin.org/get");
var response = Subject.Get<HttpBinResource>(request);
response.Resource.Headers.Should().NotContainKey("Cookie");
ExceptionVerification.IgnoreErrors();
}
finally
{
}
} }
} }

View File

@@ -11,8 +11,11 @@ namespace NzbDrone.Common.Test.InstrumentationTests
// Indexer Urls // Indexer Urls
[TestCase(@"https://iptorrents.com/torrents/rss?u=mySecret;tp=mySecret;l5;download")] [TestCase(@"https://iptorrents.com/torrents/rss?u=mySecret;tp=mySecret;l5;download")]
[TestCase(@"http://rss.torrentleech.org/mySecret")] [TestCase(@"http://rss.torrentleech.org/mySecret")]
[TestCase(@"http://rss.torrentleech.org/rss/download/12345/01233210/filename.torrent")]
[TestCase(@"http://www.bitmetv.org/rss.php?uid=mySecret&passkey=mySecret")] [TestCase(@"http://www.bitmetv.org/rss.php?uid=mySecret&passkey=mySecret")]
[TestCase(@"https://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=sonarr&api=mySecret&eng=1")] [TestCase(@"https://rss.omgwtfnzbs.org/rss-search.php?catid=19,20&user=sonarr&api=mySecret&eng=1")]
[TestCase(@"https://dognzb.cr/fetch/2b51db35e1912ffc138825a12b9933d2/2b51db35e1910123321025a12b9933d2")]
[TestCase(@"https://baconbits.org/feeds.php?feed=torrents_tv&user=12345&auth=2b51db35e1910123321025a12b9933d2&passkey=mySecret&authkey=2b51db35e1910123321025a12b9933d2")]
// NzbGet // NzbGet
[TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")] [TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")]
[TestCase(@"{ ""Name"" : ""Server1.Username"", ""Value"" : ""mySecret"" }, { ""Name"" : ""Server1.Password"", ""Value"" : ""mySecret"" }, ")] [TestCase(@"{ ""Name"" : ""Server1.Username"", ""Value"" : ""mySecret"" }, { ""Name"" : ""Server1.Password"", ""Value"" : ""mySecret"" }, ")]
@@ -44,6 +47,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
var cleansedMessage = CleanseLogMessage.Cleanse(message); var cleansedMessage = CleanseLogMessage.Cleanse(message);
cleansedMessage.Should().NotContain("mySecret"); cleansedMessage.Should().NotContain("mySecret");
cleansedMessage.Should().NotContain("01233210");
} }
} }
} }

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -37,12 +37,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FluentAssertions, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentAssertions.Core, Version=3.4.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> <Reference Include="FluentAssertions.Core, Version=4.2.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
<HintPath>..\packages\FluentAssertions.3.4.0\lib\net40\FluentAssertions.Core.dll</HintPath> <HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
@@ -60,11 +64,9 @@
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CacheTests\CachedDictionaryFixture.cs" />
<Compile Include="CacheTests\CachedFixture.cs" /> <Compile Include="CacheTests\CachedFixture.cs" />
<Compile Include="CacheTests\CachedManagerFixture.cs" /> <Compile Include="CacheTests\CachedManagerFixture.cs" />
<Compile Include="ConfigFileProviderTest.cs" /> <Compile Include="ConfigFileProviderTest.cs" />
@@ -141,15 +143,14 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent Condition="'$(Configuration)|$(OS)' == 'Debug|Windows_NT'">xcopy /s /y "$(SolutionDir)\ExternalModules\CurlSharp\libs\i386\*" "$(TargetDir)"</PostBuildEvent> <PostBuildEvent Condition="'$(Configuration)|$(OS)' == 'Debug|Windows_NT'">xcopy /s /y "$(SolutionDir)\ExternalModules\CurlSharp\libs\i386\*" "$(TargetDir)"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
</Target> </Target>
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@@ -24,6 +24,7 @@ namespace NzbDrone.Common.Test
[TestCase("/rooted/linux/path", OsPathKind.Unix)] [TestCase("/rooted/linux/path", OsPathKind.Unix)]
[TestCase("/", OsPathKind.Unix)] [TestCase("/", OsPathKind.Unix)]
[TestCase("linux/path", OsPathKind.Unix)] [TestCase("linux/path", OsPathKind.Unix)]
[TestCase(@"Castle:unrooted+linux+path", OsPathKind.Unknown)]
public void should_auto_detect_kind(string path, OsPathKind kind) public void should_auto_detect_kind(string path, OsPathKind kind)
{ {
var result = new OsPath(path); var result = new OsPath(path);
@@ -94,6 +95,8 @@ namespace NzbDrone.Common.Test
[TestCase(@"rooted\windows\path")] [TestCase(@"rooted\windows\path")]
[TestCase(@"path")] [TestCase(@"path")]
[TestCase("linux/path")] [TestCase("linux/path")]
[TestCase(@"Castle:unrooted+linux+path")]
[TestCase(@"C:unrooted+linux+path")]
public void should_detect_unrooted_ospaths(string path) public void should_detect_unrooted_ospaths(string path)
{ {
var osPath = new OsPath(path); var osPath = new OsPath(path);

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="FluentAssertions" version="3.4.0" targetFramework="net40" /> <package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
<package id="Moq" version="4.0.10827" /> <package id="Moq" version="4.0.10827" />
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
<package id="NUnit" version="2.6.3" targetFramework="net40" /> <package id="NUnit" version="2.6.3" targetFramework="net40" />
</packages> </packages>

View File

@@ -6,8 +6,9 @@ namespace NzbDrone.Common.Cache
{ {
public interface ICacheManager public interface ICacheManager
{ {
ICached<T> GetCache<T>(Type host, string name);
ICached<T> GetCache<T>(Type host); ICached<T> GetCache<T>(Type host);
ICached<T> GetCache<T>(Type host, string name);
ICachedDictionary<T> GetCacheDictionary<T>(Type host, string name, Func<IDictionary<string, T>> fetchFunc = null, TimeSpan? lifeTime = null);
void Clear(); void Clear();
ICollection<ICached> Caches { get; } ICollection<ICached> Caches { get; }
} }
@@ -22,12 +23,6 @@ namespace NzbDrone.Common.Cache
} }
public ICached<T> GetCache<T>(Type host)
{
Ensure.That(host, () => host).IsNotNull();
return GetCache<T>(host, host.FullName);
}
public void Clear() public void Clear()
{ {
_cache.Clear(); _cache.Clear();
@@ -35,6 +30,12 @@ namespace NzbDrone.Common.Cache
public ICollection<ICached> Caches { get { return _cache.Values; } } public ICollection<ICached> Caches { get { return _cache.Values; } }
public ICached<T> GetCache<T>(Type host)
{
Ensure.That(host, () => host).IsNotNull();
return GetCache<T>(host, host.FullName);
}
public ICached<T> GetCache<T>(Type host, string name) public ICached<T> GetCache<T>(Type host, string name)
{ {
Ensure.That(host, () => host).IsNotNull(); Ensure.That(host, () => host).IsNotNull();
@@ -42,5 +43,13 @@ namespace NzbDrone.Common.Cache
return (ICached<T>)_cache.Get(host.FullName + "_" + name, () => new Cached<T>()); return (ICached<T>)_cache.Get(host.FullName + "_" + name, () => new Cached<T>());
} }
public ICachedDictionary<T> GetCacheDictionary<T>(Type host, string name, Func<IDictionary<string, T>> fetchFunc = null, TimeSpan? lifeTime = null)
{
Ensure.That(host, () => host).IsNotNull();
Ensure.That(name, () => name).IsNotNullOrWhiteSpace();
return (ICachedDictionary<T>)_cache.Get("dict_" + host.FullName + "_" + name, () => new CachedDictionary<T>(fetchFunc, lifeTime));
}
} }
} }

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace NzbDrone.Common.Cache
{
public class CachedDictionary<TValue> : ICachedDictionary<TValue>
{
private readonly Func<IDictionary<string, TValue>> _fetchFunc;
private readonly TimeSpan? _ttl;
private DateTime _lastRefreshed = DateTime.MinValue;
private ConcurrentDictionary<string, TValue> _items = new ConcurrentDictionary<string, TValue>();
public CachedDictionary(Func<IDictionary<string, TValue>> fetchFunc = null, TimeSpan? ttl = null)
{
_fetchFunc = fetchFunc;
_ttl = ttl;
}
public bool IsExpired(TimeSpan ttl)
{
return _lastRefreshed.Add(ttl) < DateTime.UtcNow;
}
public void RefreshIfExpired()
{
if (_ttl.HasValue && _fetchFunc != null)
{
RefreshIfExpired(_ttl.Value);
}
}
public void RefreshIfExpired(TimeSpan ttl)
{
if (IsExpired(ttl))
{
Refresh();
}
}
public void Refresh()
{
if (_fetchFunc == null)
{
throw new InvalidOperationException("Cannot update cache without data source.");
}
Update(_fetchFunc());
ExtendTTL();
}
public void Update(IDictionary<string, TValue> items)
{
_items = new ConcurrentDictionary<string, TValue>(items);
ExtendTTL();
}
public void ExtendTTL()
{
_lastRefreshed = DateTime.UtcNow;
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public ICollection<TValue> Values
{
get
{
RefreshIfExpired();
return _items.Values;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public int Count
{
get
{
RefreshIfExpired();
return _items.Count;
}
}
public TValue Get(string key)
{
RefreshIfExpired();
TValue result;
if (!_items.TryGetValue(key, out result))
{
throw new KeyNotFoundException(string.Format("Item {0} not found in cache.", key));
}
return result;
}
public TValue Find(string key)
{
RefreshIfExpired();
TValue result;
_items.TryGetValue(key, out result);
return result;
}
public void Clear()
{
_items.Clear();
_lastRefreshed = DateTime.MinValue;
}
public void ClearExpired()
{
if (!_ttl.HasValue)
{
throw new InvalidOperationException("Checking expiry without ttl not possible.");
}
if (IsExpired(_ttl.Value))
{
Clear();
}
}
public void Remove(string key)
{
TValue item;
_items.TryRemove(key, out item);
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace NzbDrone.Common.Cache
{
public interface ICachedDictionary<TValue> : ICached
{
void RefreshIfExpired();
void RefreshIfExpired(TimeSpan ttl);
void Refresh();
void Update(IDictionary<string, TValue> items);
void ExtendTTL();
TValue Get(string key);
TValue Find(string key);
bool IsExpired(TimeSpan ttl);
}
}

View File

@@ -304,7 +304,7 @@ namespace NzbDrone.Common.Disk
} }
catch (Exception e) catch (Exception e)
{ {
Logger.WarnException(string.Format("Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}", filename, accountSid, rights, controlType), e); Logger.Warn(e, string.Format("Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}", filename, accountSid, rights, controlType));
throw; throw;
} }
@@ -346,12 +346,12 @@ namespace NzbDrone.Common.Disk
public string[] GetFixedDrives() public string[] GetFixedDrives()
{ {
return (DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.Fixed).Select(x => x.Name)).ToArray(); return GetMounts().Where(x => x.DriveType == DriveType.Fixed).Select(x => x.RootDirectory).ToArray();
} }
public string GetVolumeLabel(string path) public string GetVolumeLabel(string path)
{ {
var driveInfo = DriveInfo.GetDrives().SingleOrDefault(d => d.Name == path); var driveInfo = GetMounts().SingleOrDefault(d => d.RootDirectory.PathEquals(path));
if (driveInfo == null) if (driveInfo == null)
{ {
@@ -376,11 +376,36 @@ namespace NzbDrone.Common.Disk
return new FileStream(path, FileMode.Create); return new FileStream(path, FileMode.Create);
} }
public List<DriveInfo> GetDrives() public virtual List<IMount> GetMounts()
{
return GetDriveInfoMounts();
}
public virtual IMount GetMount(string path)
{
try
{
var mounts = GetMounts();
return mounts.Where(drive => drive.RootDirectory.PathEquals(path) ||
drive.RootDirectory.IsParentPath(path))
.OrderByDescending(drive => drive.RootDirectory.Length)
.FirstOrDefault();
}
catch (Exception ex)
{
Logger.Debug(ex, string.Format("Failed to get mount for path {0}", path));
return null;
}
}
protected List<IMount> GetDriveInfoMounts()
{ {
return DriveInfo.GetDrives() return DriveInfo.GetDrives()
.Where(d => d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network) .Where(d => d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable)
.Where(d => d.IsReady) .Where(d => d.IsReady)
.Select(d => new DriveInfoMount(d))
.Cast<IMount>()
.ToList(); .ToList();
} }
@@ -401,5 +426,19 @@ namespace NzbDrone.Common.Disk
return di.GetFiles().ToList(); return di.GetFiles().ToList();
} }
public void RemoveEmptySubfolders(string path)
{
var subfolders = GetDirectories(path);
var files = GetFiles(path, SearchOption.AllDirectories);
foreach (var subfolder in subfolders)
{
if (files.None(f => subfolder.IsParentPath(f)))
{
DeleteFolder(subfolder, false);
}
}
}
} }
} }

View File

@@ -21,6 +21,7 @@ namespace NzbDrone.Common.Disk
{ {
None, None,
VerifyOnly, VerifyOnly,
TryTransactional,
Transactional Transactional
} }
@@ -40,19 +41,21 @@ namespace NzbDrone.Common.Disk
// TODO: Atm we haven't seen partial transfers on windows so we disable verified transfer. // TODO: Atm we haven't seen partial transfers on windows so we disable verified transfer.
// (If enabled in the future, be sure to check specifically for ReFS, which doesn't support hardlinks.) // (If enabled in the future, be sure to check specifically for ReFS, which doesn't support hardlinks.)
VerificationMode = OsInfo.IsWindows ? DiskTransferVerificationMode.VerifyOnly : DiskTransferVerificationMode.Transactional; VerificationMode = OsInfo.IsWindows ? DiskTransferVerificationMode.VerifyOnly : DiskTransferVerificationMode.TryTransactional;
} }
public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, bool verified = true) public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, bool verified = true)
{
var verificationMode = verified ? VerificationMode : DiskTransferVerificationMode.VerifyOnly;
return TransferFolder(sourcePath, targetPath, mode, verificationMode);
}
public TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, DiskTransferVerificationMode verificationMode)
{ {
Ensure.That(sourcePath, () => sourcePath).IsValidPath(); Ensure.That(sourcePath, () => sourcePath).IsValidPath();
Ensure.That(targetPath, () => targetPath).IsValidPath(); Ensure.That(targetPath, () => targetPath).IsValidPath();
if (VerificationMode != DiskTransferVerificationMode.Transactional)
{
verified = false;
}
if (!_diskProvider.FolderExists(targetPath)) if (!_diskProvider.FolderExists(targetPath))
{ {
_diskProvider.CreateFolder(targetPath); _diskProvider.CreateFolder(targetPath);
@@ -62,14 +65,14 @@ namespace NzbDrone.Common.Disk
foreach (var subDir in _diskProvider.GetDirectoryInfos(sourcePath)) foreach (var subDir in _diskProvider.GetDirectoryInfos(sourcePath))
{ {
result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verified); result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verificationMode);
} }
foreach (var sourceFile in _diskProvider.GetFileInfos(sourcePath)) foreach (var sourceFile in _diskProvider.GetFileInfos(sourcePath))
{ {
var destFile = Path.Combine(targetPath, sourceFile.Name); var destFile = Path.Combine(targetPath, sourceFile.Name);
result &= TransferFile(sourceFile.FullName, destFile, mode, true, verified); result &= TransferFile(sourceFile.FullName, destFile, mode, true, verificationMode);
} }
if (mode.HasFlag(TransferMode.Move)) if (mode.HasFlag(TransferMode.Move))
@@ -81,16 +84,20 @@ namespace NzbDrone.Common.Disk
} }
public TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite = false, bool verified = true) public TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite = false, bool verified = true)
{
var verificationMode = verified ? VerificationMode : DiskTransferVerificationMode.None;
return TransferFile(sourcePath, targetPath, mode, overwrite, verificationMode);
}
public TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite, DiskTransferVerificationMode verificationMode)
{ {
Ensure.That(sourcePath, () => sourcePath).IsValidPath(); Ensure.That(sourcePath, () => sourcePath).IsValidPath();
Ensure.That(targetPath, () => targetPath).IsValidPath(); Ensure.That(targetPath, () => targetPath).IsValidPath();
if (VerificationMode != DiskTransferVerificationMode.Transactional)
{
verified = false;
}
_logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath); _logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath);
var originalSize = _diskProvider.GetFileSize(sourcePath);
if (sourcePath == targetPath) if (sourcePath == targetPath)
{ {
@@ -127,6 +134,15 @@ namespace NzbDrone.Common.Disk
return TransferMode.None; return TransferMode.None;
} }
if (sourcePath.GetParentPath() == targetPath.GetParentPath())
{
if (mode.HasFlag(TransferMode.Move))
{
TryMoveFileVerified(sourcePath, targetPath, originalSize);
return TransferMode.Move;
}
}
if (sourcePath.IsParentPath(targetPath)) if (sourcePath.IsParentPath(targetPath))
{ {
throw new IOException(string.Format("Destination cannot be a child of the source [{0}] => [{1}]", sourcePath, targetPath)); throw new IOException(string.Format("Destination cannot be a child of the source [{0}] => [{1}]", sourcePath, targetPath));
@@ -147,81 +163,59 @@ namespace NzbDrone.Common.Disk
} }
} }
if (verified) // We force a transactional transfer if the transfer occurs between mounts and one of the mounts is cifs, it would be a copy anyway.
if (verificationMode == DiskTransferVerificationMode.TryTransactional && OsInfo.IsNotWindows)
{ {
if (mode.HasFlag(TransferMode.Copy)) var sourceMount = _diskProvider.GetMount(sourcePath);
var targetMount = _diskProvider.GetMount(targetPath);
if (sourceMount != null && targetMount != null && sourceMount.RootDirectory != targetMount.RootDirectory &&
(sourceMount.DriveFormat == "cifs" || targetMount.DriveFormat == "cifs"))
{ {
if (TryCopyFile(sourcePath, targetPath)) verificationMode = DiskTransferVerificationMode.Transactional;
}
}
if (mode.HasFlag(TransferMode.Copy))
{
if (verificationMode == DiskTransferVerificationMode.Transactional || verificationMode == DiskTransferVerificationMode.TryTransactional)
{
if (TryCopyFileTransactional(sourcePath, targetPath, originalSize))
{ {
return TransferMode.Copy; return TransferMode.Copy;
} }
}
if (mode.HasFlag(TransferMode.Move)) throw new IOException(string.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
}
else if (verificationMode == DiskTransferVerificationMode.VerifyOnly)
{ {
if (TryMoveFile(sourcePath, targetPath)) TryCopyFileVerified(sourcePath, targetPath, originalSize);
{ return TransferMode.Copy;
return TransferMode.Move;
}
} }
else
throw new IOException(string.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
}
else if (VerificationMode == DiskTransferVerificationMode.VerifyOnly)
{
var originalSize = _diskProvider.GetFileSize(sourcePath);
if (mode.HasFlag(TransferMode.Copy))
{
try
{
_diskProvider.CopyFile(sourcePath, targetPath);
var targetSize = _diskProvider.GetFileSize(targetPath);
if (targetSize != originalSize)
{
throw new IOException(string.Format("File copy incomplete. [{0}] was {1} bytes long instead of {2} bytes.", targetPath, targetSize, originalSize));
}
return TransferMode.Copy;
}
catch
{
RollbackCopy(sourcePath, targetPath);
throw;
}
}
if (mode.HasFlag(TransferMode.Move))
{
try
{
_diskProvider.MoveFile(sourcePath, targetPath);
var targetSize = _diskProvider.GetFileSize(targetPath);
if (targetSize != originalSize)
{
throw new IOException(string.Format("File copy incomplete, data loss may have occured. [{0}] was {1} bytes long instead of the expected {2}.", targetPath, targetSize, originalSize));
}
return TransferMode.Move;
}
catch
{
RollbackPartialMove(sourcePath, targetPath);
throw;
}
}
}
else
{
if (mode.HasFlag(TransferMode.Copy))
{ {
_diskProvider.CopyFile(sourcePath, targetPath); _diskProvider.CopyFile(sourcePath, targetPath);
return TransferMode.Copy; return TransferMode.Copy;
} }
}
if (mode.HasFlag(TransferMode.Move)) if (mode.HasFlag(TransferMode.Move))
{
if (verificationMode == DiskTransferVerificationMode.Transactional || verificationMode == DiskTransferVerificationMode.TryTransactional)
{
if (TryMoveFileTransactional(sourcePath, targetPath, originalSize, verificationMode))
{
return TransferMode.Move;
}
throw new IOException(string.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
}
else if (verificationMode == DiskTransferVerificationMode.VerifyOnly)
{
TryMoveFileVerified(sourcePath, targetPath, originalSize);
return TransferMode.Move;
}
else
{ {
_diskProvider.MoveFile(sourcePath, targetPath); _diskProvider.MoveFile(sourcePath, targetPath);
return TransferMode.Move; return TransferMode.Move;
@@ -265,7 +259,7 @@ namespace NzbDrone.Common.Disk
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException(string.Format("Failed to properly rollback the file move [{0}] to [{1}], incomplete file may be left in target path.", sourcePath, targetPath), ex); _logger.Error(ex, string.Format("Failed to properly rollback the file move [{0}] to [{1}], incomplete file may be left in target path.", sourcePath, targetPath));
} }
} }
@@ -281,7 +275,7 @@ namespace NzbDrone.Common.Disk
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException(string.Format("Failed to properly rollback the file move [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath), ex); _logger.Error(ex, string.Format("Failed to properly rollback the file move [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath));
} }
} }
@@ -300,7 +294,7 @@ namespace NzbDrone.Common.Disk
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException(string.Format("Failed to properly rollback the file copy [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath), ex); _logger.Error(ex, string.Format("Failed to properly rollback the file copy [{0}] to [{1}], file may be left in target path.", sourcePath, targetPath));
} }
} }
@@ -310,10 +304,8 @@ namespace NzbDrone.Common.Disk
Thread.Sleep(3000); Thread.Sleep(3000);
} }
private bool TryCopyFile(string sourcePath, string targetPath) private bool TryCopyFileTransactional(string sourcePath, string targetPath, long originalSize)
{ {
var originalSize = _diskProvider.GetFileSize(sourcePath);
var tempTargetPath = targetPath + ".partial~"; var tempTargetPath = targetPath + ".partial~";
if (_diskProvider.FileExists(tempTargetPath)) if (_diskProvider.FileExists(tempTargetPath))
@@ -367,10 +359,8 @@ namespace NzbDrone.Common.Disk
return false; return false;
} }
private bool TryMoveFile(string sourcePath, string targetPath) private bool TryMoveFileTransactional(string sourcePath, string targetPath, long originalSize, DiskTransferVerificationMode verificationMode)
{ {
var originalSize = _diskProvider.GetFileSize(sourcePath);
var backupPath = sourcePath + ".backup~"; var backupPath = sourcePath + ".backup~";
var tempTargetPath = targetPath + ".partial~"; var tempTargetPath = targetPath + ".partial~";
@@ -423,16 +413,63 @@ namespace NzbDrone.Common.Disk
} }
} }
_logger.Trace("Hardlink move failed, reverting to copy."); if (verificationMode == DiskTransferVerificationMode.Transactional)
if (TryCopyFile(sourcePath, targetPath))
{ {
_logger.Trace("Copy succeeded, deleting source."); _logger.Trace("Hardlink move failed, reverting to copy.");
_diskProvider.DeleteFile(sourcePath); if (TryCopyFileTransactional(sourcePath, targetPath, originalSize))
{
_logger.Trace("Copy succeeded, deleting source.");
_diskProvider.DeleteFile(sourcePath);
return true;
}
}
else
{
_logger.Trace("Hardlink move failed, reverting to move.");
TryMoveFileVerified(sourcePath, targetPath, originalSize);
return true; return true;
} }
_logger.Trace("Copy failed."); _logger.Trace("Move failed.");
return false; return false;
} }
private void TryCopyFileVerified(string sourcePath, string targetPath, long originalSize)
{
try
{
_diskProvider.CopyFile(sourcePath, targetPath);
var targetSize = _diskProvider.GetFileSize(targetPath);
if (targetSize != originalSize)
{
throw new IOException(string.Format("File copy incomplete. [{0}] was {1} bytes long instead of {2} bytes.", targetPath, targetSize, originalSize));
}
}
catch
{
RollbackCopy(sourcePath, targetPath);
throw;
}
}
private void TryMoveFileVerified(string sourcePath, string targetPath, long originalSize)
{
try
{
_diskProvider.MoveFile(sourcePath, targetPath);
var targetSize = _diskProvider.GetFileSize(targetPath);
if (targetSize != originalSize)
{
throw new IOException(string.Format("File move incomplete, data loss may have occurred. [{0}] was {1} bytes long instead of the expected {2}.", targetPath, targetSize, originalSize));
}
}
catch
{
RollbackPartialMove(sourcePath, targetPath);
throw;
}
}
} }
} }

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Disk
{
public class DriveInfoMount : IMount
{
private readonly DriveInfo _driveInfo;
public DriveInfoMount(DriveInfo driveInfo)
{
_driveInfo = driveInfo;
}
public long AvailableFreeSpace
{
get { return _driveInfo.AvailableFreeSpace; }
}
public string DriveFormat
{
get { return _driveInfo.DriveFormat; }
}
public DriveType DriveType
{
get { return _driveInfo.DriveType; }
}
public bool IsReady
{
get { return _driveInfo.IsReady; }
}
public string Name
{
get { return _driveInfo.Name; }
}
public string RootDirectory
{
get { return _driveInfo.RootDirectory.FullName; }
}
public long TotalFreeSpace
{
get { return _driveInfo.TotalFreeSpace; }
}
public long TotalSize
{
get { return _driveInfo.TotalSize; }
}
public string VolumeLabel
{
get { return _driveInfo.VolumeLabel; }
}
public string VolumeName
{
get
{
if (VolumeLabel.IsNullOrWhiteSpace())
{
return Name;
}
return string.Format("{0} ({1})", Name, VolumeLabel);
}
}
}
}

View File

@@ -103,12 +103,12 @@ namespace NzbDrone.Common.Disk
private List<FileSystemModel> GetDrives() private List<FileSystemModel> GetDrives()
{ {
return _diskProvider.GetDrives() return _diskProvider.GetMounts()
.Select(d => new FileSystemModel .Select(d => new FileSystemModel
{ {
Type = FileSystemEntityType.Drive, Type = FileSystemEntityType.Drive,
Name = GetVolumeName(d), Name = d.VolumeLabel,
Path = d.Name, Path = d.RootDirectory,
LastModified = null LastModified = null
}) })
.ToList(); .ToList();
@@ -157,16 +157,6 @@ namespace NzbDrone.Common.Disk
return path; return path;
} }
private string GetVolumeName(DriveInfo driveInfo)
{
if (driveInfo.VolumeLabel.IsNullOrWhiteSpace())
{
return driveInfo.Name;
}
return string.Format("{0} ({1})", driveInfo.Name, driveInfo.VolumeLabel);
}
private string GetParent(string path) private string GetParent(string path)
{ {

View File

@@ -40,12 +40,13 @@ namespace NzbDrone.Common.Disk
void SetPermissions(string filename, WellKnownSidType accountSid, FileSystemRights rights, AccessControlType controlType); void SetPermissions(string filename, WellKnownSidType accountSid, FileSystemRights rights, AccessControlType controlType);
FileAttributes GetFileAttributes(string path); FileAttributes GetFileAttributes(string path);
void EmptyFolder(string path); void EmptyFolder(string path);
string[] GetFixedDrives();
string GetVolumeLabel(string path); string GetVolumeLabel(string path);
FileStream OpenReadStream(string path); FileStream OpenReadStream(string path);
FileStream OpenWriteStream(string path); FileStream OpenWriteStream(string path);
List<DriveInfo> GetDrives(); List<IMount> GetMounts();
IMount GetMount(string path);
List<DirectoryInfo> GetDirectoryInfos(string path); List<DirectoryInfo> GetDirectoryInfos(string path);
List<FileInfo> GetFileInfos(string path); List<FileInfo> GetFileInfos(string path);
void RemoveEmptySubfolders(string path);
} }
} }

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace NzbDrone.Common.Disk
{
public interface IMount
{
long AvailableFreeSpace { get; }
string DriveFormat { get; }
DriveType DriveType { get; }
bool IsReady { get; }
string Name { get; }
string RootDirectory { get; }
long TotalFreeSpace { get; }
long TotalSize { get; }
string VolumeLabel { get; }
}
}

View File

@@ -44,7 +44,7 @@ namespace NzbDrone.Common.Disk
{ {
return OsPathKind.Unix; return OsPathKind.Unix;
} }
if (path.Contains(':') || path.Contains('\\')) if (HasWindowsDriveLetter(path) || path.Contains('\\'))
{ {
return OsPathKind.Windows; return OsPathKind.Windows;
} }
@@ -55,6 +55,15 @@ namespace NzbDrone.Common.Disk
return OsPathKind.Unknown; return OsPathKind.Unknown;
} }
private static bool HasWindowsDriveLetter(string path)
{
if (path.Length < 2) return false;
if (!char.IsLetter(path[0]) || path[1] != ':') return false;
if (path.Length > 2 && path[2] != '\\' && path[2] != '/') return false;
return true;
}
private static string FixSlashes(string path, OsPathKind kind) private static string FixSlashes(string path, OsPathKind kind)
{ {
switch (kind) switch (kind)
@@ -97,7 +106,7 @@ namespace NzbDrone.Common.Disk
{ {
if (IsWindowsPath) if (IsWindowsPath)
{ {
return _path.StartsWith(@"\\") || _path.Contains(':'); return _path.StartsWith(@"\\") || HasWindowsDriveLetter(_path);
} }
if (IsUnixPath) if (IsUnixPath)
{ {
@@ -153,6 +162,14 @@ namespace NzbDrone.Common.Disk
} }
} }
public bool IsValid
{
get
{
return _path.IsPathValid();
}
}
private int GetFileNameIndex() private int GetFileNameIndex()
{ {
if (_path.Length < 2) if (_path.Length < 2)

View File

@@ -43,7 +43,7 @@ namespace NzbDrone.Common.EnvironmentInfo
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.WarnException("Coudn't set app folder permission", ex); _logger.Warn(ex, "Coudn't set app folder permission");
} }
} }
} }

View File

@@ -60,7 +60,7 @@ namespace NzbDrone.Common.EnvironmentInfo
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.WarnException("Error checking if the current user is an administrator.", ex); _logger.Warn(ex, "Error checking if the current user is an administrator.");
return false; return false;
} }
} }

View File

@@ -73,8 +73,14 @@ namespace NzbDrone.Common.Extensions
public static bool IsParentPath(this string parentPath, string childPath) public static bool IsParentPath(this string parentPath, string childPath)
{ {
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar); if (parentPath != "/")
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar); {
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
}
if (childPath != "/")
{
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
}
var parent = new DirectoryInfo(parentPath); var parent = new DirectoryInfo(parentPath);
var child = new DirectoryInfo(childPath); var child = new DirectoryInfo(childPath);

View File

@@ -92,5 +92,13 @@ namespace NzbDrone.Common.Extensions
return "\"" + text + "\""; return "\"" + text + "\"";
} }
public static byte[] HexToByteArray(this string input)
{
return Enumerable.Range(0, input.Length)
.Where(x => x%2 == 0)
.Select(x => Convert.ToByte(input.Substring(x, 2), 16))
.ToArray();
}
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@@ -28,7 +29,7 @@ namespace NzbDrone.Common.Http.Dispatchers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.TraceException("Initializing curl failed", ex); _logger.Trace(ex, "Initializing curl failed");
return false; return false;
} }
} }
@@ -111,7 +112,15 @@ namespace NzbDrone.Common.Http.Dispatchers
if (result != CurlCode.Ok) if (result != CurlCode.Ok)
{ {
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url)); switch (result)
{
case CurlCode.SslCaCert:
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));
default:
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
}
} }
} }
@@ -164,7 +173,14 @@ namespace NzbDrone.Common.Http.Dispatchers
var setCookie = webHeaderCollection.Get("Set-Cookie"); var setCookie = webHeaderCollection.Get("Set-Cookie");
if (setCookie != null && setCookie.Length > 0 && cookies != null) if (setCookie != null && setCookie.Length > 0 && cookies != null)
{ {
cookies.SetCookies(request.Url, FixSetCookieHeader(setCookie)); try
{
cookies.SetCookies(request.Url, FixSetCookieHeader(setCookie));
}
catch (CookieException ex)
{
_logger.Debug("Rejected cookie {0}: {1}", ex.InnerException.Message, setCookie);
}
} }
return webHeaderCollection; return webHeaderCollection;
@@ -175,9 +191,15 @@ namespace NzbDrone.Common.Http.Dispatchers
// fix up the date if it was malformed // fix up the date if it was malformed
var setCookieClean = ExpiryDate.Replace(setCookie, delegate(Match match) var setCookieClean = ExpiryDate.Replace(setCookie, delegate(Match match)
{ {
string format = "ddd, dd-MMM-yyyy HH:mm:ss"; string shortFormat = "ddd, dd-MMM-yy HH:mm:ss";
DateTime dt = Convert.ToDateTime(match.Groups[2].Value); string longFormat = "ddd, dd-MMM-yyyy HH:mm:ss";
return match.Groups[1].Value + dt.ToUniversalTime().ToString(format) + " GMT"; DateTime dt;
if (DateTime.TryParseExact(match.Groups[2].Value, longFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
DateTime.TryParseExact(match.Groups[2].Value, shortFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
DateTime.TryParse(match.Groups[2].Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt))
return match.Groups[1].Value + dt.ToUniversalTime().ToString(longFormat, CultureInfo.InvariantCulture) + " GMT";
else
return match.Value;
}); });
return setCookieClean; return setCookieClean;
} }

View File

@@ -100,7 +100,6 @@ namespace NzbDrone.Common.Http.Dispatchers
break; break;
case "Range": case "Range":
throw new NotImplementedException(); throw new NotImplementedException();
break;
case "Referer": case "Referer":
webRequest.Referer = header.Value.ToString(); webRequest.Referer = header.Value.ToString();
break; break;
@@ -111,7 +110,6 @@ namespace NzbDrone.Common.Http.Dispatchers
throw new NotSupportedException("User-Agent other than Sonarr not allowed."); throw new NotSupportedException("User-Agent other than Sonarr not allowed.");
case "Proxy-Connection": case "Proxy-Connection":
throw new NotImplementedException(); throw new NotImplementedException();
break;
default: default:
webRequest.Headers.Add(header.Key, header.Value.ToString()); webRequest.Headers.Add(header.Key, header.Value.ToString());
break; break;

View File

@@ -28,7 +28,6 @@ namespace NzbDrone.Common.Http
private readonly Logger _logger; private readonly Logger _logger;
private readonly IRateLimitService _rateLimitService; private readonly IRateLimitService _rateLimitService;
private readonly ICached<CookieContainer> _cookieContainerCache; private readonly ICached<CookieContainer> _cookieContainerCache;
private readonly ICached<bool> _curlTLSFallbackCache;
private readonly List<IHttpRequestInterceptor> _requestInterceptors; private readonly List<IHttpRequestInterceptor> _requestInterceptors;
private readonly IHttpDispatcher _httpDispatcher; private readonly IHttpDispatcher _httpDispatcher;
@@ -177,7 +176,7 @@ namespace NzbDrone.Common.Http
} }
catch (Exception e) catch (Exception e)
{ {
_logger.WarnException("Failed to get response from: " + url, e); _logger.Warn(e, "Failed to get response from: " + url);
throw; throw;
} }
} }

View File

@@ -53,7 +53,7 @@ namespace NzbDrone.Common.Http
} }
catch (Exception e) catch (Exception e)
{ {
_logger.WarnException("Failed to get response from: " + url, e); _logger.Warn(e, "Failed to get response from: " + url);
throw; throw;
} }
} }

View File

@@ -14,7 +14,18 @@ namespace NzbDrone.Common.Http
{ {
if (response.Headers.ContainsKey("Retry-After")) if (response.Headers.ContainsKey("Retry-After"))
{ {
RetryAfter = TimeSpan.FromSeconds(int.Parse(response.Headers["Retry-After"].ToString())); var retryAfter = response.Headers["Retry-After"].ToString();
int seconds;
DateTime date;
if (int.TryParse(retryAfter, out seconds))
{
RetryAfter = TimeSpan.FromSeconds(seconds);
}
else if (DateTime.TryParse(retryAfter, out date))
{
RetryAfter = date.ToUniversalTime() - DateTime.UtcNow;
}
} }
} }
} }

View File

@@ -9,10 +9,12 @@ namespace NzbDrone.Common.Instrumentation
private static readonly Regex[] CleansingRules = new[] private static readonly Regex[] CleansingRules = new[]
{ {
// Url // Url
new Regex(@"(?<=\?|&)(apikey|token|passkey|uid|api)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"(?<=\?|&)(apikey|token|passkey|auth|authkey|user|uid|api)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"torrentleech\.org/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
// Path // Path
new Regex(@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
@@ -26,7 +28,7 @@ namespace NzbDrone.Common.Instrumentation
new Regex(@"""email_(account|to|from|pwd)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"""email_(account|to|from|pwd)""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// uTorrent // uTorrent
new Regex(@"\[""[a-z._]*(|username|password)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"\[""[a-z._]*(username|password)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new Regex(@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// BroadcastheNet // BroadcastheNet
@@ -48,7 +50,7 @@ namespace NzbDrone.Common.Instrumentation
var value = m.Value; var value = m.Value;
foreach (var capture in m.Groups["secret"].Captures.OfType<Capture>().Reverse()) foreach (var capture in m.Groups["secret"].Captures.OfType<Capture>().Reverse())
{ {
value = value.Replace(capture.Index - m.Index, capture.Length, "<removed>"); value = value.Replace(capture.Index - m.Index, capture.Length, "(removed)");
} }
return value; return value;

View File

@@ -19,7 +19,7 @@ namespace NzbDrone.Common.Instrumentation
var exception = e.Exception; var exception = e.Exception;
Console.WriteLine("Task Error: {0}", exception); Console.WriteLine("Task Error: {0}", exception);
Logger.Error("Task Error: " + exception.Message, exception); Logger.Error(exception, "Task Error: " + exception.Message);
} }
private static void HandleAppDomainException(object sender, UnhandledExceptionEventArgs e) private static void HandleAppDomainException(object sender, UnhandledExceptionEventArgs e)
@@ -40,13 +40,13 @@ namespace NzbDrone.Common.Instrumentation
if (exception is TypeInitializationException && exception.InnerException is DllNotFoundException || if (exception is TypeInitializationException && exception.InnerException is DllNotFoundException ||
exception is DllNotFoundException) exception is DllNotFoundException)
{ {
Logger.DebugException("Minor Fail: " + exception.Message, exception); Logger.Debug(exception, "Minor Fail: " + exception.Message);
return; return;
} }
} }
Console.WriteLine("EPIC FAIL: {0}", exception); Console.WriteLine("EPIC FAIL: {0}", exception);
Logger.FatalException("EPIC FAIL: " + exception.Message, exception); Logger.Fatal(exception, "EPIC FAIL: " + exception.Message);
} }
} }
} }

View File

@@ -76,7 +76,7 @@ namespace NzbDrone.Common.Instrumentation
{ {
DebuggerTarget target = new DebuggerTarget(); DebuggerTarget target = new DebuggerTarget();
target.Name = "debuggerLogger"; target.Name = "debuggerLogger";
target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}"; target.Layout = "[${level}] [${threadid}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}}";
var loggingRule = new LoggingRule("*", LogLevel.Trace, target); var loggingRule = new LoggingRule("*", LogLevel.Trace, target);
LogManager.Configuration.AddTarget("debugger", target); LogManager.Configuration.AddTarget("debugger", target);
@@ -91,7 +91,7 @@ namespace NzbDrone.Common.Instrumentation
var coloredConsoleTarget = new ColoredConsoleTarget(); var coloredConsoleTarget = new ColoredConsoleTarget();
coloredConsoleTarget.Name = "consoleLogger"; coloredConsoleTarget.Name = "consoleLogger";
coloredConsoleTarget.Layout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}"; coloredConsoleTarget.Layout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}}";
var loggingRule = new LoggingRule("*", level, coloredConsoleTarget); var loggingRule = new LoggingRule("*", level, coloredConsoleTarget);
@@ -99,29 +99,38 @@ namespace NzbDrone.Common.Instrumentation
LogManager.Configuration.LoggingRules.Add(loggingRule); LogManager.Configuration.LoggingRules.Add(loggingRule);
} }
const string FILE_LOG_LAYOUT = @"${date:format=yy-M-d HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}"; const string FILE_LOG_LAYOUT = @"${date:format=yy-M-d HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}}";
private static void RegisterAppFile(IAppFolderInfo appFolderInfo) private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{
RegisterAppFile(appFolderInfo, "appFileInfo", "sonarr.txt", 5);
RegisterAppFile(appFolderInfo, "appFileDebug", "sonarr.debug.txt", 50);
RegisterAppFile(appFolderInfo, "appFileTrace", "sonarr.trace.txt", 50);
}
private static LoggingRule RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles)
{ {
var fileTarget = new NzbDroneFileTarget(); var fileTarget = new NzbDroneFileTarget();
fileTarget.Name = "rollingFileLogger"; fileTarget.Name = name;
fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), "nzbdrone.txt"); fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), fileName);
fileTarget.AutoFlush = true; fileTarget.AutoFlush = true;
fileTarget.KeepFileOpen = false; fileTarget.KeepFileOpen = false;
fileTarget.ConcurrentWrites = false; fileTarget.ConcurrentWrites = false;
fileTarget.ConcurrentWriteAttemptDelay = 50; fileTarget.ConcurrentWriteAttemptDelay = 50;
fileTarget.ConcurrentWriteAttempts = 10; fileTarget.ConcurrentWriteAttempts = 10;
fileTarget.ArchiveAboveSize = 1024000; fileTarget.ArchiveAboveSize = 1024000;
fileTarget.MaxArchiveFiles = 5; fileTarget.MaxArchiveFiles = maxArchiveFiles;
fileTarget.EnableFileDelete = true; fileTarget.EnableFileDelete = true;
fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling; fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling;
fileTarget.Layout = FILE_LOG_LAYOUT; fileTarget.Layout = FILE_LOG_LAYOUT;
var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget); var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget);
LogManager.Configuration.AddTarget("appfile", fileTarget); LogManager.Configuration.AddTarget(name, fileTarget);
LogManager.Configuration.LoggingRules.Add(loggingRule); LogManager.Configuration.LoggingRules.Add(loggingRule);
return loggingRule;
} }
private static void RegisterUpdateFile(IAppFolderInfo appFolderInfo) private static void RegisterUpdateFile(IAppFolderInfo appFolderInfo)

View File

@@ -43,6 +43,10 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" /> <Reference Include="System.Configuration.Install" />
@@ -51,10 +55,7 @@
<Reference Include="System.ServiceProcess" /> <Reference Include="System.ServiceProcess" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="ICSharpCode.SharpZipLib"> <Reference Include="ICSharpCode.SharpZipLib">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath> <HintPath>..\packages\ICSharpCode.SharpZipLib.Patched.0.86.5\lib\net20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@@ -63,7 +64,9 @@
<Compile Include="ArchiveService.cs" /> <Compile Include="ArchiveService.cs" />
<Compile Include="Cache\Cached.cs" /> <Compile Include="Cache\Cached.cs" />
<Compile Include="Cache\CacheManager.cs" /> <Compile Include="Cache\CacheManager.cs" />
<Compile Include="Cache\CachedDictionary.cs" />
<Compile Include="Cache\ICached.cs" /> <Compile Include="Cache\ICached.cs" />
<Compile Include="Cache\ICachedDictionary.cs" />
<Compile Include="Cloud\CloudClient.cs" /> <Compile Include="Cloud\CloudClient.cs" />
<Compile Include="Composition\Container.cs" /> <Compile Include="Composition\Container.cs" />
<Compile Include="Composition\ContainerBuilderBase.cs" /> <Compile Include="Composition\ContainerBuilderBase.cs" />
@@ -72,6 +75,8 @@
<Compile Include="ConvertBase32.cs" /> <Compile Include="ConvertBase32.cs" />
<Compile Include="Crypto\HashProvider.cs" /> <Compile Include="Crypto\HashProvider.cs" />
<Compile Include="Disk\FileSystemLookupService.cs" /> <Compile Include="Disk\FileSystemLookupService.cs" />
<Compile Include="Disk\DriveInfoMount.cs" />
<Compile Include="Disk\IMount.cs" />
<Compile Include="Disk\RelativeFileSystemModel.cs" /> <Compile Include="Disk\RelativeFileSystemModel.cs" />
<Compile Include="Disk\FileSystemModel.cs" /> <Compile Include="Disk\FileSystemModel.cs" />
<Compile Include="Disk\FileSystemResult.cs" /> <Compile Include="Disk\FileSystemResult.cs" />
@@ -229,7 +234,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -37,7 +37,7 @@ namespace NzbDrone.Common.Processes
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error("Unable to write PID file: " + filename, ex); _logger.Error(ex, "Unable to write PID file: " + filename);
throw; throw;
} }
} }

View File

@@ -292,7 +292,7 @@ namespace NzbDrone.Common.Processes
} }
catch (Win32Exception e) catch (Win32Exception e)
{ {
_logger.WarnException("Couldn't get process info for " + process.ProcessName, e); _logger.Warn(e, "Couldn't get process info for " + process.ProcessName);
} }
return processInfo; return processInfo;

View File

@@ -17,7 +17,7 @@ namespace NzbDrone.Common.TPL
var aggregateException = t.Exception.Flatten(); var aggregateException = t.Exception.Flatten();
foreach (var exception in aggregateException.InnerExceptions) foreach (var exception in aggregateException.InnerExceptions)
{ {
Logger.ErrorException("Task Error", exception); Logger.Error(exception, "Task Error");
} }
} }
}, TaskContinuationOptions.OnlyOnFaulted); }, TaskContinuationOptions.OnlyOnFaulted);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="ICSharpCode.SharpZipLib.Patched" version="0.86.5" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" /> <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net40" />
</packages> </packages>

View File

@@ -32,7 +32,7 @@ namespace NzbDrone.Console
{ {
System.Console.WriteLine(""); System.Console.WriteLine("");
System.Console.WriteLine(""); System.Console.WriteLine("");
Logger.FatalException("EPIC FAIL!", e); Logger.Fatal(e, "EPIC FAIL!");
System.Console.WriteLine("Press any key to exit..."); System.Console.WriteLine("Press any key to exit...");
System.Console.ReadLine(); System.Console.ReadLine();
Environment.Exit(1); Environment.Exit(1);

View File

@@ -75,11 +75,12 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.1.0\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Owin"> <Reference Include="Owin">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath> <HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference> </Reference>
@@ -143,7 +144,6 @@
<PreBuildEvent> <PreBuildEvent>
</PreBuildEvent> </PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -3,6 +3,6 @@
<package id="Microsoft.Owin" version="2.1.0" targetFramework="net40" /> <package id="Microsoft.Owin" version="2.1.0" targetFramework="net40" />
<package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net40" /> <package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" /> <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" /> <package id="NLog" version="4.2.3" targetFramework="net40" />
<package id="Owin" version="1.0" targetFramework="net40" /> <package id="Owin" version="1.0" targetFramework="net40" />
</packages> </packages>

View File

@@ -1,110 +1,59 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Configuration namespace NzbDrone.Core.Test.Configuration
{ {
[TestFixture] [TestFixture]
public class ConfigServiceFixture : DbTest<ConfigService, Config> public class ConfigServiceFixture : TestBase<ConfigService>
{ {
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
Mocker.SetConstant<IConfigRepository>(Mocker.Resolve<ConfigRepository>());
Db.All<Config>().ForEach(Db.Delete);
} }
[Test] [Test]
public void Add_new_value_to_database() public void Add_new_value_to_database()
{ {
const string key = "MY_KEY"; const string key = "RssSyncInterval";
const string value = "MY_VALUE"; const int value = 12;
Subject.SetValue(key, value); Subject.RssSyncInterval = value;
Subject.GetValue(key, "").Should().Be(value);
}
[Test] AssertUpsert(key, value);
public void Get_value_from_database()
{
const string key = "MY_KEY";
const string value = "MY_VALUE";
Db.Insert(new Config { Key = key, Value = value });
Db.Insert(new Config { Key = "Other Key", Value = "OtherValue" });
var result = Subject.GetValue(key, "");
result.Should().Be(value);
} }
[Test] [Test]
public void Get_value_should_return_default_when_no_value() public void Get_value_should_return_default_when_no_value()
{ {
const string key = "MY_KEY"; Subject.RssSyncInterval.Should().Be(15);
const string value = "MY_VALUE";
var result = Subject.GetValue(key, value);
result.Should().Be(value);
}
[Test]
public void New_value_should_update_old_value_new_value()
{
const string key = "MY_KEY";
const string originalValue = "OLD_VALUE";
const string newValue = "NEW_VALUE";
Db.Insert(new Config { Key = key, Value = originalValue });
Subject.SetValue(key, newValue);
var result = Subject.GetValue(key, "");
result.Should().Be(newValue);
AllStoredModels.Should().HaveCount(1);
}
[Test]
public void New_value_should_update_old_value_same_value()
{
const string key = "MY_KEY";
const string value = "OLD_VALUE";
Subject.SetValue(key, value);
Subject.SetValue(key, value);
var result = Subject.GetValue(key, "");
result.Should().Be(value);
AllStoredModels.Should().HaveCount(1);
} }
[Test] [Test]
public void get_value_with_persist_should_store_default_value() public void get_value_with_persist_should_store_default_value()
{ {
const string key = "MY_KEY"; var salt = Subject.HmacSalt;
string value = Guid.NewGuid().ToString(); salt.Should().NotBeNullOrWhiteSpace();
AssertUpsert("HmacSalt", salt);
Subject.GetValue(key, value, persist: true).Should().Be(value);
Subject.GetValue(key, string.Empty).Should().Be(value);
} }
[Test] [Test]
public void get_value_with_out_persist_should_not_store_default_value() public void get_value_with_out_persist_should_not_store_default_value()
{ {
const string key = "MY_KEY"; var interval = Subject.RssSyncInterval;
string value1 = Guid.NewGuid().ToString(); interval.Should().Be(15);
string value2 = Guid.NewGuid().ToString(); Mocker.GetMock<IConfigRepository>().Verify(c => c.Insert(It.IsAny<Config>()), Times.Never());
}
Subject.GetValue(key, value1).Should().Be(value1); private void AssertUpsert(string key, object value)
Subject.GetValue(key, value2).Should().Be(value2); {
Mocker.GetMock<IConfigRepository>().Verify(c => c.Upsert(key.ToLowerInvariant(), value.ToString()));
} }
[Test] [Test]
@@ -114,7 +63,16 @@ namespace NzbDrone.Core.Test.Configuration
var configProvider = Subject; var configProvider = Subject;
var allProperties = typeof(ConfigService).GetProperties().Where(p => p.GetSetMethod() != null).ToList(); var allProperties = typeof(ConfigService).GetProperties().Where(p => p.GetSetMethod() != null).ToList();
var keys = new List<string>();
var values = new List<Config>();
Mocker.GetMock<IConfigRepository>().Setup(c => c.Upsert(It.IsAny<string>(), It.IsAny<string>())).Callback<string, string>((key, value) =>
{
keys.Add(key);
values.Add(new Config { Key = key, Value = value });
});
Mocker.GetMock<IConfigRepository>().Setup(c => c.All()).Returns(values);
foreach (var propertyInfo in allProperties) foreach (var propertyInfo in allProperties)
{ {
@@ -148,8 +106,7 @@ namespace NzbDrone.Core.Test.Configuration
returnValue.Should().Be(value, propertyInfo.Name); returnValue.Should().Be(value, propertyInfo.Name);
} }
AllStoredModels.Should() keys.Should().OnlyHaveUniqueItems();
.HaveSameCount(allProperties, "two different properties are writing to the same key in db. Copy/Past fail.");
} }
} }
} }

View File

@@ -124,7 +124,7 @@ namespace NzbDrone.Core.Test.DataAugmentation.Scene
.Returns(Builder<SceneMapping>.CreateListOfSize(1).Build()); .Returns(Builder<SceneMapping>.CreateListOfSize(1).Build());
Subject.HandleAsync(new ApplicationStartedEvent()); Subject.Execute(new UpdateSceneMappingCommand());
Mocker.GetMock<ISceneMappingRepository>() Mocker.GetMock<ISceneMappingRepository>()
.Verify(v => v.All(), Times.Once()); .Verify(v => v.All(), Times.Once());

View File

@@ -11,6 +11,7 @@ using NzbDrone.Core.DataAugmentation.Xem.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events; using NzbDrone.Core.Tv.Events;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
{ {
@@ -144,6 +145,25 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
Mocker.GetMock<ISeriesService>() Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never()); .Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_not_clear_scenenumbering_if_thexem_throws()
{
GivenExistingMapping();
Mocker.GetMock<IXemProxy>()
.Setup(v => v.GetXemSeriesIds())
.Throws(new InvalidOperationException());
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
ExceptionVerification.ExpectedWarns(1);
} }
[Test] [Test]

View File

@@ -2,11 +2,7 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
@@ -16,7 +12,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[Test] [Test]
public void should_migrate_old_delays() public void should_migrate_old_delays()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new c.Insert.IntoTable("Profiles").Row(new
{ {
@@ -35,10 +31,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var allProfiles = Mocker.Resolve<DelayProfileRepository>().All().ToList(); var allProfiles = db.Query<DelayProfile70>("SELECT * FROM DelayProfiles");
allProfiles.Should().HaveCount(3); allProfiles.Should().HaveCount(3);
allProfiles.Should().OnlyContain(c => c.PreferredProtocol == DownloadProtocol.Usenet); allProfiles.Should().OnlyContain(c => c.PreferredProtocol == 1);
allProfiles.Should().OnlyContain(c => c.TorrentDelay == 0); allProfiles.Should().OnlyContain(c => c.TorrentDelay == 0);
allProfiles.Should().Contain(c => c.UsenetDelay == 60); allProfiles.Should().Contain(c => c.UsenetDelay == 60);
allProfiles.Should().Contain(c => c.UsenetDelay == 120); allProfiles.Should().Contain(c => c.UsenetDelay == 120);
@@ -47,17 +43,18 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[Test] [Test]
public void should_create_tag_for_delay_profile() public void should_create_tag_for_delay_profile()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new c.Insert.IntoTable("Profiles").Row(new
{ {
GrabDelay = 1, GrabDelay = 1,
Name = "OneHour", Name = "OneHour",
Cutoff = 0, Cutoff = 0,
Items = "[]" Items = "[]"
}) });
); });
var tags = Mocker.Resolve<TagRepository>().All().ToList(); var tags = db.Query<Tag69>("SELECT * FROM Tags");
tags.Should().HaveCount(1); tags.Should().HaveCount(1);
tags.First().Label.Should().Be("delay-60"); tags.First().Label.Should().Be("delay-60");
@@ -66,7 +63,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[Test] [Test]
public void should_add_tag_to_series_that_had_a_profile_with_delay_attached() public void should_add_tag_to_series_that_had_a_profile_with_delay_attached()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new c.Insert.IntoTable("Profiles").Row(new
{ {
@@ -95,12 +92,11 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var tag = Mocker.Resolve<TagRepository>().All().ToList().First(); var tag = db.Query<Tag69>("SELECT Id, Label FROM Tags").Single();
var series = Mocker.Resolve<SeriesRepository>().All().ToList(); var series = db.Query<Series69>("SELECT Tags FROM Series");
series.Should().HaveCount(1); series.Should().HaveCount(1);
series.First().Tags.Should().HaveCount(1); series.First().Tags.Should().BeEquivalentTo(tag.Id);
series.First().Tags.First().Should().Be(tag.Id);
} }
} }
} }

View File

@@ -2,12 +2,7 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
@@ -17,10 +12,11 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[Test] [Test]
public void should_add_unknown_to_old_profile() public void should_add_unknown_to_old_profile()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new c.Insert.IntoTable("Profiles").Row(new
{ {
Id = 0,
Name = "SDTV", Name = "SDTV",
Cutoff = 1, Cutoff = 1,
Items = "[ { \"quality\": 1, \"allowed\": true } ]", Items = "[ { \"quality\": 1, \"allowed\": true } ]",
@@ -28,11 +24,12 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var allProfiles = Mocker.Resolve<ProfileRepository>().All().ToList(); var profiles = db.Query<Profile70>("SELECT Items FROM Profiles LIMIT 1");
allProfiles.Should().HaveCount(1); var items = profiles.First().Items;
allProfiles.First().Items.Should().HaveCount(2); items.Should().HaveCount(2);
allProfiles.First().Items.Should().Contain(i => i.Quality.Id == 0 && i.Allowed == false); items.First().Quality.Should().Be(0);
items.First().Allowed.Should().Be(false);
} }
} }
} }

View File

@@ -6,7 +6,6 @@ using FluentMigrator;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.History;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
@@ -17,7 +16,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[Test] [Test]
public void should_move_grab_id_from_date_to_columns() public void should_move_grab_id_from_date_to_columns()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
InsertHistory(c, new Dictionary<string, string> InsertHistory(c, new Dictionary<string, string>
{ {
@@ -33,19 +32,19 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
var allProfiles = Mocker.Resolve<HistoryRepository>().All().ToList(); var history = db.Query<History72>("SELECT DownloadId, Data FROM History");
allProfiles.Should().HaveCount(2); history.Should().HaveCount(2);
allProfiles.Should().NotContain(c => c.Data.ContainsKey("downloadClientId")); history.Should().NotContain(c => c.Data.ContainsKey("downloadClientId"));
allProfiles.Should().Contain(c => c.DownloadId == "123"); history.Should().Contain(c => c.DownloadId == "123");
allProfiles.Should().Contain(c => c.DownloadId == "abc"); history.Should().Contain(c => c.DownloadId == "abc");
} }
[Test] [Test]
public void should_leave_items_with_no_grabid() public void should_leave_items_with_no_grabid()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
InsertHistory(c, new Dictionary<string, string> InsertHistory(c, new Dictionary<string, string>
{ {
@@ -60,18 +59,18 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
var allProfiles = Mocker.Resolve<HistoryRepository>().All().ToList(); var history = db.Query<History72>("SELECT DownloadId, Data FROM History");
allProfiles.Should().HaveCount(2); history.Should().HaveCount(2);
allProfiles.Should().NotContain(c => c.Data.ContainsKey("downloadClientId")); history.Should().NotContain(c => c.Data.ContainsKey("downloadClientId"));
allProfiles.Should().Contain(c => c.DownloadId == "123"); history.Should().Contain(c => c.DownloadId == "123");
allProfiles.Should().Contain(c => c.DownloadId == null); history.Should().Contain(c => c.DownloadId == null);
} }
[Test] [Test]
public void should_leave_other_data() public void should_leave_other_data()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
InsertHistory(c, new Dictionary<string, string> InsertHistory(c, new Dictionary<string, string>
{ {
@@ -81,16 +80,15 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var allProfiles = Mocker.Resolve<HistoryRepository>().All().Single(); var history = db.Query<History72>("SELECT DownloadId, Data FROM History").Single();
allProfiles.Data.Should().NotContainKey("downloadClientId"); history.Data.Should().NotContainKey("downloadClientId");
allProfiles.Data.Should().Contain(new KeyValuePair<string, string>("indexer", "test")); history.Data.Should().Contain(new KeyValuePair<string, string>("indexer", "test"));
allProfiles.Data.Should().Contain(new KeyValuePair<string, string>("group", "test2")); history.Data.Should().Contain(new KeyValuePair<string, string>("group", "test2"));
allProfiles.DownloadId.Should().Be("123"); history.DownloadId.Should().Be("123");
} }
private void InsertHistory(MigrationBase migrationBase, Dictionary<string, string> data) private void InsertHistory(MigrationBase migrationBase, Dictionary<string, string> data)
{ {
migrationBase.Insert.IntoTable("History").Row(new migrationBase.Insert.IntoTable("History").Row(new

View File

@@ -1,29 +1,28 @@
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Datastore.Migration;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class force_lib_updateFixture : MigrationTest<Core.Datastore.Migration.force_lib_update> public class force_lib_updateFixture : MigrationTest<force_lib_update>
{ {
[Test] [Test]
public void should_not_fail_on_empty_db() public void should_not_fail_on_empty_db()
{ {
WithTestDb(c => { }); var db = WithMigrationTestDb();
Mocker.Resolve<ScheduledTaskRepository>().All().Should().BeEmpty(); db.Query("SELECT * FROM ScheduledTasks").Should().BeEmpty();
Mocker.Resolve<SeriesRepository>().All().Should().BeEmpty(); db.Query("SELECT * FROM Series").Should().BeEmpty();
} }
[Test] [Test]
public void should_reset_job_last_execution_time() public void should_reset_job_last_execution_time()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("ScheduledTasks").Row(new c.Insert.IntoTable("ScheduledTasks").Row(new
{ {
@@ -40,7 +39,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var jobs = Mocker.Resolve<ScheduledTaskRepository>().All().ToList(); var jobs = db.Query<ScheduledTasks75>("SELECT TypeName, LastExecution FROM ScheduledTasks");
jobs.Single(c => c.TypeName == "NzbDrone.Core.Tv.Commands.RefreshSeriesCommand") jobs.Single(c => c.TypeName == "NzbDrone.Core.Tv.Commands.RefreshSeriesCommand")
.LastExecution.Year.Should() .LastExecution.Year.Should()
@@ -51,11 +50,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
.Be(2000); .Be(2000);
} }
[Test] [Test]
public void should_reset_series_last_sync_time() public void should_reset_series_last_sync_time()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
@@ -92,9 +90,9 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var jobs = Mocker.Resolve<SeriesRepository>().All().ToList(); var series = db.Query<Series69>("SELECT LastInfoSync FROM Series");
jobs.Should().OnlyContain(c => c.LastInfoSync.Value.Year == 2014); series.Should().OnlyContain(c => c.LastInfoSync.Value.Year == 2014);
} }
} }
} }

View File

@@ -1,21 +1,18 @@
using System; using System.Linq;
using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class dedupe_tagsFixture : MigrationTest<Core.Datastore.Migration.dedupe_tags> public class dedupe_tagsFixture : MigrationTest<dedupe_tags>
{ {
[Test] [Test]
public void should_not_fail_if_series_tags_are_null() public void should_not_fail_if_series_tags_are_null()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
@@ -40,13 +37,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
Mocker.Resolve<TagRepository>().All().Should().HaveCount(1); var tags = db.Query<Tag69>("SELECT * FROM Tags");
tags.Should().HaveCount(1);
} }
[Test] [Test]
public void should_not_fail_if_series_tags_are_empty() public void should_not_fail_if_series_tags_are_empty()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
@@ -72,13 +70,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
Mocker.Resolve<TagRepository>().All().Should().HaveCount(1); var tags = db.Query<Tag69>("SELECT * FROM Tags");
tags.Should().HaveCount(1);
} }
[Test] [Test]
public void should_remove_duplicate_labels_from_tags() public void should_remove_duplicate_labels_from_tags()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Tags").Row(new c.Insert.IntoTable("Tags").Row(new
{ {
@@ -91,13 +90,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
Mocker.Resolve<TagRepository>().All().Should().HaveCount(1); var tags = db.Query<Tag69>("SELECT * FROM Tags");
tags.Should().HaveCount(1);
} }
[Test] [Test]
public void should_not_allow_duplicate_tag_to_be_inserted() public void should_not_allow_duplicate_tag_to_be_inserted()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Tags").Row(new c.Insert.IntoTable("Tags").Row(new
{ {
@@ -105,13 +105,13 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
Assert.That(() => Mocker.Resolve<TagRepository>().Insert(new Tag { Label = "test" }), Throws.Exception); Assert.That(() => db.Query("INSERT INTO Tags (Label) VALUES ('test')"), Throws.Exception);
} }
[Test] [Test]
public void should_replace_duplicated_tag_with_proper_tag() public void should_replace_duplicated_tag_with_proper_tag()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
@@ -142,13 +142,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
Mocker.Resolve<SeriesRepository>().Get(1).Tags.First().Should().Be(1); var series = db.Query<Series69>("SELECT Tags FROM Series WHERE Id = 1").Single();
series.Tags.First().Should().Be(1);
} }
[Test] [Test]
public void should_only_update_affected_series() public void should_only_update_affected_series()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
@@ -197,7 +198,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
Mocker.Resolve<SeriesRepository>().Get(2).Tags.Should().BeEmpty(); var series = db.Query<Series69>("SELECT Tags FROM Series WHERE Id = 2").Single();
series.Tags.Should().BeEmpty();
} }
} }
} }

View File

@@ -3,27 +3,25 @@ using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Sabnzbd;
using NzbDrone.Core.Download.Clients.Transmission;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Datastore.Migration;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class move_dot_prefix_to_transmission_categoryFixture : MigrationTest<Core.Datastore.Migration.move_dot_prefix_to_transmission_category> public class move_dot_prefix_to_transmission_categoryFixture : MigrationTest<move_dot_prefix_to_transmission_category>
{ {
[Test] [Test]
public void should_not_fail_if_no_transmission() public void should_not_fail_if_no_transmission()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("DownloadClients").Row(new c.Insert.IntoTable("DownloadClients").Row(new
{ {
Enable = 1, Enable = 1,
Name = "Sab", Name = "Sab",
Implementation = "Sabnzbd", Implementation = "Sabnzbd",
Settings = new SabnzbdSettings Settings = new
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
TvCategory = "abc" TvCategory = "abc"
@@ -32,24 +30,23 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<DownloadClientRepository>().All(); var downloadClients = db.Query<DownloadClientDefinition81>("SELECT Settings FROM DownloadClients");
items.Should().HaveCount(1); downloadClients.Should().HaveCount(1);
downloadClients.First().Settings.ToObject<SabnzbdSettings81>().TvCategory.Should().Be("abc");
items.First().Settings.As<SabnzbdSettings>().TvCategory.Should().Be("abc");
} }
[Test] [Test]
public void should_be_updated_for_transmission() public void should_be_updated_for_transmission()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("DownloadClients").Row(new c.Insert.IntoTable("DownloadClients").Row(new
{ {
Enable = 1, Enable = 1,
Name = "Trans", Name = "Trans",
Implementation = "Transmission", Implementation = "Transmission",
Settings = new TransmissionSettings Settings = new
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
TvCategory = "abc" TvCategory = "abc"
@@ -58,24 +55,23 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<DownloadClientRepository>().All(); var downloadClients = db.Query<DownloadClientDefinition81>("SELECT Settings FROM DownloadClients");
items.Should().HaveCount(1); downloadClients.Should().HaveCount(1);
downloadClients.First().Settings.ToObject<TransmissionSettings81>().TvCategory.Should().Be(".abc");
items.First().Settings.As<TransmissionSettings>().TvCategory.Should().Be(".abc");
} }
[Test] [Test]
public void should_leave_empty_category_untouched() public void should_leave_empty_category_untouched()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("DownloadClients").Row(new c.Insert.IntoTable("DownloadClients").Row(new
{ {
Enable = 1, Enable = 1,
Name = "Trans", Name = "Trans",
Implementation = "Transmission", Implementation = "Transmission",
Settings = new TransmissionSettings Settings = new
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
TvCategory = "" TvCategory = ""
@@ -84,11 +80,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<DownloadClientRepository>().All(); var downloadClients = db.Query<DownloadClientDefinition81>("SELECT Settings FROM DownloadClients");
items.Should().HaveCount(1); downloadClients.Should().HaveCount(1);
downloadClients.First().Settings.ToObject<TransmissionSettings81>().TvCategory.Should().Be("");
items.First().Settings.As<TransmissionSettings>().TvCategory.Should().Be("");
} }
} }
} }

View File

@@ -1,32 +1,28 @@
using System; using System.Linq;
using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class update_quality_minmax_sizeFixture : MigrationTest<Core.Datastore.Migration.update_quality_minmax_size> public class update_quality_minmax_sizeFixture : MigrationTest<update_quality_minmax_size>
{ {
[Test] [Test]
public void should_not_fail_if_empty() public void should_not_fail_if_empty()
{ {
WithTestDb(c => var db = WithMigrationTestDb();
{
}); var qualityDefinitions = db.Query<QualityDefinition84>("SELECT * FROM QualityDefinitions");
var items = Mocker.Resolve<QualityDefinitionRepository>().All(); qualityDefinitions.Should().BeEmpty();
items.Should().HaveCount(0);
} }
[Test] [Test]
public void should_set_rawhd_to_null() public void should_set_rawhd_to_null()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("QualityDefinitions").Row(new c.Insert.IntoTable("QualityDefinitions").Row(new
{ {
@@ -44,17 +40,16 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<QualityDefinitionRepository>().All(); var qualityDefinitions = db.Query<QualityDefinition84>("SELECT * FROM QualityDefinitions");
items.Should().HaveCount(2); qualityDefinitions.Should().HaveCount(2);
qualityDefinitions.First(v => v.Quality == 10).MaxSize.Should().NotHaveValue();
items.First(v => v.Quality.Id == 10).MaxSize.Should().NotHaveValue();
} }
[Test] [Test]
public void should_set_zero_maxsize_to_null() public void should_set_zero_maxsize_to_null()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("QualityDefinitions").Row(new c.Insert.IntoTable("QualityDefinitions").Row(new
{ {
@@ -65,17 +60,16 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<QualityDefinitionRepository>().All(); var qualityDefinitions = db.Query<QualityDefinition84>("SELECT * FROM QualityDefinitions");
items.Should().HaveCount(1); qualityDefinitions.Should().HaveCount(1);
qualityDefinitions.First(v => v.Quality == 1).MaxSize.Should().NotHaveValue();
items.First(v => v.Quality.Id == 1).MaxSize.Should().NotHaveValue();
} }
[Test] [Test]
public void should_preserve_values() public void should_preserve_values()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("QualityDefinitions").Row(new c.Insert.IntoTable("QualityDefinitions").Row(new
{ {
@@ -93,11 +87,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<QualityDefinitionRepository>().All(); var qualityDefinitions = db.Query<QualityDefinition84>("SELECT * FROM QualityDefinitions");
items.Should().HaveCount(2); qualityDefinitions.Should().HaveCount(2);
qualityDefinitions.First(v => v.Quality == 1).MaxSize.Should().Be(100);
items.First(v => v.Quality.Id == 1).MaxSize.Should().Be(100);
} }
} }
} }

View File

@@ -1,30 +1,26 @@
using System; using System.Linq;
using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Download; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Download.Clients.Deluge;
using NzbDrone.Core.Download.Clients.Sabnzbd;
using NzbDrone.Core.Download.Clients.Transmission;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using System.Drawing;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class expand_transmission_urlbaseFixture : MigrationTest<Core.Datastore.Migration.expand_transmission_urlbase> public class expand_transmission_urlbaseFixture : MigrationTest<expand_transmission_urlbase>
{ {
[Test] [Test]
public void should_not_fail_if_no_transmission() public void should_not_fail_if_no_transmission()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("DownloadClients").Row(new c.Insert.IntoTable("DownloadClients").Row(new
{ {
Enable = 1, Enable = 1,
Name = "Deluge", Name = "Deluge",
Implementation = "Deluge", Implementation = "Deluge",
Settings = new DelugeSettings Settings = new DelugeSettings85
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
TvCategory = "abc", TvCategory = "abc",
@@ -34,51 +30,48 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<DownloadClientRepository>().All(); var items = db.Query<DownloadClientDefinition81>("SELECT * FROM DownloadClients");
items.Should().HaveCount(1); items.Should().HaveCount(1);
items.First().Settings.ToObject<DelugeSettings85>().UrlBase.Should().Be("/my/");
items.First().Settings.As<DelugeSettings>().UrlBase.Should().Be("/my/");
} }
[Test] [Test]
public void should_be_updated_for_transmission() public void should_be_updated_for_transmission()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("DownloadClients").Row(new c.Insert.IntoTable("DownloadClients").Row(new
{ {
Enable = 1, Enable = 1,
Name = "Trans", Name = "Trans",
Implementation = "Transmission", Implementation = "Transmission",
Settings = new TransmissionSettings Settings = new TransmissionSettings81
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
TvCategory = "abc", TvCategory = "abc"
UrlBase = null
}.ToJson(), }.ToJson(),
ConfigContract = "TransmissionSettings" ConfigContract = "TransmissionSettings"
}); });
}); });
var items = Mocker.Resolve<DownloadClientRepository>().All(); var items = db.Query<DownloadClientDefinition81>("SELECT * FROM DownloadClients");
items.Should().HaveCount(1); items.Should().HaveCount(1);
items.First().Settings.ToObject<TransmissionSettings81>().UrlBase.Should().Be("/transmission/");
items.First().Settings.As<TransmissionSettings>().UrlBase.Should().Be("/transmission/");
} }
[Test] [Test]
public void should_be_append_to_existing_urlbase() public void should_be_append_to_existing_urlbase()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("DownloadClients").Row(new c.Insert.IntoTable("DownloadClients").Row(new
{ {
Enable = 1, Enable = 1,
Name = "Trans", Name = "Trans",
Implementation = "Transmission", Implementation = "Transmission",
Settings = new TransmissionSettings Settings = new TransmissionSettings81
{ {
Host = "127.0.0.1", Host = "127.0.0.1",
TvCategory = "abc", TvCategory = "abc",
@@ -88,11 +81,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<DownloadClientRepository>().All(); var items = db.Query<DownloadClientDefinition81>("SELECT * FROM DownloadClients");
items.Should().HaveCount(1); items.Should().HaveCount(1);
items.First().Settings.ToObject<TransmissionSettings81>().UrlBase.Should().Be("/my/url/transmission/");
items.First().Settings.As<TransmissionSettings>().UrlBase.Should().Be("/my/url/transmission/");
} }
} }
} }

View File

@@ -2,19 +2,18 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Notifications; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Notifications.PushBullet;
using NzbDrone.Core.Notifications.Pushover;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class pushbullet_device_idsFixture : MigrationTest<Core.Datastore.Migration.pushbullet_device_ids> public class pushbullet_device_idsFixture : MigrationTest<pushbullet_device_ids>
{ {
[Test] [Test]
public void should_not_fail_if_no_pushbullet() public void should_not_fail_if_no_pushbullet()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Notifications").Row(new c.Insert.IntoTable("Notifications").Row(new
{ {
@@ -23,12 +22,12 @@ namespace NzbDrone.Core.Test.Datastore.Migration
OnUpgrade = false, OnUpgrade = false,
Name = "Pushover", Name = "Pushover",
Implementation = "Pushover", Implementation = "Pushover",
Settings = new PushoverSettings().ToJson(), Settings = "{}",
ConfigContract = "PushoverSettings" ConfigContract = "PushoverSettings"
}); });
}); });
var items = Mocker.Resolve<NotificationRepository>().All(); var items = db.Query<Notification86>("SELECT * FROM Notifications");
items.Should().HaveCount(1); items.Should().HaveCount(1);
} }
@@ -36,7 +35,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[Test] [Test]
public void should_not_fail_if_deviceId_is_not_set() public void should_not_fail_if_deviceId_is_not_set()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Notifications").Row(new c.Insert.IntoTable("Notifications").Row(new
{ {
@@ -47,13 +46,13 @@ namespace NzbDrone.Core.Test.Datastore.Migration
Implementation = "PushBullet", Implementation = "PushBullet",
Settings = new Settings = new
{ {
ApiKey = "my_api_key", ApiKey = "my_api_key"
}.ToJson(), }.ToJson(),
ConfigContract = "PushBulletSettings" ConfigContract = "PushBulletSettings"
}); });
}); });
var items = Mocker.Resolve<NotificationRepository>().All(); var items = db.Query<Notification86>("SELECT * FROM Notifications");
items.Should().HaveCount(1); items.Should().HaveCount(1);
} }
@@ -63,7 +62,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{ {
var deviceId = "device_id"; var deviceId = "device_id";
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Notifications").Row(new c.Insert.IntoTable("Notifications").Row(new
{ {
@@ -81,10 +80,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<NotificationRepository>().All(); var items = db.Query<Notification86>("SELECT * FROM Notifications");
items.Should().HaveCount(1); items.Should().HaveCount(1);
items.First().Settings.As<PushBulletSettings>().DeviceIds.First().Should().Be(deviceId); items.First().Settings.ToObject<PushBulletSettings86>().DeviceIds.First().Should().Be(deviceId);
} }
} }
} }

View File

@@ -2,21 +2,18 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Notifications; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Notifications.PushBullet;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
{ {
[TestFixture] [TestFixture]
public class pushbullet_devices_channels : MigrationTest<Core.Datastore.Migration.pushbullet_devices_channels_list> public class pushbullet_devices_channels_listFixture : MigrationTest<pushbullet_devices_channels_list>
{ {
[Test] [Test]
public void should_convert_comma_separted_string_to_list() public void should_convert_comma_separted_string_to_list()
{ {
var deviceId = "device_id"; var db = WithMigrationTestDb(c =>
WithTestDb(c =>
{ {
c.Insert.IntoTable("Notifications").Row(new c.Insert.IntoTable("Notifications").Row(new
{ {
@@ -34,11 +31,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<NotificationRepository>().All(); var items = db.Query<Notification86>("SELECT * FROM Notifications");
items.Should().HaveCount(1); items.Should().HaveCount(1);
var settings = items.First().Settings.As<PushBulletSettings>(); items.First().Settings.ToObject<PushBulletSettings88>().ChannelTags.Should().HaveCount(2);
settings.ChannelTags.Should().HaveCount(2);
} }
} }
} }

View File

@@ -3,8 +3,6 @@ using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.KickassTorrents;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration namespace NzbDrone.Core.Test.Datastore.Migration
@@ -20,13 +18,13 @@ namespace NzbDrone.Core.Test.Datastore.Migration
// [TestCase("HTTP://KICKASS.SO")] Not sure if there is an easy way to do this, not sure if worth it. // [TestCase("HTTP://KICKASS.SO")] Not sure if there is an easy way to do this, not sure if worth it.
public void should_replace_old_url(string oldUrl) public void should_replace_old_url(string oldUrl)
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Indexers").Row(new c.Insert.IntoTable("Indexers").Row(new
{ {
Name = "Kickass_wrong_url", Name = "Kickass_wrong_url",
Implementation = "KickassTorrents", Implementation = "KickassTorrents",
Settings = new KickassTorrentsSettings Settings = new KickassTorrentsSettings90
{ {
BaseUrl = oldUrl BaseUrl = oldUrl
}.ToJson(), }.ToJson(),
@@ -34,22 +32,22 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<IndexerRepository>().All().ToList(); var items = db.Query<IndexerDefinition90>("SELECT * FROM Indexers");
items.Should().HaveCount(1); items.Should().HaveCount(1);
items.First().Settings.As<KickassTorrentsSettings>().BaseUrl.Should().Be("https://kat.cr"); items.First().Settings.ToObject<KickassTorrentsSettings90>().BaseUrl.Should().Be("https://kat.cr");
} }
[Test] [Test]
public void should_not_replace_other_indexers() public void should_not_replace_other_indexers()
{ {
WithTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Indexers").Row(new c.Insert.IntoTable("Indexers").Row(new
{ {
Name = "not_kickass", Name = "not_kickass",
Implementation = "NotKickassTorrents", Implementation = "NotKickassTorrents",
Settings = new KickassTorrentsSettings Settings = new KickassTorrentsSettings90
{ {
BaseUrl = "kickass.so", BaseUrl = "kickass.so",
}.ToJson(), }.ToJson(),
@@ -57,10 +55,10 @@ namespace NzbDrone.Core.Test.Datastore.Migration
}); });
}); });
var items = Mocker.Resolve<IndexerRepository>().All().ToList(); var items = db.Query<IndexerDefinition90>("SELECT * FROM Indexers");
items.Should().HaveCount(1); items.Should().HaveCount(1);
items.First().Settings.As<KickassTorrentsSettings>().BaseUrl.Should().Be("kickass.so"); items.First().Settings.ToObject<KickassTorrentsSettings90>().BaseUrl.Should().Be("kickass.so");
} }
} }
} }

View File

@@ -0,0 +1,35 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration
{
[TestFixture]
public class add_ultrahd_quality_in_profilesFixture : MigrationTest<add_ultrahd_quality_in_profiles>
{
[Test]
public void should_add_ultrahd_to_old_profile()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "SDTV",
Cutoff = 1,
Items = "[ { \"quality\": 1, \"allowed\": true } ]",
Language = 1
});
});
var profiles = db.Query<Profile70>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items.Should().HaveCount(4);
items.Select(v => v.Quality).Should().BeEquivalentTo(1, 16, 18, 19);
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false);
}
}
}

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