From ab040f5aeba2ddace1086d7d05cccb35ef7c9800 Mon Sep 17 00:00:00 2001 From: marihachi Date: Fri, 1 Jun 2018 01:55:07 +0900 Subject: [PATCH 1/3] clean --- .../AlbumArtExtraction.CommandLine.csproj | 67 -------- AlbumArtExtraction.CommandLine/App.config | 6 - AlbumArtExtraction.CommandLine/Program.cs | 93 ----------- .../Properties/AssemblyInfo.cs | 36 ----- AlbumArtExtraction.sln | 28 ---- AlbumArtExtraction/AlbumArtExtraction.csproj | 62 -------- AlbumArtExtraction/AlbumArtExtraction.nuspec | 18 --- .../DirectoryAlbumArtExtractor.cs | 51 ------ AlbumArtExtraction/FlacAlbumArtExtractor.cs | 145 ------------------ AlbumArtExtraction/IAlbumArtExtractor.cs | 11 -- AlbumArtExtraction/ID3v22AlbumArtExtractor.cs | 105 ------------- AlbumArtExtraction/ID3v2AlbumArtExtractor.cs | 127 --------------- AlbumArtExtraction/Properties/AssemblyInfo.cs | 36 ----- AlbumArtExtraction/Selector.cs | 35 ----- AlbumArtExtraction/StreamExtensions.cs | 81 ---------- 15 files changed, 901 deletions(-) delete mode 100644 AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj delete mode 100644 AlbumArtExtraction.CommandLine/App.config delete mode 100644 AlbumArtExtraction.CommandLine/Program.cs delete mode 100644 AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs delete mode 100644 AlbumArtExtraction.sln delete mode 100644 AlbumArtExtraction/AlbumArtExtraction.csproj delete mode 100644 AlbumArtExtraction/AlbumArtExtraction.nuspec delete mode 100644 AlbumArtExtraction/DirectoryAlbumArtExtractor.cs delete mode 100644 AlbumArtExtraction/FlacAlbumArtExtractor.cs delete mode 100644 AlbumArtExtraction/IAlbumArtExtractor.cs delete mode 100644 AlbumArtExtraction/ID3v22AlbumArtExtractor.cs delete mode 100644 AlbumArtExtraction/ID3v2AlbumArtExtractor.cs delete mode 100644 AlbumArtExtraction/Properties/AssemblyInfo.cs delete mode 100644 AlbumArtExtraction/Selector.cs delete mode 100644 AlbumArtExtraction/StreamExtensions.cs diff --git a/AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj b/AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj deleted file mode 100644 index 2173182..0000000 --- a/AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj +++ /dev/null @@ -1,67 +0,0 @@ - - - - - Debug - AnyCPU - {78712AFD-200B-4EBE-83D6-900AD6987343} - Exe - Properties - AlbumArtExtraction.CommandLine - AlbumArtExtraction.CommandLine - v4.6.1 - 512 - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - {89bd928a-0196-47d5-be87-9deb0947b9a5} - AlbumArtExtraction - - - - - \ No newline at end of file diff --git a/AlbumArtExtraction.CommandLine/App.config b/AlbumArtExtraction.CommandLine/App.config deleted file mode 100644 index bae5d6d..0000000 --- a/AlbumArtExtraction.CommandLine/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AlbumArtExtraction.CommandLine/Program.cs b/AlbumArtExtraction.CommandLine/Program.cs deleted file mode 100644 index 85329ef..0000000 --- a/AlbumArtExtraction.CommandLine/Program.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; - -namespace AlbumArtExtraction.CommandLine { - class Program { - static void Main(string[] args) { - var optionArgs = - from arg in args - where arg.StartsWith("-") - select arg.Substring(1); - - var mainArgs = - from arg in args - where !arg.StartsWith("-") - select arg; - - // a value indicating whether to overwrite - var noConfirm = optionArgs.FirstOrDefault(i => i == "y") != null; - - string inputPath, outputPath; - - // inputPath - if (mainArgs.Count() == 1) { - inputPath = mainArgs.ElementAt(0); - outputPath = Path.Combine(Path.GetDirectoryName(inputPath), Path.GetFileNameWithoutExtension(inputPath)); - } - // inputPath, outputPath - else if (mainArgs.Count() == 2) { - inputPath = mainArgs.ElementAt(0); - outputPath = mainArgs.ElementAt(1); - } - else { - Usage(); - return; - } - - try { - var selector = new Selector(); - var extractor = selector.SelectAlbumArtExtractor(inputPath); - Console.WriteLine($"selected extractor: {extractor}"); - - using (var albumArt = extractor.Extract(inputPath)) { - var format = albumArt.RawFormat; - outputPath += (format == ImageFormat.Png) ? ".png" : ".jpg"; - if (!noConfirm && File.Exists(outputPath)) { - Console.Write("file name already exists. do you want to overwrite it? (y/n) "); - var input = Console.ReadLine(); - if (!input.ToLower().StartsWith("y")) { - return; - } - } - albumArt.Save(outputPath); - } - - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"file creation succeeded: {outputPath}"); - Console.ResetColor(); - } - catch(FileNotFoundException) { - Error(() => Console.WriteLine($"input file is not found: {inputPath}")); - } - catch(NotSupportedException ex) { - Error(() => { - Console.WriteLine($"format of input file is not supported:"); - Console.WriteLine(ex); - }); - } - catch(Exception ex) { - Error(() => { - Console.WriteLine($"error:"); - Console.WriteLine(ex); - }); - } - } - - static void Error(Action content) { - Console.ForegroundColor = ConsoleColor.Red; - content(); - Console.ResetColor(); - } - - static void Usage() { - Console.WriteLine(); - Console.WriteLine("Usage:"); - Console.WriteLine(); - Console.WriteLine("[-y] inputPath [ outputPath ]"); - Console.WriteLine(); - Console.WriteLine("-y : Overwrite existing file"); - } - } -} diff --git a/AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs b/AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs deleted file mode 100644 index 9a88b11..0000000 --- a/AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 -// アセンブリに関連付けられている情報を変更するには、 -// これらの属性値を変更してください。 -[assembly: AssemblyTitle("AlbumArtExtraction.CommandLine")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlbumArtExtraction.CommandLine")] -[assembly: AssemblyCopyright("Copyright © 2017 Legato-Dev")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから -// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、 -// その型の ComVisible 属性を true に設定してください。 -[assembly: ComVisible(false)] - -// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります -[assembly: Guid("78712afd-200b-4ebe-83d6-900ad6987343")] - -// アセンブリのバージョン情報は次の 4 つの値で構成されています: -// -// メジャー バージョン -// マイナー バージョン -// ビルド番号 -// Revision -// -// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を -// 既定値にすることができます: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AlbumArtExtraction.sln b/AlbumArtExtraction.sln deleted file mode 100644 index b31a6cc..0000000 --- a/AlbumArtExtraction.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlbumArtExtraction", "AlbumArtExtraction\AlbumArtExtraction.csproj", "{89BD928A-0196-47D5-BE87-9DEB0947B9A5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlbumArtExtraction.CommandLine", "AlbumArtExtraction.CommandLine\AlbumArtExtraction.CommandLine.csproj", "{78712AFD-200B-4EBE-83D6-900AD6987343}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {89BD928A-0196-47D5-BE87-9DEB0947B9A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89BD928A-0196-47D5-BE87-9DEB0947B9A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89BD928A-0196-47D5-BE87-9DEB0947B9A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89BD928A-0196-47D5-BE87-9DEB0947B9A5}.Release|Any CPU.Build.0 = Release|Any CPU - {78712AFD-200B-4EBE-83D6-900AD6987343}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {78712AFD-200B-4EBE-83D6-900AD6987343}.Debug|Any CPU.Build.0 = Debug|Any CPU - {78712AFD-200B-4EBE-83D6-900AD6987343}.Release|Any CPU.ActiveCfg = Release|Any CPU - {78712AFD-200B-4EBE-83D6-900AD6987343}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/AlbumArtExtraction/AlbumArtExtraction.csproj b/AlbumArtExtraction/AlbumArtExtraction.csproj deleted file mode 100644 index 2820fd7..0000000 --- a/AlbumArtExtraction/AlbumArtExtraction.csproj +++ /dev/null @@ -1,62 +0,0 @@ - - - - - Debug - AnyCPU - {89BD928A-0196-47D5-BE87-9DEB0947B9A5} - Library - Properties - AlbumArtExtraction - AlbumArtExtraction - v4.6.1 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\AlbumArtExtraction.xml - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/AlbumArtExtraction/AlbumArtExtraction.nuspec b/AlbumArtExtraction/AlbumArtExtraction.nuspec deleted file mode 100644 index af7d265..0000000 --- a/AlbumArtExtraction/AlbumArtExtraction.nuspec +++ /dev/null @@ -1,18 +0,0 @@ - - - - AlbumArtExtraction - 0.1.0 - Legato-Dev - https://github.com/Legato-Dev/AlbumArtExtraction/blob/master/LICENSE - https://github.com/Legato-Dev/AlbumArtExtraction - https://github.com/Legato-Dev/AlbumArtExtraction/raw/master/logo64.png - true - A .NET library extracts album art from metadata such as FLAC, ID3, etc. - ID3 MP3 FLAC AlbumArt Extraction NowPlaying CSharp - - - - - - \ No newline at end of file diff --git a/AlbumArtExtraction/DirectoryAlbumArtExtractor.cs b/AlbumArtExtraction/DirectoryAlbumArtExtractor.cs deleted file mode 100644 index 853f547..0000000 --- a/AlbumArtExtraction/DirectoryAlbumArtExtractor.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Linq; - -namespace AlbumArtExtraction { - /// - /// ディレクトリからアルバムアートを抽出する機能を表します - /// - public class DirectoryAlbumArtExtractor : IAlbumArtExtractor { - /// - /// アルバムアートのようなファイル名のFileInfo一覧を取得します - /// - /// - private IEnumerable _GetFilesLikeAlbumArts(DirectoryInfo directory) => - from i in directory.GetFiles() - where i.Extension == ".png" || i.Extension == ".jpeg" || i.Extension == ".jpg" || i.Extension == ".bmp" - where - i.Name.IndexOf(Path.GetFileNameWithoutExtension(i.Name)) != -1 || - i.Name.IndexOf("folder") != -1 || - i.Name.IndexOf("front") != -1 || - i.Name.IndexOf("cover") != -1 || - i.Name.IndexOf("album") != -1 - orderby i.Length descending - select i; - - /// - /// 対象のファイルが形式と一致しているかを判別します - /// - public bool CheckType(string filePath) { - var fileCount = new FileInfo(filePath).Directory.EnumerateFiles().Count(); - var albumArtCount = _GetFilesLikeAlbumArts(new FileInfo(filePath).Directory).Count(); - - // ディレクトリのファイル数が50個以下(間違ったアルバムアートが設定されることへの防止) & アルバムアートの画像ファイルがディレクトリにある - return fileCount <= 50 && albumArtCount > 0; - } - - /// - /// アルバムアートを抽出します - /// - /// - public Image Extract(string filePath) { - if (!File.Exists(filePath)) - throw new FileNotFoundException("指定されたファイルは存在しません"); - - var fileInfo = _GetFilesLikeAlbumArts(new FileInfo(filePath).Directory).ElementAt(0); - - return Image.FromFile(fileInfo.FullName); - } - } -} diff --git a/AlbumArtExtraction/FlacAlbumArtExtractor.cs b/AlbumArtExtraction/FlacAlbumArtExtractor.cs deleted file mode 100644 index a6ee16f..0000000 --- a/AlbumArtExtraction/FlacAlbumArtExtractor.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; - -namespace AlbumArtExtraction { - /// - /// FLAC形式のファイルからアルバムアートを抽出する機能を表します - /// - public class FlacAlbumArtExtractor : IAlbumArtExtractor { - /// - /// メタデータブロックを読み取ります - /// - /// 対象の Stream - private MetaData _ReadMetaDataBlock(Stream stream) { - var isLastAndMetaDataType = stream.ReadAsByte(); - - // これが最後のメタデータブロックかどうかを示す値 - var isLast = (isLastAndMetaDataType & 0x80U) != 0; - - var metaDataType = (isLastAndMetaDataType & 0x7FU); - - var metaDataLength = stream.ReadAsUInt(3); - if (metaDataLength == 0) - throw new InvalidDataException("metaDataLength が不正です"); - - // Pictureタイプ以外はストリームから読み取らずにスキップする - List metaData; - if (metaDataType == 6) { - metaData = stream.ReadAsByteList((int)metaDataLength); - } - else { - metaData = null; - stream.Skip((int)metaDataLength); - } - - return new MetaData((MetaDataType)metaDataType, isLast, metaData); - } - - /// - /// PICTUREタイプのメタデータから Image を取り出します - /// - private Image _ParsePictureMetaData(MetaData pictureMetaData) { - if (pictureMetaData.Type != MetaDataType.PICTURE) - throw new ArgumentException("このメタデータはPICTUREタイプではありません"); - - List imageSource; - using (var memory = new MemoryStream(pictureMetaData.Data.ToArray())) { - memory.Skip(4); - var mimeTypeLength = memory.ReadAsUInt(); - if (mimeTypeLength > 128) - throw new InvalidDataException("mimeTypeLength が不正な値です"); - - memory.Skip((int)mimeTypeLength); - var explanationLength = memory.ReadAsUInt(); - - memory.Skip((int)explanationLength + 4 * 4); - var imageSourceSize = memory.ReadAsUInt(); - imageSource = memory.ReadAsByteList((int)imageSourceSize); - } - - using (var memory = new MemoryStream(imageSource.ToArray())) - using (var image = Image.FromStream(memory)) - return new Bitmap(image); - } - - /// - /// 対象のファイルが形式と一致しているかを判別します - /// - public bool CheckType(string filePath) { - using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { - var formatId = file.ReadAsAsciiString(4); - - if (formatId != "fLaC") - return false; - } - - try - { - if (Extract(filePath) != null) - return true; - } - catch - { - // noop - } - - return false; - } - - /// - /// アルバムアートを抽出します - /// - /// - /// - public Image Extract(string filePath) { - if (!File.Exists(filePath)) - throw new FileNotFoundException("指定されたファイルは存在しません"); - - using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { - file.Skip(4); - - var metaDataList = new List(); - MetaData metaData = null; - do { - metaDataList.Add(metaData = _ReadMetaDataBlock(file)); - } - while (!metaData.IsLast && metaDataList.Count < 64); - - if (metaDataList.Count >= 64) - throw new InvalidDataException("メタデータの個数が異常です"); - - var picture = metaDataList.Find(i => i.Type == MetaDataType.PICTURE); - - return (picture != null) ? _ParsePictureMetaData(picture) : null; - } - } - - public class MetaData { - public MetaData(MetaDataType type, bool isLast, List data) { - Type = type; - IsLast = isLast; - Data = data; - } - - public MetaDataType Type { get; set; } - - public bool IsLast { get; set; } - - public List Data { get; set; } - - public override string ToString() => $"FlacMetaData {{ Type = {Type}, IsLast = {IsLast}, DataSize = {Data.Count} }}"; - } - - public enum MetaDataType { - STREAMINFO = 0, - PADDING = 1, - APPLICATION = 2, - SEEKTABLE = 3, - VORBIS_COMMENT = 4, - CUESHEET = 5, - PICTURE = 6 - } - } -} diff --git a/AlbumArtExtraction/IAlbumArtExtractor.cs b/AlbumArtExtraction/IAlbumArtExtractor.cs deleted file mode 100644 index 6791afb..0000000 --- a/AlbumArtExtraction/IAlbumArtExtractor.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Drawing; - -namespace AlbumArtExtraction { - /// - /// アルバムアートを抽出するために必要となるメンバを公開します - /// - public interface IAlbumArtExtractor { - bool CheckType(string filePath); - Image Extract(string filePath); - } -} diff --git a/AlbumArtExtraction/ID3v22AlbumArtExtractor.cs b/AlbumArtExtraction/ID3v22AlbumArtExtractor.cs deleted file mode 100644 index c685f19..0000000 --- a/AlbumArtExtraction/ID3v22AlbumArtExtractor.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Text.RegularExpressions; - -namespace AlbumArtExtraction -{ - /// - /// mp3形式(ID3v2.2) のファイルからアルバムアートを抽出する機能を表します - /// - public class ID3v22AlbumArtExtractor : IAlbumArtExtractor - { - - #region Constractor - - public ID3v22AlbumArtExtractor() { } - - #endregion - - #region Parsing picture for ID3Tag. - - /// - /// ID3v2.2 タグから Image を取り出します - /// - private Image _ReadPictureInFrameHeaders(Stream file) - { - var count = 0; - while (count++ < 63) - { - // Frame Name - var frameName = file.ReadAsAsciiString(3); - - // 有効な Frame Name であるかどうかを示す - var validName = Regex.IsMatch(frameName, "^[A-Z0-9]+$"); - - // 無効な Frame Name であれば、ループ終了 - if (!validName) break; - - Debug.WriteLine($"frameName = {frameName}"); - var frameSize = file.ReadAsUInt(3); - - // PIC Frame の判定 - if (frameName == "PIC") - { - // 1Byte: 文字コード, 3Byte: フォーマット, 1Byte: 種別は必ず存在する為、読み飛ばす - file.Skip(5); - - // 説明文を読み飛ばす (終端文字含む) - var length = 1; - while ((file.ReadAsByte() != 0x00U)) - length++; - - var imageSource = file.ReadAsByteList((int)frameSize - (5 + length)); - - // byte データを画像として変換する - using (var memory = new MemoryStream()) - { - memory.Write(imageSource.ToArray(), 0, imageSource.Count); - - using (var image = Image.FromStream(memory)) - return new Bitmap(image); - } - } - - // PIC Frame でないため、フレーム自体を読み飛ばす - file.Skip((int)frameSize); - } - - return null; - } - - #endregion - - #region Interface - IAlbumArtExtractor - - /// - /// 対象のファイルが形式と一致しているかを判別します - /// - public bool CheckType(string filePath) - { - using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - return (file.ReadAsAsciiString(3) == "ID3" && file.ReadAsUShort() == 0x0200U); - } - - /// - /// アルバムアートを抽出します - /// - public Image Extract(string filePath) - { - using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - // ID3v2 Header 読み飛ばし - file.Skip(10); - - // v2.2 に関しては、ID3 Extended Header や、そのフラグは存在しない模様。 - - // Frame Headers - return _ReadPictureInFrameHeaders(file); - } - } - - #endregion - - } -} diff --git a/AlbumArtExtraction/ID3v2AlbumArtExtractor.cs b/AlbumArtExtraction/ID3v2AlbumArtExtractor.cs deleted file mode 100644 index 709a42f..0000000 --- a/AlbumArtExtraction/ID3v2AlbumArtExtractor.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Text.RegularExpressions; - -namespace AlbumArtExtraction -{ - /// - /// mp3形式(ID3v2.3/2.4) のファイルからアルバムアートを抽出する機能を表します - /// 拡張ヘッダーのあるファイルには利用されません - /// - public class ID3v2AlbumArtExtractor : IAlbumArtExtractor - { - - #region Constractor - - public ID3v2AlbumArtExtractor() { } - - #endregion - - #region Parsing picture for ID3Tag. - - /// - /// ID3v2.3/2.4 タグから Image を取り出します - /// - private Image _ReadPictureInFrameHeaders(Stream file) - { - var count = 1; - - while (true) - { - // Frame Name - var frameName = file.ReadAsAsciiString(4); - - // 有効な Frame Name であるかどうかを示す - var validName = Regex.IsMatch(frameName, "^[A-Z0-9]+$"); - - // 無効な Frame Name であれば、ループ終了 - if (!validName) break; - - Debug.WriteLine($"frameName = {frameName}"); - var frameSize = file.ReadAsUInt(); - - // フラグ読み飛ばし - file.Skip(2); - - // APIC Frame の判定 - if (frameName == "APIC") - { - var removeCount = 0; - - file.Skip(1); - removeCount += 1; - - while (file.ReadAsByte() != 0x00U) removeCount++; - - file.Skip(1); - removeCount += 1; - - while (file.ReadAsByte() != 0x00U) removeCount++; - - var imageSource = file.ReadAsByteList((int)frameSize - removeCount); - - // byte データを画像として変換する - using (var memory = new MemoryStream()) - { - memory.Write(imageSource.ToArray(), 0, imageSource.Count); - using (var image = Image.FromStream(memory)) - return new Bitmap(image); - } - } - - // PIC Frame でないため、フレーム自体を読み飛ばす - file.Skip((int)frameSize); - - if (count > 74) - throw new InvalidDataException("フレーム数が正常な範囲を超えました"); - - count++; - } - - return null; - } - - #endregion - - #region Interface - IAlbumArtExtractor - - /// - /// 対象のファイルが形式と一致しているかを判別します - /// - public bool CheckType(string filePath) - { - using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - var formatId = file.ReadAsAsciiString(3); - var version = file.ReadAsUShort(); - var headerFlag = file.ReadAsByte(); - - // フォーマット判定 - if (!(formatId == "ID3" && (version == 0x0300U || version == 0x0400U))) - return false; - - // extended header が無い場合のみ一致と判定 - return (headerFlag & 0x0040U) == 0; - } - } - - /// - /// アルバムアートを抽出します - /// - public Image Extract(string filePath) - { - using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - // ID3v2 Header 読み飛ばし - file.Skip(10); - - // Frame Headers - return _ReadPictureInFrameHeaders(file); - } - } - - #endregion - - } -} diff --git a/AlbumArtExtraction/Properties/AssemblyInfo.cs b/AlbumArtExtraction/Properties/AssemblyInfo.cs deleted file mode 100644 index 36c3d27..0000000 --- a/AlbumArtExtraction/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 -// アセンブリに関連付けられている情報を変更するには、 -// これらの属性値を変更してください。 -[assembly: AssemblyTitle("AlbumArtExtraction")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlbumArtExtraction")] -[assembly: AssemblyCopyright("Copyright © 2017 Legato-Dev")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから -// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、 -// その型の ComVisible 属性を true に設定してください。 -[assembly: ComVisible(false)] - -// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります -[assembly: Guid("89bd928a-0196-47d5-be87-9deb0947b9a5")] - -// アセンブリのバージョン情報は次の 4 つの値で構成されています: -// -// メジャー バージョン -// マイナー バージョン -// ビルド番号 -// Revision -// -// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を -// 既定値にすることができます: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] diff --git a/AlbumArtExtraction/Selector.cs b/AlbumArtExtraction/Selector.cs deleted file mode 100644 index c9c39f0..0000000 --- a/AlbumArtExtraction/Selector.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace AlbumArtExtraction { - public class Selector { - public Selector() { } - - /// - /// 対象のファイルパスに対して利用可能な AlbumArtExtractor を選択して取得します - /// - /// アルバムアート抽出の対象ファイル - /// - /// - public IAlbumArtExtractor SelectAlbumArtExtractor(string filePath) { - //if (!File.Exists(filePath)) - // throw new ArgumentException("指定されたファイルパスは無効です", "filePath"); - if (!File.Exists(filePath)) - throw new FileNotFoundException("指定されたファイルは存在しません"); - - var extractors = new List { - new FlacAlbumArtExtractor(), - new ID3v2AlbumArtExtractor(), - new ID3v22AlbumArtExtractor(), - new DirectoryAlbumArtExtractor() - }; - var extractor = extractors.Find(i => i.CheckType(filePath)); - - if (extractor == null) - throw new NotSupportedException("指定されたファイルからAlbumArtを抽出する方法が見つかりませんでした"); - - return extractor; - } - } -} diff --git a/AlbumArtExtraction/StreamExtensions.cs b/AlbumArtExtraction/StreamExtensions.cs deleted file mode 100644 index 6aa4832..0000000 --- a/AlbumArtExtraction/StreamExtensions.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace AlbumArtExtraction -{ - public static class StreamExtensions - { - /// - /// 指定したバイト数のデータを読み飛ばします - /// - /// 読み飛ばすバイト数 - public static void Skip(this Stream stream, int skip) - { - if (skip > 0) - stream.Seek(skip, SeekOrigin.Current); - } - - /// - /// 指定した長さのデータを List<byte> として読み取ります - /// - /// 読み取るデータの長さ(バイト数) - public static List ReadAsByteList(this Stream stream, int count) - { - var buf = new byte[count]; - stream.Read(buf, 0, count); - - return new List(buf); - } - - /// - /// byte(1 Bytes) として読み取ります - /// - public static byte ReadAsByte(this Stream stream) => - stream.ReadAsByteList(1)[0]; - - private static byte[] _ReadAsUIntXBase(this Stream stream, int returnSize, int count) - { - if (count < 1 || count > returnSize) - throw new ArgumentOutOfRangeException("count"); - - var buf = stream.ReadAsByteList(count); - - // 配列にゼロパディングを追加 - foreach (var i in Enumerable.Range(0, returnSize - count)) - buf.Insert(0, 0); - - buf.Reverse(); - - return buf.ToArray(); - } - - /// - /// 指定した長さのデータを ushort(2 Bytes) として読み取ります(ビッグエンディアン) - /// - /// 読み取るデータの長さ(バイト数) 範囲: 1-2 - /// - public static ushort ReadAsUShort(this Stream stream, int count = 2) => - BitConverter.ToUInt16(stream._ReadAsUIntXBase(2, count), 0); - - /// - /// 指定した長さのデータを uint(4 Bytes) として読み取ります(ビッグエンディアン) - /// - /// 読み取るデータの長さ(バイト数) 範囲: 1-4 - /// - public static uint ReadAsUInt(this Stream stream, int count = 4) => - BitConverter.ToUInt32(stream._ReadAsUIntXBase(4, count), 0); - - /// - /// ASCII文字列として指定されたカウント数だけ読み取ります - /// - public static string ReadAsAsciiString(this Stream stream, int count) - { - var bytes = stream.ReadAsByteList(count).ToArray(); - - return new string(Encoding.ASCII.GetChars(bytes)); - } - } -} From 5c9c54b4a96b8ad6f93809aac227f1c472deb15a Mon Sep 17 00:00:00 2001 From: marihachi Date: Fri, 1 Jun 2018 01:59:08 +0900 Subject: [PATCH 2/3] add projects (.net standard) --- .../AlbumArtExtraction.CommandLine.csproj | 64 ++++++++ AlbumArtExtraction.CommandLine/App.config | 9 ++ AlbumArtExtraction.CommandLine/Program.cs | 110 ++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++ AlbumArtExtraction.sln | 31 ++++ AlbumArtExtraction/AlbumArtExtraction.csproj | 7 + .../DirectoryAlbumArtExtractor.cs | 55 +++++++ AlbumArtExtraction/FlacAlbumArtExtractor.cs | 142 ++++++++++++++++++ AlbumArtExtraction/IAlbumArtExtractor.cs | 11 ++ AlbumArtExtraction/ID3v22AlbumArtExtractor.cs | 97 ++++++++++++ AlbumArtExtraction/ID3v2AlbumArtExtractor.cs | 120 +++++++++++++++ AlbumArtExtraction/Selector.cs | 35 +++++ AlbumArtExtraction/StreamExtensions.cs | 81 ++++++++++ 13 files changed, 798 insertions(+) create mode 100644 AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj create mode 100644 AlbumArtExtraction.CommandLine/App.config create mode 100644 AlbumArtExtraction.CommandLine/Program.cs create mode 100644 AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs create mode 100644 AlbumArtExtraction.sln create mode 100644 AlbumArtExtraction/AlbumArtExtraction.csproj create mode 100644 AlbumArtExtraction/DirectoryAlbumArtExtractor.cs create mode 100644 AlbumArtExtraction/FlacAlbumArtExtractor.cs create mode 100644 AlbumArtExtraction/IAlbumArtExtractor.cs create mode 100644 AlbumArtExtraction/ID3v22AlbumArtExtractor.cs create mode 100644 AlbumArtExtraction/ID3v2AlbumArtExtractor.cs create mode 100644 AlbumArtExtraction/Selector.cs create mode 100644 AlbumArtExtraction/StreamExtensions.cs diff --git a/AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj b/AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj new file mode 100644 index 0000000..712e37f --- /dev/null +++ b/AlbumArtExtraction.CommandLine/AlbumArtExtraction.CommandLine.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {442F1894-255A-4012-B20D-89A4AF86CC1C} + Exe + AlbumArtExtraction.CommandLine + AlbumArtExtraction.CommandLine + v4.7.1 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + Designer + + + + + {43b10c95-8bc7-4191-bc76-b7cce291af84} + AlbumArtExtraction + + + + \ No newline at end of file diff --git a/AlbumArtExtraction.CommandLine/App.config b/AlbumArtExtraction.CommandLine/App.config new file mode 100644 index 0000000..bd88830 --- /dev/null +++ b/AlbumArtExtraction.CommandLine/App.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/AlbumArtExtraction.CommandLine/Program.cs b/AlbumArtExtraction.CommandLine/Program.cs new file mode 100644 index 0000000..9d1cd90 --- /dev/null +++ b/AlbumArtExtraction.CommandLine/Program.cs @@ -0,0 +1,110 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; + +namespace AlbumArtExtraction.CommandLine +{ + class Program + { + static void Main(string[] args) + { + var optionArgs = + from arg in args + where arg.StartsWith("-") + select arg.Substring(1); + + var mainArgs = + from arg in args + where !arg.StartsWith("-") + select arg; + + // a value indicating whether to overwrite + var noConfirm = optionArgs.FirstOrDefault(i => i == "y") != null; + + string inputPath, outputPath; + + // inputPath + if (mainArgs.Count() == 1) + { + inputPath = mainArgs.ElementAt(0); + outputPath = Path.Combine(Path.GetDirectoryName(inputPath), Path.GetFileNameWithoutExtension(inputPath)); + } + // inputPath, outputPath + else if (mainArgs.Count() == 2) + { + inputPath = mainArgs.ElementAt(0); + outputPath = mainArgs.ElementAt(1); + } + else + { + Usage(); + return; + } + + try + { + var selector = new Selector(); + var extractor = selector.SelectAlbumArtExtractor(inputPath); + Console.WriteLine($"selected extractor: {extractor}"); + + using (var source = extractor.Extract(inputPath)) + { + var albumArt = new Bitmap(source); + var format = albumArt.RawFormat; + outputPath += (format == ImageFormat.Png) ? ".png" : ".jpg"; + if (!noConfirm && File.Exists(outputPath)) + { + Console.Write("file name already exists. do you want to overwrite it? (y/n) "); + var input = Console.ReadLine(); + if (!input.ToLower().StartsWith("y")) + { + return; + } + } + albumArt.Save(outputPath); + } + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"file creation succeeded: {outputPath}"); + Console.ResetColor(); + } + catch (FileNotFoundException) + { + Error(() => Console.WriteLine($"input file is not found: {inputPath}")); + } + catch (NotSupportedException ex) + { + Error(() => { + Console.WriteLine($"format of input file is not supported:"); + Console.WriteLine(ex); + }); + } + catch (Exception ex) + { + Error(() => { + Console.WriteLine($"error:"); + Console.WriteLine(ex); + }); + } + } + + static void Error(Action content) + { + Console.ForegroundColor = ConsoleColor.Red; + content(); + Console.ResetColor(); + } + + static void Usage() + { + Console.WriteLine(); + Console.WriteLine("Usage:"); + Console.WriteLine(); + Console.WriteLine("[-y] inputPath [ outputPath ]"); + Console.WriteLine(); + Console.WriteLine("-y : Overwrite existing file"); + } + } +} diff --git a/AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs b/AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0432b82 --- /dev/null +++ b/AlbumArtExtraction.CommandLine/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("AlbumArtExtraction.Console")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AlbumArtExtraction.Console")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから +// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(false)] + +// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります +[assembly: Guid("442f1894-255a-4012-b20d-89a4af86cc1c")] + +// アセンブリのバージョン情報は次の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// Revision +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AlbumArtExtraction.sln b/AlbumArtExtraction.sln new file mode 100644 index 0000000..e054844 --- /dev/null +++ b/AlbumArtExtraction.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.2043 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlbumArtExtraction", "AlbumArtExtraction\AlbumArtExtraction.csproj", "{43B10C95-8BC7-4191-BC76-B7CCE291AF84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlbumArtExtraction.CommandLine", "AlbumArtExtraction.CommandLine\AlbumArtExtraction.CommandLine.csproj", "{442F1894-255A-4012-B20D-89A4AF86CC1C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {43B10C95-8BC7-4191-BC76-B7CCE291AF84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43B10C95-8BC7-4191-BC76-B7CCE291AF84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43B10C95-8BC7-4191-BC76-B7CCE291AF84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43B10C95-8BC7-4191-BC76-B7CCE291AF84}.Release|Any CPU.Build.0 = Release|Any CPU + {442F1894-255A-4012-B20D-89A4AF86CC1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {442F1894-255A-4012-B20D-89A4AF86CC1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {442F1894-255A-4012-B20D-89A4AF86CC1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {442F1894-255A-4012-B20D-89A4AF86CC1C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DC603A10-E02A-463C-98AB-F1D8A3C0F4E4} + EndGlobalSection +EndGlobal diff --git a/AlbumArtExtraction/AlbumArtExtraction.csproj b/AlbumArtExtraction/AlbumArtExtraction.csproj new file mode 100644 index 0000000..b81db56 --- /dev/null +++ b/AlbumArtExtraction/AlbumArtExtraction.csproj @@ -0,0 +1,7 @@ + + + + netstandard1.3 + + + diff --git a/AlbumArtExtraction/DirectoryAlbumArtExtractor.cs b/AlbumArtExtraction/DirectoryAlbumArtExtractor.cs new file mode 100644 index 0000000..fda07cf --- /dev/null +++ b/AlbumArtExtraction/DirectoryAlbumArtExtractor.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AlbumArtExtraction { + /// + /// ディレクトリからアルバムアートを抽出する機能を表します + /// + public class DirectoryAlbumArtExtractor : IAlbumArtExtractor { + /// + /// アルバムアートのようなファイル名のFileInfo一覧を取得します + /// + /// + private IEnumerable _GetFilesLikeAlbumArts(DirectoryInfo directory) => + from i in directory.GetFiles() + where i.Extension == ".png" || i.Extension == ".jpeg" || i.Extension == ".jpg" || i.Extension == ".bmp" + where + i.Name.IndexOf(Path.GetFileNameWithoutExtension(i.Name)) != -1 || + i.Name.IndexOf("folder") != -1 || + i.Name.IndexOf("front") != -1 || + i.Name.IndexOf("cover") != -1 || + i.Name.IndexOf("album") != -1 + orderby i.Length descending + select i; + + /// + /// 対象のファイルが形式と一致しているかを判別します + /// + public bool CheckType(string filePath) { + var fileCount = new FileInfo(filePath).Directory.EnumerateFiles().Count(); + var albumArtCount = _GetFilesLikeAlbumArts(new FileInfo(filePath).Directory).Count(); + + // ディレクトリのファイル数が50個以下(間違ったアルバムアートが設定されることへの防止) & アルバムアートの画像ファイルがディレクトリにある + return fileCount <= 50 && albumArtCount > 0; + } + + /// + /// アルバムアートを抽出します + /// + /// + public Stream Extract(string filePath) { + if (!File.Exists(filePath)) + throw new FileNotFoundException("指定されたファイルは存在しません"); + + var fileInfo = _GetFilesLikeAlbumArts(new FileInfo(filePath).Directory).ElementAt(0); + + using (var file = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read)) + { + var buf = new byte[file.Length]; + file.Read(buf, 0, buf.Length); + return new MemoryStream(buf); + } + } + } +} diff --git a/AlbumArtExtraction/FlacAlbumArtExtractor.cs b/AlbumArtExtraction/FlacAlbumArtExtractor.cs new file mode 100644 index 0000000..cd0769c --- /dev/null +++ b/AlbumArtExtraction/FlacAlbumArtExtractor.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace AlbumArtExtraction { + /// + /// FLAC形式のファイルからアルバムアートを抽出する機能を表します + /// + public class FlacAlbumArtExtractor : IAlbumArtExtractor { + /// + /// メタデータブロックを読み取ります + /// + /// 対象の Stream + private MetaData _ReadMetaDataBlock(Stream stream) { + var isLastAndMetaDataType = stream.ReadAsByte(); + + // これが最後のメタデータブロックかどうかを示す値 + var isLast = (isLastAndMetaDataType & 0x80U) != 0; + + var metaDataType = (isLastAndMetaDataType & 0x7FU); + + var metaDataLength = stream.ReadAsUInt(3); + if (metaDataLength == 0) + throw new InvalidDataException("metaDataLength が不正です"); + + // Pictureタイプ以外はストリームから読み取らずにスキップする + List metaData; + if (metaDataType == 6) { + metaData = stream.ReadAsByteList((int)metaDataLength); + } + else { + metaData = null; + stream.Skip((int)metaDataLength); + } + + return new MetaData((MetaDataType)metaDataType, isLast, metaData); + } + + /// + /// PICTUREタイプのメタデータから画像データの Stream を取り出します + /// + private Stream _ParsePictureMetaData(MetaData pictureMetaData) { + if (pictureMetaData.Type != MetaDataType.PICTURE) + throw new ArgumentException("このメタデータはPICTUREタイプではありません"); + + List imageSource; + using (var memory = new MemoryStream(pictureMetaData.Data.ToArray())) { + memory.Skip(4); + var mimeTypeLength = memory.ReadAsUInt(); + if (mimeTypeLength > 128) + throw new InvalidDataException("mimeTypeLength が不正な値です"); + + memory.Skip((int)mimeTypeLength); + var explanationLength = memory.ReadAsUInt(); + + memory.Skip((int)explanationLength + 4 * 4); + var imageSourceSize = memory.ReadAsUInt(); + imageSource = memory.ReadAsByteList((int)imageSourceSize); + } + + return new MemoryStream(imageSource.ToArray()); + } + + /// + /// 対象のファイルが形式と一致しているかを判別します + /// + public bool CheckType(string filePath) { + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { + var formatId = file.ReadAsAsciiString(4); + + if (formatId != "fLaC") + return false; + } + + try + { + if (Extract(filePath) != null) + return true; + } + catch + { + // noop + } + + return false; + } + + /// + /// アルバムアートを抽出します + /// + /// + /// + public Stream Extract(string filePath) { + if (!File.Exists(filePath)) + throw new FileNotFoundException("指定されたファイルは存在しません"); + + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { + file.Skip(4); + + var metaDataList = new List(); + MetaData metaData = null; + do { + metaDataList.Add(metaData = _ReadMetaDataBlock(file)); + } + while (!metaData.IsLast && metaDataList.Count < 64); + + if (metaDataList.Count >= 64) + throw new InvalidDataException("メタデータの個数が異常です"); + + var picture = metaDataList.Find(i => i.Type == MetaDataType.PICTURE); + + return (picture != null) ? _ParsePictureMetaData(picture) : null; + } + } + + public class MetaData { + public MetaData(MetaDataType type, bool isLast, List data) { + Type = type; + IsLast = isLast; + Data = data; + } + + public MetaDataType Type { get; set; } + + public bool IsLast { get; set; } + + public List Data { get; set; } + + public override string ToString() => $"FlacMetaData {{ Type = {Type}, IsLast = {IsLast}, DataSize = {Data.Count} }}"; + } + + public enum MetaDataType { + STREAMINFO = 0, + PADDING = 1, + APPLICATION = 2, + SEEKTABLE = 3, + VORBIS_COMMENT = 4, + CUESHEET = 5, + PICTURE = 6 + } + } +} diff --git a/AlbumArtExtraction/IAlbumArtExtractor.cs b/AlbumArtExtraction/IAlbumArtExtractor.cs new file mode 100644 index 0000000..a618d5a --- /dev/null +++ b/AlbumArtExtraction/IAlbumArtExtractor.cs @@ -0,0 +1,11 @@ +using System.IO; + +namespace AlbumArtExtraction { + /// + /// アルバムアートを抽出するために必要となるメンバを公開します + /// + public interface IAlbumArtExtractor { + bool CheckType(string filePath); + Stream Extract(string filePath); + } +} diff --git a/AlbumArtExtraction/ID3v22AlbumArtExtractor.cs b/AlbumArtExtraction/ID3v22AlbumArtExtractor.cs new file mode 100644 index 0000000..3e9e29f --- /dev/null +++ b/AlbumArtExtraction/ID3v22AlbumArtExtractor.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; + +namespace AlbumArtExtraction +{ + /// + /// mp3形式(ID3v2.2) のファイルからアルバムアートを抽出する機能を表します + /// + public class ID3v22AlbumArtExtractor : IAlbumArtExtractor + { + + #region Constractor + + public ID3v22AlbumArtExtractor() { } + + #endregion + + #region Parsing picture for ID3Tag. + + /// + /// ID3v2.2 タグから画像データの Stream を取り出します + /// + private Stream _ReadPictureInFrameHeaders(Stream file) + { + var count = 0; + while (count++ < 63) + { + // Frame Name + var frameName = file.ReadAsAsciiString(3); + + // 有効な Frame Name であるかどうかを示す + var validName = Regex.IsMatch(frameName, "^[A-Z0-9]+$"); + + // 無効な Frame Name であれば、ループ終了 + if (!validName) break; + + Debug.WriteLine($"frameName = {frameName}"); + var frameSize = file.ReadAsUInt(3); + + // PIC Frame の判定 + if (frameName == "PIC") + { + // 1Byte: 文字コード, 3Byte: フォーマット, 1Byte: 種別は必ず存在する為、読み飛ばす + file.Skip(5); + + // 説明文を読み飛ばす (終端文字含む) + var length = 1; + while ((file.ReadAsByte() != 0x00U)) + length++; + + var imageSource = file.ReadAsByteList((int)frameSize - (5 + length)); + + return new MemoryStream(imageSource.ToArray()); + } + + // PIC Frame でないため、フレーム自体を読み飛ばす + file.Skip((int)frameSize); + } + + return null; + } + + #endregion + + #region Interface - IAlbumArtExtractor + + /// + /// 対象のファイルが形式と一致しているかを判別します + /// + public bool CheckType(string filePath) + { + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + return (file.ReadAsAsciiString(3) == "ID3" && file.ReadAsUShort() == 0x0200U); + } + + /// + /// アルバムアートを抽出します + /// + public Stream Extract(string filePath) + { + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + // ID3v2 Header 読み飛ばし + file.Skip(10); + + // v2.2 に関しては、ID3 Extended Header や、そのフラグは存在しない模様。 + + // Frame Headers + return _ReadPictureInFrameHeaders(file); + } + } + + #endregion + + } +} diff --git a/AlbumArtExtraction/ID3v2AlbumArtExtractor.cs b/AlbumArtExtraction/ID3v2AlbumArtExtractor.cs new file mode 100644 index 0000000..c3bba3b --- /dev/null +++ b/AlbumArtExtraction/ID3v2AlbumArtExtractor.cs @@ -0,0 +1,120 @@ +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; + +namespace AlbumArtExtraction +{ + /// + /// mp3形式(ID3v2.3/2.4) のファイルからアルバムアートを抽出する機能を表します + /// 拡張ヘッダーのあるファイルには利用されません + /// + public class ID3v2AlbumArtExtractor : IAlbumArtExtractor + { + + #region Constractor + + public ID3v2AlbumArtExtractor() { } + + #endregion + + #region Parsing picture for ID3Tag. + + /// + /// ID3v2.3/2.4 タグから画像データの Stream を取り出します + /// + private Stream _ReadPictureInFrameHeaders(Stream file) + { + var count = 1; + + while (true) + { + // Frame Name + var frameName = file.ReadAsAsciiString(4); + + // 有効な Frame Name であるかどうかを示す + var validName = Regex.IsMatch(frameName, "^[A-Z0-9]+$"); + + // 無効な Frame Name であれば、ループ終了 + if (!validName) break; + + Debug.WriteLine($"frameName = {frameName}"); + var frameSize = file.ReadAsUInt(); + + // フラグ読み飛ばし + file.Skip(2); + + // APIC Frame の判定 + if (frameName == "APIC") + { + var removeCount = 0; + + file.Skip(1); + removeCount += 1; + + while (file.ReadAsByte() != 0x00U) removeCount++; + + file.Skip(1); + removeCount += 1; + + while (file.ReadAsByte() != 0x00U) removeCount++; + + var imageSource = file.ReadAsByteList((int)frameSize - removeCount); + + return new MemoryStream(imageSource.ToArray()); + } + + // PIC Frame でないため、フレーム自体を読み飛ばす + file.Skip((int)frameSize); + + if (count > 74) + throw new InvalidDataException("フレーム数が正常な範囲を超えました"); + + count++; + } + + return null; + } + + #endregion + + #region Interface - IAlbumArtExtractor + + /// + /// 対象のファイルが形式と一致しているかを判別します + /// + public bool CheckType(string filePath) + { + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + var formatId = file.ReadAsAsciiString(3); + var version = file.ReadAsUShort(); + var headerFlag = file.ReadAsByte(); + + // フォーマット判定 + if (!(formatId == "ID3" && (version == 0x0300U || version == 0x0400U))) + return false; + + // extended header が無い場合のみ一致と判定 + return (headerFlag & 0x0040U) == 0; + } + } + + /// + /// アルバムアートを抽出します + /// + public Stream Extract(string filePath) + { + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + // ID3v2 Header 読み飛ばし + file.Skip(10); + + // Frame Headers + return _ReadPictureInFrameHeaders(file); + } + } + + #endregion + + } +} diff --git a/AlbumArtExtraction/Selector.cs b/AlbumArtExtraction/Selector.cs new file mode 100644 index 0000000..c9c39f0 --- /dev/null +++ b/AlbumArtExtraction/Selector.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace AlbumArtExtraction { + public class Selector { + public Selector() { } + + /// + /// 対象のファイルパスに対して利用可能な AlbumArtExtractor を選択して取得します + /// + /// アルバムアート抽出の対象ファイル + /// + /// + public IAlbumArtExtractor SelectAlbumArtExtractor(string filePath) { + //if (!File.Exists(filePath)) + // throw new ArgumentException("指定されたファイルパスは無効です", "filePath"); + if (!File.Exists(filePath)) + throw new FileNotFoundException("指定されたファイルは存在しません"); + + var extractors = new List { + new FlacAlbumArtExtractor(), + new ID3v2AlbumArtExtractor(), + new ID3v22AlbumArtExtractor(), + new DirectoryAlbumArtExtractor() + }; + var extractor = extractors.Find(i => i.CheckType(filePath)); + + if (extractor == null) + throw new NotSupportedException("指定されたファイルからAlbumArtを抽出する方法が見つかりませんでした"); + + return extractor; + } + } +} diff --git a/AlbumArtExtraction/StreamExtensions.cs b/AlbumArtExtraction/StreamExtensions.cs new file mode 100644 index 0000000..6aa4832 --- /dev/null +++ b/AlbumArtExtraction/StreamExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace AlbumArtExtraction +{ + public static class StreamExtensions + { + /// + /// 指定したバイト数のデータを読み飛ばします + /// + /// 読み飛ばすバイト数 + public static void Skip(this Stream stream, int skip) + { + if (skip > 0) + stream.Seek(skip, SeekOrigin.Current); + } + + /// + /// 指定した長さのデータを List<byte> として読み取ります + /// + /// 読み取るデータの長さ(バイト数) + public static List ReadAsByteList(this Stream stream, int count) + { + var buf = new byte[count]; + stream.Read(buf, 0, count); + + return new List(buf); + } + + /// + /// byte(1 Bytes) として読み取ります + /// + public static byte ReadAsByte(this Stream stream) => + stream.ReadAsByteList(1)[0]; + + private static byte[] _ReadAsUIntXBase(this Stream stream, int returnSize, int count) + { + if (count < 1 || count > returnSize) + throw new ArgumentOutOfRangeException("count"); + + var buf = stream.ReadAsByteList(count); + + // 配列にゼロパディングを追加 + foreach (var i in Enumerable.Range(0, returnSize - count)) + buf.Insert(0, 0); + + buf.Reverse(); + + return buf.ToArray(); + } + + /// + /// 指定した長さのデータを ushort(2 Bytes) として読み取ります(ビッグエンディアン) + /// + /// 読み取るデータの長さ(バイト数) 範囲: 1-2 + /// + public static ushort ReadAsUShort(this Stream stream, int count = 2) => + BitConverter.ToUInt16(stream._ReadAsUIntXBase(2, count), 0); + + /// + /// 指定した長さのデータを uint(4 Bytes) として読み取ります(ビッグエンディアン) + /// + /// 読み取るデータの長さ(バイト数) 範囲: 1-4 + /// + public static uint ReadAsUInt(this Stream stream, int count = 4) => + BitConverter.ToUInt32(stream._ReadAsUIntXBase(4, count), 0); + + /// + /// ASCII文字列として指定されたカウント数だけ読み取ります + /// + public static string ReadAsAsciiString(this Stream stream, int count) + { + var bytes = stream.ReadAsByteList(count).ToArray(); + + return new string(Encoding.ASCII.GetChars(bytes)); + } + } +} From 52a5a8518de67fa04070d5d0a64a7aabf36eef98 Mon Sep 17 00:00:00 2001 From: marihachi Date: Sat, 16 Jun 2018 22:29:13 +0900 Subject: [PATCH 3/3] update project info --- AlbumArtExtraction/AlbumArtExtraction.csproj | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/AlbumArtExtraction/AlbumArtExtraction.csproj b/AlbumArtExtraction/AlbumArtExtraction.csproj index b81db56..80e0cbb 100644 --- a/AlbumArtExtraction/AlbumArtExtraction.csproj +++ b/AlbumArtExtraction/AlbumArtExtraction.csproj @@ -1,7 +1,23 @@ - netstandard1.3 + netstandard2.0 + false + false + A .NET library extracts album art from metadata such as FLAC, ID3, etc. + (C) 2018 Legato-Dev + https://github.com/Legato-Dev/AlbumArtExtraction/blob/master/LICENSE + https://github.com/Legato-Dev/AlbumArtExtraction + https://github.com/Legato-Dev/AlbumArtExtraction/raw/master/logo64.png + ID3 MP3 FLAC AlbumArt Extraction NowPlaying CSharp + 1.0.0 + + + + bin\Release\netstandard2.0\AlbumArtExtraction.xml + bin\Release\ + Auto + 2