mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
New: OnGrab Notifications
This commit is contained in:
@@ -71,6 +71,19 @@ class SearchIndexRow extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
onSavePress = () => {
|
||||
const {
|
||||
downloadUrl,
|
||||
fileName,
|
||||
onSavePress
|
||||
} = this.props;
|
||||
|
||||
onSavePress({
|
||||
downloadUrl,
|
||||
fileName
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
@@ -85,7 +98,6 @@ class SearchIndexRow extends Component {
|
||||
publishDate,
|
||||
title,
|
||||
infoUrl,
|
||||
downloadUrl,
|
||||
indexer,
|
||||
size,
|
||||
files,
|
||||
@@ -300,7 +312,7 @@ class SearchIndexRow extends Component {
|
||||
className={styles.downloadLink}
|
||||
name={icons.SAVE}
|
||||
title={translate('Save')}
|
||||
to={downloadUrl}
|
||||
onPress={this.onSavePress}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
@@ -323,6 +335,7 @@ SearchIndexRow.propTypes = {
|
||||
ageMinutes: PropTypes.number.isRequired,
|
||||
publishDate: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
fileName: PropTypes.string.isRequired,
|
||||
infoUrl: PropTypes.string.isRequired,
|
||||
downloadUrl: PropTypes.string.isRequired,
|
||||
indexerId: PropTypes.number.isRequired,
|
||||
@@ -335,6 +348,7 @@ SearchIndexRow.propTypes = {
|
||||
indexerFlags: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onGrabPress: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
isGrabbing: PropTypes.bool.isRequired,
|
||||
isGrabbed: PropTypes.bool.isRequired,
|
||||
grabError: PropTypes.string,
|
||||
|
@@ -51,7 +51,8 @@ class SearchIndexTable extends Component {
|
||||
timeFormat,
|
||||
selectedState,
|
||||
onSelectedChange,
|
||||
onGrabPress
|
||||
onGrabPress,
|
||||
onSavePress
|
||||
} = this.props;
|
||||
|
||||
const release = items[rowIndex];
|
||||
@@ -71,6 +72,7 @@ class SearchIndexTable extends Component {
|
||||
longDateFormat={longDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
onGrabPress={onGrabPress}
|
||||
onSavePress={onSavePress}
|
||||
/>
|
||||
</VirtualTableRow>
|
||||
);
|
||||
@@ -134,6 +136,7 @@ SearchIndexTable.propTypes = {
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onGrabPress: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
allSelected: PropTypes.bool.isRequired,
|
||||
allUnselected: PropTypes.bool.isRequired,
|
||||
selectedState: PropTypes.object.isRequired,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { grabRelease, setReleasesSort } from 'Store/Actions/releaseActions';
|
||||
import { grabRelease, saveRelease, setReleasesSort } from 'Store/Actions/releaseActions';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import SearchIndexTable from './SearchIndexTable';
|
||||
|
||||
@@ -25,6 +25,9 @@ function createMapDispatchToProps(dispatch, props) {
|
||||
},
|
||||
onGrabPress(payload) {
|
||||
dispatch(grabRelease(payload));
|
||||
},
|
||||
onSavePress(payload) {
|
||||
dispatch(saveRelease(payload));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -56,17 +56,9 @@ class Notification extends Component {
|
||||
id,
|
||||
name,
|
||||
onGrab,
|
||||
onDownload,
|
||||
onUpgrade,
|
||||
onRename,
|
||||
onDelete,
|
||||
onHealthIssue,
|
||||
onApplicationUpdate,
|
||||
supportsOnGrab,
|
||||
supportsOnDownload,
|
||||
supportsOnUpgrade,
|
||||
supportsOnRename,
|
||||
supportsOnDelete,
|
||||
supportsOnHealthIssue,
|
||||
supportsOnApplicationUpdate
|
||||
} = this.props;
|
||||
@@ -88,34 +80,6 @@ class Notification extends Component {
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnDelete && onDelete &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnDelete')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnDownload && onDownload &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnImport')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnUpgrade && onDownload && onUpgrade &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnUpgrade')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnRename && onRename &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnRename')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnHealthIssue && onHealthIssue &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
@@ -132,7 +96,7 @@ class Notification extends Component {
|
||||
}
|
||||
|
||||
{
|
||||
!onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete && !onApplicationUpdate ?
|
||||
!onGrab && !onHealthIssue && !onApplicationUpdate ?
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
@@ -167,17 +131,9 @@ Notification.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onGrab: PropTypes.bool.isRequired,
|
||||
onDownload: PropTypes.bool.isRequired,
|
||||
onUpgrade: PropTypes.bool.isRequired,
|
||||
onRename: PropTypes.bool.isRequired,
|
||||
onDelete: PropTypes.bool.isRequired,
|
||||
onHealthIssue: PropTypes.bool.isRequired,
|
||||
onApplicationUpdate: PropTypes.bool.isRequired,
|
||||
supportsOnGrab: PropTypes.bool.isRequired,
|
||||
supportsOnDownload: PropTypes.bool.isRequired,
|
||||
supportsOnDelete: PropTypes.bool.isRequired,
|
||||
supportsOnUpgrade: PropTypes.bool.isRequired,
|
||||
supportsOnRename: PropTypes.bool.isRequired,
|
||||
supportsOnHealthIssue: PropTypes.bool.isRequired,
|
||||
supportsOnApplicationUpdate: PropTypes.bool.isRequired,
|
||||
onConfirmDeleteNotification: PropTypes.func.isRequired
|
||||
|
@@ -15,8 +15,11 @@ function NotificationEventItems(props) {
|
||||
} = props;
|
||||
|
||||
const {
|
||||
onGrab,
|
||||
onHealthIssue,
|
||||
onApplicationUpdate,
|
||||
supportsOnGrab,
|
||||
includeManualGrabs,
|
||||
supportsOnHealthIssue,
|
||||
includeHealthWarnings,
|
||||
supportsOnApplicationUpdate
|
||||
@@ -31,6 +34,31 @@ function NotificationEventItems(props) {
|
||||
link="https://wiki.servarr.com/prowlarr/settings#connections"
|
||||
/>
|
||||
<div className={styles.events}>
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onGrab"
|
||||
helpText={translate('OnGrabHelpText')}
|
||||
isDisabled={!supportsOnGrab.value}
|
||||
{...onGrab}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
onGrab.value &&
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeManualGrabs"
|
||||
helpText={translate('IncludeManualGrabsHelpText')}
|
||||
isDisabled={!supportsOnGrab.value}
|
||||
{...includeManualGrabs}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
|
@@ -103,9 +103,6 @@ export default {
|
||||
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
|
||||
return selectProviderSchema(state, section, payload, (selectedSchema) => {
|
||||
selectedSchema.onGrab = selectedSchema.supportsOnGrab;
|
||||
selectedSchema.onDownload = selectedSchema.supportsOnDownload;
|
||||
selectedSchema.onUpgrade = selectedSchema.supportsOnUpgrade;
|
||||
selectedSchema.onRename = selectedSchema.supportsOnRename;
|
||||
selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate;
|
||||
|
||||
return selectedSchema;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import { createAction } from 'redux-actions';
|
||||
import { batchActions } from 'redux-batched-actions';
|
||||
import { filterBuilderTypes, filterBuilderValueTypes, sortDirections } from 'Helpers/Props';
|
||||
@@ -229,6 +230,7 @@ export const CANCEL_FETCH_RELEASES = 'releases/cancelFetchReleases';
|
||||
export const SET_RELEASES_SORT = 'releases/setReleasesSort';
|
||||
export const CLEAR_RELEASES = 'releases/clearReleases';
|
||||
export const GRAB_RELEASE = 'releases/grabRelease';
|
||||
export const SAVE_RELEASE = 'releases/saveRelease';
|
||||
export const BULK_GRAB_RELEASES = 'release/bulkGrabReleases';
|
||||
export const UPDATE_RELEASE = 'releases/updateRelease';
|
||||
export const SET_RELEASES_FILTER = 'releases/setReleasesFilter';
|
||||
@@ -243,6 +245,7 @@ export const cancelFetchReleases = createThunk(CANCEL_FETCH_RELEASES);
|
||||
export const setReleasesSort = createAction(SET_RELEASES_SORT);
|
||||
export const clearReleases = createAction(CLEAR_RELEASES);
|
||||
export const grabRelease = createThunk(GRAB_RELEASE);
|
||||
export const saveRelease = createThunk(SAVE_RELEASE);
|
||||
export const bulkGrabReleases = createThunk(BULK_GRAB_RELEASES);
|
||||
export const updateRelease = createAction(UPDATE_RELEASE);
|
||||
export const setReleasesFilter = createAction(SET_RELEASES_FILTER);
|
||||
@@ -304,6 +307,32 @@ export const actionHandlers = handleThunks({
|
||||
});
|
||||
},
|
||||
|
||||
[SAVE_RELEASE]: function(getState, payload, dispatch) {
|
||||
const link = payload.downloadUrl;
|
||||
const file = payload.fileName;
|
||||
|
||||
$.ajax({
|
||||
url: link,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Prowlarr-Client': true
|
||||
},
|
||||
xhrFields: {
|
||||
responseType: 'blob'
|
||||
},
|
||||
success: function(data) {
|
||||
const a = document.createElement('a');
|
||||
const url = window.URL.createObjectURL(data);
|
||||
a.href = url;
|
||||
a.download = file;
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
[BULK_GRAB_RELEASES]: function(getState, payload, dispatch) {
|
||||
dispatch(set({
|
||||
section,
|
||||
|
@@ -16,6 +16,11 @@ function addApiKey(ajaxOptions) {
|
||||
ajaxOptions.headers['X-Api-Key'] = window.Prowlarr.apiKey;
|
||||
}
|
||||
|
||||
function addUIHeader(ajaxOptions) {
|
||||
ajaxOptions.headers = ajaxOptions.headers || {};
|
||||
ajaxOptions.headers['X-Prowlarr-Client'] = true;
|
||||
}
|
||||
|
||||
function addContentType(ajaxOptions) {
|
||||
if (
|
||||
ajaxOptions.contentType == null &&
|
||||
@@ -42,6 +47,7 @@ export default function createAjaxRequest(originalAjaxOptions) {
|
||||
if (isRelative(ajaxOptions)) {
|
||||
addRootUrl(ajaxOptions);
|
||||
addApiKey(ajaxOptions);
|
||||
addUIHeader(ajaxOptions);
|
||||
addContentType(ajaxOptions);
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using FluentValidation.Results;
|
||||
using NUnit.Framework;
|
||||
@@ -56,6 +55,11 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
{
|
||||
TestLogger.Info("OnApplicationUpdate was called");
|
||||
}
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
TestLogger.Info("OnGrab was called");
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNotificationWithNoEvents : NotificationBase<TestSetting>
|
||||
@@ -76,6 +80,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
|
||||
notification.SupportsOnHealthIssue.Should().BeTrue();
|
||||
notification.SupportsOnApplicationUpdate.Should().BeTrue();
|
||||
notification.SupportsOnGrab.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -85,6 +90,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||
|
||||
notification.SupportsOnHealthIssue.Should().BeFalse();
|
||||
notification.SupportsOnApplicationUpdate.Should().BeFalse();
|
||||
notification.SupportsOnGrab.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,15 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(029)]
|
||||
public class add_on_grab_to_notifications : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Notifications").AddColumn("OnGrab").AsBoolean().WithDefaultValue(false);
|
||||
Alter.Table("Notifications").AddColumn("IncludeManualGrabs").AsBoolean().WithDefaultValue(false).NotNullable();
|
||||
}
|
||||
}
|
||||
}
|
@@ -66,6 +66,7 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel()
|
||||
.Ignore(x => x.ImplementationName)
|
||||
.Ignore(i => i.SupportsOnGrab)
|
||||
.Ignore(i => i.SupportsOnHealthIssue)
|
||||
.Ignore(i => i.SupportsOnApplicationUpdate);
|
||||
|
||||
|
@@ -62,6 +62,15 @@ namespace NzbDrone.Core.Download
|
||||
// remoteMovie.SeedConfiguration = _seedConfigProvider.GetSeedConfiguration(remoteMovie);
|
||||
var indexer = _indexerFactory.GetInstance(_indexerFactory.Get(release.IndexerId));
|
||||
|
||||
var grabEvent = new IndexerDownloadEvent(release, true, source, host, release.Title, release.DownloadUrl)
|
||||
{
|
||||
DownloadClient = downloadClient.Name,
|
||||
DownloadClientId = downloadClient.Definition.Id,
|
||||
DownloadClientName = downloadClient.Definition.Name,
|
||||
Redirect = redirect,
|
||||
GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api
|
||||
};
|
||||
|
||||
string downloadClientId;
|
||||
try
|
||||
{
|
||||
@@ -72,13 +81,15 @@ namespace NzbDrone.Core.Download
|
||||
catch (ReleaseUnavailableException)
|
||||
{
|
||||
_logger.Trace("Release {0} no longer available on indexer.", release);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, source, host, release.Title, release.DownloadUrl, redirect));
|
||||
grabEvent.Successful = false;
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
throw;
|
||||
}
|
||||
catch (DownloadClientRejectedReleaseException)
|
||||
{
|
||||
_logger.Trace("Release {0} rejected by download client, possible duplicate.", release);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, source, host, release.Title, release.DownloadUrl, redirect));
|
||||
grabEvent.Successful = false;
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
throw;
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
@@ -92,14 +103,21 @@ namespace NzbDrone.Core.Download
|
||||
_indexerStatusService.RecordFailure(release.IndexerId);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, source, host, release.Title, release.DownloadUrl, redirect));
|
||||
grabEvent.Successful = false;
|
||||
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
_logger.ProgressInfo("Report sent to {0}. {1}", downloadClient.Definition.Name, downloadTitle);
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, true, source, host, release.Title, release.DownloadUrl, redirect));
|
||||
if (!string.IsNullOrWhiteSpace(downloadClientId))
|
||||
{
|
||||
grabEvent.DownloadId = downloadClientId;
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
}
|
||||
|
||||
public async Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title)
|
||||
@@ -117,16 +135,30 @@ namespace NzbDrone.Core.Download
|
||||
var success = false;
|
||||
var downloadedBytes = Array.Empty<byte>();
|
||||
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
Title = title,
|
||||
DownloadUrl = link,
|
||||
IndexerId = indexerId,
|
||||
Indexer = indexer.Definition.Name,
|
||||
DownloadProtocol = indexer.Protocol
|
||||
};
|
||||
|
||||
var grabEvent = new IndexerDownloadEvent(release, success, source, host, release.Title, release.DownloadUrl)
|
||||
{
|
||||
GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
downloadedBytes = await indexer.Download(url);
|
||||
_indexerStatusService.RecordSuccess(indexerId);
|
||||
success = true;
|
||||
grabEvent.Successful = true;
|
||||
}
|
||||
catch (ReleaseUnavailableException)
|
||||
{
|
||||
_logger.Trace("Release {0} no longer available on indexer.", link);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, host, title, url.AbsoluteUri));
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
throw;
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
@@ -140,19 +172,36 @@ namespace NzbDrone.Core.Download
|
||||
_indexerStatusService.RecordFailure(indexerId);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, host, title, url.AbsoluteUri));
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
throw;
|
||||
}
|
||||
|
||||
_logger.Trace("Downloaded {0} bytes from {1}", downloadedBytes.Length, link);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, host, title, url.AbsoluteUri));
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
|
||||
return downloadedBytes;
|
||||
}
|
||||
|
||||
public void RecordRedirect(string link, int indexerId, string source, string host, string title)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, true, source, host, title, link, true));
|
||||
var indexer = _indexerFactory.GetInstance(_indexerFactory.Get(indexerId));
|
||||
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
Title = title,
|
||||
DownloadUrl = link,
|
||||
IndexerId = indexerId,
|
||||
Indexer = indexer.Definition.Name,
|
||||
DownloadProtocol = indexer.Protocol
|
||||
};
|
||||
|
||||
var grabEvent = new IndexerDownloadEvent(release, true, source, host, release.Title, release.DownloadUrl)
|
||||
{
|
||||
Redirect = true,
|
||||
GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api
|
||||
};
|
||||
|
||||
_eventAggregator.PublishEvent(grabEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -185,7 +185,7 @@ namespace NzbDrone.Core.History
|
||||
var history = new History
|
||||
{
|
||||
Date = DateTime.UtcNow,
|
||||
IndexerId = message.IndexerId,
|
||||
IndexerId = message.Release.IndexerId,
|
||||
EventType = HistoryEventType.ReleaseGrabbed,
|
||||
Successful = message.Successful
|
||||
};
|
||||
|
@@ -1,26 +1,37 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Events
|
||||
{
|
||||
public class IndexerDownloadEvent : IEvent
|
||||
{
|
||||
public int IndexerId { get; set; }
|
||||
public ReleaseInfo Release { get; set; }
|
||||
public bool Successful { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Host { get; set; }
|
||||
public string Title { get; set; }
|
||||
public bool Redirect { get; set; }
|
||||
public string Url { get; set; }
|
||||
public int DownloadClientId { get; set; }
|
||||
public string DownloadClient { get; set; }
|
||||
public string DownloadClientName { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
public GrabTrigger GrabTrigger { get; set; }
|
||||
|
||||
public IndexerDownloadEvent(int indexerId, bool successful, string source, string host, string title, string url, bool redirect = false)
|
||||
public IndexerDownloadEvent(ReleaseInfo release, bool successful, string source, string host, string title, string url)
|
||||
{
|
||||
IndexerId = indexerId;
|
||||
Release = release;
|
||||
Successful = successful;
|
||||
Source = source;
|
||||
Host = host;
|
||||
Title = title;
|
||||
Redirect = redirect;
|
||||
Url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public enum GrabTrigger
|
||||
{
|
||||
Api,
|
||||
Manual
|
||||
}
|
||||
}
|
||||
|
@@ -185,6 +185,7 @@
|
||||
"IgnoredAddresses": "Ignored Addresses",
|
||||
"IllRestartLater": "I'll restart later",
|
||||
"IncludeHealthWarningsHelpText": "Include Health Warnings",
|
||||
"IncludeManualGrabsHelpText": "Include Manual Grabs made within Prowlarr",
|
||||
"Indexer": "Indexer",
|
||||
"IndexerAlreadySetup": "At least one instance of indexer is already setup",
|
||||
"IndexerAuth": "Indexer Auth",
|
||||
@@ -272,7 +273,8 @@
|
||||
"Ok": "Ok",
|
||||
"OnApplicationUpdate": "On Application Update",
|
||||
"OnApplicationUpdateHelpText": "On Application Update",
|
||||
"OnGrab": "On Grab",
|
||||
"OnGrab": "On Release Grab",
|
||||
"OnGrabHelpText": "On Release Grab",
|
||||
"OnHealthIssue": "On Health Issue",
|
||||
"OnHealthIssueHelpText": "On Health Issue",
|
||||
"OpenBrowserOnStart": "Open browser on start",
|
||||
|
@@ -17,6 +17,11 @@ namespace NzbDrone.Core.Notifications.Apprise
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(Settings, RELEASE_GRABBED_TITLE_BRANDED, $"{message.Message}");
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(Settings, HEALTH_ISSUE_TITLE_BRANDED, $"{healthCheck.Message}");
|
||||
|
@@ -15,6 +15,12 @@ namespace NzbDrone.Core.Notifications.Boxcar
|
||||
|
||||
public override string Link => "https://boxcar.io/client";
|
||||
public override string Name => "Boxcar";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck message)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, message.Message, Settings);
|
||||
|
@@ -19,6 +19,65 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
public override string Name => "Discord";
|
||||
public override string Link => "https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
var embed = new Embed
|
||||
{
|
||||
Author = new DiscordAuthor
|
||||
{
|
||||
Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author,
|
||||
IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png"
|
||||
},
|
||||
Title = RELEASE_GRABBED_TITLE,
|
||||
Description = message.Message,
|
||||
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
|
||||
Color = message.Successful ? (int)DiscordColors.Success : (int)DiscordColors.Danger,
|
||||
Fields = new List<DiscordField>()
|
||||
};
|
||||
|
||||
foreach (var field in Settings.GrabFields)
|
||||
{
|
||||
var discordField = new DiscordField();
|
||||
|
||||
switch ((DiscordGrabFieldType)field)
|
||||
{
|
||||
case DiscordGrabFieldType.Release:
|
||||
discordField.Name = "Release";
|
||||
discordField.Value = string.Format("```{0}```", message.Release.Title);
|
||||
break;
|
||||
case DiscordGrabFieldType.Indexer:
|
||||
discordField.Name = "Indexer";
|
||||
discordField.Value = message.Release.Indexer ?? string.Empty;
|
||||
break;
|
||||
case DiscordGrabFieldType.DownloadClient:
|
||||
discordField.Name = "Download Client";
|
||||
discordField.Value = message.DownloadClientName ?? string.Empty;
|
||||
break;
|
||||
case DiscordGrabFieldType.GrabTrigger:
|
||||
discordField.Name = "Grab Trigger";
|
||||
discordField.Value = message.GrabTrigger.ToString() ?? string.Empty;
|
||||
break;
|
||||
case DiscordGrabFieldType.Source:
|
||||
discordField.Name = "Source";
|
||||
discordField.Value = message.Source ?? string.Empty;
|
||||
break;
|
||||
case DiscordGrabFieldType.Host:
|
||||
discordField.Name = "Host";
|
||||
discordField.Value = message.Host ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
if (discordField.Name.IsNotNullOrWhiteSpace() && discordField.Value.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
embed.Fields.Add(discordField);
|
||||
}
|
||||
}
|
||||
|
||||
var payload = CreatePayload(null, new List<Embed> { embed });
|
||||
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
var attachments = new List<Embed>
|
||||
|
@@ -2,32 +2,11 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
public enum DiscordGrabFieldType
|
||||
{
|
||||
Overview,
|
||||
Rating,
|
||||
Genres,
|
||||
Quality,
|
||||
Group,
|
||||
Size,
|
||||
Links,
|
||||
Release,
|
||||
Poster,
|
||||
Fanart
|
||||
}
|
||||
|
||||
public enum DiscordImportFieldType
|
||||
{
|
||||
Overview,
|
||||
Rating,
|
||||
Genres,
|
||||
Quality,
|
||||
Codecs,
|
||||
Group,
|
||||
Size,
|
||||
Languages,
|
||||
Subtitles,
|
||||
Links,
|
||||
Release,
|
||||
Poster,
|
||||
Fanart
|
||||
Indexer,
|
||||
DownloadClient,
|
||||
GrabTrigger,
|
||||
Source,
|
||||
Host
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
//Set Default Fields
|
||||
GrabFields = new List<int> { 0, 1, 2, 3, 5, 6, 7, 8, 9 };
|
||||
ImportFields = new List<int> { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12 };
|
||||
}
|
||||
|
||||
private static readonly DiscordSettingsValidator Validator = new DiscordSettingsValidator();
|
||||
@@ -40,9 +39,6 @@ namespace NzbDrone.Core.Notifications.Discord
|
||||
[FieldDefinition(4, Label = "On Grab Fields", Advanced = true, SelectOptions = typeof(DiscordGrabFieldType), HelpText = "Change the fields that are passed in for this 'on grab' notification", Type = FieldType.TagSelect)]
|
||||
public IEnumerable<int> GrabFields { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "On Import Fields", Advanced = true, SelectOptions = typeof(DiscordImportFieldType), HelpText = "Change the fields that are passed for this 'on import' notification", Type = FieldType.TagSelect)]
|
||||
public IEnumerable<int> ImportFields { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@@ -26,6 +26,11 @@ namespace NzbDrone.Core.Notifications.Email
|
||||
|
||||
public override string Link => null;
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
SendEmail(Settings, RELEASE_GRABBED_TITLE_BRANDED, message.Message);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck message)
|
||||
{
|
||||
SendEmail(Settings, HEALTH_ISSUE_TITLE_BRANDED, message.Message);
|
||||
|
@@ -19,6 +19,11 @@ namespace NzbDrone.Core.Notifications.Gotify
|
||||
public override string Name => "Gotify";
|
||||
public override string Link => "https://gotify.net/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
|
||||
|
25
src/NzbDrone.Core/Notifications/GrabMessage.cs
Normal file
25
src/NzbDrone.Core/Notifications/GrabMessage.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using NzbDrone.Core.Indexers.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public class GrabMessage
|
||||
{
|
||||
public ReleaseInfo Release { get; set; }
|
||||
|
||||
public bool Successful { get; set; }
|
||||
public string Host { get; set; }
|
||||
public string Source { get; set; }
|
||||
public GrabTrigger GrabTrigger { get; set; }
|
||||
public bool Redirect { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string DownloadClientType { get; set; }
|
||||
public string DownloadClientName { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Message;
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,9 +6,11 @@ namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
string Link { get; }
|
||||
|
||||
void OnGrab(GrabMessage grabMessage);
|
||||
void OnHealthIssue(HealthCheck.HealthCheck healthCheck);
|
||||
void OnApplicationUpdate(ApplicationUpdateMessage updateMessage);
|
||||
void ProcessQueue();
|
||||
bool SupportsOnGrab { get; }
|
||||
bool SupportsOnHealthIssue { get; }
|
||||
bool SupportsOnApplicationUpdate { get; }
|
||||
}
|
||||
|
@@ -17,6 +17,11 @@ namespace NzbDrone.Core.Notifications.Join
|
||||
|
||||
public override string Link => "https://joaoapps.com/join/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE_BRANDED, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck message)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, message.Message, Settings);
|
||||
|
@@ -19,6 +19,12 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
||||
|
||||
public override string Link => "https://notifiarr.com";
|
||||
public override string Name => "Notifiarr";
|
||||
|
||||
public override void OnGrab(GrabMessage grabMessage)
|
||||
{
|
||||
_proxy.SendNotification(BuildGrabPayload(grabMessage), Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(BuildHealthPayload(healthCheck), Settings);
|
||||
|
@@ -8,9 +8,11 @@ namespace NzbDrone.Core.Notifications
|
||||
public abstract class NotificationBase<TSettings> : INotification
|
||||
where TSettings : IProviderConfig, new()
|
||||
{
|
||||
protected const string RELEASE_GRABBED_TITLE = "Release Grabbed";
|
||||
protected const string HEALTH_ISSUE_TITLE = "Health Check Failure";
|
||||
protected const string APPLICATION_UPDATE_TITLE = "Application Updated";
|
||||
|
||||
protected const string RELEASE_GRABBED_TITLE_BRANDED = "Prowlarr - " + RELEASE_GRABBED_TITLE;
|
||||
protected const string HEALTH_ISSUE_TITLE_BRANDED = "Prowlarr - " + HEALTH_ISSUE_TITLE;
|
||||
protected const string APPLICATION_UPDATE_TITLE_BRANDED = "Prowlarr - " + APPLICATION_UPDATE_TITLE;
|
||||
|
||||
@@ -27,6 +29,10 @@ namespace NzbDrone.Core.Notifications
|
||||
|
||||
public abstract string Link { get; }
|
||||
|
||||
public virtual void OnGrab(GrabMessage grabMessage)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
}
|
||||
@@ -39,6 +45,7 @@ namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
}
|
||||
|
||||
public bool SupportsOnGrab => HasConcreteImplementation("OnGrab");
|
||||
public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue");
|
||||
public bool SupportsOnApplicationUpdate => HasConcreteImplementation("OnApplicationUpdate");
|
||||
|
||||
|
@@ -6,10 +6,13 @@ namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public bool OnHealthIssue { get; set; }
|
||||
public bool OnApplicationUpdate { get; set; }
|
||||
public bool OnGrab { get; set; }
|
||||
public bool SupportsOnGrab { get; set; }
|
||||
public bool IncludeManualGrabs { get; set; }
|
||||
public bool SupportsOnHealthIssue { get; set; }
|
||||
public bool IncludeHealthWarnings { get; set; }
|
||||
public bool SupportsOnApplicationUpdate { get; set; }
|
||||
|
||||
public override bool Enable => OnHealthIssue || OnApplicationUpdate;
|
||||
public override bool Enable => OnHealthIssue || OnApplicationUpdate || OnGrab;
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public interface INotificationFactory : IProviderFactory<INotification, NotificationDefinition>
|
||||
{
|
||||
List<INotification> OnGrabEnabled();
|
||||
List<INotification> OnHealthIssueEnabled();
|
||||
List<INotification> OnApplicationUpdateEnabled();
|
||||
}
|
||||
@@ -20,6 +21,11 @@ namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
}
|
||||
|
||||
public List<INotification> OnGrabEnabled()
|
||||
{
|
||||
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnGrab).ToList();
|
||||
}
|
||||
|
||||
public List<INotification> OnHealthIssueEnabled()
|
||||
{
|
||||
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthIssue).ToList();
|
||||
@@ -34,6 +40,7 @@ namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
base.SetProviderCharacteristics(provider, definition);
|
||||
|
||||
definition.SupportsOnGrab = provider.SupportsOnGrab;
|
||||
definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue;
|
||||
definition.SupportsOnApplicationUpdate = provider.SupportsOnApplicationUpdate;
|
||||
}
|
||||
|
@@ -1,7 +1,12 @@
|
||||
using System;
|
||||
using System.Drawing.Drawing2D;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
using NzbDrone.Core.Indexers.Events;
|
||||
using NzbDrone.Core.Indexers.PassThePopcorn;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Update.History.Events;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
@@ -9,7 +14,8 @@ namespace NzbDrone.Core.Notifications
|
||||
public class NotificationService
|
||||
: IHandle<HealthCheckFailedEvent>,
|
||||
IHandleAsync<HealthCheckCompleteEvent>,
|
||||
IHandle<UpdateInstalledEvent>
|
||||
IHandle<UpdateInstalledEvent>,
|
||||
IHandle<IndexerDownloadEvent>
|
||||
{
|
||||
private readonly INotificationFactory _notificationFactory;
|
||||
private readonly Logger _logger;
|
||||
@@ -35,6 +41,43 @@ namespace NzbDrone.Core.Notifications
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ShouldHandleOnGrab(GrabMessage message, bool includeManual)
|
||||
{
|
||||
if (message.GrabTrigger == GrabTrigger.Api)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.GrabTrigger == GrabTrigger.Manual && includeManual)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetMessage(ReleaseInfo release, GrabTrigger grabTrigger, string source, string downloadClient)
|
||||
{
|
||||
var message = string.Format("{0} grabbed by {1} from {2}",
|
||||
release.Title,
|
||||
source,
|
||||
release.Indexer);
|
||||
|
||||
if (grabTrigger == GrabTrigger.Manual)
|
||||
{
|
||||
message = string.Format("{0} manually grabbed in Prowlarr from {1}",
|
||||
release.Title,
|
||||
release.Indexer);
|
||||
}
|
||||
|
||||
if (downloadClient.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
message += $" and sent to {downloadClient}";
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public void Handle(HealthCheckFailedEvent message)
|
||||
{
|
||||
// Don't send health check notifications during the start up grace period,
|
||||
@@ -99,5 +142,37 @@ namespace NzbDrone.Core.Notifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(IndexerDownloadEvent message)
|
||||
{
|
||||
var grabMessage = new GrabMessage
|
||||
{
|
||||
Release = message.Release,
|
||||
Source = message.Source,
|
||||
Host = message.Host,
|
||||
Successful = message.Successful,
|
||||
DownloadClientName = message.DownloadClientName,
|
||||
DownloadClientType = message.DownloadClient,
|
||||
DownloadId = message.DownloadId,
|
||||
Redirect = message.Redirect,
|
||||
GrabTrigger = message.GrabTrigger,
|
||||
Message = GetMessage(message.Release, message.GrabTrigger, message.Source, message.DownloadClientName)
|
||||
};
|
||||
|
||||
foreach (var notification in _notificationFactory.OnGrabEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ShouldHandleOnGrab(grabMessage, ((NotificationDefinition)notification.Definition).IncludeManualGrabs))
|
||||
{
|
||||
notification.OnGrab(grabMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send OnGrab notification to {0}", notification.Definition.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,11 @@ namespace NzbDrone.Core.Notifications.Ntfy
|
||||
|
||||
public override string Link => "https://ntfy.sh/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE_BRANDED, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck message)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, message.Message, Settings);
|
||||
|
@@ -16,6 +16,11 @@ namespace NzbDrone.Core.Notifications.Prowl
|
||||
public override string Link => "https://www.prowlapp.com/";
|
||||
public override string Name => "Prowl";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_prowlProxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_prowlProxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
|
||||
|
@@ -19,6 +19,11 @@ namespace NzbDrone.Core.Notifications.PushBullet
|
||||
public override string Name => "Pushbullet";
|
||||
public override string Link => "https://www.pushbullet.com/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE_BRANDED, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
|
||||
|
@@ -16,6 +16,11 @@ namespace NzbDrone.Core.Notifications.Pushover
|
||||
public override string Name => "Pushover";
|
||||
public override string Link => "https://pushover.net/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
|
||||
|
@@ -19,6 +19,11 @@ namespace NzbDrone.Core.Notifications.SendGrid
|
||||
public override string Name => "SendGrid";
|
||||
public override string Link => "https://sendgrid.com/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
|
||||
|
@@ -16,6 +16,11 @@ namespace NzbDrone.Core.Notifications.Simplepush
|
||||
public override string Name => "Simplepush";
|
||||
public override string Link => "https://simplepush.io/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
|
||||
|
@@ -16,6 +16,11 @@ namespace NzbDrone.Core.Notifications.Telegram
|
||||
public override string Name => "Telegram";
|
||||
public override string Link => "https://telegram.org/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
|
||||
|
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Twitter
|
||||
@@ -18,6 +19,11 @@ namespace NzbDrone.Core.Notifications.Twitter
|
||||
public override string Name => "Twitter";
|
||||
public override string Link => "https://twitter.com/";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
_twitterService.SendNotification($"Release Grabbed: {message.Message}", Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_twitterService.SendNotification($"Health Issue: {healthCheck.Message}", Settings);
|
||||
|
@@ -18,6 +18,11 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
|
||||
public override string Link => "https://wiki.servarr.com/prowlarr/settings#connect";
|
||||
|
||||
public override void OnGrab(GrabMessage grabMessage)
|
||||
{
|
||||
_proxy.SendWebhook(BuildGrabPayload(grabMessage), Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendWebhook(BuildHealthPayload(healthCheck), Settings);
|
||||
|
@@ -14,6 +14,22 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public WebhookGrabPayload BuildGrabPayload(GrabMessage message)
|
||||
{
|
||||
return new WebhookGrabPayload
|
||||
{
|
||||
EventType = WebhookEventType.Grab,
|
||||
InstanceName = _configFileProvider.InstanceName,
|
||||
Release = new WebhookRelease(message.Release),
|
||||
Trigger = message.GrabTrigger,
|
||||
Source = message.Source,
|
||||
Host = message.Host,
|
||||
DownloadClient = message.DownloadClientName,
|
||||
DownloadClientType = message.DownloadClientType,
|
||||
DownloadId = message.DownloadId
|
||||
};
|
||||
}
|
||||
|
||||
protected WebhookHealthPayload BuildHealthPayload(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
return new WebhookHealthPayload
|
||||
|
@@ -0,0 +1,15 @@
|
||||
using NzbDrone.Core.Indexers.Events;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Webhook
|
||||
{
|
||||
public class WebhookGrabPayload : WebhookPayload
|
||||
{
|
||||
public WebhookRelease Release { get; set; }
|
||||
public GrabTrigger Trigger { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Host { get; set; }
|
||||
public string DownloadClient { get; set; }
|
||||
public string DownloadClientType { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
}
|
||||
}
|
22
src/NzbDrone.Core/Notifications/Webhook/WebhookRelease.cs
Normal file
22
src/NzbDrone.Core/Notifications/Webhook/WebhookRelease.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Webhook
|
||||
{
|
||||
public class WebhookRelease
|
||||
{
|
||||
public WebhookRelease()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookRelease(ReleaseInfo release)
|
||||
{
|
||||
ReleaseTitle = release.Title;
|
||||
Indexer = release.Indexer;
|
||||
Size = release.Size;
|
||||
}
|
||||
|
||||
public string ReleaseTitle { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public long? Size { get; set; }
|
||||
}
|
||||
}
|
@@ -55,7 +55,7 @@ namespace NzbDrone.Api.V1.Indexers
|
||||
public async Task<IActionResult> GetNewznabResponse(int id, [FromQuery] NewznabRequest request)
|
||||
{
|
||||
var requestType = request.t;
|
||||
request.source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
request.source = Request.GetSource();
|
||||
request.server = Request.GetServerUrl();
|
||||
request.host = Request.GetHostName();
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace NzbDrone.Api.V1.Indexers
|
||||
|
||||
file = WebUtility.UrlDecode(file);
|
||||
|
||||
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
var source = Request.GetSource();
|
||||
var host = Request.GetHostName();
|
||||
|
||||
var unprotectedlLink = _downloadMappingService.ConvertToNormalLink(link);
|
||||
|
@@ -5,8 +5,11 @@ namespace Prowlarr.Api.V1.Notifications
|
||||
public class NotificationResource : ProviderResource<NotificationResource>
|
||||
{
|
||||
public string Link { get; set; }
|
||||
public bool OnGrab { get; set; }
|
||||
public bool OnHealthIssue { get; set; }
|
||||
public bool OnApplicationUpdate { get; set; }
|
||||
public bool SupportsOnGrab { get; set; }
|
||||
public bool IncludeManualGrabs { get; set; }
|
||||
public bool SupportsOnHealthIssue { get; set; }
|
||||
public bool IncludeHealthWarnings { get; set; }
|
||||
public bool SupportsOnApplicationUpdate { get; set; }
|
||||
@@ -24,6 +27,9 @@ namespace Prowlarr.Api.V1.Notifications
|
||||
|
||||
var resource = base.ToResource(definition);
|
||||
|
||||
resource.OnGrab = definition.OnGrab;
|
||||
resource.SupportsOnGrab = definition.SupportsOnGrab;
|
||||
resource.IncludeManualGrabs = definition.IncludeManualGrabs;
|
||||
resource.OnHealthIssue = definition.OnHealthIssue;
|
||||
resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue;
|
||||
resource.IncludeHealthWarnings = definition.IncludeHealthWarnings;
|
||||
@@ -42,6 +48,9 @@ namespace Prowlarr.Api.V1.Notifications
|
||||
|
||||
var definition = base.ToModel(resource);
|
||||
|
||||
definition.OnGrab = resource.OnGrab;
|
||||
definition.SupportsOnGrab = resource.SupportsOnGrab;
|
||||
definition.IncludeManualGrabs = resource.IncludeManualGrabs;
|
||||
definition.OnHealthIssue = resource.OnHealthIssue;
|
||||
definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue;
|
||||
definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Routing.Constraints;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using Prowlarr.Http.REST;
|
||||
@@ -36,6 +37,21 @@ namespace Prowlarr.Api.V1.Search
|
||||
public int? Seeders { get; set; }
|
||||
public int? Leechers { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get
|
||||
{
|
||||
var extension = "torrent";
|
||||
|
||||
if (Protocol == DownloadProtocol.Usenet)
|
||||
{
|
||||
extension = "nzb";
|
||||
}
|
||||
|
||||
return $"{Title}.{extension}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReleaseResourceMapper
|
||||
|
@@ -59,7 +59,7 @@ namespace Prowlarr.Api.V1.Search
|
||||
var releaseInfo = _remoteReleaseCache.Find(GetCacheKey(release));
|
||||
|
||||
var indexerDef = _indexerFactory.Get(release.IndexerId);
|
||||
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
var source = Request.GetSource();
|
||||
var host = Request.GetHostName();
|
||||
|
||||
try
|
||||
@@ -78,7 +78,7 @@ namespace Prowlarr.Api.V1.Search
|
||||
[HttpPost("bulk")]
|
||||
public ActionResult<ReleaseResource> GrabReleases(List<ReleaseResource> releases)
|
||||
{
|
||||
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
var source = Request.GetSource();
|
||||
var host = Request.GetHostName();
|
||||
|
||||
var groupedReleases = releases.GroupBy(r => r.IndexerId);
|
||||
|
@@ -4,9 +4,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
|
||||
namespace Prowlarr.Http.Extensions
|
||||
{
|
||||
@@ -129,6 +127,16 @@ namespace Prowlarr.Http.Extensions
|
||||
return remoteIP.ToString();
|
||||
}
|
||||
|
||||
public static string GetSource(this HttpRequest request)
|
||||
{
|
||||
if (request.Headers.TryGetValue("X-Prowlarr-Client", out var source))
|
||||
{
|
||||
return "Prowlarr";
|
||||
}
|
||||
|
||||
return NzbDrone.Common.Http.UserAgentParser.ParseSource(request.Headers["User-Agent"]);
|
||||
}
|
||||
|
||||
public static string GetHostName(this HttpRequest request)
|
||||
{
|
||||
string ip = request.GetRemoteIP();
|
||||
|
Reference in New Issue
Block a user