mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-17 21:26:13 -04:00
Compare commits
3 Commits
v2.0.0.300
...
v2.0.0.285
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4394860fac | ||
|
|
1eeb40aede | ||
|
|
7aaf40d9d4 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -122,4 +122,3 @@ setup/Output/
|
||||
#VS outout folders
|
||||
bin
|
||||
obj
|
||||
output/*
|
||||
|
||||
@@ -33,13 +33,8 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
- Use 4 spaces instead of tabs, this is the default for VS 2012 and WebStorm (to my knowledge)
|
||||
|
||||
### Pull Requesting ###
|
||||
- Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it
|
||||
- You're probably going to get some comments or questions from us, they will be to ensure consistency and maintainability
|
||||
- We'll try to respond to pull requests as soon as possible, if its been a day or two, please reach out to us, we may have missed it
|
||||
- Each PR should come from its own [feature branch](http://martinfowler.com/bliki/FeatureBranch.html) not develop in your fork, it should have a meaningful branch name (what is being added/fixed)
|
||||
- new-feature (Good)
|
||||
- fix-bug (Good)
|
||||
- patch (Bad)
|
||||
- develop (Bad)
|
||||
- Each PR comes from its own [feature branch](http://martinfowler.com/bliki/FeatureBranch.html) not develop in your fork
|
||||
|
||||
If you have any questions about any of this, please let us know.
|
||||
|
||||
240
Logo/Sonarr.svg
240
Logo/Sonarr.svg
@@ -1,240 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="218px"
|
||||
height="218px" viewBox="0 0 218 218" enable-background="new 0 0 218 218" xml:space="preserve">
|
||||
<symbol id="hex_grid" viewBox="-114.25 -98.617 228.55 197.233">
|
||||
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#989898" stroke-width="0.5" stroke-linecap="square" stroke-miterlimit="1" d="
|
||||
M72.15,90.3l4.7-2.7l4.65,2.7v5.4l-4.65,2.7l-4.7-2.7V90.3z M62.85,95.7l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7V95.7l4.65,2.7
|
||||
l4.65-2.7 M62.85,90.3l4.65-2.7l4.65,2.7 M62.85,79.55v-5.4l4.65-2.7l4.65,2.7v5.4L67.5,82.2L62.85,79.55L58.2,82.2l-4.65-2.65
|
||||
M72.15,74.15l4.7-2.7l4.65,2.7v5.4l-4.65,2.65l-4.7-2.65 M76.85,87.6v-5.4 M67.5,87.6v-5.4 M81.5,95.7l4.65,2.7l4.65-2.7l4.65,2.7
|
||||
l4.65-2.7l4.65,2.7l4.65-2.7v-5.4l4.65-2.7v-5.4l-4.65-2.65v-5.4l4.65-2.7v-5.4l-4.65-2.7v-5.4l4.65-2.7v-5.4l-4.65-2.65v-5.4
|
||||
l4.65-2.7v-5.4L109.4,31v-5.4l4.65-2.7v-5.4l-4.65-2.65v-5.4l4.65-2.7v-5.4l-4.65-2.7v-5.4l4.65-2.7v-5.4l-4.65-2.65v-5.4l4.65-2.7
|
||||
V-31l-4.65-2.7v-5.4l4.65-2.7v-5.4l-4.65-2.65v-5.4l4.65-2.7v-5.4l-4.65-2.7v-5.4l4.65-2.7v-5.4l-4.65-2.65v-5.4l4.65-2.7v-5.4
|
||||
l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.7-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7
|
||||
l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.7-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7L7-98.4l-4.65,2.7l-4.65-2.7
|
||||
l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.7-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7
|
||||
l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.7-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7l-4.65-2.7l-4.65,2.7v5.4l-4.65,2.7
|
||||
v5.4l4.65,2.65v5.4l-4.65,2.7v5.4l4.65,2.7v5.4l-4.65,2.7v5.4l4.65,2.65v5.4l-4.65,2.7v5.4l4.65,2.7v5.4l-4.65,2.7v5.4l4.65,2.65
|
||||
v5.4l-4.65,2.7v5.4l4.65,2.7v5.4l-4.65,2.7v5.4l4.65,2.65v5.4l-4.65,2.7V31l4.65,2.7v5.4l-4.65,2.7v5.4l4.65,2.65v5.4l-4.65,2.7
|
||||
v5.4l4.65,2.7v5.4l-4.65,2.7v5.4l4.65,2.65v5.4l-4.65,2.7v5.4l4.65,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.7,2.7
|
||||
l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.7,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7
|
||||
l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7L7,95.7l4.65,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.7,2.7l4.65-2.7l4.65,2.7l4.65-2.7l4.65,2.7
|
||||
l4.65-2.7 M44.25,95.7v-5.4l4.65-2.7l4.65,2.7 M44.25,79.55v-5.4l4.65-2.7l4.65,2.7v5.4L48.9,82.2L44.25,79.55L39.6,82.2
|
||||
l-4.65-2.65 M58.2,87.6v-5.4 M48.9,87.6v-5.4 M53.55,63.35v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7L53.55,63.35l-4.65,2.7l-4.65-2.7
|
||||
v-5.4l4.65-2.7l4.65,2.7 M62.85,74.15l-4.65-2.7v-5.4 M53.55,74.15l4.65-2.7 M48.9,71.45v-5.4 M48.9,55.25v-5.4l4.65-2.65
|
||||
l4.65,2.65v5.4 M67.5,71.45v-5.4l4.65-2.7l4.7,2.7v5.4 M67.5,66.05l-4.65-2.7 M58.2,49.85l4.65-2.65l4.65,2.65v5.4l-4.65,2.7
|
||||
M72.15,63.35v-5.4l4.7-2.7l4.65,2.7v5.4l-4.65,2.7 M76.85,55.25v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M67.5,55.25l4.65,2.7
|
||||
M34.95,95.7v-5.4l4.65-2.7l4.65,2.7 M16.3,95.7v-5.4l4.65-2.7l4.65,2.7v5.4 M25.6,90.3l4.7-2.7l4.65,2.7 M25.6,79.55v-5.4l4.7-2.7
|
||||
l4.65,2.7v5.4L30.3,82.2L25.6,79.55l-4.65,2.65l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M20.95,87.6v-5.4 M39.6,87.6v-5.4 M30.3,87.6
|
||||
v-5.4 M7,95.7v-5.4l4.65-2.7l4.65,2.7 M-2.3,95.7v-5.4l4.65-2.7L7,90.3 M2.35,82.2l-4.65-2.65v-5.4l4.65-2.7L7,74.15v5.4L2.35,82.2
|
||||
z M16.3,79.55l-4.65,2.65L7,79.55 M2.35,87.6v-5.4 M11.65,87.6v-5.4 M16.3,74.15l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M2.35,71.45
|
||||
v-5.4L7,63.35l4.65,2.7 M2.35,49.85L7,47.2l4.65,2.65v5.4L7,57.95l-4.65-2.7V49.85L-2.3,47.2v-5.4l4.65-2.7L7,41.8v5.4
|
||||
M11.65,55.25l4.65,2.7v5.4 M7,74.15l4.65-2.7 M7,57.95v5.4 M30.3,71.45v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7 M16.3,57.95
|
||||
l4.65-2.7l4.65,2.7v5.4l-4.65,2.7 M30.3,66.05l-4.7-2.7 M30.3,49.85l4.65-2.65l4.65,2.65v5.4l-4.65,2.7l-4.65-2.7V49.85l-4.7-2.65
|
||||
v-5.4l-4.65-2.7v-5.4L25.6,31l4.7,2.7v5.4l-4.7,2.7 M11.65,49.85l4.65-2.65l4.65,2.65v5.4 M25.6,57.95l4.7-2.7 M34.95,57.95v5.4
|
||||
M34.95,47.2v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65 M25.6,31v-5.4l4.7-2.7l4.65,2.7V31l-4.65,2.7 M34.95,25.6l4.65-2.7l4.65,2.7
|
||||
V31l-4.65,2.7L34.95,31 M30.3,39.1l4.65,2.7 M39.6,39.1v-5.4 M20.95,39.1l-4.65,2.7l-4.65-2.7v-5.4L16.3,31l4.65,2.7 M16.3,41.8
|
||||
v5.4 M2.35,33.7L-2.3,31v-5.4l4.65-2.7L7,25.6V31L2.35,33.7z M7,25.6l4.65-2.7l4.65,2.7V31 M2.35,39.1v-5.4 M11.65,33.7L7,31
|
||||
M7,41.8l4.65-2.7 M11.65,17.5L7,14.85v-5.4l4.65-2.7l4.65,2.7v5.4L11.65,17.5z M11.65,22.9v-5.4 M2.35,22.9v-5.4L7,14.85 M7,9.45
|
||||
l-4.65-2.7v-5.4L7-1.35l4.65,2.7v5.4 M34.95,14.85v-5.4l4.65-2.7l4.65,2.7v5.4L39.6,17.5L34.95,14.85z M39.6,22.9v-5.4 M25.6,25.6
|
||||
l-4.65-2.7v-5.4l4.65-2.65l4.7,2.65v5.4 M16.3,9.45l4.65-2.7l4.65,2.7v5.4 M34.95,9.45l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4
|
||||
M11.65,1.35l4.65-2.7l4.65,2.7v5.4 M25.6,9.45l4.7-2.7 M30.3,17.5l4.65-2.65 M16.3,25.6l4.65-2.7 M20.95,17.5l-4.65-2.65
|
||||
M76.85,49.85l-4.7-2.65v-5.4l4.7-2.7l4.65,2.7v5.4 M62.85,47.2v-5.4l4.65-2.7l4.65,2.7 M62.85,25.6l4.65-2.7l4.65,2.7V31
|
||||
l-4.65,2.7L62.85,31V25.6l-4.65-2.7v-5.4 M72.15,25.6l4.7-2.7l4.65,2.7V31l-4.65,2.7l-4.7-2.7 M76.85,39.1v-5.4 M67.5,39.1v-5.4
|
||||
M53.55,47.2v-5.4l4.65-2.7l4.65,2.7 M44.25,41.8l4.65-2.7l4.65,2.7 M44.25,25.6l4.65-2.7l4.65,2.7V31l-4.65,2.7L44.25,31
|
||||
M62.85,31l-4.65,2.7L53.55,31 M58.2,39.1v-5.4 M48.9,39.1v-5.4 M53.55,14.85v-5.4l4.65-2.7l4.65,2.7v5.4L58.2,17.5L53.55,14.85
|
||||
L48.9,17.5l-4.65-2.65 M48.9,22.9v-5.4 M53.55,9.45l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M44.25,9.45l4.65-2.7 M76.85,22.9v-5.4
|
||||
l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M76.85,17.5l-4.7-2.65v-5.4l4.7-2.7l4.65,2.7v5.4 M67.5,22.9v-5.4l4.65-2.65 M67.5,17.5
|
||||
l-4.65-2.65 M72.15,9.45l-4.65-2.7v-5.4l4.65-2.7l4.7,2.7v5.4 M62.85,9.45l4.65-2.7 M53.55,25.6l4.65-2.7 M44.25,63.35l-4.65,2.7
|
||||
M39.6,55.25l4.65,2.7 M39.6,71.45l4.65,2.7 M67.5,49.85l4.65-2.65 M48.9,49.85l-4.65-2.65 M25.6,47.2l-4.65,2.65 M104.75,87.6
|
||||
l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65V87.6l4.65,2.7 M109.4,79.55l-4.65,2.65 M100.1,95.7v-5.4 M81.5,90.3l4.65-2.7
|
||||
l4.65,2.7v5.4 M81.5,74.15l4.65-2.7l4.65,2.7v5.4l-4.65,2.65l-4.65-2.65 M90.8,79.55l4.65,2.65 M86.15,87.6v-5.4 M90.8,90.3
|
||||
l4.65-2.7 M95.45,71.45v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7L95.45,71.45l-4.65,2.7 M86.15,55.25l4.65,2.7v5.4l-4.65,2.7
|
||||
l-4.65-2.7 M86.15,71.45v-5.4 M95.45,55.25v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7L95.45,55.25l-4.65,2.7 M100.1,63.35v-5.4
|
||||
M90.8,63.35l4.65,2.7 M109.4,63.35l-4.65,2.7 M104.75,71.45l4.65,2.7 M104.75,55.25l4.65,2.7 M100.1,79.55v-5.4 M100.1,47.2v-5.4
|
||||
l4.65-2.7l4.65,2.7 M100.1,41.8l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M109.4,31l-4.65,2.7 M81.5,41.8l4.65-2.7l4.65,2.7v5.4
|
||||
l-4.65,2.65 M86.15,22.9l4.65,2.7V31l-4.65,2.7L81.5,31 M100.1,31v-5.4l4.65-2.7l4.65,2.7 M90.8,31l4.65,2.7 M86.15,39.1v-5.4
|
||||
M90.8,41.8l4.65-2.7 M86.15,17.5l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M90.8,14.85v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65
|
||||
M81.5,9.45l4.65-2.7l4.65,2.7 M86.15,6.75v-5.4l4.65-2.7l4.65,2.7v5.4 M100.1,14.85l4.65,2.65v5.4 M109.4,14.85l-4.65,2.65
|
||||
M100.1,9.45l4.65-2.7l4.65,2.7 M95.45,1.35l4.65-2.7l4.65,2.7v5.4 M95.45,22.9l4.65,2.7 M109.4,47.2l-4.65,2.65 M90.8,47.2
|
||||
l4.65,2.65 M104.75-9.45l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65V-9.45l4.65,2.7 M109.4-17.5l-4.65,2.65 M100.1-1.35v-5.4
|
||||
M90.8-1.35v-5.4l4.65-2.7 M81.5-6.75l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4L81.5-6.75v5.4l-4.65,2.7 M90.8-6.75l-4.65-2.7
|
||||
M95.45-14.85L90.8-17.5v-5.4l4.65-2.7l4.65,2.7v5.4 M76.85-14.85l-4.7-2.65v-5.4l4.7-2.7l4.65,2.7v5.4 M86.15-14.85l4.65-2.65
|
||||
M95.45-31l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4L95.45-31z M95.45-25.6V-31 M90.8-22.9l-4.65-2.7V-31l4.65-2.7 M86.15-31
|
||||
l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M86.15-41.8v-5.4l4.65-2.65l4.65,2.65v5.4 M100.1-33.7l4.65,2.7v5.4l-4.65,2.7 M109.4-33.7
|
||||
l-4.65,2.7 M95.45-47.2l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M104.75-41.8l4.65,2.7 M81.5-22.9l4.65-2.7 M104.75-25.6l4.65,2.7
|
||||
M100.1-49.85v-5.4l4.65-2.7l4.65,2.7 M109.4-66.05l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M104.75-63.35v5.4 M100.1-55.25
|
||||
l-4.65-2.7v-5.4l4.65-2.7 M86.15-47.2l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4 M86.15-74.15l4.65,2.7v5.4l-4.65,2.7l-4.65-2.7v-5.4
|
||||
L86.15-74.15v-5.4l4.65-2.65l4.65,2.65 M90.8-71.45l4.65-2.7l4.65,2.7 M95.45-63.35l-4.65-2.7 M90.8-55.25l4.65-2.7 M86.15-57.95
|
||||
v-5.4 M95.45-74.15v-5.4l4.65-2.65l4.65,2.65v5.4 M81.5-71.45l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65 M81.5-82.2v-5.4l4.65-2.7
|
||||
l4.65,2.7v5.4 M86.15-90.3v-5.4 M104.75-95.7v5.4l-4.65,2.7l-4.65-2.7v-5.4 M100.1-82.2v-5.4 M95.45-90.3l-4.65,2.7 M109.4-82.2
|
||||
l-4.65,2.65 M104.75-90.3l4.65,2.7 M109.4-49.85l-4.65,2.65 M72.15-1.35v-5.4l4.7-2.7 M67.5,1.35l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7
|
||||
M72.15-17.5l-4.65,2.65l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M67.5-14.85v5.4 M53.55-1.35v-5.4l4.65-2.7l4.65,2.7 M48.9,1.35
|
||||
l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M44.25-22.9l4.65-2.7l4.65,2.7v5.4l-4.65,2.65l-4.65-2.65V-22.9l-4.65-2.7V-31 M53.55-17.5
|
||||
l4.65,2.65v5.4 M48.9-9.45v-5.4 M53.55-33.7v-5.4l4.65-2.7l4.65,2.7v5.4L58.2-31L53.55-33.7z M62.85-22.9l-4.65-2.7V-31 M48.9-25.6
|
||||
V-31l4.65-2.7 M48.9-31l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M48.9-41.8v-5.4l4.65-2.65l4.65,2.65v5.4 M76.85-25.6V-31l4.65-2.7
|
||||
M76.85-31l-4.7-2.7v-5.4l4.7-2.7l4.65,2.7 M62.85-33.7L67.5-31v5.4 M72.15-39.1l-4.65-2.7v-5.4l4.65-2.65l4.7,2.65v5.4
|
||||
M62.85-39.1l4.65-2.7 M72.15-33.7L67.5-31 M53.55-22.9l4.65-2.7 M58.2-14.85l4.65-2.65 M30.3,1.35l-4.7-2.7v-5.4l4.7-2.7l4.65,2.7
|
||||
v5.4 M30.3-9.45v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M16.3-1.35v-5.4l4.65-2.7l4.65,2.7 M30.3-14.85l-4.7-2.65v-5.4l4.7-2.7
|
||||
l4.65,2.7v5.4 M25.6-17.5l-4.65,2.65L16.3-17.5v-5.4l4.65-2.7l4.65,2.7 M20.95-14.85v5.4 M16.3-6.75l-4.65-2.7v-5.4l4.65-2.65
|
||||
M2.35,1.35l-4.65-2.7v-5.4l4.65-2.7L7-6.75v5.4 M7-17.5l-4.65,2.65L-2.3-17.5v-5.4l4.65-2.7L7-22.9V-17.5l4.65,2.65 M11.65-9.45
|
||||
L7-6.75 M2.35-9.45v-5.4 M11.65-31L7-33.7v-5.4l4.65-2.7l4.65,2.7v5.4L11.65-31z M16.3-22.9l-4.65-2.7V-31 M2.35-25.6V-31L7-33.7
|
||||
M7-39.1l-4.65-2.7v-5.4L7-49.85l4.65,2.65v5.4 M44.25-33.7L39.6-31l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M20.95-25.6V-31l4.65-2.7
|
||||
l4.7,2.7v5.4 M20.95-47.2l4.65-2.65l4.7,2.65v5.4l-4.7,2.7l-4.65-2.7V-47.2l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M30.3-47.2
|
||||
l4.65-2.65l4.65,2.65v5.4 M30.3-41.8l4.65,2.7 M25.6-33.7v-5.4 M34.95-33.7L30.3-31 M34.95-22.9l4.65-2.7 M20.95-31l-4.65-2.7
|
||||
M16.3-39.1l4.65-2.7 M7-22.9l4.65-2.7 M34.95-55.25l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4L34.95-55.25v5.4 M25.6-49.85v-5.4
|
||||
l4.7-2.7 M30.3-63.35l-4.7-2.7v-5.4l4.7-2.7l4.65,2.7v5.4 M25.6-66.05l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M30.3-74.15
|
||||
v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M20.95-63.35v5.4 M7-49.85v-5.4l4.65-2.7l4.65,2.7 M2.35-47.2l-4.65-2.65v-5.4l4.65-2.7
|
||||
L7-55.25 M2.35-63.35l-4.65-2.7v-5.4l4.65-2.7L7-71.45v5.4L2.35-63.35z M16.3-71.45l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4
|
||||
M11.65-57.95v-5.4l4.65-2.7 M2.35-57.95v-5.4 M7-66.05l4.65,2.7 M11.65-74.15L7-71.45 M2.35-74.15v-5.4L7-82.2l4.65,2.65
|
||||
M11.65-95.7v5.4L7-87.6l-4.65-2.7v-5.4 M20.95-95.7v5.4l-4.65,2.7l-4.65-2.7 M7-87.6v5.4 M16.3-87.6v5.4 M20.95-90.3l4.65,2.7v5.4
|
||||
l-4.65,2.65 M30.3-79.55l-4.7-2.65 M30.3-95.7v5.4l-4.7,2.7 M48.9-95.7v5.4l-4.65,2.7l-4.65-2.7v-5.4 M34.95-82.2v-5.4l4.65-2.7
|
||||
M30.3-90.3l4.65,2.7 M72.15-49.85v-5.4l4.7-2.7l4.65,2.7 M67.5-47.2l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M67.5-74.15l4.65,2.7v5.4
|
||||
l-4.65,2.7l-4.65-2.7v-5.4L67.5-74.15v-5.4l4.65-2.65l4.7,2.65 M81.5-66.05l-4.65,2.7l-4.7-2.7 M72.15-71.45l4.7-2.7 M76.85-63.35
|
||||
v5.4 M67.5-57.95v-5.4 M53.55-49.85v-5.4l4.65-2.7l4.65,2.7 M48.9-47.2l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M44.25-66.05v-5.4
|
||||
l4.65-2.7l4.65,2.7v5.4l-4.65,2.7L44.25-66.05l-4.65,2.7 M62.85-66.05l-4.65,2.7l-4.65-2.7 M53.55-71.45l4.65-2.7l4.65,2.7
|
||||
M58.2-57.95v-5.4 M48.9-57.95v-5.4 M48.9-74.15v-5.4l4.65-2.65l4.65,2.65v5.4 M48.9-79.55l-4.65-2.65v-5.4 M58.2-95.7v5.4
|
||||
l-4.65,2.7l-4.65-2.7 M53.55-87.6v5.4 M58.2-79.55l4.65-2.65l4.65,2.65 M62.85-82.2v-5.4l4.65-2.7l4.65,2.7v5.4 M67.5-90.3v-5.4
|
||||
M76.85-95.7v5.4l-4.7,2.7 M58.2-90.3l4.65,2.7 M44.25-17.5l-4.65,2.65 M39.6-9.45l4.65,2.7 M39.6-74.15l4.65,2.7 M39.6-57.95
|
||||
l4.65,2.7 M44.25-49.85L39.6-47.2 M62.85-49.85L58.2-47.2 M16.3-49.85l-4.65,2.65 M44.25-82.2l-4.65,2.65 M76.85-90.3l4.65,2.7
|
||||
M81.5-49.85l-4.65,2.65 M86.15,1.35l-4.65-2.7 M44.25-1.35l-4.65,2.7 M62.85-1.35l-4.65,2.7 M109.4-1.35l-4.65,2.7 M25.6-1.35
|
||||
l-4.65,2.7 M-95.4,95.7v-5.4l4.65-2.7l4.65,2.7v5.4 M-86.1,90.3l4.7-2.7l4.65,2.7v5.4 M-104.7,95.7v-5.4l4.65-2.7l4.65,2.7
|
||||
M-100.05,82.2l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4L-100.05,82.2z M-95.4,74.15l4.65-2.7l4.65,2.7v5.4l-4.65,2.65l-4.65-2.65
|
||||
M-86.1,74.15l4.7-2.7l4.65,2.7v5.4l-4.65,2.65l-4.7-2.65 M-100.05,87.6v-5.4 M-90.75,87.6v-5.4 M-81.4,87.6v-5.4 M-109.35,87.6
|
||||
l4.65,2.7 M-104.7,79.55l-4.65,2.65 M-109.35,55.25l4.65,2.7v5.4l-4.65,2.7 M-109.35,71.45l4.65,2.7 M-90.75,66.05l-4.65-2.7v-5.4
|
||||
l4.65-2.7l4.65,2.7v5.4L-90.75,66.05v5.4 M-86.1,57.95l4.7-2.7l4.65,2.7v5.4l-4.65,2.7l-4.7-2.7 M-81.4,71.45v-5.4 M-100.05,71.45
|
||||
v-5.4l4.65-2.7 M-95.4,57.95l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4 M-81.4,55.25v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7
|
||||
M-100.05,66.05l-4.65-2.7 M-104.7,57.95l4.65-2.7 M-86.1,41.8l-4.65-2.7v-5.4l4.65-2.7l4.7,2.7v5.4L-86.1,41.8z M-81.4,49.85
|
||||
l-4.7-2.65v-5.4 M-100.05,49.85l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4 M-95.4,31l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7V31
|
||||
l4.65,2.7 M-86.1,31v-5.4l4.7-2.7l4.65,2.7V31l-4.65,2.7 M-100.05,39.1v-5.4 M-90.75,39.1l-4.65,2.7 M-109.35,39.1l4.65,2.7
|
||||
M-104.7,31l-4.65,2.7 M-109.35,6.75l4.65,2.7v5.4l-4.65,2.65 M-86.1,14.85v-5.4l4.7-2.7l4.65,2.7v5.4l-4.65,2.65L-86.1,14.85z
|
||||
M-81.4,22.9v-5.4 M-86.1,25.6l-4.65-2.7v-5.4l4.65-2.65 M-100.05,22.9v-5.4l4.65-2.65l4.65,2.65 M-100.05,6.75v-5.4l4.65-2.7
|
||||
l4.65,2.7v5.4l-4.65,2.7L-100.05,6.75z M-81.4,6.75v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7 M-90.75,6.75l4.65,2.7 M-95.4,14.85v-5.4
|
||||
M-95.4,25.6l4.65-2.7 M-100.05,17.5l-4.65-2.65 M-104.7,9.45l4.65-2.7 M-109.35,22.9l4.65,2.7 M-86.1,47.2l-4.65,2.65
|
||||
M-104.7,47.2l-4.65,2.65 M-11.6,95.7v-5.4l4.65-2.7l4.65,2.7 M-20.9,95.7v-5.4l4.65-2.7l4.65,2.7 M-16.25,82.2l-4.65-2.65v-5.4
|
||||
l4.65-2.7l4.65,2.7v5.4L-16.25,82.2v5.4 M-2.3,79.55l-4.65,2.65l-4.65-2.65 M-6.95,82.2v5.4 M-30.2,95.7v-5.4l4.65-2.7l4.65,2.7
|
||||
M-39.55,95.7v-5.4l4.7-2.7l4.65,2.7 M-39.55,79.55v-5.4l4.7-2.7l4.65,2.7v5.4l-4.65,2.65L-39.55,79.55l-4.65,2.65l-4.65-2.65
|
||||
M-20.9,79.55l-4.65,2.65l-4.65-2.65 M-34.85,87.6v-5.4 M-25.55,87.6v-5.4 M-20.9,74.15l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4
|
||||
M-30.2,74.15l4.65-2.7 M-25.55,66.05l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M-39.55,74.15l-4.65-2.7v-5.4l4.65-2.7l4.7,2.7v5.4
|
||||
M-30.2,57.95l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4 M-39.55,63.35v-5.4l4.7-2.7 M-30.2,63.35l-4.65,2.7 M-11.6,74.15l4.65-2.7
|
||||
l4.65,2.7 M-6.95,66.05l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4L-6.95,66.05v5.4 M-16.25,66.05l4.65-2.7 M-11.6,57.95l-4.65-2.7v-5.4
|
||||
l4.65-2.65l4.65,2.65v5.4 M-16.25,55.25l-4.65,2.7 M-48.85,95.7v-5.4l4.65-2.7l4.65,2.7 M-58.15,95.7v-5.4l4.65-2.7l4.65,2.7
|
||||
M-58.15,79.55v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65L-58.15,79.55l-4.65,2.65l-4.65-2.65 M-44.2,87.6v-5.4 M-53.5,87.6v-5.4
|
||||
M-67.45,95.7v-5.4l4.65-2.7l4.65,2.7 M-76.75,90.3l4.65-2.7l4.65,2.7 M-76.75,74.15l4.65-2.7l4.65,2.7v5.4l-4.65,2.65l-4.65-2.65
|
||||
M-62.8,87.6v-5.4 M-72.1,87.6v-5.4 M-67.45,74.15l4.65-2.7l4.65,2.7 M-62.8,71.45v-5.4l4.65-2.7l4.65,2.7v5.4 M-62.8,66.05
|
||||
l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M-76.75,63.35l4.65,2.7v5.4 M-62.8,55.25v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7
|
||||
M-67.45,63.35l-4.65,2.7 M-72.1,55.25l4.65,2.7 M-48.85,74.15l4.65-2.7 M-44.2,66.05l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7
|
||||
M-53.5,49.85l4.65-2.65l4.65,2.65v5.4 M-53.5,66.05l4.65-2.7 M-53.5,55.25l4.65,2.7 M-48.85,47.2v-5.4l4.65-2.7l4.65,2.7v5.4
|
||||
l-4.65,2.65 M-58.15,47.2v-5.4l4.65-2.7l4.65,2.7 M-58.15,25.6l4.65-2.7l4.65,2.7V31l-4.65,2.7l-4.65-2.7V25.6l-4.65-2.7v-5.4
|
||||
M-48.85,25.6l4.65-2.7l4.65,2.7V31l-4.65,2.7l-4.65-2.7 M-44.2,39.1v-5.4 M-53.5,39.1v-5.4 M-62.8,49.85l-4.65-2.65v-5.4l4.65-2.7
|
||||
l4.65,2.7 M-76.75,47.2v-5.4l4.65-2.7l4.65,2.7 M-76.75,25.6l4.65-2.7l4.65,2.7V31l-4.65,2.7l-4.65-2.7 M-58.15,31l-4.65,2.7
|
||||
l-4.65-2.7 M-72.1,39.1v-5.4 M-62.8,39.1v-5.4 M-67.45,14.85v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65L-67.45,14.85l-4.65,2.65
|
||||
l-4.65-2.65 M-72.1,22.9v-5.4 M-62.8,6.75v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7 M-72.1,6.75l4.65,2.7 M-44.2,22.9v-5.4l4.65-2.65
|
||||
l4.7,2.65v5.4l-4.7,2.7 M-44.2,17.5l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4 M-48.85,14.85l-4.65,2.65l-4.65-2.65 M-53.5,22.9v-5.4
|
||||
M-44.2,6.75v-5.4l4.65-2.7l4.7,2.7v5.4l-4.7,2.7 M-53.5,6.75l4.65,2.7 M-67.45,25.6l4.65-2.7 M-11.6,47.2v-5.4l4.65-2.7l4.65,2.7
|
||||
M-11.6,41.8l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M-16.25,33.7L-20.9,31v-5.4l4.65-2.7l4.65,2.7V31 M-2.3,31l-4.65,2.7
|
||||
M-30.2,47.2v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65 M-39.55,41.8l4.7-2.7l4.65,2.7 M-34.85,22.9l4.65,2.7V31l-4.65,2.7l-4.7-2.7
|
||||
M-20.9,31l-4.65,2.7L-30.2,31 M-34.85,39.1v-5.4 M-25.55,39.1v-5.4 M-20.9,25.6l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4
|
||||
M-25.55,17.5l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4 M-25.55,6.75v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7 M-30.2,14.85l-4.65,2.65
|
||||
M-34.85,6.75l4.65,2.7 M-6.95,17.5l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4L-6.95,17.5v5.4l-4.65,2.7 M-11.6,14.85l-4.65,2.65
|
||||
M-16.25,6.75l4.65,2.7 M-16.25,1.35l4.65-2.7l4.65,2.7v5.4 M-6.95,22.9l4.65,2.7 M-30.2,25.6l4.65-2.7 M-16.25,39.1l-4.65,2.7
|
||||
M-67.45,47.2l-4.65,2.65 M-2.3,47.2l-4.65,2.65 M-34.85,49.85l-4.7-2.65 M-16.25,49.85l-4.65-2.65 M-81.4,39.1l4.65,2.7
|
||||
M-11.6-1.35v-5.4l4.65-2.7l4.65,2.7 M-11.6-6.75l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4 M-16.25-14.85l-4.65-2.65v-5.4l4.65-2.7
|
||||
l4.65,2.7v5.4 M-2.3-17.5l-4.65,2.65 M-20.9-1.35v-5.4l4.65-2.7 M-20.9-6.75l-4.65-2.7v-5.4l4.65-2.65 M-25.55-9.45l-4.65,2.7
|
||||
l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65 M-30.2-6.75v5.4l-4.65,2.7 M-39.55-1.35v-5.4l4.7-2.7 M-34.85-14.85l-4.7-2.65v-5.4l4.7-2.7
|
||||
l4.65,2.7v5.4 M-30.2-33.7v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7L-30.2-33.7l-4.65,2.7l-4.7-2.7v-5.4l4.7-2.7l4.65,2.7 M-20.9-22.9
|
||||
l-4.65-2.7V-31 M-34.85-25.6V-31 M-39.55-39.1l-4.65-2.7v-5.4l4.65-2.65l4.7,2.65v5.4 M-25.55-41.8v-5.4l4.65-2.65l4.65,2.65v5.4
|
||||
l-4.65,2.7 M-6.95-31l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4L-6.95-31v5.4l-4.65,2.7 M-16.25-25.6V-31l4.65-2.7 M-16.25-41.8
|
||||
l4.65,2.7 M-16.25-47.2l4.65-2.65l4.65,2.65v5.4 M-30.2-22.9l4.65-2.7 M-16.25-31l-4.65-2.7 M-6.95-25.6l4.65,2.7 M-44.2,1.35
|
||||
l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M-58.15-1.35v-5.4l4.65-2.7l4.65,2.7 M-58.15-6.75l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4
|
||||
M-62.8-14.85l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4 M-39.55-17.5l-4.65,2.65l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M-44.2-9.45v-5.4
|
||||
M-53.5-14.85l4.65-2.65 M-62.8,1.35l-4.65-2.7v-5.4l4.65-2.7 M-76.75-6.75l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4L-76.75-6.75
|
||||
v5.4 M-76.75-17.5v-5.4l4.65-2.7l4.65,2.7 M-67.45-17.5l-4.65,2.65 M-72.1-9.45l4.65,2.7 M-67.45-33.7v-5.4l4.65-2.7l4.65,2.7v5.4
|
||||
L-62.8-31L-67.45-33.7z M-62.8-25.6V-31 M-72.1-25.6V-31l4.65-2.7 M-72.1-31l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M-76.75-39.1
|
||||
l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4 M-62.8-41.8v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7 M-44.2-25.6V-31l4.65-2.7 M-44.2-31
|
||||
l-4.65-2.7v-5.4l4.65-2.7 M-48.85-33.7L-53.5-31l-4.65-2.7 M-48.85-22.9l-4.65-2.7V-31 M-53.5-41.8l4.65,2.7 M-58.15-22.9l4.65-2.7
|
||||
M-58.15-49.85v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65 M-48.85-55.25l4.65-2.7l4.65,2.7v5.4 M-58.15-71.45l4.65-2.7l4.65,2.7v5.4
|
||||
l-4.65,2.7l-4.65-2.7V-71.45z M-48.85-71.45l4.65-2.7l4.65,2.7v5.4l-4.65,2.7l-4.65-2.7 M-44.2-57.95v-5.4 M-53.5-57.95v-5.4
|
||||
M-76.75-49.85v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65 M-67.45-55.25l4.65-2.7l4.65,2.7 M-76.75-55.25l-4.65-2.7v-5.4l4.65-2.7
|
||||
l4.65,2.7v5.4 M-81.4-63.35l-4.7-2.7v-5.4l4.7-2.7l4.65,2.7v5.4 M-81.4-74.15v-5.4l4.65-2.65l4.65,2.65v5.4l-4.65,2.7
|
||||
M-58.15-66.05l-4.65,2.7l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M-62.8-57.95v-5.4 M-72.1-74.15l4.65,2.7 M-67.45-66.05l-4.65,2.7
|
||||
M-67.45-82.2v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65L-67.45-82.2l-4.65,2.65 M-76.75-82.2v-5.4l4.65-2.7l4.65,2.7 M-72.1-90.3
|
||||
v-5.4 M-62.8-90.3v-5.4 M-53.5-74.15v-5.4l4.65-2.65l4.65,2.65v5.4 M-58.15-82.2l4.65,2.65 M-58.15-87.6l4.65-2.7l4.65,2.7v5.4
|
||||
M-53.5-90.3v-5.4 M-44.2-95.7v5.4l-4.65,2.7 M-62.8-74.15v-5.4 M-11.6-49.85v-5.4l4.65-2.7l4.65,2.7 M-20.9-49.85v-5.4l4.65-2.7
|
||||
l4.65,2.7 M-16.25-63.35l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4L-16.25-63.35v5.4 M-2.3-66.05l-4.65,2.7l-4.65-2.7 M-11.6-71.45
|
||||
l4.65-2.7l4.65,2.7 M-6.95-63.35v5.4 M-25.55-47.2l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M-39.55-55.25l4.7-2.7l4.65,2.7
|
||||
M-39.55-71.45l4.7-2.7l4.65,2.7v5.4l-4.65,2.7l-4.7-2.7 M-20.9-71.45l-4.65-2.7v-5.4l4.65-2.65l4.65,2.65v5.4 M-20.9-66.05
|
||||
l-4.65,2.7l-4.65-2.7 M-25.55-74.15l-4.65,2.7 M-34.85-63.35v5.4 M-25.55-57.95v-5.4 M-34.85-74.15v-5.4l4.65-2.65l4.65,2.65
|
||||
M-44.2-79.55l4.65-2.65l4.7,2.65 M-39.55-82.2v-5.4l4.7-2.7l4.65,2.7v5.4 M-34.85-90.3v-5.4 M-16.25-95.7v5.4l-4.65,2.7l-4.65-2.7
|
||||
v-5.4 M-25.55-90.3l-4.65,2.7 M-20.9-82.2v-5.4 M-6.95-79.55l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7v5.4L-6.95-79.55v5.4 M-16.25-79.55
|
||||
l4.65-2.65 M-11.6-87.6l-4.65-2.7 M-6.95-95.7v5.4 M-44.2-90.3l4.65,2.7 M-62.8-47.2l-4.65-2.65 M-44.2-47.2l-4.65-2.65
|
||||
M-30.2-49.85l-4.65,2.65 M-2.3-49.85l-4.65,2.65 M-95.4-1.35v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.7 M-86.1-6.75l4.7-2.7
|
||||
M-100.05,1.35l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7 M-95.4-17.5l-4.65,2.65l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7V-17.5z M-95.4-22.9
|
||||
l4.65-2.7l4.65,2.7v5.4l-4.65,2.65l-4.65-2.65 M-81.4-14.85l-4.7-2.65 M-100.05-9.45v-5.4 M-90.75-14.85v5.4 M-109.35-9.45
|
||||
l4.65,2.7 M-104.7-17.5l-4.65,2.65 M-109.35-41.8l4.65,2.7v5.4l-4.65,2.7 M-90.75-25.6V-31l4.65-2.7l4.7,2.7v5.4l-4.7,2.7
|
||||
M-100.05-25.6V-31l4.65-2.7l4.65,2.7 M-100.05-47.2l4.65-2.65l4.65,2.65v5.4l-4.65,2.7l-4.65-2.7V-47.2l-4.65-2.65 M-90.75-41.8
|
||||
l4.65,2.7v5.4 M-86.1-39.1l4.7-2.7 M-95.4-33.7v-5.4 M-109.35-25.6l4.65,2.7 M-100.05-31l-4.65-2.7 M-104.7-39.1l4.65-2.7
|
||||
M-95.4-49.85v-5.4l4.65-2.7l4.65,2.7v5.4l-4.65,2.65 M-86.1-55.25l4.7-2.7 M-95.4-55.25l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4
|
||||
M-100.05-63.35l-4.65-2.7v-5.4l4.65-2.7l4.65,2.7v5.4 M-95.4-71.45l4.65-2.7l4.65,2.7 M-86.1-66.05l-4.65,2.7 M-109.35-57.95
|
||||
l4.65,2.7v5.4l-4.65,2.65 M-109.35-74.15l4.65,2.7 M-104.7-66.05l-4.65,2.7 M-109.35-90.3l4.65,2.7v5.4l-4.65,2.65 M-81.4-79.55
|
||||
l-4.7-2.65v-5.4l4.7-2.7l4.65,2.7 M-86.1-82.2l-4.65,2.65l-4.65-2.65v-5.4l4.65-2.7l4.65,2.7 M-100.05-74.15v-5.4l4.65-2.65
|
||||
M-95.4-87.6l-4.65-2.7v-5.4 M-81.4-95.7v5.4 M-90.75-95.7v5.4 M-90.75-79.55v5.4 M-104.7-55.25l4.65-2.7 M-100.05-79.55
|
||||
l-4.65-2.65 M-104.7-87.6l4.65-2.7 M-81.4-47.2l-4.7-2.65 M-76.75-33.7L-81.4-31 M-81.4-25.6l4.65,2.7 M-67.45-1.35l-4.65,2.7
|
||||
M-104.7-1.35l-4.65,2.7 M-81.4,1.35l-4.7-2.7 M-25.55,1.35l-4.65-2.7 M-53.5,1.35l4.65-2.7 M-2.3-1.35l-4.65,2.7 M-2.3,57.95
|
||||
l4.65-2.7 M2.35,66.05l-4.65-2.7 M-2.3-39.1l4.65-2.7 M2.35-31l-4.65-2.7 M2.35-79.55L-2.3-82.2 M-2.3-87.6l4.65-2.7 M-2.3,9.45
|
||||
l4.65-2.7 M2.35,17.5l-4.65-2.65"/>
|
||||
</symbol>
|
||||
<g id="Layer_1">
|
||||
</g>
|
||||
<g id="Layer_6">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#EFEEEE" d="M217.5,108.95c0,29.833-10.533,55.399-31.6,76.7
|
||||
c-0.7,0.833-1.484,1.6-2.351,2.3c-3.466,3.399-7.134,6.483-11,9.25c-18.267,13.467-39.366,20.2-63.3,20.2
|
||||
c-23.967,0-45.033-6.733-63.2-20.2c-4.8-3.4-9.3-7.25-13.5-11.55c-16.367-16.267-26.417-35.167-30.15-56.7
|
||||
c-0.733-4.2-1.217-8.467-1.45-12.8c-0.1-2.4-0.15-4.801-0.15-7.2c0-2.534,0.05-4.95,0.15-7.25c0-0.233,0.066-0.467,0.2-0.7
|
||||
c1.567-26.6,12.033-49.583,31.4-68.95C53.85,11.017,79.417,0.5,109.25,0.5c29.934,0,55.483,10.517,76.65,31.55
|
||||
C206.967,53.483,217.5,79.117,217.5,108.95z"/>
|
||||
</g>
|
||||
<g id="Layer_5">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#010101" d="M195.45,43l-22.4,22.4c-8.833,13-13.25,27.867-13.25,44.6
|
||||
c0,17.934,5.067,33.833,15.2,47.7l19,18.95c-2.5,3.066-5.2,6.066-8.1,9c-0.7,0.833-1.484,1.6-2.351,2.3
|
||||
c-2.533,2.5-5.167,4.816-7.899,6.95L158.1,177.35c-13.934-10.733-30.133-16.1-48.6-16.1c-17.933,0-33.833,5.1-47.7,15.3
|
||||
L43.25,195.15c-3.767-2.867-7.333-6.034-10.7-9.5c-2.8-2.801-5.417-5.667-7.85-8.601l19.15-19.2
|
||||
c10.066-13.966,15.1-29.916,15.1-47.85c0-17.5-4.867-33.017-14.6-46.55l-21.05-21c2.833-3.6,5.917-7.067,9.25-10.4
|
||||
c2.934-2.867,5.934-5.55,9-8.05L61.9,44.35C75.7,54.583,91.567,59.7,109.5,59.7c18.467,0,34.666-5.367,48.6-16.1L177.4,24.35
|
||||
c2.899,2.367,5.732,4.933,8.5,7.7C189.367,35.583,192.55,39.233,195.45,43z"/>
|
||||
</g>
|
||||
<g id="Layer_4">
|
||||
<defs>
|
||||
<path id="SVGID_1_" d="M159.8,110c0-16.733,4.417-31.6,13.25-44.6l22.4-22.4c-2.9-3.767-6.083-7.417-9.55-10.95
|
||||
c-2.768-2.767-5.601-5.333-8.5-7.7L158.1,43.6c-13.934,10.733-30.133,16.1-48.6,16.1c-17.933,0-33.8-5.117-47.6-15.35L41.55,24
|
||||
c-3.066,2.5-6.066,5.183-9,8.05c-3.333,3.333-6.417,6.8-9.25,10.4l21.05,21c9.733,13.533,14.6,29.05,14.6,46.55
|
||||
c0,17.934-5.034,33.884-15.1,47.85l-19.15,19.2c2.433,2.934,5.05,5.8,7.85,8.601c3.367,3.466,6.934,6.633,10.7,9.5L61.8,176.55
|
||||
c13.867-10.2,29.767-15.3,47.7-15.3c18.467,0,34.666,5.366,48.6,16.1L175.65,194.9c2.732-2.134,5.366-4.45,7.899-6.95
|
||||
c0.866-0.7,1.65-1.467,2.351-2.3c2.899-2.934,5.6-5.934,8.1-9l-19-18.95C164.867,143.833,159.8,127.934,159.8,110z"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_2_">
|
||||
<use xlink:href="#SVGID_1_" overflow="visible"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#SVGID_2_)">
|
||||
|
||||
<use xlink:href="#hex_grid" width="228.55" height="197.233" x="-114.25" y="-98.617" transform="matrix(1.1415 0 0 -1.1415 105.5 107.75)" overflow="visible"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#35C6F4" d="M79.1,110.95c-0.033-0.667-0.05-1.333-0.05-2
|
||||
c0-0.7,0.017-1.366,0.05-2c0-0.067,0.017-0.134,0.05-0.2c0.434-7.367,3.333-13.733,8.7-19.1c5.9-5.833,12.983-8.75,21.25-8.75
|
||||
c8.301,0,15.384,2.917,21.25,8.75c5.834,5.934,8.75,13.033,8.75,21.3c0,8.267-2.916,15.35-8.75,21.25
|
||||
c-0.199,0.233-0.416,0.45-0.649,0.649c-0.967,0.934-1.983,1.784-3.05,2.551c-5.066,3.733-10.917,5.6-17.551,5.6
|
||||
c-6.633,0-12.466-1.866-17.5-5.6c-1.333-0.934-2.583-2-3.75-3.2c-4.533-4.5-7.317-9.733-8.35-15.7
|
||||
C79.3,113.334,79.167,112.15,79.1,110.95z M126.1,127.25l3.601,3.6L126.1,127.25z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#35C6F4" stroke-width="2" stroke-miterlimit="1" d="
|
||||
M158.6,60.25l-15,14.65 M31.7,33.1l40.75,40.65 M126.1,127.25l3.601,3.6 M157.05,158l27.65,28.6 M153.05,153.95l-10.75-11.2
|
||||
M186.6,33l-28,27.25 M33.15,186.25l27.35-27.4"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#35C6F4" stroke-width="7" stroke-miterlimit="1" d="
|
||||
M158.6,60.25l-16.949,17.2 M59.4,61.35L76.6,78.5 M60.5,158.85l16.75-17.399 M153.05,153.95l4,4.05 M139.45,140.4l13.6,13.55"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -221,12 +221,15 @@ Function PackageTests()
|
||||
Function RunGulp()
|
||||
{
|
||||
Write-Host "##teamcity[progressStart 'Running Gulp']"
|
||||
$gulpPath = '.\node_modules\gulp\bin\gulp'
|
||||
Invoke-Expression 'npm install'
|
||||
CheckExitCode
|
||||
|
||||
Invoke-Expression 'gulp build' -ErrorAction Continue -Verbose
|
||||
Invoke-Expression ('node ' + $gulpPath + ' build') -ErrorAction Continue -Verbose
|
||||
CheckExitCode
|
||||
|
||||
Remove-Item $outputFolder\UI\build.txt -ErrorAction Continue
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Running Gulp']"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<Query Kind="Program" />
|
||||
|
||||
void Main()
|
||||
{
|
||||
var files = Directory.GetFiles("c:\\git\\sonarr\\src\\UI","*.js", SearchOption.AllDirectories);
|
||||
|
||||
var moduleRegex = new Regex(@"module.exports\s*=\s*\(function\s*\(\)\s*{\n\s*return\s*(\w|\W)*\)\.call\(this\);$");
|
||||
var functionHead = new Regex(@"\s*\(function\s*\(\)\s*{\n\s*return\s*");
|
||||
var functionTail = new Regex(@"\}\).call\(this\);$");
|
||||
var multiVar = new Regex(@"^(?<d>var\s*\w*\s*=\s*require\(.*\)),");
|
||||
var seperateDeclearatuin = new Regex(@"^((\w|\$|_)*\s=\srequire\(.*\))(,|;)", RegexOptions.Multiline);
|
||||
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
var text = File.ReadAllText(filePath);
|
||||
|
||||
var newContent = text.Replace("// Generated by uRequire v0.7.0-beta.14 template: 'nodejs'","");
|
||||
|
||||
newContent = newContent.Replace("var __isAMD = !!(typeof define === 'function' && define.amd),","");
|
||||
newContent = newContent.Replace("__isNode = (typeof exports === 'object'),","");
|
||||
newContent = newContent.Replace("__isWeb = !__isNode;","");
|
||||
newContent = newContent.Replace("\"use strict\";","'use strict';");
|
||||
|
||||
newContent = newContent.Trim();
|
||||
|
||||
|
||||
|
||||
if(moduleRegex.IsMatch(newContent))
|
||||
{
|
||||
filePath.Dump();
|
||||
|
||||
newContent = functionHead.Replace(newContent," ");
|
||||
newContent = functionTail.Replace(newContent,"");
|
||||
}
|
||||
|
||||
if(multiVar.IsMatch(newContent))
|
||||
{
|
||||
newContent = multiVar.Replace(newContent,"$1;"); //first one
|
||||
|
||||
}
|
||||
|
||||
newContent = seperateDeclearatuin.Replace(newContent,"var $1;"); //ones after
|
||||
newContent.Replace("var $ = require('jquery'), var","var $ = require('jquery');");
|
||||
|
||||
File.WriteAllText(filePath,newContent.Trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Define other methods and classes here
|
||||
@@ -2,11 +2,12 @@ var gulp = require('gulp');
|
||||
var runSequence = require('run-sequence');
|
||||
|
||||
require('./clean');
|
||||
require('./requirejs');
|
||||
require('./less');
|
||||
require('./handlebars');
|
||||
require('./copy');
|
||||
|
||||
gulp.task('build', function () {
|
||||
return runSequence('clean',
|
||||
['webpack', 'less', 'handlebars', 'copyHtml', 'copyContent', 'copyJs']);
|
||||
});
|
||||
['requireJs', 'less', 'handlebars', 'copyIndex', 'copyContent']);
|
||||
});
|
||||
14
gulp/copy.js
14
gulp/copy.js
@@ -5,23 +5,19 @@ var cache = require('gulp-cached');
|
||||
var paths = require('./paths.js');
|
||||
|
||||
gulp.task('copyJs', function () {
|
||||
return gulp.src(
|
||||
[
|
||||
paths.src.root + "polyfills.js",
|
||||
paths.src.root + "JsLibraries\\handlebars.runtime.js",
|
||||
])
|
||||
return gulp.src(paths.src.scripts)
|
||||
.pipe(cache('copyJs'))
|
||||
.pipe(print())
|
||||
.pipe(gulp.dest(paths.dest.root));
|
||||
});
|
||||
|
||||
gulp.task('copyHtml', function () {
|
||||
return gulp.src(paths.src.html)
|
||||
.pipe(cache('copyHtml'))
|
||||
gulp.task('copyIndex', function () {
|
||||
return gulp.src(paths.src.index)
|
||||
.pipe(cache('copyIndex'))
|
||||
.pipe(gulp.dest(paths.dest.root));
|
||||
});
|
||||
|
||||
gulp.task('copyContent', function () {
|
||||
return gulp.src([paths.src.content + '**/*.*', '!**/*.less'])
|
||||
.pipe(gulp.dest(paths.dest.content));
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
require('./watch.js');
|
||||
require('./build.js');
|
||||
require('./clean.js');
|
||||
require('./requirejs.js');
|
||||
require('./jshint.js');
|
||||
require('./handlebars.js');
|
||||
require('./copy.js');
|
||||
require('./less.js');
|
||||
require('./stripBom.js');
|
||||
require('./imageMin.js');
|
||||
require('./webpack.js');
|
||||
|
||||
|
||||
|
||||
@@ -2,17 +2,18 @@ var gulp = require('gulp');
|
||||
var handlebars = require('gulp-handlebars');
|
||||
var declare = require('gulp-declare');
|
||||
var concat = require('gulp-concat');
|
||||
var wrapAmd = require('gulp-wrap-amd');
|
||||
var wrap = require("gulp-wrap");
|
||||
var path = require('path');
|
||||
var streamqueue = require('streamqueue');
|
||||
var stripbom = require('gulp-stripbom');
|
||||
|
||||
var paths = require('./paths.js');
|
||||
var bom = require('./pipelines/gulp-bom.js');
|
||||
|
||||
gulp.task('handlebars', function () {
|
||||
|
||||
var coreStream = gulp.src([paths.src.templates, '!*/**/*Partial.*'])
|
||||
.pipe(stripbom({ showLog: false }))
|
||||
.pipe(bom())
|
||||
.pipe(handlebars())
|
||||
.pipe(declare({
|
||||
namespace: 'T',
|
||||
@@ -29,7 +30,7 @@ gulp.task('handlebars', function () {
|
||||
}));
|
||||
|
||||
var partialStream = gulp.src([paths.src.partials])
|
||||
.pipe(stripbom({ showLog: false }))
|
||||
.pipe(bom())
|
||||
.pipe(handlebars())
|
||||
.pipe(wrap('Handlebars.template(<%= contents %>)'))
|
||||
.pipe(wrap('Handlebars.registerPartial(<%= processPartialName(file.relative) %>, <%= contents %>)', {}, {
|
||||
@@ -47,6 +48,10 @@ gulp.task('handlebars', function () {
|
||||
partialStream,
|
||||
coreStream
|
||||
).pipe(concat('templates.js'))
|
||||
|
||||
.pipe(wrapAmd({
|
||||
deps: ['handlebars'],
|
||||
params: ['Handlebars'],
|
||||
exports: 'this["T"]'
|
||||
}))
|
||||
.pipe(gulp.dest(paths.dest.root));
|
||||
});
|
||||
|
||||
@@ -8,6 +8,19 @@ var paths = require('./paths.js');
|
||||
gulp.task('jshint', function () {
|
||||
return gulp.src([paths.src.scripts, paths.src.exclude.libs])
|
||||
.pipe(cache('jshint'))
|
||||
.pipe(jshint())
|
||||
.pipe(jshint({
|
||||
'-W030': false,
|
||||
'-W064': false,
|
||||
'-W097': false, //Use the function form of “use strict”
|
||||
'-W100': false, //Silently deleted characters (in locales)
|
||||
'undef': true,
|
||||
'globals': {
|
||||
'require': true,
|
||||
'define': true,
|
||||
'window': true,
|
||||
'document': true,
|
||||
'console': true
|
||||
}
|
||||
}))
|
||||
.pipe(jshint.reporter(stylish));
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ module.exports = {
|
||||
src: {
|
||||
root: './src/UI/',
|
||||
templates: './src/UI/**/*.hbs',
|
||||
html: './src/UI/*.html',
|
||||
index: './src/UI/index.html',
|
||||
partials: './src/UI/**/*Partial.hbs',
|
||||
scripts: './src/UI/**/*.js',
|
||||
less: ['./src/UI/**/*.less'],
|
||||
|
||||
4
gulp/pipelines/gulp-bom.js
Normal file
4
gulp/pipelines/gulp-bom.js
Normal file
@@ -0,0 +1,4 @@
|
||||
var replace = require('gulp-replace');
|
||||
module.exports = function() {
|
||||
return replace(/^\uFEFF/, '');
|
||||
};
|
||||
32
gulp/requirejs.js
Normal file
32
gulp/requirejs.js
Normal file
@@ -0,0 +1,32 @@
|
||||
var gulp = require('gulp');
|
||||
var requirejs = require('requirejs');
|
||||
var paths = require('./paths');
|
||||
|
||||
require('./handlebars.js');
|
||||
require('./jshint.js');
|
||||
|
||||
|
||||
gulp.task('requireJs', ['jshint'], function (cb) {
|
||||
|
||||
var config = {
|
||||
mainConfigFile: 'src/UI/app.js',
|
||||
fileExclusionRegExp: /^.*\.(?!js$)[^.]+$/,
|
||||
preserveLicenseComments: false,
|
||||
dir: paths.dest.root,
|
||||
optimize: 'none',
|
||||
removeCombined: true,
|
||||
inlineText: false,
|
||||
keepBuildDir: true,
|
||||
modules: [
|
||||
{
|
||||
name: 'app',
|
||||
exclude: ['templates.js']
|
||||
}
|
||||
]};
|
||||
|
||||
requirejs.optimize(config, function (buildResponse) {
|
||||
console.log(buildResponse);
|
||||
cb();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,14 +1,22 @@
|
||||
var gulp = require('gulp');
|
||||
var paths = require('./paths.js');
|
||||
var stripbom = require('gulp-stripbom');
|
||||
var bom = require('./pipelines/gulp-bom.js');
|
||||
var gulpPrint = require('gulp-print');
|
||||
|
||||
|
||||
var stripBom = function (dest) {
|
||||
gulp.src([paths.src.scripts, paths.src.exclude.libs])
|
||||
.pipe(stripbom({ showLog: false }))
|
||||
.pipe(bom())
|
||||
.pipe(gulpPrint(function (filepath) {
|
||||
return "booming: " + filepath;
|
||||
}))
|
||||
.pipe(gulp.dest(dest));
|
||||
|
||||
gulp.src(paths.src.templates)
|
||||
.pipe(stripbom({ showLog: false }))
|
||||
.pipe(bom())
|
||||
.pipe(gulpPrint(function (filepath) {
|
||||
return "booming: " + filepath;
|
||||
}))
|
||||
.pipe(gulp.dest(dest));
|
||||
};
|
||||
|
||||
|
||||
@@ -8,26 +8,23 @@ require('./jshint.js');
|
||||
require('./handlebars.js');
|
||||
require('./less.js');
|
||||
require('./copy.js');
|
||||
require('./webpack.js');
|
||||
|
||||
|
||||
gulp.task('watch', ['jshint', 'handlebars', 'less','copyHtml', 'copyContent','copyJs'], function () {
|
||||
gulp.start('webpackWatch');
|
||||
gulp.watch([paths.src.scripts, paths.src.exclude.libs], ['jshint','copyJs']);
|
||||
gulp.task('watch', ['jshint', 'handlebars', 'less', 'copyJs','copyIndex', 'copyContent'], function () {
|
||||
gulp.watch([paths.src.scripts, paths.src.exclude.libs], ['jshint', 'copyJs']);
|
||||
gulp.watch(paths.src.templates, ['handlebars']);
|
||||
gulp.watch([paths.src.less, paths.src.exclude.libs], ['less']);
|
||||
gulp.watch([paths.src.html], ['copyHtml']);
|
||||
gulp.watch([paths.src.index], ['copyIndex']);
|
||||
gulp.watch([paths.src.content + '**/*.*', '!**/*.less'], ['copyContent']);
|
||||
});
|
||||
|
||||
gulp.task('liveReload', ['jshint', 'handlebars', 'less', 'webPack'], function () {
|
||||
gulp.task('liveReload', ['jshint', 'handlebars', 'less', 'copyJs'], function () {
|
||||
var server = livereload();
|
||||
gulp.watch([
|
||||
'app/**/*.js',
|
||||
'app/**/*.css',
|
||||
'app/index.html',
|
||||
'app/login.html'
|
||||
'app/index.html'
|
||||
]).on('change', function (file) {
|
||||
server.changed(file.path);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
var gulp = require('gulp');
|
||||
|
||||
var gulpWebpack = require('gulp-webpack');
|
||||
var webpack = require('webpack');
|
||||
var webpackConfig = require('../webpack.config');
|
||||
|
||||
webpackConfig.devtool = "#source-map";
|
||||
|
||||
gulp.task('webpack', function() {
|
||||
return gulp.src('main.js')
|
||||
.pipe(gulpWebpack(webpackConfig, webpack))
|
||||
.pipe(gulp.dest(''));
|
||||
});
|
||||
|
||||
gulp.task('webpackWatch', function() {
|
||||
webpackConfig.watch = true;
|
||||
return gulp.src('main.js')
|
||||
.pipe(gulpWebpack(webpackConfig, webpack))
|
||||
.pipe(gulp.dest(''));
|
||||
});
|
||||
@@ -2,6 +2,6 @@ EXCLUDE="-exclude:Windows -include:IntegrationTest"
|
||||
TESTDIR="."
|
||||
NUNIT="$TESTDIR/NUnit.Runners.2.6.1/tools/nunit-console-x86.exe"
|
||||
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Api.Result.xml $TESTDIR/NzbDrone.Api.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Core.Result.xml $TESTDIR/NzbDrone.Core.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Integration.Result.xml $TESTDIR/NzbDrone.Integration.Test.dll
|
||||
mono --debug $NUNIT $EXCLUDE -xml:NzbDrone.Api.Result.xml $TESTDIR/NzbDrone.Api.Test.dll
|
||||
mono --debug $NUNIT $EXCLUDE -xml:NzbDrone.Core.Result.xml $TESTDIR/NzbDrone.Core.Test.dll
|
||||
mono --debug $NUNIT $EXCLUDE -xml:NzbDrone.Integration.Result.xml $TESTDIR/NzbDrone.Integration.Test.dll
|
||||
31
package.json
31
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "Sonarr",
|
||||
"version": "2.0.0",
|
||||
"version": "0.0.0",
|
||||
"description": "Sonarr",
|
||||
"main": "main.js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"preinstall": ""
|
||||
},
|
||||
@@ -15,25 +15,22 @@
|
||||
"gitHead": "9ff7aa1bf7fe38c4c5bdb92f56c8ad556916ed67",
|
||||
"readmeFilename": "readme.md",
|
||||
"dependencies": {
|
||||
"del": "1.1.1",
|
||||
"fs-extra": "0.12.0",
|
||||
"gulp": "3.8.10",
|
||||
"gulp-cached": "1.0.2",
|
||||
"gulp-concat": "2.4.3",
|
||||
"gulp-cached": "1.0.1",
|
||||
"del": "0.1.3",
|
||||
"gulp-concat": "2.4.2",
|
||||
"gulp-declare": "0.3.0",
|
||||
"gulp-handlebars": "3.0.1",
|
||||
"gulp-jshint": "1.9.2",
|
||||
"gulp-less": "2.0.1",
|
||||
"gulp-handlebars": "2.2.0",
|
||||
"gulp-jshint": "1.9.0",
|
||||
"gulp-less": "1.3.6",
|
||||
"gulp-print": "1.1.0",
|
||||
"gulp-replace": "0.5.2",
|
||||
"gulp-stripbom": "1.0.4",
|
||||
"gulp-run": "1.6.6",
|
||||
"gulp-webpack": "1.2.0",
|
||||
"gulp-wrap": "0.10.1",
|
||||
"handlebars": "2.0.0",
|
||||
"jshint-loader": "0.8.1",
|
||||
"gulp-replace": "0.5.0",
|
||||
"gulp-wrap": "0.5.0",
|
||||
"gulp-wrap-amd": "0.3.1",
|
||||
"jshint-stylish": "1.0.0",
|
||||
"requirejs": "2.1.15",
|
||||
"run-sequence": "1.0.2",
|
||||
"streamqueue": "0.1.1",
|
||||
"webpack": "1.5.3"
|
||||
"streamqueue": "0.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://torznab.com/schemas/2015/feed"
|
||||
xmlns:torznab="http://torznab.com/schemas/2015/feed">
|
||||
<xs:simpleType name="attrNames">
|
||||
<xs:restriction base="xs:string">
|
||||
<!-- https://github.com/nZEDb/nZEDb/blob/master/docs/newznab_api_specification.txt -->
|
||||
<!-- http://newznab.readthedocs.org/en/latest/misc/api/ -->
|
||||
<!-- Original newznab attributes -->
|
||||
<!-- All -->
|
||||
<xs:enumeration value="size" />
|
||||
<xs:enumeration value="category" />
|
||||
<xs:enumeration value="guid" />
|
||||
<xs:enumeration value="poster" />
|
||||
<xs:enumeration value="team" />
|
||||
<xs:enumeration value="grabs" />
|
||||
<xs:enumeration value="comments" />
|
||||
<xs:enumeration value="year" />
|
||||
<!-- TV -->
|
||||
<xs:enumeration value="season" />
|
||||
<xs:enumeration value="episode" />
|
||||
<xs:enumeration value="rageid" />
|
||||
<xs:enumeration value="tvtitle" />
|
||||
<xs:enumeration value="tvairdate" />
|
||||
<!-- TV, Movies, Audio -->
|
||||
<xs:enumeration value="video" />
|
||||
<xs:enumeration value="audio" />
|
||||
<xs:enumeration value="resolution" />
|
||||
<xs:enumeration value="framerate" />
|
||||
<xs:enumeration value="language" />
|
||||
<xs:enumeration value="subs" />
|
||||
<!-- Movies -->
|
||||
<xs:enumeration value="imdb" />
|
||||
<xs:enumeration value="imdbscore" />
|
||||
<xs:enumeration value="imdbtitle" />
|
||||
<xs:enumeration value="imdbtagline" />
|
||||
<xs:enumeration value="imdbscore" />
|
||||
<xs:enumeration value="imdbtitle" />
|
||||
<xs:enumeration value="imdbtagline" />
|
||||
<xs:enumeration value="imdbplot" />
|
||||
<xs:enumeration value="imdbyear" />
|
||||
<xs:enumeration value="imdbdirector" />
|
||||
<xs:enumeration value="imdbactors" />
|
||||
<!-- TV, Movies -->
|
||||
<xs:enumeration value="genre" />
|
||||
<!-- Music -->
|
||||
<xs:enumeration value="artist" />
|
||||
<xs:enumeration value="album" />
|
||||
<xs:enumeration value="publisher" />
|
||||
<xs:enumeration value="tracks" />
|
||||
<!-- Mixed -->
|
||||
<xs:enumeration value="coverurl" />
|
||||
<xs:enumeration value="backdropcoverurl" />
|
||||
<xs:enumeration value="review" />
|
||||
<!-- Book -->
|
||||
<xs:enumeration value="booktitle" />
|
||||
<xs:enumeration value="publishdate" />
|
||||
<xs:enumeration value="author" />
|
||||
<xs:enumeration value="pages" />
|
||||
|
||||
<!-- Generic extensions -->
|
||||
<xs:enumeration value="type" /> <!-- series|movie|music|book if unknown just omit -->
|
||||
<xs:enumeration value="tvdbid" />
|
||||
<xs:enumeration value="bannerurl" />
|
||||
|
||||
<!-- Nzb extensions -->
|
||||
<xs:enumeration value="nzbhash" /> <!-- TBD, hash of sorted article headers of relevant content (relevant excludes stuff like par,nfo,nzb etc) -->
|
||||
|
||||
<!-- Torrent extensions -->
|
||||
<xs:enumeration value="infohash" />
|
||||
<xs:enumeration value="magneturl" />
|
||||
<xs:enumeration value="seeders" />
|
||||
<xs:enumeration value="leechers" />
|
||||
<xs:enumeration value="peers" /> <!-- seeders + leechers -->
|
||||
<xs:enumeration value="seedtype" /> <!-- TBD, which criteria must be met. was going for 'ratio,seedtime,both' but afaik it's always 'either' -->
|
||||
<xs:enumeration value="minimumratio" />
|
||||
<xs:enumeration value="minimumseedtime" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="attr">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="name" type="torznab:attrNames" />
|
||||
<xs:attribute name="value" type="xs:string" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -122,7 +122,7 @@ namespace Microsoft.AspNet.SignalR
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
throw new ArgumentNullException("user");
|
||||
}
|
||||
|
||||
if (!user.Identity.IsAuthenticated)
|
||||
|
||||
@@ -20,12 +20,8 @@ namespace Microsoft.AspNet.SignalR.Hosting
|
||||
throw new ArgumentNullException("instanceName");
|
||||
}
|
||||
|
||||
// Performance counters are broken on mono so just skip this step
|
||||
if (!MonoUtility.IsRunningMono)
|
||||
{
|
||||
// Initialize the performance counters
|
||||
resolver.InitializePerformanceCounters(instanceName, hostShutdownToken);
|
||||
}
|
||||
// Initialize the performance counters
|
||||
resolver.InitializePerformanceCounters(instanceName, hostShutdownToken);
|
||||
|
||||
// Dispose the dependency resolver on host shut down (cleanly)
|
||||
resolver.InitializeResolverDispose(hostShutdownToken);
|
||||
@@ -45,11 +41,12 @@ namespace Microsoft.AspNet.SignalR.Hosting
|
||||
// TODO: Guard against multiple calls to this
|
||||
|
||||
// When the host triggers the shutdown token, dispose the resolver
|
||||
hostShutdownToken.SafeRegister(state =>
|
||||
hostShutdownToken.Register(state =>
|
||||
{
|
||||
((IDependencyResolver)state).Dispose();
|
||||
},
|
||||
resolver);
|
||||
resolver,
|
||||
useSynchronizationContext: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,6 @@ namespace Microsoft.AspNet.SignalR.Hosting
|
||||
/// </summary>
|
||||
CancellationToken CancellationToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status code of the response.
|
||||
/// </summary>
|
||||
int StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content type of the response.
|
||||
/// </summary>
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace Microsoft.AspNet.SignalR.Hosting
|
||||
Action<string> OnMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the websocket closes
|
||||
/// Invoked when the websocket gracefully closes
|
||||
/// </summary>
|
||||
Action OnClose { get; set; }
|
||||
Action<bool> OnClose { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when there is an error
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace Microsoft.AspNet.SignalR.Hosting
|
||||
/// Accepts an websocket request using the specified user function.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback that fires when the websocket is ready.</param>
|
||||
/// <param name="initTask">The task that completes when the websocket transport is ready.</param>
|
||||
Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback, Task initTask);
|
||||
Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace Microsoft.AspNet.SignalR.Hubs
|
||||
private Task ExecuteHubEvent(IRequest request, string connectionId, Func<IHub, Task> action)
|
||||
{
|
||||
var hubs = GetHubs(request, connectionId).ToList();
|
||||
var operations = hubs.Select(instance => action(instance).OrEmpty().Catch()).ToArray();
|
||||
var operations = hubs.Select(instance => action(instance).Catch().OrEmpty()).ToArray();
|
||||
|
||||
if (operations.Length == 0)
|
||||
{
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.AspNet.SignalR.Hosting;
|
||||
|
||||
namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// A buffering text writer that supports writing binary directly as well
|
||||
/// </summary>
|
||||
internal unsafe class BinaryTextWriter : BufferTextWriter, IBinaryWriter
|
||||
{
|
||||
public BinaryTextWriter(IResponse response) :
|
||||
base((data, state) => ((IResponse)state).Write(data), response, reuseBuffers: true, bufferSize: 128)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BinaryTextWriter(IWebSocket socket) :
|
||||
base((data, state) => ((IWebSocket)state).SendChunk(data), socket, reuseBuffers: false, bufferSize: 1024)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public BinaryTextWriter(Action<ArraySegment<byte>, object> write, object state, bool reuseBuffers, int bufferSize) :
|
||||
base(write, state, reuseBuffers, bufferSize)
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(ArraySegment<byte> data)
|
||||
{
|
||||
Writer.Write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
/// we don't need to write to a long lived buffer. This saves massive amounts of memory
|
||||
/// as the number of connections grows.
|
||||
/// </summary>
|
||||
internal abstract unsafe class BufferTextWriter : TextWriter
|
||||
internal unsafe class BufferTextWriter : TextWriter, IBinaryWriter
|
||||
{
|
||||
private readonly Encoding _encoding;
|
||||
|
||||
@@ -31,13 +31,13 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
}
|
||||
|
||||
public BufferTextWriter(IWebSocket socket) :
|
||||
this((data, state) => ((IWebSocket)state).SendChunk(data), socket, reuseBuffers: false, bufferSize: 1024 * 4)
|
||||
this((data, state) => ((IWebSocket)state).SendChunk(data), socket, reuseBuffers: false, bufferSize: 128)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.TextWriter.#ctor", Justification = "It won't be used")]
|
||||
protected BufferTextWriter(Action<ArraySegment<byte>, object> write, object state, bool reuseBuffers, int bufferSize)
|
||||
public BufferTextWriter(Action<ArraySegment<byte>, object> write, object state, bool reuseBuffers, int bufferSize)
|
||||
{
|
||||
_write = write;
|
||||
_writeState = state;
|
||||
@@ -46,7 +46,7 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
protected internal ChunkedWriter Writer
|
||||
private ChunkedWriter Writer
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -79,12 +79,17 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
Writer.Write(value);
|
||||
}
|
||||
|
||||
public void Write(ArraySegment<byte> data)
|
||||
{
|
||||
Writer.Write(data);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Writer.Flush();
|
||||
}
|
||||
|
||||
internal class ChunkedWriter
|
||||
private class ChunkedWriter
|
||||
{
|
||||
private int _charPos;
|
||||
private int _charLen;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
@@ -51,14 +48,8 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
// This normally waits until the callback is finished invoked but we don't care
|
||||
if (_callbackWrapper.TrySetInvoked())
|
||||
{
|
||||
try
|
||||
{
|
||||
_registration.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Bug #1549, .NET 4.0 has a bug where this throws if the CTS is disposed.
|
||||
}
|
||||
// Bug #1549, .NET 4.0 has a bug where this throws if the CTS
|
||||
_registration.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
{
|
||||
using (var stream = new MemoryStream(128))
|
||||
{
|
||||
var bufferWriter = new BinaryTextWriter((buffer, state) =>
|
||||
var bufferWriter = new BufferTextWriter((buffer, state) =>
|
||||
{
|
||||
((MemoryStream)state).Write(buffer.Array, buffer.Offset, buffer.Count);
|
||||
},
|
||||
@@ -236,7 +236,8 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
|
||||
if (command == null)
|
||||
{
|
||||
if (MonoUtility.IsRunningMono)
|
||||
var platform = (int)Environment.OSVersion.Platform;
|
||||
if (platform == 4 || platform == 6 || platform == 128)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
{
|
||||
internal static class MonoUtility
|
||||
{
|
||||
private static readonly Lazy<bool> _isRunningMono = new Lazy<bool>(() => CheckRunningOnMono());
|
||||
|
||||
internal static bool IsRunningMono
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isRunningMono.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This should never fail")]
|
||||
private static bool CheckRunningOnMono()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Type.GetType("Mono.Runtime") != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
_maxSize = maxSize;
|
||||
}
|
||||
|
||||
#if !CLIENT_NET45 && !CLIENT_NET4 && !NETFX_CORE && !SILVERLIGHT
|
||||
#if !CLIENT_NET45
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is shared code.")]
|
||||
public IPerformanceCounter QueueSizeCounter { get; set; }
|
||||
#endif
|
||||
@@ -62,16 +62,19 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
|
||||
if (_maxSize != null)
|
||||
{
|
||||
// Increment the size if the queue
|
||||
if (Interlocked.Increment(ref _size) > _maxSize)
|
||||
if (Interlocked.Read(ref _size) == _maxSize)
|
||||
{
|
||||
Interlocked.Decrement(ref _size);
|
||||
|
||||
// REVIEW: Do we need to make the contract more clear between the
|
||||
// queue full case and the queue drained case? Should we throw an exeception instead?
|
||||
|
||||
// We failed to enqueue because the size limit was reached
|
||||
return null;
|
||||
}
|
||||
|
||||
#if !CLIENT_NET45 && !CLIENT_NET4 && !NETFX_CORE && !SILVERLIGHT
|
||||
// Increment the size if the queue
|
||||
Interlocked.Increment(ref _size);
|
||||
|
||||
#if !CLIENT_NET45
|
||||
var counter = QueueSizeCounter;
|
||||
if (counter != null)
|
||||
{
|
||||
@@ -90,7 +93,7 @@ namespace Microsoft.AspNet.SignalR.Infrastructure
|
||||
// Decrement the number of items left in the queue
|
||||
Interlocked.Decrement(ref queue._size);
|
||||
|
||||
#if !CLIENT_NET45 && !CLIENT_NET4 && !NETFX_CORE && !SILVERLIGHT
|
||||
#if !CLIENT_NET45
|
||||
var counter = QueueSizeCounter;
|
||||
if (counter != null)
|
||||
{
|
||||
|
||||
@@ -33,10 +33,8 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
_escapedKey = minifiedKey;
|
||||
}
|
||||
|
||||
public static void WriteCursors(TextWriter textWriter, IList<Cursor> cursors, string prefix)
|
||||
public static void WriteCursors(TextWriter textWriter, IList<Cursor> cursors)
|
||||
{
|
||||
textWriter.Write(prefix);
|
||||
|
||||
for (int i = 0; i < cursors.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
@@ -50,7 +48,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
}
|
||||
}
|
||||
|
||||
internal static void WriteUlongAsHexToBuffer(ulong value, TextWriter textWriter)
|
||||
private static void WriteUlongAsHexToBuffer(ulong value, TextWriter textWriter)
|
||||
{
|
||||
// This tracks the length of the output and serves as the index for the next character to be written into the pBuffer.
|
||||
// The length could reach up to 16 characters, so at least that much space should remain in the pBuffer.
|
||||
@@ -116,17 +114,17 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static List<Cursor> GetCursors(string cursor, string prefix)
|
||||
public static List<Cursor> GetCursors(string cursor)
|
||||
{
|
||||
return GetCursors(cursor, prefix, s => s);
|
||||
return GetCursors(cursor, s => s);
|
||||
}
|
||||
|
||||
public static List<Cursor> GetCursors(string cursor, string prefix, Func<string, string> keyMaximizer)
|
||||
public static List<Cursor> GetCursors(string cursor, Func<string, string> keyMaximizer)
|
||||
{
|
||||
return GetCursors(cursor, prefix, (key, state) => ((Func<string, string>)state).Invoke(key), keyMaximizer);
|
||||
return GetCursors(cursor, (key, state) => ((Func<string, string>)state).Invoke(key), keyMaximizer);
|
||||
}
|
||||
|
||||
public static List<Cursor> GetCursors(string cursor, string prefix, Func<string, object, string> keyMaximizer, object state)
|
||||
public static List<Cursor> GetCursors(string cursor, Func<string, object, string> keyMaximizer, object state)
|
||||
{
|
||||
// Technically GetCursors should never be called with a null value, so this is extra cautious
|
||||
if (String.IsNullOrEmpty(cursor))
|
||||
@@ -134,14 +132,6 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
throw new FormatException(Resources.Error_InvalidCursorFormat);
|
||||
}
|
||||
|
||||
// If the cursor does not begin with the prefix stream, it isn't necessarily a formatting problem.
|
||||
// The cursor with a different prefix might have had different, but also valid, formatting.
|
||||
// Null should be returned so new cursors will be generated
|
||||
if (!cursor.StartsWith(prefix, StringComparison.Ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var signals = new HashSet<string>();
|
||||
var cursors = new List<Cursor>();
|
||||
string currentKey = null;
|
||||
@@ -153,10 +143,8 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
var sbEscaped = new StringBuilder();
|
||||
Cursor parsedCursor;
|
||||
|
||||
for (int i = prefix.Length; i < cursor.Length; i++)
|
||||
foreach (var ch in cursor)
|
||||
{
|
||||
var ch = cursor[i];
|
||||
|
||||
// escape can only be true if we are consuming the key
|
||||
if (escape)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.SignalR.Infrastructure;
|
||||
|
||||
@@ -13,8 +11,6 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
{
|
||||
internal class DefaultSubscription : Subscription
|
||||
{
|
||||
internal static string _defaultCursorPrefix = GetCursorPrefix();
|
||||
|
||||
private List<Cursor> _cursors;
|
||||
private List<Topic> _cursorTopics;
|
||||
|
||||
@@ -40,7 +36,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
else
|
||||
{
|
||||
// Ensure delegate continues to use the C# Compiler static delegate caching optimization.
|
||||
_cursors = Cursor.GetCursors(cursor, _defaultCursorPrefix, (k, s) => UnminifyCursor(k, s), stringMinifier) ?? GetCursorsFromEventKeys(EventKeys, topics);
|
||||
_cursors = Cursor.GetCursors(cursor, (k, s) => UnminifyCursor(k, s), stringMinifier) ?? GetCursorsFromEventKeys(EventKeys, topics);
|
||||
}
|
||||
|
||||
_cursorTopics = new List<Topic>();
|
||||
@@ -130,7 +126,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
{
|
||||
lock (_cursors)
|
||||
{
|
||||
Cursor.WriteCursors(textWriter, _cursors, _defaultCursorPrefix);
|
||||
Cursor.WriteCursors(textWriter, _cursors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,22 +196,6 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
return list;
|
||||
}
|
||||
|
||||
private static string GetCursorPrefix()
|
||||
{
|
||||
using (var rng = new RNGCryptoServiceProvider())
|
||||
{
|
||||
var data = new byte[4];
|
||||
rng.GetBytes(data);
|
||||
|
||||
using (var writer = new StringWriter(CultureInfo.InvariantCulture))
|
||||
{
|
||||
var randomValue = (ulong)BitConverter.ToUInt32(data, 0);
|
||||
Cursor.WriteUlongAsHexToBuffer(randomValue, writer);
|
||||
return "d-" + writer.ToString() + "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong GetMessageId(TopicLookup topics, string key)
|
||||
{
|
||||
Topic topic;
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!subscription.UnsetQueued() || workTask.IsFaulted || workTask.IsCanceled)
|
||||
if (!subscription.UnsetQueued() || workTask.IsFaulted)
|
||||
{
|
||||
// If we don't have more work to do just make the subscription null
|
||||
subscription = null;
|
||||
@@ -271,7 +271,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
Trace.TraceEvent(TraceEventType.Error, 0, "Work failed for " + subscription.Identity + ": " + task.Exception.GetBaseException());
|
||||
}
|
||||
|
||||
if (moreWork && !task.IsFaulted && !task.IsCanceled)
|
||||
if (moreWork && !task.IsFaulted)
|
||||
{
|
||||
PumpImpl(taskCompletionSource, subscription);
|
||||
}
|
||||
@@ -295,7 +295,10 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
|
||||
Trace.TraceEvent(TraceEventType.Verbose, 0, "Dispoing the broker");
|
||||
|
||||
if (MonoUtility.IsRunningMono)
|
||||
//Check if OS is not Windows and exit
|
||||
var platform = (int)Environment.OSVersion.Platform;
|
||||
|
||||
if ((platform == 4) || (platform == 6) || (platform == 128))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
private static readonly List<ArraySegment<Message>> _emptyList = new List<ArraySegment<Message>>();
|
||||
public readonly static MessageResult TerminalMessage = new MessageResult(terminal: true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="T:IList{Message}"/> associated with the result.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an optimization to avoid allocations.")]
|
||||
public IList<ArraySegment<Message>> Messages { get; private set; }
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
{
|
||||
public class ScaleoutSubscription : Subscription
|
||||
{
|
||||
private const string _scaleoutCursorPrefix = "s-";
|
||||
|
||||
private readonly IList<ScaleoutMappingStore> _streams;
|
||||
private readonly List<Cursor> _cursors;
|
||||
|
||||
@@ -42,15 +40,10 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
}
|
||||
else
|
||||
{
|
||||
cursors = Cursor.GetCursors(cursor, _scaleoutCursorPrefix);
|
||||
cursors = Cursor.GetCursors(cursor);
|
||||
|
||||
// If the cursor had a default prefix, "d-", cursors might be null
|
||||
if (cursors == null)
|
||||
{
|
||||
cursors = new List<Cursor>();
|
||||
}
|
||||
// If the streams don't match the cursors then throw it out
|
||||
else if (cursors.Count != _streams.Count)
|
||||
if (cursors.Count != _streams.Count)
|
||||
{
|
||||
cursors.Clear();
|
||||
}
|
||||
@@ -70,7 +63,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
|
||||
public override void WriteCursor(TextWriter textWriter)
|
||||
{
|
||||
Cursor.WriteCursors(textWriter, _cursors, _scaleoutCursorPrefix);
|
||||
Cursor.WriteCursors(textWriter, _cursors);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "The list needs to be populated")]
|
||||
|
||||
@@ -120,7 +120,13 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
|
||||
WorkImpl(tcs);
|
||||
|
||||
return tcs.Task;
|
||||
// Fast Path
|
||||
if (tcs.Task.IsCompleted)
|
||||
{
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
return FinishAsync(tcs);
|
||||
}
|
||||
|
||||
public bool SetQueued()
|
||||
@@ -134,6 +140,19 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
return Interlocked.CompareExchange(ref _state, State.Idle, State.Working) != State.Working;
|
||||
}
|
||||
|
||||
private static Task FinishAsync(TaskCompletionSource<object> tcs)
|
||||
{
|
||||
return tcs.Task.ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
return TaskAsyncHelper.FromError(task.Exception);
|
||||
}
|
||||
|
||||
return TaskAsyncHelper.Empty;
|
||||
}).FastUnwrap();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "We have a sync and async code path.")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to avoid user code taking the process down.")]
|
||||
private void WorkImpl(TaskCompletionSource<object> taskCompletionSource)
|
||||
@@ -181,14 +200,7 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.InnerException is TaskCanceledException)
|
||||
{
|
||||
taskCompletionSource.TrySetCanceled();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetUnwrappedException(ex);
|
||||
}
|
||||
taskCompletionSource.TrySetUnwrappedException(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -221,10 +233,6 @@ namespace Microsoft.AspNet.SignalR.Messaging
|
||||
{
|
||||
taskCompletionSource.TrySetUnwrappedException(task.Exception);
|
||||
}
|
||||
else if (task.IsCanceled)
|
||||
{
|
||||
taskCompletionSource.TrySetCanceled();
|
||||
}
|
||||
else if (task.Result)
|
||||
{
|
||||
WorkImpl(taskCompletionSource);
|
||||
|
||||
@@ -71,11 +71,9 @@
|
||||
<Compile Include="Infrastructure\AckHandler.cs" />
|
||||
<Compile Include="Configuration\DefaultConfigurationManager.cs" />
|
||||
<Compile Include="Infrastructure\ArraySegmentTextReader.cs" />
|
||||
<Compile Include="Infrastructure\BinaryTextWriter.cs" />
|
||||
<Compile Include="Infrastructure\ConnectionManager.cs" />
|
||||
<Compile Include="ConnectionMessage.cs" />
|
||||
<Compile Include="Infrastructure\DefaultProtectedData.cs" />
|
||||
<Compile Include="Infrastructure\MonoUtility.cs" />
|
||||
<Compile Include="Infrastructure\DiffPair.cs" />
|
||||
<Compile Include="Infrastructure\DiffSet.cs" />
|
||||
<Compile Include="GlobalHost.cs" />
|
||||
@@ -282,4 +280,4 @@
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -6,7 +6,6 @@ using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.SignalR.Configuration;
|
||||
using Microsoft.AspNet.SignalR.Hosting;
|
||||
@@ -166,7 +165,7 @@ namespace Microsoft.AspNet.SignalR
|
||||
|
||||
if (Transport == null)
|
||||
{
|
||||
return FailResponse(context.Response, String.Format(CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorUnknownTransport));
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorUnknownTransport));
|
||||
}
|
||||
|
||||
string connectionToken = context.Request.QueryString["connectionToken"];
|
||||
@@ -174,17 +173,10 @@ namespace Microsoft.AspNet.SignalR
|
||||
// If there's no connection id then this is a bad request
|
||||
if (String.IsNullOrEmpty(connectionToken))
|
||||
{
|
||||
return FailResponse(context.Response, String.Format(CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorMissingConnectionToken));
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorMissingConnectionToken));
|
||||
}
|
||||
|
||||
string connectionId;
|
||||
string message;
|
||||
int statusCode;
|
||||
|
||||
if (!TryGetConnectionId(context, connectionToken, out connectionId, out message, out statusCode))
|
||||
{
|
||||
return FailResponse(context.Response, message, statusCode);
|
||||
}
|
||||
string connectionId = GetConnectionId(context, connectionToken);
|
||||
|
||||
// Set the transport's connection id to the unprotected one
|
||||
Transport.ConnectionId = connectionId;
|
||||
@@ -235,21 +227,10 @@ namespace Microsoft.AspNet.SignalR
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to catch any exception when unprotecting data.")]
|
||||
internal bool TryGetConnectionId(HostContext context,
|
||||
string connectionToken,
|
||||
out string connectionId,
|
||||
out string message,
|
||||
out int statusCode)
|
||||
internal string GetConnectionId(HostContext context, string connectionToken)
|
||||
{
|
||||
string unprotectedConnectionToken = null;
|
||||
|
||||
// connectionId is only valid when this method returns true
|
||||
connectionId = null;
|
||||
|
||||
// message and statusCode are only valid when this method returns false
|
||||
message = null;
|
||||
statusCode = 400;
|
||||
|
||||
try
|
||||
{
|
||||
unprotectedConnectionToken = ProtectedData.Unprotect(connectionToken, Purposes.ConnectionToken);
|
||||
@@ -261,24 +242,21 @@ namespace Microsoft.AspNet.SignalR
|
||||
|
||||
if (String.IsNullOrEmpty(unprotectedConnectionToken))
|
||||
{
|
||||
message = String.Format(CultureInfo.CurrentCulture, Resources.Error_ConnectionIdIncorrectFormat);
|
||||
return false;
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_ConnectionIdIncorrectFormat));
|
||||
}
|
||||
|
||||
var tokens = unprotectedConnectionToken.Split(SplitChars, 2);
|
||||
|
||||
connectionId = tokens[0];
|
||||
string connectionId = tokens[0];
|
||||
string tokenUserName = tokens.Length > 1 ? tokens[1] : String.Empty;
|
||||
string userName = GetUserIdentity(context);
|
||||
|
||||
if (!String.Equals(tokenUserName, userName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
message = String.Format(CultureInfo.CurrentCulture, Resources.Error_UnrecognizedUserIdentity);
|
||||
statusCode = 403;
|
||||
return false;
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_UnrecognizedUserIdentity));
|
||||
}
|
||||
|
||||
return true;
|
||||
return connectionId;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to prevent any failures in unprotecting")]
|
||||
@@ -499,12 +477,6 @@ namespace Microsoft.AspNet.SignalR
|
||||
return context.Response.End(data);
|
||||
}
|
||||
|
||||
private static Task FailResponse(IResponse response, string message, int statusCode = 400)
|
||||
{
|
||||
response.StatusCode = statusCode;
|
||||
return response.End(message);
|
||||
}
|
||||
|
||||
private static bool IsNegotiationRequest(IRequest request)
|
||||
{
|
||||
return request.Url.LocalPath.EndsWith("/negotiate", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* ASP.NET SignalR JavaScript Library v1.2.2
|
||||
* ASP.NET SignalR JavaScript Library v1.1.3
|
||||
* http://signalr.net/
|
||||
*
|
||||
* Copyright Microsoft Open Technologies, Inc. All rights reserved.
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
/// <reference path="..\..\SignalR.Client.JS\Scripts\jquery-1.6.4.js" />
|
||||
/// <reference path="jquery.signalR.js" />
|
||||
(function ($, window, undefined) {
|
||||
(function ($, window) {
|
||||
/// <param name="$" type="jQuery" />
|
||||
"use strict";
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.SignalR.Infrastructure;
|
||||
@@ -163,7 +159,7 @@ namespace Microsoft.AspNet.SignalR
|
||||
{
|
||||
// observe Exception
|
||||
#if !WINDOWS_PHONE && !SILVERLIGHT && !NETFX_CORE
|
||||
Trace.TraceWarning("SignalR exception thrown by Task: {0}", exception);
|
||||
Trace.TraceError("SignalR exception thrown by Task: {0}", exception);
|
||||
#endif
|
||||
handler(exception, state);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions are flowed to the caller.")]
|
||||
protected Task ProcessReceiveRequest(ITransportConnection connection)
|
||||
private Task ProcessReceiveRequest(ITransportConnection connection)
|
||||
{
|
||||
Func<Task> initialize = null;
|
||||
|
||||
@@ -273,7 +273,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
{
|
||||
var context = (MessageContext)state;
|
||||
|
||||
response.Reconnect = context.Transport.HostShutdownToken.IsCancellationRequested;
|
||||
response.TimedOut = context.Transport.IsTimedOut;
|
||||
|
||||
// If we're telling the client to disconnect then clean up the instantiated connection.
|
||||
if (response.Disconnect)
|
||||
@@ -282,7 +282,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
return context.Transport.Send(response).Then(c => OnDisconnectMessage(c), context)
|
||||
.Then(() => TaskAsyncHelper.False);
|
||||
}
|
||||
else if (context.Transport.IsTimedOut || response.Aborted)
|
||||
else if (response.TimedOut || response.Aborted)
|
||||
{
|
||||
context.Registration.Dispose();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.SignalR.Hosting;
|
||||
using Microsoft.AspNet.SignalR.Infrastructure;
|
||||
@@ -253,7 +252,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
{
|
||||
var context = (MessageContext)state;
|
||||
|
||||
response.Reconnect = context.Transport.HostShutdownToken.IsCancellationRequested;
|
||||
response.TimedOut = context.Transport.IsTimedOut;
|
||||
|
||||
Task task = TaskAsyncHelper.Empty;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
private readonly Action<TextWriter> _writeCursor;
|
||||
|
||||
public PersistentResponse()
|
||||
: this(message => false, writer => { })
|
||||
: this(message => true, writer => { })
|
||||
{
|
||||
|
||||
}
|
||||
@@ -61,10 +61,9 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
public bool Aborted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the client should try reconnecting.
|
||||
/// True if the connection timed out.
|
||||
/// </summary>
|
||||
// This is set when the host is shutting down.
|
||||
public bool Reconnect { get; set; }
|
||||
public bool TimedOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signed token representing the list of groups. Updates on change.
|
||||
@@ -107,7 +106,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
jsonWriter.WriteValue(1);
|
||||
}
|
||||
|
||||
if (Reconnect)
|
||||
if (TimedOut)
|
||||
{
|
||||
jsonWriter.WritePropertyName("T");
|
||||
jsonWriter.WriteValue(1);
|
||||
|
||||
@@ -130,14 +130,6 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
}
|
||||
}
|
||||
|
||||
protected CancellationToken HostShutdownToken
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hostShutdownToken;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTimedOut
|
||||
{
|
||||
get
|
||||
@@ -194,7 +186,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
|
||||
protected virtual TextWriter CreateResponseWriter()
|
||||
{
|
||||
return new BinaryTextWriter(Context.Response);
|
||||
return new BufferTextWriter(Context.Response);
|
||||
}
|
||||
|
||||
protected void IncrementErrors()
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
private bool _isAlive = true;
|
||||
|
||||
private readonly Action<string> _message;
|
||||
private readonly Action _closed;
|
||||
private readonly Action<bool> _closed;
|
||||
private readonly Action<Exception> _error;
|
||||
|
||||
public WebSocketTransport(HostContext context,
|
||||
@@ -74,39 +74,28 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
|
||||
public override Task ProcessRequest(ITransportConnection connection)
|
||||
{
|
||||
if (IsAbortRequest)
|
||||
var webSocketRequest = _context.Request as IWebSocketRequest;
|
||||
|
||||
// Throw if the server implementation doesn't support websockets
|
||||
if (webSocketRequest == null)
|
||||
{
|
||||
return connection.Abort(ConnectionId);
|
||||
throw new InvalidOperationException(Resources.Error_WebSocketsNotSupported);
|
||||
}
|
||||
else
|
||||
|
||||
return webSocketRequest.AcceptWebSocketRequest(socket =>
|
||||
{
|
||||
var webSocketRequest = _context.Request as IWebSocketRequest;
|
||||
_socket = socket;
|
||||
socket.OnClose = _closed;
|
||||
socket.OnMessage = _message;
|
||||
socket.OnError = _error;
|
||||
|
||||
// Throw if the server implementation doesn't support websockets
|
||||
if (webSocketRequest == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.Error_WebSocketsNotSupported);
|
||||
}
|
||||
|
||||
Connection = connection;
|
||||
InitializePersistentState();
|
||||
|
||||
return webSocketRequest.AcceptWebSocketRequest(socket =>
|
||||
{
|
||||
_socket = socket;
|
||||
socket.OnClose = _closed;
|
||||
socket.OnMessage = _message;
|
||||
socket.OnError = _error;
|
||||
|
||||
return ProcessReceiveRequest(connection);
|
||||
},
|
||||
InitializeTcs.Task);
|
||||
}
|
||||
return ProcessRequestCore(connection);
|
||||
});
|
||||
}
|
||||
|
||||
protected override TextWriter CreateResponseWriter()
|
||||
{
|
||||
return new BinaryTextWriter(_socket);
|
||||
return new BufferTextWriter(_socket);
|
||||
}
|
||||
|
||||
public override Task Send(object value)
|
||||
@@ -124,11 +113,6 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
return Send((object)response);
|
||||
}
|
||||
|
||||
protected internal override Task InitializeResponse(ITransportConnection connection)
|
||||
{
|
||||
return _socket.Send("{}");
|
||||
}
|
||||
|
||||
private static Task PerformSend(object state)
|
||||
{
|
||||
var context = (WebSocketTransportContext)state;
|
||||
@@ -147,11 +131,18 @@ namespace Microsoft.AspNet.SignalR.Transports
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
private void OnClosed(bool clean)
|
||||
{
|
||||
Trace.TraceInformation("CloseSocket({0})", ConnectionId);
|
||||
Trace.TraceInformation("CloseSocket({0}, {1})", clean, ConnectionId);
|
||||
|
||||
// If we performed a clean disconnect then we go through the normal disconnect routine. However,
|
||||
// If we performed an unclean disconnect we want to mark the connection as "not alive" and let the
|
||||
// HeartBeat clean it up. This is to maintain consistency across the transports.
|
||||
if (clean)
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
|
||||
// Require a request to /abort to stop tracking the connection. #2195
|
||||
_isAlive = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.SignalR.Hosting;
|
||||
using Microsoft.AspNet.SignalR.Owin.Infrastructure;
|
||||
@@ -66,19 +65,9 @@ namespace Microsoft.AspNet.SignalR.Owin
|
||||
|
||||
if (!_connection.Authorize(serverRequest))
|
||||
{
|
||||
IPrincipal user = hostContext.Request.User;
|
||||
if (user != null && user.Identity.IsAuthenticated)
|
||||
{
|
||||
// If we failed to authorize the request then return a 403 since the request
|
||||
// can't do anything
|
||||
return EndResponse(environment, 403, "Forbidden");
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we failed to authorize the request and the user is not authenticated
|
||||
// then return a 401
|
||||
return EndResponse(environment, 401, "Unauthorized");
|
||||
}
|
||||
// If we failed to authorize the request then return a 403 since the request
|
||||
// can't do anything
|
||||
return EndResponse(environment, 403, "Forbidden");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.SignalR.Owin.Infrastructure;
|
||||
using Microsoft.AspNet.SignalR.Hosting;
|
||||
|
||||
namespace Microsoft.AspNet.SignalR.Owin
|
||||
{
|
||||
@@ -139,17 +138,15 @@ namespace Microsoft.AspNet.SignalR.Owin
|
||||
}
|
||||
|
||||
#if NET45
|
||||
public Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback, Task initTask)
|
||||
public Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
|
||||
{
|
||||
var accept = _environment.Get<Action<IDictionary<string, object>, WebSocketFunc>>(OwinConstants.WebSocketAccept);
|
||||
if (accept == null)
|
||||
{
|
||||
var response = new ServerResponse(_environment);
|
||||
response.StatusCode = 400;
|
||||
return response.End(Resources.Error_NotWebSocketRequest);
|
||||
throw new InvalidOperationException(Resources.Error_NotWebSocketRequest);
|
||||
}
|
||||
|
||||
var handler = new OwinWebSocketHandler(callback, initTask);
|
||||
var handler = new OwinWebSocketHandler(callback);
|
||||
accept(null, handler.ProcessRequestAsync);
|
||||
return TaskAsyncHelper.Empty;
|
||||
}
|
||||
|
||||
@@ -27,18 +27,6 @@ namespace Microsoft.AspNet.SignalR.Owin
|
||||
get { return _callCancelled; }
|
||||
}
|
||||
|
||||
public int StatusCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _environment.Get<int>(OwinConstants.ResponseStatusCode);
|
||||
}
|
||||
set
|
||||
{
|
||||
_environment[OwinConstants.ResponseStatusCode] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get { return ResponseHeaders.GetHeader("Content-Type"); }
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Basic;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Bootstrapper;
|
||||
using Nancy.Cryptography;
|
||||
using NzbDrone.Api.Extensions.Pipelines;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
public class EnableAuthInNancy : IRegisterNancyPipeline
|
||||
{
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public EnableAuthInNancy(IAuthenticationService authenticationService,
|
||||
IConfigService configService,
|
||||
IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_authenticationService = authenticationService;
|
||||
_configService = configService;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
RegisterFormsAuth(pipelines);
|
||||
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
|
||||
pipelines.BeforeRequest.AddItemToEndOfPipeline(RequiresAuthentication);
|
||||
}
|
||||
|
||||
private Response RequiresAuthentication(NancyContext context)
|
||||
{
|
||||
Response response = null;
|
||||
|
||||
if (!_authenticationService.IsAuthenticated(context))
|
||||
{
|
||||
response = new Response { StatusCode = HttpStatusCode.Unauthorized };
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void RegisterFormsAuth(IPipelines pipelines)
|
||||
{
|
||||
var cryptographyConfiguration = new CryptographyConfiguration(
|
||||
new RijndaelEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase,
|
||||
new byte[] {1, 2, 3, 4, 5, 6, 7, 8})),
|
||||
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase,
|
||||
new byte[] {1, 2, 3, 4, 5, 6, 7, 8}))
|
||||
);
|
||||
|
||||
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
|
||||
{
|
||||
RedirectUrl = "~/login",
|
||||
UserMapper = _authenticationService,
|
||||
CryptographyConfiguration = cryptographyConfiguration
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.ModelBinding;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
public class AuthenticationModule : NancyModule
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public AuthenticationModule(IUserService userService, IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_userService = userService;
|
||||
_configFileProvider = configFileProvider;
|
||||
Post["/login"] = x => Login(this.Bind<LoginResource>());
|
||||
Get["/logout"] = x => Logout();
|
||||
}
|
||||
|
||||
private Response Login(LoginResource resource)
|
||||
{
|
||||
var user = _userService.FindUser(resource.Username, resource.Password);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return Context.GetRedirect("~/login?returnUrl=" + (string)Request.Query.returnUrl);
|
||||
}
|
||||
|
||||
DateTime? expiry = null;
|
||||
|
||||
if (resource.RememberMe)
|
||||
{
|
||||
expiry = DateTime.UtcNow.AddDays(7);
|
||||
}
|
||||
|
||||
return this.LoginAndRedirect(user.Identifier, expiry);
|
||||
}
|
||||
|
||||
private Response Logout()
|
||||
{
|
||||
return this.LogoutAndRedirect(_configFileProvider.UrlBase + "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,14 @@
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Basic;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Security;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
public interface IAuthenticationService : IUserValidator, IUserMapper
|
||||
public interface IAuthenticationService : IUserValidator
|
||||
{
|
||||
bool IsAuthenticated(NancyContext context);
|
||||
}
|
||||
@@ -19,52 +17,37 @@ namespace NzbDrone.Api.Authentication
|
||||
public class AuthenticationService : IAuthenticationService
|
||||
{
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IUserService _userService;
|
||||
private static readonly NzbDroneUser AnonymousUser = new NzbDroneUser { UserName = "Anonymous" };
|
||||
|
||||
private static String API_KEY;
|
||||
private static AuthenticationType AUTH_METHOD;
|
||||
|
||||
public AuthenticationService(IConfigFileProvider configFileProvider, IUserService userService)
|
||||
public AuthenticationService(IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_configFileProvider = configFileProvider;
|
||||
_userService = userService;
|
||||
API_KEY = configFileProvider.ApiKey;
|
||||
AUTH_METHOD = configFileProvider.AuthenticationMethod;
|
||||
}
|
||||
|
||||
public IUserIdentity Validate(string username, string password)
|
||||
{
|
||||
if (AUTH_METHOD == AuthenticationType.None)
|
||||
if (!Enabled)
|
||||
{
|
||||
return AnonymousUser;
|
||||
}
|
||||
|
||||
var user = _userService.FindUser(username, password);
|
||||
|
||||
if (user != null)
|
||||
if (_configFileProvider.Username.Equals(username) &&
|
||||
_configFileProvider.Password.Equals(password))
|
||||
{
|
||||
return new NzbDroneUser { UserName = user.Username };
|
||||
return new NzbDroneUser { UserName = username };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
|
||||
private bool Enabled
|
||||
{
|
||||
if (AUTH_METHOD == AuthenticationType.None)
|
||||
get
|
||||
{
|
||||
return AnonymousUser;
|
||||
return _configFileProvider.AuthenticationEnabled;
|
||||
}
|
||||
|
||||
var user = _userService.FindUser(identifier);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
return new NzbDroneUser { UserName = user.Username };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsAuthenticated(NancyContext context)
|
||||
@@ -76,13 +59,13 @@ namespace NzbDrone.Api.Authentication
|
||||
return ValidApiKey(apiKey);
|
||||
}
|
||||
|
||||
if (AUTH_METHOD == AuthenticationType.None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (context.Request.IsFeedRequest())
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ValidUser(context) || ValidApiKey(apiKey))
|
||||
{
|
||||
return true;
|
||||
@@ -91,12 +74,7 @@ namespace NzbDrone.Api.Authentication
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.Request.IsLoginRequest())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (context.Request.IsContentRequest())
|
||||
if (!Enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,46 +1,23 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Nancy;
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Basic;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Bootstrapper;
|
||||
using Nancy.Cryptography;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.Extensions.Pipelines;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
public class EnableAuthInNancy : IRegisterNancyPipeline
|
||||
{
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public EnableAuthInNancy(IAuthenticationService authenticationService,
|
||||
IConfigService configService,
|
||||
IConfigFileProvider configFileProvider)
|
||||
public EnableAuthInNancy(IAuthenticationService authenticationService)
|
||||
{
|
||||
_authenticationService = authenticationService;
|
||||
_configService = configService;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
if (_configFileProvider.AuthenticationMethod == AuthenticationType.Forms)
|
||||
{
|
||||
RegisterFormsAuth(pipelines);
|
||||
}
|
||||
|
||||
else if (_configFileProvider.AuthenticationMethod == AuthenticationType.Basic)
|
||||
{
|
||||
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
|
||||
}
|
||||
|
||||
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
|
||||
pipelines.BeforeRequest.AddItemToEndOfPipeline(RequiresAuthentication);
|
||||
pipelines.AfterRequest.AddItemToEndOfPipeline(RemoveLoginHooksForApiCalls);
|
||||
}
|
||||
|
||||
private Response RequiresAuthentication(NancyContext context)
|
||||
@@ -54,33 +31,5 @@ namespace NzbDrone.Api.Authentication
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void RegisterFormsAuth(IPipelines pipelines)
|
||||
{
|
||||
var cryptographyConfiguration = new CryptographyConfiguration(
|
||||
new RijndaelEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase, Encoding.ASCII.GetBytes(_configService.RijndaelSalt))),
|
||||
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase, Encoding.ASCII.GetBytes(_configService.HmacSalt)))
|
||||
);
|
||||
|
||||
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
|
||||
{
|
||||
RedirectUrl = _configFileProvider.UrlBase + "/login",
|
||||
UserMapper = _authenticationService,
|
||||
CryptographyConfiguration = cryptographyConfiguration
|
||||
});
|
||||
}
|
||||
|
||||
private void RemoveLoginHooksForApiCalls(NancyContext context)
|
||||
{
|
||||
if (context.Request.IsApiRequest())
|
||||
{
|
||||
if ((context.Response.StatusCode == HttpStatusCode.SeeOther &&
|
||||
context.Response.ContentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase)) ||
|
||||
context.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
context.Response = new { Error = "Unauthorized" }.AsResponse(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
public class LoginResource
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
using System.Reflection;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Update;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -15,13 +13,11 @@ namespace NzbDrone.Api.Config
|
||||
public class HostConfigModule : NzbDroneRestModule<HostConfigResource>
|
||||
{
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public HostConfigModule(IConfigFileProvider configFileProvider, IUserService userService)
|
||||
public HostConfigModule(IConfigFileProvider configFileProvider)
|
||||
: base("/config/host")
|
||||
{
|
||||
_configFileProvider = configFileProvider;
|
||||
_userService = userService;
|
||||
|
||||
GetResourceSingle = GetHostConfig;
|
||||
GetResourceById = GetHostConfig;
|
||||
@@ -30,8 +26,8 @@ namespace NzbDrone.Api.Config
|
||||
SharedValidator.RuleFor(c => c.Branch).NotEmpty().WithMessage("Branch name is required, 'master' is the default");
|
||||
SharedValidator.RuleFor(c => c.Port).ValidPort();
|
||||
|
||||
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationMethod != AuthenticationType.None);
|
||||
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationMethod != AuthenticationType.None);
|
||||
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationEnabled);
|
||||
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationEnabled);
|
||||
|
||||
SharedValidator.RuleFor(c => c.SslPort).ValidPort().When(c => c.EnableSsl);
|
||||
SharedValidator.RuleFor(c => c.SslCertHash).NotEmpty().When(c => c.EnableSsl && OsInfo.IsWindows);
|
||||
@@ -50,14 +46,6 @@ namespace NzbDrone.Api.Config
|
||||
resource.InjectFrom(_configFileProvider);
|
||||
resource.Id = 1;
|
||||
|
||||
var user = _userService.FindUser();
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
resource.Username = user.Username;
|
||||
resource.Password = user.Password;
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
@@ -73,11 +61,6 @@ namespace NzbDrone.Api.Config
|
||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
||||
|
||||
_configFileProvider.SaveConfigDictionary(dictionary);
|
||||
|
||||
if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_userService.Upsert(resource.Username, resource.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Api.Config
|
||||
@@ -12,7 +11,7 @@ namespace NzbDrone.Api.Config
|
||||
public Int32 SslPort { get; set; }
|
||||
public Boolean EnableSsl { get; set; }
|
||||
public Boolean LaunchBrowser { get; set; }
|
||||
public AuthenticationType AuthenticationMethod { get; set; }
|
||||
public bool AuthenticationEnabled { get; set; }
|
||||
public Boolean AnalyticsEnabled { get; set; }
|
||||
public String Username { get; set; }
|
||||
public String Password { get; set; }
|
||||
|
||||
@@ -9,10 +9,10 @@ namespace NzbDrone.Api.DownloadClient
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
|
||||
protected override void Validate(DownloadClientDefinition definition)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
base.Validate(definition, includeWarnings);
|
||||
base.Validate(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,5 @@ namespace NzbDrone.Api.Extensions
|
||||
request.UserHostAddress.Equals("127.0.0.1") ||
|
||||
request.UserHostAddress.Equals("::1"));
|
||||
}
|
||||
|
||||
public static bool IsLoginRequest(this Request request)
|
||||
{
|
||||
return request.Path.Equals("/login", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsContentRequest(this Request request)
|
||||
{
|
||||
return request.Path.StartsWith("/Content/", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace NzbDrone.Api.Frontend.Mappers
|
||||
var pathToFile = mapper.Map(resourceUrl);
|
||||
var hash = _hashProvider.ComputeMd5(pathToFile).ToBase64();
|
||||
|
||||
return resourceUrl + "?h=" + hash.Trim('=');
|
||||
return resourceUrl + "?h=" + hash;
|
||||
}
|
||||
|
||||
private static bool ShouldBreakCache(string path)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -18,14 +17,7 @@ namespace NzbDrone.Api.Frontend.Mappers
|
||||
|
||||
public override string Map(string resourceUrl)
|
||||
{
|
||||
var fileName = "favicon.ico";
|
||||
|
||||
if (BuildInfo.IsDebug)
|
||||
{
|
||||
fileName = "favicon-debug.ico";
|
||||
}
|
||||
|
||||
var path = Path.Combine("Content", "Images", fileName);
|
||||
var path = Path.Combine("Content", "Images", "favicon.ico");
|
||||
|
||||
return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.Api.Frontend.Mappers
|
||||
private readonly IAnalyticsService _analyticsService;
|
||||
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
|
||||
private readonly string _indexPath;
|
||||
private static readonly Regex ReplaceRegex = new Regex("(?<=(?:href|src)=\").*?(css|js)(?=\")", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex ReplaceRegex = new Regex("(?<=(?:href|src|data-main)=\").*?(?=\")", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static String API_KEY;
|
||||
private static String URL_BASE;
|
||||
@@ -49,7 +49,7 @@ namespace NzbDrone.Api.Frontend.Mappers
|
||||
|
||||
public override bool CanHandle(string resourceUrl)
|
||||
{
|
||||
return !resourceUrl.Contains(".") && !resourceUrl.StartsWith("/login");
|
||||
return !resourceUrl.Contains(".");
|
||||
}
|
||||
|
||||
public override Response GetResponse(string resourceUrl)
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Nancy;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Frontend.Mappers
|
||||
{
|
||||
public class LoginHtmlMapper : StaticResourceMapperBase
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
|
||||
private readonly string _indexPath;
|
||||
private static readonly Regex ReplaceRegex = new Regex("(?<=(?:href|src|data-main)=\").*?(?=\")", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static String URL_BASE;
|
||||
private string _generatedContent;
|
||||
|
||||
public LoginHtmlMapper(IAppFolderInfo appFolderInfo,
|
||||
IDiskProvider diskProvider,
|
||||
IConfigFileProvider configFileProvider,
|
||||
Func<ICacheBreakerProvider> cacheBreakProviderFactory,
|
||||
Logger logger)
|
||||
: base(diskProvider, logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_cacheBreakProviderFactory = cacheBreakProviderFactory;
|
||||
_indexPath = Path.Combine(appFolderInfo.StartUpFolder, "UI", "login.html");
|
||||
|
||||
URL_BASE = configFileProvider.UrlBase;
|
||||
}
|
||||
|
||||
public override string Map(string resourceUrl)
|
||||
{
|
||||
return _indexPath;
|
||||
}
|
||||
|
||||
public override bool CanHandle(string resourceUrl)
|
||||
{
|
||||
return resourceUrl.StartsWith("/login");
|
||||
}
|
||||
|
||||
public override Response GetResponse(string resourceUrl)
|
||||
{
|
||||
var response = base.GetResponse(resourceUrl);
|
||||
response.Headers["X-UA-Compatible"] = "IE=edge";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected override Stream GetContentStream(string filePath)
|
||||
{
|
||||
var text = GetLoginText();
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.Write(text);
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
|
||||
private string GetLoginText()
|
||||
{
|
||||
if (RuntimeInfoBase.IsProduction && _generatedContent != null)
|
||||
{
|
||||
return _generatedContent;
|
||||
}
|
||||
|
||||
var text = _diskProvider.ReadAllText(_indexPath);
|
||||
|
||||
var cacheBreakProvider = _cacheBreakProviderFactory();
|
||||
|
||||
text = ReplaceRegex.Replace(text, match =>
|
||||
{
|
||||
var url = cacheBreakProvider.AddCacheBreakerToPath(match.Value);
|
||||
return URL_BASE + url;
|
||||
});
|
||||
|
||||
_generatedContent = text;
|
||||
|
||||
return _generatedContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ namespace NzbDrone.Api.Frontend.Mappers
|
||||
{
|
||||
return resourceUrl.StartsWith("/Content") ||
|
||||
resourceUrl.EndsWith(".js") ||
|
||||
resourceUrl.EndsWith(".map") ||
|
||||
resourceUrl.EndsWith(".css") ||
|
||||
(resourceUrl.EndsWith(".ico") && !resourceUrl.Equals("/favicon.ico")) ||
|
||||
resourceUrl.EndsWith(".swf");
|
||||
|
||||
@@ -9,10 +9,10 @@ namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
|
||||
protected override void Validate(IndexerDefinition definition)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
base.Validate(definition, includeWarnings);
|
||||
base.Validate(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ namespace NzbDrone.Api.Metadata
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(MetadataDefinition definition, bool includeWarnings)
|
||||
protected override void Validate(MetadataDefinition definition)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
base.Validate(definition, includeWarnings);
|
||||
base.Validate(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ namespace NzbDrone.Api.Notifications
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(NotificationDefinition definition, bool includeWarnings)
|
||||
protected override void Validate(NotificationDefinition definition)
|
||||
{
|
||||
if (!definition.OnGrab && !definition.OnDownload) return;
|
||||
base.Validate(definition, includeWarnings);
|
||||
base.Validate(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,6 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Nancy.Authentication.Basic.0.23.2\lib\net40\Nancy.Authentication.Basic.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Nancy.Authentication.Forms">
|
||||
<HintPath>..\packages\Nancy.Authentication.Forms.0.23.2\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
@@ -83,8 +80,6 @@
|
||||
</Compile>
|
||||
<Compile Include="Authentication\AuthenticationService.cs" />
|
||||
<Compile Include="Authentication\EnableAuthInNancy.cs" />
|
||||
<Compile Include="Authentication\AuthenticationModule.cs" />
|
||||
<Compile Include="Authentication\LoginResource.cs" />
|
||||
<Compile Include="Authentication\NzbDroneUser.cs" />
|
||||
<Compile Include="Blacklist\BlacklistModule.cs" />
|
||||
<Compile Include="Blacklist\BlacklistResource.cs" />
|
||||
@@ -99,7 +94,6 @@
|
||||
<Compile Include="Commands\CommandResource.cs" />
|
||||
<Compile Include="Extensions\AccessControlHeaders.cs" />
|
||||
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
|
||||
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
|
||||
<Compile Include="Profiles\Delay\DelayProfileModule.cs" />
|
||||
<Compile Include="Profiles\Delay\DelayProfileResource.cs" />
|
||||
<Compile Include="Profiles\Delay\DelayProfileValidator.cs" />
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
namespace NzbDrone.Api
|
||||
@@ -74,9 +72,12 @@ namespace NzbDrone.Api
|
||||
|
||||
private int CreateProvider(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, false);
|
||||
var providerDefinition = GetDefinition(providerResource);
|
||||
|
||||
Test(providerDefinition, false);
|
||||
if (providerDefinition.Enable)
|
||||
{
|
||||
Test(providerDefinition);
|
||||
}
|
||||
|
||||
providerDefinition = _providerFactory.Create(providerDefinition);
|
||||
|
||||
@@ -85,14 +86,12 @@ namespace NzbDrone.Api
|
||||
|
||||
private void UpdateProvider(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, false);
|
||||
|
||||
Test(providerDefinition, false);
|
||||
var providerDefinition = GetDefinition(providerResource);
|
||||
|
||||
_providerFactory.Update(providerDefinition);
|
||||
}
|
||||
|
||||
private TProviderDefinition GetDefinition(TProviderResource providerResource, bool includeWarnings = false)
|
||||
private TProviderDefinition GetDefinition(TProviderResource providerResource)
|
||||
{
|
||||
var definition = new TProviderDefinition();
|
||||
|
||||
@@ -106,7 +105,7 @@ namespace NzbDrone.Api
|
||||
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
|
||||
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract, preset);
|
||||
|
||||
Validate(definition, includeWarnings);
|
||||
Validate(definition);
|
||||
|
||||
return definition;
|
||||
}
|
||||
@@ -150,42 +149,31 @@ namespace NzbDrone.Api
|
||||
|
||||
private Response Test(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, true);
|
||||
var providerDefinition = GetDefinition(providerResource);
|
||||
|
||||
Test(providerDefinition, true);
|
||||
Test(providerDefinition);
|
||||
|
||||
return "{}";
|
||||
}
|
||||
|
||||
protected virtual void Validate(TProviderDefinition definition, bool includeWarnings)
|
||||
private void Test(TProviderDefinition providerDefinition)
|
||||
{
|
||||
var validationResult = definition.Settings.Validate();
|
||||
|
||||
VerifyValidationResult(validationResult, includeWarnings);
|
||||
}
|
||||
|
||||
protected virtual void Test(TProviderDefinition definition, bool includeWarnings)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
|
||||
var validationResult = _providerFactory.Test(definition);
|
||||
|
||||
VerifyValidationResult(validationResult, includeWarnings);
|
||||
}
|
||||
|
||||
protected void VerifyValidationResult(ValidationResult validationResult, bool includeWarnings)
|
||||
{
|
||||
var result = new NzbDroneValidationResult(validationResult.Errors);
|
||||
|
||||
if (includeWarnings && (!result.IsValid || result.HasWarnings))
|
||||
{
|
||||
throw new ValidationException(result.Failures);
|
||||
}
|
||||
var result = _providerFactory.Test(providerDefinition);
|
||||
|
||||
if (!result.IsValid)
|
||||
{
|
||||
throw new ValidationException(result.Errors);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Validate(TProviderDefinition definition)
|
||||
{
|
||||
var validationResult = definition.Settings.Validate();
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,9 +109,7 @@ namespace NzbDrone.Api.Series
|
||||
|
||||
private int AddSeries(SeriesResource seriesResource)
|
||||
{
|
||||
var series = _seriesService.AddSeries(seriesResource.InjectTo<Core.Tv.Series>());
|
||||
|
||||
return series.Id;
|
||||
return GetNewId<Core.Tv.Series>(_seriesService.AddSeries, seriesResource);
|
||||
}
|
||||
|
||||
private void UpdateSeries(SeriesResource seriesResource)
|
||||
|
||||
@@ -67,7 +67,6 @@ namespace NzbDrone.Api.Series
|
||||
public List<String> Genres { get; set; }
|
||||
public HashSet<Int32> Tags { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public AddSeriesOptions AddOptions { get; set; }
|
||||
|
||||
//Used to support legacy consumers
|
||||
public Int32 QualityProfileId
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace NzbDrone.Api.System
|
||||
IsOsx = OsInfo.IsOsx,
|
||||
IsWindows = OsInfo.IsWindows,
|
||||
Branch = _configFileProvider.Branch,
|
||||
Authentication = _configFileProvider.AuthenticationMethod,
|
||||
Authentication = _configFileProvider.AuthenticationEnabled,
|
||||
SqliteVersion = _database.Version,
|
||||
UrlBase = _configFileProvider.UrlBase,
|
||||
RuntimeVersion = _runtimeInfo.RuntimeVersion
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tags;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace NzbDrone.Api.Tags
|
||||
{
|
||||
public class TagModule : NzbDroneRestModuleWithSignalR<TagResource, Tag>, IHandle<TagsUpdatedEvent>
|
||||
public class TagModule : NzbDroneRestModule<TagResource>
|
||||
{
|
||||
private readonly ITagService _tagService;
|
||||
|
||||
public TagModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
ITagService tagService)
|
||||
: base(signalRBroadcaster)
|
||||
public TagModule(ITagService tagService)
|
||||
{
|
||||
_tagService = tagService;
|
||||
|
||||
@@ -49,10 +44,5 @@ namespace NzbDrone.Api.Tags
|
||||
{
|
||||
_tagService.Delete(id);
|
||||
}
|
||||
|
||||
public void Handle(TagsUpdatedEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,22 +22,19 @@ namespace NzbDrone.Api.Update
|
||||
.OrderByDescending(u => u.Version)
|
||||
.InjectTo<List<UpdateResource>>();
|
||||
|
||||
if (resources.Any())
|
||||
var first = resources.First();
|
||||
first.Latest = true;
|
||||
|
||||
if (first.Version > BuildInfo.Version)
|
||||
{
|
||||
var first = resources.First();
|
||||
first.Latest = true;
|
||||
first.Installable = true;
|
||||
}
|
||||
|
||||
if (first.Version > BuildInfo.Version)
|
||||
{
|
||||
first.Installable = true;
|
||||
}
|
||||
var installed = resources.SingleOrDefault(r => r.Version == BuildInfo.Version);
|
||||
|
||||
var installed = resources.SingleOrDefault(r => r.Version == BuildInfo.Version);
|
||||
|
||||
if (installed != null)
|
||||
{
|
||||
installed.Installed = true;
|
||||
}
|
||||
if (installed != null)
|
||||
{
|
||||
installed.Installed = true;
|
||||
}
|
||||
|
||||
return resources;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<package id="FluentValidation" version="5.5.0.0" 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.Forms" version="0.23.2" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
|
||||
<package id="NLog" version="2.1.0" targetFramework="net40" />
|
||||
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
|
||||
|
||||
@@ -45,8 +45,6 @@ namespace NzbDrone.Automation.Test
|
||||
var page = new PageBase(driver);
|
||||
page.WaitForNoSpinner();
|
||||
|
||||
driver.ExecuteScript("window.NzbDrone.NameViews = true;");
|
||||
|
||||
GetPageErrors().Should().BeEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
@@ -16,8 +12,6 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
public class ConfigFileProviderTest : TestBase<ConfigFileProvider>
|
||||
{
|
||||
private string _configFileContents;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
@@ -25,24 +19,8 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
var configFile = Mocker.Resolve<IAppFolderInfo>().GetConfigPath();
|
||||
|
||||
_configFileContents = null;
|
||||
|
||||
WithMockConfigFile(configFile);
|
||||
}
|
||||
|
||||
protected void WithMockConfigFile(string configFile)
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FileExists(configFile))
|
||||
.Returns<string>(p => _configFileContents != null);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.ReadAllText(configFile))
|
||||
.Returns<string>(p => _configFileContents);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.WriteAllText(configFile, It.IsAny<string>()))
|
||||
.Callback<string, string>((p, t) => _configFileContents = t);
|
||||
if (File.Exists(configFile))
|
||||
File.Delete(configFile);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -148,9 +126,9 @@ namespace NzbDrone.Common.Test
|
||||
[Test]
|
||||
public void GetAuthenticationType_No_Existing_Value()
|
||||
{
|
||||
var result = Subject.AuthenticationMethod;
|
||||
var result = Subject.AuthenticationEnabled;
|
||||
|
||||
result.Should().Be(AuthenticationType.None);
|
||||
result.Should().Be(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -163,28 +141,8 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
Subject.SaveConfigDictionary(dic);
|
||||
|
||||
Subject.Port.Should().Be(port);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveDictionary_should_only_save_specified_values()
|
||||
{
|
||||
int port = 20555;
|
||||
int origSslPort = 20551;
|
||||
int sslPort = 20552;
|
||||
|
||||
var dic = Subject.GetConfigDictionary();
|
||||
dic["Port"] = port;
|
||||
dic["SslPort"] = origSslPort;
|
||||
Subject.SaveConfigDictionary(dic);
|
||||
|
||||
|
||||
dic = new Dictionary<string, object>();
|
||||
dic["SslPort"] = sslPort;
|
||||
Subject.SaveConfigDictionary(dic);
|
||||
|
||||
Subject.Port.Should().Be(port);
|
||||
Subject.SslPort.Should().Be(sslPort);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Common.Test.DiskTests
|
||||
{
|
||||
public abstract class DiskProviderFixtureBase<TSubject> : TestBase<TSubject> where TSubject : class, IDiskProvider
|
||||
public class DiskProviderFixtureBase<TSubject> : TestBase<TSubject> where TSubject : class, IDiskProvider
|
||||
{
|
||||
public DirectoryInfo GetFilledTempFolder()
|
||||
{
|
||||
@@ -46,38 +46,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
Subject.FolderExists(@"C:\ThisBetterNotExist\".AsOsAgnostic()).Should().BeFalse();
|
||||
}
|
||||
|
||||
protected abstract void SetWritePermissions(string path, bool writable);
|
||||
|
||||
[Test]
|
||||
public void FolderWritable_should_return_true_for_writable_directory()
|
||||
{
|
||||
var tempFolder = GetTempFilePath();
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
|
||||
var result = Subject.FolderWritable(tempFolder);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FolderWritable_should_return_false_for_unwritable_directory()
|
||||
{
|
||||
var tempFolder = GetTempFilePath();
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
|
||||
SetWritePermissions(tempFolder, false);
|
||||
try
|
||||
{
|
||||
var result = Subject.FolderWritable(tempFolder);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetWritePermissions(tempFolder, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MoveFile_should_overwrite_existing_file()
|
||||
{
|
||||
@@ -346,15 +314,6 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
Console.WriteLine(GetFilledTempFolder().LastWriteTimeUtc);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetParentFolder_should_remove_trailing_slash_before_getting_parent_folder()
|
||||
{
|
||||
var path = @"C:\Test\TV\".AsOsAgnostic();
|
||||
var parent = @"C:\Test".AsOsAgnostic();
|
||||
|
||||
Subject.GetParentFolder(path).Should().Be(parent);
|
||||
}
|
||||
|
||||
private void VerifyCopy(string source, string destination)
|
||||
{
|
||||
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
|
||||
|
||||
@@ -70,9 +70,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/status/" + (int)statusCode);
|
||||
|
||||
Subject.Get<HttpBinResource>(request);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Assert.Throws<Exception>(() => Subject.Get<HttpBinResource>(request));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -109,25 +109,6 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
}
|
||||
|
||||
public bool FolderWritable(string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
|
||||
try
|
||||
{
|
||||
var testPath = Path.Combine(path, "sonarr_write_test.txt");
|
||||
var testContent = string.Format("This file was created to verify if '{0}' is writable. It should've been automatically deleted. Feel free to delete it.", path);
|
||||
File.WriteAllText(testPath, testContent);
|
||||
File.Delete(testPath);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Trace("Directory '{0}' isn't writable. {1}", path, e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] GetDirectories(string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
@@ -355,7 +336,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
|
||||
var parent = Directory.GetParent(path.TrimEnd(Path.DirectorySeparatorChar));
|
||||
var parent = Directory.GetParent(path);
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace NzbDrone.Common.Disk
|
||||
bool FolderExists(string path);
|
||||
bool FileExists(string path);
|
||||
bool FileExists(string path, StringComparison stringComparison);
|
||||
bool FolderWritable(string path);
|
||||
string[] GetDirectories(string path);
|
||||
string[] GetFiles(string path, SearchOption searchOption);
|
||||
long GetFolderSize(string path);
|
||||
|
||||
@@ -59,15 +59,6 @@ namespace NzbDrone.Common.Http
|
||||
AddRequestHeaders(webRequest, request.Headers);
|
||||
}
|
||||
|
||||
if (request.Cookies.Count != 0)
|
||||
{
|
||||
webRequest.CookieContainer = new CookieContainer();
|
||||
foreach (var pair in request.Cookies)
|
||||
{
|
||||
webRequest.CookieContainer.Add(new Cookie(pair.Key, pair.Value, "/", request.Url.Host));
|
||||
}
|
||||
}
|
||||
|
||||
if (!request.Body.IsNullOrWhiteSpace())
|
||||
{
|
||||
var bytes = request.Headers.GetEncodingFromContentType().GetBytes(request.Body.ToCharArray());
|
||||
@@ -110,12 +101,12 @@ namespace NzbDrone.Common.Http
|
||||
var response = new HttpResponse(request, new HttpHeader(httpWebResponse.Headers), data, httpWebResponse.StatusCode);
|
||||
_logger.Trace("{0} ({1:n0} ms)", response, stopWatch.ElapsedMilliseconds);
|
||||
|
||||
if (request.AllowAutoRedirect && !RuntimeInfoBase.IsProduction &&
|
||||
if (!RuntimeInfoBase.IsProduction &&
|
||||
(response.StatusCode == HttpStatusCode.Moved ||
|
||||
response.StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
response.StatusCode == HttpStatusCode.Found))
|
||||
{
|
||||
_logger.Error("Server requested a redirect to [" + response.Headers["Location"] + "]. Update the request URL to avoid this redirect.");
|
||||
throw new Exception("Server requested a redirect to [" + response.Headers["Location"] + "]. Update the request URL to avoid this redirect.");
|
||||
}
|
||||
|
||||
if (!request.SuppressHttpError && response.HasHttpError)
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace NzbDrone.Common.Http
|
||||
Headers = new HttpHeader();
|
||||
_segments = new Dictionary<string, string>();
|
||||
AllowAutoRedirect = true;
|
||||
Cookies = new Dictionary<string, string>();
|
||||
|
||||
if (httpAccept != null)
|
||||
{
|
||||
@@ -45,7 +44,6 @@ namespace NzbDrone.Common.Http
|
||||
public NetworkCredential NetworkCredential { get; set; }
|
||||
public bool SuppressHttpError { get; set; }
|
||||
public bool AllowAutoRedirect { get; set; }
|
||||
public Dictionary<string, string> Cookies { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -68,20 +66,5 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
_segments.Add(key, value);
|
||||
}
|
||||
|
||||
public void AddCookie(string key, string value)
|
||||
{
|
||||
Cookies[key] = value;
|
||||
}
|
||||
|
||||
public void AddCookie(string cookies)
|
||||
{
|
||||
foreach (var pair in cookies.Split(';'))
|
||||
{
|
||||
var split = pair.Split('=');
|
||||
|
||||
Cookies[split[0].Trim()] = split[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,11 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
var level = LogLevel.Trace;
|
||||
|
||||
if (RuntimeInfoBase.IsProduction)
|
||||
{
|
||||
level = LogLevel.Info;
|
||||
}
|
||||
|
||||
var coloredConsoleTarget = new ColoredConsoleTarget();
|
||||
|
||||
coloredConsoleTarget.Name = "consoleLogger";
|
||||
|
||||
@@ -134,7 +134,6 @@
|
||||
<Compile Include="Extensions\DateTimeExtensions.cs" />
|
||||
<Compile Include="Crypto\HashConverter.cs" />
|
||||
<Compile Include="Extensions\Int64Extensions.cs" />
|
||||
<Compile Include="Extensions\ObjectExtensions.cs" />
|
||||
<Compile Include="Extensions\StreamExtensions.cs" />
|
||||
<Compile Include="Extensions\XmlExtentions.cs" />
|
||||
<Compile Include="HashUtil.cs" />
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace NzbDrone.Common.Processes
|
||||
{
|
||||
if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
args = GetMonoArgs(path, args);
|
||||
args = path + " " + args;
|
||||
path = "mono";
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace NzbDrone.Common.Processes
|
||||
{
|
||||
if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
args = GetMonoArgs(path, args);
|
||||
args = path + " " + args;
|
||||
path = "mono";
|
||||
}
|
||||
|
||||
@@ -302,10 +302,5 @@ namespace NzbDrone.Common.Processes
|
||||
|
||||
return processes;
|
||||
}
|
||||
|
||||
private string GetMonoArgs(string path, string args)
|
||||
{
|
||||
return String.Format("--debug {0} {1}", path, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Jobs;
|
||||
using NzbDrone.Core.Tags;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class dedupe_tags : MigrationTest<Core.Datastore.Migration.dedupe_tags>
|
||||
{
|
||||
[Test]
|
||||
public void should_not_fail_if_series_tags_are_null()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Series").Row(new
|
||||
{
|
||||
Tvdbid = 1,
|
||||
TvRageId = 1,
|
||||
Title = "Title1",
|
||||
CleanTitle = "CleanTitle1",
|
||||
Status = 1,
|
||||
Images = "",
|
||||
Path = "c:\\test",
|
||||
Monitored = 1,
|
||||
SeasonFolder = 1,
|
||||
Runtime = 0,
|
||||
SeriesType = 0,
|
||||
UseSceneNumbering = 0,
|
||||
LastInfoSync = "2000-01-01 00:00:00"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
});
|
||||
|
||||
Mocker.Resolve<TagRepository>().All().Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_fail_if_series_tags_are_empty()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Series").Row(new
|
||||
{
|
||||
Tvdbid = 1,
|
||||
TvRageId = 1,
|
||||
Title = "Title1",
|
||||
CleanTitle = "CleanTitle1",
|
||||
Status = 1,
|
||||
Images = "",
|
||||
Path = "c:\\test",
|
||||
Monitored = 1,
|
||||
SeasonFolder = 1,
|
||||
Runtime = 0,
|
||||
SeriesType = 0,
|
||||
UseSceneNumbering = 0,
|
||||
LastInfoSync = "2000-01-01 00:00:00",
|
||||
Tags = "[]"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
});
|
||||
|
||||
Mocker.Resolve<TagRepository>().All().Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_duplicate_labels_from_tags()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
});
|
||||
|
||||
Mocker.Resolve<TagRepository>().All().Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_allow_duplicate_tag_to_be_inserted()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
});
|
||||
|
||||
Assert.That(() => Mocker.Resolve<TagRepository>().Insert(new Tag { Label = "test" }), Throws.Exception);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_replace_duplicated_tag_with_proper_tag()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Series").Row(new
|
||||
{
|
||||
Tvdbid = 1,
|
||||
TvRageId = 1,
|
||||
Title = "Title1",
|
||||
CleanTitle = "CleanTitle1",
|
||||
Status = 1,
|
||||
Images = "",
|
||||
Path = "c:\\test",
|
||||
Monitored = 1,
|
||||
SeasonFolder = 1,
|
||||
Runtime = 0,
|
||||
SeriesType = 0,
|
||||
UseSceneNumbering = 0,
|
||||
LastInfoSync = "2000-01-01 00:00:00",
|
||||
Tags = "[2]"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
});
|
||||
|
||||
Mocker.Resolve<SeriesRepository>().Get(1).Tags.First().Should().Be(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_update_affected_series()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Series").Row(new
|
||||
{
|
||||
Tvdbid = 1,
|
||||
TvRageId = 1,
|
||||
Title = "Title1",
|
||||
CleanTitle = "CleanTitle1",
|
||||
Status = 1,
|
||||
Images = "",
|
||||
Path = "c:\\test",
|
||||
Monitored = 1,
|
||||
SeasonFolder = 1,
|
||||
Runtime = 0,
|
||||
SeriesType = 0,
|
||||
UseSceneNumbering = 0,
|
||||
LastInfoSync = "2000-01-01 00:00:00",
|
||||
Tags = "[2]"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Series").Row(new
|
||||
{
|
||||
Tvdbid = 2,
|
||||
TvRageId = 2,
|
||||
Title = "Title2",
|
||||
CleanTitle = "CleanTitle2",
|
||||
Status = 1,
|
||||
Images = "",
|
||||
Path = "c:\\test",
|
||||
Monitored = 1,
|
||||
SeasonFolder = 1,
|
||||
Runtime = 0,
|
||||
SeriesType = 0,
|
||||
UseSceneNumbering = 0,
|
||||
LastInfoSync = "2000-01-01 00:00:00",
|
||||
Tags = "[]"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
|
||||
c.Insert.IntoTable("Tags").Row(new
|
||||
{
|
||||
Label = "test"
|
||||
});
|
||||
});
|
||||
|
||||
Mocker.Resolve<SeriesRepository>().Get(2).Tags.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
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;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class move_dot_prefix_to_transmission_category : MigrationTest<Core.Datastore.Migration.move_dot_prefix_to_transmission_category>
|
||||
{
|
||||
[Test]
|
||||
public void should_not_fail_if_no_transmission()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("DownloadClients").Row(new
|
||||
{
|
||||
Enable = 1,
|
||||
Name = "Sab",
|
||||
Implementation = "Sabnzbd",
|
||||
Settings = new SabnzbdSettings
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
TvCategory = "abc"
|
||||
}.ToJson(),
|
||||
ConfigContract = "SabnzbdSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = Mocker.Resolve<DownloadClientRepository>().All();
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Settings.As<SabnzbdSettings>().TvCategory.Should().Be("abc");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_updated_for_transmission()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("DownloadClients").Row(new
|
||||
{
|
||||
Enable = 1,
|
||||
Name = "Trans",
|
||||
Implementation = "Transmission",
|
||||
Settings = new TransmissionSettings
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
TvCategory = "abc"
|
||||
}.ToJson(),
|
||||
ConfigContract = "TransmissionSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = Mocker.Resolve<DownloadClientRepository>().All();
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Settings.As<TransmissionSettings>().TvCategory.Should().Be(".abc");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_leave_empty_category_untouched()
|
||||
{
|
||||
WithTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("DownloadClients").Row(new
|
||||
{
|
||||
Enable = 1,
|
||||
Name = "Trans",
|
||||
Implementation = "Transmission",
|
||||
Settings = new TransmissionSettings
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
TvCategory = ""
|
||||
}.ToJson(),
|
||||
ConfigContract = "TransmissionSettings"
|
||||
});
|
||||
});
|
||||
|
||||
var items = Mocker.Resolve<DownloadClientRepository>().All();
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Settings.As<TransmissionSettings>().TvCategory.Should().Be("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,10 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Reject("fail3"));
|
||||
|
||||
_reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } };
|
||||
_remoteEpisode = new RemoteEpisode {
|
||||
Series = new Series(),
|
||||
Episodes = new List<Episode> { new Episode() }
|
||||
};
|
||||
_remoteEpisode = new RemoteEpisode { Series = new Series() };
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()))
|
||||
@@ -242,33 +239,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
approvedDecisions.Count.Should().Be(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_allow_download_if_series_is_unknown()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
_remoteEpisode.Series = null;
|
||||
|
||||
var result = Subject.GetRssDecision(_reports);
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
|
||||
result.First().RemoteEpisode.DownloadAllowed.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_allow_download_if_no_episodes_found()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
_remoteEpisode.Episodes = new List<Episode>();
|
||||
|
||||
var result = Subject.GetRssDecision(_reports);
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
|
||||
result.First().RemoteEpisode.DownloadAllowed.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,37 +91,24 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void only_first_episode_not_monitored_should_return_false()
|
||||
public void only_first_episode_not_monitored_should_return_monitored()
|
||||
{
|
||||
WithFirstEpisodeUnmonitored();
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void only_second_episode_not_monitored_should_return_false()
|
||||
public void only_second_episode_not_monitored_should_return_monitored()
|
||||
{
|
||||
WithSecondEpisodeUnmonitored();
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_for_single_episode_search()
|
||||
public void should_return_true_if_it_is_a_search()
|
||||
{
|
||||
_fakeSeries.Monitored = false;
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_episode_is_monitored_for_season_search()
|
||||
{
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultSingle, new SeasonSearchCriteria()).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_episode_is_not_monitored_for_season_search()
|
||||
{
|
||||
WithFirstEpisodeUnmonitored();
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultSingle, new SeasonSearchCriteria()).Accepted.Should().BeFalse();
|
||||
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultMulti, new SeasonSearchCriteria()).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -16,7 +15,7 @@ using NzbDrone.Core.Test.Framework;
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class QueueSpecificationFixture : CoreTest<QueueSpecification>
|
||||
public class NotInQueueSpecificationFixture : CoreTest<NotInQueueSpecification>
|
||||
{
|
||||
private Series _series;
|
||||
private Episode _episode;
|
||||
@@ -28,8 +27,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.Resolve<QualityUpgradableSpecification>();
|
||||
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
@@ -94,11 +91,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_quality_in_queue_is_lower()
|
||||
{
|
||||
_series.Profile.Value.Cutoff = Quality.Bluray1080p;
|
||||
|
||||
var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
|
||||
.With(r => r.Series = _series)
|
||||
.With(r => r.Episodes = new List<Episode> { _episode })
|
||||
@@ -147,8 +143,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
[Test]
|
||||
public void should_return_false_when_quality_in_queue_is_better()
|
||||
{
|
||||
_series.Profile.Value.Cutoff = Quality.Bluray1080p;
|
||||
|
||||
var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
|
||||
.With(r => r.Series = _series)
|
||||
.With(r => r.Episodes = new List<Episode> { _episode })
|
||||
@@ -236,24 +230,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
GivenQueue(remoteEpisodes);
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_quality_in_queue_meets_cutoff()
|
||||
{
|
||||
_series.Profile.Value.Cutoff = _remoteEpisode.ParsedEpisodeInfo.Quality.Quality;
|
||||
|
||||
var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
|
||||
.With(r => r.Series = _series)
|
||||
.With(r => r.Episodes = new List<Episode> { _episode })
|
||||
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
Quality = new QualityModel(Quality.HDTV720p)
|
||||
})
|
||||
.Build();
|
||||
|
||||
GivenQueue(new List<RemoteEpisode> { remoteEpisode });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.Bluray720p, false },
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.WEBDL720p, false },
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 1, Quality.WEBDL720p, false },
|
||||
new object[] { Quality.SDTV, 1, Quality.SDTV, 2, Quality.SDTV, true },
|
||||
new object[] { Quality.WEBDL1080p, 1, Quality.WEBDL1080p, 1, Quality.WEBDL1080p, false }
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.History;
|
||||
@@ -59,10 +58,6 @@ namespace NzbDrone.Core.Test.Download
|
||||
.Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId))
|
||||
.Returns(new History.History());
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.GetSeries("Drone.S01E01.HDTV"))
|
||||
.Returns(remoteEpisode.Series);
|
||||
|
||||
}
|
||||
|
||||
private void GivenNoGrabbedHistory()
|
||||
@@ -151,27 +146,6 @@ namespace NzbDrone.Core.Test.Download
|
||||
AssertNoAttemptedImport();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_mark_as_imported_if_all_episodes_were_imported()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"})),
|
||||
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E02.mkv"}))
|
||||
});
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
|
||||
AssertCompletedDownload();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_mark_as_imported_if_all_files_were_rejected()
|
||||
{
|
||||
@@ -179,42 +153,14 @@ namespace NzbDrone.Core.Test.Download
|
||||
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}, new Rejection("Rejected!")), "Test Failure"),
|
||||
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E02.mkv"},new Rejection("Rejected!")), "Test Failure")
|
||||
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}, "Rejected!"),"Test Failure"),
|
||||
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E02.mkv"}, "Rejected!"),"Test Failure")
|
||||
});
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(v => v.PublishEvent<DownloadCompletedEvent>(It.IsAny<DownloadCompletedEvent>()), Times.Never());
|
||||
|
||||
AssertNoCompletedDownload();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_mark_as_imported_if_no_episodes_were_parsed()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}, new Rejection("Rejected!")), "Test Failure"),
|
||||
|
||||
new ImportResult(
|
||||
new ImportDecision(
|
||||
new LocalEpisode {Path = @"C:\TestPath\Droned.S01E02.mkv"},new Rejection("Rejected!")), "Test Failure")
|
||||
});
|
||||
|
||||
_trackedDownload.RemoteEpisode.Episodes.Clear();
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
_trackedDownload.State.Should().NotBe(TrackedDownloadStage.Imported);
|
||||
|
||||
AssertNoCompletedDownload();
|
||||
}
|
||||
@@ -287,10 +233,6 @@ namespace NzbDrone.Core.Test.Download
|
||||
[Test]
|
||||
public void should_not_import_when_there_is_a_title_mismatch()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.GetSeries("Drone.S01E01.HDTV"))
|
||||
.Returns((Series)null);
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
|
||||
AssertNoCompletedDownload();
|
||||
@@ -337,9 +279,6 @@ namespace NzbDrone.Core.Test.Download
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Verify(v => v.ProcessPath(_trackedDownload.DownloadItem.OutputPath.FullPath, _trackedDownload.RemoteEpisode.Series, _trackedDownload.DownloadItem), Times.Once());
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(v => v.PublishEvent(It.IsAny<DownloadCompletedEvent>()), Times.Once());
|
||||
|
||||
_trackedDownload.State.Should().Be(TrackedDownloadStage.Imported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients.TorrentBlackhole;
|
||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
{
|
||||
@@ -34,14 +33,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
TorrentFolder = _blackholeFolder,
|
||||
WatchFolder = _completedDownloadFolder
|
||||
};
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.OpenWriteStream(It.IsAny<string>()))
|
||||
.Returns(() => new FileStream(GetTempFilePath(), FileMode.Create));
|
||||
|
||||
Mocker.GetMock<ITorrentFileInfoReader>()
|
||||
.Setup(c => c.GetHashFromTorrentFile(It.IsAny<byte[]>()))
|
||||
.Returns("myhash");
|
||||
}
|
||||
|
||||
protected void GivenFailedDownload()
|
||||
@@ -95,9 +86,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(_downloadUrl, _filePath), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -111,9 +100,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients.UsenetBlackhole;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
{
|
||||
@@ -35,16 +35,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
NzbFolder = _blackholeFolder,
|
||||
WatchFolder = _completedDownloadFolder
|
||||
};
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.OpenWriteStream(It.IsAny<string>()))
|
||||
.Returns(() => new FileStream(GetTempFilePath(), FileMode.Create));
|
||||
}
|
||||
|
||||
protected void GivenFailedDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(c => c.Get(It.IsAny<HttpRequest>()))
|
||||
.Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Throws(new WebException());
|
||||
}
|
||||
|
||||
@@ -93,9 +89,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(_downloadUrl, _filePath), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -109,9 +103,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
Mocker.GetMock<ITransmissionProxy>()
|
||||
.Verify(v => v.AddTorrentFromData(It.IsAny<Byte[]>(), @"C:/Downloads/Finished/transmission/nzbdrone", It.IsAny<TransmissionSettings>()), Times.Once());
|
||||
.Verify(v => v.AddTorrentFromData(It.IsAny<Byte[]>(), @"C:/Downloads/Finished/transmission/.nzbdrone", It.IsAny<TransmissionSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -246,7 +246,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
Mocker.GetMock<ITransmissionProxy>()
|
||||
.Verify(v => v.AddTorrentFromData(It.IsAny<Byte[]>(), @"C:/Downloads/Finished/transmission/nzbdrone", It.IsAny<TransmissionSettings>()), Times.Once());
|
||||
.Verify(v => v.AddTorrentFromData(It.IsAny<Byte[]>(), @"C:/Downloads/Finished/transmission/.nzbdrone", It.IsAny<TransmissionSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
@@ -327,7 +327,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
{
|
||||
GivenTvCategory();
|
||||
|
||||
_downloading.DownloadDir = @"C:/Downloads/Finished/transmission/nzbdrone";
|
||||
_downloading.DownloadDir = @"C:/Downloads/Finished/transmission/.nzbdrone";
|
||||
|
||||
GivenTorrents(new List<TransmissionTorrent>
|
||||
{
|
||||
|
||||
@@ -68,28 +68,6 @@ namespace NzbDrone.Core.Test.Download
|
||||
AssertDownloadNotFailed();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_warn_if_matching_history_is_not_found()
|
||||
{
|
||||
_trackedDownload.DownloadItem.Status = DownloadItemStatus.Failed;
|
||||
GivenNoGrabbedHistory();
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
|
||||
_trackedDownload.StatusMessages.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_warn_if_matching_history_is_not_found_and_not_failed()
|
||||
{
|
||||
_trackedDownload.DownloadItem.Status = DownloadItemStatus.Failed;
|
||||
GivenNoGrabbedHistory();
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
|
||||
_trackedDownload.StatusMessages.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_mark_failed_if_encrypted()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Parser;
|
||||
@@ -13,6 +12,7 @@ using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
|
||||
@@ -4,16 +4,17 @@ using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
@@ -15,6 +14,7 @@ using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="1.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:torznab="http://torznab.com/schemas/2015/feed">
|
||||
<channel>
|
||||
<atom:link href="https://hdaccess.net/api" rel="self" type="application/rss+xml" />
|
||||
<title>HDAccess</title>
|
||||
<description>HDAccess API</description>
|
||||
<link>https://hdaccess.net</link>
|
||||
<language>en-us</language>
|
||||
<webMaster>($email) (HDA Invites)</webMaster>
|
||||
<category>search</category>
|
||||
<image>
|
||||
<url>https://hdaccess.net/logo_small.png</url>
|
||||
<title>HDAccess</title>
|
||||
<link>https://hdaccess.net</link>
|
||||
<description>HDAccess API</description>
|
||||
</image>
|
||||
|
||||
<item>
|
||||
<title>Better Call Saul S01E05 Alpine Shepherd 1080p NF WEBRip DD5.1 x264</title>
|
||||
<guid isPermaLink="true">https://hdaccess.net/details.php?id=11515</guid>
|
||||
<link>https://hdaccess.net/download.php?torrent=11515&passkey=123456</link>
|
||||
<comments>https://hdaccess.net/details.php?id=11515&hit=1#comments</comments>
|
||||
<pubDate>Sat, 14 Mar 2015 17:10:42 -0400</pubDate>
|
||||
<category>HDTV 1080p</category>
|
||||
<size>2538463390</size>
|
||||
<description>Better.Call.Saul.S01E05.Alpine.Shepherd.1080p.NF.WEBRip.DD5.1.x264.torrent</description>
|
||||
<enclosure url="https://hdaccess.net/download.php?torrent=11515&passkey=123456" length="2538463390" type="application/x-bittorrent" />
|
||||
<torznab:attr name="rageid" value="37780" />
|
||||
<torznab:attr name="imdb" value="3032476" />
|
||||
<torznab:attr name="tvdbid" value="273181" />
|
||||
<torznab:attr name="category" value="5000" />
|
||||
<torznab:attr name="category" value="5040" />
|
||||
<torznab:attr name="category" value="100009" />
|
||||
<torznab:attr name="category" value="100036" />
|
||||
<torznab:attr name="type" value="series" />
|
||||
<torznab:attr name="seeders" value="7" />
|
||||
<torznab:attr name="peers" value="7" />
|
||||
<torznab:attr name="coverurl" value="https://hdaccess.net/images/posters/273181.jpg" />
|
||||
<torznab:attr name="bannerurl" value="https://hdaccess.net/images/banners/273181.jpg" />
|
||||
<torznab:attr name="infohash" value="63e07ff523710ca268567dad344ce1e0e6b7e8a3" />
|
||||
<torznab:attr name="minimumratio" value="1.0" />
|
||||
<torznab:attr name="minimumseedtime" value="172800" />
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Ocean Giants 2013 1080p 3D BluRay Remux MVC DTS-HD MA 5.1-HDAccess</title>
|
||||
<guid isPermaLink="true">https://hdaccess.net/details.php?id=11511</guid>
|
||||
<link>https://hdaccess.net/download.php?torrent=11511&passkey=123456</link>
|
||||
<comments>https://hdaccess.net/details.php?id=11511&hit=1#comments</comments>
|
||||
<pubDate>Sat, 14 Mar 2015 16:33:42 -0400</pubDate>
|
||||
<category>CUSTOM 3D BD</category>
|
||||
<size>15330508800</size>
|
||||
<description>Ocean Giants 2013 1080p 3D BluRay Remux MVC DTS-HD MA 5.1-HDAccess.torrent</description>
|
||||
<enclosure url="https://hdaccess.net/download.php?torrent=11511&passkey=123456" length="15330508800" type="application/x-bittorrent" />
|
||||
<torznab:attr name="category" value="2000" />
|
||||
<torznab:attr name="category" value="2060" />
|
||||
<torznab:attr name="category" value="100001" />
|
||||
<torznab:attr name="category" value="100018" />
|
||||
<torznab:attr name="type" value="movie" />
|
||||
<torznab:attr name="seeders" value="41" />
|
||||
<torznab:attr name="peers" value="41" />
|
||||
<torznab:attr name="coverurl" value="http://s5.postimg.org/gl7z658on/screenshot_1008.png" />
|
||||
<torznab:attr name="infohash" value="ec039a525a6feac4b15889323f4f443de381e7cc" />
|
||||
<torznab:attr name="minimumratio" value="1.0" />
|
||||
<torznab:attr name="minimumseedtime" value="172800" />
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Wild 2014 720p BluRay DTS x264-HDAccess</title>
|
||||
<guid isPermaLink="true">https://hdaccess.net/details.php?id=11506</guid>
|
||||
<link>https://hdaccess.net/download.php?torrent=11506&passkey=123456</link>
|
||||
<comments>https://hdaccess.net/details.php?id=11506&hit=1#comments</comments>
|
||||
<pubDate>Sat, 14 Mar 2015 14:28:43 -0400</pubDate>
|
||||
<category>720p</category>
|
||||
<size>6501510357</size>
|
||||
<description>Wild.2014.720p.BluRay.DTS.x264-HDAccess.torrent</description>
|
||||
<enclosure url="https://hdaccess.net/download.php?torrent=11506&passkey=123456" length="6501510357" type="application/x-bittorrent" />
|
||||
<torznab:attr name="imdb" value="2305051" />
|
||||
<torznab:attr name="category" value="2000" />
|
||||
<torznab:attr name="category" value="2060" />
|
||||
<torznab:attr name="category" value="100002" />
|
||||
<torznab:attr name="category" value="100022" />
|
||||
<torznab:attr name="type" value="movie" />
|
||||
<torznab:attr name="seeders" value="57" />
|
||||
<torznab:attr name="peers" value="58" />
|
||||
<torznab:attr name="coverurl" value="https://hdaccess.net/imdb/images/2305051.jpg" />
|
||||
<torznab:attr name="infohash" value="6704c29a00304f01b7dbb7959bfea5ccefe7d7d5" />
|
||||
<torznab:attr name="minimumratio" value="1.0" />
|
||||
<torznab:attr name="minimumseedtime" value="172800" />
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Absolute Power 1997.1080p BluRay Remux AVC DTS-HD MA 5.1-HDX</title>
|
||||
<guid isPermaLink="true">https://hdaccess.net/details.php?id=11504</guid>
|
||||
<link>https://hdaccess.net/download.php?torrent=11504&passkey=123456</link>
|
||||
<comments>https://hdaccess.net/details.php?id=11504&hit=1#comments</comments>
|
||||
<pubDate>Sat, 14 Mar 2015 13:34:08 -0400</pubDate>
|
||||
<category>REMUX</category>
|
||||
<size>25267070253</size>
|
||||
<description>Absolute.Power.1997.1080p.BluRay.Remux.AVC.DTS-HD.MA.5.1-HDX.mkv.torrent</description>
|
||||
<enclosure url="https://hdaccess.net/download.php?torrent=11504&passkey=123456" length="25267070253" type="application/x-bittorrent" />
|
||||
<torznab:attr name="imdb" value="0118548" />
|
||||
<torznab:attr name="category" value="2000" />
|
||||
<torznab:attr name="category" value="2060" />
|
||||
<torznab:attr name="category" value="100002" />
|
||||
<torznab:attr name="category" value="100026" />
|
||||
<torznab:attr name="type" value="movie" />
|
||||
<torznab:attr name="seeders" value="6" />
|
||||
<torznab:attr name="peers" value="7" />
|
||||
<torznab:attr name="coverurl" value="https://hdaccess.net/imdb/images/0118548.jpg" />
|
||||
<torznab:attr name="infohash" value="668c1fed4b6bad43b1c84656da30d5f4eb58afdb" />
|
||||
<torznab:attr name="minimumratio" value="1.0" />
|
||||
<torznab:attr name="minimumseedtime" value="172800" />
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>12 Monkeys S01E09 Tomorrow 720p WEB-DL DD5.1 H.264-BS</title>
|
||||
<guid isPermaLink="true">https://hdaccess.net/details.php?id=11501</guid>
|
||||
<link>https://hdaccess.net/download.php?torrent=11501&passkey=123456</link>
|
||||
<comments>https://hdaccess.net/details.php?id=11501&hit=1#comments</comments>
|
||||
<pubDate>Sat, 14 Mar 2015 12:42:19 -0400</pubDate>
|
||||
<category>TV 720p WEB-DL</category>
|
||||
<size>1397243303</size>
|
||||
<description>12.Monkeys.S01E09.Tomorrow.720p.WEB-DL.DD5.1.H.264-BS.torrent</description>
|
||||
<enclosure url="https://hdaccess.net/download.php?torrent=11501&passkey=123456" length="1397243303" type="application/x-bittorrent" />
|
||||
<torznab:attr name="rageid" value="36903" />
|
||||
<torznab:attr name="imdb" value="3148266" />
|
||||
<torznab:attr name="tvdbid" value="272644" />
|
||||
<torznab:attr name="category" value="5000" />
|
||||
<torznab:attr name="category" value="5040" />
|
||||
<torznab:attr name="category" value="100004" />
|
||||
<torznab:attr name="category" value="100038" />
|
||||
<torznab:attr name="type" value="series" />
|
||||
<torznab:attr name="seeders" value="6" />
|
||||
<torznab:attr name="peers" value="6" />
|
||||
<torznab:attr name="coverurl" value="https://hdaccess.net/images/posters/272644.jpg" />
|
||||
<torznab:attr name="bannerurl" value="https://hdaccess.net/images/banners/272644.jpg" />
|
||||
<torznab:attr name="infohash" value="9fbf7d6d52eb9847700591dad758988fb0799c53" />
|
||||
<torznab:attr name="minimumratio" value="1.0" />
|
||||
<torznab:attr name="minimumseedtime" value="172800" />
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user