mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-13 15:34:56 -04:00
Compare commits
19 Commits
sonarr-pul
...
v4.2.3.657
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5616c5ba1 | ||
|
|
61979bff7a | ||
|
|
90d0d8bec8 | ||
|
|
2d814ecd20 | ||
|
|
6542119402 | ||
|
|
b9185574f3 | ||
|
|
99e0d42b71 | ||
|
|
d01fa5e6a4 | ||
|
|
2ce9d099e1 | ||
|
|
12829580e5 | ||
|
|
dadd796737 | ||
|
|
a3f508b8d4 | ||
|
|
1ab3df03a3 | ||
|
|
5558e10711 | ||
|
|
573405bae7 | ||
|
|
43d77308f9 | ||
|
|
b3c3f7ddae | ||
|
|
dd5bc41eda | ||
|
|
c8ab4f8c68 |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -5,9 +5,9 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
- label: I have searched the existing open and closed issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -42,12 +42,14 @@ body:
|
||||
- **Docker Install**: Yes
|
||||
- **Using Reverse Proxy**: No
|
||||
- **Browser**: Firefox 90 (If UI related)
|
||||
- **Database**: Sqlite 3.36.0
|
||||
value: |
|
||||
- OS:
|
||||
- Radarr:
|
||||
- Docker Install:
|
||||
- Using Reverse Proxy:
|
||||
- Browser:
|
||||
- Database:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '4.2.2'
|
||||
majorVersion: '4.2.3'
|
||||
minorVersion: $[counter('minorVersion', 2000)]
|
||||
radarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
|
||||
|
||||
@@ -21,6 +21,7 @@ function HostSettings(props) {
|
||||
port,
|
||||
urlBase,
|
||||
instanceName,
|
||||
applicationUrl,
|
||||
enableSsl,
|
||||
sslPort,
|
||||
sslCertPath,
|
||||
@@ -90,6 +91,21 @@ function HostSettings(props) {
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>{translate('ApplicationURL')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="applicationUrl"
|
||||
helpText={translate('ApplicationUrlHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...applicationUrl}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
|
||||
@@ -262,10 +262,10 @@ export const defaultState = {
|
||||
type: filterBuilderTypes.ARRAY,
|
||||
optionsSelector: function(items) {
|
||||
const collectionList = items.reduce((acc, movie) => {
|
||||
if (movie.collection) {
|
||||
if (movie.collection && movie.collection.title) {
|
||||
acc.push({
|
||||
id: movie.collection.name,
|
||||
name: movie.collection.name
|
||||
id: movie.collection.title,
|
||||
name: movie.collection.title
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ export const filterPredicates = {
|
||||
const predicate = filterTypePredicates[type];
|
||||
const { collection } = item;
|
||||
|
||||
return predicate(collection ? collection.title : '', filterValue);
|
||||
return predicate(collection && collection.title ? collection.title : '', filterValue);
|
||||
},
|
||||
|
||||
originalLanguage: function(item, filterValue, type) {
|
||||
|
||||
@@ -227,7 +227,7 @@ export const defaultState = {
|
||||
collection: function(item) {
|
||||
const { collection ={} } = item;
|
||||
|
||||
return collection.name;
|
||||
return collection.title;
|
||||
},
|
||||
|
||||
originalLanguage: function(item) {
|
||||
@@ -339,7 +339,7 @@ export const defaultState = {
|
||||
type: filterBuilderTypes.ARRAY,
|
||||
optionsSelector: function(items) {
|
||||
const collectionList = items.reduce((acc, movie) => {
|
||||
if (movie.collection) {
|
||||
if (movie.collection && movie.collection.title) {
|
||||
acc.push({
|
||||
id: movie.collection.title,
|
||||
name: movie.collection.title
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
|
||||
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||
|
||||
// Announce URLs (passkeys) Magnet & Tracker
|
||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
||||
@@ -84,9 +85,24 @@ namespace NzbDrone.Common.Test.InstrumentationTests
|
||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||
|
||||
cleansedMessage.Should().NotContain("mySecret");
|
||||
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
|
||||
cleansedMessage.Should().NotContain("01233210");
|
||||
}
|
||||
|
||||
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
|
||||
public void should_keep_message(string message)
|
||||
{
|
||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||
|
||||
cleansedMessage.Should().NotContain("mySecret");
|
||||
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
|
||||
cleansedMessage.Should().NotContain("01233210");
|
||||
|
||||
cleansedMessage.Should().Contain("shouldkeep1");
|
||||
cleansedMessage.Should().Contain("shouldkeep2");
|
||||
cleansedMessage.Should().Contain("shouldkeep3");
|
||||
}
|
||||
|
||||
[TestCase(@"Some message (from 32.2.3.5 user agent)")]
|
||||
[TestCase(@"Auth-Invalidated ip 32.2.3.5")]
|
||||
[TestCase(@"Auth-Success ip 32.2.3.5")]
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
||||
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"\b[^;=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
new Regex(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
|
||||
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
|
||||
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
|
||||
|
||||
245
src/NzbDrone.Core.Test/Extras/ExtraServiceFixture.cs
Normal file
245
src/NzbDrone.Core.Test/Extras/ExtraServiceFixture.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Extras;
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Extras
|
||||
{
|
||||
[TestFixture]
|
||||
public class ExtraServiceFixture : CoreTest<ExtraService>
|
||||
{
|
||||
private Movie _movie;
|
||||
private MovieFile _movieFile;
|
||||
private LocalMovie _localMovie;
|
||||
|
||||
private string _movieFolder;
|
||||
private string _releaseFolder;
|
||||
|
||||
private Mock<IManageExtraFiles> _subtitleService;
|
||||
private Mock<IManageExtraFiles> _otherExtraService;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_movieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
|
||||
_releaseFolder = @"C:\Test\Unsorted TV\Movie.Title.2022".AsOsAgnostic();
|
||||
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Path = _movieFolder)
|
||||
.Build();
|
||||
|
||||
_movieFile = Builder<MovieFile>.CreateNew()
|
||||
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
|
||||
.With(f => f.RelativePath = @"Movie Title - 2022.mkv".AsOsAgnostic())
|
||||
.Build();
|
||||
|
||||
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.Movie = _movie)
|
||||
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
|
||||
.Build();
|
||||
|
||||
_subtitleService = new Mock<IManageExtraFiles>();
|
||||
_subtitleService.SetupGet(s => s.Order).Returns(0);
|
||||
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(false);
|
||||
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), ".srt", It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
|
||||
_otherExtraService = new Mock<IManageExtraFiles>();
|
||||
_otherExtraService.SetupGet(s => s.Order).Returns(1);
|
||||
_otherExtraService.Setup(s => s.CanImportFile(It.IsAny<LocalMovie>(), It.IsAny<MovieFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.SetConstant<IEnumerable<IManageExtraFiles>>(new[]
|
||||
{
|
||||
_subtitleService.Object,
|
||||
_otherExtraService.Object
|
||||
});
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(s => s.FolderExists(It.IsAny<string>()))
|
||||
.Returns(false);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
|
||||
.Returns((string path) => Directory.GetParent(path).FullName);
|
||||
|
||||
WithExistingFolder(_movie.Path);
|
||||
WithExistingFile(_movieFile.Path);
|
||||
WithExistingFile(_localMovie.Path);
|
||||
|
||||
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(true);
|
||||
Mocker.GetMock<IConfigService>().Setup(v => v.ExtraFileExtensions).Returns("nfo,srt");
|
||||
}
|
||||
|
||||
private void WithExistingFolder(string path, bool exists = true)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
|
||||
if (exists && dir.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
WithExistingFolder(dir);
|
||||
}
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(v => v.FolderExists(path)).Returns(exists);
|
||||
}
|
||||
|
||||
private void WithExistingFile(string path, bool exists = true, int size = 1000)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
|
||||
if (exists && dir.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
WithExistingFolder(dir);
|
||||
}
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(v => v.FileExists(path)).Returns(exists);
|
||||
Mocker.GetMock<IDiskProvider>().Setup(v => v.GetFileSize(path)).Returns(size);
|
||||
}
|
||||
|
||||
private void WithExistingFiles(List<string> files)
|
||||
{
|
||||
foreach (string file in files)
|
||||
{
|
||||
WithExistingFile(file);
|
||||
}
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(_releaseFolder, It.IsAny<SearchOption>()))
|
||||
.Returns(files.ToArray());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_pass_file_if_import_disabled()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
|
||||
|
||||
var nfofile = Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic();
|
||||
|
||||
var files = new List<string>
|
||||
{
|
||||
_localMovie.Path,
|
||||
nfofile
|
||||
};
|
||||
|
||||
WithExistingFiles(files);
|
||||
|
||||
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||
|
||||
_subtitleService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||
_otherExtraService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("Movie Title - 2022.sub")]
|
||||
[TestCase("Movie Title - 2022.ass")]
|
||||
public void should_not_pass_unwanted_file(string filePath)
|
||||
{
|
||||
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
|
||||
|
||||
var nfofile = Path.Combine(_releaseFolder, filePath).AsOsAgnostic();
|
||||
|
||||
var files = new List<string>
|
||||
{
|
||||
_localMovie.Path,
|
||||
nfofile
|
||||
};
|
||||
|
||||
WithExistingFiles(files);
|
||||
|
||||
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||
|
||||
_subtitleService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||
_otherExtraService.Verify(v => v.CanImportFile(_localMovie, _movieFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_pass_subtitle_file_to_subtitle_service()
|
||||
{
|
||||
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
|
||||
|
||||
var files = new List<string>
|
||||
{
|
||||
_localMovie.Path,
|
||||
subtitleFile
|
||||
};
|
||||
|
||||
WithExistingFiles(files);
|
||||
|
||||
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||
|
||||
_subtitleService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true), Times.Once());
|
||||
_otherExtraService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_pass_nfo_file_to_other_service()
|
||||
{
|
||||
var nfofile = Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic();
|
||||
|
||||
var files = new List<string>
|
||||
{
|
||||
_localMovie.Path,
|
||||
nfofile
|
||||
};
|
||||
|
||||
WithExistingFiles(files);
|
||||
|
||||
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||
|
||||
_subtitleService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { nfofile }, true), Times.Never());
|
||||
_otherExtraService.Verify(v => v.ImportFiles(_localMovie, _movieFile, new List<string> { nfofile }, true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_search_subtitles_when_importing_from_job_folder()
|
||||
{
|
||||
_localMovie.FolderMovieInfo = new ParsedMovieInfo();
|
||||
|
||||
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
|
||||
|
||||
var files = new List<string>
|
||||
{
|
||||
_localMovie.Path,
|
||||
subtitleFile
|
||||
};
|
||||
|
||||
WithExistingFiles(files);
|
||||
|
||||
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.AllDirectories), Times.Once);
|
||||
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.TopDirectoryOnly), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_search_subtitles_when_not_importing_from_job_folder()
|
||||
{
|
||||
_localMovie.FolderMovieInfo = null;
|
||||
|
||||
var subtitleFile = Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic();
|
||||
|
||||
var files = new List<string>
|
||||
{
|
||||
_localMovie.Path,
|
||||
subtitleFile
|
||||
};
|
||||
|
||||
WithExistingFiles(files);
|
||||
|
||||
Subject.ImportMovie(_localMovie, _movieFile, true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.AllDirectories), Times.Never);
|
||||
Mocker.GetMock<IDiskProvider>().Verify(v => v.GetFiles(_releaseFolder, SearchOption.TopDirectoryOnly), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Extras.Others;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Extras.Others
|
||||
{
|
||||
[TestFixture]
|
||||
public class OtherExtraServiceFixture : CoreTest<OtherExtraService>
|
||||
{
|
||||
private Movie _movie;
|
||||
private MovieFile _movieFile;
|
||||
private LocalMovie _localMovie;
|
||||
|
||||
private string _movieFolder;
|
||||
private string _releaseFolder;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_movieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
|
||||
_releaseFolder = @"C:\Test\Unsorted Movies\Movie.Title.2022".AsOsAgnostic();
|
||||
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Path = _movieFolder)
|
||||
.Build();
|
||||
|
||||
_movieFile = Builder<MovieFile>.CreateNew()
|
||||
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
|
||||
.With(f => f.RelativePath = @"Movie Title - 2022.mkv")
|
||||
.Build();
|
||||
|
||||
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.Movie = _movie)
|
||||
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
|
||||
.With(l => l.FileMovieInfo = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitles = new List<string> { "Movie Title" },
|
||||
Year = 2022
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("Movie Title - 2022.nfo", "Movie Title - 2022.nfo")]
|
||||
[TestCase("Movie.Title.2022.nfo", "Movie Title - 2022.nfo")]
|
||||
[TestCase("Movie Title 2022.nfo", "Movie Title - 2022.nfo")]
|
||||
[TestCase("Movie_Title_2022.nfo", "Movie Title - 2022.nfo")]
|
||||
[TestCase(@"Movie.Title.2022\thumb.jpg", "Movie Title - 2022.jpg")]
|
||||
public void should_import_matching_file(string filePath, string expectedOutputPath)
|
||||
{
|
||||
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(1);
|
||||
|
||||
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_import_multiple_nfo_files()
|
||||
{
|
||||
var files = new List<string>
|
||||
{
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.nfo").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Movie_Title_2022.nfo").AsOsAgnostic(),
|
||||
};
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
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.Extras.Subtitles;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Extras.Subtitles
|
||||
{
|
||||
[TestFixture]
|
||||
public class SubtitleServiceFixture : CoreTest<SubtitleService>
|
||||
{
|
||||
private Movie _movie;
|
||||
private MovieFile _movieFile;
|
||||
private LocalMovie _localMovie;
|
||||
|
||||
private string _MovieFolder;
|
||||
private string _releaseFolder;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_MovieFolder = @"C:\Test\Movies\Movie Title".AsOsAgnostic();
|
||||
_releaseFolder = @"C:\Test\Unsorted Movies\Movie.Title.2022".AsOsAgnostic();
|
||||
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Path = _MovieFolder)
|
||||
.Build();
|
||||
|
||||
_movieFile = Builder<MovieFile>.CreateNew()
|
||||
.With(f => f.Path = Path.Combine(_movie.Path, "Movie Title - 2022.mkv").AsOsAgnostic())
|
||||
.With(f => f.RelativePath = @"Movie Title - 2022.mkv".AsOsAgnostic())
|
||||
.Build();
|
||||
|
||||
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.Movie = _movie)
|
||||
.With(l => l.Path = Path.Combine(_releaseFolder, "Movie.Title.2022.mkv").AsOsAgnostic())
|
||||
.With(l => l.FileMovieInfo = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitles = new List<string> { "Movie Title" },
|
||||
Year = 2022
|
||||
})
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
|
||||
.Returns((string path) => Directory.GetParent(path).FullName);
|
||||
|
||||
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<MovieMetadata>(), It.IsAny<string>()))
|
||||
.Returns(DetectSampleResult.NotSample);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("Movie.Title.2022.en.nfo")]
|
||||
public void should_not_import_non_subtitle_file(string filePath)
|
||||
{
|
||||
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("Movie Title - 2022.srt", "Movie Title - 2022.srt")]
|
||||
[TestCase("Movie.Title.2022.en.srt", "Movie Title - 2022.en.srt")]
|
||||
[TestCase("Movie.Title.2022.english.srt", "Movie Title - 2022.en.srt")]
|
||||
[TestCase("Movie Title 2022_en_sdh_forced.srt", "Movie Title - 2022.en.sdh.forced.srt")]
|
||||
[TestCase("Movie_Title_2022 en.srt", "Movie Title - 2022.en.srt")]
|
||||
[TestCase(@"Subs\Movie.Title.2022\2_en.srt", "Movie Title - 2022.en.srt")]
|
||||
[TestCase("sub.srt", "Movie Title - 2022.srt")]
|
||||
public void should_import_matching_subtitle_file(string filePath, string expectedOutputPath)
|
||||
{
|
||||
var files = new List<string> { Path.Combine(_releaseFolder, filePath).AsOsAgnostic() };
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(1);
|
||||
|
||||
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_multiple_subtitle_files_per_language()
|
||||
{
|
||||
var files = new List<string>
|
||||
{
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.en.srt").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.eng.srt").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Subs", "Movie_Title_2022_en_forced.srt").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Subs", "Movie.Title.2022", "2_fr.srt").AsOsAgnostic()
|
||||
};
|
||||
|
||||
var expectedOutputs = new string[]
|
||||
{
|
||||
"Movie Title - 2022.1.en.srt",
|
||||
"Movie Title - 2022.2.en.srt",
|
||||
"Movie Title - 2022.en.forced.srt",
|
||||
"Movie Title - 2022.fr.srt",
|
||||
};
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(expectedOutputs.Length);
|
||||
|
||||
for (int i = 0; i < expectedOutputs.Length; i++)
|
||||
{
|
||||
results[i].RelativePath.AsOsAgnostic().PathEquals(expectedOutputs[i].AsOsAgnostic()).Should().Be(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_multiple_subtitle_files_per_language_with_tags()
|
||||
{
|
||||
var files = new List<string>
|
||||
{
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.cc.srt").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.other.en.forced.cc.srt").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.sdh.srt").AsOsAgnostic(),
|
||||
Path.Combine(_releaseFolder, "Movie.Title.2022.en.forced.default.srt").AsOsAgnostic(),
|
||||
};
|
||||
|
||||
var expectedOutputs = new[]
|
||||
{
|
||||
"Movie Title - 2022.1.en.forced.cc.srt",
|
||||
"Movie Title - 2022.2.en.forced.cc.srt",
|
||||
"Movie Title - 2022.en.forced.sdh.srt",
|
||||
"Movie Title - 2022.en.forced.default.srt"
|
||||
};
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(expectedOutputs.Length);
|
||||
|
||||
for (int i = 0; i < expectedOutputs.Length; i++)
|
||||
{
|
||||
results[i].RelativePath.AsOsAgnostic().PathEquals(expectedOutputs[i].AsOsAgnostic()).Should().Be(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(@"Subs\2_en.srt", "Movie Title - 2022.en.srt")]
|
||||
public void should_import_unmatching_subtitle_file_if_only_episode(string filePath, string expectedOutputPath)
|
||||
{
|
||||
var subtitleFile = Path.Combine(_releaseFolder, filePath).AsOsAgnostic();
|
||||
|
||||
var sampleFile = Path.Combine(_movie.Path, "Movie Title - 2022.sample.mkv").AsOsAgnostic();
|
||||
|
||||
var videoFiles = new string[]
|
||||
{
|
||||
_localMovie.Path,
|
||||
sampleFile
|
||||
};
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
|
||||
.Returns(videoFiles);
|
||||
|
||||
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<MovieMetadata>(), sampleFile))
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList();
|
||||
|
||||
results.Count().Should().Be(1);
|
||||
|
||||
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,8 @@ namespace NzbDrone.Core.Test.Languages
|
||||
new object[] { 32, Language.Ukrainian },
|
||||
new object[] { 33, Language.Persian },
|
||||
new object[] { 34, Language.Bengali },
|
||||
new object[] { 35, Language.Slovak },
|
||||
new object[] { 36, Language.Latvian },
|
||||
};
|
||||
|
||||
public static object[] ToIntCases =
|
||||
@@ -88,6 +90,8 @@ namespace NzbDrone.Core.Test.Languages
|
||||
new object[] { Language.Ukrainian, 32 },
|
||||
new object[] { Language.Persian, 33 },
|
||||
new object[] { Language.Bengali, 34 },
|
||||
new object[] { Language.Slovak, 35 },
|
||||
new object[] { Language.Latvian, 36 },
|
||||
};
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -20,9 +20,7 @@ using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||
{
|
||||
[Platform(Exclude = "Win")]
|
||||
[TestFixture]
|
||||
|
||||
public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
|
||||
{
|
||||
private Movie _movie;
|
||||
|
||||
@@ -27,6 +27,22 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
result.Languages.Should().BeEquivalentTo(Language.Unknown);
|
||||
}
|
||||
|
||||
[TestCase("Movie Title - 2022.en.sub")]
|
||||
[TestCase("Movie Title - 2022.EN.sub")]
|
||||
[TestCase("Movie Title - 2022.eng.sub")]
|
||||
[TestCase("Movie Title - 2022.ENG.sub")]
|
||||
[TestCase("Movie Title - 2022.English.sub")]
|
||||
[TestCase("Movie Title - 2022.english.sub")]
|
||||
[TestCase("Movie Title - 2022.en.cc.sub")]
|
||||
[TestCase("Movie Title - 2022.en.sdh.sub")]
|
||||
[TestCase("Movie Title - 2022.en.forced.sub")]
|
||||
[TestCase("Movie Title - 2022.en.sdh.forced.sub")]
|
||||
public void should_parse_subtitle_language_english(string fileName)
|
||||
{
|
||||
var result = LanguageParser.ParseSubtitleLanguage(fileName);
|
||||
result.Should().Be(Language.English);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.1994.French.1080p.XviD-LOL")]
|
||||
[TestCase("Movie Title : Other Title 2011 AVC.1080p.Blu-ray HD.VOSTFR.VFF")]
|
||||
[TestCase("Movie Title - Other Title 2011 Bluray 4k HDR HEVC AC3 VFF")]
|
||||
@@ -352,6 +368,16 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
result.Languages.Should().BeEquivalentTo(Language.Slovak);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.2022.LV.WEBRip.XviD-LOL")]
|
||||
[TestCase("Movie.Title.2022.lv.WEBRip.XviD-LOL")]
|
||||
[TestCase("Movie.Title.2022.LATVIAN.WEBRip.XviD-LOL")]
|
||||
[TestCase("Movie.Title.2022.Latvian.WEBRip.XviD-LOL")]
|
||||
public void should_parse_language_latvian(string postTitle)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle);
|
||||
result.Languages.Should().BeEquivalentTo(Language.Latvian);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.en.sub")]
|
||||
[TestCase("Movie Title.eng.sub")]
|
||||
[TestCase("Movie.Title.eng.forced.sub")]
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("A.I.Artificial.Movie.(2001)", "A.I. Artificial Movie")]
|
||||
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
|
||||
[TestCase("www.Torrenting.com - Movie.2008.720p.X264-DIMENSION", "Movie")]
|
||||
[TestCase("www.5MovieRulz.tc - Movie (2000) Malayalam HQ HDRip - x264 - AAC - 700MB.mkv", "Movie")]
|
||||
[TestCase("Movie: The Movie World 2013", "Movie: The Movie World")]
|
||||
[TestCase("Movie.The.Final.Chapter.2016", "Movie The Final Chapter")]
|
||||
[TestCase("Der.Movie.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Movie James")]
|
||||
|
||||
@@ -428,6 +428,8 @@ namespace NzbDrone.Core.Configuration
|
||||
public CertificateValidationType CertificateValidation =>
|
||||
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
|
||||
|
||||
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
|
||||
|
||||
private string GetValue(string key)
|
||||
{
|
||||
return GetValue(key, string.Empty);
|
||||
|
||||
@@ -104,5 +104,6 @@ namespace NzbDrone.Core.Configuration
|
||||
int BackupRetention { get; }
|
||||
|
||||
CertificateValidationType CertificateValidation { get; }
|
||||
string ApplicationUrl { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
protected Regex _regex;
|
||||
protected string _raw;
|
||||
|
||||
[FieldDefinition(1, Label = "Regular Expression")]
|
||||
[FieldDefinition(1, Label = "Regular Expression", HelpText = "Custom Format RegEx is Case Insensitive")]
|
||||
public string Value
|
||||
{
|
||||
get => _raw;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(214)]
|
||||
public class add_language_tags_to_subtitle_files : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("SubtitleFiles").AddColumn("LanguageTags").AsString().Nullable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -31,7 +30,6 @@ namespace NzbDrone.Core.Extras
|
||||
public void Handle(MovieScannedEvent message)
|
||||
{
|
||||
var movie = message.Movie;
|
||||
var extraFiles = new List<ExtraFile>();
|
||||
|
||||
if (!_diskProvider.FolderExists(movie.Path))
|
||||
{
|
||||
@@ -43,17 +41,16 @@ namespace NzbDrone.Core.Extras
|
||||
var filesOnDisk = _diskScanService.GetNonVideoFiles(movie.Path);
|
||||
var possibleExtraFiles = _diskScanService.FilterPaths(movie.Path, filesOnDisk, false);
|
||||
|
||||
var filteredFiles = possibleExtraFiles;
|
||||
var importedFiles = new List<string>();
|
||||
|
||||
foreach (var existingExtraFileImporter in _existingExtraFileImporters)
|
||||
{
|
||||
var imported = existingExtraFileImporter.ProcessFiles(movie, filteredFiles, importedFiles);
|
||||
var imported = existingExtraFileImporter.ProcessFiles(movie, possibleExtraFiles, importedFiles);
|
||||
|
||||
importedFiles.AddRange(imported.Select(f => Path.Combine(movie.Path, f.RelativePath)));
|
||||
}
|
||||
|
||||
_logger.Info("Found {0} extra files", extraFiles.Count);
|
||||
_logger.Info("Found {0} possible extra files, imported {1} files.", possibleExtraFiles.Count, importedFiles.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace NzbDrone.Core.Extras
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly List<IManageExtraFiles> _extraFileManagers;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ExtraService(IMediaFileService mediaFileService,
|
||||
IMovieService movieService,
|
||||
@@ -45,7 +44,6 @@ namespace NzbDrone.Core.Extras
|
||||
_diskProvider = diskProvider;
|
||||
_configService = configService;
|
||||
_extraFileManagers = extraFileManagers.OrderBy(e => e.Order).ToList();
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void ImportMovie(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly)
|
||||
@@ -62,61 +60,42 @@ namespace NzbDrone.Core.Extras
|
||||
return;
|
||||
}
|
||||
|
||||
var sourcePath = localMovie.Path;
|
||||
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
|
||||
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
|
||||
var files = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories).Where(f => f != localMovie.Path);
|
||||
var folderSearchOption = localMovie.FolderMovieInfo == null
|
||||
? SearchOption.TopDirectoryOnly
|
||||
: SearchOption.AllDirectories;
|
||||
|
||||
var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(e => e.Trim(' ', '.'))
|
||||
.Select(e => e.Trim(' ', '.')
|
||||
.Insert(0, "."))
|
||||
.ToList();
|
||||
|
||||
var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
var filteredFilenames = new List<string>();
|
||||
var hasNfo = false;
|
||||
var sourceFolder = _diskProvider.GetParentFolder(localMovie.Path);
|
||||
var files = _diskProvider.GetFiles(sourceFolder, folderSearchOption);
|
||||
var managedFiles = _extraFileManagers.Select((i) => new List<string>()).ToArray();
|
||||
|
||||
foreach (var matchingFilename in matchingFilenames)
|
||||
foreach (var file in files)
|
||||
{
|
||||
// Filter out duplicate NFO files
|
||||
if (matchingFilename.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (hasNfo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasNfo = true;
|
||||
}
|
||||
|
||||
filteredFilenames.Add(matchingFilename);
|
||||
}
|
||||
|
||||
foreach (var matchingFilename in filteredFilenames)
|
||||
{
|
||||
var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e));
|
||||
var extension = Path.GetExtension(file);
|
||||
var matchingExtension = wantedExtensions.FirstOrDefault(e => e.Equals(extension));
|
||||
|
||||
if (matchingExtension == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
for (int i = 0; i < _extraFileManagers.Count; i++)
|
||||
{
|
||||
foreach (var extraFileManager in _extraFileManagers)
|
||||
if (_extraFileManagers[i].CanImportFile(localMovie, movieFile, file, extension, isReadOnly))
|
||||
{
|
||||
var extension = Path.GetExtension(matchingFilename);
|
||||
var extraFile = extraFileManager.Import(localMovie.Movie, movieFile, matchingFilename, extension, isReadOnly);
|
||||
|
||||
if (extraFile != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
managedFiles[i].Add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _extraFileManagers.Count; i++)
|
||||
{
|
||||
_extraFileManagers[i].ImportFiles(localMovie, movieFile, managedFiles[i], isReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Extras.Files
|
||||
{
|
||||
@@ -19,7 +20,8 @@ namespace NzbDrone.Core.Extras.Files
|
||||
IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
||||
IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
|
||||
IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
||||
ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||
bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||
IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
|
||||
}
|
||||
|
||||
public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
|
||||
@@ -47,7 +49,8 @@ namespace NzbDrone.Core.Extras.Files
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
|
||||
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
||||
public abstract ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||
public abstract bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||
public abstract IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
|
||||
|
||||
protected TExtraFile ImportFile(Movie movie, MovieFile movieFile, string path, bool readOnly, string extension, string fileNameSuffix = null)
|
||||
{
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
||||
|
||||
details.Add(new XElement("country"));
|
||||
|
||||
if (movie.MovieMetadata.Value.CollectionTitle != null)
|
||||
if (Settings.AddCollectionName && movie.MovieMetadata.Value.CollectionTitle != null)
|
||||
{
|
||||
var setElement = new XElement("set");
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
||||
MovieMetadataLanguage = (int)Language.English;
|
||||
MovieImages = true;
|
||||
UseMovieNfo = false;
|
||||
AddCollectionName = true;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)]
|
||||
@@ -41,6 +42,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
||||
[FieldDefinition(4, Label = "Use Movie.nfo", Type = FieldType.Checkbox, HelpText = "Radarr will write metadata to movie.nfo instead of the default <movie-filename>.nfo")]
|
||||
public bool UseMovieNfo { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Collection Name", Type = FieldType.Checkbox, HelpText = "Radarr will write the collection name to the .nfo file", Advanced = true)]
|
||||
public bool AddCollectionName { get; set; }
|
||||
|
||||
public bool IsValid => true;
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -13,6 +13,7 @@ using NzbDrone.Core.Extras.Metadata.Files;
|
||||
using NzbDrone.Core.Extras.Others;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Extras.Metadata
|
||||
{
|
||||
@@ -191,9 +192,14 @@ namespace NzbDrone.Core.Extras.Metadata
|
||||
return movedFiles;
|
||||
}
|
||||
|
||||
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||
public override bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
|
||||
{
|
||||
return Enumerable.Empty<ExtraFile>();
|
||||
}
|
||||
|
||||
private List<MetadataFile> GetMetadataFilesForConsumer(IMetadata consumer, List<MetadataFile> movieMetadata)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
@@ -7,13 +9,16 @@ using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Extras.Others
|
||||
{
|
||||
public class OtherExtraService : ExtraFileManager<OtherExtraFile>
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IOtherExtraFileService _otherExtraFileService;
|
||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public OtherExtraService(IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
@@ -23,8 +28,10 @@ namespace NzbDrone.Core.Extras.Others
|
||||
Logger logger)
|
||||
: base(configService, diskProvider, diskTransferService, logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_otherExtraFileService = otherExtraFileService;
|
||||
_mediaFileAttributeService = mediaFileAttributeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override int Order => 2;
|
||||
@@ -69,17 +76,79 @@ namespace NzbDrone.Core.Extras.Others
|
||||
return movedFiles;
|
||||
}
|
||||
|
||||
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||
public override bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||
{
|
||||
var extraFile = ImportFile(movie, movieFile, path, readOnly, extension, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (extraFile != null)
|
||||
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
|
||||
{
|
||||
var importedFiles = new List<ExtraFile>();
|
||||
var filteredFiles = files.Where(f => CanImportFile(localMovie, movieFile, f, Path.GetExtension(f), isReadOnly)).ToList();
|
||||
var sourcePath = localMovie.Path;
|
||||
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
|
||||
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
|
||||
var matchingFiles = new List<string>();
|
||||
var hasNfo = false;
|
||||
|
||||
foreach (var file in filteredFiles)
|
||||
{
|
||||
_mediaFileAttributeService.SetFilePermissions(path);
|
||||
_otherExtraFileService.Upsert(extraFile);
|
||||
try
|
||||
{
|
||||
// Filter out duplicate NFO files
|
||||
if (file.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (hasNfo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasNfo = true;
|
||||
}
|
||||
|
||||
// Filename match
|
||||
if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
matchingFiles.Add(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Movie match
|
||||
var fileMovieInfo = Parser.Parser.ParseMoviePath(file) ?? new ParsedMovieInfo();
|
||||
|
||||
if (fileMovieInfo.MovieTitle == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileMovieInfo.MovieTitle == localMovie.FileMovieInfo.MovieTitle &&
|
||||
fileMovieInfo.Year.Equals(localMovie.FileMovieInfo.Year))
|
||||
{
|
||||
matchingFiles.Add(file);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to import extra file: {0}", file);
|
||||
}
|
||||
}
|
||||
|
||||
return extraFile;
|
||||
foreach (string file in matchingFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var extraFile = ImportFile(localMovie.Movie, movieFile, file, isReadOnly, Path.GetExtension(file), null);
|
||||
_mediaFileAttributeService.SetFilePermissions(file);
|
||||
_otherExtraFileService.Upsert(extraFile);
|
||||
importedFiles.Add(extraFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to import extra file: {0}", file);
|
||||
}
|
||||
}
|
||||
|
||||
return importedFiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
@@ -5,6 +6,17 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
{
|
||||
public class SubtitleFile : ExtraFile
|
||||
{
|
||||
public SubtitleFile()
|
||||
{
|
||||
LanguageTags = new List<string>();
|
||||
}
|
||||
|
||||
public Language Language { get; set; }
|
||||
|
||||
public string AggregateString => Language + LanguageTagsAsString + Extension;
|
||||
|
||||
public List<string> LanguageTags { get; set; }
|
||||
|
||||
private string LanguageTagsAsString => string.Join(".", LanguageTags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -9,13 +10,17 @@ using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Extras.Subtitles
|
||||
{
|
||||
public class SubtitleService : ExtraFileManager<SubtitleFile>
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly ISubtitleFileService _subtitleFileService;
|
||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
||||
private readonly Logger _logger;
|
||||
@@ -23,11 +28,14 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
public SubtitleService(IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
IDiskTransferService diskTransferService,
|
||||
IDetectSample detectSample,
|
||||
ISubtitleFileService subtitleFileService,
|
||||
IMediaFileAttributeService mediaFileAttributeService,
|
||||
Logger logger)
|
||||
: base(configService, diskProvider, diskTransferService, logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_detectSample = detectSample;
|
||||
_subtitleFileService = subtitleFileService;
|
||||
_mediaFileAttributeService = mediaFileAttributeService;
|
||||
_logger = logger;
|
||||
@@ -64,21 +72,16 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
foreach (var movieFile in movieFiles)
|
||||
{
|
||||
var groupedExtraFilesForMovieFile = subtitleFiles.Where(m => m.MovieFileId == movieFile.Id)
|
||||
.GroupBy(s => s.Language + s.Extension).ToList();
|
||||
.GroupBy(s => s.AggregateString).ToList();
|
||||
|
||||
foreach (var group in groupedExtraFilesForMovieFile)
|
||||
{
|
||||
var groupCount = group.Count();
|
||||
var copy = 1;
|
||||
|
||||
if (groupCount > 1)
|
||||
{
|
||||
_logger.Warn("Multiple subtitle files found with the same language and extension for {0}", Path.Combine(movie.Path, movieFile.RelativePath));
|
||||
}
|
||||
|
||||
foreach (var subtitleFile in group)
|
||||
{
|
||||
var suffix = GetSuffix(subtitleFile.Language, copy, groupCount > 1);
|
||||
var suffix = GetSuffix(subtitleFile.Language, copy, subtitleFile.LanguageTags, groupCount > 1);
|
||||
movedFiles.AddIfNotNull(MoveFile(movie, movieFile, subtitleFile, suffix));
|
||||
|
||||
copy++;
|
||||
@@ -91,25 +94,141 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
return movedFiles;
|
||||
}
|
||||
|
||||
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||
public override bool CanImportFile(LocalMovie localEpisode, MovieFile movieFile, string path, string extension, bool readOnly)
|
||||
{
|
||||
if (SubtitleFileExtensions.Extensions.Contains(Path.GetExtension(path)))
|
||||
{
|
||||
var language = LanguageParser.ParseSubtitleLanguage(path);
|
||||
var suffix = GetSuffix(language, 1, false);
|
||||
var subtitleFile = ImportFile(movie, movieFile, path, readOnly, extension, suffix);
|
||||
subtitleFile.Language = language;
|
||||
|
||||
_mediaFileAttributeService.SetFilePermissions(path);
|
||||
_subtitleFileService.Upsert(subtitleFile);
|
||||
|
||||
return subtitleFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
return SubtitleFileExtensions.Extensions.Contains(extension.ToLowerInvariant());
|
||||
}
|
||||
|
||||
private string GetSuffix(Language language, int copy, bool multipleCopies = false)
|
||||
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
|
||||
{
|
||||
var importedFiles = new List<SubtitleFile>();
|
||||
|
||||
var filteredFiles = files.Where(f => CanImportFile(localMovie, movieFile, f, Path.GetExtension(f), isReadOnly)).ToList();
|
||||
|
||||
var sourcePath = localMovie.Path;
|
||||
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
|
||||
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
|
||||
|
||||
var matchingFiles = new List<string>();
|
||||
|
||||
foreach (var file in filteredFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Filename match
|
||||
if (Path.GetFileNameWithoutExtension(file).StartsWithIgnoreCase(sourceFileName))
|
||||
{
|
||||
matchingFiles.Add(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Movie match
|
||||
var fileMovieInfo = Parser.Parser.ParseMoviePath(file) ?? new ParsedMovieInfo();
|
||||
|
||||
if (fileMovieInfo.MovieTitle == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileMovieInfo.MovieTitle == localMovie.FileMovieInfo.MovieTitle &&
|
||||
fileMovieInfo.Year.Equals(localMovie.FileMovieInfo.Year))
|
||||
{
|
||||
matchingFiles.Add(file);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to import subtitle file: {0}", file);
|
||||
}
|
||||
}
|
||||
|
||||
// Use any sub if only episode in folder
|
||||
if (matchingFiles.Count == 0 && filteredFiles.Count > 0)
|
||||
{
|
||||
var videoFiles = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories)
|
||||
.Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file)))
|
||||
.ToList();
|
||||
|
||||
if (videoFiles.Count() > 2)
|
||||
{
|
||||
return importedFiles;
|
||||
}
|
||||
|
||||
// Filter out samples
|
||||
videoFiles = videoFiles.Where(file =>
|
||||
{
|
||||
var sample = _detectSample.IsSample(localMovie.Movie.MovieMetadata, file);
|
||||
|
||||
if (sample == DetectSampleResult.Sample)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
if (videoFiles.Count == 1)
|
||||
{
|
||||
matchingFiles.AddRange(filteredFiles);
|
||||
|
||||
_logger.Warn("Imported any available subtitle file for movie: {0}", localMovie);
|
||||
}
|
||||
}
|
||||
|
||||
var subtitleFiles = new List<SubtitleFile>();
|
||||
|
||||
foreach (string file in matchingFiles)
|
||||
{
|
||||
var language = LanguageParser.ParseSubtitleLanguage(file);
|
||||
var extension = Path.GetExtension(file);
|
||||
var languageTags = LanguageParser.ParseLanguageTags(file);
|
||||
var subFile = new SubtitleFile
|
||||
{
|
||||
Language = language,
|
||||
Extension = extension
|
||||
};
|
||||
subFile.LanguageTags = languageTags.ToList();
|
||||
subFile.RelativePath = PathExtensions.GetRelativePath(sourceFolder, file);
|
||||
subtitleFiles.Add(subFile);
|
||||
}
|
||||
|
||||
var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.AggregateString).ToList();
|
||||
|
||||
foreach (var group in groupedSubtitleFiles)
|
||||
{
|
||||
var groupCount = group.Count();
|
||||
var copy = 1;
|
||||
|
||||
foreach (var file in group)
|
||||
{
|
||||
var path = Path.Combine(sourceFolder, file.RelativePath);
|
||||
var language = file.Language;
|
||||
var extension = file.Extension;
|
||||
var suffix = GetSuffix(language, copy, file.LanguageTags, groupCount > 1);
|
||||
try
|
||||
{
|
||||
var subtitleFile = ImportFile(localMovie.Movie, movieFile, path, isReadOnly, extension, suffix);
|
||||
subtitleFile.Language = language;
|
||||
subtitleFile.LanguageTags = file.LanguageTags;
|
||||
|
||||
_mediaFileAttributeService.SetFilePermissions(path);
|
||||
_subtitleFileService.Upsert(subtitleFile);
|
||||
|
||||
importedFiles.Add(subtitleFile);
|
||||
|
||||
copy++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to import subtitle file: {0}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return importedFiles;
|
||||
}
|
||||
|
||||
private string GetSuffix(Language language, int copy, List<string> languageTags, bool multipleCopies = false)
|
||||
{
|
||||
var suffixBuilder = new StringBuilder();
|
||||
|
||||
@@ -125,6 +244,12 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
suffixBuilder.Append(IsoLanguages.Get(language).TwoLetterCode);
|
||||
}
|
||||
|
||||
if (languageTags.Any())
|
||||
{
|
||||
suffixBuilder.Append(".");
|
||||
suffixBuilder.Append(string.Join(".", languageTags));
|
||||
}
|
||||
|
||||
return suffixBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
||||
public TraktPopularSettings()
|
||||
{
|
||||
TraktListType = (int)TraktPopularListType.Popular;
|
||||
Rating = "0-100";
|
||||
Certification = "NR,G,PG,PG-13,R,NC-17";
|
||||
Genres = "";
|
||||
Years = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "Type of list you're seeking to import from")]
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace NzbDrone.Core.Languages
|
||||
public static Language Persian => new Language(33, "Persian");
|
||||
public static Language Bengali => new Language(34, "Bengali");
|
||||
public static Language Slovak => new Language(35, "Slovak");
|
||||
public static Language Latvian => new Language(36, "Latvian");
|
||||
public static Language Any => new Language(-1, "Any");
|
||||
public static Language Original => new Language(-2, "Original");
|
||||
|
||||
@@ -151,6 +152,7 @@ namespace NzbDrone.Core.Languages
|
||||
Persian,
|
||||
Bengali,
|
||||
Slovak,
|
||||
Latvian,
|
||||
Any,
|
||||
Original
|
||||
};
|
||||
|
||||
@@ -639,7 +639,7 @@
|
||||
"RetryingDownloadInterp": "Herunterladen nochmal versuchen {0} um {1}",
|
||||
"RemovingTag": "Tag entfernen",
|
||||
"ReleaseWillBeProcessedInterp": "Release wird verarbeitet {0}",
|
||||
"Queued": "Eingereiht",
|
||||
"Queued": "In der Warteschlange",
|
||||
"QualityProfileDeleteConfirm": "Qualitätsprofil '{0}' wirklich löschen",
|
||||
"Pending": "Ausstehend",
|
||||
"Paused": "Pausiert",
|
||||
@@ -653,7 +653,7 @@
|
||||
"MarkAsFailedMessageText": "'{0}' wirklich als fehlgeschlagen markieren?",
|
||||
"Manual": "Manuell",
|
||||
"LogLevelTraceHelpTextWarning": "Trace logging sollte nur kurzzeitig aktiviert werden",
|
||||
"LastDuration": "Letzte Laufzeit",
|
||||
"LastDuration": "Letzte Dauer",
|
||||
"IncludeRecommendationsHelpText": "Radarrs Empfehlungen in der Entdeckungsansicht mit einbeziehen",
|
||||
"IncludeRadarrRecommendations": "Radarr Empfehlungen einbeziehen",
|
||||
"ImportFailedInterp": "Import fehlgeschlagen: {0}",
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
"ApiKey": "API Key",
|
||||
"AppDataDirectory": "AppData directory",
|
||||
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
||||
"ApplicationURL": "Application URL",
|
||||
"ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base",
|
||||
"Apply": "Apply",
|
||||
"ApplyTags": "Apply Tags",
|
||||
"ApplyTagsHelpTexts1": "How to apply tags to the selected movies",
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"Search": "Buscar",
|
||||
"Scheduled": "Programado",
|
||||
"SaveChanges": "Guardar Cambios",
|
||||
"RSSSync": "Sincronización RSS",
|
||||
"RSSSync": "Sincronizar RSS",
|
||||
"RootFolders": "Carpetas de Origen",
|
||||
"RootFolderCheckSingleMessage": "La carpeta de origen no existe: {0}",
|
||||
"RootFolderCheckMultipleMessage": "Varias carpetas de origen no existen: {0}",
|
||||
@@ -232,7 +232,7 @@
|
||||
"OAuthPopupMessage": "Pop-ups bloqueados por su navegador",
|
||||
"Name": "Nombre",
|
||||
"MoveFiles": "Mover Archivos",
|
||||
"Monitored": "Monitoreadas",
|
||||
"Monitored": "Monitoreada",
|
||||
"Message": "Mensaje",
|
||||
"Location": "Localización",
|
||||
"Level": "Nivel",
|
||||
@@ -461,7 +461,7 @@
|
||||
"ShowMonitoredHelpText": "Mostrar el estado de monitoreo debajo del poster",
|
||||
"ShowCutoffUnmetIconHelpText": "Mostrar el icono para los ficheros cuando no se ha alcanzado el corte",
|
||||
"ShowAsAllDayEvents": "Mostrar como Eventos de Todo el Día",
|
||||
"ShouldMonitorHelpText": "Si se habilita, las películas añadidas desde esta lista serán también monitoreadas",
|
||||
"ShouldMonitorHelpText": "Si las películas o colecciones en esta lista deberían ser agregadas como monitoreadas",
|
||||
"SetPermissionsLinuxHelpTextWarning": "Si no estas seguro de lo que hacen estos ajustes, no los modifiques.",
|
||||
"SetPermissionsLinuxHelpText": "Debe chmod ser ejecutado una vez los archivos hayan sido importados/renombrados?",
|
||||
"SetPermissions": "Ajustar Permisos",
|
||||
@@ -614,8 +614,8 @@
|
||||
"CleanLibraryLevel": "Limpiar el Nivel de la Librería",
|
||||
"BindAddress": "Dirección de Ligado",
|
||||
"OpenBrowserOnStart": "Abrir navegador al arrancar",
|
||||
"OnUpgradeHelpText": "Al Actualizar",
|
||||
"OnRenameHelpText": "En Renombrado",
|
||||
"OnUpgradeHelpText": "Al Mejorar La Calidad",
|
||||
"OnRenameHelpText": "Al Renombrar",
|
||||
"OnHealthIssueHelpText": "En Problema de Salud",
|
||||
"OnGrabHelpText": "Al Capturar",
|
||||
"SettingsRuntimeFormat": "Formato de Tiempo de ejecución",
|
||||
@@ -633,7 +633,7 @@
|
||||
"ShownClickToHide": "Mostrado, clic para ocultar",
|
||||
"ShowGenres": "Mostrar Géneros",
|
||||
"ShowCertification": "Mostrar Certificación",
|
||||
"SearchOnAddHelpText": "Buscar películas en esta lista cuando se añadan a Radarr",
|
||||
"SearchOnAddHelpText": "Buscar películas en esta lista cuando se añada a la biblioteca",
|
||||
"RSSSyncIntervalHelpTextWarning": "Se aplicará a todos los indexers, por favor sigue las reglas de los mismos",
|
||||
"RSSIsNotSupportedWithThisIndexer": "RSS no son soportadas por este indexer",
|
||||
"RetryingDownloadInterp": "Re-intentando descarga {0} en {1}",
|
||||
@@ -874,11 +874,11 @@
|
||||
"MovieFilesTotaling": "Totalización de archivos de película",
|
||||
"OnGrab": "Al Capturar",
|
||||
"OnHealthIssue": "En Problema de Salud",
|
||||
"OnImport": "Al importar",
|
||||
"OnImport": "Al Importar",
|
||||
"OnLatestVersion": "La última versión de Radarr ya está instalada",
|
||||
"OnlyTorrent": "Solo Torrent",
|
||||
"OnlyUsenet": "Solo Usenet",
|
||||
"OnRename": "En Renombrado",
|
||||
"OnRename": "Al Renombrar",
|
||||
"PreferTorrent": "Prefiero Torrent",
|
||||
"PreferUsenet": "Prefiero Usenet",
|
||||
"Presets": "Preajustes",
|
||||
@@ -988,12 +988,12 @@
|
||||
"Score": "Puntuación",
|
||||
"Script": "Guión",
|
||||
"SearchCutoffUnmet": "Corte de búsqueda no alcanzado",
|
||||
"SearchMissing": "Búsqueda que falta",
|
||||
"SearchMissing": "Buscar Faltantes",
|
||||
"Seconds": "Segundos",
|
||||
"SelectDotDot": "Seleccione...",
|
||||
"SelectLanguage": "Seleccione el idioma",
|
||||
"SelectMovie": "Seleccionar película",
|
||||
"SelectQuality": "Seleccionar calidad",
|
||||
"SelectLanguage": "Seleccionar Idioma",
|
||||
"SelectMovie": "Seleccionar Película",
|
||||
"SelectQuality": "Seleccionar Calidad",
|
||||
"Small": "Pequeña",
|
||||
"Socks4": "Calcetines4",
|
||||
"Socks5": "Socks5 (soporte TOR)",
|
||||
@@ -1031,13 +1031,13 @@
|
||||
"showCinemaReleaseHelpText": "Mostrar la fecha de estreno en el cine debajo del cartel",
|
||||
"ShowReleaseDate": "Mostrar fecha de lanzamiento",
|
||||
"ShowReleaseDateHelpText": "Mostrar la fecha de lanzamiento debajo del cartel",
|
||||
"OnMovieDelete": "Al eliminar la película",
|
||||
"OnMovieDeleteHelpText": "Al eliminar la película",
|
||||
"OnMovieFileDelete": "Al eliminar archivo de película",
|
||||
"OnMovieFileDeleteHelpText": "Al eliminar archivo de película",
|
||||
"OnMovieFileDeleteForUpgrade": "En archivo de película Eliminar para actualizar",
|
||||
"OnMovieFileDeleteForUpgradeHelpText": "En archivo de película Eliminar para actualizar",
|
||||
"OnUpgrade": "Al Actualizar",
|
||||
"OnMovieDelete": "Al Eliminar Película",
|
||||
"OnMovieDeleteHelpText": "Al Eliminar Película",
|
||||
"OnMovieFileDelete": "Al Eliminar Archivo De Película",
|
||||
"OnMovieFileDeleteHelpText": "Al Eliminar Archivo De Película",
|
||||
"OnMovieFileDeleteForUpgrade": "Al Eliminar Archivo De Pelicula Para Mejorar La Calidad",
|
||||
"OnMovieFileDeleteForUpgradeHelpText": "Al Eliminar Archivo De Pelicula Para Mejorar La Calidad",
|
||||
"OnUpgrade": "Al Mejorar La Calidad",
|
||||
"Reddit": "Reddit",
|
||||
"More": "Más",
|
||||
"Download": "Descargar",
|
||||
@@ -1064,7 +1064,7 @@
|
||||
"List": "Lista",
|
||||
"ManualImportSetReleaseGroup": "Importación manual - Configurar el grupo de lanzamiento",
|
||||
"NotificationTriggersHelpText": "Seleccione qué eventos deben activar esta notificación",
|
||||
"OnApplicationUpdate": "Al actualizar la aplicación",
|
||||
"OnApplicationUpdate": "Al Actualizar La Aplicación",
|
||||
"RemotePathMappingCheckFileRemoved": "El archivo {0} fue eliminado a mitad del proceso.",
|
||||
"RemotePath": "Ruta remota",
|
||||
"RemotePathMappingCheckBadDockerPath": "Está utilizando docker; el cliente de descarga {0} coloca las descargas en {1} pero esta no es una ruta válida {2}. Revisa tus mapeos de rutas remotas y la configuración del cliente de descarga.",
|
||||
@@ -1072,7 +1072,7 @@
|
||||
"RemotePathMappingCheckFilesBadDockerPath": "Está utilizando docker; el cliente de descarga {0} informó de archivos en {1} pero esta no es una ruta válida {2}. Revise sus mapeos de rutas remotas y la configuración del cliente de descarga.",
|
||||
"RemotePathMappingCheckLocalWrongOSPath": "El cliente de descarga local {0} coloca las descargas en {1} pero ésta no es una ruta válida {2}. Revise la configuración de su cliente de descarga.",
|
||||
"RemotePathMappingCheckRemoteDownloadClient": "El cliente de descarga remota {0} informó de la existencia de archivos en {1} pero este directorio no parece existir. Probablemente falta mapear la ruta remota.",
|
||||
"SelectLanguages": "Seleccionar idiomas",
|
||||
"SelectLanguages": "Seleccionar Idiomas",
|
||||
"IndexerDownloadClientHelpText": "Especifica qué cliente de descargas se utiliza para las descargas de este indexador",
|
||||
"AnnouncedMsg": "Película anunciada",
|
||||
"Auto": "Auto",
|
||||
@@ -1097,7 +1097,7 @@
|
||||
"TmdbRating": "TMDb valoración",
|
||||
"TmdbVotes": "Votos en TMDb",
|
||||
"Filters": "Filtros",
|
||||
"OnApplicationUpdateHelpText": "Al actualizar la aplicación",
|
||||
"OnApplicationUpdateHelpText": "Al Actualizar La Aplicación",
|
||||
"OriginalTitle": "Título original",
|
||||
"OriginalLanguage": "Idioma original",
|
||||
"Rating": "Puntuación",
|
||||
@@ -1108,7 +1108,7 @@
|
||||
"RemotePathMappingCheckFilesLocalWrongOSPath": "El cliente de descarga local {0} informó de la existencia de archivos en {1}, pero no es una ruta válida {2}. Revise la configuración de su cliente de descarga.",
|
||||
"RemoveSelectedItem": "Eliminar el elemento seleccionado",
|
||||
"RemoveSelectedItems": "Eliminar los elementos seleccionados",
|
||||
"SelectReleaseGroup": "Seleccionar el grupo de lanzamiento",
|
||||
"SelectReleaseGroup": "Seleccionar Grupo De Lanzamiento",
|
||||
"SetReleaseGroup": "Configurar el grupo de lanzamiento",
|
||||
"TaskUserAgentTooltip": "User-Agent proporcionado por la aplicación llamó a la API",
|
||||
"RssSyncHelpText": "Intervalo en minutos. Ajustar a cero para inhabilitar (esto dentendrá toda captura de estrenos automática)",
|
||||
@@ -1120,5 +1120,27 @@
|
||||
"CollectionOptions": "Opciones de la Colección",
|
||||
"CollectionShowDetailsHelpText": "Mostrar el estado y propiedades de la colección",
|
||||
"CollectionShowOverviewsHelpText": "Mostrar resumenes de la colección",
|
||||
"CollectionShowPostersHelpText": "Mostrar póster de artículos de colección"
|
||||
"CollectionShowPostersHelpText": "Mostrar póster de artículos de colección",
|
||||
"RefreshMonitoredIntervalHelpText": "Cada cuánto actualizar las descargas monitoreadeas desde los clientes de descarga, mínimo 1 minuto",
|
||||
"SearchOnAddCollectionHelpText": "Buscar películas en esta colección cuando se añada a la biblioteca",
|
||||
"TotalMovies": "Películas Totales",
|
||||
"CollectionsSelectedInterp": "{0} Colecciones Seleccionadas",
|
||||
"EditCollection": "Editar Colección",
|
||||
"MonitorCollection": "Monitorear Colección",
|
||||
"MonitoredCollectionHelpText": "Monitorear para que las películas de esta colección se añadan automáticamente a la biblioteca",
|
||||
"MovieAndCollection": "Película y Colección",
|
||||
"MovieCollectionMultipleMissingRoots": "Múltiples carpetas raices faltantes para colecciones de películas: {0}",
|
||||
"OnMovieAdded": "Al Agregar Pelicula",
|
||||
"OnMovieAddedHelpText": "Al Agregar Película",
|
||||
"RefreshCollections": "Actualizar Colecciones",
|
||||
"ShowCollectionDetails": "Mostrar Estado De Colección",
|
||||
"InstanceName": "Nombre de Instancia",
|
||||
"InstanceNameHelpText": "Nombre de instancia en pestaña y para nombre de aplicación en Syslog",
|
||||
"RottenTomatoesRating": "Tomato Rating",
|
||||
"ScrollMovies": "Desplazar Películas",
|
||||
"ShowPosters": "Mostrar Posters",
|
||||
"UnableToLoadCollections": "No se han podido cargar las colecciones",
|
||||
"MovieCollectionMissingRoot": "Carpeta raíz faltante para colección de películas: {0}",
|
||||
"MovieOnly": "Solo Película",
|
||||
"Database": "Base de Datos"
|
||||
}
|
||||
|
||||
@@ -1143,5 +1143,7 @@
|
||||
"CollectionShowDetailsHelpText": "Näytä kokoelmien tila ja ominaisuudet",
|
||||
"CollectionShowPostersHelpText": "Näytä kokoelmien julisteet",
|
||||
"RottenTomatoesRating": "Tomaattiarvio",
|
||||
"TotalMovies": "Elokuvia yhteensä"
|
||||
"TotalMovies": "Elokuvia yhteensä",
|
||||
"ApplicationURL": "Sovelluksen URL-osoite",
|
||||
"ApplicationUrlHelpText": "Sovelluksen ulkoinen URL-osoite, johon sisältyy http(s)://, portti ja URL-perusta."
|
||||
}
|
||||
|
||||
@@ -1137,5 +1137,10 @@
|
||||
"UnableToLoadCollections": "Impossible de charger les collections",
|
||||
"RottenTomatoesRating": "Classement Rotten Tomatoes",
|
||||
"ShowCollectionDetails": "Afficher l'état de la collection",
|
||||
"EditCollection": "Modifier la collection"
|
||||
"EditCollection": "Modifier la collection",
|
||||
"ApplicationURL": "URL de l'application",
|
||||
"ApplicationUrlHelpText": "URL externe de cette application, y compris http(s)://, le port et l'URL de base",
|
||||
"InstanceName": "Nom de l'instance",
|
||||
"InstanceNameHelpText": "Nom de l'instance dans l'onglet du navigateur et pour le nom d'application dans Syslog",
|
||||
"CollectionShowPostersHelpText": "Afficher les affiches des éléments de la collection"
|
||||
}
|
||||
|
||||
@@ -1143,5 +1143,7 @@
|
||||
"ShowPosters": "Poszterek megjelenítése",
|
||||
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
|
||||
"RottenTomatoesRating": "Tomato Értékelés",
|
||||
"TotalMovies": "Összes film"
|
||||
"TotalMovies": "Összes film",
|
||||
"ApplicationUrlHelpText": "Az alkalmazás külső URL-címe, beleértve a \"http(s)://\"-t, a \"Portot\" és az \"URL-alapot\" is",
|
||||
"ApplicationURL": "Alkalmazás URL-je"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"IndexerStatusCheckAllClientMessage": "Todos os indexadores estão indisponíveis devido a falhas",
|
||||
"IndexersSettingsSummary": "Indexadores e restrições de lançamento",
|
||||
"IndexerSettings": "Configurações do indexador",
|
||||
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa ativada, o Radarr não fornecerá nenhum resultado de pesquisa interativo",
|
||||
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa Interativa habilitada, Radarr não irá prover quaisquer resultados para pesquisa interativa",
|
||||
"IndexerSearchCheckNoAvailableIndexersMessage": "Todos os indexadores com capacidade de pesquisa estão temporariamente indisponíveis devido a erros recentes do indexador",
|
||||
"IndexerSearchCheckNoAutomaticMessage": "Nenhum indexador disponível com a Pesquisa automática habilitada, o Radarr não fornecerá nenhum resultado de pesquisa automática",
|
||||
"Indexers": "Indexadores",
|
||||
@@ -928,8 +928,8 @@
|
||||
"OnImport": "Ao importar",
|
||||
"OnHealthIssueHelpText": "Ao ter problema de integridade",
|
||||
"OnHealthIssue": "Em Problema de Saúde",
|
||||
"OnGrabHelpText": "Ao obter",
|
||||
"OnGrab": "Ao Baixar",
|
||||
"OnGrabHelpText": "Em Espera",
|
||||
"OnGrab": "Em Espera",
|
||||
"OnDownloadHelpText": "Ao importar",
|
||||
"Ok": "Ok",
|
||||
"OAuthPopupMessage": "Os pop-ups estão bloqueados em seu navegador",
|
||||
@@ -1117,8 +1117,8 @@
|
||||
"InstanceName": "Nome da instância",
|
||||
"InstanceNameHelpText": "Nome da instância na guia e para o nome do aplicativo Syslog",
|
||||
"AllCollectionsHiddenDueToFilter": "Todos os filmes estão ocultos devido ao filtro aplicado.",
|
||||
"Collections": "Coleção",
|
||||
"MonitorMovies": "Monitorar filme",
|
||||
"Collections": "Coleções",
|
||||
"MonitorMovies": "Monitorar Filmes",
|
||||
"NoCollections": "Nenhum filme encontrado. Para começar, adicione um novo filme ou importe alguns existentes",
|
||||
"MovieOnly": "Somente Filme",
|
||||
"UnableToLoadCollections": "Não foi possível carregar as coleções",
|
||||
@@ -1143,5 +1143,7 @@
|
||||
"CollectionShowOverviewsHelpText": "Mostrar visão geral da coleção",
|
||||
"CollectionShowPostersHelpText": "Mostrar pôsteres de itens da coleção",
|
||||
"RottenTomatoesRating": "Avaliação Tomato",
|
||||
"TotalMovies": "Total de Filmes"
|
||||
"TotalMovies": "Total de Filmes",
|
||||
"ApplicationURL": "URL da Aplicação",
|
||||
"ApplicationUrlHelpText": "O URL externa deste aplicativo, incluindo http(s)://, porta e base de URL"
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace NzbDrone.Core.Parser
|
||||
new IsoLanguage("fa", "", "fas", "Persian", Language.Persian),
|
||||
new IsoLanguage("be", "", "ben", "Bengali", Language.Bengali),
|
||||
new IsoLanguage("lt", "", "lit", "Lithuanian", Language.Lithuanian),
|
||||
new IsoLanguage("sk", "", "slk", "Slovak", Language.Slovak)
|
||||
new IsoLanguage("sk", "", "slk", "Slovak", Language.Slovak),
|
||||
new IsoLanguage("lv", "", "lav", "Latvian", Language.Latvian)
|
||||
};
|
||||
|
||||
public static IsoLanguage Find(string isoCode)
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
@@ -26,7 +27,8 @@ namespace NzbDrone.Core.Parser
|
||||
(?<hebrew>\bHebDub\b)|
|
||||
(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|
|
||||
(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|
|
||||
(?<ukrainian>(?:(?:\dx)?UKR))",
|
||||
(?<ukrainian>(?:(?:\dx)?UKR))|
|
||||
(?<latvian>\bLV\b)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static readonly Regex CaseSensitiveLanguageRegex = new Regex(@"(?:(?i)(?<!SUB[\W|_|^]))(?:(?<lithuanian>\bLT\b)|
|
||||
@@ -36,7 +38,7 @@ namespace NzbDrone.Core.Parser
|
||||
(?<slovak>\bSK\b)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?<iso_code>[a-z]{2,3})(?:[-_. ]forced)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?<iso_code>[a-z]{2,3})([-_. ](?<tags>full|forced|foreign|default|cc|psdh|sdh))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static List<Language> ParseLanguages(string title)
|
||||
{
|
||||
@@ -188,6 +190,11 @@ namespace NzbDrone.Core.Parser
|
||||
languages.Add(Language.Slovak);
|
||||
}
|
||||
|
||||
if (lowerTitle.Contains("latvian"))
|
||||
{
|
||||
languages.Add(Language.Latvian);
|
||||
}
|
||||
|
||||
// Case sensitive
|
||||
var caseSensitiveMatch = CaseSensitiveLanguageRegex.Match(title);
|
||||
|
||||
@@ -294,6 +301,11 @@ namespace NzbDrone.Core.Parser
|
||||
{
|
||||
languages.Add(Language.Ukrainian);
|
||||
}
|
||||
|
||||
if (match.Groups["latvian"].Success)
|
||||
{
|
||||
languages.Add(Language.Latvian);
|
||||
}
|
||||
}
|
||||
|
||||
if (!languages.Any())
|
||||
@@ -304,6 +316,25 @@ namespace NzbDrone.Core.Parser
|
||||
return languages.DistinctBy(l => (int)l).ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ParseLanguageTags(string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var simpleFilename = Path.GetFileNameWithoutExtension(fileName);
|
||||
var match = SubtitleLanguageRegex.Match(simpleFilename);
|
||||
var languageTags = match.Groups["tags"].Captures.Cast<Capture>()
|
||||
.Where(tag => !tag.Value.Empty())
|
||||
.Select(tag => tag.Value.ToLower());
|
||||
return languageTags;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug(ex, "Failed parsing language tags from subtitle file: {0}", fileName);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
public static Language ParseSubtitleLanguage(string fileName)
|
||||
{
|
||||
try
|
||||
@@ -316,11 +347,19 @@ namespace NzbDrone.Core.Parser
|
||||
if (languageMatch.Success)
|
||||
{
|
||||
var isoCode = languageMatch.Groups["iso_code"].Value;
|
||||
var isoLanguage = IsoLanguages.Find(isoCode);
|
||||
var isoLanguage = IsoLanguages.Find(isoCode.ToLower());
|
||||
|
||||
return isoLanguage?.Language ?? Language.Unknown;
|
||||
}
|
||||
|
||||
foreach (Language language in Language.All)
|
||||
{
|
||||
if (simpleFilename.EndsWith(language.ToString(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return language;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Debug("Unable to parse langauge from subtitle file: {0}", fileName);
|
||||
}
|
||||
catch (Exception)
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
private static readonly Regex SimpleReleaseTitleRegex = new Regex(@"\s*(?:[<>?*:|])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly RegexReplace WebsitePrefixRegex = new RegexReplace(@"^\[\s*[-a-z]+(\.[a-z]+)+\s*\][- ]*|^www\.[a-z]+\.(?:com|net|org)[ -]*",
|
||||
private static readonly RegexReplace WebsitePrefixRegex = new RegexReplace(@"^\[\s*[-a-z]+(\.[a-z]+)+\s*\][- ]*|^www\.[a-z0-9]+\.(?:com|net|org|casa|pics|tc)[ -]*",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Radarr.Api.V3.Config
|
||||
public string SslCertPassword { get; set; }
|
||||
public string UrlBase { get; set; }
|
||||
public string InstanceName { get; set; }
|
||||
public string ApplicationUrl { get; set; }
|
||||
public bool UpdateAutomatically { get; set; }
|
||||
public UpdateMechanism UpdateMechanism { get; set; }
|
||||
public string UpdateScriptPath { get; set; }
|
||||
@@ -82,7 +83,8 @@ namespace Radarr.Api.V3.Config
|
||||
CertificateValidation = configService.CertificateValidation,
|
||||
BackupFolder = configService.BackupFolder,
|
||||
BackupInterval = configService.BackupInterval,
|
||||
BackupRetention = configService.BackupRetention
|
||||
BackupRetention = configService.BackupRetention,
|
||||
ApplicationUrl = configService.ApplicationUrl
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9594,6 +9594,10 @@
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"applicationUrl": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"updateAutomatically": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user