1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-17 21:26:13 -04:00

Compare commits

...

23 Commits

Author SHA1 Message Date
Weblate
6c8758c27a Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: fordas <fordas15@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/es/
Translation: Servarr/Sonarr
2024-03-01 17:32:55 -08:00
Mark McDowall
086d3b5afa Increase migration timeout to 5 minutes 2024-03-01 17:26:26 -08:00
Mark McDowall
f8a0751775 New: Release Type (Single/Multi episode and Season Pack) for Custom Formats
Closes #3562
2024-03-01 17:26:26 -08:00
Mark McDowall
c99d81e79b New: Bypass archived history for failed downloads in SABnzbd 2024-03-01 17:26:10 -08:00
Mark McDowall
9fd193d2a8 New: URL Base setting for Media Server connections
Closes #4416
2024-03-01 17:24:52 -08:00
Bogdan
64f4365fe9 Update caniuse-lite 2024-03-01 17:24:41 -08:00
Bogdan
2773f77e1c New: Options button for Missing/Cutoff Unmet 2024-03-01 17:24:41 -08:00
Weblate
0a84b4a8e9 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translation: Servarr/Sonarr
2024-03-01 17:24:35 -08:00
Weblate
236d8e4c50 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translation: Servarr/Sonarr
2024-02-27 20:44:11 -08:00
Mark McDowall
16d3827dbd Fixed: Processing updated episodes in series after refresh
Closes #6560
2024-02-27 20:42:27 -08:00
Bogdan
fa600e62e0 Fixed: Error when download client information is unavailable for Manual Interaction Required event
Closes #6558
(cherry picked from commit 173b1d6a4c0f2125c4413c0c09b269d87a1f1ee8)

Co-authored-by: Qstick <qstick@gmail.com>
2024-02-27 23:42:08 -05:00
Mark McDowall
fb6fc568c5 Fixed: Don't store seasons from import list items in database
Closes #6555
2024-02-27 20:40:39 -08:00
Bogdan
1f97679868 Fixed: Selection of last added custom filter
Plus some translations and typos
2024-02-27 20:40:33 -08:00
Mark McDowall
b34e0f8259 Fixed: Ignore language in split episode title 2024-02-27 00:33:07 -05:00
Mark McDowall
4c170d0452 New: Update anime episodes by season/episode number instead of absolute episode number
Closes #6547
2024-02-26 21:32:46 -08:00
Mark McDowall
6dc0a88004 New: Search for recently aired anime episodes with added absolute episode number
Closes #2044
2024-02-26 21:32:46 -08:00
Mark McDowall
33b44a8a53 New: Option to sync season monitoring state when importing from another Sonarr instance
Closes #6542
2024-02-26 21:32:39 -08:00
Mark McDowall
cb72e752f9 Fixed: Parsing of subtitle languages separated by dash
Closes #6494
2024-02-27 00:32:31 -05:00
Weblate
a11ee7bc11 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: EDUYO <eduardoestabiel@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Magyar <kochnorbert@icloud.com>
Co-authored-by: Sadi A. Nogueira <contato@sadi.eti.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Xupix <colinaubert25@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translation: Servarr/Sonarr
2024-02-26 21:32:04 -08:00
Stevie Robinson
98d60e1a8e Replace URLs in translations with tokens 2024-02-27 00:30:58 -05:00
Bruno Garcia
6377c688fc Update Sentry SDK add features
Co-authored-by: Stefan Jandl <reg@bitfox.at>
2024-02-27 00:30:32 -05:00
Mark McDowall
7a37f130f9 Bump version to 4.0.2 2024-02-26 20:54:08 -08:00
Mark McDowall
724dd7e733 Clean branch name to remove slashes 2024-02-20 21:23:52 -08:00
78 changed files with 1022 additions and 373 deletions

View File

@@ -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"

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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' });

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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>
}

View File

@@ -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}

View File

@@ -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}

View File

@@ -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",

View File

@@ -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" />

View File

@@ -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)

View File

@@ -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}"

View File

@@ -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);

View File

@@ -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" />

View File

@@ -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; }
}
}
}

View File

@@ -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()
}));
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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")]

View File

@@ -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();
}
}
}

View File

@@ -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; }

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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}");
}
});
}

View File

@@ -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));
}
}
}

View 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);
}
}
}

View File

@@ -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 =>
{

View File

@@ -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()

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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
}
});
}

View File

@@ -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; }
}
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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",

View File

@@ -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}",

View File

@@ -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"
}

View File

@@ -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",

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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",

View File

@@ -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"
}

View File

@@ -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}",

View File

@@ -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()
{

View File

@@ -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)
{

View File

@@ -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
{

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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);

View File

@@ -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; }

View File

@@ -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!");
}

View File

@@ -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;

View File

@@ -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
};

View File

@@ -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)

View File

@@ -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; }

View File

@@ -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)

View File

@@ -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]

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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; }

View File

@@ -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]";

View 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
}
}

View File

@@ -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, " ");

View File

@@ -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()
{

View File

@@ -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);

View File

@@ -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());
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -27,7 +27,8 @@ namespace NzbDrone.Core.Tv
Recent,
MonitorSpecials,
UnmonitorSpecials,
None
None,
Skip
}
public enum NewItemMonitorTypes

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,
};
}
}

View File

@@ -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
View File

@@ -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==