mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-17 21:26:13 -04:00
Compare commits
23 Commits
v4.0.1.116
...
v4.0.2.122
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c8758c27a | ||
|
|
086d3b5afa | ||
|
|
f8a0751775 | ||
|
|
c99d81e79b | ||
|
|
9fd193d2a8 | ||
|
|
64f4365fe9 | ||
|
|
2773f77e1c | ||
|
|
0a84b4a8e9 | ||
|
|
236d8e4c50 | ||
|
|
16d3827dbd | ||
|
|
fa600e62e0 | ||
|
|
fb6fc568c5 | ||
|
|
1f97679868 | ||
|
|
b34e0f8259 | ||
|
|
4c170d0452 | ||
|
|
6dc0a88004 | ||
|
|
33b44a8a53 | ||
|
|
cb72e752f9 | ||
|
|
a11ee7bc11 | ||
|
|
98d60e1a8e | ||
|
|
6377c688fc | ||
|
|
7a37f130f9 | ||
|
|
724dd7e733 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -20,9 +20,9 @@ concurrency:
|
||||
|
||||
env:
|
||||
FRAMEWORK: net6.0
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
SONARR_MAJOR_VERSION: 4
|
||||
VERSION: 4.0.1
|
||||
VERSION: 4.0.2
|
||||
|
||||
jobs:
|
||||
backend:
|
||||
@@ -48,6 +48,8 @@ jobs:
|
||||
|
||||
echo "SDK_PATH=${{ env.DOTNET_ROOT }}/sdk/${DOTNET_VERSION}" >> "$GITHUB_ENV"
|
||||
echo "SONARR_VERSION=$SONARR_VERSION" >> "$GITHUB_ENV"
|
||||
echo "BRANCH=${RAW_BRANCH_NAME/\//-}" >> "$GITHUB_ENV"
|
||||
|
||||
echo "framework=${{ env.FRAMEWORK }}" >> "$GITHUB_OUTPUT"
|
||||
echo "major_version=${{ env.SONARR_MAJOR_VERSION }}" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$SONARR_VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
@@ -117,7 +117,7 @@ class FileBrowserModalContent extends Component {
|
||||
className={styles.mappedDrivesWarning}
|
||||
kind={kinds.WARNING}
|
||||
>
|
||||
<InlineMarkdown data={translate('MappedNetworkDrivesWindowsService')} />
|
||||
<InlineMarkdown data={translate('MappedNetworkDrivesWindowsService', { url: 'https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server' })} />
|
||||
</Alert>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { maxBy } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
@@ -8,6 +9,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import FilterBuilderRow from './FilterBuilderRow';
|
||||
import styles from './FilterBuilderModalContent.css';
|
||||
|
||||
@@ -49,7 +51,7 @@ class FilterBuilderModalContent extends Component {
|
||||
if (id) {
|
||||
dispatchSetFilter({ selectedFilterKey: id });
|
||||
} else {
|
||||
const last = customFilters[customFilters.length -1];
|
||||
const last = maxBy(customFilters, 'id');
|
||||
dispatchSetFilter({ selectedFilterKey: last.id });
|
||||
}
|
||||
|
||||
@@ -107,7 +109,7 @@ class FilterBuilderModalContent extends Component {
|
||||
this.setState({
|
||||
labelErrors: [
|
||||
{
|
||||
message: 'Label is required'
|
||||
message: translate('LabelIsRequired')
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -145,13 +147,13 @@ class FilterBuilderModalContent extends Component {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Custom Filter
|
||||
{translate('CustomFilter')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div className={styles.labelContainer}>
|
||||
<div className={styles.label}>
|
||||
Label
|
||||
{translate('Label')}
|
||||
</div>
|
||||
|
||||
<div className={styles.labelInputContainer}>
|
||||
@@ -165,7 +167,9 @@ class FilterBuilderModalContent extends Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.label}>Filters</div>
|
||||
<div className={styles.label}>
|
||||
{translate('Filters')}
|
||||
</div>
|
||||
|
||||
<div className={styles.rows}>
|
||||
{
|
||||
@@ -192,7 +196,7 @@ class FilterBuilderModalContent extends Component {
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onCancelPress}>
|
||||
Cancel
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
@@ -200,7 +204,7 @@ class FilterBuilderModalContent extends Component {
|
||||
error={saveError}
|
||||
onPress={this.onSaveFilterPress}
|
||||
>
|
||||
Save
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
||||
@@ -37,8 +37,8 @@ class CustomFilter extends Component {
|
||||
dispatchSetFilter
|
||||
} = this.props;
|
||||
|
||||
// Assume that delete and then unmounting means the delete was successful.
|
||||
// Moving this check to a ancestor would be more accurate, but would have
|
||||
// Assume that delete and then unmounting means the deletion was successful.
|
||||
// Moving this check to an ancestor would be more accurate, but would have
|
||||
// more boilerplate.
|
||||
if (this.state.isDeleting && id === selectedFilterKey) {
|
||||
dispatchSetFilter({ selectedFilterKey: 'all' });
|
||||
|
||||
@@ -55,10 +55,10 @@ function EditSpecificationModalContent(props) {
|
||||
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
|
||||
</div>
|
||||
<div>
|
||||
<InlineMarkdown data={translate('RegularExpressionsTutorialLink')} />
|
||||
<InlineMarkdown data={translate('RegularExpressionsTutorialLink', { url: 'https://www.regular-expressions.info/tutorial.html' })} />
|
||||
</div>
|
||||
<div>
|
||||
<InlineMarkdown data={translate('RegularExpressionsCanBeTested')} />
|
||||
<InlineMarkdown data={translate('RegularExpressionsCanBeTested', { url: 'http://regexstorm.net/tester' })} />
|
||||
</div>
|
||||
</Alert>
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import translate from 'Utilities/String/translate';
|
||||
import styles from './TheTvdb.css';
|
||||
|
||||
function TheTvdb(props) {
|
||||
debugger;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<img
|
||||
@@ -15,8 +16,7 @@ function TheTvdb(props) {
|
||||
<div className={styles.title}>
|
||||
{translate('TheTvdb')}
|
||||
</div>
|
||||
|
||||
<InlineMarkdown data={translate('SeriesAndEpisodeInformationIsProvidedByTheTVDB')} />
|
||||
<InlineMarkdown data={translate('SeriesAndEpisodeInformationIsProvidedByTheTVDB', { url: 'https://www.thetvdb.com/subscribe' })} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -86,10 +86,10 @@ function EditSpecificationModalContent(props) {
|
||||
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
|
||||
</div>
|
||||
<div>
|
||||
<InlineMarkdown data={translate('RegularExpressionsTutorialLink')} />
|
||||
<InlineMarkdown data={translate('RegularExpressionsTutorialLink', { url: 'https://www.regular-expressions.info/tutorial.html' })} />
|
||||
</div>
|
||||
<div>
|
||||
<InlineMarkdown data={translate('RegularExpressionsCanBeTested')} />
|
||||
<InlineMarkdown data={translate('RegularExpressionsCanBeTested', { url: 'http://regexstorm.net/tester' })} />
|
||||
</div>
|
||||
</Alert>
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import getFilterValue from 'Utilities/Filter/getFilterValue';
|
||||
@@ -180,6 +181,16 @@ class CutoffUnmet extends Component {
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
<TableOptionsModalWrapper
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
|
||||
<FilterMenu
|
||||
alignMenu={align.RIGHT}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
|
||||
@@ -12,6 +12,7 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import TablePager from 'Components/Table/TablePager';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
@@ -193,6 +194,16 @@ class Missing extends Component {
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
<TableOptionsModalWrapper
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label={translate('Options')}
|
||||
iconName={icons.TABLE}
|
||||
/>
|
||||
</TableOptionsModalWrapper>
|
||||
|
||||
<FilterMenu
|
||||
alignMenu={align.RIGHT}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@juggle/resize-observer": "3.4.0",
|
||||
"@microsoft/signalr": "6.0.21",
|
||||
"@sentry/browser": "7.51.2",
|
||||
"@sentry/integrations": "7.51.2",
|
||||
"@sentry/browser": "7.100.0",
|
||||
"@types/node": "18.16.8",
|
||||
"@types/react": "18.2.6",
|
||||
"@types/react-dom": "18.2.4",
|
||||
|
||||
@@ -98,6 +98,35 @@
|
||||
<RootNamespace Condition="'$(SonarrProject)'=='true'">$(MSBuildProjectName.Replace('Sonarr','NzbDrone'))</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TestProject)'!='true'">
|
||||
<!-- Annotates .NET assemblies with repository information including SHA -->
|
||||
<!-- Sentry uses this to link directly to GitHub at the exact version/file/line -->
|
||||
<!-- This is built-in on .NET 8 and can be removed once the project is updated -->
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Sentry specific configuration: Only in Release mode -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<!-- https://docs.sentry.io/platforms/dotnet/configuration/msbuild/ -->
|
||||
<!-- OrgSlug, ProjectSlug and AuthToken are required.
|
||||
They can be set below, via argument to 'msbuild -p:' or environment variable -->
|
||||
<SentryOrg></SentryOrg>
|
||||
<SentryProject></SentryProject>
|
||||
<SentryUrl></SentryUrl> <!-- If empty, assumed to be sentry.io -->
|
||||
<SentryAuthToken></SentryAuthToken> <!-- Use env var instead: SENTRY_AUTH_TOKEN -->
|
||||
|
||||
<!-- Upload PDBs to Sentry, enabling stack traces with line numbers and file paths
|
||||
without the need to deploy the application with PDBs -->
|
||||
<SentryUploadSymbols>true</SentryUploadSymbols>
|
||||
|
||||
<!-- Source Link settings -->
|
||||
<!-- https://github.com/dotnet/sourcelink/blob/main/docs/README.md#publishrepositoryurl -->
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<!-- Embeds all source code in the respective PDB. This can make it a bit bigger but since it'll be uploaded
|
||||
to Sentry and not distributed to run on the server, it helps debug crashes while making releases smaller -->
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Standard testing packages -->
|
||||
<ItemGroup Condition="'$(TestProject)'=='true'">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NLog;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Sentry;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
@@ -26,7 +27,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_subject = new SentryTarget("https://aaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/111111");
|
||||
_subject = new SentryTarget("https://aaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/111111", Mocker.GetMock<IAppFolderInfo>().Object);
|
||||
}
|
||||
|
||||
private LogEventInfo GivenLogEvent(LogLevel level, Exception ex, string message)
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
RegisterDebugger();
|
||||
}
|
||||
|
||||
RegisterSentry(updateApp);
|
||||
RegisterSentry(updateApp, appFolderInfo);
|
||||
|
||||
if (updateApp)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
|
||||
private static void RegisterSentry(bool updateClient)
|
||||
private static void RegisterSentry(bool updateClient, IAppFolderInfo appFolderInfo)
|
||||
{
|
||||
string dsn;
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
Target target;
|
||||
try
|
||||
{
|
||||
target = new SentryTarget(dsn)
|
||||
target = new SentryTarget(dsn, appFolderInfo)
|
||||
{
|
||||
Name = "sentryTarget",
|
||||
Layout = "${message}"
|
||||
|
||||
@@ -9,6 +9,7 @@ using NLog;
|
||||
using NLog.Common;
|
||||
using NLog.Targets;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using Sentry;
|
||||
|
||||
namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
@@ -96,7 +97,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
public bool FilterEvents { get; set; }
|
||||
public bool SentryEnabled { get; set; }
|
||||
|
||||
public SentryTarget(string dsn)
|
||||
public SentryTarget(string dsn, IAppFolderInfo appFolderInfo)
|
||||
{
|
||||
_sdk = SentrySdk.Init(o =>
|
||||
{
|
||||
@@ -104,9 +105,33 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
o.AttachStacktrace = true;
|
||||
o.MaxBreadcrumbs = 200;
|
||||
o.Release = BuildInfo.Release;
|
||||
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
|
||||
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
||||
o.SetBeforeSend(x => SentryCleanser.CleanseEvent(x));
|
||||
o.SetBeforeBreadcrumb(x => SentryCleanser.CleanseBreadcrumb(x));
|
||||
o.Environment = BuildInfo.Branch;
|
||||
|
||||
// Crash free run statistics (sends a ping for healthy and for crashes sessions)
|
||||
o.AutoSessionTracking = true;
|
||||
|
||||
// Caches files in the event device is offline
|
||||
// Sentry creates a 'sentry' sub directory, no need to concat here
|
||||
o.CacheDirectoryPath = appFolderInfo.GetAppDataPath();
|
||||
|
||||
// default environment is production
|
||||
if (!RuntimeInfo.IsProduction)
|
||||
{
|
||||
if (RuntimeInfo.IsDevelopment)
|
||||
{
|
||||
o.Environment = "development";
|
||||
}
|
||||
else if (RuntimeInfo.IsTesting)
|
||||
{
|
||||
o.Environment = "testing";
|
||||
}
|
||||
else
|
||||
{
|
||||
o.Environment = "other";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
InitializeScope();
|
||||
@@ -125,7 +150,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
{
|
||||
SentrySdk.ConfigureScope(scope =>
|
||||
{
|
||||
scope.User = new User
|
||||
scope.User = new SentryUser
|
||||
{
|
||||
Id = HashUtil.AnonymousToken()
|
||||
};
|
||||
@@ -285,13 +310,21 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
}
|
||||
}
|
||||
|
||||
var level = LoggingLevelMap[logEvent.Level];
|
||||
var sentryEvent = new SentryEvent(logEvent.Exception)
|
||||
{
|
||||
Level = LoggingLevelMap[logEvent.Level],
|
||||
Level = level,
|
||||
Logger = logEvent.LoggerName,
|
||||
Message = logEvent.FormattedMessage
|
||||
};
|
||||
|
||||
if (level is SentryLevel.Fatal && logEvent.Exception is not null)
|
||||
{
|
||||
// Usages of 'fatal' here indicates the process will crash. In Sentry this is represented with
|
||||
// the 'unhandled' exception flag
|
||||
logEvent.Exception.SetSentryMechanism("Logger.Fatal", "Logger.Fatal was called", false);
|
||||
}
|
||||
|
||||
sentryEvent.SetExtras(extras);
|
||||
sentryEvent.SetFingerprint(fingerPrint);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="6.0.3" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||
<PackageReference Include="Sentry" Version="3.23.1" />
|
||||
<PackageReference Include="Sentry" Version="4.0.2" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.8" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class release_typeFixture : MigrationTest<release_type>
|
||||
{
|
||||
[Test]
|
||||
public void should_convert_single_episode_without_folder()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("EpisodeFiles").Row(new
|
||||
{
|
||||
SeriesId = 1,
|
||||
SeasonNumber = 1,
|
||||
RelativePath = "Season 01/S01E05.mkv",
|
||||
Size = 125.Megabytes(),
|
||||
DateAdded = DateTime.UtcNow.AddDays(-5),
|
||||
OriginalFilePath = "Series.Title.S01E05.720p.HDTV.x265-Sonarr.mkv",
|
||||
ReleaseGroup = "Sonarr",
|
||||
Quality = new QualityModel(Quality.HDTV720p).ToJson(),
|
||||
Languages = "[1]"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<EpisodeFile203>("SELECT * FROM \"EpisodeFiles\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().ReleaseType.Should().Be((int)ReleaseType.SingleEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_convert_single_episode_with_folder()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("EpisodeFiles").Row(new
|
||||
{
|
||||
SeriesId = 1,
|
||||
SeasonNumber = 1,
|
||||
RelativePath = "Season 01/S01E05.mkv",
|
||||
Size = 125.Megabytes(),
|
||||
DateAdded = DateTime.UtcNow.AddDays(-5),
|
||||
OriginalFilePath = "Series.Title.S01E05.720p.HDTV.x265-Sonarr/S01E05.mkv",
|
||||
ReleaseGroup = "Sonarr",
|
||||
Quality = new QualityModel(Quality.HDTV720p).ToJson(),
|
||||
Languages = "[1]"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<EpisodeFile203>("SELECT * FROM \"EpisodeFiles\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().ReleaseType.Should().Be((int)ReleaseType.SingleEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_convert_multi_episode_without_folder()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("EpisodeFiles").Row(new
|
||||
{
|
||||
SeriesId = 1,
|
||||
SeasonNumber = 1,
|
||||
RelativePath = "Season 01/S01E05.mkv",
|
||||
Size = 125.Megabytes(),
|
||||
DateAdded = DateTime.UtcNow.AddDays(-5),
|
||||
OriginalFilePath = "Series.Title.S01E05E06.720p.HDTV.x265-Sonarr.mkv",
|
||||
ReleaseGroup = "Sonarr",
|
||||
Quality = new QualityModel(Quality.HDTV720p).ToJson(),
|
||||
Languages = "[1]"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<EpisodeFile203>("SELECT * FROM \"EpisodeFiles\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().ReleaseType.Should().Be((int)ReleaseType.MultiEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_convert_multi_episode_with_folder()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("EpisodeFiles").Row(new
|
||||
{
|
||||
SeriesId = 1,
|
||||
SeasonNumber = 1,
|
||||
RelativePath = "Season 01/S01E05.mkv",
|
||||
Size = 125.Megabytes(),
|
||||
DateAdded = DateTime.UtcNow.AddDays(-5),
|
||||
OriginalFilePath = "Series.Title.S01E05E06.720p.HDTV.x265-Sonarr/S01E05E06.mkv",
|
||||
ReleaseGroup = "Sonarr",
|
||||
Quality = new QualityModel(Quality.HDTV720p).ToJson(),
|
||||
Languages = "[1]"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<EpisodeFile203>("SELECT * FROM \"EpisodeFiles\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().ReleaseType.Should().Be((int)ReleaseType.MultiEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_convert_season_pack_with_folder()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("EpisodeFiles").Row(new
|
||||
{
|
||||
SeriesId = 1,
|
||||
SeasonNumber = 1,
|
||||
RelativePath = "Season 01/S01E05.mkv",
|
||||
Size = 125.Megabytes(),
|
||||
DateAdded = DateTime.UtcNow.AddDays(-5),
|
||||
OriginalFilePath = "Series.Title.S01.720p.HDTV.x265-Sonarr/S01E05.mkv",
|
||||
ReleaseGroup = "Sonarr",
|
||||
Quality = new QualityModel(Quality.HDTV720p).ToJson(),
|
||||
Languages = "[1]"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<EpisodeFile203>("SELECT * FROM \"EpisodeFiles\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().ReleaseType.Should().Be((int)ReleaseType.SeasonPack);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_convert_episode_without_original_file_path()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("EpisodeFiles").Row(new
|
||||
{
|
||||
SeriesId = 1,
|
||||
SeasonNumber = 1,
|
||||
RelativePath = "Season 01/S01E05.mkv",
|
||||
Size = 125.Megabytes(),
|
||||
DateAdded = DateTime.UtcNow.AddDays(-5),
|
||||
ReleaseGroup = "Sonarr",
|
||||
Quality = new QualityModel(Quality.HDTV720p).ToJson(),
|
||||
Languages = "[1]"
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<EpisodeFile203>("SELECT * FROM \"EpisodeFiles\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().ReleaseType.Should().Be((int)ReleaseType.Unknown);
|
||||
}
|
||||
|
||||
public class EpisodeFile203
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public string RelativePath { get; set; }
|
||||
public long Size { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
public string OriginalFilePath { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public long IndexerFlags { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public List<int> Languages { get; set; }
|
||||
public long ReleaseType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!")));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!")));
|
||||
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), new Rejection("Rejected!")));
|
||||
_rejectedDecisions.ForEach(r => r.LocalEpisode.FileEpisodeInfo = new ParsedEpisodeInfo());
|
||||
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
@@ -59,7 +60,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
Episodes = new List<Episode> { episode },
|
||||
Path = Path.Combine(series.Path, "30 Rock - S01E01 - Pilot.avi"),
|
||||
Quality = new QualityModel(Quality.Bluray720p),
|
||||
ReleaseGroup = "DRONE"
|
||||
ReleaseGroup = "DRONE",
|
||||
FileEpisodeInfo = new ParsedEpisodeInfo()
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -441,6 +441,10 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].testtitle.forced.fra.ass", new[] { "forced" }, "testtitle", "French")]
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].fra.forced.testtitle.ass", new[] { "forced" }, "testtitle", "French")]
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].forced.fra.testtitle.ass", new[] { "forced" }, "testtitle", "French")]
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].ru-something-else.srt", new string[0], "something-else", "Russian")]
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].Full Subtitles.eng.ass", new string[0], "Full Subtitles", "English")]
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].mytitle - 1.en.ass", new string[0], "mytitle", "English")]
|
||||
[TestCase("Name (2020) - S01E20 - [AAC 2.0].mytitle.en.ass", new string[0], "mytitle", "English")]
|
||||
public void should_parse_title_and_tags(string postTitle, string[] expectedTags, string expectedTitle, string expectedLanguage)
|
||||
{
|
||||
var subtitleTitleInfo = LanguageParser.ParseSubtitleLanguageInformation(postTitle);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class NormalizeEpisodeTitleFixture : CoreTest
|
||||
{
|
||||
[TestCase("Episode Title", "episode title")]
|
||||
[TestCase("A.B,C;", "a b c")]
|
||||
[TestCase("Episode Title", "episode title")]
|
||||
[TestCase("French Title (1)", "french title")]
|
||||
[TestCase("Series.Title.S01.Special.Episode.Title.720p.HDTV.x264-Sonarr", "episode title")]
|
||||
[TestCase("Series.Title.S01E00.Episode.Title.720p.HDTV.x264-Sonarr", "episode title")]
|
||||
public void should_normalize_episode_title(string input, string expected)
|
||||
{
|
||||
var result = Parser.Parser.NormalizeEpisodeTitle(input);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using NzbDrone.Core.Test.Framework;
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class NormalizeTitleFixture : CoreTest
|
||||
public class NormalizeSeriesTitleFixture : CoreTest
|
||||
{
|
||||
[TestCase("Series", "series")]
|
||||
[TestCase("Series (2009)", "series2009")]
|
||||
@@ -40,7 +40,6 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
private Series GetSeries()
|
||||
{
|
||||
var series = _gameOfThrones.Item1.JsonClone();
|
||||
series.Seasons = new List<Season>();
|
||||
|
||||
return series;
|
||||
}
|
||||
@@ -49,7 +48,6 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
var series = Builder<Series>.CreateNew().Build();
|
||||
series.SeriesType = SeriesTypes.Anime;
|
||||
series.Seasons = new List<Season>();
|
||||
|
||||
return series;
|
||||
}
|
||||
@@ -178,34 +176,6 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_monitored_status_for_old_episodes_to_false_if_no_episodes_existed()
|
||||
{
|
||||
var series = GetSeries();
|
||||
series.Seasons = new List<Season>();
|
||||
|
||||
var episodes = GetEpisodes().OrderBy(v => v.SeasonNumber).ThenBy(v => v.EpisodeNumber).Take(4).ToList();
|
||||
|
||||
episodes[1].AirDateUtc = DateTime.UtcNow.AddDays(-15);
|
||||
episodes[2].AirDateUtc = DateTime.UtcNow.AddDays(-10);
|
||||
episodes[3].AirDateUtc = DateTime.UtcNow.AddDays(1);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, episodes);
|
||||
|
||||
_insertedEpisodes = _insertedEpisodes.OrderBy(v => v.EpisodeNumber).ToList();
|
||||
|
||||
_insertedEpisodes.Should().HaveSameCount(episodes);
|
||||
_insertedEpisodes[0].Monitored.Should().Be(false);
|
||||
_insertedEpisodes[1].Monitored.Should().Be(false);
|
||||
_insertedEpisodes[2].Monitored.Should().Be(false);
|
||||
_insertedEpisodes[3].Monitored.Should().Be(true);
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_duplicate_remote_episodes_before_processing()
|
||||
{
|
||||
@@ -259,65 +229,6 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_get_new_season_and_episode_numbers_when_absolute_episode_number_match_found()
|
||||
{
|
||||
const int expectedSeasonNumber = 10;
|
||||
const int expectedEpisodeNumber = 5;
|
||||
const int expectedAbsoluteNumber = 3;
|
||||
|
||||
var episode = Builder<Episode>.CreateNew()
|
||||
.With(e => e.SeasonNumber = expectedSeasonNumber)
|
||||
.With(e => e.EpisodeNumber = expectedEpisodeNumber)
|
||||
.With(e => e.AbsoluteEpisodeNumber = expectedAbsoluteNumber)
|
||||
.Build();
|
||||
|
||||
var existingEpisode = episode.JsonClone();
|
||||
existingEpisode.SeasonNumber = 1;
|
||||
existingEpisode.EpisodeNumber = 1;
|
||||
existingEpisode.AbsoluteEpisodeNumber = expectedAbsoluteNumber;
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode> { existingEpisode });
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), new List<Episode> { episode });
|
||||
|
||||
_insertedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(expectedSeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(expectedEpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(expectedAbsoluteNumber);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_absolute_match_over_season_and_epsiode_match()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
episodes[0].AbsoluteEpisodeNumber = null;
|
||||
episodes[0].SeasonNumber.Should().NotBe(episodes[1].SeasonNumber);
|
||||
episodes[0].EpisodeNumber.Should().NotBe(episodes[1].EpisodeNumber);
|
||||
|
||||
var existingEpisode = new Episode
|
||||
{
|
||||
SeasonNumber = episodes[0].SeasonNumber,
|
||||
EpisodeNumber = episodes[0].EpisodeNumber,
|
||||
AbsoluteEpisodeNumber = episodes[1].AbsoluteEpisodeNumber
|
||||
};
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode> { existingEpisode });
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(episodes[1].SeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[1].AbsoluteEpisodeNumber);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_ignore_episodes_with_no_absolute_episode_in_distinct_by_absolute()
|
||||
{
|
||||
@@ -427,14 +338,14 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_regular_season_when_absolute_numbers_conflict()
|
||||
public void should_match_anime_episodes_by_season_and_episode_numbers()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.Build()
|
||||
.ToList();
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
episodes[0].AbsoluteEpisodeNumber = episodes[1].AbsoluteEpisodeNumber;
|
||||
episodes[0].SeasonNumber = 0;
|
||||
episodes[0].AbsoluteEpisodeNumber = null;
|
||||
episodes[0].SeasonNumber.Should().NotBe(episodes[1].SeasonNumber);
|
||||
episodes[0].EpisodeNumber.Should().NotBe(episodes[1].EpisodeNumber);
|
||||
|
||||
var existingEpisode = new Episode
|
||||
@@ -449,9 +360,41 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(episodes[0].SeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[0].EpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[0].AbsoluteEpisodeNumber);
|
||||
|
||||
_insertedEpisodes.First().SeasonNumber.Should().Be(episodes[1].SeasonNumber);
|
||||
_insertedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber);
|
||||
_insertedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[1].AbsoluteEpisodeNumber);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_mark_updated_episodes_that_have_newly_added_absolute_episode_number()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(3)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var existingEpisodes = new List<Episode>
|
||||
{
|
||||
episodes[0],
|
||||
episodes[1]
|
||||
};
|
||||
|
||||
existingEpisodes[0].AbsoluteEpisodeNumber = null;
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(existingEpisodes);
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(episodes[1].SeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[1].AbsoluteEpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().NotBeNull();
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumberAdded.Should().BeTrue();
|
||||
|
||||
_insertedEpisodes.Any(e => e.AbsoluteEpisodeNumberAdded).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace NzbDrone.Core.Blocklisting
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string TorrentInfoHash { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
|
||||
@@ -194,6 +194,11 @@ namespace NzbDrone.Core.Blocklisting
|
||||
blocklist.IndexerFlags = flags;
|
||||
}
|
||||
|
||||
if (Enum.TryParse(message.Data.GetValueOrDefault("releaseType"), true, out ReleaseType releaseType))
|
||||
{
|
||||
blocklist.ReleaseType = releaseType;
|
||||
}
|
||||
|
||||
_blocklistRepository.Insert(blocklist);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace NzbDrone.Core.CustomFormats
|
||||
Series = remoteEpisode.Series,
|
||||
Size = size,
|
||||
Languages = remoteEpisode.Languages,
|
||||
IndexerFlags = remoteEpisode.Release?.IndexerFlags ?? 0
|
||||
IndexerFlags = remoteEpisode.Release?.IndexerFlags ?? 0,
|
||||
ReleaseType = remoteEpisode.ParsedEpisodeInfo.ReleaseType
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input);
|
||||
@@ -76,7 +77,8 @@ namespace NzbDrone.Core.CustomFormats
|
||||
Series = series,
|
||||
Size = blocklist.Size ?? 0,
|
||||
Languages = blocklist.Languages,
|
||||
IndexerFlags = blocklist.IndexerFlags
|
||||
IndexerFlags = blocklist.IndexerFlags,
|
||||
ReleaseType = blocklist.ReleaseType
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input);
|
||||
@@ -88,6 +90,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
|
||||
long.TryParse(history.Data.GetValueOrDefault("size"), out var size);
|
||||
Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags indexerFlags);
|
||||
Enum.TryParse(history.Data.GetValueOrDefault("releaseType"), out ReleaseType releaseType);
|
||||
|
||||
var episodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
@@ -104,7 +107,8 @@ namespace NzbDrone.Core.CustomFormats
|
||||
Series = series,
|
||||
Size = size,
|
||||
Languages = history.Languages,
|
||||
IndexerFlags = indexerFlags
|
||||
IndexerFlags = indexerFlags,
|
||||
ReleaseType = releaseType
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input);
|
||||
@@ -128,6 +132,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
Size = localEpisode.Size,
|
||||
Languages = localEpisode.Languages,
|
||||
IndexerFlags = localEpisode.IndexerFlags,
|
||||
ReleaseType = localEpisode.ReleaseType,
|
||||
Filename = Path.GetFileName(localEpisode.Path)
|
||||
};
|
||||
|
||||
@@ -188,7 +193,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
ReleaseTitle = releaseTitle,
|
||||
Quality = episodeFile.Quality,
|
||||
Languages = episodeFile.Languages,
|
||||
ReleaseGroup = episodeFile.ReleaseGroup
|
||||
ReleaseGroup = episodeFile.ReleaseGroup,
|
||||
};
|
||||
|
||||
var input = new CustomFormatInput
|
||||
@@ -198,7 +203,8 @@ namespace NzbDrone.Core.CustomFormats
|
||||
Size = episodeFile.Size,
|
||||
Languages = episodeFile.Languages,
|
||||
IndexerFlags = episodeFile.IndexerFlags,
|
||||
Filename = Path.GetFileName(episodeFile.RelativePath)
|
||||
ReleaseType = episodeFile.ReleaseType,
|
||||
Filename = Path.GetFileName(episodeFile.RelativePath),
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input, allCustomFormats);
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public string Filename { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
|
||||
public CustomFormatInput()
|
||||
{
|
||||
|
||||
@@ -11,11 +11,11 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public IndexerFlagSpecificationValidator()
|
||||
{
|
||||
RuleFor(c => c.Value).NotEmpty();
|
||||
RuleFor(c => c.Value).Custom((qualityValue, context) =>
|
||||
RuleFor(c => c.Value).Custom((flag, context) =>
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(IndexerFlags), qualityValue))
|
||||
if (!Enum.IsDefined(typeof(IndexerFlags), flag))
|
||||
{
|
||||
context.AddFailure($"Invalid indexer flag condition value: {qualityValue}");
|
||||
context.AddFailure($"Invalid indexer flag condition value: {flag}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class SeasonPackSpecificationValidator : AbstractValidator<SeasonPackSpecification>
|
||||
{
|
||||
public SeasonPackSpecificationValidator()
|
||||
{
|
||||
RuleFor(c => c.Value).Custom((releaseType, context) =>
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(ReleaseType), releaseType))
|
||||
{
|
||||
context.AddFailure($"Invalid release type condition value: {releaseType}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class SeasonPackSpecification : CustomFormatSpecificationBase
|
||||
{
|
||||
private static readonly SeasonPackSpecificationValidator Validator = new ();
|
||||
|
||||
public override int Order => 10;
|
||||
public override string ImplementationName => "Release Type";
|
||||
|
||||
[FieldDefinition(1, Label = "ReleaseType", Type = FieldType.Select, SelectOptions = typeof(ReleaseType))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
return input.ReleaseType == (ReleaseType)Value;
|
||||
}
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/NzbDrone.Core/Datastore/Migration/203_release_type.cs
Normal file
58
src/NzbDrone.Core/Datastore/Migration/203_release_type.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(203)]
|
||||
public class release_type : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Blocklist").AddColumn("ReleaseType").AsInt32().WithDefaultValue(0);
|
||||
Alter.Table("EpisodeFiles").AddColumn("ReleaseType").AsInt32().WithDefaultValue(0);
|
||||
|
||||
Execute.WithConnection(UpdateEpisodeFiles);
|
||||
}
|
||||
|
||||
private void UpdateEpisodeFiles(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updates = new List<object>();
|
||||
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tran;
|
||||
cmd.CommandText = "SELECT \"Id\", \"OriginalFilePath\" FROM \"EpisodeFiles\" WHERE \"OriginalFilePath\" IS NOT NULL";
|
||||
|
||||
using var reader = cmd.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var originalFilePath = reader.GetString(1);
|
||||
|
||||
var folderName = Path.GetDirectoryName(originalFilePath);
|
||||
var fileName = Path.GetFileNameWithoutExtension(originalFilePath);
|
||||
var title = folderName.IsNullOrWhiteSpace() ? fileName : folderName;
|
||||
var parsedEpisodeInfo = Parser.Parser.ParseTitle(title);
|
||||
|
||||
if (parsedEpisodeInfo != null && parsedEpisodeInfo.ReleaseType != ReleaseType.Unknown)
|
||||
{
|
||||
updates.Add(new
|
||||
{
|
||||
Id = id,
|
||||
ReleaseType = (int)parsedEpisodeInfo.ReleaseType
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateEpisodeFilesSql = "UPDATE \"EpisodeFiles\" SET \"ReleaseType\" = @ReleaseType WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateEpisodeFilesSql, updates, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
.Configure<ProcessorOptions>(opt =>
|
||||
{
|
||||
opt.PreviewOnly = false;
|
||||
opt.Timeout = TimeSpan.FromSeconds(60);
|
||||
opt.Timeout = TimeSpan.FromMinutes(5);
|
||||
})
|
||||
.Configure<SelectingProcessorAccessorOptions>(cfg =>
|
||||
{
|
||||
|
||||
@@ -82,7 +82,8 @@ namespace NzbDrone.Core.Datastore
|
||||
.Ignore(i => i.Enable);
|
||||
|
||||
Mapper.Entity<ImportListItemInfo>("ImportListItems").RegisterModel()
|
||||
.Ignore(i => i.ImportList);
|
||||
.Ignore(i => i.ImportList)
|
||||
.Ignore(i => i.Seasons);
|
||||
|
||||
Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel()
|
||||
.Ignore(x => x.ImplementationName)
|
||||
@@ -126,6 +127,7 @@ namespace NzbDrone.Core.Datastore
|
||||
.Ignore(e => e.SeriesTitle)
|
||||
.Ignore(e => e.Series)
|
||||
.Ignore(e => e.HasFile)
|
||||
.Ignore(e => e.AbsoluteEpisodeNumberAdded)
|
||||
.HasOne(s => s.EpisodeFile, s => s.EpisodeFileId);
|
||||
|
||||
Mapper.Entity<QualityDefinition>("QualityDefinitions").RegisterModel()
|
||||
|
||||
@@ -205,11 +205,11 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
DeleteItemData(item);
|
||||
}
|
||||
|
||||
_proxy.RemoveFrom("history", item.DownloadId, deleteData, Settings);
|
||||
_proxy.RemoveFromHistory(item.DownloadId, deleteData, item.Status == DownloadItemStatus.Failed, Settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
_proxy.RemoveFrom("queue", item.DownloadId, deleteData, Settings);
|
||||
_proxy.RemoveFromQueue(item.DownloadId, deleteData, Settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
{
|
||||
string GetBaseUrl(SabnzbdSettings settings, string relativePath = null);
|
||||
SabnzbdAddResponse DownloadNzb(byte[] nzbData, string filename, string category, int priority, SabnzbdSettings settings);
|
||||
void RemoveFrom(string source, string id, bool deleteData, SabnzbdSettings settings);
|
||||
void RemoveFromQueue(string id, bool deleteData, SabnzbdSettings settings);
|
||||
void RemoveFromHistory(string id, bool deleteData, bool deletePermanently, SabnzbdSettings settings);
|
||||
string GetVersion(SabnzbdSettings settings);
|
||||
SabnzbdConfig GetConfig(SabnzbdSettings settings);
|
||||
SabnzbdFullStatus GetFullStatus(SabnzbdSettings settings);
|
||||
@@ -60,9 +61,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
return response;
|
||||
}
|
||||
|
||||
public void RemoveFrom(string source, string id, bool deleteData, SabnzbdSettings settings)
|
||||
public void RemoveFromQueue(string id, bool deleteData, SabnzbdSettings settings)
|
||||
{
|
||||
var request = BuildRequest(source, settings);
|
||||
var request = BuildRequest("queue", settings);
|
||||
request.AddQueryParam("name", "delete");
|
||||
request.AddQueryParam("del_files", deleteData ? 1 : 0);
|
||||
request.AddQueryParam("value", id);
|
||||
@@ -70,6 +71,17 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public void RemoveFromHistory(string id, bool deleteData, bool deletePermanently, SabnzbdSettings settings)
|
||||
{
|
||||
var request = BuildRequest("history", settings);
|
||||
request.AddQueryParam("name", "delete");
|
||||
request.AddQueryParam("del_files", deleteData ? 1 : 0);
|
||||
request.AddQueryParam("value", id);
|
||||
request.AddQueryParam("archive", deletePermanently ? 0 : 1);
|
||||
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public string GetVersion(SabnzbdSettings settings)
|
||||
{
|
||||
var request = BuildRequest("version", settings);
|
||||
|
||||
@@ -170,6 +170,7 @@ namespace NzbDrone.Core.History
|
||||
history.Data.Add("SeriesMatchType", message.Episode.SeriesMatchType.ToString());
|
||||
history.Data.Add("ReleaseSource", message.Episode.ReleaseSource.ToString());
|
||||
history.Data.Add("IndexerFlags", message.Episode.Release.IndexerFlags.ToString());
|
||||
history.Data.Add("ReleaseType", message.Episode.ParsedEpisodeInfo.ReleaseType.ToString());
|
||||
|
||||
if (!message.Episode.ParsedEpisodeInfo.ReleaseHash.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -222,6 +223,7 @@ namespace NzbDrone.Core.History
|
||||
history.Data.Add("CustomFormatScore", message.EpisodeInfo.CustomFormatScore.ToString());
|
||||
history.Data.Add("Size", message.EpisodeInfo.Size.ToString());
|
||||
history.Data.Add("IndexerFlags", message.ImportedEpisode.IndexerFlags.ToString());
|
||||
history.Data.Add("ReleaseType", message.ImportedEpisode.ReleaseType.ToString());
|
||||
|
||||
_historyRepository.Insert(history);
|
||||
}
|
||||
@@ -283,6 +285,7 @@ namespace NzbDrone.Core.History
|
||||
history.Data.Add("ReleaseGroup", message.EpisodeFile.ReleaseGroup);
|
||||
history.Data.Add("Size", message.EpisodeFile.Size.ToString());
|
||||
history.Data.Add("IndexerFlags", message.EpisodeFile.IndexerFlags.ToString());
|
||||
history.Data.Add("ReleaseType", message.EpisodeFile.ReleaseType.ToString());
|
||||
|
||||
_historyRepository.Insert(history);
|
||||
}
|
||||
@@ -315,6 +318,7 @@ namespace NzbDrone.Core.History
|
||||
history.Data.Add("ReleaseGroup", message.EpisodeFile.ReleaseGroup);
|
||||
history.Data.Add("Size", message.EpisodeFile.Size.ToString());
|
||||
history.Data.Add("IndexerFlags", message.EpisodeFile.IndexerFlags.ToString());
|
||||
history.Data.Add("ReleaseType", message.EpisodeFile.ReleaseType.ToString());
|
||||
|
||||
_historyRepository.Insert(history);
|
||||
}
|
||||
@@ -343,6 +347,7 @@ namespace NzbDrone.Core.History
|
||||
history.Data.Add("Message", message.Message);
|
||||
history.Data.Add("ReleaseGroup", message.TrackedDownload?.RemoteEpisode?.ParsedEpisodeInfo?.ReleaseGroup);
|
||||
history.Data.Add("Size", message.TrackedDownload?.DownloadItem.TotalSize.ToString());
|
||||
history.Data.Add("ReleaseType", message.TrackedDownload?.RemoteEpisode?.ParsedEpisodeInfo?.ReleaseType.ToString());
|
||||
|
||||
historyToAdd.Add(history);
|
||||
}
|
||||
|
||||
@@ -222,11 +222,14 @@ namespace NzbDrone.Core.ImportLists
|
||||
QualityProfileId = importList.QualityProfileId,
|
||||
SeriesType = importList.SeriesType,
|
||||
SeasonFolder = importList.SeasonFolder,
|
||||
Seasons = item.Seasons,
|
||||
Tags = importList.Tags,
|
||||
AddOptions = new AddSeriesOptions
|
||||
{
|
||||
SearchForMissingEpisodes = importList.SearchForMissingEpisodes,
|
||||
Monitor = importList.ShouldMonitor
|
||||
|
||||
// If seasons are provided use them for syncing monitored status, otherwise use the list setting.
|
||||
Monitor = item.Seasons.Any() ? MonitorTypes.Skip : importList.ShouldMonitor
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
public int QualityProfileId { get; set; }
|
||||
public int LanguageProfileId { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
public List<SonarrSeason> Seasons { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
}
|
||||
|
||||
@@ -35,4 +36,10 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
public string Path { get; set; }
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public class SonarrSeason
|
||||
{
|
||||
public int SeasonNumber { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
@@ -61,11 +62,22 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
continue;
|
||||
}
|
||||
|
||||
series.Add(new ImportListItemInfo
|
||||
var info = new ImportListItemInfo
|
||||
{
|
||||
TvdbId = item.TvdbId,
|
||||
Title = item.Title
|
||||
});
|
||||
};
|
||||
|
||||
if (Settings.SyncSeasonMonitoring)
|
||||
{
|
||||
info.Seasons = item.Seasons.Select(s => new Season
|
||||
{
|
||||
SeasonNumber = s.SeasonNumber,
|
||||
Monitored = s.Monitored
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
series.Add(info);
|
||||
}
|
||||
|
||||
_importListStatusService.RecordSuccess(Definition.Id);
|
||||
|
||||
@@ -35,12 +35,11 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
[FieldDefinition(1, Label = "ApiKey", HelpText = "ImportListsSonarrSettingsApiKeyHelpText")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(2, Type = FieldType.Select, SelectOptionsProviderAction = "getProfiles", Label = "QualityProfiles", HelpText = "ImportListsSonarrSettingsQualityProfilesHelpText")]
|
||||
public IEnumerable<int> ProfileIds { get; set; }
|
||||
[FieldDefinition(2, Label = "ImportListsSonarrSettingsSyncSeasonMonitoring", HelpText = "ImportListsSonarrSettingsSyncSeasonMonitoringHelpText", Type = FieldType.Checkbox)]
|
||||
public bool SyncSeasonMonitoring { get; set; }
|
||||
|
||||
// TODO: Remove this eventually, no translation added as deprecated
|
||||
[FieldDefinition(3, Type = FieldType.Select, SelectOptionsProviderAction = "getLanguageProfiles", Label = "Language Profiles", HelpText = "Language Profiles from the source instance to import from")]
|
||||
public IEnumerable<int> LanguageProfileIds { get; set; }
|
||||
[FieldDefinition(3, Type = FieldType.Select, SelectOptionsProviderAction = "getProfiles", Label = "QualityProfiles", HelpText = "ImportListsSonarrSettingsQualityProfilesHelpText")]
|
||||
public IEnumerable<int> ProfileIds { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Select, SelectOptionsProviderAction = "getTags", Label = "Tags", HelpText = "ImportListsSonarrSettingsTagsHelpText")]
|
||||
public IEnumerable<int> TagIds { get; set; }
|
||||
@@ -48,6 +47,10 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
[FieldDefinition(5, Type = FieldType.Select, SelectOptionsProviderAction = "getRootFolders", Label = "RootFolders", HelpText = "ImportListsSonarrSettingsRootFoldersHelpText")]
|
||||
public IEnumerable<string> RootFolderPaths { get; set; }
|
||||
|
||||
// TODO: Remove this eventually, no translation added as deprecated
|
||||
[FieldDefinition(6, Type = FieldType.Select, SelectOptionsProviderAction = "getLanguageProfiles", Label = "Language Profiles", HelpText = "Language Profiles from the source instance to import from")]
|
||||
public IEnumerable<int> LanguageProfileIds { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -422,7 +422,7 @@
|
||||
"DownloadClientSabnzbdValidationEnableDisableDateSortingDetail": "Sie müssen die Datumssortierung für die von {appName} verwendete Kategorie deaktivieren, um Importprobleme zu vermeiden. Gehen Sie zu Sabnzbd, um das Problem zu beheben.",
|
||||
"DownloadClientSabnzbdValidationEnableDisableMovieSorting": "Deaktivieren Sie die Filmsortierung",
|
||||
"AllResultsAreHiddenByTheAppliedFilter": "Alle Ergebnisse werden durch den angewendeten Filter ausgeblendet",
|
||||
"RegularExpressionsCanBeTested": "Reguläre Ausdrücke können [hier] getestet werden (http://regexstorm.net/tester).",
|
||||
"RegularExpressionsCanBeTested": "Reguläre Ausdrücke können [hier] getestet werden ({url}).",
|
||||
"ReleaseSceneIndicatorUnknownSeries": "Unbekannte Folge oder Serie.",
|
||||
"RemoveFilter": "Filter entfernen",
|
||||
"RemoveFailedDownloadsHelpText": "Entfernen Sie fehlgeschlagene Downloads aus dem Download-Client-Verlauf",
|
||||
@@ -669,7 +669,7 @@
|
||||
"AppUpdatedVersion": "{appName} wurde auf die Version „{version}“ aktualisiert. Um die neuesten Änderungen zu erhalten, müssen Sie {appName} neu laden ",
|
||||
"UseHardlinksInsteadOfCopy": "Verwende Hardlinks statt Kopieren",
|
||||
"DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "Der Download-Client {downloadClientName} ist so eingestellt, dass abgeschlossene Downloads entfernt werden. Dies kann dazu führen, dass Downloads von Ihrem Client entfernt werden, bevor {appName} sie importieren kann.",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Informationen zu Serien und Episoden werden von TheTVDB.com bereitgestellt. [Bitte denken Sie darüber nach, sie zu unterstützen](https://www.thetvdb.com/subscribe).",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Informationen zu Serien und Episoden werden von TheTVDB.com bereitgestellt. [Bitte denken Sie darüber nach, sie zu unterstützen]({url}).",
|
||||
"ShownClickToHide": "Angezeigt, zum Ausblenden klicken",
|
||||
"Tba": "Wird noch bekannt gegeben",
|
||||
"TaskUserAgentTooltip": "Benutzeragent, bereitgestellt von der App, die die API aufgerufen hat",
|
||||
@@ -694,7 +694,7 @@
|
||||
"Discord": "Discord",
|
||||
"Restart": "Neu starten",
|
||||
"Rejections": "Ablehnungen",
|
||||
"RegularExpressionsTutorialLink": "Weitere Details zu regulären Ausdrücken finden Sie [hier](https://www.regular-expressions.info/tutorial.html).",
|
||||
"RegularExpressionsTutorialLink": "Weitere Details zu regulären Ausdrücken finden Sie [hier]({url}).",
|
||||
"RegularExpression": "Regulären Ausdruck",
|
||||
"RenameEpisodes": "Episoden umbenennen",
|
||||
"RemovedSeriesSingleRemovedHealthCheckMessage": "Die Serie {series} wurde aus TheTVDB entfernt",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"ConnectionLostReconnect": "{appName} will try to connect automatically, or you can click reload below.",
|
||||
"ConnectionLostToBackend": "{appName} has lost its connection to the backend and will need to be reloaded to restore functionality.",
|
||||
"Connections": "Connections",
|
||||
"ConnectionSettingsUrlBaseHelpText": "Adds a prefix to the {connectionName} url, such as {url}",
|
||||
"Continuing": "Continuing",
|
||||
"ContinuingOnly": "Continuing Only",
|
||||
"ContinuingSeriesDescription": "More episodes/another season is expected",
|
||||
@@ -266,6 +267,7 @@
|
||||
"CreateGroup": "Create Group",
|
||||
"CurrentlyInstalled": "Currently Installed",
|
||||
"Custom": "Custom",
|
||||
"CustomFilter": "Custom Filter",
|
||||
"CustomFilters": "Custom Filters",
|
||||
"CustomFormat": "Custom Format",
|
||||
"CustomFormatHelpText": "{appName} scores each release using the sum of scores for matching custom formats. If a new release would improve the score, at the same or better quality, then {appName} will grab it.",
|
||||
@@ -696,6 +698,7 @@
|
||||
"FilterNotInNext": "not in the next",
|
||||
"FilterSeriesPlaceholder": "Filter series",
|
||||
"FilterStartsWith": "starts with",
|
||||
"Filters": "Filters",
|
||||
"FinaleTooltip": "Series or season finale",
|
||||
"FirstDayOfWeek": "First Day of Week",
|
||||
"Fixed": "Fixed",
|
||||
@@ -851,6 +854,8 @@
|
||||
"ImportListsSimklSettingsUserListTypePlanToWatch": "Plan To Watch",
|
||||
"ImportListsSimklSettingsUserListTypeWatching": "Watching",
|
||||
"ImportListsSonarrSettingsApiKeyHelpText": "API Key of the {appName} instance to import from",
|
||||
"ImportListsSonarrSettingsSyncSeasonMonitoring": "Sync Season Monitoring",
|
||||
"ImportListsSonarrSettingsSyncSeasonMonitoringHelpText": "Sync season monitoring from {appName} instance, if enabled 'Monitor' will be ignored",
|
||||
"ImportListsSonarrSettingsFullUrl": "Full URL",
|
||||
"ImportListsSonarrSettingsFullUrlHelpText": "URL, including port, of the {appName} instance to import from",
|
||||
"ImportListsSonarrSettingsQualityProfilesHelpText": "Quality Profiles from the source instance to import from",
|
||||
@@ -1024,6 +1029,8 @@
|
||||
"KeyboardShortcutsFocusSearchBox": "Focus Search Box",
|
||||
"KeyboardShortcutsOpenModal": "Open This Modal",
|
||||
"KeyboardShortcutsSaveSettings": "Save Settings",
|
||||
"Label": "Label",
|
||||
"LabelIsRequired": "Label is required",
|
||||
"Language": "Language",
|
||||
"Languages": "Languages",
|
||||
"LanguagesLoadError": "Unable to load languages",
|
||||
@@ -1079,7 +1086,7 @@
|
||||
"ManualGrab": "Manual Grab",
|
||||
"ManualImport": "Manual Import",
|
||||
"ManualImportItemsLoadError": "Unable to load manual import items",
|
||||
"MappedNetworkDrivesWindowsService": "Mapped network drives are not available when running as a Windows Service, see the [FAQ](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server) for more information.",
|
||||
"MappedNetworkDrivesWindowsService": "Mapped network drives are not available when running as a Windows Service, see the [FAQ]({url}) for more information.",
|
||||
"Mapping": "Mapping",
|
||||
"MarkAsFailed": "Mark as Failed",
|
||||
"MarkAsFailedConfirmation": "Are you sure you want to mark '{sourceTitle}' as failed?",
|
||||
@@ -1567,8 +1574,8 @@
|
||||
"RefreshAndScanTooltip": "Refresh information and scan disk",
|
||||
"RefreshSeries": "Refresh Series",
|
||||
"RegularExpression": "Regular Expression",
|
||||
"RegularExpressionsCanBeTested": "Regular expressions can be tested [here](http://regexstorm.net/tester).",
|
||||
"RegularExpressionsTutorialLink": "More details on regular expressions can be found [here](https://www.regular-expressions.info/tutorial.html).",
|
||||
"RegularExpressionsCanBeTested": "Regular expressions can be tested [here]({url}).",
|
||||
"RegularExpressionsTutorialLink": "More details on regular expressions can be found [here]({url}).",
|
||||
"RejectionCount": "Rejection Count",
|
||||
"Rejections": "Rejections",
|
||||
"RelativePath": "Relative Path",
|
||||
@@ -1590,6 +1597,7 @@
|
||||
"ReleaseSceneIndicatorUnknownMessage": "Numbering varies for this episode and release does not match any known mappings.",
|
||||
"ReleaseSceneIndicatorUnknownSeries": "Unknown episode or series.",
|
||||
"ReleaseTitle": "Release Title",
|
||||
"ReleaseType": "Release Type",
|
||||
"Reload": "Reload",
|
||||
"RemotePath": "Remote Path",
|
||||
"RemotePathMappingBadDockerPathHealthCheckMessage": "You are using docker; download client {downloadClientName} places downloads in {path} but this is not a valid {osName} path. Review your remote path mappings and download client settings.",
|
||||
@@ -1763,7 +1771,7 @@
|
||||
"SelectSeries": "Select Series",
|
||||
"SendAnonymousUsageData": "Send Anonymous Usage Data",
|
||||
"Series": "Series",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Series and episode information is provided by TheTVDB.com. [Please consider supporting them](https://www.thetvdb.com/subscribe).",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Series and episode information is provided by TheTVDB.com. [Please consider supporting them]({url}) .",
|
||||
"SeriesCannotBeFound": "Sorry, that series cannot be found.",
|
||||
"SeriesDetailsCountEpisodeFiles": "{episodeFileCount} episode files",
|
||||
"SeriesDetailsGoTo": "Go to {title}",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"Year": "Año",
|
||||
"Reload": "Recargar",
|
||||
"AbsoluteEpisodeNumber": "Número de Episodio Absoluto",
|
||||
"AddAutoTagError": "No se pudo añadir una nueva etiqueta automática, inténtelo de nuevo.",
|
||||
"AddAutoTagError": "No se pudo añadir una nueva etiqueta automática, por favor inténtalo de nuevo.",
|
||||
"AddConditionError": "No se pudo añadir una nueva condición, inténtelo de nuevo.",
|
||||
"AddConnection": "Añadir Conexión",
|
||||
"AddCustomFormat": "Añadir Formato Personalizado",
|
||||
@@ -1231,6 +1231,135 @@
|
||||
"MaximumSize": "Tamaño máximo",
|
||||
"IndexerValidationNoResultsInConfiguredCategories": "Petición con éxito, pero no se devolvió ningún resultado en las categorías configuradas de tu indexador. Esto puede ser un problema con el indexador o tus ajustes de categoría de tu indexador.",
|
||||
"IndexerValidationNoRssFeedQueryAvailable": "Ninguna consulta de canales RSS disponible. Esto puede ser un problema con el indexador o con los ajustes de categoría de tu indexador.",
|
||||
"MappedNetworkDrivesWindowsService": "Los discos de red mapeados no están disponibles cuando se ejecutan como un servicio de Windows, consulta el [FAQ](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server) para más información.",
|
||||
"MediaInfoFootNote": "MediaInfo Full/Idiomas de audio/Idiomas de subtítulo soporta un sufijo `:ES+DE` que te permite filtrar los idiomas incluidos en el nombre de archivo. Usa `-DE` para excluir idiomas específicos. Añadir `+` (eg `:ES+`) devolverá `[ES]`/`[ES+--]`/`[--]` dependiendo de los idiomas excluidos. Por ejemplo `{MediaInfo Full:ES+DE}`."
|
||||
"MappedNetworkDrivesWindowsService": "Los discos de red mapeados no están disponibles cuando se ejecutan como un servicio de Windows, consulta el [FAQ]({url}) para más información.",
|
||||
"MediaInfoFootNote": "MediaInfo Full/Idiomas de audio/Idiomas de subtítulo soporta un sufijo `:ES+DE` que te permite filtrar los idiomas incluidos en el nombre de archivo. Usa `-DE` para excluir idiomas específicos. Añadir `+` (eg `:ES+`) devolverá `[ES]`/`[ES+--]`/`[--]` dependiendo de los idiomas excluidos. Por ejemplo `{MediaInfo Full:ES+DE}`.",
|
||||
"MidseasonFinale": "Final de mitad de temporada",
|
||||
"Min": "Min",
|
||||
"MonitorFutureEpisodesDescription": "Monitoriza episodios que no se han emitido aún",
|
||||
"NoDelay": "Sin retraso",
|
||||
"NoImportListsFound": "Ninguna lista de importación encontrada",
|
||||
"Name": "Nombre",
|
||||
"No": "No",
|
||||
"NotificationTriggers": "Disparadores de notificación",
|
||||
"ClickToChangeIndexerFlags": "Clic para cambiar las banderas del indexador",
|
||||
"MinutesSixty": "60 minutos: {sixty}",
|
||||
"MonitorMissingEpisodesDescription": "Monitoriza episodios que no tienen archivos o que no se han emitido aún",
|
||||
"MoveFiles": "Mover archivos",
|
||||
"MustNotContain": "No debe contener",
|
||||
"NoEpisodeInformation": "No hay información de episodio disponible.",
|
||||
"NoLeaveIt": "No, déjalo",
|
||||
"NoLimitForAnyRuntime": "No hay límites para ningún tiempo de ejecución",
|
||||
"NoMatchFound": "¡Ninguna coincidencia encontrada!",
|
||||
"NoMinimumForAnyRuntime": "No hay mínimo para ningún tiempo de ejecución",
|
||||
"NoMonitoredEpisodes": "No hay episodios monitorizados en esta serie",
|
||||
"NotificationStatusSingleClientHealthCheckMessage": "Notificaciones no disponible debido a fallos: {notificationNames}",
|
||||
"CustomFormatsSpecificationFlag": "Bandera",
|
||||
"Never": "Nunca",
|
||||
"MinimumAge": "Edad mínima",
|
||||
"Mixed": "Mezclado",
|
||||
"MultiLanguages": "Multi-idiomas",
|
||||
"NoEpisodesFoundForSelectedSeason": "No se encontró ningún episodio para la temporada seleccionada",
|
||||
"NoEventsFound": "Ningún evento encontrado",
|
||||
"IndexerFlags": "Banderas del indexador",
|
||||
"CustomFilter": "Filtros personalizados",
|
||||
"Filters": "Filtros",
|
||||
"Label": "Etiqueta",
|
||||
"MonitorExistingEpisodes": "Episodios existentes",
|
||||
"MonitoringOptions": "Opciones de monitorización",
|
||||
"NoIssuesWithYourConfiguration": "No hay problemas con tu configuración",
|
||||
"NoSeriesHaveBeenAdded": "No has añadido ninguna serie aún. ¿Quieres importar alguna o todas tus series primero?",
|
||||
"MetadataXmbcSettingsSeriesMetadataEpisodeGuideHelpText": "Incluye elemento de guía de episodios formateados en JSON en tvshow.nfo (Requiere 'Metadatos de series')",
|
||||
"MinimumAgeHelpText": "Solo Usenet: Edad mínima en minutos de NZBs antes de que sean capturados. Usa esto para dar tiempo a los nuevos lanzamientos para propagarse a tu proveedor usenet.",
|
||||
"MinimumFreeSpace": "Espacio libre mínimo",
|
||||
"MonitorAllEpisodesDescription": "Monitoriza todos los episodios salvo especiales",
|
||||
"MonitorAllSeasonsDescription": "Monitoriza todas las nuevas temporadas automáticamente",
|
||||
"MonitorExistingEpisodesDescription": "Monitoriza episodios que tienen archivos o que no se han emitido aún",
|
||||
"MonitorFirstSeason": "Primera temporada",
|
||||
"MonitorNewSeasonsHelpText": "Qué nuevas temporadas deberían ser monitorizadas automáticamente",
|
||||
"MonitoredStatus": "Monitorizados/Estado",
|
||||
"MoveSeriesFoldersDontMoveFiles": "No, moveré los archivos yo mismo",
|
||||
"MultiEpisode": "Multi-episodio",
|
||||
"MultiEpisodeInvalidFormat": "Multi-episodio: Formato inválido",
|
||||
"MultiSeason": "Multi-temporada",
|
||||
"NamingSettingsLoadError": "No se pudo cargar las opciones de nombrado",
|
||||
"NoChanges": "Sin cambios",
|
||||
"NoEpisodeOverview": "No hay sinopsis de episodio",
|
||||
"MultiEpisodeStyle": "Estilo de multi-episodio",
|
||||
"NotificationStatusAllClientHealthCheckMessage": "Las notificaciones no están disponibles debido a fallos",
|
||||
"NotificationsAppriseSettingsConfigurationKeyHelpText": "Clave de configuración para la Solución de almacenamiento persistente. Dejar vacío si se usan URLs sin estado.",
|
||||
"NotificationsAppriseSettingsPasswordHelpText": "Autenticación básica HTTP de contraseña",
|
||||
"Monitoring": "Monitorizando",
|
||||
"MustContainHelpText": "El lanzamiento debe contener al menos uno de estos términos (insensible a mayúsculas)",
|
||||
"NotificationTriggersHelpText": "Selecciona qué eventos deberían disparar esta notificación",
|
||||
"None": "Ninguno",
|
||||
"Network": "Red",
|
||||
"MetadataXmbcSettingsEpisodeMetadataImageThumbsHelpText": "Incluye etiquetas de imagen en miniatura en <nombre de archivo>.nfo (Requiere 'Metadatos de episodio')",
|
||||
"MetadataXmbcSettingsSeriesMetadataHelpText": "tvshow.nfo con metadatos completos de series",
|
||||
"MetadataXmbcSettingsSeriesMetadataUrlHelpText": "Incluye la URL de show de TheTVDB en tvshow.nfo (puede ser combinado con 'Metadatos de series')",
|
||||
"MustNotContainHelpText": "El lanzamiento será rechazado si contiene uno o más de estos términos (insensible a mayúsculas)",
|
||||
"NamingSettings": "Opciones de nombrado",
|
||||
"MustContain": "Debe contener",
|
||||
"NotificationsAppriseSettingsTags": "Etiquetas de Apprise",
|
||||
"NotificationsAppriseSettingsTagsHelpText": "Opcionalmente notifica solo esas etiquetas en consecuencia.",
|
||||
"NotificationsAppriseSettingsUsernameHelpText": "Autenticación básica HTTP de usuario",
|
||||
"NotificationsCustomScriptSettingsArguments": "Argumentos",
|
||||
"NotificationsCustomScriptSettingsArgumentsHelpText": "Argumentos a pasar al script",
|
||||
"NotificationsCustomScriptValidationFileDoesNotExist": "El archivo no existe",
|
||||
"MinimumCustomFormatScoreHelpText": "Puntuación mínima de formato personalizado permitida para descargar",
|
||||
"MinimumLimits": "Límites mínimos",
|
||||
"Monday": "Lunes",
|
||||
"MyComputer": "Mi ordenador",
|
||||
"NoChange": "Sin cambio",
|
||||
"NoTagsHaveBeenAddedYet": "Ninguna etiqueta ha sido añadida aún",
|
||||
"NoUpdatesAreAvailable": "No hay actualizaciones disponibles",
|
||||
"MoveSeriesFoldersMoveFiles": "Sí, mueve los archivos",
|
||||
"MoveSeriesFoldersToNewPath": "¿Te gustaría mover los archivos de series de '{originalPath}' a '{destinationPath}'?",
|
||||
"MoveSeriesFoldersToRootFolder": "¿Te gustaría mover las carpetas de series a '{destinationRootFolder}'?",
|
||||
"NoMonitoredEpisodesSeason": "No hay episodios monitorizados en esta temporada",
|
||||
"NoResultsFound": "Ningún resultado encontrado",
|
||||
"NoSeasons": "No hay temporadas",
|
||||
"MinimumFreeSpaceHelpText": "Evita importar si se quedaría menos que esta cantidad de disco disponible",
|
||||
"MonitorFirstSeasonDescription": "Monitoriza todos los episodios de la primera temporada. El resto de temporadas serán ignoradas",
|
||||
"MonitorLastSeasonDescription": "Monitoriza todos los episodios de la última temporada",
|
||||
"MonitorFutureEpisodes": "Episodios futuros",
|
||||
"MonitorNewSeasons": "Monitorizar nuevas temporadas",
|
||||
"NoBackupsAreAvailable": "No hay copias de seguridad disponibles",
|
||||
"Negated": "Anulado",
|
||||
"NextAiring": "Siguiente emisión",
|
||||
"NextExecution": "Siguiente ejecución",
|
||||
"MonitorLastSeason": "Última temporada",
|
||||
"MonitorNoEpisodesDescription": "Ningún episodio será monitorizado",
|
||||
"MinimumCustomFormatScore": "Puntuación mínima de formato personalizado",
|
||||
"MinutesThirty": "30 minutos: {thirty}",
|
||||
"MinutesFortyFive": "45 minutos: {fortyFive}",
|
||||
"Monitor": "Monitorizar",
|
||||
"MonitorAllEpisodes": "Todos los episodios",
|
||||
"MonitorAllSeasons": "Todas las temporadas",
|
||||
"NotificationsCustomScriptSettingsProviderMessage": "El test ejecutará el script con el EventType establecido en {eventTypeSet}, asegúrate de que tu script maneja esto correctamente",
|
||||
"NotificationsDiscordSettingsAvatar": "Avatar",
|
||||
"NotificationsDiscordSettingsAvatarHelpText": "Cambia el avatar que es usado para mensajes desde esta integración",
|
||||
"NotificationsAppriseSettingsNotificationType": "Tipo de notificación de Apprise",
|
||||
"NotificationsAppriseSettingsConfigurationKey": "Clave de configuración de Apprise",
|
||||
"NotificationsAppriseSettingsServerUrl": "URL de servidor de Apprise",
|
||||
"NotificationsAppriseSettingsStatelessUrls": "URLs sin estado de Apprise",
|
||||
"NotificationsAppriseSettingsStatelessUrlsHelpText": "Una o más URLs separadas por comas identificando a dónde debería ser enviada la notificación. Dejar vacío si se usa Almacenamiento persistente.",
|
||||
"NotificationsAppriseSettingsServerUrlHelpText": "URL de servidor de Apprise, incluyendo http(s):// y puerto si es necesario",
|
||||
"MonitoredOnly": "Solo monitorizados",
|
||||
"NoLogFiles": "No hay archivos de registro",
|
||||
"NoSeriesFoundImportOrAdd": "Ninguna serie encontrada, para comenzar querrás importar tus series existentes o añadir una nueva serie.",
|
||||
"NotSeasonPack": "No hay paquete de temporada",
|
||||
"NotificationsDiscordSettingsAuthorHelpText": "Sobrescribe el autor incrustado que se muestra para esta notificación. En blanco es el nombre de la instancia",
|
||||
"MonitorNewItems": "Monitorizar nuevos elementos",
|
||||
"MonitoredEpisodesHelpText": "Descargar episodios monitorizados en estas series",
|
||||
"NegateHelpText": "Si se elige, el formato personalizado no se aplica si coincide la condición {implementationName}.",
|
||||
"NotificationsCustomScriptSettingsName": "Script personalizado",
|
||||
"ImportListsSonarrSettingsSyncSeasonMonitoring": "Sincronizar la monitorización de temporada",
|
||||
"ImportListsSonarrSettingsSyncSeasonMonitoringHelpText": "Sincroniza la monitorización de temporada de la instancia de {appName}, si se habilita 'Monitorizar' será ignorado",
|
||||
"LabelIsRequired": "Se requiere etiqueta",
|
||||
"Month": "Mes",
|
||||
"More": "Más",
|
||||
"MoreDetails": "Más detalles",
|
||||
"MoreInfo": "Más información",
|
||||
"NoEpisodesInThisSeason": "No hay episodios en esta temporada",
|
||||
"NoLinks": "No hay enlaces"
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@
|
||||
"RemotePathMappingDockerFolderMissingHealthCheckMessage": "Käytät Dockeria ja lataustyökalu \"{downloadClientName}\" tallentaa lataukset kohteeseen \"{path}\", mutta sitä ei löydy Docker-säiliöstä. Tarkista etäsijaintien kohdistukset ja säiliön tallennusmedian asetukset.",
|
||||
"TorrentBlackholeSaveMagnetFilesHelpText": "Tallenna magnet-linkki, jos .torrent-tiedostoa ei ole käytettävissä (hyödyllinen vain lataustyökalun tukiessa tiedostoon tallennettuja magnet-linkkejä).",
|
||||
"IndexerPriorityHelpText": "Tietolähteen painotus, 1– 50 (korkein-alin). Oletusarvo on 25. Käytetään muutoin tasaveroisten julkaisujen kaappauspäätökseen. Kaikkia käytössä olevia tietolähteitä käytetään edelleen RSS-synkronointiin ja hakuun.",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Sarjojen ja jaksojen tiedot tarjoaa TheTVDB.com. [Harkitse palvelun tukemista](https://www.thetvdb.com/subscribe)",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Sarjojen ja jaksojen tiedot tarjoaa TheTVDB.com. [Harkitse palvelun tukemista]({url})",
|
||||
"DownloadClientQbittorrentValidationRemovesAtRatioLimitDetail": "{appName} ei voi suorittaa valmistuneiden latausten hallintaa määritetyllä tavalla. Voit korjata tämän vaihtamalla qBittorentin asetusten \"BitTorrent\"-osion \"Jakosuhderajoitukset\"-osion toiminnoksi pysäytyksen poiston sijaan.",
|
||||
"FullColorEventsHelpText": "Vaihtoehtoinen tyyli, jossa koko tapahtuma väritetään tilavärillä pelkän vasemman laidan sijaan. Ei vaikuta agendan esitykseen.",
|
||||
"Yesterday": "Eilen",
|
||||
@@ -1727,7 +1727,7 @@
|
||||
"DownloadClientSettingsOlderPriorityEpisodeHelpText": "Yli 14 päivää sitten julkaistujen jaksojen kaappauksille käytettävä painotus.",
|
||||
"ImportListsTraktSettingsGenresHelpText": "Suodata sarjoja Trakt-lajityyppien slug-arvoilla (pilkuin eroteltuna). Koskee vain suosituimpia listoja.",
|
||||
"ImportListsTraktSettingsWatchedListFilterHelpText": "Jos \"Listan tyyppi\" on \"Valvottu\", valitse sarjatyyppi, jonka haluat tuoda.",
|
||||
"MappedNetworkDrivesWindowsService": "Yhdistetyt verkkoasemat eivät ole käytettävissä kun sovellus suoritetaan Windows-palveluna. Saat lisätietoja [UKK:sta](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server).",
|
||||
"MappedNetworkDrivesWindowsService": "Yhdistetyt verkkoasemat eivät ole käytettävissä kun sovellus suoritetaan Windows-palveluna. Saat lisätietoja [UKK:sta]({url}).",
|
||||
"MetadataSettingsSeriesMetadata": "Sarjojen metatiedot",
|
||||
"MetadataSettingsSeriesMetadataUrl": "Sarjojen metatietojen URL",
|
||||
"MetadataSettingsSeriesMetadataEpisodeGuide": "Sarjojen metatietojen jakso-opas",
|
||||
@@ -1744,7 +1744,7 @@
|
||||
"UpgradesAllowedHelpText": "Jos käytöstä poistettuja laatuja ei päivitetä.",
|
||||
"Repack": "Uudelleenpaketoitu",
|
||||
"SupportedAutoTaggingProperties": "{appName} tukee automaattimerkinnän säännöissä seuraavia arvoja",
|
||||
"RegularExpressionsCanBeTested": "Säännöllisiä lausekkeita voidaan testata [täällä](http://regexstorm.net/tester).",
|
||||
"RegularExpressionsCanBeTested": "Säännöllisiä lausekkeita voidaan testata [täällä]({url}).",
|
||||
"RssSyncIntervalHelpTextWarning": "Tämä koskee kaikkia tietolähteitä. Noudata niiden asettamia sääntöjä.",
|
||||
"DownloadClientFreeboxSettingsApiUrlHelpText": "Määritä Freebox-rajapinnan perus-URL rajapinnan versiolla. Esimerkiksi \"{url}\". Oletus on \"{defaultApiUrl}\".",
|
||||
"DownloadClientFreeboxSettingsHostHelpText": "Freeboxin isäntänimi tai IP-osoite. Oletus on \"{url}\" (toimii vain samassa verkossa).",
|
||||
@@ -1764,7 +1764,7 @@
|
||||
"MetadataPlexSettingsSeriesPlexMatchFile": "Luo Plex Match -tiedostot",
|
||||
"MetadataPlexSettingsSeriesPlexMatchFileHelpText": "Luo sarjojen kansioihin .plexmatch-tiedostot.",
|
||||
"NoResultsFound": "Tuloksia ei löytynyt.",
|
||||
"RegularExpressionsTutorialLink": "Lisätietoja säännöllisistä lausekkeista löytyy [täältä](https://www.regular-expressions.info/tutorial.html).",
|
||||
"RegularExpressionsTutorialLink": "Lisätietoja säännöllisistä lausekkeista löytyy [täältä]({url}).",
|
||||
"ResetAPIKey": "Korvaa rajapinnan avain",
|
||||
"Reset": "Uudista",
|
||||
"ResetDefinitions": "Palauta määritykset",
|
||||
|
||||
@@ -647,7 +647,7 @@
|
||||
"RefreshAndScan": "Actualiser et analyser",
|
||||
"RefreshAndScanTooltip": "Actualiser les informations et analyser le disque",
|
||||
"RegularExpression": "Expression régulière",
|
||||
"RegularExpressionsTutorialLink": "Plus de détails sur les expressions régulières peuvent être trouvés [ici](https://www.regular-expressions.info/tutorial.html).",
|
||||
"RegularExpressionsTutorialLink": "Plus de détails sur les expressions régulières peuvent être trouvés [ici]({url}).",
|
||||
"RelativePath": "Chemin relatif",
|
||||
"Release": "Version",
|
||||
"ReleaseGroup": "Groupe de versions",
|
||||
@@ -703,7 +703,7 @@
|
||||
"SelectFolder": "Sélectionner le dossier",
|
||||
"SelectLanguage": "Choisir la langue",
|
||||
"SendAnonymousUsageData": "Envoyer des données d'utilisation anonymes",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Les informations sur les séries et les épisodes sont fournies par TheTVDB.com. [Veuillez envisager de les soutenir](https://www.thetvdb.com/subscribe).",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "Les informations sur les séries et les épisodes sont fournies par TheTVDB.com. [Veuillez envisager de les soutenir]({url}).",
|
||||
"SeriesCannotBeFound": "Désolé, cette série est introuvable.",
|
||||
"SeriesDetailsCountEpisodeFiles": "{episodeFileCount} fichiers d'épisode",
|
||||
"SeriesDetailsRuntime": "{runtime} Minutes",
|
||||
@@ -933,7 +933,7 @@
|
||||
"OnEpisodeFileDeleteForUpgrade": "Lors de la suppression du fichier de l'épisode pour la mise à niveau",
|
||||
"OnGrab": "À saisir",
|
||||
"OnlyForBulkSeasonReleases": "Uniquement pour les versions de saison en masse",
|
||||
"RegularExpressionsCanBeTested": "Les expressions régulières peuvent être testées [ici](http://regexstorm.net/tester).",
|
||||
"RegularExpressionsCanBeTested": "Les expressions régulières peuvent être testées [ici]({url}).",
|
||||
"ReleaseProfileIndexerHelpText": "Spécifiez à quel indexeur le profil s'applique",
|
||||
"RemotePathMappings": "Mappages de chemins distants",
|
||||
"RescanAfterRefreshHelpTextWarning": "{appName} ne détectera pas automatiquement les modifications apportées aux fichiers lorsqu'il n'est pas défini sur 'Toujours'",
|
||||
@@ -1870,7 +1870,7 @@
|
||||
"EpisodeFileMissingTooltip": "Fichier de l'épisode manquant",
|
||||
"AutoTaggingSpecificationGenre": "Genre(s)",
|
||||
"AutoTaggingSpecificationMaximumYear": "Année maximum",
|
||||
"AutoTaggingSpecificationMinimumYear": "Année maximum",
|
||||
"AutoTaggingSpecificationMinimumYear": "Année minimum",
|
||||
"AutoTaggingSpecificationOriginalLanguage": "Langue",
|
||||
"AutoTaggingSpecificationQualityProfile": "Profil de Qualité",
|
||||
"AutoTaggingSpecificationRootFolder": "Dossier Racine",
|
||||
@@ -1921,5 +1921,7 @@
|
||||
"IgnoreDownloadsHint": "Empêche {appName} de poursuivre le traitement de ces téléchargements",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Rejeter les hachages de torrents bloqués lors de la saisie",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "Si un torrent est bloqué par le hachage, il peut ne pas être correctement rejeté pendant le RSS/recherche pour certains indexeurs. L'activation de cette fonction permet de le rejeter après que le torrent a été saisi, mais avant qu'il ne soit envoyé au client.",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Emplacement facultatif pour les téléchargements, laisser vide pour utiliser l'emplacement par défaut Aria2"
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Emplacement facultatif pour les téléchargements, laisser vide pour utiliser l'emplacement par défaut Aria2",
|
||||
"AddDelayProfileError": "Impossible d'ajouter un nouveau profil de délai, veuillez réessayer.",
|
||||
"BlocklistReleaseHelpText": "Bloque le téléchargement de cette version par {appName} via RSS ou Recherche automatique"
|
||||
}
|
||||
|
||||
@@ -627,7 +627,7 @@
|
||||
"LastWriteTime": "Utolsó írási idő",
|
||||
"LogFiles": "Naplófájlok",
|
||||
"ManualImport": "Kézi importálás",
|
||||
"MappedNetworkDrivesWindowsService": "A leképezett hálózati meghajtók nem érhetők el, ha Windows szolgáltatásként futnak, lásd a [GYIK](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server) for more information.",
|
||||
"MappedNetworkDrivesWindowsService": "A leképezett hálózati meghajtók nem érhetők el, ha Windows szolgáltatásként futnak, lásd a [GYIK]({url}) for more information.",
|
||||
"MediaManagementSettingsLoadError": "Nem sikerült betölteni a médiakezelési beállításokat",
|
||||
"MediaManagementSettings": "Médiakezelési beállítások",
|
||||
"NoMonitoredEpisodesSeason": "Ebben az évadban nincsenek felügyelt epizódok",
|
||||
@@ -856,7 +856,7 @@
|
||||
"DeleteAutoTagHelpText": "Biztosan törli a(z) „{name}” automatikus címkét?",
|
||||
"RecyclingBinHelpText": "A fájlok törléskor ide kerülnek a végleges törlés helyett",
|
||||
"ApplyTagsHelpTextReplace": "Csere: Cserélje ki a címkéket a megadott címkékkel (az összes címke törléséhez ne írjon be címkéket)",
|
||||
"RegularExpressionsCanBeTested": "A reguláris kifejezések [here] tesztelhetők (http://regexstorm.net/tester).",
|
||||
"RegularExpressionsCanBeTested": "A reguláris kifejezések [here] tesztelhetők ({url}).",
|
||||
"SelectLanguage": "Válasszon nyelvet",
|
||||
"SeriesEditor": "Sorozat szerkesztő",
|
||||
"SeriesFolderFormat": "Sorozat mappa formátum",
|
||||
@@ -901,7 +901,7 @@
|
||||
"RemotePathMappingHostHelpText": "Ugyanaz a gazdagép, amelyet a távoli letöltési klienshez megadott",
|
||||
"SelectQuality": "Minőség kiválasztása",
|
||||
"SeriesIndexFooterEnded": "Befejeződött (az összes epizód letöltve)",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "A sorozatokkal és epizódokkal kapcsolatos információkat a TheTVDB.com biztosítja. [Kérjük, fontolja meg támogatásukat](https://www.thetvdb.com/subscribe).",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "A sorozatokkal és epizódokkal kapcsolatos információkat a TheTVDB.com biztosítja. [Kérjük, fontolja meg támogatásukat]({url}).",
|
||||
"SeriesDetailsCountEpisodeFiles": "{episodeFileCount} epizódfájl",
|
||||
"SeriesIsMonitored": "A sorozatot figyelik",
|
||||
"SeriesTitle": "Sorozat címe",
|
||||
@@ -1405,5 +1405,10 @@
|
||||
"PendingChangesDiscardChanges": "Vesse el a változtatásokat, és lépjen ki",
|
||||
"SeasonPremiere": "Évad Premier",
|
||||
"SeasonPremieresOnly": "Csak az évad premierjei",
|
||||
"PasswordConfirmation": "Jelszó megerősítése"
|
||||
"PasswordConfirmation": "Jelszó megerősítése",
|
||||
"QualitiesHelpText": "A listán magasabb minőséget részesítik előnyben. Ugyanazon csoporton belül a tulajdonságok egyenlőek. Csak ellenőrzött minőségek szükségesek",
|
||||
"Progress": "Előrehalad",
|
||||
"ParseModalUnableToParse": "A megadott cím nem elemezhető, próbálkozzon újra.",
|
||||
"ParseModalHelpText": "Adja meg a kiadás címét a fenti bevitelben",
|
||||
"ProtocolHelpText": "Válassza ki, melyik protokoll(oka)t használja, és melyiket részesíti előnyben, ha az egyébként egyenlő kiadások közül választ"
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"AutoTaggingNegateHelpText": "Se marcada, a regra de etiqueta automática não será aplicada se esta condição {implementationName} corresponder.",
|
||||
"AddNew": "Adicionar Novo",
|
||||
"Age": "Idade",
|
||||
"AddAutoTagError": "Não foi possível adicionar uma nova etiqueta automática, tente novamente.",
|
||||
"AddAutoTagError": "Não foi possível adicionar uma nova tag automática. Por favor, tente novamente.",
|
||||
"AddConditionError": "Não foi possível adicionar uma nova condição, tente novamente.",
|
||||
"AddConnection": "Adicionar Conexão",
|
||||
"AddCustomFormat": "Adicionar formato personalizado",
|
||||
|
||||
@@ -768,8 +768,8 @@
|
||||
"QualityProfileInUseSeriesListCollection": "Não é possível excluir um perfil de qualidade anexado a uma série, lista ou coleção",
|
||||
"EnableColorImpairedModeHelpText": "Estilo alterado para permitir que usuários com deficiência de cor distingam melhor as informações codificadas por cores",
|
||||
"RegularExpression": "Expressão Regular",
|
||||
"RegularExpressionsCanBeTested": "Expressões regulares podem ser testadas [aqui](http://regexstorm.net/tester).",
|
||||
"RegularExpressionsTutorialLink": "Mais detalhes sobre expressões regulares podem ser encontrados [aqui](https://www.regular-expressions.info/tutorial.html).",
|
||||
"RegularExpressionsCanBeTested": "Expressões regulares podem ser testadas [aqui]({url}).",
|
||||
"RegularExpressionsTutorialLink": "Mais detalhes sobre expressões regulares podem ser encontrados [aqui]({url}).",
|
||||
"ReleaseProfile": "Perfil de Lançamento",
|
||||
"ReleaseProfileIndexerHelpText": "Especifique a qual indexador o perfil se aplica",
|
||||
"ReleaseProfileIndexerHelpTextWarning": "Usar um indexador específico com perfis de lançamento pode levar à captura de lançamentos duplicados",
|
||||
@@ -821,7 +821,7 @@
|
||||
"SeasonFolderFormat": "Formato da Pasta da Temporada",
|
||||
"Security": "Segurança",
|
||||
"SendAnonymousUsageData": "Enviar dados de uso anônimos",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "As informações sobre séries e episódios são fornecidas por TheTVDB.com. [Por favor, considere apoiá-los](https://www.thetvdb.com/subscribe).",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "As informações sobre séries e episódios são fornecidas por TheTVDB.com. [Por favor, considere apoiá-los]({url}).",
|
||||
"SeriesFolderFormat": "Formato de Pasta das Séries",
|
||||
"SeriesFolderFormatHelpText": "Usado ao adicionar uma nova série ou mover uma série por meio do editor de séries",
|
||||
"SeriesID": "ID da Série",
|
||||
@@ -1123,7 +1123,7 @@
|
||||
"KeyboardShortcutsFocusSearchBox": "Selecionar a caixa de pesquisa",
|
||||
"KeyboardShortcutsSaveSettings": "Salvar configurações",
|
||||
"LocalStorageIsNotSupported": "O armazenamento local não é compatível ou está desabilitado. Um plugin ou a navegação privada pode tê-lo desativado.",
|
||||
"MappedNetworkDrivesWindowsService": "As unidades de rede mapeadas não estão disponíveis ao executar como um serviço do Windows, consulte as [Perguntas frequentes](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote -server) para saber mais.",
|
||||
"MappedNetworkDrivesWindowsService": "As unidades de rede mapeadas não estão disponíveis quando executadas como um serviço do Windows. Consulte as [FAQ]({url}) para obter mais informações.",
|
||||
"AirsTbaOn": "A ser anunciado em {networkLabel}",
|
||||
"AirsTimeOn": "{time} em {networkLabel}",
|
||||
"AirsTomorrowOn": "Amanhã às {time} em {networkLabel}",
|
||||
@@ -1872,8 +1872,8 @@
|
||||
"EpisodeFileMissingTooltip": "Arquivo do episódio ausente",
|
||||
"DownloadClientAriaSettingsDirectoryHelpText": "Local opcional para colocar downloads, deixe em branco para usar o local padrão do Aria2",
|
||||
"DownloadClientPriorityHelpText": "Prioridade do Cliente de Download de 1 (mais alta) a 50 (mais baixa). Padrão: 1. Round-Robin é usado para clientes com a mesma prioridade.",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Rejeitar Torrent com Hashes na Lista de Bloqueio Enquanto Capturando",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "se um torrent for bloqueado por hash, ele pode não ser rejeitado corretamente durante o RSS/Pesquisa de alguns indexadores. Ativar isso permitirá que ele seja rejeitado após o torrent ser capturado, mas antes de ser enviado ao cliente.",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashes": "Rejeitar Hashes de Torrent Bloqueados Durante a Captura",
|
||||
"IndexerSettingsRejectBlocklistedTorrentHashesHelpText": "Se um torrent for bloqueado por hash, ele pode não ser rejeitado corretamente durante o RSS/Pesquisa de alguns indexadores. Ativar isso permitirá que ele seja rejeitado após o torrent ser capturado, mas antes de ser enviado ao cliente.",
|
||||
"ImportListsSimklSettingsListTypeHelpText": "Tipo de lista da qual você deseja importar",
|
||||
"ImportListsTraktSettingsPopularListTypeTopWeekShows": "Séries Mais Assistidas por Semana",
|
||||
"ImportListsTraktSettingsPopularListTypeTopYearShows": "Séries Mais Assistidas por Ano",
|
||||
@@ -2038,5 +2038,17 @@
|
||||
"ListSyncTagHelpText": "Esta etiqueta será adicionada quando uma série cair ou não estiver mais na(s) sua(s) lista(s)",
|
||||
"LogOnly": "Só Registro",
|
||||
"CleanLibraryLevel": "Limpar Nível da Biblioteca",
|
||||
"AddDelayProfileError": "Não foi possível adicionar um novo perfil de atraso. Tente novamente."
|
||||
"AddDelayProfileError": "Não foi possível adicionar um novo perfil de atraso. Tente novamente.",
|
||||
"ClickToChangeIndexerFlags": "Clique para alterar sinalizadores do indexador",
|
||||
"SelectIndexerFlags": "Selecionar Sinalizadores do Indexador",
|
||||
"SetIndexerFlagsModalTitle": "{modalTitle} - Definir Sinalizadores do Indexador",
|
||||
"CustomFormatsSpecificationFlag": "Sinalizador",
|
||||
"IndexerFlags": "Sinalizadores do Indexador",
|
||||
"SetIndexerFlags": "Definir Sinalizadores de Indexador",
|
||||
"ImportListsSonarrSettingsSyncSeasonMonitoring": "Sincronização do Monitoramento da Temporada",
|
||||
"ImportListsSonarrSettingsSyncSeasonMonitoringHelpText": "Sincronização do monitoramento de temporada da instância {appName}, se ativado, 'Monitorar' será ignorado",
|
||||
"CustomFilter": "Filtro Personalizado",
|
||||
"Filters": "Filtros",
|
||||
"Label": "Rótulo",
|
||||
"LabelIsRequired": "Rótulo é requerido"
|
||||
}
|
||||
|
||||
@@ -550,8 +550,8 @@
|
||||
"AutoRedownloadFailedHelpText": "自动搜索并尝试下载不同的版本",
|
||||
"AutoTaggingLoadError": "无法加载自动标记",
|
||||
"Automatic": "自动化",
|
||||
"AutoTaggingRequiredHelpText": "这个{0}条件必须匹配自动标记规则才能应用。否则,一个{0}匹配就足够了。",
|
||||
"AutoTaggingNegateHelpText": "如果选中,当 {0} 条件匹配时,自动标记不会应用。",
|
||||
"AutoTaggingRequiredHelpText": "这个{implementationName}条件必须匹配自动标记规则才能应用。否则,一个{implementationName}匹配就足够了。",
|
||||
"AutoTaggingNegateHelpText": "如果选中,当 {implementationName} 条件匹配时,自动标记不会应用。",
|
||||
"BackupRetentionHelpText": "超过保留期限的自动备份将被自动清理",
|
||||
"BackupsLoadError": "无法加载备份",
|
||||
"BypassDelayIfAboveCustomFormatScoreMinimumScore": "最小自定义格式分数",
|
||||
@@ -795,7 +795,7 @@
|
||||
"SeriesFinale": "大结局",
|
||||
"SeriesFolderFormat": "剧集文件夹格式",
|
||||
"ReplaceIllegalCharactersHelpText": "替换非法字符,如未勾选,则会被{appName}移除",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "剧集和剧集信息由TheTVDB.com提供。[请考虑支持他们](https://www.thetvdb.com/subscribe)。",
|
||||
"SeriesAndEpisodeInformationIsProvidedByTheTVDB": "剧集和剧集信息由TheTVDB.com提供。[请考虑支持他们]({url})。",
|
||||
"DeleteSelectedSeries": "删除选中的剧集",
|
||||
"ProxyBypassFilterHelpText": "使用“ , ”作为分隔符,和“ *. ”作为二级域名的通配符",
|
||||
"DeleteSeriesFolderCountConfirmation": "你确定要删除选中的 {count} 个剧集吗?",
|
||||
@@ -803,7 +803,7 @@
|
||||
"FormatAgeDay": "天",
|
||||
"RegularExpression": "正则表达式",
|
||||
"FormatAgeDays": "天",
|
||||
"RegularExpressionsTutorialLink": "有关正则表达式的更多详细信息,请参阅[此处](https://www.regular-expressions.info/tutorial.html)。",
|
||||
"RegularExpressionsTutorialLink": "有关正则表达式的更多详细信息,请参阅[此处]({url})。",
|
||||
"FormatDateTime": "{formattedDate} {formattedTime}",
|
||||
"ReleaseProfileTagSeriesHelpText": "发布配置将应用于至少有一个匹配标记的剧集。留空适用于所有剧集",
|
||||
"FormatShortTimeSpanHours": "{hours}小时",
|
||||
@@ -963,7 +963,7 @@
|
||||
"LocalStorageIsNotSupported": "不支持或禁用本地存储。插件或私人浏览可能已将其禁用。",
|
||||
"ManualGrab": "手动抓取",
|
||||
"ManualImport": "手动导入",
|
||||
"MappedNetworkDrivesWindowsService": "映射网络驱动器在作为Windows服务运行时不可用,请参阅[常见问题解答](https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server)获取更多信息。",
|
||||
"MappedNetworkDrivesWindowsService": "映射网络驱动器在作为Windows服务运行时不可用,请参阅[常见问题解答]({url})获取更多信息。",
|
||||
"Mapping": "映射",
|
||||
"MaximumLimits": "最大限制",
|
||||
"MarkAsFailedConfirmation": "是否确实要将“{sourceTitle}”标记为失败?",
|
||||
@@ -1453,7 +1453,7 @@
|
||||
"RecentChanges": "最近修改",
|
||||
"RecyclingBinCleanupHelpText": "设置为0关闭自动清理",
|
||||
"RecyclingBinHelpText": "文件将在删除时移动到此处,而不是永久删除",
|
||||
"RegularExpressionsCanBeTested": "正则表达式可在[此处](http://regexstorm.net/tester)测试。",
|
||||
"RegularExpressionsCanBeTested": "正则表达式可在[此处]({url})测试。",
|
||||
"SslPort": "SSL端口",
|
||||
"TablePageSizeMinimum": "页面大小必须至少为 {minimumValue}",
|
||||
"TorrentDelayTime": "Torrent延时:{torrentDelay}",
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
public LazyLoaded<List<Episode>> Episodes { get; set; }
|
||||
public LazyLoaded<Series> Series { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -97,6 +97,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
episodeFile.ReleaseGroup = localEpisode.ReleaseGroup;
|
||||
episodeFile.Languages = localEpisode.Languages;
|
||||
|
||||
// Prefer the release type from the download client, folder and finally the file so we have the most accurate information.
|
||||
episodeFile.ReleaseType = localEpisode.DownloadClientEpisodeInfo?.ReleaseType ??
|
||||
localEpisode.FolderEpisodeInfo?.ReleaseType ??
|
||||
localEpisode.FileEpisodeInfo.ReleaseType;
|
||||
|
||||
if (downloadClientItem?.DownloadId.IsNotNullOrWhiteSpace() == true)
|
||||
{
|
||||
var grabHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId)
|
||||
@@ -107,12 +112,27 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
episodeFile.IndexerFlags = flags;
|
||||
}
|
||||
|
||||
// Prefer the release type from the grabbed history
|
||||
if (Enum.TryParse(grabHistory?.Data.GetValueOrDefault("releaseType"), true, out ReleaseType releaseType))
|
||||
{
|
||||
episodeFile.ReleaseType = releaseType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
episodeFile.IndexerFlags = localEpisode.IndexerFlags;
|
||||
}
|
||||
|
||||
// Fall back to parsed information if history is unavailable or missing
|
||||
if (episodeFile.ReleaseType == ReleaseType.Unknown)
|
||||
{
|
||||
// Prefer the release type from the download client, folder and finally the file so we have the most accurate information.
|
||||
episodeFile.ReleaseType = localEpisode.DownloadClientEpisodeInfo?.ReleaseType ??
|
||||
localEpisode.FolderEpisodeInfo?.ReleaseType ??
|
||||
localEpisode.FileEpisodeInfo.ReleaseType;
|
||||
}
|
||||
|
||||
bool copyOnly;
|
||||
switch (importMode)
|
||||
{
|
||||
|
||||
@@ -119,6 +119,10 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
|
||||
localEpisode.FileEpisodeInfo = fileEpisodeInfo;
|
||||
localEpisode.Size = _diskProvider.GetFileSize(localEpisode.Path);
|
||||
localEpisode.ReleaseType = localEpisode.DownloadClientEpisodeInfo?.ReleaseType ??
|
||||
localEpisode.FolderEpisodeInfo?.ReleaseType ??
|
||||
localEpisode.FileEpisodeInfo?.ReleaseType ??
|
||||
ReleaseType.Unknown;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
@@ -17,6 +18,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
public List<Language> Languages { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public int IndexerFlags { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
|
||||
public bool Equals(ManualImportFile other)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
@@ -25,6 +26,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
public int CustomFormatScore { get; set; }
|
||||
public int IndexerFlags { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
public IEnumerable<Rejection> Rejections { get; set; }
|
||||
|
||||
public ManualImportItem()
|
||||
|
||||
@@ -425,6 +425,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
item.Size = _diskProvider.GetFileSize(decision.LocalEpisode.Path);
|
||||
item.Rejections = decision.Rejections;
|
||||
item.IndexerFlags = (int)decision.LocalEpisode.IndexerFlags;
|
||||
item.ReleaseType = decision.LocalEpisode.ReleaseType;
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -444,6 +445,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
item.Quality = episodeFile.Quality;
|
||||
item.Languages = episodeFile.Languages;
|
||||
item.IndexerFlags = (int)episodeFile.IndexerFlags;
|
||||
item.ReleaseType = episodeFile.ReleaseType;
|
||||
item.Size = _diskProvider.GetFileSize(item.Path);
|
||||
item.Rejections = Enumerable.Empty<Rejection>();
|
||||
item.EpisodeFileId = episodeFile.Id;
|
||||
@@ -481,6 +483,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
Quality = file.Quality,
|
||||
Languages = file.Languages,
|
||||
IndexerFlags = (IndexerFlags)file.IndexerFlags,
|
||||
ReleaseType = file.ReleaseType,
|
||||
Series = series,
|
||||
Size = 0
|
||||
};
|
||||
@@ -510,6 +513,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
localEpisode.Quality = file.Quality;
|
||||
localEpisode.Languages = file.Languages;
|
||||
localEpisode.IndexerFlags = (IndexerFlags)file.IndexerFlags;
|
||||
localEpisode.ReleaseType = file.ReleaseType;
|
||||
|
||||
// TODO: Cleanup non-tracked downloads
|
||||
|
||||
|
||||
@@ -351,8 +351,8 @@ namespace NzbDrone.Core.Notifications.CustomScript
|
||||
environmentVariables.Add("Sonarr_Series_OriginalLanguage", IsoLanguages.Get(series.OriginalLanguage).ThreeLetterCode);
|
||||
environmentVariables.Add("Sonarr_Series_Genres", string.Join("|", series.Genres));
|
||||
environmentVariables.Add("Sonarr_Series_Tags", string.Join("|", series.Tags.Select(t => _tagRepository.Get(t).Label)));
|
||||
environmentVariables.Add("Sonarr_Download_Client", message.DownloadClientName ?? string.Empty);
|
||||
environmentVariables.Add("Sonarr_Download_Client_Type", message.DownloadClientType ?? string.Empty);
|
||||
environmentVariables.Add("Sonarr_Download_Client", message.DownloadClientInfo?.Name ?? string.Empty);
|
||||
environmentVariables.Add("Sonarr_Download_Client_Type", message.DownloadClientInfo?.Type ?? string.Empty);
|
||||
environmentVariables.Add("Sonarr_Download_Id", message.DownloadId ?? string.Empty);
|
||||
environmentVariables.Add("Sonarr_Download_Size", message.TrackedDownload.DownloadItem.TotalSize.ToString());
|
||||
environmentVariables.Add("Sonarr_Download_Title", message.TrackedDownload.DownloadItem.Title);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@@ -12,8 +13,7 @@ namespace NzbDrone.Core.Notifications
|
||||
public RemoteEpisode Episode { get; set; }
|
||||
public TrackedDownload TrackedDownload { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public string DownloadClientType { get; set; }
|
||||
public string DownloadClientName { get; set; }
|
||||
public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
public GrabbedReleaseInfo Release { get; set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
@@ -61,7 +61,7 @@ namespace NzbDrone.Core.Notifications.Emby
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Debug("Testing connection to MediaBrowser: {0}", settings.Address);
|
||||
_logger.Debug("Testing connection to Emby/Jellyfin : {0}", settings.Address);
|
||||
|
||||
Notify(settings, "Test from Sonarr", "Success! MediaBrowser has been successfully configured!");
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Notifications.Emby
|
||||
RuleFor(c => c.ApiKey).NotEmpty();
|
||||
RuleFor(c => c.MapFrom).NotEmpty().Unless(c => c.MapTo.IsNullOrWhiteSpace());
|
||||
RuleFor(c => c.MapTo).NotEmpty().Unless(c => c.MapFrom.IsNullOrWhiteSpace());
|
||||
RuleFor(c => c.UrlBase).ValidUrlBase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,25 +38,30 @@ namespace NzbDrone.Core.Notifications.Emby
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "serviceName", "Emby/Jellyfin")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey)]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "connectionName", "Emby/Jellyfin")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/mediabrowser")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey)]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "NotificationsEmbySettingsSendNotifications", HelpText = "NotificationsEmbySettingsSendNotificationsHelpText", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(5, Label = "NotificationsEmbySettingsSendNotifications", HelpText = "NotificationsEmbySettingsSendNotificationsHelpText", Type = FieldType.Checkbox)]
|
||||
public bool Notify { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "NotificationsSettingsUpdateLibrary", HelpText = "NotificationsEmbySettingsUpdateLibraryHelpText", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(6, Label = "NotificationsSettingsUpdateLibrary", HelpText = "NotificationsEmbySettingsUpdateLibraryHelpText", Type = FieldType.Checkbox)]
|
||||
public bool UpdateLibrary { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "NotificationsSettingsUpdateMapPathsFrom", HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText", Type = FieldType.Textbox, Advanced = true)]
|
||||
[FieldDefinition(7, Label = "NotificationsSettingsUpdateMapPathsFrom", HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText", Type = FieldType.Textbox, Advanced = true)]
|
||||
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsFrom", "serviceName", "Emby/Jellyfin")]
|
||||
public string MapFrom { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "NotificationsSettingsUpdateMapPathsTo", HelpText = "NotificationsSettingsUpdateMapPathsToHelpText", Type = FieldType.Textbox, Advanced = true)]
|
||||
[FieldDefinition(8, Label = "NotificationsSettingsUpdateMapPathsTo", HelpText = "NotificationsSettingsUpdateMapPathsToHelpText", Type = FieldType.Textbox, Advanced = true)]
|
||||
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsTo", "serviceName", "Emby/Jellyfin")]
|
||||
public string MapTo { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string Address => $"{Host.ToUrlHost()}:{Port}";
|
||||
public string Address => $"{Host.ToUrlHost()}:{Port}{UrlBase}";
|
||||
|
||||
public bool IsValid => !string.IsNullOrWhiteSpace(Host) && Port > 0;
|
||||
|
||||
|
||||
@@ -240,8 +240,7 @@ namespace NzbDrone.Core.Notifications
|
||||
Quality = message.Episode.ParsedEpisodeInfo.Quality,
|
||||
Episode = message.Episode,
|
||||
TrackedDownload = message.TrackedDownload,
|
||||
DownloadClientType = message.TrackedDownload.DownloadItem.DownloadClientInfo.Type,
|
||||
DownloadClientName = message.TrackedDownload.DownloadItem.DownloadClientInfo.Name,
|
||||
DownloadClientInfo = message.TrackedDownload.DownloadItem.DownloadClientInfo,
|
||||
DownloadId = message.TrackedDownload.DownloadItem.DownloadId,
|
||||
Release = message.Release
|
||||
};
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
{
|
||||
var scheme = settings.UseSsl ? "https" : "http";
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host.ToUrlHost()}:{settings.Port}")
|
||||
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host.ToUrlHost()}:{settings.Port}{settings.UrlBase}")
|
||||
.Accept(HttpAccept.Json)
|
||||
.AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier)
|
||||
.AddQueryParam("X-Plex-Product", BuildInfo.AppName)
|
||||
|
||||
@@ -38,20 +38,25 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "serviceName", "Plex")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "NotificationsPlexSettingsAuthToken", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, Advanced = true)]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "connectionName", "Plex")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/plex")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "NotificationsPlexSettingsAuthToken", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, Advanced = true)]
|
||||
public string AuthToken { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "NotificationsPlexSettingsAuthenticateWithPlexTv", Type = FieldType.OAuth)]
|
||||
[FieldDefinition(5, Label = "NotificationsPlexSettingsAuthenticateWithPlexTv", Type = FieldType.OAuth)]
|
||||
public string SignIn { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "NotificationsSettingsUpdateLibrary", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(6, Label = "NotificationsSettingsUpdateLibrary", Type = FieldType.Checkbox)]
|
||||
public bool UpdateLibrary { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "NotificationsSettingsUpdateMapPathsFrom", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText")]
|
||||
[FieldDefinition(7, Label = "NotificationsSettingsUpdateMapPathsFrom", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsFrom", "serviceName", "Plex")]
|
||||
public string MapFrom { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "NotificationsSettingsUpdateMapPathsTo", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsToHelpText")]
|
||||
[FieldDefinition(8, Label = "NotificationsSettingsUpdateMapPathsTo", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsToHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsTo", "serviceName", "Plex")]
|
||||
public string MapTo { get; set; }
|
||||
|
||||
|
||||
@@ -175,8 +175,8 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
Series = new WebhookSeries(message.Series),
|
||||
Episodes = remoteEpisode.Episodes.ConvertAll(x => new WebhookEpisode(x)),
|
||||
DownloadInfo = new WebhookDownloadClientItem(quality, message.TrackedDownload.DownloadItem),
|
||||
DownloadClient = message.DownloadClientName,
|
||||
DownloadClientType = message.DownloadClientType,
|
||||
DownloadClient = message.DownloadClientInfo?.Name,
|
||||
DownloadClientType = message.DownloadClientInfo?.Type,
|
||||
DownloadId = message.DownloadId,
|
||||
CustomFormatInfo = new WebhookCustomFormatInfo(remoteEpisode.CustomFormats, remoteEpisode.CustomFormatScore),
|
||||
Release = new WebhookGrabbedRelease(message.Release)
|
||||
|
||||
@@ -37,26 +37,31 @@ namespace NzbDrone.Core.Notifications.Xbmc
|
||||
[FieldToken(TokenField.HelpText, "UseSsl", "serviceName", "Kodi")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Username", Privacy = PrivacyLevel.UserName)]
|
||||
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "connectionName", "Kodi")]
|
||||
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/kodi")]
|
||||
public string UrlBase { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", Privacy = PrivacyLevel.UserName)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[DefaultValue(5)]
|
||||
[FieldDefinition(5, Label = "NotificationsKodiSettingsDisplayTime", HelpText = "NotificationsKodiSettingsDisplayTimeHelpText")]
|
||||
[FieldDefinition(6, Label = "NotificationsKodiSettingsDisplayTime", HelpText = "NotificationsKodiSettingsDisplayTimeHelpText")]
|
||||
public int DisplayTime { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "NotificationsKodiSettingsGuiNotification", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(7, Label = "NotificationsKodiSettingsGuiNotification", Type = FieldType.Checkbox)]
|
||||
public bool Notify { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "NotificationsSettingsUpdateLibrary", HelpText = "NotificationsKodiSettingsUpdateLibraryHelpText", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(8, Label = "NotificationsSettingsUpdateLibrary", HelpText = "NotificationsKodiSettingsUpdateLibraryHelpText", Type = FieldType.Checkbox)]
|
||||
public bool UpdateLibrary { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "NotificationsKodiSettingsCleanLibrary", HelpText = "NotificationsKodiSettingsCleanLibraryHelpText", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(9, Label = "NotificationsKodiSettingsCleanLibrary", HelpText = "NotificationsKodiSettingsCleanLibraryHelpText", Type = FieldType.Checkbox)]
|
||||
public bool CleanLibrary { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "NotificationsKodiSettingAlwaysUpdate", HelpText = "NotificationsKodiSettingAlwaysUpdateHelpText", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(10, Label = "NotificationsKodiSettingAlwaysUpdate", HelpText = "NotificationsKodiSettingAlwaysUpdateHelpText", Type = FieldType.Checkbox)]
|
||||
public bool AlwaysUpdate { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
|
||||
@@ -29,9 +29,9 @@ namespace NzbDrone.Core.Parser
|
||||
private static readonly Regex GermanDualLanguageRegex = new (@"(?<!WEB[-_. ]?)\bDL\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex GermanMultiLanguageRegex = new (@"\bML\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?([-_. ](?<tags>full|forced|foreign|default|cc|psdh|sdh))*[-_. ](?<iso_code>[a-z]{2,3})([-_. ](?<tags>full|forced|foreign|default|cc|psdh|sdh))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?([-_. ](?<tags>forced|foreign|default|cc|psdh|sdh))*[-_. ](?<iso_code>[a-z]{2,3})([-_. ](?<tags>forced|foreign|default|cc|psdh|sdh))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex SubtitleLanguageTitleRegex = new Regex(@".+?(\.((?<tags1>full|forced|foreign|default|cc|psdh|sdh)|(?<iso_code>[a-z]{2,3})))*\.(?<title>[^.]*)(\.((?<tags2>full|forced|foreign|default|cc|psdh|sdh)|(?<iso_code>[a-z]{2,3})))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex SubtitleLanguageTitleRegex = new Regex(@".+?(\.((?<tags1>forced|foreign|default|cc|psdh|sdh)|(?<iso_code>[a-z]{2,3})))*[-_. ](?<title>[^.]*)(\.((?<tags2>forced|foreign|default|cc|psdh|sdh)|(?<iso_code>[a-z]{2,3})))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex SubtitleTitleRegex = new Regex(@"((?<title>.+) - )?(?<copy>(?<!\d+)\d{1,3}(?!\d+))$", RegexOptions.Compiled);
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Model
|
||||
{
|
||||
public class ImportListItemInfo : ModelBase
|
||||
{
|
||||
public ImportListItemInfo()
|
||||
{
|
||||
Seasons = new List<Season>();
|
||||
}
|
||||
|
||||
public int ImportListId { get; set; }
|
||||
public string ImportList { get; set; }
|
||||
public string Title { get; set; }
|
||||
@@ -15,6 +22,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||
public int MalId { get; set; }
|
||||
public int AniListId { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public List<Season> Seasons { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public bool ExistingFile { get; set; }
|
||||
public bool SceneSource { get; set; }
|
||||
|
||||
@@ -90,6 +90,29 @@ namespace NzbDrone.Core.Parser.Model
|
||||
}
|
||||
}
|
||||
|
||||
public ReleaseType ReleaseType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EpisodeNumbers.Length > 1 || AbsoluteEpisodeNumbers.Length > 1)
|
||||
{
|
||||
return Model.ReleaseType.MultiEpisode;
|
||||
}
|
||||
|
||||
if (EpisodeNumbers.Length == 1 || AbsoluteEpisodeNumbers.Length == 1)
|
||||
{
|
||||
return Model.ReleaseType.SingleEpisode;
|
||||
}
|
||||
|
||||
if (FullSeason)
|
||||
{
|
||||
return Model.ReleaseType.SeasonPack;
|
||||
}
|
||||
|
||||
return Model.ReleaseType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var episodeString = "[Unknown Episode]";
|
||||
|
||||
18
src/NzbDrone.Core/Parser/Model/ReleaseType.cs
Normal file
18
src/NzbDrone.Core/Parser/Model/ReleaseType.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Model
|
||||
{
|
||||
public enum ReleaseType
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
[FieldOption(label: "Single Episode")]
|
||||
SingleEpisode = 1,
|
||||
|
||||
[FieldOption(label: "Multi-Episode")]
|
||||
MultiEpisode = 2,
|
||||
|
||||
[FieldOption(label: "Season Pack")]
|
||||
SeasonPack = 3
|
||||
}
|
||||
}
|
||||
@@ -541,6 +541,7 @@ namespace NzbDrone.Core.Parser
|
||||
private static readonly Regex TitleComponentsRegex = new Regex(@"^(?:(?<title>.+?) \((?<title>.+?)\)|(?<title>.+?) \| (?<title>.+?))$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex PartRegex = new Regex(@"\(\d+\)$", RegexOptions.Compiled);
|
||||
private static readonly Regex PunctuationRegex = new Regex(@"[^\w\s]", RegexOptions.Compiled);
|
||||
private static readonly Regex ArticleWordRegex = new Regex(@"^(a|an|the)\s", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex SpecialEpisodeWordRegex = new Regex(@"\b(part|special|edition|christmas)\b\s?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
@@ -792,6 +793,7 @@ namespace NzbDrone.Core.Parser
|
||||
// Disabled, Until we run into specific testcases for the removal of these words.
|
||||
// title = SpecialEpisodeWordRegex.Replace(title, string.Empty);
|
||||
|
||||
title = PartRegex.Replace(title, "");
|
||||
title = PunctuationRegex.Replace(title, " ");
|
||||
title = DuplicateSpacesRegex.Replace(title, " ");
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace NzbDrone.Core.Tv
|
||||
public Series Series { get; set; }
|
||||
|
||||
public bool HasFile => EpisodeFileId > 0;
|
||||
public bool AbsoluteEpisodeNumberAdded { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -40,6 +40,12 @@ namespace NzbDrone.Core.Tv
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip episode level monitoring and use season information when series was added
|
||||
if (monitoringOptions.Monitor == MonitorTypes.Skip)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var firstSeason = series.Seasons.Select(s => s.SeasonNumber).Where(s => s > 0).MinOrDefault();
|
||||
var lastSeason = series.Seasons.Select(s => s.SeasonNumber).MaxOrDefault();
|
||||
var episodes = _episodeService.GetEpisodeBySeries(series.Id);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
@@ -11,19 +11,19 @@ using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IEpisodeAddedService
|
||||
public interface IEpisodeRefreshedService
|
||||
{
|
||||
void SearchForRecentlyAdded(int seriesId);
|
||||
void Search(int seriesId);
|
||||
}
|
||||
|
||||
public class EpisodeAddedService : IHandle<EpisodeInfoRefreshedEvent>, IEpisodeAddedService
|
||||
public class EpisodeRefreshedService : IEpisodeRefreshedService, IHandle<EpisodeInfoRefreshedEvent>
|
||||
{
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly Logger _logger;
|
||||
private readonly ICached<List<int>> _addedEpisodesCache;
|
||||
private readonly ICached<List<int>> _searchCache;
|
||||
|
||||
public EpisodeAddedService(ICacheManager cacheManager,
|
||||
public EpisodeRefreshedService(ICacheManager cacheManager,
|
||||
IManageCommandQueue commandQueueManager,
|
||||
IEpisodeService episodeService,
|
||||
Logger logger)
|
||||
@@ -31,12 +31,12 @@ namespace NzbDrone.Core.Tv
|
||||
_commandQueueManager = commandQueueManager;
|
||||
_episodeService = episodeService;
|
||||
_logger = logger;
|
||||
_addedEpisodesCache = cacheManager.GetCache<List<int>>(GetType());
|
||||
_searchCache = cacheManager.GetCache<List<int>>(GetType());
|
||||
}
|
||||
|
||||
public void SearchForRecentlyAdded(int seriesId)
|
||||
public void Search(int seriesId)
|
||||
{
|
||||
var previouslyAired = _addedEpisodesCache.Find(seriesId.ToString());
|
||||
var previouslyAired = _searchCache.Find(seriesId.ToString());
|
||||
|
||||
if (previouslyAired != null && previouslyAired.Any())
|
||||
{
|
||||
@@ -48,42 +48,54 @@ namespace NzbDrone.Core.Tv
|
||||
}
|
||||
}
|
||||
|
||||
_addedEpisodesCache.Remove(seriesId.ToString());
|
||||
_searchCache.Remove(seriesId.ToString());
|
||||
}
|
||||
|
||||
public void Handle(EpisodeInfoRefreshedEvent message)
|
||||
{
|
||||
if (message.Series.AddOptions == null)
|
||||
{
|
||||
var toSearch = new List<int>();
|
||||
|
||||
if (!message.Series.Monitored)
|
||||
{
|
||||
_logger.Debug("Series is not monitored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Added.Empty())
|
||||
{
|
||||
_logger.Debug("No new episodes, skipping search");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Added.None(a => a.AirDateUtc.HasValue))
|
||||
{
|
||||
_logger.Debug("No new episodes have an air date");
|
||||
return;
|
||||
}
|
||||
|
||||
var previouslyAired = message.Added.Where(a => a.AirDateUtc.HasValue
|
||||
&& a.AirDateUtc.Value.Between(DateTime.UtcNow.AddDays(-14), DateTime.UtcNow.AddDays(1))
|
||||
&& a.Monitored).ToList();
|
||||
var previouslyAired = message.Added.Where(a =>
|
||||
a.AirDateUtc.HasValue &&
|
||||
a.AirDateUtc.Value.Between(DateTime.UtcNow.AddDays(-14), DateTime.UtcNow.AddDays(1)) &&
|
||||
a.Monitored)
|
||||
.Select(e => e.Id)
|
||||
.ToList();
|
||||
|
||||
if (previouslyAired.Empty())
|
||||
{
|
||||
_logger.Debug("Newly added episodes all air in the future");
|
||||
return;
|
||||
}
|
||||
|
||||
_addedEpisodesCache.Set(message.Series.Id.ToString(), previouslyAired.Select(e => e.Id).ToList());
|
||||
toSearch.AddRange(previouslyAired);
|
||||
|
||||
var absoluteEpisodeNumberAdded = message.Updated.Where(a =>
|
||||
a.AbsoluteEpisodeNumberAdded &&
|
||||
a.AirDateUtc.HasValue &&
|
||||
a.AirDateUtc.Value.Between(DateTime.UtcNow.AddDays(-14), DateTime.UtcNow.AddDays(1)) &&
|
||||
a.Monitored)
|
||||
.Select(e => e.Id)
|
||||
.ToList();
|
||||
|
||||
if (absoluteEpisodeNumberAdded.Empty())
|
||||
{
|
||||
_logger.Debug("No updated episodes recently aired and had absolute episode number added");
|
||||
}
|
||||
|
||||
toSearch.AddRange(absoluteEpisodeNumberAdded);
|
||||
|
||||
if (toSearch.Any())
|
||||
{
|
||||
_searchCache.Set(message.Series.Id.ToString(), toSearch.Distinct().ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using NzbDrone.Common.Messaging;
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Tv.Events
|
||||
{
|
||||
Series = series;
|
||||
Added = new ReadOnlyCollection<Episode>(added);
|
||||
Updated = new ReadOnlyCollection<Episode>(updated);
|
||||
Removed = new ReadOnlyCollection<Episode>(removed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace NzbDrone.Core.Tv
|
||||
Recent,
|
||||
MonitorSpecials,
|
||||
UnmonitorSpecials,
|
||||
None
|
||||
None,
|
||||
Skip
|
||||
}
|
||||
|
||||
public enum NewItemMonitorTypes
|
||||
|
||||
@@ -53,12 +53,20 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
try
|
||||
{
|
||||
var episodeToUpdate = GetEpisodeToUpdate(series, episode, existingEpisodes);
|
||||
var episodeToUpdate = existingEpisodes.FirstOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber);
|
||||
|
||||
if (episodeToUpdate != null)
|
||||
{
|
||||
existingEpisodes.Remove(episodeToUpdate);
|
||||
updateList.Add(episodeToUpdate);
|
||||
|
||||
// Anime series with newly added absolute episode number
|
||||
if (series.SeriesType == SeriesTypes.Anime &&
|
||||
!episodeToUpdate.AbsoluteEpisodeNumber.HasValue &&
|
||||
episode.AbsoluteEpisodeNumber.HasValue)
|
||||
{
|
||||
episodeToUpdate.AbsoluteEpisodeNumberAdded = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -227,24 +235,6 @@ namespace NzbDrone.Core.Tv
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private Episode GetEpisodeToUpdate(Series series, Episode episode, List<Episode> existingEpisodes)
|
||||
{
|
||||
if (series.SeriesType == SeriesTypes.Anime)
|
||||
{
|
||||
if (episode.AbsoluteEpisodeNumber.HasValue)
|
||||
{
|
||||
var matchingEpisode = existingEpisodes.FirstOrDefault(e => e.AbsoluteEpisodeNumber == episode.AbsoluteEpisodeNumber);
|
||||
|
||||
if (matchingEpisode != null)
|
||||
{
|
||||
return matchingEpisode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return existingEpisodes.FirstOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber);
|
||||
}
|
||||
|
||||
private IEnumerable<Episode> OrderEpisodes(Series series, List<Episode> episodes)
|
||||
{
|
||||
if (series.SeriesType == SeriesTypes.Anime)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace NzbDrone.Core.Tv
|
||||
private readonly IEpisodeMonitoredService _episodeMonitoredService;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IEpisodeAddedService _episodeAddedService;
|
||||
private readonly IEpisodeRefreshedService _episodeRefreshedService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
private readonly Logger _logger;
|
||||
@@ -21,14 +21,14 @@ namespace NzbDrone.Core.Tv
|
||||
public SeriesScannedHandler(IEpisodeMonitoredService episodeMonitoredService,
|
||||
ISeriesService seriesService,
|
||||
IManageCommandQueue commandQueueManager,
|
||||
IEpisodeAddedService episodeAddedService,
|
||||
IEpisodeRefreshedService episodeRefreshedService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_episodeMonitoredService = episodeMonitoredService;
|
||||
_seriesService = seriesService;
|
||||
_commandQueueManager = commandQueueManager;
|
||||
_episodeAddedService = episodeAddedService;
|
||||
_episodeRefreshedService = episodeRefreshedService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Tv
|
||||
|
||||
if (addOptions == null)
|
||||
{
|
||||
_episodeAddedService.SearchForRecentlyAdded(series.Id);
|
||||
_episodeRefreshedService.Search(series.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,11 @@ namespace Sonarr.Api.V3.EpisodeFiles
|
||||
{
|
||||
episodeFile.IndexerFlags = (IndexerFlags)resourceEpisodeFile.IndexerFlags;
|
||||
}
|
||||
|
||||
if (resourceEpisodeFile.ReleaseType != null)
|
||||
{
|
||||
episodeFile.ReleaseType = (ReleaseType)resourceEpisodeFile.ReleaseType;
|
||||
}
|
||||
}
|
||||
|
||||
_mediaFileService.Update(episodeFiles);
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Sonarr.Api.V3.EpisodeFiles
|
||||
public List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public int CustomFormatScore { get; set; }
|
||||
public int? IndexerFlags { get; set; }
|
||||
public int? ReleaseType { get; set; }
|
||||
public MediaInfoResource MediaInfo { get; set; }
|
||||
|
||||
public bool QualityCutoffNotMet { get; set; }
|
||||
@@ -33,34 +34,6 @@ namespace Sonarr.Api.V3.EpisodeFiles
|
||||
|
||||
public static class EpisodeFileResourceMapper
|
||||
{
|
||||
private static EpisodeFileResource ToResource(this EpisodeFile model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new EpisodeFileResource
|
||||
{
|
||||
Id = model.Id,
|
||||
|
||||
SeriesId = model.SeriesId,
|
||||
SeasonNumber = model.SeasonNumber,
|
||||
RelativePath = model.RelativePath,
|
||||
|
||||
// Path
|
||||
Size = model.Size,
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
ReleaseGroup = model.ReleaseGroup,
|
||||
Languages = model.Languages,
|
||||
Quality = model.Quality,
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName)
|
||||
|
||||
// QualityCutoffNotMet
|
||||
};
|
||||
}
|
||||
|
||||
public static EpisodeFileResource ToResource(this EpisodeFile model, NzbDrone.Core.Tv.Series series, IUpgradableSpecification upgradableSpecification, ICustomFormatCalculationService formatCalculationService)
|
||||
{
|
||||
if (model == null)
|
||||
@@ -90,7 +63,8 @@ namespace Sonarr.Api.V3.EpisodeFiles
|
||||
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(series.QualityProfile.Value, model.Quality),
|
||||
CustomFormats = customFormats.ToResource(false),
|
||||
CustomFormatScore = customFormatScore,
|
||||
IndexerFlags = (int)model.IndexerFlags
|
||||
IndexerFlags = (int)model.IndexerFlags,
|
||||
ReleaseType = (int)model.ReleaseType,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using NzbDrone.Common.Crypto;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Manual;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using Sonarr.Api.V3.CustomFormats;
|
||||
using Sonarr.Api.V3.Episodes;
|
||||
@@ -31,6 +32,7 @@ namespace Sonarr.Api.V3.ManualImport
|
||||
public List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public int CustomFormatScore { get; set; }
|
||||
public int IndexerFlags { get; set; }
|
||||
public ReleaseType ReleaseType { get; set; }
|
||||
public IEnumerable<Rejection> Rejections { get; set; }
|
||||
}
|
||||
|
||||
@@ -67,6 +69,7 @@ namespace Sonarr.Api.V3.ManualImport
|
||||
// QualityWeight
|
||||
DownloadId = model.DownloadId,
|
||||
IndexerFlags = model.IndexerFlags,
|
||||
ReleaseType = model.ReleaseType,
|
||||
Rejections = model.Rejections
|
||||
};
|
||||
}
|
||||
|
||||
137
yarn.lock
137
yarn.lock
@@ -1261,68 +1261,76 @@
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
|
||||
integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
|
||||
|
||||
"@sentry-internal/tracing@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.51.2.tgz#17833047646426ca71445327018ffcb33506a699"
|
||||
integrity sha512-OBNZn7C4CyocmlSMUPfkY9ORgab346vTHu5kX35PgW5XR51VD2nO5iJCFbyFcsmmRWyCJcZzwMNARouc2V4V8A==
|
||||
"@sentry-internal/feedback@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.100.0.tgz#38d8d4cb8ac3e6e24d91b13878bd6208a55bcab3"
|
||||
integrity sha512-SMW2QhNKOuSjw8oPtvryDlJjiwrNyAKljbgtMk057os/fd8QMp38Yt1ImqLCM4B2rTQZ6REJ6hRGRTRcfqoG+w==
|
||||
dependencies:
|
||||
"@sentry/core" "7.51.2"
|
||||
"@sentry/types" "7.51.2"
|
||||
"@sentry/utils" "7.51.2"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/core" "7.100.0"
|
||||
"@sentry/types" "7.100.0"
|
||||
"@sentry/utils" "7.100.0"
|
||||
|
||||
"@sentry/browser@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.51.2.tgz#c01758a54c613be45df58ab503805737256f51a4"
|
||||
integrity sha512-FQFEaTFbvYHPQE2emFjNoGSy+jXplwzoM/XEUBRjrGo62lf8BhMvWnPeG3H3UWPgrWA1mq0amvHRwXUkwofk0g==
|
||||
"@sentry-internal/replay-canvas@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.100.0.tgz#b462346832631ed5a9446686419113ff331bd984"
|
||||
integrity sha512-DePinj5IgNiC4RZv0yX0DLccMZebfFdKl3zHwDeLBeZqtMz9VrPzchv57IWP+5MI1+iuOn+WOg4oTNBUG6hFRw==
|
||||
dependencies:
|
||||
"@sentry-internal/tracing" "7.51.2"
|
||||
"@sentry/core" "7.51.2"
|
||||
"@sentry/replay" "7.51.2"
|
||||
"@sentry/types" "7.51.2"
|
||||
"@sentry/utils" "7.51.2"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/core" "7.100.0"
|
||||
"@sentry/replay" "7.100.0"
|
||||
"@sentry/types" "7.100.0"
|
||||
"@sentry/utils" "7.100.0"
|
||||
|
||||
"@sentry/core@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.51.2.tgz#f2c938de334f9bf26f4416079168275832423964"
|
||||
integrity sha512-p8ZiSBxpKe+rkXDMEcgmdoyIHM/1bhpINLZUFPiFH8vzomEr7sgnwRhyrU8y/ADnkPeNg/2YF3QpDpk0OgZJUA==
|
||||
"@sentry-internal/tracing@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.100.0.tgz#01f0925a287a6e5d0becd731ab361cabbd27c007"
|
||||
integrity sha512-qf4W1STXky9WOQYoPSw2AmCBDK4FzvAyq5yeD2sLU7OCUEfbRUcN0lQljUvmWRKv/jTIAyeU5icDLJPZuR50nA==
|
||||
dependencies:
|
||||
"@sentry/types" "7.51.2"
|
||||
"@sentry/utils" "7.51.2"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/core" "7.100.0"
|
||||
"@sentry/types" "7.100.0"
|
||||
"@sentry/utils" "7.100.0"
|
||||
|
||||
"@sentry/integrations@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.51.2.tgz#fce58b9ced601c7f93344b508c67c69a9c883f3d"
|
||||
integrity sha512-ZnSptbuDQOoQ13mFX9vvLDfXlbMGjenW2fMIssi9+08B7fD6qxmetkYnWmBK+oEipjoGA//0240Fj8FUvZr0Qg==
|
||||
"@sentry/browser@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.100.0.tgz#adf57f660baa6190a7e1709605f73b94818ee04b"
|
||||
integrity sha512-XpM0jEVe6DJWXjMSOjtJxsSNR/XnJKrlcuyoI4Re3qLG+noEF5QLc0r3VJkySXPRFnmdW05sLswQ6a/n9Sijmg==
|
||||
dependencies:
|
||||
"@sentry/types" "7.51.2"
|
||||
"@sentry/utils" "7.51.2"
|
||||
localforage "^1.8.1"
|
||||
tslib "^1.9.3"
|
||||
"@sentry-internal/feedback" "7.100.0"
|
||||
"@sentry-internal/replay-canvas" "7.100.0"
|
||||
"@sentry-internal/tracing" "7.100.0"
|
||||
"@sentry/core" "7.100.0"
|
||||
"@sentry/replay" "7.100.0"
|
||||
"@sentry/types" "7.100.0"
|
||||
"@sentry/utils" "7.100.0"
|
||||
|
||||
"@sentry/replay@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.51.2.tgz#1f54e92b472ab87dfdb4e8cd6b8c8252600fe7b0"
|
||||
integrity sha512-W8YnSxkK9LTUXDaYciM7Hn87u57AX9qvH8jGcxZZnvpKqHlDXOpSV8LRtBkARsTwgLgswROImSifY0ic0lyCWg==
|
||||
"@sentry/core@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.100.0.tgz#5b28c7b3e41e45e4d50e3bdea5d35434fd78e86b"
|
||||
integrity sha512-eWRPuP0Zdj4a2F7SybqNjf13LGOVgGwvW6sojweQp9oxGAfCPp/EMDGBhlpYbMJeLbzmqzJ4ZFHIedaiEC+7kg==
|
||||
dependencies:
|
||||
"@sentry/core" "7.51.2"
|
||||
"@sentry/types" "7.51.2"
|
||||
"@sentry/utils" "7.51.2"
|
||||
"@sentry/types" "7.100.0"
|
||||
"@sentry/utils" "7.100.0"
|
||||
|
||||
"@sentry/types@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.51.2.tgz#cb742f374d9549195f62c462c915adeafed31d65"
|
||||
integrity sha512-/hLnZVrcK7G5BQoD/60u9Qak8c9AvwV8za8TtYPJDUeW59GrqnqOkFji7RVhI7oH1OX4iBxV+9pAKzfYE6A6SA==
|
||||
|
||||
"@sentry/utils@7.51.2":
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.51.2.tgz#2a52ac2cfb00ffd128248981279c0a561b39eccb"
|
||||
integrity sha512-EcjBU7qG4IG+DpIPvdgIBcdIofROMawKoRUNKraeKzH/waEYH9DzCaqp/mzc5/rPBhpDB4BShX9xDDSeH+8c0A==
|
||||
"@sentry/replay@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.100.0.tgz#4f2e35155626ab286692ade3e31da282c73bd402"
|
||||
integrity sha512-6Yo56J+x+eedaMXri8pPlFxXOofnSXVdsUuFj+kJ7lC/qHrwIbgC5g1ONEK/WlYwpVH4gA0aNnCa5AOkMu+ZTg==
|
||||
dependencies:
|
||||
"@sentry/types" "7.51.2"
|
||||
tslib "^1.9.3"
|
||||
"@sentry-internal/tracing" "7.100.0"
|
||||
"@sentry/core" "7.100.0"
|
||||
"@sentry/types" "7.100.0"
|
||||
"@sentry/utils" "7.100.0"
|
||||
|
||||
"@sentry/types@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.100.0.tgz#a16f60d78613bd9810298e9e8d80134be58b24f8"
|
||||
integrity sha512-c+RHwZwpKeBk7h8sUX4nQcelxBz8ViCojifnbEe3tcn8O15HOLvZqRKgLLOiff3MoErxiv4oxs0sPbEFRm/IvA==
|
||||
|
||||
"@sentry/utils@7.100.0":
|
||||
version "7.100.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.100.0.tgz#a9d36c01eede117c3e17b0350d399a87934e9c66"
|
||||
integrity sha512-LAhZMEGq3C125prZN/ShqeXpRfdfgJkl9RAKjfq8cmMFsF7nsF72dEHZgIwrZ0lgNmtaWAB83AwJcyN83RwOxQ==
|
||||
dependencies:
|
||||
"@sentry/types" "7.100.0"
|
||||
|
||||
"@types/archiver@^5.3.1":
|
||||
version "5.3.2"
|
||||
@@ -2268,9 +2276,9 @@ camelcase@^5.3.1:
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001517:
|
||||
version "1.0.30001525"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz#d2e8fdec6116ffa36284ca2c33ef6d53612fe1c8"
|
||||
integrity sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q==
|
||||
version "1.0.30001591"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz"
|
||||
integrity sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==
|
||||
|
||||
chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
@@ -3747,11 +3755,6 @@ image-size@~0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
|
||||
integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
|
||||
|
||||
immediate@~3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||
|
||||
"immutable@^3.8.1 || ^4.0.0", immutable@^4.0.0:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f"
|
||||
@@ -4205,13 +4208,6 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
lilconfig@^2.0.5:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
|
||||
@@ -4267,13 +4263,6 @@ loader-utils@^3.2.1:
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576"
|
||||
integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==
|
||||
|
||||
localforage@^1.8.1:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
|
||||
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
@@ -6448,7 +6437,7 @@ tsconfig-paths@^4.1.2:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^1.8.1, tslib@^1.9.3:
|
||||
tslib@^1.8.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
Reference in New Issue
Block a user