mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-16 21:35:04 -04:00
Compare commits
108 Commits
v0.1.0.361
...
v0.1.0.545
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfb254d2dc | ||
|
|
07c03b0a12 | ||
|
|
eb0cf2d5f6 | ||
|
|
69c04ebe7a | ||
|
|
135db6d2ff | ||
|
|
c3deace9e6 | ||
|
|
9042594b14 | ||
|
|
44df0f5c3d | ||
|
|
7b9446eb35 | ||
|
|
c6b6daaf80 | ||
|
|
3b42b6a7e0 | ||
|
|
b6238f469c | ||
|
|
ae00c3aa6b | ||
|
|
8bf9d1b016 | ||
|
|
19ed7aa804 | ||
|
|
4d129ada95 | ||
|
|
4ec8ea0e4d | ||
|
|
bd79d3c828 | ||
|
|
7fb6c539d4 | ||
|
|
7f6fa2efbe | ||
|
|
b1727d9d91 | ||
|
|
3435d9db6e | ||
|
|
8e597c8179 | ||
|
|
80ec66514e | ||
|
|
88e677d973 | ||
|
|
a00f32c508 | ||
|
|
538db52d16 | ||
|
|
1ce7b0e56e | ||
|
|
87d91a0f15 | ||
|
|
61c1e934a5 | ||
|
|
97b09335df | ||
|
|
5e34fd2a9f | ||
|
|
7e620bd156 | ||
|
|
a97b801b24 | ||
|
|
9e64acd407 | ||
|
|
f72269f91b | ||
|
|
94d7f768a1 | ||
|
|
78cdc78cf9 | ||
|
|
2fc1257f42 | ||
|
|
210311cb38 | ||
|
|
9d7ec89314 | ||
|
|
9c279701a6 | ||
|
|
743e2e9b21 | ||
|
|
4a8daea940 | ||
|
|
bd90b74c12 | ||
|
|
4dff0c075a | ||
|
|
b8f57507dd | ||
|
|
61bfa9e7ed | ||
|
|
bbea256c85 | ||
|
|
5a1186639e | ||
|
|
a8f2700fe6 | ||
|
|
eeec505182 | ||
|
|
66dc53b92f | ||
|
|
334f3514df | ||
|
|
6ce35f6a24 | ||
|
|
f6a5f887ce | ||
|
|
bc90415394 | ||
|
|
a8fdd46cd3 | ||
|
|
b6a08bdd9e | ||
|
|
59df0351ac | ||
|
|
0fd242cd62 | ||
|
|
ed7c5a937f | ||
|
|
f6906d0f18 | ||
|
|
561563b48c | ||
|
|
e383036c84 | ||
|
|
202f439a60 | ||
|
|
449e60afc0 | ||
|
|
9675171aff | ||
|
|
bcee5f1754 | ||
|
|
d1a3e61979 | ||
|
|
b4a0c272c9 | ||
|
|
75cbabf716 | ||
|
|
6612202384 | ||
|
|
6b52dd6e7a | ||
|
|
974ab6387f | ||
|
|
9ac435bc41 | ||
|
|
b0f04bb9d7 | ||
|
|
52bff3d7bd | ||
|
|
d965cb3c98 | ||
|
|
b592a137cf | ||
|
|
b0819c97ed | ||
|
|
5802d20b93 | ||
|
|
58e30cc9a6 | ||
|
|
513b9d2324 | ||
|
|
3ebdc1c1ac | ||
|
|
e9a5c5f7d2 | ||
|
|
23c01f7dbe | ||
|
|
c35864cc7f | ||
|
|
e545e0a129 | ||
|
|
1e72944998 | ||
|
|
d234cbda8a | ||
|
|
09a9731dae | ||
|
|
3b4df1706e | ||
|
|
20038e4757 | ||
|
|
c57c77d8e7 | ||
|
|
c61c3a9c45 | ||
|
|
d6d418f7b3 | ||
|
|
ab5cf45d88 | ||
|
|
40d55b915e | ||
|
|
4a851c37d5 | ||
|
|
63b6adf0e1 | ||
|
|
d9e211472b | ||
|
|
fa05dbc642 | ||
|
|
709dc0ee5f | ||
|
|
67f26fe185 | ||
|
|
5f4218ae91 | ||
|
|
30b54d8340 | ||
|
|
ee6ae386ca |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,6 +1,6 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
github: Prowlarr # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: prowlarr
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,6 +7,7 @@ assignees: ''
|
||||
|
||||
---
|
||||
<!-- Support Requests will be closed immediately, if you are unsure go to our Reddit or Discord first. Exceptions do not mean you found a bug! -->
|
||||
<!-- Note: Text between <!- and -> will be hidden -->
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
@@ -33,4 +34,5 @@ assignees: ''
|
||||
|
||||
Turn on Trace logs under Settings -> General and wait for the bug to occur again.
|
||||
**Upload the full log file here (or another site (e.g. pastebin) and link it). Issues will be closed, if they do not include this!**
|
||||
<!-- Trace logs are named Prowlarr.trace.txt or Prowlarr.trace.#.txt and will contain "trace" in them-->
|
||||
<!-- Trace logs are named Prowlarr.trace.txt or Prowlarr.trace.#.txt and will contain "trace" in them-->
|
||||
<!-- Please see the Wiki for how to provide proper and useful trace log files https://wiki.servarr.com/prowlarr/troubleshooting#logging-and-log-files -->
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Indexer Requests
|
||||
url: https://requests.prowlarr.com/
|
||||
about: Request new indexers to be added. Vote on existing requests.
|
||||
- name: Support via Discord
|
||||
url: https://prowlarr.com/discord
|
||||
about: Chat with users and devs on support and setup related topics.
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -5,6 +5,7 @@ YES | NO
|
||||
A few sentences describing the overall goals of the pull request's commits.
|
||||
|
||||
#### Screenshot (if UI related)
|
||||
|
||||
#### Todos
|
||||
- [ ] Tests
|
||||
- [ ] Translation Keys
|
||||
|
||||
@@ -3,15 +3,31 @@
|
||||
We're always looking for people to help make Prowlarr even better, there are a number of ways to contribute.
|
||||
|
||||
## Documentation ##
|
||||
Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
Setup guides, FAQ, the more information we have on the [wiki](https://wiki.servarr.com/prowlarr) the better.
|
||||
|
||||
## Development ##
|
||||
|
||||
See the readme for information on setting up your development environment.
|
||||
### Tools required ###
|
||||
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
|
||||
- HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [NodeJS](https://nodejs.org/en/download/) (Node 12.X.X or higher)
|
||||
- [Yarn](https://yarnpkg.com/)
|
||||
- .NET Core 5.0.
|
||||
|
||||
### Getting started ###
|
||||
|
||||
1. Fork Prowlarr
|
||||
2. Clone the repository into your development machine. [*info*](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github)
|
||||
3. Install the required Node Packages `yarn install`
|
||||
4. Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
|
||||
5. Build the project in Visual Studio, Setting startup project to `Prowlarr.Console` and framework to `net5.0`
|
||||
6. Debug the project in Visual Studio
|
||||
7. Open http://localhost:9696
|
||||
|
||||
### Contributing Code ###
|
||||
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Prowlarr/Prowlarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
||||
- Rebase from Prowlarr's develop branch, don't merge
|
||||
- Rebase from Radarr's develop branch, don't merge
|
||||
- Make meaningful commits, or squash them
|
||||
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
|
||||
- Reach out to us on the discord if you have any questions
|
||||
@@ -20,6 +36,10 @@ See the readme for information on setting up your development environment.
|
||||
- One feature/bug fix per pull request to keep things clean and easy to understand
|
||||
- Use 4 spaces instead of tabs, this is the default for VS 2019 and WebStorm (to my knowledge)
|
||||
|
||||
### Contributing Indexers ###
|
||||
- If you're contributing an indexer please phrase your commit as something like: `New: (Indexer) {Indexer Name}`, `New: (Indexer) {Usenet|Torrent} {Indexer Name}`, `New: (Indexer) {Torznab|Newznab} {Indexer Name}`
|
||||
- If you're updating an indexer please phrase your commit as something like: `Fixed: (Indexer) {Indexer Name} {changes}` e.g. `Fixed: (Indexer) Changed BHD to use API`
|
||||
|
||||
### 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
|
||||
|
||||
17
README.md
17
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
|
||||
[](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
|
||||
[](https://wikijs.servarr.com/prowlarr/installation#docker)
|
||||
[](https://wiki.servarr.com/prowlarr/installation#docker)
|
||||

|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
@@ -10,12 +10,14 @@
|
||||
Prowlarr is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Sonarr, Radarr, Lidarr, and Readarr offering complete management of your indexers with no per app Indexer setup required (we do it all).
|
||||
|
||||
## Major Features Include:
|
||||
- Usenet support for any Newznab compatible indexer, including Headphones VIP
|
||||
- Torrent support 400+ trackers & more coming soon
|
||||
- Usenet support for 24 indexers natively, including Headphones VIP, and support for any Newznab compatible indexer via "Generic Newznab"
|
||||
- Torrent support for almost 500 trackers & more coming soon
|
||||
- Torrent support for any Torznab compatible tracker via "Generic Torznab"
|
||||
- Indexer Sync to Sonarr/Radarr/Readarr/Lidarr, so no manual configuration of the other applications are required
|
||||
- Indexer History and Statistics
|
||||
- Manual Searching of Trackers & Indexers at a category level
|
||||
- Support for pushing releases directly to your download clients from Prowlarr
|
||||
- Indexer health and status notifications
|
||||
|
||||
## Support
|
||||
Note: Prowlarr is currently early in life, thus bugs should be expected
|
||||
@@ -23,7 +25,14 @@ Note: Prowlarr is currently early in life, thus bugs should be expected
|
||||
[](https://prowlarr.com/discord)
|
||||
[](https://www.reddit.com/r/Prowlarr)
|
||||
[](https://github.com/Prowlarr/Prowlarr/issues)
|
||||
[](https://wikijs.servarr.com/prowlarr)
|
||||
[](https://wiki.servarr.com/prowlarr)
|
||||
|
||||
## Indexers/Trackers
|
||||
|
||||
[Supported Indexers](https://wiki.servarr.com/en/prowlarr/supported-indexers)
|
||||
|
||||
[Indexer Requests](https://requests.prowlarr.com)
|
||||
- Request or vote on an existing request for a new tracker/indexer
|
||||
|
||||
## Feature Requests
|
||||
|
||||
|
||||
@@ -23,6 +23,16 @@ class BarChart extends Component {
|
||||
this.myChart = new Chart(this.canvasRef.current, {
|
||||
type: 'bar',
|
||||
options: {
|
||||
x: {
|
||||
ticks: {
|
||||
stepSize: this.props.stepSize
|
||||
}
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
stepSize: this.props.stepSize
|
||||
}
|
||||
},
|
||||
indexAxis: this.props.horizontal ? 'y' : 'x',
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
@@ -64,7 +74,8 @@ BarChart.propTypes = {
|
||||
horizontal: PropTypes.bool,
|
||||
legend: PropTypes.bool,
|
||||
title: PropTypes.string.isRequired,
|
||||
kind: PropTypes.oneOf(kinds.all).isRequired
|
||||
kind: PropTypes.oneOf(kinds.all).isRequired,
|
||||
stepSize: PropTypes.number
|
||||
};
|
||||
|
||||
BarChart.defaultProps = {
|
||||
@@ -72,7 +83,8 @@ BarChart.defaultProps = {
|
||||
horizontal: false,
|
||||
legend: false,
|
||||
title: '',
|
||||
kind: kinds.INFO
|
||||
kind: kinds.INFO,
|
||||
stepSize: 1
|
||||
};
|
||||
|
||||
export default BarChart;
|
||||
|
||||
@@ -16,10 +16,16 @@ class StackedBarChart extends Component {
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true
|
||||
stacked: true,
|
||||
ticks: {
|
||||
stepSize: this.props.stepSize
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true
|
||||
stacked: true,
|
||||
ticks: {
|
||||
stepSize: this.props.stepSize
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
@@ -63,11 +69,13 @@ class StackedBarChart extends Component {
|
||||
|
||||
StackedBarChart.propTypes = {
|
||||
data: PropTypes.object.isRequired,
|
||||
title: PropTypes.string.isRequired
|
||||
title: PropTypes.string.isRequired,
|
||||
stepSize: PropTypes.number
|
||||
};
|
||||
|
||||
StackedBarChart.defaultProps = {
|
||||
title: ''
|
||||
title: '',
|
||||
stepSize: 1
|
||||
};
|
||||
|
||||
export default StackedBarChart;
|
||||
|
||||
@@ -129,7 +129,7 @@ class FileBrowserModalContent extends Component {
|
||||
className={styles.mappedDrivesWarning}
|
||||
kind={kinds.WARNING}
|
||||
>
|
||||
<Link to="https://wikijs.servarr.com/prowlarr/faq#why-cant-prowlarr-see-my-files-on-a-remote-server">
|
||||
<Link to="https://wiki.servarr.com/prowlarr/faq#why-cant-prowlarr-see-my-files-on-a-remote-server">
|
||||
{translate('MappedDrivesRunningAsService')}
|
||||
</Link>
|
||||
</Alert>
|
||||
|
||||
@@ -25,7 +25,7 @@ function FormInputHelpText(props) {
|
||||
isCheckInput && styles.isCheckInput
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
<div dangerouslySetInnerHTML={{ __html: text }} />
|
||||
|
||||
{
|
||||
link ?
|
||||
|
||||
@@ -73,7 +73,7 @@ class HistoryOptions extends Component {
|
||||
}
|
||||
|
||||
HistoryOptions.propTypes = {
|
||||
historyCleanupDays: PropTypes.bool.isRequired,
|
||||
historyCleanupDays: PropTypes.number.isRequired,
|
||||
dispatchSaveGeneralSettings: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -20,19 +20,25 @@ import styles from './AddIndexerModalContent.css';
|
||||
const columns = [
|
||||
{
|
||||
name: 'protocol',
|
||||
label: 'Protocol',
|
||||
label: translate('Protocol'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
label: translate('Name'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'language',
|
||||
label: translate('Language'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'privacy',
|
||||
label: 'Privacy',
|
||||
label: translate('Privacy'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ class SelectIndexerRow extends Component {
|
||||
const {
|
||||
protocol,
|
||||
privacy,
|
||||
name
|
||||
name,
|
||||
language
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -41,6 +42,10 @@ class SelectIndexerRow extends Component {
|
||||
{name}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
{language}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
{privacy}
|
||||
</TableRowCell>
|
||||
@@ -53,6 +58,7 @@ SelectIndexerRow.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
privacy: PropTypes.string.isRequired,
|
||||
language: PropTypes.string.isRequired,
|
||||
implementation: PropTypes.string.isRequired,
|
||||
onIndexerSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ class TagsModalContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
movieTags,
|
||||
indexerTags,
|
||||
tagList,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
@@ -108,7 +108,7 @@ class TagsModalContent extends Component {
|
||||
|
||||
<div className={styles.result}>
|
||||
{
|
||||
movieTags.map((t) => {
|
||||
indexerTags.map((t) => {
|
||||
const tag = _.find(tagList, { id: t });
|
||||
|
||||
if (!tag) {
|
||||
@@ -140,7 +140,7 @@ class TagsModalContent extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (movieTags.indexOf(t) > -1) {
|
||||
if (indexerTags.indexOf(t) > -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ class TagsModalContent extends Component {
|
||||
}
|
||||
|
||||
TagsModalContent.propTypes = {
|
||||
movieTags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
indexerTags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onApplyTagsPress: PropTypes.func.isRequired
|
||||
|
||||
@@ -10,15 +10,15 @@ function createMapStateToProps() {
|
||||
(state, { indexerIds }) => indexerIds,
|
||||
createAllIndexersSelector(),
|
||||
createTagsSelector(),
|
||||
(indexerIds, allMovies, tagList) => {
|
||||
const movies = _.intersectionWith(allMovies, indexerIds, (s, id) => {
|
||||
(indexerIds, allIndexers, tagList) => {
|
||||
const indexers = _.intersectionWith(allIndexers, indexerIds, (s, id) => {
|
||||
return s.id === id;
|
||||
});
|
||||
|
||||
const movieTags = _.uniq(_.concat(..._.map(movies, 'tags')));
|
||||
const indexerTags = _.uniq(_.concat(..._.map(indexers, 'tags')));
|
||||
|
||||
return {
|
||||
movieTags,
|
||||
indexerTags,
|
||||
tagList
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import withScrollPosition from 'Components/withScrollPosition';
|
||||
import { testAllIndexers } from 'Store/Actions/indexerActions';
|
||||
import { saveMovieEditor, setMovieFilter, setMovieSort, setMovieTableOption } from 'Store/Actions/indexerIndexActions';
|
||||
import { saveIndexerEditor, setMovieFilter, setMovieSort, setMovieTableOption } from 'Store/Actions/indexerIndexActions';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import createIndexerClientSideCollectionItemsSelector from 'Store/Selectors/createIndexerClientSideCollectionItemsSelector';
|
||||
@@ -40,8 +40,8 @@ function createMapDispatchToProps(dispatch, props) {
|
||||
dispatch(setMovieFilter({ selectedFilterKey }));
|
||||
},
|
||||
|
||||
dispatchSaveMovieEditor(payload) {
|
||||
dispatch(saveMovieEditor(payload));
|
||||
dispatchSaveIndexerEditor(payload) {
|
||||
dispatch(saveIndexerEditor(payload));
|
||||
},
|
||||
|
||||
onTestAllPress() {
|
||||
@@ -56,7 +56,7 @@ class IndexerIndexConnector extends Component {
|
||||
// Listeners
|
||||
|
||||
onSaveSelected = (payload) => {
|
||||
this.props.dispatchSaveMovieEditor(payload);
|
||||
this.props.dispatchSaveIndexerEditor(payload);
|
||||
}
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
@@ -79,7 +79,7 @@ class IndexerIndexConnector extends Component {
|
||||
|
||||
IndexerIndexConnector.propTypes = {
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
dispatchSaveMovieEditor: PropTypes.func.isRequired,
|
||||
dispatchSaveIndexerEditor: PropTypes.func.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object)
|
||||
};
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ class IndexerIndexRow extends Component {
|
||||
className={styles.externalLink}
|
||||
name={icons.EXTERNAL_LINK}
|
||||
title={'Website'}
|
||||
to={baseUrl}
|
||||
to={baseUrl.replace('api.', '')}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
|
||||
@@ -19,6 +19,10 @@ function getAverageResponseTimeData(indexerStats) {
|
||||
};
|
||||
});
|
||||
|
||||
data.sort((a, b) => {
|
||||
return b.value - a.value;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import React, { Component } from 'react';
|
||||
import IndexersSelectInputConnector from 'Components/Form/IndexersSelectInputConnector';
|
||||
import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector';
|
||||
import TextInput from 'Components/Form/TextInput';
|
||||
import keyboardShortcuts from 'Components/keyboardShortcuts';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||
import SearchFooterLabel from './SearchFooterLabel';
|
||||
@@ -38,9 +39,11 @@ class SearchFooter extends Component {
|
||||
searchQuery
|
||||
} = this.state;
|
||||
|
||||
if (searchQuery !== '' || searchCategories !== [] || searchIndexerIds !== []) {
|
||||
if (searchQuery !== '' || searchCategories.length > 0 || searchIndexerIds.length > 0) {
|
||||
this.onSearchPress();
|
||||
}
|
||||
|
||||
this.props.bindShortcut('enter', this.onSearchPress, { isGlobal: true });
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -114,6 +117,7 @@ class SearchFooter extends Component {
|
||||
|
||||
<TextInput
|
||||
name='searchQuery'
|
||||
autoFocus={true}
|
||||
value={searchQuery}
|
||||
isDisabled={isFetching}
|
||||
onChange={onInputChange}
|
||||
@@ -181,7 +185,8 @@ SearchFooter.propTypes = {
|
||||
onSearchPress: PropTypes.func.isRequired,
|
||||
hasIndexers: PropTypes.bool.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
searchError: PropTypes.object
|
||||
searchError: PropTypes.object,
|
||||
bindShortcut: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SearchFooter;
|
||||
export default keyboardShortcuts(SearchFooter);
|
||||
|
||||
@@ -18,9 +18,9 @@ import translate from 'Utilities/String/translate';
|
||||
import styles from './EditApplicationModalContent.css';
|
||||
|
||||
const syncLevelOptions = [
|
||||
{ key: 'disabled', value: 'Disabled' },
|
||||
{ key: 'addOnly', value: 'Add and Remove Only' },
|
||||
{ key: 'fullSync', value: 'Full Sync' }
|
||||
{ key: 'disabled', value: translate('Disabled') },
|
||||
{ key: 'addOnly', value: translate('AddRemoveOnly') },
|
||||
{ key: 'fullSync', value: translate('FullSync') }
|
||||
];
|
||||
|
||||
function EditApplicationModalContent(props) {
|
||||
@@ -53,7 +53,7 @@ function EditApplicationModalContent(props) {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{`${id ? 'Edit' : 'Add'} Application`}
|
||||
{`${id ? translate('Edit') : translate('Add')} ${translate('Application')}`}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
@@ -94,13 +94,13 @@ function EditApplicationModalContent(props) {
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{'Sync Level'}</FormLabel>
|
||||
<FormLabel>{translate('SyncLevel')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
values={syncLevelOptions}
|
||||
name="syncLevel"
|
||||
helpText={'Sync Level'}
|
||||
helpText={`${translate('SyncLevelAddRemove')}<br>${translate('SyncLevelFull')}`}
|
||||
{...syncLevel}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
|
||||
@@ -53,6 +53,9 @@ class AddDownloadClientModalContent extends Component {
|
||||
<div>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>
|
||||
{translate('AddDownloadClientToProwlarr')}
|
||||
</div>
|
||||
<div>
|
||||
{translate('ProwlarrSupportsAnyDownloadClient')}
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ function UpdateSettings(props) {
|
||||
type={inputTypes.TEXT}
|
||||
name="branch"
|
||||
helpText={usingExternalUpdateMechanism ? translate('BranchUpdateMechanism') : translate('BranchUpdate')}
|
||||
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates"
|
||||
helpLink="https://wiki.servarr.com/prowlarr/settings#updates"
|
||||
{...branch}
|
||||
onChange={onInputChange}
|
||||
readOnly={usingExternalUpdateMechanism}
|
||||
@@ -92,7 +92,7 @@ function UpdateSettings(props) {
|
||||
name="updateMechanism"
|
||||
values={updateOptions}
|
||||
helpText={translate('UpdateMechanismHelpText')}
|
||||
helpLink="https://wikijs.servarr.com/prowlarr/settings#updates"
|
||||
helpLink="https://wiki.servarr.com/prowlarr/settings#updates"
|
||||
onChange={onInputChange}
|
||||
{...updateMechanism}
|
||||
/>
|
||||
|
||||
@@ -25,8 +25,8 @@ function NotificationEventItems(props) {
|
||||
<FormLabel>{translate('NotificationTriggers')}</FormLabel>
|
||||
<div>
|
||||
<FormInputHelpText
|
||||
text={translate('NotifcationTriggersHelpText')}
|
||||
link="https://wikijs.servarr.com/prowlarr/settings#connections"
|
||||
text={translate('NotificationTriggersHelpText')}
|
||||
link="https://wiki.servarr.com/prowlarr/settings#connections"
|
||||
/>
|
||||
<div className={styles.events}>
|
||||
<div>
|
||||
|
||||
@@ -176,7 +176,7 @@ export const SET_MOVIE_SORT = 'indexerIndex/setMovieSort';
|
||||
export const SET_MOVIE_FILTER = 'indexerIndex/setMovieFilter';
|
||||
export const SET_MOVIE_VIEW = 'indexerIndex/setMovieView';
|
||||
export const SET_MOVIE_TABLE_OPTION = 'indexerIndex/setMovieTableOption';
|
||||
export const SAVE_MOVIE_EDITOR = 'indexerIndex/saveMovieEditor';
|
||||
export const SAVE_INDEXER_EDITOR = 'indexerIndex/saveIndexerEditor';
|
||||
export const BULK_DELETE_INDEXERS = 'indexerIndex/bulkDeleteIndexers';
|
||||
|
||||
//
|
||||
@@ -186,14 +186,14 @@ export const setMovieSort = createAction(SET_MOVIE_SORT);
|
||||
export const setMovieFilter = createAction(SET_MOVIE_FILTER);
|
||||
export const setMovieView = createAction(SET_MOVIE_VIEW);
|
||||
export const setMovieTableOption = createAction(SET_MOVIE_TABLE_OPTION);
|
||||
export const saveMovieEditor = createThunk(SAVE_MOVIE_EDITOR);
|
||||
export const saveIndexerEditor = createThunk(SAVE_INDEXER_EDITOR);
|
||||
export const bulkDeleteIndexers = createThunk(BULK_DELETE_INDEXERS);
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
export const actionHandlers = handleThunks({
|
||||
[SAVE_MOVIE_EDITOR]: function(getState, payload, dispatch) {
|
||||
[SAVE_INDEXER_EDITOR]: function(getState, payload, dispatch) {
|
||||
dispatch(set({
|
||||
section,
|
||||
isSaving: true
|
||||
|
||||
@@ -13,7 +13,7 @@ function createHealthCheckSelector() {
|
||||
source: 'UI',
|
||||
type: 'warning',
|
||||
message: translate('CouldNotConnectSignalR'),
|
||||
wikiUrl: 'https://wikijs.servarr.com/prowlarr/system#could-not-connect-to-signalr'
|
||||
wikiUrl: 'https://wiki.servarr.com/prowlarr/system#could-not-connect-to-signalr'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,32 +15,32 @@ class MoreInfo extends Component {
|
||||
return (
|
||||
<FieldSet legend={translate('MoreInfo')}>
|
||||
<DescriptionList>
|
||||
<DescriptionListItemTitle>Home page</DescriptionListItemTitle>
|
||||
<DescriptionListItemTitle>{translate('HomePage')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://prowlarr.com/">prowlarr.com</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Discord</DescriptionListItemTitle>
|
||||
<DescriptionListItemTitle>{translate('Wiki')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://wiki.servarr.com/prowlarr">wiki.servarr.com/prowlarr</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>{translate('Reddit')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://reddit.com/r/prowlarr">r/prowlarr</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>{translate('Discord')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://prowlarr.com/discord">prowlarr.com/discord</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Wiki</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://wikijs.servarr.com/prowlarr">wikijs.servarr.com/prowlarr</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Donations</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://opencollective.com/prowlarr">opencollective.com/prowlarr</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Source</DescriptionListItemTitle>
|
||||
<DescriptionListItemTitle>{translate('Source')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://github.com/Prowlarr/Prowlarr/">github.com/Prowlarr/Prowlarr</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
<DescriptionListItemTitle>Feature Requests</DescriptionListItemTitle>
|
||||
<DescriptionListItemTitle>{translate('FeatureRequests')}</DescriptionListItemTitle>
|
||||
<DescriptionListItemDescription>
|
||||
<Link to="https://github.com/Prowlarr/Prowlarr/issues">github.com/Prowlarr/Prowlarr/issues</Link>
|
||||
</DescriptionListItemDescription>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
.logoContainer {
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
margin: 0.5em;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
outline: none;
|
||||
|
||||
@@ -24,7 +24,7 @@ class UpdateChanges extends Component {
|
||||
<ul>
|
||||
{
|
||||
changes.map((change, index) => {
|
||||
const checkChange = change.replace(/#\d{4,5}\b/g, (match, contents) => {
|
||||
const checkChange = change.replace(/#\d{3,5}\b/g, (match, contents) => {
|
||||
return `[${match}](https://github.com/Prowlarr/Prowlarr/issues/${match.substring(1)})`;
|
||||
});
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
</span>
|
||||
|
||||
<a
|
||||
href="https://wikijs.servarr.com/prowlarr/faq#help-i-have-locked-myself-out"
|
||||
href="https://wiki.servarr.com/prowlarr/faq#help-i-have-locked-myself-out"
|
||||
class="forgot-password"
|
||||
>Forgot your password?</a
|
||||
>
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase(@"https://baconbits.org/feeds.php?feed=torrents_tv&user=12345&auth=2b51db35e1910123321025a12b9933d2&passkey=mySecret&authkey=2b51db35e1910123321025a12b9933d2")]
|
||||
[TestCase(@"http://127.0.0.1:9117/dl/indexername?jackett_apikey=flwjiefewklfjacketmySecretsdfldskjfsdlk&path=we0re9f0sdfbase64sfdkfjsdlfjk&file=The+Torrent+File+Name.torrent")]
|
||||
[TestCase(@"http://nzb.su/getnzb/2b51db35e1912ffc138825a12b9933d2.nzb&i=37292&r=2b51db35e1910123321025a12b9933d2")]
|
||||
[TestCase(@"https://horrorcharnel.org/takeloginhorror.php: username=mySecret&password=mySecret&use_sslvalue==&perm_ssl=1&submitme=X&use_ssl=1&returnto=%2F&captchaSelection=1230456")]
|
||||
[TestCase(@"https://torrentdb.net/login: _token=2b51db35e1912ffc138825a12b9933d2&username=mySecret&password=mySecret&remember=on")]
|
||||
[TestCase(@" var authkey = ""2b51db35e1910123321025a12b9933d2"";")]
|
||||
|
||||
// NzbGet
|
||||
[TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")]
|
||||
@@ -88,5 +91,14 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
|
||||
cleansedMessage.Should().Be(message);
|
||||
}
|
||||
|
||||
[TestCase(@"&useToken=2b51db35e1910123321025a12b9933d2")]
|
||||
[TestCase(@"&useToken=2b51db35e1910123321025a12b9933d2")]
|
||||
public void should_not_clean_usetoken(string message)
|
||||
{
|
||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||
|
||||
cleansedMessage.Should().Be(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,13 @@ namespace NzbDrone.Common.Http
|
||||
throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError);
|
||||
}
|
||||
|
||||
// 302 or 303 should default to GET on redirect even if POST on original
|
||||
if (response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectMethod)
|
||||
{
|
||||
request.Method = HttpMethod.GET;
|
||||
request.ContentData = null;
|
||||
}
|
||||
|
||||
response = await ExecuteRequestAsync(request, cookieContainer);
|
||||
}
|
||||
while (response.HasHttpRedirect);
|
||||
|
||||
@@ -62,6 +62,20 @@ namespace NzbDrone.Common.Http
|
||||
StatusCode == HttpStatusCode.TemporaryRedirect ||
|
||||
StatusCode == HttpStatusCode.Found;
|
||||
|
||||
public string RedirectUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
var newUrl = Headers["Location"];
|
||||
if (newUrl == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return (Request.Url += new HttpUri(newUrl)).FullUri;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] GetCookieHeaders()
|
||||
{
|
||||
return Headers.GetValues("Set-Cookie") ?? Array.Empty<string>();
|
||||
|
||||
@@ -12,12 +12,13 @@ namespace NzbDrone.Common.Instrumentation
|
||||
{
|
||||
// Url
|
||||
new Regex(@"(?<=\?|&|: |;)(apikey|token|passkey|auth|authkey|user|uid|api|[a-z_]*apikey|account|passwd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=\?|&)[^=]*?(username|password)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=\?|&| )[^=]*?(_?(?<!use)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
||||
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"(?<=authkey = "")(?<secret>[^&=]+?)(?="")", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Path
|
||||
new Regex(@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
@@ -77,7 +78,6 @@ namespace NzbDrone.Common.Instrumentation
|
||||
private static string CleanseRemoteIP(Match match)
|
||||
{
|
||||
var group = match.Groups[1];
|
||||
var valueAll = match.Value;
|
||||
var valueIP = group.Value;
|
||||
|
||||
if (IPAddress.TryParse(valueIP, out var address) && !address.IsLocalAddress())
|
||||
|
||||
@@ -33,14 +33,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[TestCase("Develop")]
|
||||
[TestCase("develop")]
|
||||
public void should_return_error_when_branch_is_v1(string branch)
|
||||
{
|
||||
GivenValidBranch(branch);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[TestCase("nightly")]
|
||||
[TestCase("Nightly")]
|
||||
public void should_return_no_warning_when_branch_valid(string branch)
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace NzbDrone.Core.Test.HealthCheck
|
||||
[TestFixture]
|
||||
public class HealthCheckFixture : CoreTest
|
||||
{
|
||||
private const string WikiRoot = "https://wikijs.servarr.com/";
|
||||
private const string WikiRoot = "https://wiki.servarr.com/";
|
||||
|
||||
[TestCase("I blew up because of some weird user mistake", null, WikiRoot + "prowlarr/system#i-blew-up-because-of-some-weird-user-mistake")]
|
||||
[TestCase("I blew up because of some weird user mistake", "#my-health-check", WikiRoot + "prowlarr/system#my-health-check")]
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.FileList;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@@ -21,6 +22,33 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
Username = "somename"
|
||||
};
|
||||
|
||||
Subject.Capabilities = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
},
|
||||
Flags = new List<IndexerFlag>
|
||||
{
|
||||
IndexerFlag.FreeLeech
|
||||
}
|
||||
};
|
||||
|
||||
Subject.Capabilities.Categories.AddCategoryMapping(1, NewznabStandardCategory.MoviesSD, "Filme SD");
|
||||
Subject.Capabilities.Categories.AddCategoryMapping(2, NewznabStandardCategory.MoviesDVD, "Filme DVD");
|
||||
|
||||
_movieSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
SearchTerm = "Star Wars",
|
||||
@@ -38,7 +66,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
[Test]
|
||||
public void should_use_categories_for_feed()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(new MovieSearchCriteria { Categories = new int[] { 1, 2 } });
|
||||
var results = Subject.GetSearchRequests(new MovieSearchCriteria { Categories = new int[] { NewznabStandardCategory.MoviesSD.Id, NewznabStandardCategory.MoviesDVD.Id } });
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
@@ -50,7 +78,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
[Test]
|
||||
public void should_not_search_by_imdbid_if_not_supported()
|
||||
{
|
||||
_movieSearchCriteria.ImdbId = "tt0076759";
|
||||
_movieSearchCriteria.ImdbId = "0076759";
|
||||
var results = Subject.GetSearchRequests(_movieSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
_movieSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
Categories = new int[] { 2000, 2010 },
|
||||
ImdbId = "tt0076759"
|
||||
ImdbId = "0076759"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
_movieSearchCriteria = new MovieSearchCriteria
|
||||
{
|
||||
Categories = new int[] { 2000, 2010 },
|
||||
ImdbId = "tt0076759"
|
||||
ImdbId = "0076759"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
public void should_search_by_imdbid_if_supported()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(_movieSearchCriteria);
|
||||
var imdbQuery = int.Parse(_movieSearchCriteria.ImdbId.Substring(2));
|
||||
var imdbQuery = int.Parse(_movieSearchCriteria.ImdbId);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -31,7 +32,25 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_lidarrV1Proxy.Test(Settings));
|
||||
var testIndexer = new IndexerDefinition
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Test",
|
||||
Protocol = DownloadProtocol.Usenet,
|
||||
Capabilities = new IndexerCapabilities()
|
||||
};
|
||||
|
||||
testIndexer.Capabilities.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio);
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_lidarrV1Proxy.TestConnection(BuildLidarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Lidarr"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
other.Name == Name &&
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Lidarr sees it, including http(s):// and port if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Lidarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Lidarr Server", HelpText = "Lidarr server URL, including http(s):// and port if needed")]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
List<LidarrIndexer> GetIndexerSchema(LidarrSettings settings);
|
||||
void RemoveIndexer(int indexerId, LidarrSettings settings);
|
||||
LidarrIndexer UpdateIndexer(LidarrIndexer indexer, LidarrSettings settings);
|
||||
ValidationFailure Test(LidarrSettings settings);
|
||||
ValidationFailure TestConnection(LidarrIndexer indexer, LidarrSettings settings);
|
||||
}
|
||||
|
||||
public class LidarrV1Proxy : ILidarrV1Proxy
|
||||
@@ -91,11 +91,15 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
return Execute<LidarrIndexer>(request);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(LidarrSettings settings)
|
||||
public ValidationFailure TestConnection(LidarrIndexer indexer, LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/test", HttpMethod.POST);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
{
|
||||
GetStatus(settings);
|
||||
Execute<LidarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -105,8 +109,14 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Lidarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("ApiKey", "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -31,7 +32,25 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_radarrV3Proxy.Test(Settings));
|
||||
var testIndexer = new IndexerDefinition
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Test",
|
||||
Protocol = DownloadProtocol.Usenet,
|
||||
Capabilities = new IndexerCapabilities()
|
||||
};
|
||||
|
||||
testIndexer.Capabilities.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies);
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_radarrV3Proxy.TestConnection(BuildRadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Radarr"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
other.Name == Name &&
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Radarr sees it, including http(s):// and port if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Radarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Radarr Server", HelpText = "Radarr server URL, including http(s):// and port if needed")]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
List<RadarrIndexer> GetIndexerSchema(RadarrSettings settings);
|
||||
void RemoveIndexer(int indexerId, RadarrSettings settings);
|
||||
RadarrIndexer UpdateIndexer(RadarrIndexer indexer, RadarrSettings settings);
|
||||
ValidationFailure Test(RadarrSettings settings);
|
||||
ValidationFailure TestConnection(RadarrIndexer indexer, RadarrSettings settings);
|
||||
}
|
||||
|
||||
public class RadarrV3Proxy : IRadarrV3Proxy
|
||||
@@ -91,11 +91,15 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
return Execute<RadarrIndexer>(request);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(RadarrSettings settings)
|
||||
public ValidationFailure TestConnection(RadarrIndexer indexer, RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/test", HttpMethod.POST);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
{
|
||||
GetStatus(settings);
|
||||
Execute<RadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -105,8 +109,14 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Radarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("ApiKey", "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -31,7 +32,25 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_readarrV1Proxy.Test(Settings));
|
||||
var testIndexer = new IndexerDefinition
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Test",
|
||||
Protocol = DownloadProtocol.Usenet,
|
||||
Capabilities = new IndexerCapabilities()
|
||||
};
|
||||
|
||||
testIndexer.Capabilities.Categories.AddCategoryMapping(1, NewznabStandardCategory.Books);
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_readarrV1Proxy.TestConnection(BuildReadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Readarr"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
other.Name == Name &&
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Readarr sees it, including http(s):// and port if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Readarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Readarr Server", HelpText = "Readarr server URL, including http(s):// and port if needed")]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
List<ReadarrIndexer> GetIndexerSchema(ReadarrSettings settings);
|
||||
void RemoveIndexer(int indexerId, ReadarrSettings settings);
|
||||
ReadarrIndexer UpdateIndexer(ReadarrIndexer indexer, ReadarrSettings settings);
|
||||
ValidationFailure Test(ReadarrSettings settings);
|
||||
ValidationFailure TestConnection(ReadarrIndexer indexer, ReadarrSettings settings);
|
||||
}
|
||||
|
||||
public class ReadarrV1Proxy : IReadarrV1Proxy
|
||||
@@ -91,11 +91,15 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
return Execute<ReadarrIndexer>(request);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(ReadarrSettings settings)
|
||||
public ValidationFailure TestConnection(ReadarrIndexer indexer, ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v1/indexer/test", HttpMethod.POST);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
{
|
||||
GetStatus(settings);
|
||||
Execute<ReadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -105,8 +109,14 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Readarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("ApiKey", "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -31,7 +32,25 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_sonarrV3Proxy.Test(Settings));
|
||||
var testIndexer = new IndexerDefinition
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Test",
|
||||
Protocol = DownloadProtocol.Usenet,
|
||||
Capabilities = new IndexerCapabilities()
|
||||
};
|
||||
|
||||
testIndexer.Capabilities.Categories.AddCategoryMapping(1, NewznabStandardCategory.TV);
|
||||
|
||||
try
|
||||
{
|
||||
failures.AddIfNotNull(_sonarrV3Proxy.TestConnection(BuildSonarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Sonarr"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
return other.EnableRss == EnableRss &&
|
||||
other.EnableAutomaticSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableAutomaticSearch &&
|
||||
other.EnableInteractiveSearch == EnableInteractiveSearch &&
|
||||
other.Name == Name &&
|
||||
other.Implementation == Implementation &&
|
||||
other.Priority == Priority &&
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public IEnumerable<int> SyncCategories { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Sonarr sees it, including http(s):// and port if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Sonarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Sonarr Server", HelpText = "Sonarr server URL, including http(s):// and port if needed")]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
List<SonarrIndexer> GetIndexerSchema(SonarrSettings settings);
|
||||
void RemoveIndexer(int indexerId, SonarrSettings settings);
|
||||
SonarrIndexer UpdateIndexer(SonarrIndexer indexer, SonarrSettings settings);
|
||||
ValidationFailure Test(SonarrSettings settings);
|
||||
ValidationFailure TestConnection(SonarrIndexer indexer, SonarrSettings settings);
|
||||
}
|
||||
|
||||
public class SonarrV3Proxy : ISonarrV3Proxy
|
||||
@@ -91,11 +91,15 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
return Execute<SonarrIndexer>(request);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(SonarrSettings settings)
|
||||
public ValidationFailure TestConnection(SonarrIndexer indexer, SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"/api/v3/indexer/test", HttpMethod.POST);
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
{
|
||||
GetStatus(settings);
|
||||
Execute<SonarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
@@ -105,8 +109,14 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Sonarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("ApiKey", "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace NzbDrone.Core.Configuration
|
||||
public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false);
|
||||
|
||||
// TODO: Change back to "master" for the first stable release.
|
||||
public string Branch => GetValue("Branch", "nightly").ToLowerInvariant();
|
||||
public string Branch => GetValue("Branch", "develop").ToLowerInvariant();
|
||||
|
||||
public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
|
||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Configuration
|
||||
var releaseInfoPath = Path.Combine(bin, "release_info");
|
||||
|
||||
PackageUpdateMechanism = UpdateMechanism.BuiltIn;
|
||||
DefaultBranch = "nightly";
|
||||
DefaultBranch = "develop";
|
||||
|
||||
if (Path.GetFileName(bin) == "bin" && diskProvider.FileExists(packageInfoPath))
|
||||
{
|
||||
|
||||
36
src/NzbDrone.Core/Datastore/Converters/CookieConverter.cs
Normal file
36
src/NzbDrone.Core/Datastore/Converters/CookieConverter.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using Dapper;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class CookieConverter : SqlMapper.TypeHandler<IDictionary<string, string>>
|
||||
{
|
||||
protected readonly JsonSerializerOptions SerializerSettings;
|
||||
|
||||
public CookieConverter()
|
||||
{
|
||||
var serializerSettings = new JsonSerializerOptions
|
||||
{
|
||||
AllowTrailingCommas = true,
|
||||
IgnoreNullValues = true,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
SerializerSettings = serializerSettings;
|
||||
}
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, IDictionary<string, string> value)
|
||||
{
|
||||
parameter.Value = JsonSerializer.Serialize(value, SerializerSettings);
|
||||
}
|
||||
|
||||
public override IDictionary<string, string> Parse(object value)
|
||||
{
|
||||
return JsonSerializer.Deserialize<Dictionary<string, string>>((string)value, SerializerSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,10 +108,10 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
if (OsInfo.IsOsx)
|
||||
{
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wikijs.servarr.com/prowlarr/faq#i-use-prowlarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/prowlarr/faq#i-use-prowlarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
|
||||
}
|
||||
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wikijs.servarr.com/prowlarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/prowlarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace NzbDrone.Core.Datastore
|
||||
SqlMapper.RemoveTypeMap(typeof(DateTime));
|
||||
SqlMapper.AddTypeHandler(new DapperUtcConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Dictionary<string, string>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<IDictionary<string, string>>());
|
||||
SqlMapper.AddTypeHandler(new CookieConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<KeyValuePair<string, int>>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<KeyValuePair<string, int>>());
|
||||
|
||||
@@ -41,10 +41,10 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Sabnzbd")]
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to NZBGet")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the nzbget url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")]
|
||||
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the NZBGet url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
|
||||
@@ -23,11 +23,6 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
if (!Enum.GetNames(typeof(ReleaseBranches)).Any(x => x.ToLower() == currentBranch))
|
||||
{
|
||||
if (currentBranch == "develop")
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckPreviousVersionMessage"), _configFileService.Branch), "#branch-is-for-a-previous-version");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch-is-not-a-valid-release-branch");
|
||||
}
|
||||
|
||||
@@ -36,6 +31,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public enum ReleaseBranches
|
||||
{
|
||||
Develop,
|
||||
Nightly
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||
|
||||
private static HttpUri MakeWikiUrl(string fragment)
|
||||
{
|
||||
return new HttpUri("https://wikijs.servarr.com/prowlarr/system#") + new HttpUri(fragment);
|
||||
return new HttpUri("https://wiki.servarr.com/prowlarr/system#") + new HttpUri(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,14 +119,14 @@ namespace NzbDrone.Core.History
|
||||
|
||||
if (message.Query is MovieSearchCriteria)
|
||||
{
|
||||
history.Data.Add("ImdbId", ((MovieSearchCriteria)message.Query).ImdbId ?? string.Empty);
|
||||
history.Data.Add("ImdbId", ((MovieSearchCriteria)message.Query).FullImdbId ?? string.Empty);
|
||||
history.Data.Add("TmdbId", ((MovieSearchCriteria)message.Query).TmdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TraktId", ((MovieSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
|
||||
}
|
||||
|
||||
if (message.Query is TvSearchCriteria)
|
||||
{
|
||||
history.Data.Add("ImdbId", ((TvSearchCriteria)message.Query).ImdbId ?? string.Empty);
|
||||
history.Data.Add("ImdbId", ((TvSearchCriteria)message.Query).FullImdbId ?? string.Empty);
|
||||
history.Data.Add("TvdbId", ((TvSearchCriteria)message.Query).TvdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TraktId", ((TvSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("RId", ((TvSearchCriteria)message.Query).RId?.ToString() ?? string.Empty);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.IndexerSearch.Definitions
|
||||
{
|
||||
@@ -20,5 +21,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
|
||||
public string SanitizedTvSearchString => (SanitizedSearchTerm + " " + EpisodeSearchString).Trim();
|
||||
public string EpisodeSearchString => GetEpisodeSearchString();
|
||||
|
||||
public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId);
|
||||
|
||||
public override bool RssSearch
|
||||
{
|
||||
get
|
||||
|
||||
@@ -80,13 +80,13 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
r.InfoUrl == null ? null : new XElement("comments", r.InfoUrl),
|
||||
r.PublishDate == DateTime.MinValue ? new XElement("pubDate", XmlDateFormat(DateTime.Now)) : new XElement("pubDate", XmlDateFormat(r.PublishDate)),
|
||||
new XElement("size", r.Size),
|
||||
r.Category == null ? null : from c in r.Category select new XElement("category", c.Id),
|
||||
r.Categories == null ? null : from c in r.Categories select new XElement("category", c.Id),
|
||||
new XElement(
|
||||
"enclosure",
|
||||
new XAttribute("url", r.DownloadUrl ?? t.MagnetUrl ?? string.Empty),
|
||||
r.Size == null ? null : new XAttribute("length", r.Size),
|
||||
new XAttribute("type", protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb")),
|
||||
r.Category == null ? null : from c in r.Category select GetNabElement("category", c.Id, protocol),
|
||||
r.Categories == null ? null : from c in r.Categories select GetNabElement("category", c.Id, protocol),
|
||||
r.IndexerFlags == null ? null : from f in r.IndexerFlags select GetNabElement("tag", f.Name, protocol),
|
||||
GetNabElement("rageid", r.TvRageId, protocol),
|
||||
GetNabElement("thetvdb", r.TvdbId, protocol),
|
||||
|
||||
@@ -6,6 +6,7 @@ using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
@@ -24,7 +25,7 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
public class IndexerDefinitionUpdateService : IIndexerDefinitionUpdateService, IExecute<IndexerDefinitionUpdateCommand>
|
||||
{
|
||||
private const int DEFINITION_VERSION = 1;
|
||||
private readonly List<string> _defintionBlacklist = new List<string>() { "blutopia", "beyond-hd", "beyond-hd-oneurl", "hdbits" };
|
||||
private readonly List<string> _defintionBlacklist = new List<string>() { "aither", "animeworld", "blutopia", "beyond-hd", "beyond-hd-oneurl", "hdbits", "shareisland" };
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
@@ -59,6 +60,40 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
var request = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}");
|
||||
var response = _httpClient.Get<List<CardigannMetaDefinition>>(request);
|
||||
indexerList = response.Resource.Where(i => !_defintionBlacklist.Contains(i.File)).ToList();
|
||||
|
||||
var definitionFolder = Path.Combine(_appFolderInfo.AppDataFolder, "Definitions", "Custom");
|
||||
|
||||
var directoryInfo = new DirectoryInfo(definitionFolder);
|
||||
|
||||
if (directoryInfo.Exists)
|
||||
{
|
||||
var files = directoryInfo.GetFiles($"*.yml");
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
_logger.Debug("Loading Custom Cardigann definition " + file.FullName);
|
||||
|
||||
try
|
||||
{
|
||||
var definitionString = File.ReadAllText(file.FullName);
|
||||
var definition = _deserializer.Deserialize<CardigannMetaDefinition>(definitionString);
|
||||
|
||||
definition.File = Path.GetFileNameWithoutExtension(file.Name);
|
||||
|
||||
if (indexerList.Any(i => i.File == definition.File || i.Name == definition.Name))
|
||||
{
|
||||
_logger.Warn("Custom Cardigann definition {0} does not have unique file name or Indexer name", file.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
indexerList.Add(definition);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error($"Error while parsing custom Cardigann definition {file.FullName}\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -108,7 +143,7 @@ namespace NzbDrone.Core.IndexerVersions
|
||||
|
||||
if (directoryInfo.Exists)
|
||||
{
|
||||
var files = directoryInfo.GetFiles($"{fileKey}.yml");
|
||||
var files = directoryInfo.GetFiles($"{fileKey}.yml", SearchOption.AllDirectories);
|
||||
|
||||
if (files.Any())
|
||||
{
|
||||
|
||||
59
src/NzbDrone.Core/Indexers/Definitions/Aither.cs
Normal file
59
src/NzbDrone.Core/Indexers/Definitions/Aither.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.UNIT3D;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class Aither : Unit3dBase
|
||||
{
|
||||
public override string Name => "Aither";
|
||||
public override string BaseUrl => "https://aither.cc/";
|
||||
public override string Description => "Aither is a Private Torrent Tracker for HD MOVIES / TV";
|
||||
public override string Language => "en-us";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public Aither(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movie");
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV");
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio, "Music");
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCGames, "Games");
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.XXX, "XXX");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TVSport, "Sport");
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.PC, "Software/Apps");
|
||||
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.BooksEBook, "Ebooks/Magazines");
|
||||
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.AudioAudiobook, "AudioBooks");
|
||||
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.Other, "Education");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +449,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Guid = guid.AbsoluteUri,
|
||||
DownloadUrl = linkUri.AbsoluteUri,
|
||||
PublishDate = publishDate,
|
||||
Category = category,
|
||||
Categories = category,
|
||||
Description = description,
|
||||
Size = size,
|
||||
Seeders = seeders,
|
||||
@@ -494,10 +494,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Username = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Passkey", HelpText = "Site Passkey")]
|
||||
[FieldDefinition(1, Label = "Passkey", Privacy = PrivacyLevel.Password, Type = FieldType.Password, HelpText = "Site Passkey")]
|
||||
public string Passkey { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -294,7 +294,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
rCat = rCat.Substring(rCatIdx + 4);
|
||||
}
|
||||
|
||||
release.Category = _categories.MapTrackerCatToNewznab(rCat);
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(rCat);
|
||||
|
||||
if (row.QuerySelector("img[alt=\"Gold Torrent\"]") != null)
|
||||
{
|
||||
@@ -350,10 +350,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Password = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Site password", Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
61
src/NzbDrone.Core/Indexers/Definitions/AnimeWorld.cs
Normal file
61
src/NzbDrone.Core/Indexers/Definitions/AnimeWorld.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Definitions.UNIT3D;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class AnimeWorld : Unit3dBase
|
||||
{
|
||||
public override string Name => "AnimeWorld";
|
||||
public override string BaseUrl => "https://animeworld.cx/";
|
||||
public override string Description => "AnimeWorld (AW) is a GERMAN Private site for ANIME / MANGA / HENTAI";
|
||||
public override string Language => "de-de";
|
||||
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public AnimeWorld(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Anime Movie");
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVAnime, "Anime Series");
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio, "Anime Musik/OST");
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCGames, "Anime Spiele");
|
||||
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.XXX, "Hentai");
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.PCGames, "Spiele Linux");
|
||||
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Other, "Sonstiges");
|
||||
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.Movies, "Filme");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TV, "Serien");
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.PCGames, "Spiele");
|
||||
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.Audio, "Musik");
|
||||
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.BooksComics, "Mangas");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
}
|
||||
332
src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs
Normal file
332
src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class Anthelion : TorrentIndexerBase<AnthelionSettings>
|
||||
{
|
||||
public override string Name => "Anthelion";
|
||||
public override string BaseUrl => "https://anthelion.me/";
|
||||
private string LoginUrl => BaseUrl + "login.php";
|
||||
public override string Description => "A movies tracker";
|
||||
public override string Language => "en-us";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public Anthelion(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new AnthelionRequestGenerator() { Settings = Settings, Capabilities = Capabilities, BaseUrl = BaseUrl };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new AnthelionParser(Settings, Capabilities.Categories, BaseUrl);
|
||||
}
|
||||
|
||||
protected override async Task DoLogin()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(LoginUrl)
|
||||
{
|
||||
LogResponseContent = true,
|
||||
AllowAutoRedirect = true
|
||||
};
|
||||
|
||||
requestBuilder.Method = HttpMethod.POST;
|
||||
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
|
||||
|
||||
var cookies = Cookies;
|
||||
|
||||
Cookies = null;
|
||||
var authLoginRequest = requestBuilder
|
||||
.AddFormParameter("username", Settings.Username)
|
||||
.AddFormParameter("password", Settings.Password)
|
||||
.AddFormParameter("keeplogged", "1")
|
||||
.AddFormParameter("login", "Log+In!")
|
||||
.SetHeader("Content-Type", "multipart/form-data")
|
||||
.Build();
|
||||
|
||||
var headers = new NameValueCollection
|
||||
{
|
||||
{ "Referer", LoginUrl }
|
||||
};
|
||||
|
||||
authLoginRequest.Headers.Add(headers);
|
||||
|
||||
var response = await ExecuteAuth(authLoginRequest);
|
||||
|
||||
if (!response.Content.Contains("logout.php"))
|
||||
{
|
||||
throw new IndexerAuthException("Anthelion Auth Failed");
|
||||
}
|
||||
|
||||
cookies = response.GetCookies();
|
||||
UpdateCookies(cookies, DateTime.Now + TimeSpan.FromDays(30));
|
||||
|
||||
_logger.Debug("Anthelion authentication succeeded.");
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (!httpResponse.Content.Contains("logout.php"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.Movies, "Film/Feature");
|
||||
caps.Categories.AddCategoryMapping("2", NewznabStandardCategory.Movies, "Film/Short");
|
||||
caps.Categories.AddCategoryMapping("3", NewznabStandardCategory.TV, "TV/Miniseries");
|
||||
caps.Categories.AddCategoryMapping("4", NewznabStandardCategory.Other, "Other");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnthelionRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public AnthelionSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
public AnthelionRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/torrents.php", BaseUrl.TrimEnd('/'));
|
||||
|
||||
// TODO: IMDB search is available but it requires to parse the details page
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{ "order_by", "time" },
|
||||
{ "order_way", "desc" },
|
||||
{ "action", "basic" },
|
||||
{ "searchsubmit", "1" },
|
||||
{ "searchstr", imdbId.IsNotNullOrWhiteSpace() ? imdbId : term }
|
||||
};
|
||||
|
||||
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
|
||||
foreach (var cat in catList)
|
||||
{
|
||||
qc.Add($"filter_cat[{cat}]", "1");
|
||||
}
|
||||
|
||||
searchUrl = searchUrl + "?" + qc.GetQueryString();
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class AnthelionParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly AnthelionSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly string _baseUrl;
|
||||
|
||||
public AnthelionParser(AnthelionSettings settings, IndexerCapabilitiesCategories categories, string baseurl)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
_baseUrl = baseurl;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
var parser = new HtmlParser();
|
||||
var doc = parser.ParseDocument(indexerResponse.Content);
|
||||
var rows = doc.QuerySelectorAll("table.torrent_table > tbody > tr.torrent");
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var qDetailsLink = row.QuerySelector("a.torrent_name");
|
||||
var year = qDetailsLink.NextSibling.TextContent.Replace("[", "").Replace("]", "").Trim();
|
||||
var tags = row.QuerySelector("div.torrent_info").FirstChild.TextContent.Replace(" / ", " ").Trim();
|
||||
var title = $"{qDetailsLink.TextContent} {year} {tags}";
|
||||
var description = row.QuerySelector("div.tags").TextContent.Trim();
|
||||
var details = _baseUrl + qDetailsLink.GetAttribute("href");
|
||||
var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last();
|
||||
var link = _baseUrl + "torrents.php?action=download&id=" + torrentId;
|
||||
var posterStr = qDetailsLink.GetAttribute("data-cover");
|
||||
var poster = !string.IsNullOrWhiteSpace(posterStr) ? new Uri(qDetailsLink.GetAttribute("data-cover")) : null;
|
||||
|
||||
var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent);
|
||||
var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent);
|
||||
var size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent);
|
||||
var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent);
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);
|
||||
|
||||
var dlVolumeFactor = row.QuerySelector("strong.tl_free") != null ? 0 : 1;
|
||||
|
||||
var cat = row.QuerySelector("td.cats_col > div").GetAttribute("class").Replace("tooltip cats_", "");
|
||||
var category = new List<IndexerCategory>
|
||||
{
|
||||
cat switch
|
||||
{
|
||||
"featurefilm" => NewznabStandardCategory.Movies,
|
||||
"shortfilm" => NewznabStandardCategory.Movies,
|
||||
"miniseries" => NewznabStandardCategory.TV,
|
||||
"other" => NewznabStandardCategory.Other,
|
||||
_ => throw new Exception($"Unknown category: {cat}")
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: TMDb is also available
|
||||
var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]");
|
||||
var imdb = qImdb != null ? ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 259200,
|
||||
Description = description,
|
||||
Title = title,
|
||||
PublishDate = publishDate,
|
||||
Categories = category,
|
||||
DownloadUrl = link,
|
||||
InfoUrl = details,
|
||||
Guid = link,
|
||||
ImdbId = imdb.GetValueOrDefault(),
|
||||
Seeders = seeders,
|
||||
Peers = leechers + seeders,
|
||||
Size = size,
|
||||
Grabs = grabs,
|
||||
Files = files,
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class AnthelionSettingsValidator : AbstractValidator<AnthelionSettings>
|
||||
{
|
||||
public AnthelionSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Username).NotEmpty();
|
||||
RuleFor(c => c.Password).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class AnthelionSettings : IProviderConfig
|
||||
{
|
||||
private static readonly AnthelionSettingsValidator Validator = new AnthelionSettingsValidator();
|
||||
|
||||
public AnthelionSettings()
|
||||
{
|
||||
Username = "";
|
||||
Password = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,11 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
var torrentInfos = new List<TorrentInfo>();
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
|
||||
@@ -50,7 +55,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
InfoHash = row.InfoHash,
|
||||
InfoUrl = details,
|
||||
Guid = details,
|
||||
Category = cats,
|
||||
Categories = cats,
|
||||
PublishDate = row.CreatedAt,
|
||||
Size = row.FileSize,
|
||||
Files = row.FileCount,
|
||||
|
||||
@@ -74,9 +74,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
var parameters = GetBasicSearchParameters(searchCriteria.Categories);
|
||||
|
||||
if (searchCriteria.ImdbId != null)
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Add("imdb", searchCriteria.ImdbId);
|
||||
parameters.Add("imdb", searchCriteria.FullImdbId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -103,9 +103,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
var parameters = GetBasicSearchParameters(searchCriteria.Categories);
|
||||
|
||||
if (searchCriteria.ImdbId != null)
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters.Add("imdb", searchCriteria.ImdbId);
|
||||
parameters.Add("imdb", searchCriteria.FullImdbId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
{
|
||||
RuleFor(c => c.Username).NotEmpty();
|
||||
RuleFor(c => c.Password).NotEmpty();
|
||||
RuleFor(c => c.Pid).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +26,10 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
|
||||
public string Token { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Username", Privacy = PrivacyLevel.UserName)]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Password", Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "PID", HelpText = "PID from My Account or My Profile page")]
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.Title = release.Title.Substring(0, insertPoint) + " Season 1 " + release.Title.Substring(insertPoint);
|
||||
}
|
||||
|
||||
release.Category = currentCategories;
|
||||
release.Categories = currentCategories;
|
||||
|
||||
//release.Description = row.QuerySelector("span.tags")?.TextContent;
|
||||
release.Guid = _baseUrl + qTitleLink.GetAttribute("href");
|
||||
@@ -402,10 +402,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Password = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Site password", Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Add Romaji Title", Type = FieldType.Checkbox, HelpText = "Add releases for Romaji Title")]
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId, searchCriteria.TmdbId.GetValueOrDefault()));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId, searchCriteria.TmdbId.GetValueOrDefault()));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -141,7 +141,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -190,11 +190,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
|
||||
}
|
||||
|
||||
// TODO Have BHD fix their API response content type so we can proper check here
|
||||
// if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
// {
|
||||
// throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
|
||||
// }
|
||||
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
var jsonResponse = new HttpResponse<BeyondHDResponse>(indexerResponse.HttpResponse);
|
||||
|
||||
foreach (var row in jsonResponse.Resource.Results)
|
||||
@@ -209,7 +209,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
InfoHash = row.InfoHash,
|
||||
InfoUrl = details,
|
||||
Guid = details,
|
||||
Category = _categories.MapTrackerCatDescToNewznab(row.Category),
|
||||
Categories = _categories.MapTrackerCatDescToNewznab(row.Category),
|
||||
PublishDate = DateTime.Parse(row.CreatedAt, CultureInfo.InvariantCulture),
|
||||
Size = row.Size,
|
||||
Grabs = row.Grabs,
|
||||
@@ -217,10 +217,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
ImdbId = ParseUtil.GetImdbID(row.ImdbId).GetValueOrDefault(),
|
||||
TmdbId = row.TmdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.CoerceInt(row.TmdbId.Split("/")[1]),
|
||||
Peers = row.Leechers + row.Seeders,
|
||||
DownloadVolumeFactor = row.Freeleech ? 0 : row.Promo75 ? 0.25 : row.Promo50 ? 0.5 : row.Promo25 ? 0.75 : 1,
|
||||
DownloadVolumeFactor = row.Freeleech || row.Limited ? 0 : row.Promo75 ? 0.25 : row.Promo50 ? 0.5 : row.Promo25 ? 0.75 : 1,
|
||||
UploadVolumeFactor = 1,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800, // 48 hours
|
||||
MinimumSeedTime = 172800, // 120 hours
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
@@ -300,5 +300,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public bool Promo25 { get; set; }
|
||||
public bool Promo50 { get; set; }
|
||||
public bool Promo75 { get; set; }
|
||||
public bool Limited { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
|
||||
torrentInfo.DownloadVolumeFactor = 0;
|
||||
torrentInfo.MinimumRatio = 1;
|
||||
|
||||
torrentInfo.Category = _categories.MapTrackerCatToNewznab(torrent.Resolution);
|
||||
torrentInfo.Categories = _categories.MapTrackerCatToNewznab(torrent.Resolution);
|
||||
|
||||
// Default to TV if category could not be mapped
|
||||
if (torrentInfo.Category == null || !torrentInfo.Category.Any())
|
||||
if (torrentInfo.Categories == null || !torrentInfo.Categories.Any())
|
||||
{
|
||||
torrentInfo.Category = new List<IndexerCategory> { NewznabStandardCategory.TV };
|
||||
torrentInfo.Categories = new List<IndexerCategory> { NewznabStandardCategory.TV };
|
||||
}
|
||||
|
||||
results.Add(torrentInfo);
|
||||
|
||||
@@ -59,6 +59,16 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
};
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> GetCookies()
|
||||
{
|
||||
if (Settings.ExtraFieldData.TryGetValue("cookie", out var cookies))
|
||||
{
|
||||
return CookieUtil.CookieHeaderToDictionary((string)cookies);
|
||||
}
|
||||
|
||||
return base.GetCookies();
|
||||
}
|
||||
|
||||
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
{
|
||||
get
|
||||
@@ -107,6 +117,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
Enable = true,
|
||||
Name = definition.Name,
|
||||
Language = definition.Language,
|
||||
Description = definition.Description,
|
||||
Implementation = GetType().Name,
|
||||
Settings = new CardigannSettings { DefinitionFile = definition.File },
|
||||
Protocol = DownloadProtocol.Torrent,
|
||||
@@ -121,6 +133,11 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (httpResponse.HasHttpError)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var generator = (CardigannRequestGenerator)GetRequestGenerator();
|
||||
|
||||
SetCookieFunctions(generator);
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
}
|
||||
else if (setting.Type == "checkbox")
|
||||
{
|
||||
variables[name] = ((bool)value) ? ".True" : ".False";
|
||||
variables[name] = ((bool)value) ? ".True" : null;
|
||||
}
|
||||
else if (setting.Type == "select")
|
||||
{
|
||||
@@ -557,7 +557,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
case "querystring":
|
||||
var param = (string)filter.Args;
|
||||
|
||||
// data = ParseUtil.GetArgumentFromQueryString(data, param);
|
||||
data = ParseUtil.GetArgumentFromQueryString(data, param);
|
||||
break;
|
||||
case "timeparse":
|
||||
case "dateparse":
|
||||
|
||||
@@ -36,11 +36,11 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
// Remove cookie cache
|
||||
if (indexerResponse.HttpResponse.HasHttpRedirect && indexerResponse.HttpResponse.Headers["Location"]
|
||||
if (indexerResponse.HttpResponse.HasHttpRedirect && indexerResponse.HttpResponse.RedirectUrl
|
||||
.ContainsIgnoreCase("login.php"))
|
||||
{
|
||||
CookiesUpdater(null, null);
|
||||
throw new IndexerException(indexerResponse, "We are being redirected to the PTP login page. Most likely your session expired or was killed. Try testing the indexer in the settings.");
|
||||
throw new IndexerException(indexerResponse, "We are being redirected to the login page. Most likely your session expired or was killed. Try testing the indexer in the settings.");
|
||||
}
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
|
||||
@@ -194,17 +194,17 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
var cats = MapTrackerCatToNewznab(value);
|
||||
if (cats.Any())
|
||||
{
|
||||
if (release.Category == null || fieldModifiers.Contains("noappend"))
|
||||
if (release.Categories == null || fieldModifiers.Contains("noappend"))
|
||||
{
|
||||
release.Category = cats;
|
||||
release.Categories = cats;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Category = release.Category.Union(cats).ToList();
|
||||
release.Categories = release.Categories.Union(cats).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
value = release.Category.ToString();
|
||||
value = release.Categories.ToString();
|
||||
break;
|
||||
case "size":
|
||||
release.Size = ReleaseInfo.GetBytes(value);
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
variables[".Query.Movie"] = null;
|
||||
variables[".Query.Year"] = null;
|
||||
variables[".Query.IMDBID"] = searchCriteria.ImdbId;
|
||||
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId?.TrimStart('t') ?? null;
|
||||
variables[".Query.IMDBID"] = searchCriteria.FullImdbId;
|
||||
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId;
|
||||
variables[".Query.TMDBID"] = searchCriteria.TmdbId;
|
||||
variables[".Query.TraktID"] = searchCriteria.TraktId;
|
||||
|
||||
@@ -78,8 +78,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
variables[".Query.Series"] = null;
|
||||
variables[".Query.Ep"] = searchCriteria.Episode;
|
||||
variables[".Query.Season"] = searchCriteria.Season;
|
||||
variables[".Query.IMDBID"] = searchCriteria.ImdbId;
|
||||
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId?.Replace("tt", "") ?? null;
|
||||
variables[".Query.IMDBID"] = searchCriteria.FullImdbId;
|
||||
variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId;
|
||||
variables[".Query.TVDBID"] = searchCriteria.TvdbId;
|
||||
variables[".Query.TVRageID"] = searchCriteria.RId;
|
||||
variables[".Query.TVMazeID"] = searchCriteria.TvMazeId;
|
||||
@@ -635,14 +635,14 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
if (requestUrl.StartsWith(SiteLink) && !redirectUrl.StartsWith(SiteLink))
|
||||
{
|
||||
var uri = new Uri(redirectUrl);
|
||||
var uri = new HttpUri(redirectUrl);
|
||||
return uri.Scheme + "://" + uri.Host + "/";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected string GetRedirectDomainHint(HttpResponse result) => GetRedirectDomainHint(result.Request.Url.ToString(), result.Headers.GetSingleValue("Location"));
|
||||
protected string GetRedirectDomainHint(HttpResponse result) => GetRedirectDomainHint(result.Request.Url.ToString(), result.RedirectUrl);
|
||||
|
||||
protected async Task<HttpResponse> HandleRequest(RequestBlock request, Dictionary<string, object> variables = null, string referer = null)
|
||||
{
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -197,7 +197,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.MinimumRatio = 1.1;
|
||||
release.MinimumSeedTime = 432000; // 120 hours
|
||||
release.Title = row.name;
|
||||
release.Category = _categories.MapTrackerCatToNewznab(row.category.ToString());
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(row.category.ToString());
|
||||
release.Size = row.size;
|
||||
release.Seeders = row.seeders;
|
||||
release.Peers = row.leechers + release.Seeders;
|
||||
|
||||
@@ -24,12 +24,12 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new FileListRequestGenerator() { Settings = Settings, BaseUrl = BaseUrl };
|
||||
return new FileListRequestGenerator() { Settings = Settings, BaseUrl = BaseUrl, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new FileListParser(Settings, BaseUrl);
|
||||
return new FileListParser(Settings, BaseUrl, Capabilities.Categories);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
|
||||
@@ -22,5 +22,6 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
public bool DoubleUp { get; set; }
|
||||
[JsonProperty(PropertyName = "upload_date")]
|
||||
public DateTime UploadDate { get; set; }
|
||||
public string Category { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
{
|
||||
private readonly string _baseUrl;
|
||||
private readonly FileListSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public FileListParser(FileListSettings settings, string baseUrl)
|
||||
public FileListParser(FileListSettings settings, string baseUrl, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_baseUrl = baseUrl;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
@@ -38,11 +40,6 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
var flags = new List<IndexerFlag>();
|
||||
|
||||
if (result.FreeLeech)
|
||||
{
|
||||
flags.Add(IndexerFlag.FreeLeech);
|
||||
}
|
||||
|
||||
var imdbId = 0;
|
||||
if (result.ImdbId != null && result.ImdbId.Length > 2)
|
||||
{
|
||||
@@ -57,6 +54,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
Guid = string.Format("FileList-{0}", id),
|
||||
Title = result.Name,
|
||||
Size = result.Size,
|
||||
Categories = _categories.MapTrackerCatDescToNewznab(result.Category),
|
||||
DownloadUrl = GetDownloadUrl(id),
|
||||
InfoUrl = GetInfoUrl(id),
|
||||
Seeders = result.Seeders,
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
{
|
||||
public string BaseUrl { get; set; }
|
||||
public FileListSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
@@ -20,7 +21,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=imdb&query={0}", searchCriteria.ImdbId)));
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=imdb&query={0}", searchCriteria.FullImdbId)));
|
||||
}
|
||||
else if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
@@ -57,7 +58,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=imdb&query={0}&season={1}&episode={2}", searchCriteria.ImdbId, searchCriteria.Season, searchCriteria.Episode)));
|
||||
pageableRequests.Add(GetRequest("search-torrents", searchCriteria.Categories, string.Format("&type=imdb&query={0}&season={1}&episode={2}", searchCriteria.FullImdbId, searchCriteria.Season, searchCriteria.Episode)));
|
||||
}
|
||||
else if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
@@ -106,7 +107,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchType, int[] categories, string parameters)
|
||||
{
|
||||
var categoriesQuery = string.Join(",", categories.Distinct());
|
||||
var categoriesQuery = string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(categories));
|
||||
|
||||
var baseUrl = string.Format("{0}/api.php?action={1}&category={2}&username={3}&passkey={4}{5}", BaseUrl.TrimEnd('/'), searchType, categoriesQuery, Settings.Username.Trim(), Settings.Passkey.Trim(), parameters);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Indexers.FileList
|
||||
{
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)]
|
||||
[FieldDefinition(0, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Passkey", Privacy = PrivacyLevel.ApiKey)]
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
{
|
||||
public class GazelleParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly GazelleSettings _settings;
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
private readonly string _baseUrl;
|
||||
protected readonly GazelleSettings _settings;
|
||||
protected readonly IndexerCapabilities _capabilities;
|
||||
protected readonly string _baseUrl;
|
||||
|
||||
public GazelleParser(GazelleSettings settings, IndexerCapabilities capabilities, string baseUrl)
|
||||
{
|
||||
@@ -88,11 +88,11 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
var category = torrent.Category;
|
||||
if (category == null || category.Contains("Select Category"))
|
||||
{
|
||||
release.Category = _capabilities.Categories.MapTrackerCatToNewznab("1");
|
||||
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Category = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
|
||||
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
@@ -120,11 +120,11 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
var category = result.Category;
|
||||
if (category == null || category.Contains("Select Category"))
|
||||
{
|
||||
release.Category = _capabilities.Categories.MapTrackerCatToNewznab("1");
|
||||
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Category = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
|
||||
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
@@ -138,11 +138,12 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private string GetDownloadUrl(int torrentId)
|
||||
protected virtual string GetDownloadUrl(int torrentId)
|
||||
{
|
||||
var url = new HttpUri(_baseUrl)
|
||||
.CombinePath("/torrents.php")
|
||||
.AddQueryParam("action", "download")
|
||||
.AddQueryParam("useToken", _settings.UseFreeleechToken ? "1" : "0")
|
||||
.AddQueryParam("id", torrentId);
|
||||
|
||||
return url.FullUri;
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
public Logger Logger { get; set; }
|
||||
|
||||
protected virtual string APIUrl => BaseUrl + "ajax.php";
|
||||
protected virtual string DownloadUrl => BaseUrl + "torrents.php?action=download&usetoken=" + (Settings.UseFreeleechToken ? "1" : "0") + "&id=";
|
||||
protected virtual string DetailsUrl => BaseUrl + "torrents.php?torrentid=";
|
||||
protected virtual bool ImdbInTags => false;
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
|
||||
@@ -25,10 +25,10 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
||||
public string AuthKey;
|
||||
public string PassKey;
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Username", Privacy = PrivacyLevel.UserName)]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Password", Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use Freeleech Token")]
|
||||
|
||||
426
src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs
Normal file
426
src/NzbDrone.Core/Indexers/Definitions/GazelleGames.cs
Normal file
@@ -0,0 +1,426 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using AngleSharp.Html.Parser;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class GazelleGames : TorrentIndexerBase<GazelleGamesSettings>
|
||||
{
|
||||
public override string Name => "GazelleGames";
|
||||
public override string BaseUrl => "https://gazellegames.net/";
|
||||
public override string Description => "A gaming tracker.";
|
||||
public override string Language => "en-us";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public GazelleGames(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new GazelleGamesRequestGenerator() { Settings = Settings, Capabilities = Capabilities, BaseUrl = BaseUrl };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new GazelleGamesParser(Settings, Capabilities.Categories, BaseUrl);
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> GetCookies()
|
||||
{
|
||||
return CookieUtil.CookieHeaderToDictionary(Settings.Cookie);
|
||||
}
|
||||
|
||||
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
|
||||
{
|
||||
if (httpResponse.HasHttpRedirect && httpResponse.RedirectUrl.EndsWith("login.php"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping("Mac", NewznabStandardCategory.ConsoleOther, "Mac");
|
||||
caps.Categories.AddCategoryMapping("iOS", NewznabStandardCategory.PCMobileiOS, "iOS");
|
||||
caps.Categories.AddCategoryMapping("Apple Bandai Pippin", NewznabStandardCategory.ConsoleOther, "Apple Bandai Pippin");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Android", NewznabStandardCategory.PCMobileAndroid, "Android");
|
||||
|
||||
caps.Categories.AddCategoryMapping("DOS", NewznabStandardCategory.PCGames, "DOS");
|
||||
caps.Categories.AddCategoryMapping("Windows", NewznabStandardCategory.PCGames, "Windows");
|
||||
caps.Categories.AddCategoryMapping("Xbox", NewznabStandardCategory.ConsoleXBox, "Xbox");
|
||||
caps.Categories.AddCategoryMapping("Xbox 360", NewznabStandardCategory.ConsoleXBox360, "Xbox 360");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Game Boy", NewznabStandardCategory.ConsoleOther, "Game Boy");
|
||||
caps.Categories.AddCategoryMapping("Game Boy Advance", NewznabStandardCategory.ConsoleOther, "Game Boy Advance");
|
||||
caps.Categories.AddCategoryMapping("Game Boy Color", NewznabStandardCategory.ConsoleOther, "Game Boy Color");
|
||||
caps.Categories.AddCategoryMapping("NES", NewznabStandardCategory.ConsoleOther, "NES");
|
||||
caps.Categories.AddCategoryMapping("Nintendo 64", NewznabStandardCategory.ConsoleOther, "Nintendo 64");
|
||||
caps.Categories.AddCategoryMapping("Nintendo 3DS", NewznabStandardCategory.ConsoleOther, "Nintendo 3DS");
|
||||
caps.Categories.AddCategoryMapping("New Nintendo 3DS", NewznabStandardCategory.ConsoleOther, "New Nintendo 3DS");
|
||||
caps.Categories.AddCategoryMapping("Nintendo DS", NewznabStandardCategory.ConsoleNDS, "Nintendo DS");
|
||||
caps.Categories.AddCategoryMapping("Nintendo GameCube", NewznabStandardCategory.ConsoleOther, "Nintendo GameCube");
|
||||
caps.Categories.AddCategoryMapping("Pokemon Mini", NewznabStandardCategory.ConsoleOther, "Pokemon Mini");
|
||||
caps.Categories.AddCategoryMapping("SNES", NewznabStandardCategory.ConsoleOther, "SNES");
|
||||
caps.Categories.AddCategoryMapping("Virtual Boy", NewznabStandardCategory.ConsoleOther, "Virtual Boy");
|
||||
caps.Categories.AddCategoryMapping("Wii", NewznabStandardCategory.ConsoleWii, "Wii");
|
||||
caps.Categories.AddCategoryMapping("Wii U", NewznabStandardCategory.ConsoleWiiU, "Wii U");
|
||||
|
||||
caps.Categories.AddCategoryMapping("PlayStation 1", NewznabStandardCategory.ConsoleOther, "PlayStation 1");
|
||||
caps.Categories.AddCategoryMapping("PlayStation 2", NewznabStandardCategory.ConsoleOther, "PlayStation 2");
|
||||
caps.Categories.AddCategoryMapping("PlayStation 3", NewznabStandardCategory.ConsolePS3, "PlayStation 3");
|
||||
caps.Categories.AddCategoryMapping("PlayStation 4", NewznabStandardCategory.ConsolePS4, "PlayStation 4");
|
||||
caps.Categories.AddCategoryMapping("PlayStation Portable", NewznabStandardCategory.ConsolePSP, "PlayStation Portable");
|
||||
caps.Categories.AddCategoryMapping("PlayStation Vita", NewznabStandardCategory.ConsolePSVita, "PlayStation Vita");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Dreamcast", NewznabStandardCategory.ConsoleOther, "Dreamcast");
|
||||
caps.Categories.AddCategoryMapping("Game Gear", NewznabStandardCategory.ConsoleOther, "Game Gear");
|
||||
caps.Categories.AddCategoryMapping("Master System", NewznabStandardCategory.ConsoleOther, "Master System");
|
||||
caps.Categories.AddCategoryMapping("Mega Drive", NewznabStandardCategory.ConsoleOther, "Mega Drive");
|
||||
caps.Categories.AddCategoryMapping("Pico", NewznabStandardCategory.ConsoleOther, "Pico");
|
||||
caps.Categories.AddCategoryMapping("Saturn", NewznabStandardCategory.ConsoleOther, "Saturn");
|
||||
caps.Categories.AddCategoryMapping("SG-1000", NewznabStandardCategory.ConsoleOther, "SG-1000");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Atari 2600", NewznabStandardCategory.ConsoleOther, "Atari 2600");
|
||||
caps.Categories.AddCategoryMapping("Atari 5200", NewznabStandardCategory.ConsoleOther, "Atari 5200");
|
||||
caps.Categories.AddCategoryMapping("Atari 7800", NewznabStandardCategory.ConsoleOther, "Atari 7800");
|
||||
caps.Categories.AddCategoryMapping("Atari Jaguar", NewznabStandardCategory.ConsoleOther, "Atari Jaguar");
|
||||
caps.Categories.AddCategoryMapping("Atari Lynx", NewznabStandardCategory.ConsoleOther, "Atari Lynx");
|
||||
caps.Categories.AddCategoryMapping("Atari ST", NewznabStandardCategory.ConsoleOther, "Atari ST");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Amstrad CPC", NewznabStandardCategory.ConsoleOther, "Amstrad CPC");
|
||||
|
||||
caps.Categories.AddCategoryMapping("ZX Spectrum", NewznabStandardCategory.ConsoleOther, "ZX Spectrum");
|
||||
|
||||
caps.Categories.AddCategoryMapping("MSX", NewznabStandardCategory.ConsoleOther, "MSX");
|
||||
caps.Categories.AddCategoryMapping("MSX 2", NewznabStandardCategory.ConsoleOther, "MSX 2");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Game.com", NewznabStandardCategory.ConsoleOther, "Game.com");
|
||||
caps.Categories.AddCategoryMapping("Gizmondo", NewznabStandardCategory.ConsoleOther, "Gizmondo");
|
||||
|
||||
caps.Categories.AddCategoryMapping("V.Smile", NewznabStandardCategory.ConsoleOther, "V.Smile");
|
||||
caps.Categories.AddCategoryMapping("CreatiVision", NewznabStandardCategory.ConsoleOther, "CreatiVision");
|
||||
|
||||
caps.Categories.AddCategoryMapping("Board Game", NewznabStandardCategory.ConsoleOther, "Board Game");
|
||||
caps.Categories.AddCategoryMapping("Card Game", NewznabStandardCategory.ConsoleOther, "Card Game");
|
||||
caps.Categories.AddCategoryMapping("Miniature Wargames", NewznabStandardCategory.ConsoleOther, "Miniature Wargames");
|
||||
caps.Categories.AddCategoryMapping("Pen and Paper RPG", NewznabStandardCategory.ConsoleOther, "Pen and Paper RPG");
|
||||
|
||||
caps.Categories.AddCategoryMapping("3DO", NewznabStandardCategory.ConsoleOther, "3DO");
|
||||
caps.Categories.AddCategoryMapping("Bandai WonderSwan", NewznabStandardCategory.ConsoleOther, "Bandai WonderSwan");
|
||||
caps.Categories.AddCategoryMapping("Bandai WonderSwan Color", NewznabStandardCategory.ConsoleOther, "Bandai WonderSwan Color");
|
||||
caps.Categories.AddCategoryMapping("Casio Loopy", NewznabStandardCategory.ConsoleOther, "Casio Loopy");
|
||||
caps.Categories.AddCategoryMapping("Casio PV-1000", NewznabStandardCategory.ConsoleOther, "Casio PV-1000");
|
||||
caps.Categories.AddCategoryMapping("Colecovision", NewznabStandardCategory.ConsoleOther, "Colecovision");
|
||||
caps.Categories.AddCategoryMapping("Commodore 64", NewznabStandardCategory.ConsoleOther, "Commodore 64");
|
||||
caps.Categories.AddCategoryMapping("Commodore 128", NewznabStandardCategory.ConsoleOther, "Commodore 128");
|
||||
caps.Categories.AddCategoryMapping("Commodore Amiga", NewznabStandardCategory.ConsoleOther, "Commodore Amiga");
|
||||
caps.Categories.AddCategoryMapping("Commodore Plus-4", NewznabStandardCategory.ConsoleOther, "Commodore Plus-4");
|
||||
caps.Categories.AddCategoryMapping("Commodore VIC-20", NewznabStandardCategory.ConsoleOther, "Commodore VIC-20");
|
||||
caps.Categories.AddCategoryMapping("Emerson Arcadia 2001", NewznabStandardCategory.ConsoleOther, "Emerson Arcadia 2001");
|
||||
caps.Categories.AddCategoryMapping("Entex Adventure Vision", NewznabStandardCategory.ConsoleOther, "Entex Adventure Vision");
|
||||
caps.Categories.AddCategoryMapping("Epoch Super Casette Vision", NewznabStandardCategory.ConsoleOther, "Epoch Super Casette Vision");
|
||||
caps.Categories.AddCategoryMapping("Fairchild Channel F", NewznabStandardCategory.ConsoleOther, "Fairchild Channel F");
|
||||
caps.Categories.AddCategoryMapping("Funtech Super Acan", NewznabStandardCategory.ConsoleOther, "Funtech Super Acan");
|
||||
caps.Categories.AddCategoryMapping("GamePark GP32", NewznabStandardCategory.ConsoleOther, "GamePark GP32");
|
||||
caps.Categories.AddCategoryMapping("General Computer Vectrex", NewznabStandardCategory.ConsoleOther, "General Computer Vectrex");
|
||||
caps.Categories.AddCategoryMapping("Interactive DVD", NewznabStandardCategory.ConsoleOther, "Interactive DVD");
|
||||
caps.Categories.AddCategoryMapping("Linux", NewznabStandardCategory.ConsoleOther, "Linux");
|
||||
caps.Categories.AddCategoryMapping("Hartung Game Master", NewznabStandardCategory.ConsoleOther, "Hartung Game Master");
|
||||
caps.Categories.AddCategoryMapping("Magnavox-Phillips Odyssey", NewznabStandardCategory.ConsoleOther, "Magnavox-Phillips Odyssey");
|
||||
caps.Categories.AddCategoryMapping("Mattel Intellivision", NewznabStandardCategory.ConsoleOther, "Mattel Intellivision");
|
||||
caps.Categories.AddCategoryMapping("Memotech MTX", NewznabStandardCategory.ConsoleOther, "Memotech MTX");
|
||||
caps.Categories.AddCategoryMapping("Miles Gordon Sam Coupe", NewznabStandardCategory.ConsoleOther, "Miles Gordon Sam Coupe");
|
||||
caps.Categories.AddCategoryMapping("NEC PC-98", NewznabStandardCategory.ConsoleOther, "NEC PC-98");
|
||||
caps.Categories.AddCategoryMapping("NEC PC-FX", NewznabStandardCategory.ConsoleOther, "NEC PC-FX");
|
||||
caps.Categories.AddCategoryMapping("NEC SuperGrafx", NewznabStandardCategory.ConsoleOther, "NEC SuperGrafx");
|
||||
caps.Categories.AddCategoryMapping("NEC TurboGrafx-16", NewznabStandardCategory.ConsoleOther, "NEC TurboGrafx-16");
|
||||
caps.Categories.AddCategoryMapping("Nokia N-Gage", NewznabStandardCategory.ConsoleOther, "Nokia N-Gage");
|
||||
caps.Categories.AddCategoryMapping("Ouya", NewznabStandardCategory.ConsoleOther, "Ouya");
|
||||
caps.Categories.AddCategoryMapping("Philips Videopac+", NewznabStandardCategory.ConsoleOther, "Philips Videopac+");
|
||||
caps.Categories.AddCategoryMapping("Phone/PDA", NewznabStandardCategory.ConsoleOther, "Phone/PDA");
|
||||
caps.Categories.AddCategoryMapping("RCA Studio II", NewznabStandardCategory.ConsoleOther, "RCA Studio II");
|
||||
caps.Categories.AddCategoryMapping("Sharp X1", NewznabStandardCategory.ConsoleOther, "Sharp X1");
|
||||
caps.Categories.AddCategoryMapping("Sharp X68000", NewznabStandardCategory.ConsoleOther, "Sharp X68000");
|
||||
caps.Categories.AddCategoryMapping("SNK Neo Geo", NewznabStandardCategory.ConsoleOther, "SNK Neo Geo");
|
||||
caps.Categories.AddCategoryMapping("SNK Neo Geo Pocket", NewznabStandardCategory.ConsoleOther, "SNK Neo Geo Pocket");
|
||||
caps.Categories.AddCategoryMapping("Taito Type X", NewznabStandardCategory.ConsoleOther, "Taito Type X");
|
||||
caps.Categories.AddCategoryMapping("Tandy Color Computer", NewznabStandardCategory.ConsoleOther, "Tandy Color Computer");
|
||||
caps.Categories.AddCategoryMapping("Tangerine Oric", NewznabStandardCategory.ConsoleOther, "Tangerine Oric");
|
||||
caps.Categories.AddCategoryMapping("Thomson MO5", NewznabStandardCategory.ConsoleOther, "Thomson MO5");
|
||||
caps.Categories.AddCategoryMapping("Watara Supervision", NewznabStandardCategory.ConsoleOther, "Watara Supervision");
|
||||
caps.Categories.AddCategoryMapping("Retro - Other", NewznabStandardCategory.ConsoleOther, "Retro - Other");
|
||||
|
||||
caps.Categories.AddCategoryMapping("OST", NewznabStandardCategory.AudioOther, "OST");
|
||||
caps.Categories.AddCategoryMapping("Applications", NewznabStandardCategory.PC0day, "Applications");
|
||||
caps.Categories.AddCategoryMapping("E-Books", NewznabStandardCategory.BooksEBook, "E-Books");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class GazelleGamesRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public GazelleGamesSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
public GazelleGamesRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories)
|
||||
{
|
||||
var searchUrl = string.Format("{0}/torrents.php", BaseUrl.TrimEnd('/'));
|
||||
|
||||
var searchString = term;
|
||||
|
||||
var searchType = Settings.SearchGroupNames ? "groupname" : "searchstr";
|
||||
|
||||
var queryCollection = new NameValueCollection
|
||||
{
|
||||
{ searchType, searchString },
|
||||
{ "order_by", "time" },
|
||||
{ "order_way", "desc" },
|
||||
{ "action", "basic" },
|
||||
{ "searchsubmit", "1" }
|
||||
};
|
||||
|
||||
var i = 0;
|
||||
|
||||
foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories))
|
||||
{
|
||||
queryCollection.Add($"artistcheck[{i++}]", cat);
|
||||
}
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class GazelleGamesParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly GazelleGamesSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly string _baseUrl;
|
||||
|
||||
public GazelleGamesParser(GazelleGamesSettings settings, IndexerCapabilitiesCategories categories, string baseurl)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
_baseUrl = baseurl;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
var rowsSelector = ".torrent_table > tbody > tr";
|
||||
|
||||
var searchResultParser = new HtmlParser();
|
||||
var searchResultDocument = searchResultParser.ParseDocument(indexerResponse.Content);
|
||||
var rows = searchResultDocument.QuerySelectorAll(rowsSelector);
|
||||
|
||||
var stickyGroup = false;
|
||||
string categoryStr;
|
||||
ICollection<IndexerCategory> groupCategory = null;
|
||||
string groupTitle = null;
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (row.ClassList.Contains("torrent"))
|
||||
{
|
||||
// garbage rows
|
||||
continue;
|
||||
}
|
||||
else if (row.ClassList.Contains("group"))
|
||||
{
|
||||
stickyGroup = row.ClassList.Contains("sticky");
|
||||
var dispalyname = row.QuerySelector("#displayname");
|
||||
var qCat = row.QuerySelector("td.cats_col > div");
|
||||
categoryStr = qCat.GetAttribute("title");
|
||||
var qArtistLink = dispalyname.QuerySelector("#groupplatform > a");
|
||||
if (qArtistLink != null)
|
||||
{
|
||||
categoryStr = ParseUtil.GetArgumentFromQueryString(qArtistLink.GetAttribute("href"), "artistname");
|
||||
}
|
||||
|
||||
groupCategory = _categories.MapTrackerCatToNewznab(categoryStr);
|
||||
|
||||
var qDetailsLink = dispalyname.QuerySelector("#groupname > a");
|
||||
groupTitle = qDetailsLink.TextContent;
|
||||
}
|
||||
else if (row.ClassList.Contains("group_torrent"))
|
||||
{
|
||||
if (row.QuerySelector("td.edition_info") != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sizeString = row.QuerySelector("td:nth-child(4)").TextContent;
|
||||
if (string.IsNullOrEmpty(sizeString))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]");
|
||||
var title = qDetailsLink.TextContent.Replace(", Freeleech!", "").Replace(", Neutral Leech!", "");
|
||||
|
||||
//if (stickyGroup && (query.ImdbID == null || !NewznabStandardCategory.MovieSearchImdbAvailable) && !query.MatchQueryStringAND(title)) // AND match for sticky releases
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
var qDescription = qDetailsLink.QuerySelector("span.torrent_info_tags");
|
||||
var qDLLink = row.QuerySelector("a[href^=\"torrents.php?action=download\"]");
|
||||
var qTime = row.QuerySelector("span.time");
|
||||
var qGrabs = row.QuerySelector("td:nth-child(5)");
|
||||
var qSeeders = row.QuerySelector("td:nth-child(6)");
|
||||
var qLeechers = row.QuerySelector("td:nth-child(7)");
|
||||
var qFreeLeech = row.QuerySelector("strong.freeleech_label");
|
||||
var qNeutralLeech = row.QuerySelector("strong.neutralleech_label");
|
||||
var time = qTime.GetAttribute("title");
|
||||
var link = _baseUrl + qDLLink.GetAttribute("href");
|
||||
var seeders = ParseUtil.CoerceInt(qSeeders.TextContent);
|
||||
var publishDate = DateTime.SpecifyKind(
|
||||
DateTime.ParseExact(time, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture),
|
||||
DateTimeKind.Unspecified).ToLocalTime();
|
||||
var details = _baseUrl + qDetailsLink.GetAttribute("href");
|
||||
var grabs = ParseUtil.CoerceInt(qGrabs.TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(qLeechers.TextContent);
|
||||
var size = ReleaseInfo.GetBytes(sizeString);
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 288000, //80 hours
|
||||
Categories = groupCategory,
|
||||
PublishDate = publishDate,
|
||||
Size = size,
|
||||
InfoUrl = details,
|
||||
DownloadUrl = link,
|
||||
Guid = link,
|
||||
Grabs = grabs,
|
||||
Seeders = seeders,
|
||||
Peers = leechers + seeders,
|
||||
Title = title,
|
||||
Description = qDescription?.TextContent,
|
||||
UploadVolumeFactor = qNeutralLeech is null ? 1 : 0,
|
||||
DownloadVolumeFactor = qFreeLeech != null || qNeutralLeech != null ? 0 : 1
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class GazelleGamesSettingsValidator : AbstractValidator<GazelleGamesSettings>
|
||||
{
|
||||
public GazelleGamesSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Cookie).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class GazelleGamesSettings : IProviderConfig
|
||||
{
|
||||
private static readonly GazelleGamesSettingsValidator Validator = new GazelleGamesSettingsValidator();
|
||||
|
||||
public GazelleGamesSettings()
|
||||
{
|
||||
Cookie = "";
|
||||
SearchGroupNames = false;
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Cookie", HelpText = "Login cookie from website")]
|
||||
public string Cookie { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Search Group Names", Type = FieldType.Checkbox, HelpText = "Search Group Names Only")]
|
||||
public bool SearchGroupNames { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,11 +57,6 @@ namespace NzbDrone.Core.Indexers.HDBits
|
||||
|
||||
var flags = new List<IndexerFlag>();
|
||||
|
||||
if (result.FreeLeech == "yes")
|
||||
{
|
||||
flags.Add(IndexerFlag.FreeLeech);
|
||||
}
|
||||
|
||||
if (internalRelease)
|
||||
{
|
||||
flags.Add(IndexerFlag.Internal);
|
||||
@@ -83,6 +78,8 @@ namespace NzbDrone.Core.Indexers.HDBits
|
||||
Internal = internalRelease,
|
||||
ImdbId = result.ImdbInfo?.Id ?? 0,
|
||||
TvdbId = result.TvdbInfo?.Id ?? 0,
|
||||
DownloadVolumeFactor = result.FreeLeech == "yes" ? 0 : 1,
|
||||
UploadVolumeFactor = 1,
|
||||
IndexerFlags = flags
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Indexers.HDBits
|
||||
Mediums = System.Array.Empty<int>();
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)]
|
||||
[FieldDefinition(0, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)]
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -188,7 +188,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -335,7 +335,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
DownloadUrl = link.AbsoluteUri,
|
||||
InfoUrl = details.AbsoluteUri,
|
||||
PublishDate = publishDate,
|
||||
Category = cat,
|
||||
Categories = cat,
|
||||
ImdbId = imdb ?? 0,
|
||||
Size = size,
|
||||
Grabs = grabs,
|
||||
@@ -375,10 +375,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Password = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Site password", Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace NzbDrone.Core.Indexers.Headphones
|
||||
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
public virtual NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -42,6 +42,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
return new IPTorrentsParser(Settings, Capabilities.Categories, BaseUrl);
|
||||
}
|
||||
|
||||
protected override IDictionary<string, string> GetCookies()
|
||||
{
|
||||
return CookieUtil.CookieHeaderToDictionary(Settings.Cookie);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
@@ -120,6 +125,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(86, NewznabStandardCategory.PC0day, "Appz/Non-English");
|
||||
caps.Categories.AddCategoryMapping(64, NewznabStandardCategory.AudioAudiobook, "AudioBook");
|
||||
caps.Categories.AddCategoryMapping(35, NewznabStandardCategory.Books, "Books");
|
||||
caps.Categories.AddCategoryMapping(102, NewznabStandardCategory.Books, "Books/Non-English");
|
||||
caps.Categories.AddCategoryMapping(94, NewznabStandardCategory.BooksComics, "Books/Comics");
|
||||
caps.Categories.AddCategoryMapping(95, NewznabStandardCategory.BooksOther, "Books/Educational");
|
||||
caps.Categories.AddCategoryMapping(98, NewznabStandardCategory.Other, "Other/Fonts");
|
||||
@@ -172,11 +178,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
foreach (var cookie in CookieUtil.CookieHeaderToDictionary(Settings.Cookie))
|
||||
{
|
||||
request.HttpRequest.Cookies.Add(cookie.Key, cookie.Value);
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
@@ -184,7 +185,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -202,7 +203,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -307,7 +308,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
DownloadUrl = link.AbsoluteUri,
|
||||
InfoUrl = details.AbsoluteUri,
|
||||
PublishDate = publishDate,
|
||||
Category = cat,
|
||||
Categories = cat,
|
||||
Size = size,
|
||||
Files = files,
|
||||
Grabs = grabs,
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -204,7 +204,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -290,7 +290,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
catLink = catLink.Substring(catSplit + 9);
|
||||
}
|
||||
|
||||
release.Category = _categories.MapTrackerCatToNewznab(catLink);
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(catLink);
|
||||
|
||||
var grabs = row.QuerySelector("td:nth-child(6)").TextContent;
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
@@ -345,10 +345,10 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Password = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Site password", Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(2, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -198,7 +198,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
InfoUrl = details,
|
||||
Guid = details,
|
||||
PublishDate = publishDate,
|
||||
Category = _categories.MapTrackerCatToNewznab(torrent.Category.ToString()),
|
||||
Categories = _categories.MapTrackerCatToNewznab(torrent.Category.ToString()),
|
||||
Size = torrent.Size,
|
||||
Seeders = torrent.Seeders,
|
||||
Peers = torrent.Seeders + torrent.PartialSeeders + torrent.Leechers,
|
||||
|
||||
@@ -329,7 +329,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
}
|
||||
|
||||
var category = item.Category;
|
||||
release.Category = _categories.MapTrackerCatToNewznab(category);
|
||||
release.Categories = _categories.MapTrackerCatToNewznab(category);
|
||||
|
||||
release.DownloadUrl = _baseUrl + "/tor/download.php?tid=" + id;
|
||||
release.InfoUrl = _baseUrl + "/t/" + id;
|
||||
@@ -375,7 +375,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
MamId = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Mam Id", HelpText = "Mam Session Id")]
|
||||
[FieldDefinition(1, Label = "Mam Id", HelpText = "Mam Session Id (Created Under Profile -> Security)")]
|
||||
public string MamId { get; set; }
|
||||
|
||||
[FieldDefinition(2, Type = FieldType.Checkbox, Label = "Exclude VIP", HelpText = "Exclude VIP Torrents from search results")]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user