mirror of
https://github.com/Radarr/Radarr
synced 2026-05-09 11:10:23 +02:00
Merge branch 'develop' into AddMetadataURL
This commit is contained in:
commit
abf738ffee
49 changed files with 1427 additions and 309 deletions
|
|
@ -11,3 +11,4 @@ RUN curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz && tar xvf go*.
|
|||
ENV GOPATH=$HOME/work
|
||||
ENV PATH="${PATH}:/usr/local/go/bin:$GOPATH/bin"
|
||||
RUN go get github.com/aktau/github-release
|
||||
RUN npm install -g yarn
|
||||
|
|
@ -2,7 +2,7 @@ version: 2
|
|||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: gallileo/radarr-cci-primary:5.8.7
|
||||
- image: gallileo/radarr-cci-primary:5.8.8
|
||||
environment:
|
||||
BUILD_VERSION: 0.2.0
|
||||
|
||||
|
|
@ -62,6 +62,7 @@ jobs:
|
|||
- _tests
|
||||
- setup
|
||||
- .circleci
|
||||
- deploy.sh
|
||||
unit_tests:
|
||||
<<: *defaults
|
||||
steps:
|
||||
|
|
@ -105,10 +106,10 @@ jobs:
|
|||
zip -r _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.windows.zip _packages/Radarr
|
||||
rm -rf _packages/Radarr
|
||||
cp -r _output_mono/ _packages/Radarr
|
||||
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz _packages/Radarr
|
||||
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz -C _packages Radarr
|
||||
rm -rf _packages/Radarr
|
||||
cp -r _output_osx/ _packages/Radarr
|
||||
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx.tar.gz _packages/Radarr
|
||||
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx.tar.gz -C _packages Radarr
|
||||
rm -rf _packages/Radarr
|
||||
cd _output_osx_app/
|
||||
zip -r ../_packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx-app.zip *
|
||||
|
|
@ -118,6 +119,9 @@ jobs:
|
|||
- store_artifacts:
|
||||
path: _packages
|
||||
destination: artifacts
|
||||
- run:
|
||||
name: "Deploying"
|
||||
command: chmod +x deploy.sh && ./deploy.sh
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
# Must be relative path from root
|
||||
|
|
@ -150,16 +154,16 @@ workflows:
|
|||
- unit_tests:
|
||||
requires:
|
||||
- build
|
||||
- integration_tests:
|
||||
requires:
|
||||
- build
|
||||
#- integration_tests:
|
||||
# requires:
|
||||
# - build
|
||||
- publish_artifacts:
|
||||
requires:
|
||||
- build
|
||||
- request_deploy:
|
||||
type: approval
|
||||
requires:
|
||||
- publish_artifacts
|
||||
- deploy:
|
||||
requires:
|
||||
- request_deploy
|
||||
#- request_deploy:
|
||||
# type: approval
|
||||
# requires:
|
||||
# - publish_artifacts
|
||||
#- deploy:
|
||||
# requires:
|
||||
# - request_deploy
|
||||
|
|
|
|||
|
|
@ -82,13 +82,13 @@ ignore_regexps = [
|
|||
## whenever you are tweaking this variable.
|
||||
##
|
||||
section_regexps = [
|
||||
('**New features**', [
|
||||
('**New features:**', [
|
||||
r'^[aA]dded?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||
r'^[uU]pdated?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||
r'^[cC]hanged?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||
r'^[nN]ew?\s*:?\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||
]),
|
||||
('**Fixes**', [
|
||||
('**Fixes:**', [
|
||||
r'^(?![mM]erge\s*)'
|
||||
]
|
||||
),
|
||||
|
|
@ -151,7 +151,7 @@ subject_process = (strip |
|
|||
ReSub(r'^([cC]hang(ed?)?)(\s?:?\s)(.*)$', r'\4') |
|
||||
ReSub(r'^([fF]ix(ed?)?)(\s?:?\s)(.*)$', r'\4') |
|
||||
ReSub(r'^([uU]pdat(ed?)?)(\s?:?\s)(.*)$', r'\4') |
|
||||
ReSub(r'#(\d{3,4})', r'<a href="https://github.com/Radarr/Radarr/issues/\1">\1</a>') |
|
||||
ReSub(r'#(\d{3,4})', r'Issue #\1') |
|
||||
SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
||||
|
||||
|
||||
|
|
|
|||
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -3,6 +3,16 @@
|
|||
## (unreleased)
|
||||
|
||||
### **New features**
|
||||
-  64bit mediainfo.dll to 32bit to resolve issue: https://github.com/Radarr/Radarr/issues/3138. [<a href="https://github.com/geogolem">geogolem</a>]
|
||||
-  Refactor MediaInfo tokens (fixes old tokens adds new stuff) ([#3058](https://github.com/Radarr/Radarr/issues/3058)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Don't hide custom formats behind advanced settings when editing quality. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Upped rate at which we scan the download client. Should reduce cpu and ram usage as well as decrease pressure on download clients. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Improve model and UI handling for lists. Should finally fix root folder errors. ([#3133](https://github.com/Radarr/Radarr/issues/3133)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Don't return unmapped folders on rootfolder API call. Massively improves loading time. ([#3116](https://github.com/Radarr/Radarr/issues/3116)) [<a href="https://github.com/Justin Kromlinger">Justin Kromlinger</a>]
|
||||
-  Support for Homebrew-installed mono ([#3090](https://github.com/Radarr/Radarr/issues/3090)) [<a href="https://github.com/Jeff Byrnes">Jeff Byrnes</a>]
|
||||
-  mk3d file format ([#2795](https://github.com/Radarr/Radarr/issues/2795)) [<a href="https://github.com/Qstick">Qstick</a>]
|
||||
-  "Add Paused" option to Deluge and Transmission ([#3038](https://github.com/Radarr/Radarr/issues/3038)) [<a href="https://github.com/cookandy">cookandy</a>]
|
||||
-  All-around small improvements ([#3032](https://github.com/Radarr/Radarr/issues/3032)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Czech Language ([#2948](https://github.com/Radarr/Radarr/issues/2948)) [<a href="https://github.com/halali">halali</a>]
|
||||
-  Fallback to Bitrate_Nominal for MediaInfo ([#2886](https://github.com/Radarr/Radarr/issues/2886)) [<a href="https://github.com/Qstick">Qstick</a>]
|
||||
-  All new custom formats 9000! (Rescan old files, delete formats, polish UI, etc. See discord for full changes): [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
|
|
@ -13,6 +23,21 @@
|
|||
-  "importing an episode" to "importing a movie file" ([#2829](https://github.com/Radarr/Radarr/issues/2829)) [<a href="https://github.com/Travis Boss">Travis Boss</a>]
|
||||
|
||||
### **Fixes**
|
||||
-  Fallback to 'VideoCodec' if 'VideoFormat' is unavailable ([#3142](https://github.com/Radarr/Radarr/issues/3142)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Read video 'BitRate_Nominal' if 'BitRate' is empty ([#3144](https://github.com/Radarr/Radarr/issues/3144)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  UpdateMovieQualityService Tests. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Ignore "special drives" from System » Disk Space ([#3050](https://github.com/Radarr/Radarr/issues/3050)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Tweak style of movie path template on "add movies" screen ([#3108](https://github.com/Radarr/Radarr/issues/3108)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Unable to update custom formats for releases with bad Source Titles. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Do not search movie if unmonitored ([#3131](https://github.com/Radarr/Radarr/issues/3131)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Quality badges not being shown on bulk import. ([#3121](https://github.com/Radarr/Radarr/issues/3121)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Trim filename from Kodi movie path before sending library scan request. ([#3097](https://github.com/Radarr/Radarr/issues/3097)) [<a href="https://github.com/Lawrence">Lawrence</a>]
|
||||
-  Hopefully fixed bulk import not showing files. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  MPEG-2 remuxes being detected as "Raw-HD" quality. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Allow directory to be parsed similar to past implementation ([#3057](https://github.com/Radarr/Radarr/issues/3057)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Class names on the 'add movies screen' ([#3047](https://github.com/Radarr/Radarr/issues/3047)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Use proper cursor for text and linked labels ([#3041](https://github.com/Radarr/Radarr/issues/3041)) [<a href="https://github.com/Ricardo Amaral">Ricardo Amaral</a>]
|
||||
-  Donate button requiring two clicks to actually work. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Templates for custom format using wrong modifiers. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  Profiles always failing validation. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
-  ImdbIds not being padded with zeroes, which messes up matching. [<a href="https://github.com/Leonardo Galli">Leonardo Galli</a>]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
{{#sections}}
|
||||
{{{label}}}
|
||||
{{#commits}}
|
||||
- {{{subject}}} [<a href="https://github.com/{{{author}}}">{{{author}}}</a>]
|
||||
- {{{subject}}} [{{{author}}}]
|
||||
{{/commits}}
|
||||
|
||||
{{/sections}}
|
||||
|
|
|
|||
7
deploy.sh
Normal file
7
deploy.sh
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
|
||||
echo "We are building a normal branch, deploying as such..."
|
||||
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=branch&name=${CIRCLE_BRANCH}"
|
||||
else
|
||||
echo "We are building a pr, deploying as such..."
|
||||
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=pr&name=${CIRCLE_PR_NUMBER}"
|
||||
fi
|
||||
|
|
@ -9,7 +9,11 @@ APPNAME="Radarr"
|
|||
|
||||
#set up environment
|
||||
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||
# Macports and mono-supplied installer path
|
||||
export PATH="/opt/local/bin:$PATH"
|
||||
elif [[ -x '/usr/local/bin/mono' ]]; then
|
||||
# Homebrew-supplied path to mono
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR"
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -14,6 +14,7 @@
|
|||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Profiles;
|
||||
|
||||
namespace NzbDrone.Api.Movies
|
||||
{
|
||||
|
|
@ -34,12 +35,13 @@ public class MovieBulkImportModule : NzbDroneRestModule<MovieResource>
|
|||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly ICached<Core.Movies.Movie> _mappedMovies;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IMovieService _movieService;
|
||||
|
||||
public MovieBulkImportModule(ISearchForNewMovie searchProxy, IRootFolderService rootFolderService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IDiskScanService diskScanService, ICacheManager cacheManager,
|
||||
IParsingService parsingService, IMovieService movieService)
|
||||
IParsingService parsingService, IProfileService profileService, IMovieService movieService)
|
||||
: base("/movies/bulkimport")
|
||||
{
|
||||
_searchProxy = searchProxy;
|
||||
|
|
@ -48,6 +50,7 @@ public MovieBulkImportModule(ISearchForNewMovie searchProxy, IRootFolderService
|
|||
_diskScanService = diskScanService;
|
||||
_mappedMovies = cacheManager.GetCache<Core.Movies.Movie>(GetType(), "mappedMoviesCache");
|
||||
_movieService = movieService;
|
||||
_profileService = profileService;
|
||||
_parsingService = parsingService;
|
||||
Get["/"] = x => Search();
|
||||
}
|
||||
|
|
@ -60,6 +63,8 @@ private Response Search()
|
|||
//Todo error handling
|
||||
}
|
||||
|
||||
Profile tempProfile = _profileService.All().First();
|
||||
|
||||
RootFolder rootFolder = _rootFolderService.Get(Request.Query.Id);
|
||||
|
||||
int page = Request.Query.page;
|
||||
|
|
@ -100,6 +105,7 @@ private Response Search()
|
|||
{
|
||||
Title = f.Name.Replace(".", " ").Replace("-", " "),
|
||||
Path = f.Path,
|
||||
Profile = tempProfile
|
||||
};
|
||||
}
|
||||
else
|
||||
|
|
@ -111,7 +117,8 @@ private Response Search()
|
|||
Title = parsedTitle.MovieTitle,
|
||||
Year = parsedTitle.Year,
|
||||
ImdbId = parsedTitle.ImdbId,
|
||||
Path = f.Path
|
||||
Path = f.Path,
|
||||
Profile = tempProfile
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ private int CreateRootFolder(RootFolderResource rootFolderResource)
|
|||
|
||||
private List<RootFolderResource> GetRootFolders()
|
||||
{
|
||||
return _rootFolderService.AllWithUnmappedFolders().ToResource();
|
||||
return _rootFolderService.AllWithSpace().ToResource();
|
||||
}
|
||||
|
||||
private void DeleteFolder(int id)
|
||||
|
|
|
|||
|
|
@ -78,6 +78,21 @@ public static bool IsNotNullOrWhiteSpace(this string text)
|
|||
return !string.IsNullOrWhiteSpace(text);
|
||||
}
|
||||
|
||||
public static bool StartsWithIgnoreCase(this string text, string startsWith)
|
||||
{
|
||||
return text.StartsWith(startsWith, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool EndsWithIgnoreCase(this string text, string startsWith)
|
||||
{
|
||||
return text.EndsWith(startsWith, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool EqualsIgnoreCase(this string text, string equals)
|
||||
{
|
||||
return text.Equals(equals, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool ContainsIgnoreCase(this string text, string contains)
|
||||
{
|
||||
return text.IndexOf(contains, StringComparison.InvariantCultureIgnoreCase) > -1;
|
||||
|
|
@ -118,4 +133,4 @@ public static string FromOctalString(this string octalValue)
|
|||
return Encoding.ASCII.GetString(new [] { byteResult });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
160
src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs
Normal file
160
src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DiskSpace;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.DiskSpace
|
||||
{
|
||||
[TestFixture]
|
||||
public class DiskSpaceServiceFixture : CoreTest<DiskSpaceService>
|
||||
{
|
||||
private string _moviesFolder;
|
||||
private string _moviesFolder2;
|
||||
private string _droneFactoryFolder;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_moviesFolder = @"G:\fasdlfsdf\movies".AsOsAgnostic();
|
||||
_moviesFolder2 = @"G:\fasdlfsdf\movies2".AsOsAgnostic();
|
||||
_droneFactoryFolder = @"G:\dronefactory".AsOsAgnostic();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetMounts())
|
||||
.Returns(new List<IMount>());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetPathRoot(It.IsAny<string>()))
|
||||
.Returns(@"G:\".AsOsAgnostic());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetAvailableSpace(It.IsAny<string>()))
|
||||
.Returns(0);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetTotalSize(It.IsAny<string>()))
|
||||
.Returns(0);
|
||||
|
||||
GivenMovies();
|
||||
}
|
||||
|
||||
private void GivenMovies(params Movie[] movies)
|
||||
{
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Setup(v => v.GetAllMovies())
|
||||
.Returns(movies.ToList());
|
||||
}
|
||||
|
||||
private void GivenExistingFolder(string folder)
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FolderExists(folder))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_check_diskspace_for_movies_folders()
|
||||
{
|
||||
GivenMovies(new Movie { Path = _moviesFolder });
|
||||
|
||||
GivenExistingFolder(_moviesFolder);
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_check_diskspace_for_same_root_folder_only_once()
|
||||
{
|
||||
GivenMovies(new Movie { Path = _moviesFolder }, new Movie { Path = _moviesFolder2 });
|
||||
|
||||
GivenExistingFolder(_moviesFolder);
|
||||
GivenExistingFolder(_moviesFolder2);
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().HaveCount(1);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetAvailableSpace(It.IsAny<string>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Unknown failure")]
|
||||
public void should_not_check_diskspace_for_missing_movies_folders()
|
||||
{
|
||||
GivenMovies(new Movie { Path = _moviesFolder });
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetAvailableSpace(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_check_diskspace_for_dronefactory_folder()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(v => v.DownloadedMoviesFolder)
|
||||
.Returns(_droneFactoryFolder);
|
||||
|
||||
GivenExistingFolder(_droneFactoryFolder);
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Unknown failure")]
|
||||
public void should_not_check_diskspace_for_missing_dronefactory_folder()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(v => v.DownloadedMoviesFolder)
|
||||
.Returns(_droneFactoryFolder);
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetAvailableSpace(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[TestCase("/boot")]
|
||||
[TestCase("/var/lib/rancher")]
|
||||
[TestCase("/var/lib/rancher/volumes")]
|
||||
[TestCase("/var/lib/kubelet")]
|
||||
[TestCase("/var/lib/docker")]
|
||||
[TestCase("/some/place/docker/aufs")]
|
||||
[TestCase("/etc/network")]
|
||||
[TestCase("/snap/filebot/9")]
|
||||
[TestCase("/snap/core/5145")]
|
||||
public void should_not_check_diskspace_for_irrelevant_mounts(string path)
|
||||
{
|
||||
var mount = new Mock<IMount>();
|
||||
mount.SetupGet(v => v.RootDirectory).Returns(path);
|
||||
mount.SetupGet(v => v.DriveType).Returns(System.IO.DriveType.Fixed);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetMounts())
|
||||
.Returns(new List<IMount> { mount.Object });
|
||||
|
||||
var freeSpace = Subject.GetFreeSpace();
|
||||
|
||||
freeSpace.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FormatAudioChannelsFixture : TestBase
|
||||
{
|
||||
[Test]
|
||||
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 6,
|
||||
AudioChannelPositions = null,
|
||||
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_AudioChannels_as_total_channels_if_LFE_not_in_AudioChannelPositionsText()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = null,
|
||||
AudioChannelPositionsText = "Front: L R"
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_0_if_schema_revision_is_less_than_3_and_other_properties_are_null()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = null,
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 2
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_AudioChannels_if_schema_revision_is_3_and_other_properties_are_null()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = null,
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_sum_AudioChannelPositions()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = "2/0/0",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_sum_AudioChannelPositions_including_decimal()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = "3/2/0.1",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_cleanup_extraneous_text_from_AudioChannelPositions()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = "Object Based / 3/2/2.1",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_empty_groups_in_AudioChannelPositions()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = " / 2/0/0.0",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_sum_first_series_of_numbers_from_AudioChannelPositions()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = "3/2/2.1 / 3/2/2.1",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_sum_dual_mono_representation_AudioChannelPositions()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = "1+1",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2.0m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_AudioChannelPositionText_when_AudioChannelChannelPosition_is_invalid()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 6,
|
||||
AudioChannelPositions = "15 objects",
|
||||
AudioChannelPositionsText = "15 objects / Front: L C R, Side: L R, LFE",
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_atmos_objects_from_AudioChannelPostions()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = "15 objects / 3/2.1",
|
||||
AudioChannelPositionsText = null,
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FormatAudioCodecFixture : TestBase
|
||||
{
|
||||
private static string sceneName = "My.Series.S01E01-Sonarr";
|
||||
|
||||
[TestCase("AC-3", "AC3")]
|
||||
[TestCase("E-AC-3", "EAC3")]
|
||||
[TestCase("MPEG Audio", "MPEG Audio")]
|
||||
[TestCase("DTS", "DTS")]
|
||||
public void should_format_audio_format_legacy(string audioFormat, string expectedFormat)
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioFormat = audioFormat
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[TestCase("MPEG Audio, A_MPEG/L2, , ", "droned.s01e03.swedish.720p.hdtv.x264-prince", "MP2")]
|
||||
[TestCase("Vorbis, A_VORBIS, , Xiph.Org libVorbis I 20101101 (Schaufenugget)", "DB Super HDTV", "Vorbis")]
|
||||
[TestCase("PCM, 1, , ", "DW DVDRip XviD-idTV", "PCM")] // Dubbed most likely
|
||||
[TestCase("TrueHD, A_TRUEHD, , ", "", "TrueHD")]
|
||||
[TestCase("WMA, 161, , ", "Droned.wmv", "WMA")]
|
||||
[TestCase("WMA, 162, Pro, ", "B.N.S04E18.720p.WEB-DL", "WMA")]
|
||||
[TestCase("Opus, A_OPUS, , ", "Roadkill Ep3x11 - YouTube.webm", "Opus")]
|
||||
[TestCase("mp3 , 0, , ", "climbing.mp4", "MP3")]
|
||||
public void should_format_audio_format(string audioFormatPack, string sceneName, string expectedFormat)
|
||||
{
|
||||
var split = audioFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioFormat = split[0],
|
||||
AudioCodecID = split[1],
|
||||
AudioProfile = split[2],
|
||||
AudioCodecLibrary = split[3]
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_MP3_for_MPEG_Audio_with_Layer_3_for_the_profile()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioFormat = "MPEG Audio",
|
||||
AudioProfile = "Layer 3"
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be("MP3");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_AudioFormat_by_default()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
AudioFormat = "Other Audio Format",
|
||||
AudioCodecID = "Other Audio Codec"
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(mediaInfoModel.AudioFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FormatVideoCodecFixture : TestBase
|
||||
{
|
||||
[TestCase("AVC", null, "x264")]
|
||||
[TestCase("AVC", "source.title.x264.720p-Sonarr", "x264")]
|
||||
[TestCase("AVC", "source.title.h264.720p-Sonarr", "h264")]
|
||||
[TestCase("V_MPEGH/ISO/HEVC", null, "x265")]
|
||||
[TestCase("V_MPEGH/ISO/HEVC", "source.title.x265.720p-Sonarr", "x265")]
|
||||
[TestCase("V_MPEGH/ISO/HEVC", "source.title.h265.720p-Sonarr", "h265")]
|
||||
[TestCase("MPEG-2 Video", null, "MPEG2")]
|
||||
public void should_format_video_codec_with_source_title_legacy(string videoCodec, string sceneName, string expectedFormat)
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoCodec = videoCodec
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[TestCase("MPEG Video, 2, Main@High, ", "Droned.S01E02.1080i.HDTV.DD5.1.MPEG2-NTb", "MPEG2")]
|
||||
[TestCase("MPEG Video, V_MPEG2, Main@High, ", "", "MPEG2")]
|
||||
[TestCase("MPEG Video, , , ", "The.Simpsons.S13E04.INTERNAL-ANiVCD.mpg", "MPEG")]
|
||||
[TestCase("VC-1, WVC1, Advanced@L4, ", "B.N.S04E18.720p.WEB-DL", "VC1")]
|
||||
[TestCase("VC-1, V_MS/VFW/FOURCC / WVC1, Advanced@L3, ", "", "VC1")]
|
||||
[TestCase("VC-1, WMV3, MP@LL, ", "It's Always Sunny S07E13 The Gang's RevengeHDTV.XviD-2HD.avi", "VC1")]
|
||||
[TestCase("V.MPEG4/ISO/AVC, V.MPEG4/ISO/AVC, , ", "pd.2015.S03E08.720p.iP.WEBRip.AAC2.0.H264-BTW", "h264")]
|
||||
[TestCase("WMV2, WMV2, , ", "Droned.wmv", "WMV")]
|
||||
[TestCase("xvid, xvid, , ", "", "XviD")]
|
||||
[TestCase("div3, div3, , ", "spsm.dvdrip.divx.avi'.", "DivX")]
|
||||
[TestCase("VP6, 4, , ", "Top Gear - S12E01 - Lorries - SD TV.flv", "VP6")]
|
||||
[TestCase("VP7, VP70, General, ", "Sweet Seymour.avi", "VP7")]
|
||||
[TestCase("VP8, V_VP8, , ", "Dick.mkv", "VP8")]
|
||||
[TestCase("VP9, V_VP9, , ", "Roadkill Ep3x11 - YouTube.webm", "VP9")]
|
||||
[TestCase("x264, x264, , ", "Ghost Advent - S04E05 - Stanley Hotel SDTV.avi", "x264")]
|
||||
[TestCase("V_MPEGH/ISO/HEVC, V_MPEGH/ISO/HEVC, , ", "The BBT S11E12 The Matrimonial Metric 1080p 10bit AMZN WEB-DL", "h265")]
|
||||
[TestCase("MPEG-4 Visual, 20, Simple@L1, Lavc52.29.0", "Will.And.Grace.S08E14.WS.DVDrip.XviD.I.Love.L.Gay-Obfuscated", "XviD")]
|
||||
[TestCase("MPEG-4 Visual, 20, Advanced Simple@L5, XviD0046", "", "XviD")]
|
||||
[TestCase("mp4v, mp4v, , ", "American.Chopper.S06E07.Mountain.Creek.Bike.DSR.XviD-KRS", "XviD")]
|
||||
public void should_format_video_format(string videoFormatPack, string sceneName, string expectedFormat)
|
||||
{
|
||||
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoFormat = split[0],
|
||||
VideoCodecID = split[1],
|
||||
VideoProfile = split[2],
|
||||
VideoCodecLibrary = split[3]
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[TestCase("AVC, AVC, , x264", "Some.Video.S01E01.h264", "x264")] // Force mediainfo tag
|
||||
[TestCase("HEVC, HEVC, , x265", "Some.Video.S01E01.h265", "x265")] // Force mediainfo tag
|
||||
[TestCase("AVC, AVC, , ", "Some.Video.S01E01.x264", "x264")] // Not seen in practice, but honor tag if otherwise unknown
|
||||
[TestCase("HEVC, HEVC, , ", "Some.Video.S01E01.x265", "x265")] // Not seen in practice, but honor tag if otherwise unknown
|
||||
[TestCase("AVC, AVC, , ", "Some.Video.S01E01", "h264")] // Default value
|
||||
[TestCase("HEVC, HEVC, , ", "Some.Video.S01E01", "h265")] // Default value
|
||||
public void should_format_video_format_fallbacks(string videoFormatPack, string sceneName, string expectedFormat)
|
||||
{
|
||||
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoFormat = split[0],
|
||||
VideoCodecID = split[1],
|
||||
VideoProfile = split[2],
|
||||
VideoCodecLibrary = split[3]
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_VideoFormat_by_default()
|
||||
{
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoFormat = "VideoCodec"
|
||||
};
|
||||
|
||||
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, null).Should().Be(mediaInfoModel.VideoFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,15 +16,15 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
|||
[TestFixture]
|
||||
public class UpdateMediaInfoServiceFixture : CoreTest<UpdateMediaInfoService>
|
||||
{
|
||||
private Movie _series;
|
||||
private Movie _movie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = new Movie
|
||||
_movie = new Movie
|
||||
{
|
||||
Id = 1,
|
||||
Path = @"C:\series".AsOsAgnostic()
|
||||
Path = @"C:\movie".AsOsAgnostic()
|
||||
};
|
||||
|
||||
Mocker.GetMock<IConfigService>()
|
||||
|
|
@ -60,7 +60,7 @@ public void should_skip_up_to_date_media_info()
|
|||
.All()
|
||||
.With(v => v.RelativePath = "media.mkv")
|
||||
.TheFirst(1)
|
||||
.With(v => v.MediaInfo = new MediaInfoModel { SchemaRevision = 3 })
|
||||
.With(v => v.MediaInfo = new MediaInfoModel { SchemaRevision = VideoFileInfoReader.CURRENT_MEDIA_INFO_SCHEMA_REVISION })
|
||||
.BuildList();
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
|
|
@ -70,10 +70,36 @@ public void should_skip_up_to_date_media_info()
|
|||
GivenFileExists();
|
||||
GivenSuccessfulScan();
|
||||
|
||||
Subject.Handle(new MovieScannedEvent(_series));
|
||||
Subject.Handle(new MovieScannedEvent(_movie));
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2));
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_movie.Path, "media.mkv")), Times.Exactly(2));
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Verify(v => v.Update(It.IsAny<MovieFile>()), Times.Exactly(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_not_yet_date_media_info()
|
||||
{
|
||||
var episodeFiles = Builder<MovieFile>.CreateListOfSize(3)
|
||||
.All()
|
||||
.With(v => v.RelativePath = "media.mkv")
|
||||
.TheFirst(1)
|
||||
.With(v => v.MediaInfo = new MediaInfoModel { SchemaRevision = VideoFileInfoReader.MINIMUM_MEDIA_INFO_SCHEMA_REVISION })
|
||||
.BuildList();
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(v => v.GetFilesByMovie(1))
|
||||
.Returns(episodeFiles);
|
||||
|
||||
GivenFileExists();
|
||||
GivenSuccessfulScan();
|
||||
|
||||
Subject.Handle(new MovieScannedEvent(_movie));
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_movie.Path, "media.mkv")), Times.Exactly(2));
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Verify(v => v.Update(It.IsAny<MovieFile>()), Times.Exactly(2));
|
||||
|
|
@ -96,10 +122,10 @@ public void should_update_outdated_media_info()
|
|||
GivenFileExists();
|
||||
GivenSuccessfulScan();
|
||||
|
||||
Subject.Handle(new MovieScannedEvent(_series));
|
||||
Subject.Handle(new MovieScannedEvent(_movie));
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(3));
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_movie.Path, "media.mkv")), Times.Exactly(3));
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Verify(v => v.Update(It.IsAny<MovieFile>()), Times.Exactly(3));
|
||||
|
|
@ -119,7 +145,7 @@ public void should_ignore_missing_files()
|
|||
|
||||
GivenSuccessfulScan();
|
||||
|
||||
Subject.Handle(new MovieScannedEvent(_series));
|
||||
Subject.Handle(new MovieScannedEvent(_movie));
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Verify(v => v.GetMediaInfo("media.mkv"), Times.Never());
|
||||
|
|
@ -144,12 +170,12 @@ public void should_continue_after_failure()
|
|||
|
||||
GivenFileExists();
|
||||
GivenSuccessfulScan();
|
||||
GivenFailedScan(Path.Combine(_series.Path, "media2.mkv"));
|
||||
GivenFailedScan(Path.Combine(_movie.Path, "media2.mkv"));
|
||||
|
||||
Subject.Handle(new MovieScannedEvent(_series));
|
||||
Subject.Handle(new MovieScannedEvent(_movie));
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(1));
|
||||
.Verify(v => v.GetMediaInfo(Path.Combine(_movie.Path, "media.mkv")), Times.Exactly(1));
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Verify(v => v.Update(It.IsAny<MovieFile>()), Times.Exactly(1));
|
||||
|
|
|
|||
|
|
@ -31,10 +31,8 @@ public void get_runtime()
|
|||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
|
||||
|
||||
Subject.GetRunTime(path).Seconds.Should().Be(10);
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void get_info()
|
||||
{
|
||||
|
|
@ -42,21 +40,27 @@ public void get_info()
|
|||
|
||||
var info = Subject.GetMediaInfo(path);
|
||||
|
||||
|
||||
info.VideoFormat.Should().Be("AVC");
|
||||
info.VideoCodecID.Should().Be("avc1");
|
||||
info.VideoProfile.Should().Be("Baseline@L2.1");
|
||||
info.VideoCodecLibrary.Should().Be("");
|
||||
info.VideoMultiViewCount.Should().Be(0);
|
||||
info.VideoColourPrimaries.Should().Be("BT.601 NTSC");
|
||||
info.VideoTransferCharacteristics.Should().Be("BT.709");
|
||||
info.AudioFormat.Should().Be("AAC");
|
||||
info.AudioCodecID.Should().BeOneOf("40", "mp4a-40-2");
|
||||
info.AudioCodecLibrary.Should().Be("");
|
||||
info.AudioBitrate.Should().Be(128000);
|
||||
info.AudioChannels.Should().Be(2);
|
||||
info.AudioFormat.Should().Be("AAC");
|
||||
info.AudioLanguages.Should().Be("English");
|
||||
info.AudioProfile.Should().Be("LC");
|
||||
info.AudioAdditionalFeatures.Should().Be("");
|
||||
info.Height.Should().Be(320);
|
||||
info.RunTime.Seconds.Should().Be(10);
|
||||
info.ScanType.Should().Be("Progressive");
|
||||
info.Subtitles.Should().Be("");
|
||||
info.VideoBitrate.Should().Be(193329);
|
||||
info.VideoCodec.Should().Be("AVC");
|
||||
info.VideoFps.Should().Be(24);
|
||||
info.Width.Should().Be(480);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -73,20 +77,27 @@ public void get_info_unicode()
|
|||
|
||||
var info = Subject.GetMediaInfo(path);
|
||||
|
||||
info.VideoFormat.Should().Be("AVC");
|
||||
info.VideoCodecID.Should().Be("avc1");
|
||||
info.VideoProfile.Should().Be("Baseline@L2.1");
|
||||
info.VideoCodecLibrary.Should().Be("");
|
||||
info.VideoMultiViewCount.Should().Be(0);
|
||||
info.VideoColourPrimaries.Should().Be("BT.601 NTSC");
|
||||
info.VideoTransferCharacteristics.Should().Be("BT.709");
|
||||
info.AudioFormat.Should().Be("AAC");
|
||||
info.AudioCodecID.Should().BeOneOf("40", "mp4a-40-2");
|
||||
info.AudioCodecLibrary.Should().Be("");
|
||||
info.AudioBitrate.Should().Be(128000);
|
||||
info.AudioChannels.Should().Be(2);
|
||||
info.AudioFormat.Should().Be("AAC");
|
||||
info.AudioLanguages.Should().Be("English");
|
||||
info.AudioProfile.Should().Be("LC");
|
||||
info.AudioAdditionalFeatures.Should().Be("");
|
||||
info.Height.Should().Be(320);
|
||||
info.RunTime.Seconds.Should().Be(10);
|
||||
info.ScanType.Should().Be("Progressive");
|
||||
info.Subtitles.Should().Be("");
|
||||
info.VideoBitrate.Should().Be(193329);
|
||||
info.VideoCodec.Should().Be("AVC");
|
||||
info.VideoFps.Should().Be(24);
|
||||
info.Width.Should().Be(480);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -100,23 +111,5 @@ public void should_dispose_file_after_scanning_mediainfo()
|
|||
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("/ Front: L R", 2.0)]
|
||||
public void should_correctly_read_audio_channels(string ChannelPositions, decimal formattedChannels)
|
||||
{
|
||||
var info = new MediaInfoModel()
|
||||
{
|
||||
VideoCodec = "AVC",
|
||||
AudioFormat = "DTS",
|
||||
AudioLanguages = "English",
|
||||
Subtitles = "English",
|
||||
AudioChannels = 2,
|
||||
AudioChannelPositions = ChannelPositions,
|
||||
SchemaRevision = 3,
|
||||
};
|
||||
|
||||
info.FormattedAudioChannels.Should().Be(formattedChannels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles
|
||||
{
|
||||
|
|
@ -58,6 +59,8 @@ public void should_not_update_if_unable_to_parse()
|
|||
{
|
||||
ExecuteCommand();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(s => s.Update(It.IsAny<MovieFile>()), Times.Never());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@
|
|||
<Compile Include="DecisionEngineTests\Search\MovieSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
|
||||
<Compile Include="DiskSpace\DiskSpaceServiceFixture.cs" />
|
||||
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
|
||||
<Compile Include="Download\DownloadApprovedReportsTests\DownloadApprovedFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\Blackhole\ScanWatchFolderFixture.cs" />
|
||||
|
|
@ -314,6 +315,9 @@
|
|||
<Compile Include="OrganizerTests\CleanFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileServiceTests\FilterFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileTableCleanupServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioChannelsFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatVideoCodecFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReaderFixture.cs" />
|
||||
<Compile Include="MediaFiles\RenameMovieFileServiceFixture.cs" />
|
||||
|
|
@ -580,4 +584,4 @@
|
|||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -260,14 +260,14 @@ public void should_format_mediainfo_properly()
|
|||
|
||||
_movieFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
||||
{
|
||||
VideoCodec = "AVC",
|
||||
VideoFormat = "AVC",
|
||||
AudioFormat = "DTS",
|
||||
AudioLanguages = "English/Spanish",
|
||||
Subtitles = "English/Spanish/Italian"
|
||||
};
|
||||
|
||||
Subject.BuildFileName(_movie, _movieFile)
|
||||
.Should().Be("South.Park.X264.DTS[EN+ES].[EN+ES+IT]");
|
||||
.Should().Be("South.Park.H264.DTS[EN+ES].[EN+ES+IT]");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -277,14 +277,52 @@ public void should_exclude_english_in_mediainfo_audio_language()
|
|||
|
||||
_movieFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
||||
{
|
||||
VideoCodec = "AVC",
|
||||
VideoFormat = "AVC",
|
||||
AudioFormat = "DTS",
|
||||
AudioLanguages = "English",
|
||||
Subtitles = "English/Spanish/Italian"
|
||||
};
|
||||
|
||||
Subject.BuildFileName(_movie, _movieFile)
|
||||
.Should().Be("South.Park.X264.DTS.[EN+ES+IT]");
|
||||
.Should().Be("South.Park.H264.DTS.[EN+ES+IT]");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_format_mediainfo_3d_properly()
|
||||
{
|
||||
_namingConfig.StandardMovieFormat = "{Movie.Title}.{MEDIAINFO.3D}.{MediaInfo.Simple}";
|
||||
|
||||
_movieFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
||||
{
|
||||
VideoFormat = "AVC",
|
||||
VideoMultiViewCount = 2,
|
||||
AudioFormat = "DTS",
|
||||
AudioLanguages = "English",
|
||||
Subtitles = "English/Spanish/Italian"
|
||||
};
|
||||
|
||||
Subject.BuildFileName(_movie, _movieFile)
|
||||
.Should().Be("South.Park.3D.h264.DTS");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_format_mediainfo_hdr_properly()
|
||||
{
|
||||
_namingConfig.StandardMovieFormat = "{Movie.Title}.{MEDIAINFO.HDR}.{MediaInfo.Simple}";
|
||||
|
||||
_movieFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
||||
{
|
||||
VideoFormat = "AVC",
|
||||
VideoBitDepth = 10,
|
||||
VideoColourPrimaries = "BT.2020",
|
||||
VideoTransferCharacteristics = "PQ",
|
||||
AudioFormat = "DTS",
|
||||
AudioLanguages = "English",
|
||||
Subtitles = "English/Spanish/Italian"
|
||||
};
|
||||
|
||||
Subject.BuildFileName(_movie, _movieFile)
|
||||
.Should().Be("South.Park.HDR.h264.DTS");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
|
|
@ -236,6 +236,8 @@ public void should_parse_bluray576p_quality(string title)
|
|||
[TestCase("Contract.to.Kill.2016.REMUX.1080p.BluRay.AVC.DTS-HD.MA.5.1-iFT")]
|
||||
[TestCase("27.Dresses.2008.REMUX.1080p.Bluray.AVC.DTS-HR.MA.5.1-LEGi0N")]
|
||||
[TestCase("27.Dresses.2008.BDREMUX.1080p.Bluray.AVC.DTS-HR.MA.5.1-LEGi0N")]
|
||||
[TestCase("The.Stoning.of.Soraya.M.2008.USA.BluRay.Remux.1080p.MPEG-2.DD.5.1-TDD")]
|
||||
[TestCase("Wildling.2018.1080p.BluRay.REMUX.MPEG-2.DTS-HD.MA.5.1-EPSiLON")]
|
||||
public void should_parse_remux1080p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080P, Modifier.REMUX);
|
||||
|
|
@ -344,7 +346,10 @@ public void should_parse_hardcoded_subs(string postTitle, string sub)
|
|||
private void ParseAndVerifyQuality(string title, Source source, bool proper, Resolution resolution, Modifier modifier = Modifier.NONE)
|
||||
{
|
||||
var result = QualityParser.ParseQuality(title);
|
||||
result.Resolution.Should().Be(resolution);
|
||||
if (resolution != Resolution.Unknown)
|
||||
{
|
||||
result.Resolution.Should().Be(resolution);
|
||||
}
|
||||
result.Source.Should().Be(source);
|
||||
if (modifier != Modifier.NONE)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
|
@ -22,6 +23,8 @@ public class DiskSpaceService : IDiskSpaceService
|
|||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc|snap)(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled);
|
||||
|
||||
public DiskSpaceService(IMovieService movieService, IConfigService configService, IDiskProvider diskProvider, Logger logger)
|
||||
{
|
||||
_movieService = movieService;
|
||||
|
|
@ -59,7 +62,10 @@ private IEnumerable<DiskSpace> GetDroneFactoryFreeSpace()
|
|||
|
||||
private IEnumerable<DiskSpace> GetFixedDisksFreeSpace()
|
||||
{
|
||||
return GetDiskSpace(_diskProvider.GetMounts().Where(d => d.DriveType == DriveType.Fixed).Select(d => d.RootDirectory), true);
|
||||
return GetDiskSpace(_diskProvider.GetMounts()
|
||||
.Where(d => d.DriveType == DriveType.Fixed)
|
||||
.Where(d => !_regexSpecialDrive.IsMatch(d.RootDirectory))
|
||||
.Select(d => d.RootDirectory), true);
|
||||
}
|
||||
|
||||
private IEnumerable<DiskSpace> GetDiskSpace(IEnumerable<string> paths, bool suppressWarnings = false)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
using NzbDrone.Core.Extras.Metadata.Files;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
||||
|
|
@ -32,7 +33,7 @@ public XbmcMetadata(IDetectXbmcNfo detectNfo,
|
|||
_mediaCoverService = mediaCoverService;
|
||||
_diskProvider = diskProvider;
|
||||
_detectNfo = detectNfo;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static readonly Regex MovieImagesRegex = new Regex(@"^(?<type>poster|banner|fanart|clearart|discart|landscape|logo|backdrop|clearlogo)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
|
@ -157,13 +158,15 @@ public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFil
|
|||
|
||||
if (movieFile.MediaInfo != null)
|
||||
{
|
||||
var sceneName = movieFile.GetSceneOrFileName();
|
||||
|
||||
var fileInfo = new XElement("fileinfo");
|
||||
var streamDetails = new XElement("streamdetails");
|
||||
|
||||
var video = new XElement("video");
|
||||
video.Add(new XElement("aspect", (float)movieFile.MediaInfo.Width / (float)movieFile.MediaInfo.Height));
|
||||
video.Add(new XElement("bitrate", movieFile.MediaInfo.VideoBitrate));
|
||||
video.Add(new XElement("codec", movieFile.MediaInfo.VideoCodec));
|
||||
video.Add(new XElement("codec", MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName)));
|
||||
video.Add(new XElement("framerate", movieFile.MediaInfo.VideoFps));
|
||||
video.Add(new XElement("height", movieFile.MediaInfo.Height));
|
||||
video.Add(new XElement("scantype", movieFile.MediaInfo.ScanType));
|
||||
|
|
@ -180,7 +183,7 @@ public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFil
|
|||
var audio = new XElement("audio");
|
||||
audio.Add(new XElement("bitrate", movieFile.MediaInfo.AudioBitrate));
|
||||
audio.Add(new XElement("channels", movieFile.MediaInfo.AudioChannels));
|
||||
audio.Add(new XElement("codec", GetAudioCodec(movieFile.MediaInfo.AudioFormat)));
|
||||
audio.Add(new XElement("codec", MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName)));
|
||||
audio.Add(new XElement("language", movieFile.MediaInfo.AudioLanguages));
|
||||
streamDetails.Add(audio);
|
||||
|
||||
|
|
@ -251,16 +254,6 @@ private string GetMovieMetadataFilename(string movieFilePath)
|
|||
}
|
||||
}
|
||||
|
||||
private string GetAudioCodec(string audioCodec)
|
||||
{
|
||||
if (audioCodec == "AC-3")
|
||||
{
|
||||
return "AC3";
|
||||
}
|
||||
|
||||
return audioCodec;
|
||||
}
|
||||
|
||||
private bool GetExistingWatchedStatus(Movie movie, string movieFilePath)
|
||||
{
|
||||
var fullPath = Path.Combine(movie.Path, GetMovieMetadataFilename(movieFilePath));
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public void Execute(MoviesSearchCommand message)
|
|||
if (!movies.Monitored)
|
||||
{
|
||||
_logger.Debug("Movie {0} is not monitored, skipping search", movies.Title);
|
||||
continue;
|
||||
}
|
||||
|
||||
var decisions = _nzbSearchService.MovieSearch(movieId, false);//_nzbSearchService.SeasonSearch(message.MovieId, season.SeasonNumber, false, message.Trigger == CommandTrigger.Manual);
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public void Handle(ApplicationStartedEvent message)
|
|||
|
||||
var defaultTasks = new[]
|
||||
{
|
||||
new ScheduledTask{ Interval = 0.25f, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
||||
new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
||||
new ScheduledTask{ Interval = 1*60, TypeName = typeof(PreDBSyncCommand).FullName},
|
||||
new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).FullName},
|
||||
new ScheduledTask{ Interval = updateInterval, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
||||
|
|
|
|||
470
src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
Normal file
470
src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||
{
|
||||
public static class MediaInfoFormatter
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(MediaInfoFormatter));
|
||||
|
||||
public static decimal FormatAudioChannels(MediaInfoModel mediaInfo)
|
||||
{
|
||||
var audioChannels = FormatAudioChannelsFromAudioChannelPositions(mediaInfo);
|
||||
|
||||
if (audioChannels == null)
|
||||
{
|
||||
audioChannels = FormatAudioChannelsFromAudioChannelPositionsText(mediaInfo);
|
||||
}
|
||||
|
||||
if (audioChannels == null)
|
||||
{
|
||||
audioChannels = FormatAudioChannelsFromAudioChannels(mediaInfo);
|
||||
}
|
||||
|
||||
return audioChannels ?? 0;
|
||||
}
|
||||
|
||||
public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName)
|
||||
{
|
||||
if (mediaInfo.AudioCodecID == null)
|
||||
{
|
||||
return FormatAudioCodecLegacy(mediaInfo, sceneName);
|
||||
}
|
||||
|
||||
var audioFormat = mediaInfo.AudioFormat;
|
||||
var audioCodecID = mediaInfo.AudioCodecID ?? string.Empty;
|
||||
var audioProfile = mediaInfo.AudioProfile ?? string.Empty;
|
||||
var audioAdditionalFeatures = mediaInfo.AudioAdditionalFeatures ?? string.Empty;
|
||||
var audioCodecLibrary = mediaInfo.AudioCodecLibrary ?? string.Empty;
|
||||
|
||||
if (audioFormat.IsNullOrWhiteSpace())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("AC-3"))
|
||||
{
|
||||
return "AC3";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("E-AC-3"))
|
||||
{
|
||||
return "EAC3";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("AAC"))
|
||||
{
|
||||
if (audioCodecID == "A_AAC/MPEG4/LC/SBR")
|
||||
{
|
||||
return "HE-AAC";
|
||||
}
|
||||
|
||||
return "AAC";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("DTS"))
|
||||
{
|
||||
if (audioAdditionalFeatures.StartsWithIgnoreCase("XLL"))
|
||||
{
|
||||
if (audioAdditionalFeatures.EndsWithIgnoreCase("X"))
|
||||
{
|
||||
return "DTS-X";
|
||||
}
|
||||
|
||||
return "DTS-HD MA";
|
||||
}
|
||||
|
||||
if (audioAdditionalFeatures.EqualsIgnoreCase("ES"))
|
||||
{
|
||||
return "DTS-ES";
|
||||
}
|
||||
|
||||
if (audioAdditionalFeatures.EqualsIgnoreCase("XBR"))
|
||||
{
|
||||
return "DTS-HD HRA";
|
||||
}
|
||||
|
||||
return "DTS";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("FLAC"))
|
||||
{
|
||||
return "FLAC";
|
||||
}
|
||||
|
||||
if (audioFormat.Trim().EqualsIgnoreCase("mp3"))
|
||||
{
|
||||
return "MP3";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("MPEG Audio"))
|
||||
{
|
||||
if (mediaInfo.AudioCodecID == "55" || mediaInfo.AudioCodecID == "A_MPEG/L3" || mediaInfo.AudioProfile == "Layer 3")
|
||||
{
|
||||
return "MP3";
|
||||
}
|
||||
|
||||
if (mediaInfo.AudioCodecID == "A_MPEG/L2" || mediaInfo.AudioProfile == "Layer 2")
|
||||
{
|
||||
return "MP2";
|
||||
}
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("Opus"))
|
||||
{
|
||||
return "Opus";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("PCM"))
|
||||
{
|
||||
return "PCM";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("MLP FBA"))
|
||||
{
|
||||
if (audioAdditionalFeatures == "16-ch")
|
||||
{
|
||||
return "TrueHD Atmos";
|
||||
}
|
||||
|
||||
return "TrueHD";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("Vorbis"))
|
||||
{
|
||||
return "Vorbis";
|
||||
}
|
||||
|
||||
if (audioFormat == "WMA")
|
||||
{
|
||||
return "WMA";
|
||||
}
|
||||
|
||||
Logger.Debug("Unknown audio format: '{0}' in '{1}'.", string.Join(", ", audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures, audioCodecLibrary), sceneName);
|
||||
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
public static string FormatAudioCodecLegacy(MediaInfoModel mediaInfo, string sceneName)
|
||||
{
|
||||
var audioFormat = mediaInfo.AudioFormat;
|
||||
|
||||
if (audioFormat.IsNullOrWhiteSpace())
|
||||
{
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("AC-3"))
|
||||
{
|
||||
return "AC3";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("E-AC-3"))
|
||||
{
|
||||
return "EAC3";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("AAC"))
|
||||
{
|
||||
return "AAC";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("MPEG Audio") && mediaInfo.AudioProfile == "Layer 3")
|
||||
{
|
||||
return "MP3";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("DTS"))
|
||||
{
|
||||
return "DTS";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("TrueHD"))
|
||||
{
|
||||
return "TrueHD";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("FLAC"))
|
||||
{
|
||||
return "FLAC";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("Vorbis"))
|
||||
{
|
||||
return "Vorbis";
|
||||
}
|
||||
|
||||
if (audioFormat.EqualsIgnoreCase("Opus"))
|
||||
{
|
||||
return "Opus";
|
||||
}
|
||||
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
public static string FormatVideoCodec(MediaInfoModel mediaInfo, string sceneName)
|
||||
{
|
||||
if (mediaInfo.VideoFormat == null)
|
||||
{
|
||||
return FormatVideoCodecLegacy(mediaInfo, sceneName);
|
||||
}
|
||||
|
||||
var videoFormat = mediaInfo.VideoFormat;
|
||||
var videoCodecID = mediaInfo.VideoCodecID ?? string.Empty;
|
||||
var videoProfile = mediaInfo.VideoProfile ?? string.Empty;
|
||||
var videoCodecLibrary = mediaInfo.VideoCodecLibrary ?? string.Empty;
|
||||
|
||||
var result = videoFormat;
|
||||
|
||||
if (videoFormat.IsNullOrWhiteSpace())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (videoFormat == "x264")
|
||||
{
|
||||
return "x264";
|
||||
}
|
||||
|
||||
if (videoFormat == "AVC" || videoFormat == "V.MPEG4/ISO/AVC")
|
||||
{
|
||||
if (videoCodecLibrary.StartsWithIgnoreCase("x264"))
|
||||
{
|
||||
return "x264";
|
||||
}
|
||||
|
||||
return GetSceneNameMatch(sceneName, "AVC", "x264", "h264");
|
||||
}
|
||||
|
||||
if (videoFormat == "HEVC" || videoFormat == "V_MPEGH/ISO/HEVC")
|
||||
{
|
||||
if (videoCodecLibrary.StartsWithIgnoreCase("x265"))
|
||||
{
|
||||
return "x265";
|
||||
}
|
||||
|
||||
return GetSceneNameMatch(sceneName, "HEVC", "x265", "h265");
|
||||
}
|
||||
|
||||
if (videoFormat == "MPEG Video")
|
||||
{
|
||||
if (videoCodecID == "2" || videoCodecID == "V_MPEG2")
|
||||
{
|
||||
return "MPEG2";
|
||||
}
|
||||
|
||||
if (videoCodecID.IsNullOrWhiteSpace())
|
||||
{
|
||||
return "MPEG";
|
||||
}
|
||||
}
|
||||
|
||||
if (videoFormat == "MPEG-2 Video")
|
||||
{
|
||||
return "MPEG2";
|
||||
}
|
||||
|
||||
if (videoFormat == "MPEG-4 Visual")
|
||||
{
|
||||
if (videoCodecID.ContainsIgnoreCase("XVID") ||
|
||||
videoCodecLibrary.StartsWithIgnoreCase("XviD"))
|
||||
{
|
||||
return "XviD";
|
||||
}
|
||||
|
||||
if (videoCodecID.ContainsIgnoreCase("DIV3") ||
|
||||
videoCodecID.ContainsIgnoreCase("DIVX") ||
|
||||
videoCodecID.ContainsIgnoreCase("DX50") ||
|
||||
videoCodecLibrary.StartsWithIgnoreCase("DivX"))
|
||||
{
|
||||
return "DivX";
|
||||
}
|
||||
}
|
||||
|
||||
if (videoFormat == "MPEG-4 Visual" || videoFormat == "mp4v")
|
||||
{
|
||||
result = GetSceneNameMatch(sceneName, "XviD", "DivX", "");
|
||||
if (result.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (videoFormat == "VC-1")
|
||||
{
|
||||
return "VC1";
|
||||
}
|
||||
|
||||
if (videoFormat.EqualsIgnoreCase("VP6") || videoFormat.EqualsIgnoreCase("VP7") ||
|
||||
videoFormat.EqualsIgnoreCase("VP8") || videoFormat.EqualsIgnoreCase("VP9"))
|
||||
{
|
||||
return videoFormat.ToUpperInvariant();
|
||||
}
|
||||
|
||||
if (videoFormat == "WMV2")
|
||||
{
|
||||
return "WMV";
|
||||
}
|
||||
|
||||
if (videoFormat.EqualsIgnoreCase("DivX") || videoFormat.EqualsIgnoreCase("div3"))
|
||||
{
|
||||
return "DivX";
|
||||
}
|
||||
|
||||
if (videoFormat.EqualsIgnoreCase("XviD"))
|
||||
{
|
||||
return "XviD";
|
||||
}
|
||||
|
||||
Logger.Debug("Unknown video format: '{0}' in '{1}'.", string.Join(", ", videoFormat, videoCodecID, videoProfile, videoCodecLibrary), sceneName);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string FormatVideoCodecLegacy(MediaInfoModel mediaInfo, string sceneName)
|
||||
{
|
||||
var videoCodec = mediaInfo.VideoCodec;
|
||||
|
||||
if (videoCodec.IsNullOrWhiteSpace())
|
||||
{
|
||||
return videoCodec;
|
||||
}
|
||||
|
||||
if (videoCodec == "AVC")
|
||||
{
|
||||
return GetSceneNameMatch(sceneName, "AVC", "h264", "x264");
|
||||
}
|
||||
|
||||
if (videoCodec == "V_MPEGH/ISO/HEVC" || videoCodec == "HEVC")
|
||||
{
|
||||
return GetSceneNameMatch(sceneName, "HEVC", "h265", "x265");
|
||||
}
|
||||
|
||||
if (videoCodec == "MPEG-2 Video")
|
||||
{
|
||||
return "MPEG2";
|
||||
}
|
||||
|
||||
if (videoCodec == "MPEG-4 Visual")
|
||||
{
|
||||
return GetSceneNameMatch(sceneName, "DivX", "XviD");
|
||||
}
|
||||
|
||||
if (videoCodec.StartsWithIgnoreCase("XviD"))
|
||||
{
|
||||
return "XviD";
|
||||
}
|
||||
|
||||
if (videoCodec.StartsWithIgnoreCase("DivX"))
|
||||
{
|
||||
return "DivX";
|
||||
}
|
||||
|
||||
if (videoCodec.EqualsIgnoreCase("VC-1"))
|
||||
{
|
||||
return "VC1";
|
||||
}
|
||||
|
||||
return videoCodec;
|
||||
}
|
||||
|
||||
private static decimal? FormatAudioChannelsFromAudioChannelPositions(MediaInfoModel mediaInfo)
|
||||
{
|
||||
var audioChannelPositions = mediaInfo.AudioChannelPositions;
|
||||
|
||||
if (audioChannelPositions.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Formatting audio channels using 'AudioChannelPositions', with a value of: '{0}'", audioChannelPositions);
|
||||
|
||||
if (audioChannelPositions.Contains("+"))
|
||||
{
|
||||
return audioChannelPositions.Split('+')
|
||||
.Sum(s => decimal.Parse(s.Trim(), CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (audioChannelPositions.Contains("/"))
|
||||
{
|
||||
return Regex.Replace(audioChannelPositions, @"^\d+\sobjects", "", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||
.Replace("Object Based / ", "")
|
||||
.Split(new string[] { " / " }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.FirstOrDefault()
|
||||
?.Split('/')
|
||||
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e, "Unable to format audio channels using 'AudioChannelPositions'");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static decimal? FormatAudioChannelsFromAudioChannelPositionsText(MediaInfoModel mediaInfo)
|
||||
{
|
||||
var audioChannelPositionsText = mediaInfo.AudioChannelPositionsText;
|
||||
var audioChannels = mediaInfo.AudioChannels;
|
||||
|
||||
if (audioChannelPositionsText.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Formatting audio channels using 'AudioChannelPositionsText', with a value of: '{0}'", audioChannelPositionsText);
|
||||
|
||||
return audioChannelPositionsText.ContainsIgnoreCase("LFE") ? audioChannels - 1 + 0.1m : audioChannels;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e, "Unable to format audio channels using 'AudioChannelPositionsText'");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static decimal? FormatAudioChannelsFromAudioChannels(MediaInfoModel mediaInfo)
|
||||
{
|
||||
var audioChannels = mediaInfo.AudioChannels;
|
||||
|
||||
if (mediaInfo.SchemaRevision >= 3)
|
||||
{
|
||||
Logger.Debug("Formatting audio channels using 'AudioChannels', with a value of: '{0}'", audioChannels);
|
||||
|
||||
return audioChannels;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetSceneNameMatch(string sceneName, params string[] tokens)
|
||||
{
|
||||
sceneName = sceneName.IsNotNullOrWhiteSpace() ? Parser.Parser.RemoveFileExtension(sceneName) : string.Empty;
|
||||
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
if (sceneName.ContainsIgnoreCase(token))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
// Last token is the default.
|
||||
return tokens.Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
|
@ -10,12 +9,23 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
|||
{
|
||||
public class MediaInfoModel : IEmbeddedDocument
|
||||
{
|
||||
public string ContainerFormat { get; set; }
|
||||
public string VideoCodec { get; set; }
|
||||
public string VideoFormat { get; set; }
|
||||
public string VideoCodecID { get; set; }
|
||||
public string VideoProfile { get; set; }
|
||||
public string VideoCodecLibrary { get; set; }
|
||||
public int VideoBitrate { get; set; }
|
||||
public int VideoBitDepth { get; set; }
|
||||
public int VideoMultiViewCount { get; set; }
|
||||
public string VideoColourPrimaries { get; set; }
|
||||
public string VideoTransferCharacteristics { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public string AudioFormat { get; set; }
|
||||
public string AudioCodecID { get; set; }
|
||||
public string AudioCodecLibrary { get; set; }
|
||||
public string AudioAdditionalFeatures { get; set; }
|
||||
public int AudioBitrate { get; set; }
|
||||
public TimeSpan RunTime { get; set; }
|
||||
public int AudioStreamCount { get; set; }
|
||||
|
|
@ -28,40 +38,5 @@ public class MediaInfoModel : IEmbeddedDocument
|
|||
public string Subtitles { get; set; }
|
||||
public string ScanType { get; set; }
|
||||
public int SchemaRevision { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public decimal FormattedAudioChannels
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return
|
||||
AudioChannelPositions.Replace("Object Based /", "").Replace(" / ", "$")
|
||||
.Split('$')
|
||||
.First()
|
||||
.Split('/')
|
||||
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
if (AudioChannelPositionsText.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (SchemaRevision >= 3)
|
||||
{
|
||||
return AudioChannels;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? AudioChannels - 1 + 0.1m : AudioChannels;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ public class UpdateMediaInfoService : IHandle<MovieScannedEvent>
|
|||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 3;
|
||||
|
||||
public UpdateMediaInfoService(IDiskProvider diskProvider,
|
||||
IMediaFileService mediaFileService,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
|
|
@ -49,7 +47,6 @@ private void UpdateMediaInfo(Movie movie, List<MovieFile> mediaFiles)
|
|||
|
||||
if (mediaFile.MediaInfo != null)
|
||||
{
|
||||
mediaFile.MediaInfo.SchemaRevision = CURRENT_MEDIA_INFO_SCHEMA_REVISION;
|
||||
_mediaFileService.Update(mediaFile);
|
||||
_logger.Debug("Updated MediaInfo for '{0}'", path);
|
||||
}
|
||||
|
|
@ -65,7 +62,7 @@ public void Handle(MovieScannedEvent message)
|
|||
}
|
||||
|
||||
var allMediaFiles = _mediaFileService.GetFilesByMovie(message.Movie.Id);
|
||||
var filteredMediaFiles = allMediaFiles.Where(c => c.MediaInfo == null || c.MediaInfo.SchemaRevision < CURRENT_MEDIA_INFO_SCHEMA_REVISION).ToList();
|
||||
var filteredMediaFiles = allMediaFiles.Where(c => c.MediaInfo == null || c.MediaInfo.SchemaRevision < VideoFileInfoReader.MINIMUM_MEDIA_INFO_SCHEMA_REVISION).ToList();
|
||||
|
||||
UpdateMediaInfo(message.Movie, filteredMediaFiles);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ public class VideoFileInfoReader : IVideoFileInfoReader
|
|||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public const int MINIMUM_MEDIA_INFO_SCHEMA_REVISION = 4;
|
||||
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 5;
|
||||
|
||||
public VideoFileInfoReader(IDiskProvider diskProvider, Logger logger)
|
||||
{
|
||||
|
|
@ -90,6 +92,7 @@ public MediaInfoModel GetMediaInfo(string filename)
|
|||
int audioChannels;
|
||||
int videoBitDepth;
|
||||
decimal videoFrameRate;
|
||||
int videoMultiViewCount;
|
||||
|
||||
string subtitles = mediaInfo.Get(StreamKind.General, 0, "Text_Language_List");
|
||||
string scanType = mediaInfo.Get(StreamKind.Video, 0, "ScanType");
|
||||
|
|
@ -102,65 +105,60 @@ public MediaInfoModel GetMediaInfo(string filename)
|
|||
}
|
||||
decimal.TryParse(mediaInfo.Get(StreamKind.Video, 0, "FrameRate"), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out videoFrameRate);
|
||||
int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "BitDepth"), out videoBitDepth);
|
||||
int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "MultiView_Count"), out videoMultiViewCount);
|
||||
|
||||
//Runtime
|
||||
int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out videoRuntime);
|
||||
int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "PlayTime"), out audioRuntime);
|
||||
int.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime);
|
||||
|
||||
string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate");
|
||||
int aBindex = aBitRate.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (aBindex > 0)
|
||||
{
|
||||
aBitRate = aBitRate.Remove(aBindex);
|
||||
}
|
||||
string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||
|
||||
int.TryParse(aBitRate, out audioBitRate);
|
||||
int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount);
|
||||
|
||||
|
||||
string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)");
|
||||
int aCindex = audioChannelsStr.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
if (aCindex > 0)
|
||||
{
|
||||
audioChannelsStr = audioChannelsStr.Remove(aCindex);
|
||||
}
|
||||
string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||
|
||||
var audioChannelPositions = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions/String2");
|
||||
var audioChannelPositionsText = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions");
|
||||
|
||||
string audioLanguages = mediaInfo.Get(StreamKind.General, 0, "Audio_Language_List");
|
||||
string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile");
|
||||
|
||||
int aPindex = audioProfile.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
if (aPindex > 0)
|
||||
{
|
||||
audioProfile = audioProfile.Remove(aPindex);
|
||||
}
|
||||
string videoProfile = mediaInfo.Get(StreamKind.Video, 0, "Format_Profile").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||
string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||
|
||||
int.TryParse(audioChannelsStr, out audioChannels);
|
||||
var mediaInfoModel = new MediaInfoModel
|
||||
{
|
||||
VideoCodec = mediaInfo.Get(StreamKind.Video, 0, "Codec/String"),
|
||||
VideoBitrate = videoBitRate,
|
||||
VideoBitDepth = videoBitDepth,
|
||||
Height = height,
|
||||
Width = width,
|
||||
AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"),
|
||||
AudioBitrate = audioBitRate,
|
||||
RunTime = GetBestRuntime(audioRuntime, videoRuntime, generalRuntime),
|
||||
AudioStreamCount = streamCount,
|
||||
AudioChannels = audioChannels,
|
||||
AudioChannelPositions = audioChannelPositions,
|
||||
AudioChannelPositionsText = audioChannelPositionsText,
|
||||
AudioProfile = audioProfile.Trim(),
|
||||
VideoFps = videoFrameRate,
|
||||
AudioLanguages = audioLanguages,
|
||||
Subtitles = subtitles,
|
||||
ScanType = scanType
|
||||
};
|
||||
{
|
||||
ContainerFormat = mediaInfo.Get(StreamKind.General, 0, "Format"),
|
||||
VideoFormat = mediaInfo.Get(StreamKind.Video, 0, "Format"),
|
||||
VideoCodecID = mediaInfo.Get(StreamKind.Video, 0, "CodecID"),
|
||||
VideoProfile = videoProfile,
|
||||
VideoCodecLibrary = mediaInfo.Get(StreamKind.Video, 0, "Encoded_Library"),
|
||||
VideoBitrate = videoBitRate,
|
||||
VideoBitDepth = videoBitDepth,
|
||||
VideoMultiViewCount = videoMultiViewCount,
|
||||
VideoColourPrimaries = mediaInfo.Get(StreamKind.Video, 0, "colour_primaries"),
|
||||
VideoTransferCharacteristics = mediaInfo.Get(StreamKind.Video, 0, "transfer_characteristics"),
|
||||
Height = height,
|
||||
Width = width,
|
||||
AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"),
|
||||
AudioCodecID = mediaInfo.Get(StreamKind.Audio, 0, "CodecID"),
|
||||
AudioProfile = audioProfile,
|
||||
AudioCodecLibrary = mediaInfo.Get(StreamKind.Audio, 0, "Encoded_Library"),
|
||||
AudioAdditionalFeatures = mediaInfo.Get(StreamKind.Audio, 0, "Format_AdditionalFeatures"),
|
||||
AudioBitrate = audioBitRate,
|
||||
RunTime = GetBestRuntime(audioRuntime, videoRuntime, generalRuntime),
|
||||
AudioStreamCount = streamCount,
|
||||
AudioChannels = audioChannels,
|
||||
AudioChannelPositions = audioChannelPositions,
|
||||
AudioChannelPositionsText = audioChannelPositionsText,
|
||||
VideoFps = videoFrameRate,
|
||||
AudioLanguages = audioLanguages,
|
||||
Subtitles = subtitles,
|
||||
ScanType = scanType,
|
||||
SchemaRevision = CURRENT_MEDIA_INFO_SCHEMA_REVISION
|
||||
};
|
||||
|
||||
return mediaInfoModel;
|
||||
}
|
||||
|
|
@ -175,7 +173,7 @@ public MediaInfoModel GetMediaInfo(string filename)
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse media info from file: " + filename);
|
||||
_logger.Error(ex, "Unable to parse media info from file: {0}", filename);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
|
|
@ -27,5 +28,20 @@ public override string ToString()
|
|||
{
|
||||
return string.Format("[{0}] {1}", Id, RelativePath);
|
||||
}
|
||||
|
||||
public string GetSceneOrFileName()
|
||||
{
|
||||
if (SceneName.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return SceneName;
|
||||
}
|
||||
|
||||
if (RelativePath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return System.IO.Path.GetFileName(RelativePath);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
|
@ -37,6 +38,7 @@ public class ImportDecisionMaker : IMakeImportDecision
|
|||
private readonly IQualityDefinitionService _qualitiesService;
|
||||
private readonly IConfigService _config;
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly ICached<string> _warnedFiles;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
||||
|
|
@ -48,6 +50,7 @@ public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> speci
|
|||
IQualityDefinitionService qualitiesService,
|
||||
IConfigService config,
|
||||
IHistoryService historyService,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
|
|
@ -59,6 +62,7 @@ public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> speci
|
|||
_qualitiesService = qualitiesService;
|
||||
_config = config;
|
||||
_historyService = historyService;
|
||||
_warnedFiles = cacheManager.GetCache<string>(this.GetType());
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +152,16 @@ private ImportDecision GetDecision(string file, Movie movie, DownloadClientItem
|
|||
|
||||
if (MediaFileExtensions.Extensions.Contains(Path.GetExtension(file)))
|
||||
{
|
||||
_logger.Warn("Unable to parse movie info from path {0}", file);
|
||||
if (_warnedFiles.Find(file) == null)
|
||||
{
|
||||
_warnedFiles.Set(file, "warned");
|
||||
_logger.Warn("Unable to parse movie info from path {0}", file);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Trace("Already warned user that we are unable to parse movie info from path: {0}", file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
decision = new ImportDecision(localMovie, new Rejection("Unable to parse file"));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
|
|
@ -9,6 +10,7 @@
|
|||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using RestSharp.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
|
|
@ -51,6 +53,7 @@ public void Execute(UpdateMovieFileQualityCommand command)
|
|||
|
||||
var history = _historyService.FindByMovieId(movieFile.MovieId).OrderByDescending(h => h.Date);
|
||||
var latestImported = history.FirstOrDefault(h => h.EventType == HistoryEventType.DownloadFolderImported);
|
||||
var latestImportedName = latestImported?.SourceTitle;
|
||||
var latestGrabbed = history.FirstOrDefault(h => h.EventType == HistoryEventType.Grabbed);
|
||||
var sizeMovie = new LocalMovie();
|
||||
sizeMovie.Size = movieFile.Size;
|
||||
|
|
@ -67,7 +70,18 @@ public void Execute(UpdateMovieFileQualityCommand command)
|
|||
helpers.Add(latestGrabbed);
|
||||
}
|
||||
|
||||
var parsedMovieInfo = _parsingService.ParseMovieInfo(latestImported?.SourceTitle ?? movieFile.RelativePath, helpers);
|
||||
ParsedMovieInfo parsedMovieInfo = null;
|
||||
|
||||
if (latestImportedName?.IsNotNullOrWhiteSpace() == true)
|
||||
{
|
||||
parsedMovieInfo = _parsingService.ParseMovieInfo(latestImportedName, helpers);
|
||||
}
|
||||
|
||||
if (parsedMovieInfo == null)
|
||||
{
|
||||
_logger.Debug("Could not parse movie info from history source title, using current path instead: {0}.", movieFile.RelativePath);
|
||||
parsedMovieInfo = _parsingService.ParseMovieInfo(movieFile.RelativePath, helpers);
|
||||
}
|
||||
|
||||
//Only update Custom formats for now.
|
||||
if (parsedMovieInfo != null)
|
||||
|
|
@ -78,7 +92,7 @@ public void Execute(UpdateMovieFileQualityCommand command)
|
|||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Could not update custom formats for {0}, since it's title could not be parsed!", movieFile);
|
||||
_logger.Warn("Could not update custom formats for {0}, since it's title could not be parsed!", movieFile);
|
||||
}
|
||||
|
||||
count++;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Notifications.Xbmc.Model;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Common.Disk;
|
||||
using System.IO;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Xbmc
|
||||
{
|
||||
|
|
@ -85,6 +87,7 @@ private void UpdateMovieLibrary(XbmcSettings settings, Movie movie)
|
|||
|
||||
if (moviePath != null)
|
||||
{
|
||||
moviePath = new OsPath(moviePath).Directory.FullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
_logger.Debug("Updating movie {0} (Path: {1}) on XBMC host: {2}", movie, moviePath, settings.Address);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -847,6 +847,7 @@
|
|||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MediaFiles\MediaFileTableCleanupService.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatter.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoLib.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoModel.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoService.cs" />
|
||||
|
|
@ -1318,11 +1319,11 @@
|
|||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
|
|
@ -333,12 +334,12 @@ private void AddImdbIdTokens(Dictionary<string, Func<TokenMatch, string>> tokenH
|
|||
tokenHandlers["{IMDb Id}"] = m => $"{imdbId}";
|
||||
}
|
||||
|
||||
private void AddMovieFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile episodeFile)
|
||||
private void AddMovieFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile movieFile)
|
||||
{
|
||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile);
|
||||
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile);
|
||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(movieFile);
|
||||
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(movieFile);
|
||||
//tokenHandlers["{IMDb Id}"] = m =>
|
||||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Radarr");
|
||||
tokenHandlers["{Release Group}"] = m => movieFile.ReleaseGroup ?? m.DefaultValue("Radarr");
|
||||
}
|
||||
|
||||
private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Movie movie, MovieFile movieFile)
|
||||
|
|
@ -366,98 +367,22 @@ private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tok
|
|||
{
|
||||
if (movieFile.MediaInfo == null) return;
|
||||
|
||||
string videoCodec;
|
||||
switch (movieFile.MediaInfo.VideoCodec)
|
||||
var sceneName = movieFile.GetSceneOrFileName();
|
||||
|
||||
var videoCodec = MediaInfoFormatter.FormatVideoCodec(movieFile.MediaInfo, sceneName);
|
||||
var audioCodec = MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, sceneName);
|
||||
var audioChannels = MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo);
|
||||
|
||||
// Workaround until https://github.com/MediaArea/MediaInfo/issues/299 is fixed and release
|
||||
if (audioCodec.EqualsIgnoreCase("DTS-X"))
|
||||
{
|
||||
case "AVC":
|
||||
if (movieFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(movieFile.SceneName).Contains("h264"))
|
||||
{
|
||||
videoCodec = "h264";
|
||||
}
|
||||
else
|
||||
{
|
||||
videoCodec = "x264";
|
||||
}
|
||||
break;
|
||||
|
||||
case "V_MPEGH/ISO/HEVC":
|
||||
if (movieFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(movieFile.SceneName).Contains("h265"))
|
||||
{
|
||||
videoCodec = "h265";
|
||||
}
|
||||
else
|
||||
{
|
||||
videoCodec = "x265";
|
||||
}
|
||||
break;
|
||||
|
||||
case "MPEG-2 Video":
|
||||
videoCodec = "MPEG2";
|
||||
break;
|
||||
|
||||
default:
|
||||
videoCodec = movieFile.MediaInfo.VideoCodec;
|
||||
break;
|
||||
}
|
||||
|
||||
string audioCodec;
|
||||
switch (movieFile.MediaInfo.AudioFormat)
|
||||
{
|
||||
case "AC-3":
|
||||
audioCodec = "AC3";
|
||||
break;
|
||||
|
||||
case "E-AC-3":
|
||||
audioCodec = "EAC3";
|
||||
break;
|
||||
|
||||
case "Atmos / TrueHD":
|
||||
audioCodec = "Atmos TrueHD";
|
||||
break;
|
||||
|
||||
case "MPEG Audio":
|
||||
if (movieFile.MediaInfo.AudioProfile == "Layer 3")
|
||||
{
|
||||
audioCodec = "MP3";
|
||||
}
|
||||
else
|
||||
{
|
||||
audioCodec = movieFile.MediaInfo.AudioFormat;
|
||||
}
|
||||
break;
|
||||
|
||||
case "DTS":
|
||||
if (movieFile.MediaInfo.AudioProfile == "ES" || movieFile.MediaInfo.AudioProfile == "ES Discrete" || movieFile.MediaInfo.AudioProfile == "ES Matrix")
|
||||
{
|
||||
audioCodec = "DTS-ES";
|
||||
}
|
||||
else if (movieFile.MediaInfo.AudioProfile == "MA")
|
||||
{
|
||||
audioCodec = "DTS-HD MA";
|
||||
}
|
||||
else if (movieFile.MediaInfo.AudioProfile == "HRA")
|
||||
{
|
||||
audioCodec = "DTS-HD HRA";
|
||||
}
|
||||
else if (movieFile.MediaInfo.AudioProfile == "X")
|
||||
{
|
||||
audioCodec = "DTS-X";
|
||||
}
|
||||
else
|
||||
{
|
||||
audioCodec = movieFile.MediaInfo.AudioFormat;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
audioCodec = movieFile.MediaInfo.AudioFormat;
|
||||
break;
|
||||
audioChannels = audioChannels - 1 + 0.1m;
|
||||
}
|
||||
|
||||
var mediaInfoAudioLanguages = GetLanguagesToken(movieFile.MediaInfo.AudioLanguages);
|
||||
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
|
||||
{
|
||||
mediaInfoAudioLanguages = string.Format("[{0}]", mediaInfoAudioLanguages);
|
||||
mediaInfoAudioLanguages = $"[{mediaInfoAudioLanguages}]";
|
||||
}
|
||||
var mediaInfoAudioLanguagesAll = mediaInfoAudioLanguages;
|
||||
if (mediaInfoAudioLanguages == "[EN]")
|
||||
|
|
@ -465,17 +390,32 @@ private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tok
|
|||
mediaInfoAudioLanguages = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
var mediaInfoSubtitleLanguages = GetLanguagesToken(movieFile.MediaInfo.Subtitles);
|
||||
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
|
||||
{
|
||||
mediaInfoSubtitleLanguages = string.Format("[{0}]", mediaInfoSubtitleLanguages);
|
||||
mediaInfoSubtitleLanguages = $"[{mediaInfoSubtitleLanguages}]";
|
||||
}
|
||||
|
||||
var videoBitDepth = movieFile.MediaInfo.VideoBitDepth > 0 ? movieFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
|
||||
var audioChannels = movieFile.MediaInfo.FormattedAudioChannels > 0 ?
|
||||
movieFile.MediaInfo.FormattedAudioChannels.ToString("F1", CultureInfo.InvariantCulture) :
|
||||
string.Empty;
|
||||
var audioChannelsFormatted = audioChannels > 0 ?
|
||||
audioChannels.ToString("F1", CultureInfo.InvariantCulture) :
|
||||
string.Empty;
|
||||
|
||||
var mediaInfo3D = movieFile.MediaInfo.VideoMultiViewCount > 1 ? "3D" : string.Empty;
|
||||
|
||||
var videoColourPrimaries = movieFile.MediaInfo.VideoColourPrimaries ?? string.Empty;
|
||||
var videoTransferCharacteristics = movieFile.MediaInfo.VideoTransferCharacteristics ?? string.Empty;
|
||||
var mediaInfoHDR = string.Empty;
|
||||
|
||||
if (movieFile.MediaInfo.VideoBitDepth >= 10 && !videoColourPrimaries.IsNullOrWhiteSpace() && !videoTransferCharacteristics.IsNullOrWhiteSpace())
|
||||
{
|
||||
string[] validTransferFunctions = new string[] { "PQ", "HLG" };
|
||||
|
||||
if (videoColourPrimaries.EqualsIgnoreCase("BT.2020") && validTransferFunctions.Any(videoTransferCharacteristics.Contains))
|
||||
{
|
||||
mediaInfoHDR = "HDR";
|
||||
}
|
||||
}
|
||||
|
||||
tokenHandlers["{MediaInfo Video}"] = m => videoCodec;
|
||||
tokenHandlers["{MediaInfo VideoCodec}"] = m => videoCodec;
|
||||
|
|
@ -483,14 +423,17 @@ private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tok
|
|||
|
||||
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
|
||||
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
|
||||
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannels;
|
||||
|
||||
tokenHandlers["{MediaInfo Simple}"] = m => string.Format("{0} {1}", videoCodec, audioCodec);
|
||||
|
||||
tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
|
||||
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannelsFormatted;
|
||||
tokenHandlers["{MediaInfo AudioLanguages}"] = m => mediaInfoAudioLanguages;
|
||||
tokenHandlers["{MediaInfo AudioLanguagesAll}"] = m => mediaInfoAudioLanguagesAll;
|
||||
|
||||
tokenHandlers["{MediaInfo SubtitleLanguages}"] = m => mediaInfoSubtitleLanguages;
|
||||
|
||||
tokenHandlers["{MediaInfo 3D}"] = m => mediaInfo3D;
|
||||
tokenHandlers["{MediaInfo HDR}"] = m => mediaInfoHDR;
|
||||
|
||||
tokenHandlers["{MediaInfo Simple}"] = m => $"{videoCodec} {audioCodec}";
|
||||
tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{mediaInfoAudioLanguages} {mediaInfoSubtitleLanguages}";
|
||||
}
|
||||
|
||||
private string GetLanguagesToken(string mediaInfoLanguages)
|
||||
|
|
@ -610,24 +553,24 @@ private string GetQualityReal(Movie movie, QualityModel quality)
|
|||
return string.Empty;
|
||||
}
|
||||
|
||||
private string GetOriginalTitle(MovieFile episodeFile)
|
||||
private string GetOriginalTitle(MovieFile movieFile)
|
||||
{
|
||||
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
||||
if (movieFile.SceneName.IsNullOrWhiteSpace())
|
||||
{
|
||||
return GetOriginalFileName(episodeFile);
|
||||
return GetOriginalFileName(movieFile);
|
||||
}
|
||||
|
||||
return episodeFile.SceneName;
|
||||
return movieFile.SceneName;
|
||||
}
|
||||
|
||||
private string GetOriginalFileName(MovieFile episodeFile)
|
||||
private string GetOriginalFileName(MovieFile movieFile)
|
||||
{
|
||||
if (episodeFile.RelativePath.IsNullOrWhiteSpace())
|
||||
if (movieFile.RelativePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(episodeFile.Path);
|
||||
return Path.GetFileNameWithoutExtension(movieFile.Path);
|
||||
}
|
||||
|
||||
return Path.GetFileNameWithoutExtension(episodeFile.RelativePath);
|
||||
return Path.GetFileNameWithoutExtension(movieFile.RelativePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,11 @@ public FileNameSampleService(IBuildFileNames buildFileNames)
|
|||
|
||||
var mediaInfo = new MediaInfoModel()
|
||||
{
|
||||
VideoCodec = "AVC",
|
||||
VideoBitDepth = 8,
|
||||
VideoFormat = "AVC",
|
||||
VideoBitDepth = 10,
|
||||
VideoMultiViewCount = 2,
|
||||
VideoColourPrimaries = "BT.2020",
|
||||
VideoTransferCharacteristics = "PQ",
|
||||
AudioFormat = "DTS",
|
||||
AudioChannels = 6,
|
||||
AudioChannelPositions = "3/2/0.1",
|
||||
|
|
@ -37,8 +40,11 @@ public FileNameSampleService(IBuildFileNames buildFileNames)
|
|||
|
||||
var mediaInfoAnime = new MediaInfoModel()
|
||||
{
|
||||
VideoCodec = "AVC",
|
||||
VideoBitDepth = 8,
|
||||
VideoFormat = "AVC",
|
||||
VideoBitDepth = 10,
|
||||
VideoMultiViewCount = 2,
|
||||
VideoColourPrimaries = "BT.2020",
|
||||
VideoTransferCharacteristics = "PQ",
|
||||
AudioFormat = "DTS",
|
||||
AudioChannels = 6,
|
||||
AudioChannelPositions = "3/2/0.1",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public interface IParsingService
|
|||
MappingResult Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
|
||||
ParsedMovieInfo ParseMovieInfo(string title, List<object> helpers);
|
||||
ParsedMovieInfo ParseMoviePathInfo(string path, List<object> helpers);
|
||||
ParsedMovieInfo ParseMinimalMovieInfo(string path);
|
||||
ParsedMovieInfo ParseMinimalMovieInfo(string path, bool isDir = false);
|
||||
ParsedMovieInfo ParseMinimalPathMovieInfo(string path);
|
||||
List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo);
|
||||
List<FormatTagMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo);
|
||||
|
|
@ -192,16 +192,16 @@ public LocalMovie GetLocalMovie(string filename, ParsedMovieInfo minimalInfo, Mo
|
|||
};
|
||||
}
|
||||
|
||||
public ParsedMovieInfo ParseMinimalMovieInfo(string file)
|
||||
public ParsedMovieInfo ParseMinimalMovieInfo(string file, bool isDir = false)
|
||||
{
|
||||
return Parser.ParseMovieTitle(file, _config.ParsingLeniency > 0);
|
||||
return Parser.ParseMovieTitle(file, _config.ParsingLeniency > 0, isDir);
|
||||
}
|
||||
|
||||
public ParsedMovieInfo ParseMinimalPathMovieInfo(string path)
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
var result = ParseMinimalMovieInfo(fileInfo.Name);
|
||||
var result = ParseMinimalMovieInfo(fileInfo.Name, true);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -104,13 +104,6 @@ public static QualityModel ParseQuality(string name)
|
|||
}
|
||||
}
|
||||
|
||||
if (RawHDRegex.IsMatch(normalizedName))
|
||||
{
|
||||
result.Modifier = Modifier.RAWHD;
|
||||
result.Source = Source.TV;
|
||||
return result;
|
||||
}
|
||||
|
||||
var sourceMatch = SourceRegex.Matches(normalizedName).OfType<Match>().LastOrDefault();
|
||||
var resolution = ParseResolution(normalizedName);
|
||||
var codecRegex = CodecRegex.Match(normalizedName);
|
||||
|
|
@ -129,6 +122,13 @@ public static QualityModel ParseQuality(string name)
|
|||
result.Source = Source.BLURAY;
|
||||
return result; //We found remux!
|
||||
}
|
||||
|
||||
if (RawHDRegex.IsMatch(normalizedName) && result.Modifier != Modifier.BRDISK)
|
||||
{
|
||||
result.Modifier = Modifier.RAWHD;
|
||||
result.Source = Source.TV;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch != null && sourceMatch.Success)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public interface IRootFolderService
|
|||
{
|
||||
List<RootFolder> All();
|
||||
List<RootFolder> AllWithUnmappedFolders();
|
||||
List<RootFolder> AllWithSpace();
|
||||
RootFolder Add(RootFolder rootDir);
|
||||
void Remove(int id);
|
||||
RootFolder Get(int id);
|
||||
|
|
@ -62,6 +63,31 @@ public List<RootFolder> All()
|
|||
return rootFolders;
|
||||
}
|
||||
|
||||
public List<RootFolder> AllWithSpace()
|
||||
{
|
||||
var rootFolders = _rootFolderRepository.All().ToList();
|
||||
|
||||
rootFolders.ForEach(folder =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (folder.Path.IsPathValid() && _diskProvider.FolderExists(folder.Path))
|
||||
{
|
||||
folder.FreeSpace = _diskProvider.GetAvailableSpace(folder.Path);
|
||||
folder.TotalSpace = _diskProvider.GetTotalSize(folder.Path);
|
||||
}
|
||||
}
|
||||
//We don't want an exception to prevent the root folders from loading in the UI, so they can still be deleted
|
||||
catch (Exception ex)
|
||||
{
|
||||
folder.FreeSpace = 0;
|
||||
_logger.Error(ex, "Unable to get free space for root folder {0}", folder.Path);
|
||||
}
|
||||
});
|
||||
|
||||
return rootFolders;
|
||||
}
|
||||
|
||||
public List<RootFolder> AllWithUnmappedFolders()
|
||||
{
|
||||
var rootFolders = _rootFolderRepository.All().ToList();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -189,8 +189,6 @@ private uint GetGroupId(string group)
|
|||
}
|
||||
|
||||
return g.gr_gid;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -111,6 +111,18 @@ private IMount ParseLine(string line)
|
|||
return null;
|
||||
}
|
||||
|
||||
if (mount.StartsWith("/var/lib/"))
|
||||
{
|
||||
// Could be /var/lib/docker when docker uses zfs. Very unlikely that a useful mount is located in /var/lib.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mount.StartsWith("/snap/"))
|
||||
{
|
||||
// Mount point for snap packages
|
||||
return null;
|
||||
}
|
||||
|
||||
var driveType = FindDriveType.Find(type);
|
||||
|
||||
if (name.StartsWith("/dev/") || GetFileSystems().GetValueOrDefault(type, false))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72-8088-4F75-A582-1158CF8291F7}"
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
{{path}}<br>
|
||||
<span title="{{#if movieFile.relativePath}} {{movieFile.relativePath}}{{/if}}" class="hint" style="font-size: 12px;">{{#if movieFile.relativePath}} {{movieFile.relativePath}}{{else}} Movie File Not Found{{/if}}</span>
|
||||
<span title="{{#if movieFile.relativePath}} {{movieFile.relativePath}}{{/if}}" class="hint path">
|
||||
{{#if movieFile.relativePath}}┕ {{movieFile.relativePath}}{{else}}┕ Movie File Not Found{{/if}}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{{#if_gt proper compare="1"}}
|
||||
<span class="badge badge-info" title="PROPER">{{movieFile.quality.qualityDefinition.title}}</span>
|
||||
<span class="badge badge-info" title="PROPER">{{movieFile.quality.quality.name}}</span>
|
||||
{{else}}
|
||||
<span class="badge" title="{{#if movieFile.quality.hardcodedSubs}}Warning: {{movieFile.quality.hardcodedSubs}}{{/if}}">{{movieFile.quality.qualityDefinition.title}}</span>
|
||||
<span class="badge" title="{{#if movieFile.quality.hardcodedSubs}}Warning: {{movieFile.quality.hardcodedSubs}}{{/if}}">{{movieFile.quality.quality.name}}</span>
|
||||
{{/if_gt}}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
@import "../Content/Bootstrap/mixins";
|
||||
@import "../Shared/Styles/card.less";
|
||||
@import "../Shared/Styles/clickable.less";
|
||||
|
||||
|
|
@ -130,6 +131,13 @@
|
|||
.hint {
|
||||
color : #999999;
|
||||
font-style : italic;
|
||||
|
||||
&.path {
|
||||
.text-overflow();
|
||||
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.monitor-tooltip-contents {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ module.exports = NzbDroneCell.extend({
|
|||
if (runtime) {
|
||||
runtime = runtime.split(".")[0];
|
||||
}
|
||||
var video = "{0} ({1}x{2}) ({3})".format(info.videoCodec, info.width, info.height, runtime);
|
||||
var video = "{0} ({1}x{2}) ({3})".format(info.videoFormat || info.videoCodec, info.width, info.height, runtime);
|
||||
var audio = "{0} ({1})".format(info.audioFormat, info.audioLanguages);
|
||||
this.$el.html(video + " " + audio);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group advanced-setting">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Custom Formats</label>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control x-tags" tag-source="{{formats}}" tag-class-name="label label-success">
|
||||
</div>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-radarr-form-info" title="Edit matched custom formats for this file. ADVANCED USERS ONLY!"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -44,11 +44,9 @@ var view = Marionette.ItemView.extend({
|
|||
onRender : function() {
|
||||
var rootFolder = this.model.get("rootFolderPath");
|
||||
if (rootFolder !== "") {
|
||||
//this.ui.rootFolder.val(rootFolder);
|
||||
this.ui.rootFolder.children().filter(function() {
|
||||
//may want to use $.trim in here
|
||||
return $(this).text() === rootFolder;
|
||||
}).attr('selected', true);
|
||||
return $.trim($(this).text()) === rootFolder;
|
||||
}).prop('selected', true);
|
||||
} else {
|
||||
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
|
||||
if (RootFolders.get(defaultRoot)) {
|
||||
|
|
@ -58,7 +56,7 @@ var view = Marionette.ItemView.extend({
|
|||
},
|
||||
|
||||
_onBeforeSave : function() {
|
||||
var profile = this.ui.profile.val();
|
||||
var profile = parseInt(this.ui.profile.val(), 10);
|
||||
var minAvail = this.ui.minimumAvailability.val();
|
||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
||||
this.model.set({
|
||||
|
|
|
|||
Loading…
Reference in a new issue