diff --git a/build.ps1 b/build.ps1 index 9e493454f..0ca821f4a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -114,8 +114,8 @@ 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 "Adding NzbDrone.Core.dll.config (for dllmap)" + Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" $outputFolderMono Write-Host Renaming NzbDrone.Console.exe to NzbDrone.exe Get-ChildItem $outputFolderMono -File -Filter "NzbDrone.exe*" -Recurse | foreach ($_) {remove-item $_.fullname} @@ -212,8 +212,8 @@ Function PackageTests() CleanFolder $testPackageFolder $true - Write-Host "Adding MediaInfoDotNet.dll.config (for dllmap)" - Copy-Item "$sourceFolder\MediaInfoDotNet.dll.config" -Destination $testPackageFolder -Force + Write-Host "Adding NzbDrone.Core.dll.config (for dllmap)" + Copy-Item "$sourceFolder\NzbDrone.Core\NzbDrone.Core.dll.config" -Destination $testPackageFolder -Force Write-Host "##teamcity[progressFinish 'Creating Test Package']" } diff --git a/src/MediaInfoDotNet.dll.config b/src/MediaInfoDotNet.dll.config deleted file mode 100644 index 3094db159..000000000 --- a/src/MediaInfoDotNet.dll.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs index 89ee06773..438766c0f 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs @@ -19,6 +19,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo Mocker.GetMock() .Setup(s => s.FileExists(It.IsAny())) .Returns(true); + + Mocker.GetMock() + .Setup(s => s.OpenReadStream(It.IsAny())) + .Returns(s => new FileStream(s, FileMode.Open, FileAccess.Read)); } [Test] @@ -56,7 +60,6 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo } [Test] - [Ignore] public void get_info_unicode() { var srcPath = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4"); diff --git a/src/NzbDrone.Core/HealthCheck/Checks/MediaInfoDllCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/MediaInfoDllCheck.cs index f8eab9451..d52e6e58f 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/MediaInfoDllCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/MediaInfoDllCheck.cs @@ -1,6 +1,6 @@ using System; using System.Runtime.CompilerServices; -using MediaInfoLib; +using NzbDrone.Core.MediaFiles.MediaInfo; namespace NzbDrone.Core.HealthCheck.Checks { diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoLib.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoLib.cs new file mode 100644 index 000000000..a4fcb03be --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoLib.cs @@ -0,0 +1,328 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Core.MediaFiles.MediaInfo +{ + public enum StreamKind + { + General, + Video, + Audio, + Text, + Other, + Image, + Menu, + } + + public enum InfoKind + { + Name, + Text, + Measure, + Options, + NameText, + MeasureText, + Info, + HowTo + } + + public enum InfoOptions + { + ShowInInform, + Support, + ShowInSupported, + TypeOfValue + } + + public enum InfoFileOptions + { + FileOption_Nothing = 0x00, + FileOption_NoRecursive = 0x01, + FileOption_CloseAll = 0x02, + FileOption_Max = 0x04 + }; + + + public class MediaInfo : IDisposable + { + private IntPtr _handle; + + public bool MustUseAnsi { get; set; } + public Encoding Encoding { get; set; } + + public MediaInfo() + { + _handle = MediaInfo_New(); + + InitializeEncoding(); + } + + ~MediaInfo() + { + MediaInfo_Delete(_handle); + } + + public void Dispose() + { + MediaInfo_Delete(_handle); + GC.SuppressFinalize(this); + } + + private void InitializeEncoding() + { + if (Environment.OSVersion.ToString().IndexOf("Windows") != -1) + { + // Windows guaranteed UCS-2 + MustUseAnsi = false; + Encoding = Encoding.Unicode; + } + else + { + // Linux normally UCS-4. As fallback we try UCS-2 and plain Ansi. + MustUseAnsi = false; + Encoding = Encoding.UTF32; + + if (Option("Info_Version", "").StartsWith("MediaInfoLib")) + { + return; + } + + Encoding = Encoding.Unicode; + + if (Option("Info_Version", "").StartsWith("MediaInfoLib")) + { + return; + } + + MustUseAnsi = true; + Encoding = Encoding.Default; + + if (Option("Info_Version", "").StartsWith("MediaInfoLib")) + { + return; + } + + throw new NotSupportedException("Unsupported MediaInfoLib encoding"); + } + } + + private IntPtr MakeStringParameter(string value) + { + var buffer = Encoding.GetBytes(value); + + Array.Resize(ref buffer, buffer.Length + 4); + + var buf = Marshal.AllocHGlobal(buffer.Length); + Marshal.Copy(buffer, 0, buf, buffer.Length); + + return buf; + } + + private string MakeStringResult(IntPtr value) + { + if (Encoding == Encoding.Unicode) + { + return Marshal.PtrToStringUni(value); + } + else if (Encoding == Encoding.UTF32) + { + int i = 0; + for (; i < 1024; i += 4) + { + var data = Marshal.ReadInt32(value, i); + if (data == 0) + { + break; + } + } + + var buffer = new byte[i]; + Marshal.Copy(value, buffer, 0, i); + + return Encoding.GetString(buffer, 0, i); + } + else + { + return Marshal.PtrToStringAnsi(value); + } + } + + public int Open(string fileName) + { + var pFileName = MakeStringParameter(fileName); + try + { + if (MustUseAnsi) + { + return (int)MediaInfoA_Open(_handle, pFileName); + } + else + { + return (int)MediaInfo_Open(_handle, pFileName); + } + } + finally + { + Marshal.FreeHGlobal(pFileName); + } + } + + public int Open(Stream stream) + { + var buffer = new byte[64 * 1024]; + + var isValid = (int)MediaInfo_Open_Buffer_Init(_handle, stream.Length, 0); + if (isValid == 1) + { + int bufferRead; + + do + { + bufferRead = stream.Read(buffer, 0, buffer.Length); + + if (MediaInfo_Open_Buffer_Continue(_handle, buffer, (IntPtr)bufferRead) == (IntPtr)0) + { + break; + } + + var seekPos = MediaInfo_Open_Buffer_Continue_GoTo_Get(_handle); + if (seekPos != -1) + { + seekPos = stream.Seek(seekPos, SeekOrigin.Begin); + MediaInfo_Open_Buffer_Init(_handle, stream.Length, seekPos); + } + } while (bufferRead > 0); + + MediaInfo_Open_Buffer_Finalize(_handle); + } + + return isValid; + } + + public void Close() + { + MediaInfo_Close(_handle); + } + + public string Get(StreamKind streamKind, int streamNumber, string parameter, InfoKind infoKind = InfoKind.Text, InfoKind searchKind = InfoKind.Name) + { + var pParameter = MakeStringParameter(parameter); + try + { + if (MustUseAnsi) + { + return MakeStringResult(MediaInfoA_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, pParameter, (IntPtr)infoKind, (IntPtr)searchKind)); + } + else + { + return MakeStringResult(MediaInfo_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, pParameter, (IntPtr)infoKind, (IntPtr)searchKind)); + } + } + finally + { + Marshal.FreeHGlobal(pParameter); + } + } + + public string Get(StreamKind streamKind, int streamNumber, int parameter, InfoKind infoKind) + { + if (MustUseAnsi) + { + return MakeStringResult(MediaInfoA_GetI(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, (IntPtr)parameter, (IntPtr)infoKind)); + } + else + { + return MakeStringResult(MediaInfo_GetI(_handle, (IntPtr)streamKind, (IntPtr)streamNumber, (IntPtr)parameter, (IntPtr)infoKind)); + } + } + + public String Option(String option, String value) + { + var pOption = MakeStringParameter(option); + var pValue = MakeStringParameter(value); + try + { + if (MustUseAnsi) + { + return MakeStringResult(MediaInfoA_Option(_handle, pOption, pValue)); + } + else + { + return MakeStringResult(MediaInfo_Option(_handle, pOption, pValue)); + } + } + finally + { + Marshal.FreeHGlobal(pOption); + Marshal.FreeHGlobal(pValue); + } + } + + public int State_Get() + { + return (int)MediaInfo_State_Get(_handle); + } + + public int Count_Get(StreamKind streamKind, int streamNumber = -1) + { + return (int)MediaInfo_Count_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber); + } + + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_New(); + [DllImport("MediaInfo.dll")] + private static extern void MediaInfo_Delete(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Open(IntPtr handle, IntPtr fileName); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Open_Buffer_Init(IntPtr handle, Int64 fileSize, Int64 fileOffset); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize); + [DllImport("MediaInfo.dll")] + private static extern Int64 MediaInfo_Open_Buffer_Continue_GoTo_Get(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Open_Buffer_Finalize(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern void MediaInfo_Close(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Option(IntPtr handle, IntPtr option, IntPtr value); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_State_Get(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfo_Count_Get(IntPtr handle, IntPtr StreamKind, IntPtr streamNumber); + + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_New(); + [DllImport("MediaInfo.dll")] + private static extern void MediaInfoA_Delete(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Open(IntPtr handle, IntPtr fileName); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Open_Buffer_Init(IntPtr handle, Int64 fileSize, Int64 fileOffset); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize); + [DllImport("MediaInfo.dll")] + private static extern Int64 MediaInfoA_Open_Buffer_Continue_GoTo_Get(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Open_Buffer_Finalize(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern void MediaInfoA_Close(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Option(IntPtr handle, IntPtr option, IntPtr value); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_State_Get(IntPtr handle); + [DllImport("MediaInfo.dll")] + private static extern IntPtr MediaInfoA_Count_Get(IntPtr handle, IntPtr StreamKind, IntPtr streamNumber); + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs index 85666bde3..e43b3cf48 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs @@ -2,7 +2,6 @@ using System.Globalization; using System.IO; using System.Text; -using MediaInfoLib; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -33,29 +32,16 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo if (!_diskProvider.FileExists(filename)) throw new FileNotFoundException("Media file does not exist: " + filename); - MediaInfoLib.MediaInfo mediaInfo = null; + MediaInfo mediaInfo = null; try { - mediaInfo = new MediaInfoLib.MediaInfo(); + mediaInfo = new MediaInfo(); _logger.Debug("Getting media info from {0}", filename); mediaInfo.Option("ParseSpeed", "0.2"); - int open; - if (OsInfo.IsWindows) - { - open = mediaInfo.Open(filename); - } - else - { - mediaInfo.Option("CharSet", "UTF-8"); - - // On non-Windows the wrapper uses the ansi library methods, which libmediainfo converts internally to unicode from multibyte (utf8). - // To avoid building MediaInfoDotNet ourselves we simply trick the wrapper to send utf8 strings instead of ansi. - var utf8filename = Encoding.Default.GetString(Encoding.UTF8.GetBytes(filename)); - open = mediaInfo.Open(utf8filename); - } + int open = mediaInfo.Open(_diskProvider.OpenReadStream(filename)); if (open != 0) { diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index e09d61b9e..071cc4746 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -93,9 +93,6 @@ - - ..\packages\MediaInfoNet.0.3\lib\MediaInfoDotNet.dll - ..\packages\NLog.2.1.0\lib\net40\NLog.dll @@ -606,6 +603,7 @@ Code + @@ -940,6 +938,9 @@ + + Always + diff --git a/src/NzbDrone.Core/NzbDrone.Core.dll.config b/src/NzbDrone.Core/NzbDrone.Core.dll.config new file mode 100644 index 000000000..a139791b4 --- /dev/null +++ b/src/NzbDrone.Core/NzbDrone.Core.dll.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/NzbDrone.Core/packages.config b/src/NzbDrone.Core/packages.config index b7a2989ee..4d6664b8a 100644 --- a/src/NzbDrone.Core/packages.config +++ b/src/NzbDrone.Core/packages.config @@ -4,7 +4,6 @@ -