diff --git a/build.ps1 b/build.ps1 index 486549865..a84a1de8e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,6 +3,7 @@ $outputFolder = '.\_output' $outputFolderMono = '.\_output_mono' $testPackageFolder = '.\_tests\' $testSearchPattern = '*.Test\bin\x86\Release' +$sourceFolder = '.\src' Function Build() { @@ -84,6 +85,9 @@ Function PackageMono() get-childitem $outputFolderMono -File -Filter sqlite3.* -Recurse | foreach ($_) {remove-item $_.fullname} get-childitem $outputFolderMono -File -Filter MediaInfo.* -Recurse | foreach ($_) {remove-item $_.fullname} + Write-Host "Adding MediaInfoDotNet.dll.config (for dllmap)" + Copy-Item "$sourceFolder\MediaInfoDotNet.dll.config" $outputFolderMono + Write-Host Renaming NzbDrone.Console.exe to NzbDrone.exe get-childitem $outputFolderMono -File -Filter NzbDrone.exe -Recurse | foreach ($_) {remove-item $_.fullname} Rename-Item "$outputFolderMono\NzbDrone.Console.exe" "NzbDrone.exe" @@ -103,7 +107,7 @@ Function PackageTests() { Write-Host Packaging Tests - Write-Host "##teamcity[progressStart 'Creating Mono Package']" + Write-Host "##teamcity[progressStart 'Creating Test Package']" if(Test-Path $testPackageFolder) { @@ -117,8 +121,8 @@ Function PackageTests() .\src\.nuget\NuGet.exe install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder - Copy-Item $outputFolder\*.dll -Destination $testPackageFolder -Force - Copy-Item $outputFolder\*.pdb -Destination $testPackageFolder -Force + Copy-Item $outputFolder\*.dll -Destination $testPackageFolder -Force + Copy-Item $outputFolder\*.pdb -Destination $testPackageFolder -Force Copy-Item .\*.sh -Destination $testPackageFolder -Force @@ -126,7 +130,10 @@ Function PackageTests() CleanFolder $testPackageFolder - Write-Host "##teamcity[progressFinish 'Creating Mono Package']" + Write-Host "Adding MediaInfoDotNet.dll.config (for dllmap)" + Copy-Item "$sourceFolder\MediaInfoDotNet.dll.config" -Destination $testPackageFolder -Force + + Write-Host "##teamcity[progressFinish 'Creating Test Package']" } diff --git a/debian/control b/debian/control index e7f6a7b11..99d801309 100644 --- a/debian/control +++ b/debian/control @@ -8,5 +8,5 @@ Vcs-Browser: https://github.com/NzbDrone/NzbDrone Package: nzbdrone Architecture: all -Depends: libmono-cil-dev (>= 2.10.1) +Depends: libmono-cil-dev (>= 2.10.1), sqlite3 (>= 3.7), mediainfo (>= 0.7.52) Description: NZBDrone is a PVR for newsgroup users diff --git a/src/MediaInfoDotNet.dll.config b/src/MediaInfoDotNet.dll.config new file mode 100644 index 000000000..3de7bdea3 --- /dev/null +++ b/src/MediaInfoDotNet.dll.config @@ -0,0 +1,5 @@ + + + + + diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs index b8fe97056..9088e46ee 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs @@ -85,21 +85,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications } [Test] - public void should_not_run_runtime_check_on_linux() + public void should_use_runtime() { - LinuxOnly(); - GivenFileSize(1000.Megabytes()); - - Subject.IsSatisfiedBy(_localEpisode); - - Mocker.GetMock().Verify(v => v.GetRunTime(It.IsAny()), Times.Never()); - } - - [Test] - public void should_run_runtime_check_on_windows() - { - WindowsOnly(); - GivenRuntime(120); GivenFileSize(1000.Megabytes()); @@ -111,7 +98,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications [Test] public void should_return_false_if_runtime_is_less_than_minimum() { - WindowsOnly(); GivenRuntime(60); Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); @@ -120,32 +106,30 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications [Test] public void should_return_true_if_runtime_greater_than_than_minimum() { - WindowsOnly(); GivenRuntime(120); Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); } [Test] - public void should_return_false_if_file_size_is_under_minimum() + public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size() { - LinuxOnly(); + Mocker.GetMock() + .Setup(s => s.GetRunTime(It.IsAny())) + .Throws(); - GivenRuntime(120); - GivenFileSize(20.Megabytes()); - - Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); + GivenFileSize(1000.Megabytes()); + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); } [Test] - public void should_return_false_if_file_size_is_under_minimum_for_larger_limits() + public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_undersize() { - LinuxOnly(); - - GivenRuntime(120); - GivenFileSize(120.Megabytes()); - _localEpisode.Quality = new QualityModel(Quality.Bluray1080p); + Mocker.GetMock() + .Setup(s => s.GetRunTime(It.IsAny())) + .Throws(); + GivenFileSize(1.Megabytes()); Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs index c3267089b..dbf6aba36 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs @@ -1,6 +1,8 @@ using System.IO; using FluentAssertions; +using Moq; using NUnit.Framework; +using NzbDrone.Common; using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common.Categories; @@ -11,6 +13,14 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo [DiskAccessTest] public class VideoFileInfoReaderFixture : CoreTest { + [SetUp] + public void Setup() + { + Mocker.GetMock() + .Setup(s => s.FileExists(It.IsAny())) + .Returns(true); + } + [Test] public void get_runtime() { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs index ccf833ce7..7e2fd7ed6 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs @@ -61,7 +61,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications return true; } - if (OsInfo.IsWindows) + try { var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path); @@ -76,12 +76,17 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications _logger.Trace("[{0}] appears to be a sample. Size: {1} Runtime: {2}", localEpisode.Path, localEpisode.Size, runTime); return false; } - - _logger.Trace("Runtime is over 2 minutes, skipping file size check"); - return true; } - return CheckSize(localEpisode); + catch (DllNotFoundException) + { + _logger.Trace("Falling back to file size detection"); + + return CheckSize(localEpisode); + } + + _logger.Trace("Runtime is over 90 seconds"); + return true; } private bool CheckSize(LocalEpisode localEpisode) @@ -90,12 +95,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications { if (localEpisode.Size < SampleSizeLimit * 2) { + _logger.Trace("1080p file is less than sample limit"); return false; } } if (localEpisode.Size < SampleSizeLimit) { + _logger.Trace("File is less than sample limit"); return false; } diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs index 69e8f4760..c18ec0870 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs @@ -1,4 +1,6 @@ -namespace NzbDrone.Core.MediaFiles.MediaInfo +using System; + +namespace NzbDrone.Core.MediaFiles.MediaInfo { public class MediaInfoModel { @@ -8,7 +10,7 @@ public int Height { get; set; } public string AudioFormat { get; set; } public int AudioBitrate { get; set; } - public int RunTime { get; set; } + public TimeSpan RunTime { get; set; } public int AudioStreamCount { get; set; } public int AudioChannels { get; set; } public string AudioProfile { get; set; } diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs index 2ad10fa14..60cee01a4 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs @@ -30,10 +30,11 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo if (!_diskProvider.FileExists(filename)) throw new FileNotFoundException("Media file does not exist: " + filename); - var mediaInfo = new MediaInfoLib.MediaInfo(); + MediaInfoLib.MediaInfo mediaInfo = null; try { + mediaInfo = new MediaInfoLib.MediaInfo(); _logger.Trace("Getting media info from {0}", filename); mediaInfo.Option("ParseSpeed", "0.2"); @@ -56,26 +57,26 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "BitRate"), out videoBitRate); string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate"); - int ABindex = aBitRate.IndexOf(" /"); - if (ABindex > 0) - aBitRate = aBitRate.Remove(ABindex); + int aBindex = aBitRate.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase); + if (aBindex > 0) + aBitRate = aBitRate.Remove(aBindex); Int32.TryParse(aBitRate, out audioBitRate); Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out runTime); Int32.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount); string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)"); - int ACindex = audioChannelsStr.IndexOf(" /"); - if (ACindex > 0) - audioChannelsStr = audioChannelsStr.Remove(ACindex); + int aCindex = audioChannelsStr.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase); + if (aCindex > 0) + audioChannelsStr = audioChannelsStr.Remove(aCindex); string audioLanguages = mediaInfo.Get(StreamKind.General, 0, "Audio_Language_List"); decimal videoFrameRate = Decimal.Parse(mediaInfo.Get(StreamKind.Video, 0, "FrameRate")); string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile"); - int APindex = audioProfile.IndexOf(" /"); - if (APindex > 0) - audioProfile = audioProfile.Remove(APindex); + int aPindex = audioProfile.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase); + if (aPindex > 0) + audioProfile = audioProfile.Remove(aPindex); Int32.TryParse(audioChannelsStr, out audioChannels); var mediaInfoModel = new MediaInfoModel @@ -84,9 +85,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo VideoBitrate = videoBitRate, Height = height, Width = width, + AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"), AudioBitrate = audioBitRate, - RunTime = (runTime / 1000), //InSeconds + RunTime = TimeSpan.FromMilliseconds(runTime), AudioStreamCount = streamCount, AudioChannels = audioChannels, AudioProfile = audioProfile.Trim(), @@ -100,34 +102,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo return mediaInfoModel; } } - catch (Exception ex) + catch (DllNotFoundException ex) { - _logger.ErrorException("Unable to parse media info from file: " + filename, ex); - mediaInfo.Close(); - } - - return null; - } - - public TimeSpan GetRunTime(string filename) - { - MediaInfoLib.MediaInfo mediaInfo = null; - try - { - mediaInfo = new MediaInfoLib.MediaInfo(); - _logger.Trace("Getting media info from {0}", filename); - - mediaInfo.Option("ParseSpeed", "0.2"); - int open = mediaInfo.Open(filename); - - if (open != 0) - { - int runTime; - Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out runTime); - - mediaInfo.Close(); - return TimeSpan.FromMilliseconds(runTime); - } + _logger.ErrorException("mediainfo is required but was not found", ex); + throw; } catch (Exception ex) { @@ -141,7 +119,19 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo } } - return new TimeSpan(); + return null; + } + + public TimeSpan GetRunTime(string filename) + { + var info = GetMediaInfo(filename); + + if (info == null) + { + return new TimeSpan(); + } + + return info.RunTime; } } }