1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-14 20:54:58 -04:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Mark McDowall
98c737a146 New: Move auth success logging to debug
Closes #7978
2025-08-10 21:15:05 -07:00
18 changed files with 86 additions and 186 deletions

View File

@@ -90,7 +90,7 @@ function QueueStatus(props: QueueStatusProps) {
if (trackedDownloadState === 'importing') {
title += ` - ${translate('Importing')}`;
iconKind = kinds.PRIMARY;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'failedPending') {

View File

@@ -26,10 +26,6 @@
color: var(--warningColor);
}
.primary {
color: var(--primaryColor);
}
.purple {
color: var(--purple);
}

View File

@@ -6,7 +6,6 @@ interface CssExports {
'disabled': string;
'info': string;
'pink': string;
'primary': string;
'purple': string;
'success': string;
'warning': string;

View File

@@ -4,8 +4,6 @@ namespace NzbDrone.Common.Extensions
{
public static class DateTimeExtensions
{
public static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static bool InNextDays(this DateTime dateTime, int days)
{
return InNext(dateTime, new TimeSpan(days, 0, 0, 0));
@@ -45,10 +43,5 @@ namespace NzbDrone.Common.Extensions
{
return dateTime.AddTicks(-(dateTime.Ticks % TimeSpan.TicksPerSecond));
}
public static DateTime WithTicksFrom(this DateTime dateTime, DateTime other)
{
return dateTime.WithoutTicks().AddTicks(other.Ticks % TimeSpan.TicksPerSecond);
}
}
}

View File

@@ -1,118 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.UpdateEpisodeFileServiceTests
{
[TestFixture]
public class ChangeFileDateForFileFixture : CoreTest<UpdateEpisodeFileService>
{
private readonly DateTime _veryOldAirDateUtc = new(1965, 01, 01, 0, 0, 0, 512, 512, DateTimeKind.Utc);
private DateTime _lastWrite = new(2025, 07, 27, 12, 0, 0, 512, 512, DateTimeKind.Utc);
private Series _series;
private EpisodeFile _episodeFile;
private string _seriesFolder;
private List<Episode> _episodes;
[SetUp]
public void Setup()
{
_seriesFolder = @"C:\Test\TV\Series Title".AsOsAgnostic();
_series = Builder<Series>.CreateNew()
.With(s => s.Path = _seriesFolder)
.Build();
_episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.AirDateUtc = _lastWrite.AddDays(2))
.Build()
.ToList();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = Path.Combine(_series.Path, "Season 1", "Series Title - S01E01.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Season 1\Series Title - S01E01.mkv".AsOsAgnostic())
.Build();
Mocker.GetMock<IDiskProvider>()
.Setup(x => x.FileGetLastWrite(_episodeFile.Path))
.Returns(() => _lastWrite);
Mocker.GetMock<IDiskProvider>()
.Setup(x => x.FileSetLastWriteTime(_episodeFile.Path, It.IsAny<DateTime>()))
.Callback<string, DateTime>((path, dateTime) =>
{
_lastWrite = dateTime.Kind == DateTimeKind.Utc
? dateTime
: dateTime.ToUniversalTime();
});
Mocker.GetMock<IConfigService>()
.Setup(x => x.FileDate)
.Returns(FileDateType.LocalAirDate);
}
[Test]
public void should_change_date_once_only()
{
var previousWrite = new DateTime(_lastWrite.Ticks, _lastWrite.Kind);
Subject.ChangeFileDateForFile(_episodeFile, _series, _episodes);
Subject.ChangeFileDateForFile(_episodeFile, _series, _episodes);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.FileSetLastWriteTime(_episodeFile.Path, It.IsAny<DateTime>()), Times.Once());
var actualWriteTime = Mocker.GetMock<IDiskProvider>().Object.FileGetLastWrite(_episodeFile.Path).ToLocalTime();
actualWriteTime.Should().Be(_episodes[0].AirDateUtc.Value.ToLocalTime().WithTicksFrom(previousWrite));
}
[Test]
public void should_clamp_mtime_on_posix()
{
PosixOnly();
var previousWrite = new DateTime(_lastWrite.Ticks, _lastWrite.Kind);
_episodes[0].AirDateUtc = _veryOldAirDateUtc;
Subject.ChangeFileDateForFile(_episodeFile, _series, _episodes);
Subject.ChangeFileDateForFile(_episodeFile, _series, _episodes);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.FileSetLastWriteTime(_episodeFile.Path, It.IsAny<DateTime>()), Times.Once());
var actualWriteTime = Mocker.GetMock<IDiskProvider>().Object.FileGetLastWrite(_episodeFile.Path).ToLocalTime();
actualWriteTime.Should().Be(DateTimeExtensions.EpochTime.ToLocalTime().WithTicksFrom(previousWrite));
}
[Test]
public void should_not_clamp_mtime_on_windows()
{
WindowsOnly();
var previousWrite = new DateTime(_lastWrite.Ticks, _lastWrite.Kind);
_episodes[0].AirDateUtc = _veryOldAirDateUtc;
Subject.ChangeFileDateForFile(_episodeFile, _series, _episodes);
Subject.ChangeFileDateForFile(_episodeFile, _series, _episodes);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.FileSetLastWriteTime(_episodeFile.Path, It.IsAny<DateTime>()), Times.Once());
var actualWriteTime = Mocker.GetMock<IDiskProvider>().Object.FileGetLastWrite(_episodeFile.Path).ToLocalTime();
actualWriteTime.Should().Be(_episodes[0].AirDateUtc.Value.ToLocalTime().WithTicksFrom(previousWrite));
}
}
}

View File

@@ -86,8 +86,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Series Title S01 1080p Eng Fra [mkvonly]")]
[TestCase("Series Title S01 Eng Fre Multi Subs 720p [H264 mp4]")]
[TestCase("Series-Title-S01-[DVDRip]-H264-Fra-Ac3-2-0-Eng-5-1")]
[TestCase("Series Title S01 1080p FR ENG [mkvonly]")]
[TestCase("Series Title S01 1080p ENG FR [mkvonly]")]
public void should_parse_language_french_english(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);

View File

@@ -1874,6 +1874,7 @@
"DownloadClientQbittorrentValidationQueueingNotEnabledDetail": "La cua de torrent no està activada a la configuració del qBittorrent. Activeu-lo a qBittorrent o seleccioneu 'Last' com a prioritat.",
"DownloadClientQbittorrentValidationRemovesAtRatioLimit": "qBittorrent està configurat per a eliminar els torrents quan arribin al límit de la relació de compartició",
"DownloadClientTransmissionSettingsUrlBaseHelpText": "Afegeix un prefix a l'URL rpc de {clientName}, ex. {url}, per defecte a {defaultUrl}",
"DownloadClientUTorrentProviderMessage": "uTorrent té un historial d'inclusió de criptominers, programari maliciós i anuncis, us animem a triar un client diferent.",
"DownloadClientValidationAuthenticationFailureDetail": "Verifiqueu el vostre nom d'usuari i contrasenya. Verifiqueu també si el servidor que executa {appName} no està bloquejat per accedir a {clientName} per les limitacions de WhiteList a la configuració {clientName}.",
"DownloadClientValidationCategoryMissingDetail": "La categoria que heu introduït no existeix a {clientName}. Primer creeu-lo a {clientName}.",
"DownloadClientValidationSslConnectFailure": "No s'ha pogut connectar a través de SSL",

View File

@@ -537,6 +537,7 @@
"DownloadClientFreeboxUnableToReachFreebox": "Nelze se připojit k Freebox API. Zkontrolujte nastavení 'Host', 'Port' nebo 'Použít SSL'. (Chyba: {exceptionMessage})",
"IndexerHDBitsSettingsCodecsHelpText": "Pokud nespecifikováno, použijí se všechny možnosti.",
"DownloadClientDownloadStationValidationSharedFolderMissing": "Sdílená složka neexistuje",
"DownloadClientUTorrentProviderMessage": "uTorrent je známý tím, že zahrnuje cryptominery, malware a reklamy, důrazně vám doporučujeme zvolit jiného klienta.",
"DownloadClientDownloadStationValidationNoDefaultDestinationDetail": "Přihlaste se do vaší DiskStation jako {username} a ručně to nastavte v nastavení DownloadStation pod BT/HTTP/FTP/NZB -> Umístění.",
"IndexerLongTermStatusUnavailableHealthCheckMessage": "Indexery nedostupné z důvodu selhání delším než 6 hodin: {indexerNames}",
"DownloadClientFloodSettingsRemovalInfo": "{appName} se postará o automatické mazání torrentů podle aktuálních kritérií seedování v Nastavení -> Indexery",

View File

@@ -2151,6 +2151,7 @@
"NotificationsTelegramSettingsLinkPreviewHelpText": "Determina qué enlaces se previsualizarán en las notificaciones de Telegram. Elige 'Ninguno' para deshabilitarlo",
"MediaInfoFootNote2": "MediaInfo AudioLanguages excluye el inglés si es el único idioma. Usa MediaInfo AudioLanguagesAll para incluir solo el inglés",
"ReleaseSource": "Fuente de lanzamiento",
"DownloadClientUTorrentProviderMessage": "uTorrent tiene un amplio historial de incluir criptomineros, malware y publicidad, por lo que recomendamos encarecidamente que elijas un cliente diferente.",
"NotificationsPushcutSettingsIncludePoster": "Incluir póster",
"NotificationsPushcutSettingsIncludePosterHelpText": "Incluir póster con notificación",
"NotificationsPushcutSettingsMetadataLinks": "Enlaces de metadatos",

View File

@@ -2152,6 +2152,7 @@
"QualityDefinitionsSizeNotice": "Kokorajoitukset on siirretty laatuprofiileihin",
"NotificationsTelegramSettingsLinkPreview": "Linkin esikatselu",
"NotificationsTelegramSettingsLinkPreviewHelpText": "Määrittää minkä linkin esikatselu Telegram-ilmoituksessa näytetään. Poista käytöstä valitsemalla \"Ei mitään\".",
"DownloadClientUTorrentProviderMessage": "Koska uTorrent on tunnettu crypto-, haitta- and mainossisällöstä ja sovelluksista, suosittelemme qBittorrentin, Delugen ja ruTorrentin kaltaisten vaihtoehtojen käyttämistä.",
"NotificationsPushcutSettingsIncludePoster": "Sisällytä juliste",
"NotificationsPushcutSettingsIncludePosterHelpText": "Näytä juliste ilmoituksessa.",
"NotificationsPushcutSettingsMetadataLinks": "Metatietolinkit",

View File

@@ -800,7 +800,7 @@
"UpdaterLogFiles": "Journaux du programme de mise à jour",
"UpgradeUntil": "Mise à niveau jusqu'à",
"UpgradeUntilCustomFormatScore": "Mise à niveau jusqu'au score de format personnalisé",
"UpgradeUntilCustomFormatScoreEpisodeHelpText": "Une fois que la qualité minimum est atteinte ou dépassée et que le score de format personnalisé est atteint, {appName} ne récupérera plus les sorties d'épisodes",
"UpgradeUntilCustomFormatScoreEpisodeHelpText": "Une fois ce score de format personnalisé atteint, {appName} ne récupérera plus les sorties d'épisodes",
"UrlBase": "URL de base",
"UseHardlinksInsteadOfCopy": "Utiliser les liens durs au lieu de copier",
"UseSeasonFolder": "Utiliser le dossier de la saison",
@@ -904,7 +904,7 @@
"UnmappedFilesOnly": "Fichiers non mappés uniquement",
"UnmonitorSpecialsEpisodesDescription": "Annulez la surveillance de tous les épisodes spéciaux sans modifier le statut surveillé des autres épisodes",
"UpdateUiNotWritableHealthCheckMessage": "Impossible d'installer la mise à jour, car le dossier de l'interface utilisateur « {uiFolder} » n'est pas accessible en écriture par l'utilisateur « {userName} ».",
"UpgradeUntilEpisodeHelpText": "Une fois cette qualité atteinte, {appName} ne téléchargera plus d'épisodes une fois le que le score du format personnalisé est atteint ou dépassé",
"UpgradeUntilEpisodeHelpText": "Une fois cette qualité atteinte, {appName} ne téléchargera plus d'épisodes",
"UpgradeUntilThisQualityIsMetOrExceeded": "Mise à niveau jusqu'à ce que cette qualité soit atteinte ou dépassée",
"UseProxy": "Utiliser le proxy",
"WaitingToImport": "En attente d'import",
@@ -2152,6 +2152,7 @@
"NotificationsTelegramSettingsLinkPreview": "Aperçu du lien",
"FavoriteFolderAdd": "Ajouter un dossier favori",
"FavoriteFolderRemove": "Supprimer le dossier favori",
"DownloadClientUTorrentProviderMessage": "uTorrent a l'habitude d'inclure des cryptomineurs, des logiciels malveillants et des publicités, nous vous encourageons fortement à choisir un client différent.",
"DownloadClientQbittorrentSettingsAddSeriesTags": "Ajouter des tags de séries",
"DownloadClientQbittorrentSettingsAddSeriesTagsHelpText": "Ajouter des tags de séries aux nouveaux torrents ajoutés au client de téléchargement (qBittorrent 4.1.0+)",
"FavoriteFolders": "Dossier favori",
@@ -2162,7 +2163,5 @@
"UserRejectedExtensions": "Extensions de fichiers rejetées supplémentaires",
"UserRejectedExtensionsHelpText": "Liste séparée par des virgules des extensions de fichiers à échouer (“Échouer les téléchargements” doit également être activé dans lindexeur)",
"UserRejectedExtensionsTextsExamples": "Examples : '.ext, .xyz' or 'ext,xyz'",
"Warning": "Avertissement",
"QualityDefinitionsSizeNotice": "Les restrictions de taille sont maintenant dans les profils de qualité",
"UserInvokedSearch": "Recherche invoquée par lutilisateur"
"Warning": "Avertissement"
}

View File

@@ -2152,6 +2152,7 @@
"QualityDefinitionsSizeNotice": "As restrições de tamanho foram transferidas para Perfis de Qualidade",
"NotificationsTelegramSettingsLinkPreview": "Prévia do Link",
"NotificationsTelegramSettingsLinkPreviewHelpText": "Determina qual link será visualizado na notificação do Telegram. Escolha 'Nenhum' para desativar",
"DownloadClientUTorrentProviderMessage": "O uTorrent tem um histórico de incluir criptomineradores, malware e anúncios, recomendamos fortemente que você escolha um cliente diferente.",
"NotificationsPushcutSettingsIncludePoster": "Incluir pôster",
"NotificationsPushcutSettingsMetadataLinks": "Links de metadados",
"NotificationsPushcutSettingsIncludePosterHelpText": "Incluir pôster com notificação",

View File

@@ -2155,6 +2155,7 @@
"UpdatePath": "Обновить путь",
"UpdateSeriesPath": "Обновить путь до сериала",
"ReleasePush": "Через API",
"DownloadClientUTorrentProviderMessage": "Мы настоятельно советуем не использовать uTorrent, т.к. он известен как программа-шифровальщик и в целом вредоносное ПО.",
"CloneImportList": "Копировать список импорта",
"EpisodesInSeason": "{episodeCount} эпизодов в сезоне",
"DefaultNameCopiedImportList": "{name} - копировать",

View File

@@ -2152,6 +2152,7 @@
"QualityDefinitionsSizeNotice": "Boyut kısıtlamaları Kalite Profillerine taşındı",
"NotificationsTelegramSettingsLinkPreview": "Bağlantı Önizlemesi",
"NotificationsTelegramSettingsLinkPreviewHelpText": "Telegram bildiriminde hangi bağlantının önizleneceğini belirler. Devre dışı bırakmak için 'Hiçbiri'ni seçin",
"DownloadClientUTorrentProviderMessage": "uTorrent'in kripto para madenciliği, kötü amaçlı yazılım ve reklam içerme geçmişi vardır, bu nedenle farklı bir istemci seçmenizi önemle tavsiye ederiz.",
"NotificationsPushcutSettingsIncludePoster": "Posteri Dahil Et",
"NotificationsPushcutSettingsMetadataLinks": "Meta Veri Bağlantıları",
"NotificationsPushcutSettingsMetadataLinksHelpText": "Bildirim içeriğine meta verilerin bağlantılarını ekleyin",

View File

@@ -2123,6 +2123,7 @@
"Airs": "Ефіри",
"DoneEditingSizes": "Редагування розмірів завершено",
"IndexerSettingsFailDownloads": "Не вдалося завантажити",
"DownloadClientUTorrentProviderMessage": "uTorrent має історію включення криптомайнерів, шкідливого програмного забезпечення та реклами. Ми наполегливо рекомендуємо вибрати інший клієнт.",
"EditSelectedCustomFormats": "Змінити вибрані власні формати",
"EditSizes": "Змінити розміри",
"FailedToFetchSettings": "Не вдалося отримати налаштування",

View File

@@ -26,6 +26,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IConfigService _configService;
private readonly IEpisodeService _episodeService;
private readonly Logger _logger;
private static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public UpdateEpisodeFileService(IDiskProvider diskProvider,
IConfigService configService,
@@ -46,48 +47,90 @@ namespace NzbDrone.Core.MediaFiles
private bool ChangeFileDate(EpisodeFile episodeFile, Series series, List<Episode> episodes)
{
var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath);
var airDateUtc = episodes.First().AirDateUtc;
if (!airDateUtc.HasValue)
switch (_configService.FileDate)
{
return false;
case FileDateType.LocalAirDate:
{
var airDate = episodes.First().AirDate;
var airTime = series.AirTime;
if (airDate.IsNullOrWhiteSpace() || airTime.IsNullOrWhiteSpace())
{
return false;
}
return ChangeFileDateToLocalAirDate(episodeFilePath, airDate, airTime);
}
case FileDateType.UtcAirDate:
{
var airDateUtc = episodes.First().AirDateUtc;
if (!airDateUtc.HasValue)
{
return false;
}
return ChangeFileDateToUtcAirDate(episodeFilePath, airDateUtc.Value);
}
}
return _configService.FileDate switch
{
FileDateType.LocalAirDate =>
ChangeFileDateToLocalDate(episodeFilePath, airDateUtc.Value.ToLocalTime()),
// Intentionally pass UTC as local per user preference
FileDateType.UtcAirDate =>
ChangeFileDateToLocalDate(
episodeFilePath,
DateTime.SpecifyKind(airDateUtc.Value, DateTimeKind.Local)),
_ => false,
};
return false;
}
private bool ChangeFileDateToLocalDate(string filePath, DateTime localDate)
private bool ChangeFileDateToLocalAirDate(string filePath, string fileDate, string fileTime)
{
// FileGetLastWrite returns UTC; convert to local to compare
var oldLastWrite = _diskProvider.FileGetLastWrite(filePath).ToLocalTime();
if (OsInfo.IsNotWindows && localDate.ToUniversalTime() < DateTimeExtensions.EpochTime)
if (DateTime.TryParse(fileDate + ' ' + fileTime, out var airDate))
{
_logger.Debug("Setting date of file to 1970-01-01 as actual airdate is before that time and will not be set properly");
localDate = DateTimeExtensions.EpochTime.ToLocalTime();
// avoiding false +ve checks and set date skewing by not using UTC (Windows)
var oldLastWrite = _diskProvider.FileGetLastWrite(filePath);
if (OsInfo.IsNotWindows && airDate < EpochTime)
{
_logger.Debug("Setting date of file to 1970-01-01 as actual airdate is before that time and will not be set properly");
airDate = EpochTime;
}
if (!DateTime.Equals(airDate.WithoutTicks(), oldLastWrite.WithoutTicks()))
{
try
{
_diskProvider.FileSetLastWriteTime(filePath, airDate);
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldLastWrite, airDate);
return true;
}
catch (Exception ex)
{
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
}
}
}
else
{
_logger.Debug("Could not create valid date to change file [{0}]", filePath);
}
if (!DateTime.Equals(localDate.WithoutTicks(), oldLastWrite.WithoutTicks()))
return false;
}
private bool ChangeFileDateToUtcAirDate(string filePath, DateTime airDateUtc)
{
var oldLastWrite = _diskProvider.FileGetLastWrite(filePath);
if (OsInfo.IsNotWindows && airDateUtc < EpochTime)
{
_logger.Debug("Setting date of file to 1970-01-01 as actual airdate is before that time and will not be set properly");
airDateUtc = EpochTime;
}
if (!DateTime.Equals(airDateUtc.WithoutTicks(), oldLastWrite.WithoutTicks()))
{
try
{
// Preserve prior mtime subseconds per https://github.com/Sonarr/Sonarr/issues/7228
var mtime = localDate.WithTicksFrom(oldLastWrite);
_diskProvider.FileSetLastWriteTime(filePath, mtime);
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldLastWrite, mtime);
_diskProvider.FileSetLastWriteTime(filePath, airDateUtc.AddMilliseconds(oldLastWrite.Millisecond));
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldLastWrite, airDateUtc);
return true;
}

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Parser
new RegexReplace(@".*?[_. ](S\d{2}(?:E\d{2,4})*[_. ].*)", "$1", RegexOptions.Compiled | RegexOptions.IgnoreCase)
};
private static readonly Regex LanguageRegex = new Regex(@"(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>(?:swiss)?german\b|videomann|ger[. ]dub|\bger\b)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_|\b)(?:FR|VF|VF2|VFF|VFI|VFQ|TRUEFRENCH|FRENCH|FRE|FRA)(?:\W|_|\b))|(?<russian>\b(?:rus|ru)\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|(?<bulgarian>\bbgaudio\b)|(?<spanish>\b(?:español|castellano|esp|spa(?!\(Latino\)))\b)|(?<ukrainian>\b(?:\dx?)?(?:ukr))|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)|(?<turkish>\b(?:tur)\b)|(?<urdu>\burdu\b)|(?<romansh>\b(?:romansh|rumantsch|romansch)\b)|(?<japanese>\(JA\))|(?<original>\b(?:orig|original)\b)",
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>(?:swiss)?german\b|videomann|ger[. ]dub|\bger\b)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_|\b)(?:FR|VF|VF2|VFF|VFI|VFQ|TRUEFRENCH|FRENCH|FRE|FRA)(?:\W|_|\b))|(?<russian>\b(?:rus|ru)\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|(?<bulgarian>\bbgaudio\b)|(?<spanish>\b(?:español|castellano|esp|spa(?!\(Latino\)))\b)|(?<ukrainian>\b(?:\dx?)?(?:ukr))|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)|(?<turkish>\b(?:tur)\b)|(?<urdu>\burdu\b)|(?<romansh>\b(?:romansh|rumantsch|romansch)\b)|(?<japanese>\(JA\))|(?<original>\b(?:orig|original)\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex CaseSensitiveLanguageRegex = new Regex(@"(?:(?i)(?<!SUB[\W|_|^]))(?:(?<lithuanian>\bLT\b)|(?<czech>\bCZ\b)|(?<polish>\bPL\b)|(?<bulgarian>\bBG\b)|(?<slovak>\bSK\b)|(?<german>\bDE\b))(?:(?i)(?![\W|_|^]SUB))",

View File

@@ -1062,24 +1062,6 @@
"type": "integer",
"format": "int32"
},
"malIds": {
"uniqueItems": true,
"type": "array",
"items": {
"type": "integer",
"format": "int32"
},
"nullable": true
},
"aniListIds": {
"uniqueItems": true,
"type": "array",
"items": {
"type": "integer",
"format": "int32"
},
"nullable": true
},
"firstAired": {
"type": "string",
"format": "date-time",