Browse Source

commit: adjust namespaces and import MediaTagger

HOME 5 years ago
parent
commit
757a30112f

+ 3 - 2
LyricTools/DebugLauncherProgram.cs

@@ -1,10 +1,11 @@
 using System;
+using LrcTool;
 
-namespace LyricToolsNet4DebugLauncher
+namespace LrcToolNet4DebugLauncher
 {
     internal class DebugLauncherProgram
     {
         [STAThread]
-        private static void Main() => LyricTools.Program.Main();
+        private static void Main() => Program.Main();
     }
 }

+ 1 - 1
LyricTools/HiraganaPhoneticHelper.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Runtime.InteropServices;
 
-namespace LyricTools
+namespace LrcTool
 {
     public class HiraganaPhoneticHelper : IDisposable
     {

+ 1 - 1
LyricTools/LrcFetcherPage.Designer.cs

@@ -1,4 +1,4 @@
-namespace LyricTools
+namespace LrcTool
 {
     partial class LrcFetcherPage
     {

+ 1 - 1
LyricTools/LrcFetcherPage.cs

@@ -5,7 +5,7 @@ using System.Threading;
 using System.Web;
 using System.Windows.Forms;
 
-namespace LyricTools
+namespace LrcTool
 {
     public partial class LrcFetcherPage : UserControl
     {

+ 1 - 1
LyricTools/LrcKanjiHiraganaInjectPage.cs

@@ -5,7 +5,7 @@ using System.Text;
 using System.Text.RegularExpressions;
 using System.Windows.Forms;
 
-namespace LyricTools
+namespace LrcTool
 {
     internal static class LrcKanjiHiraganaInjectPage
     {

+ 1 - 1
LyricTools/LrcPreProcessPage.Designer.cs

@@ -1,4 +1,4 @@
-namespace LyricTools
+namespace LrcTool
 {
     partial class LrcPreProcessPage
     {

+ 1 - 1
LyricTools/LrcPreProcessPage.cs

@@ -3,7 +3,7 @@ using System.Text.RegularExpressions;
 using System.Threading;
 using System.Windows.Forms;
 
-namespace LyricTools
+namespace LrcTool
 {
     public partial class LrcPreProcessPage : UserControl
     {

+ 1 - 1
LyricTools/Program.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Windows.Forms;
 
-namespace LyricTools
+namespace LrcTool
 {
     public static class Program
     {

+ 305 - 0
MediaTagger/MainForm.Designer.cs

@@ -0,0 +1,305 @@
+namespace MediaTagger
+{
+    partial class MainForm
+    {
+        /// <summary>
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows 窗体设计器生成的代码
+
+        /// <summary>
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.TopGroupBox = new System.Windows.Forms.GroupBox();
+            this.MediaFileDragDropLabel = new System.Windows.Forms.Label();
+            this.SaveButton = new System.Windows.Forms.Button();
+            this.DiscardButton = new System.Windows.Forms.Button();
+            this.AdvPropertyGrid = new System.Windows.Forms.PropertyGrid();
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.PropertyTabControl = new System.Windows.Forms.TabControl();
+            this.tabPage4 = new System.Windows.Forms.TabPage();
+            this.EasyPropertyGrid = new System.Windows.Forms.PropertyGrid();
+            this.tabPage3 = new System.Windows.Forms.TabPage();
+            this.tabControl1 = new System.Windows.Forms.TabControl();
+            this.tabPage1 = new System.Windows.Forms.TabPage();
+            this.CoverPictureBox = new System.Windows.Forms.PictureBox();
+            this.CoverFileDragDropLabel = new System.Windows.Forms.Label();
+            this.tabPage2 = new System.Windows.Forms.TabPage();
+            this.LyricsTextBox = new System.Windows.Forms.TextBox();
+            this.TopGroupBox.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.Panel2.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.PropertyTabControl.SuspendLayout();
+            this.tabPage4.SuspendLayout();
+            this.tabPage3.SuspendLayout();
+            this.tabControl1.SuspendLayout();
+            this.tabPage1.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.CoverPictureBox)).BeginInit();
+            this.tabPage2.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // TopGroupBox
+            // 
+            this.TopGroupBox.Controls.Add(this.MediaFileDragDropLabel);
+            this.TopGroupBox.Controls.Add(this.SaveButton);
+            this.TopGroupBox.Controls.Add(this.DiscardButton);
+            this.TopGroupBox.Dock = System.Windows.Forms.DockStyle.Top;
+            this.TopGroupBox.Location = new System.Drawing.Point(2, 2);
+            this.TopGroupBox.Margin = new System.Windows.Forms.Padding(2);
+            this.TopGroupBox.Name = "TopGroupBox";
+            this.TopGroupBox.Padding = new System.Windows.Forms.Padding(2);
+            this.TopGroupBox.Size = new System.Drawing.Size(750, 67);
+            this.TopGroupBox.TabIndex = 0;
+            this.TopGroupBox.TabStop = false;
+            this.TopGroupBox.Text = "File";
+            // 
+            // MediaFileDragDropLabel
+            // 
+            this.MediaFileDragDropLabel.AllowDrop = true;
+            this.MediaFileDragDropLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.MediaFileDragDropLabel.Location = new System.Drawing.Point(54, 16);
+            this.MediaFileDragDropLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+            this.MediaFileDragDropLabel.Name = "MediaFileDragDropLabel";
+            this.MediaFileDragDropLabel.Size = new System.Drawing.Size(635, 49);
+            this.MediaFileDragDropLabel.TabIndex = 0;
+            this.MediaFileDragDropLabel.Text = "Drag and drop file into here...";
+            this.MediaFileDragDropLabel.DragDrop += new System.Windows.Forms.DragEventHandler(this.MediaFileDragDropLabel_DragDrop);
+            this.MediaFileDragDropLabel.DragEnter += new System.Windows.Forms.DragEventHandler(this.Common_DragEnter);
+            // 
+            // SaveButton
+            // 
+            this.SaveButton.Dock = System.Windows.Forms.DockStyle.Left;
+            this.SaveButton.Location = new System.Drawing.Point(2, 16);
+            this.SaveButton.Margin = new System.Windows.Forms.Padding(2);
+            this.SaveButton.Name = "SaveButton";
+            this.SaveButton.Size = new System.Drawing.Size(52, 49);
+            this.SaveButton.TabIndex = 2;
+            this.SaveButton.Text = "Save and Close";
+            this.SaveButton.UseVisualStyleBackColor = true;
+            this.SaveButton.Click += new System.EventHandler(this.SaveButton_Click);
+            // 
+            // DiscardButton
+            // 
+            this.DiscardButton.Dock = System.Windows.Forms.DockStyle.Right;
+            this.DiscardButton.Location = new System.Drawing.Point(689, 16);
+            this.DiscardButton.Margin = new System.Windows.Forms.Padding(2);
+            this.DiscardButton.Name = "DiscardButton";
+            this.DiscardButton.Size = new System.Drawing.Size(59, 49);
+            this.DiscardButton.TabIndex = 1;
+            this.DiscardButton.Text = "Discard and Close";
+            this.DiscardButton.UseVisualStyleBackColor = true;
+            this.DiscardButton.Click += new System.EventHandler(this.DiscardButton_Click);
+            // 
+            // AdvPropertyGrid
+            // 
+            this.AdvPropertyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.AdvPropertyGrid.Location = new System.Drawing.Point(3, 3);
+            this.AdvPropertyGrid.Margin = new System.Windows.Forms.Padding(2);
+            this.AdvPropertyGrid.Name = "AdvPropertyGrid";
+            this.AdvPropertyGrid.Size = new System.Drawing.Size(213, 511);
+            this.AdvPropertyGrid.TabIndex = 1;
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.Location = new System.Drawing.Point(2, 69);
+            this.splitContainer1.Margin = new System.Windows.Forms.Padding(2);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.PropertyTabControl);
+            // 
+            // splitContainer1.Panel2
+            // 
+            this.splitContainer1.Panel2.Controls.Add(this.tabControl1);
+            this.splitContainer1.Size = new System.Drawing.Size(750, 543);
+            this.splitContainer1.SplitterDistance = 227;
+            this.splitContainer1.SplitterWidth = 3;
+            this.splitContainer1.TabIndex = 2;
+            // 
+            // PropertyTabControl
+            // 
+            this.PropertyTabControl.Controls.Add(this.tabPage4);
+            this.PropertyTabControl.Controls.Add(this.tabPage3);
+            this.PropertyTabControl.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.PropertyTabControl.Location = new System.Drawing.Point(0, 0);
+            this.PropertyTabControl.Name = "PropertyTabControl";
+            this.PropertyTabControl.SelectedIndex = 0;
+            this.PropertyTabControl.Size = new System.Drawing.Size(227, 543);
+            this.PropertyTabControl.TabIndex = 2;
+            // 
+            // tabPage4
+            // 
+            this.tabPage4.Controls.Add(this.EasyPropertyGrid);
+            this.tabPage4.Location = new System.Drawing.Point(4, 22);
+            this.tabPage4.Name = "tabPage4";
+            this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
+            this.tabPage4.Size = new System.Drawing.Size(219, 517);
+            this.tabPage4.TabIndex = 1;
+            this.tabPage4.Text = "Easy View";
+            this.tabPage4.UseVisualStyleBackColor = true;
+            // 
+            // EasyPropertyGrid
+            // 
+            this.EasyPropertyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.EasyPropertyGrid.Location = new System.Drawing.Point(3, 3);
+            this.EasyPropertyGrid.Margin = new System.Windows.Forms.Padding(2);
+            this.EasyPropertyGrid.Name = "EasyPropertyGrid";
+            this.EasyPropertyGrid.Size = new System.Drawing.Size(213, 511);
+            this.EasyPropertyGrid.TabIndex = 2;
+            // 
+            // tabPage3
+            // 
+            this.tabPage3.Controls.Add(this.AdvPropertyGrid);
+            this.tabPage3.Location = new System.Drawing.Point(4, 22);
+            this.tabPage3.Name = "tabPage3";
+            this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
+            this.tabPage3.Size = new System.Drawing.Size(219, 517);
+            this.tabPage3.TabIndex = 0;
+            this.tabPage3.Text = "Advance View";
+            this.tabPage3.UseVisualStyleBackColor = true;
+            // 
+            // tabControl1
+            // 
+            this.tabControl1.Controls.Add(this.tabPage1);
+            this.tabControl1.Controls.Add(this.tabPage2);
+            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.tabControl1.Location = new System.Drawing.Point(0, 0);
+            this.tabControl1.Margin = new System.Windows.Forms.Padding(2);
+            this.tabControl1.Name = "tabControl1";
+            this.tabControl1.SelectedIndex = 0;
+            this.tabControl1.Size = new System.Drawing.Size(520, 543);
+            this.tabControl1.TabIndex = 1;
+            // 
+            // tabPage1
+            // 
+            this.tabPage1.Controls.Add(this.CoverPictureBox);
+            this.tabPage1.Controls.Add(this.CoverFileDragDropLabel);
+            this.tabPage1.Location = new System.Drawing.Point(4, 22);
+            this.tabPage1.Margin = new System.Windows.Forms.Padding(2);
+            this.tabPage1.Name = "tabPage1";
+            this.tabPage1.Padding = new System.Windows.Forms.Padding(2);
+            this.tabPage1.Size = new System.Drawing.Size(512, 517);
+            this.tabPage1.TabIndex = 0;
+            this.tabPage1.Text = "Cover";
+            this.tabPage1.UseVisualStyleBackColor = true;
+            // 
+            // CoverPictureBox
+            // 
+            this.CoverPictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+            this.CoverPictureBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.CoverPictureBox.Location = new System.Drawing.Point(2, 45);
+            this.CoverPictureBox.Margin = new System.Windows.Forms.Padding(2);
+            this.CoverPictureBox.Name = "CoverPictureBox";
+            this.CoverPictureBox.Size = new System.Drawing.Size(508, 470);
+            this.CoverPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.CoverPictureBox.TabIndex = 2;
+            this.CoverPictureBox.TabStop = false;
+            this.CoverPictureBox.Click += new System.EventHandler(this.CoverPictureBox_Click);
+            // 
+            // CoverFileDragDropLabel
+            // 
+            this.CoverFileDragDropLabel.AllowDrop = true;
+            this.CoverFileDragDropLabel.Dock = System.Windows.Forms.DockStyle.Top;
+            this.CoverFileDragDropLabel.Location = new System.Drawing.Point(2, 2);
+            this.CoverFileDragDropLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+            this.CoverFileDragDropLabel.Name = "CoverFileDragDropLabel";
+            this.CoverFileDragDropLabel.Size = new System.Drawing.Size(508, 43);
+            this.CoverFileDragDropLabel.TabIndex = 1;
+            this.CoverFileDragDropLabel.Text = "JPEG ONLY\r\nDrag && drop HERE to import\r\nClick PICTURE to export";
+            this.CoverFileDragDropLabel.DragDrop += new System.Windows.Forms.DragEventHandler(this.CoverFileDragDropLabel_DragDrop);
+            this.CoverFileDragDropLabel.DragEnter += new System.Windows.Forms.DragEventHandler(this.Common_DragEnter);
+            // 
+            // tabPage2
+            // 
+            this.tabPage2.Controls.Add(this.LyricsTextBox);
+            this.tabPage2.Location = new System.Drawing.Point(4, 22);
+            this.tabPage2.Margin = new System.Windows.Forms.Padding(2);
+            this.tabPage2.Name = "tabPage2";
+            this.tabPage2.Padding = new System.Windows.Forms.Padding(2);
+            this.tabPage2.Size = new System.Drawing.Size(512, 517);
+            this.tabPage2.TabIndex = 1;
+            this.tabPage2.Text = "Lyrics";
+            this.tabPage2.UseVisualStyleBackColor = true;
+            // 
+            // LyricsTextBox
+            // 
+            this.LyricsTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.LyricsTextBox.Location = new System.Drawing.Point(2, 2);
+            this.LyricsTextBox.Margin = new System.Windows.Forms.Padding(2);
+            this.LyricsTextBox.Multiline = true;
+            this.LyricsTextBox.Name = "LyricsTextBox";
+            this.LyricsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+            this.LyricsTextBox.Size = new System.Drawing.Size(508, 513);
+            this.LyricsTextBox.TabIndex = 0;
+            // 
+            // MainForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(754, 614);
+            this.Controls.Add(this.splitContainer1);
+            this.Controls.Add(this.TopGroupBox);
+            this.Margin = new System.Windows.Forms.Padding(2);
+            this.Name = "MainForm";
+            this.Padding = new System.Windows.Forms.Padding(2);
+            this.Text = "Media Tagger";
+            this.TopGroupBox.ResumeLayout(false);
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            this.splitContainer1.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.PropertyTabControl.ResumeLayout(false);
+            this.tabPage4.ResumeLayout(false);
+            this.tabPage3.ResumeLayout(false);
+            this.tabControl1.ResumeLayout(false);
+            this.tabPage1.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.CoverPictureBox)).EndInit();
+            this.tabPage2.ResumeLayout(false);
+            this.tabPage2.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox TopGroupBox;
+        private System.Windows.Forms.Label MediaFileDragDropLabel;
+        private System.Windows.Forms.Button SaveButton;
+        private System.Windows.Forms.Button DiscardButton;
+        private System.Windows.Forms.PropertyGrid AdvPropertyGrid;
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.TabControl tabControl1;
+        private System.Windows.Forms.TabPage tabPage1;
+        private System.Windows.Forms.PictureBox CoverPictureBox;
+        private System.Windows.Forms.Label CoverFileDragDropLabel;
+        private System.Windows.Forms.TabPage tabPage2;
+        private System.Windows.Forms.TextBox LyricsTextBox;
+        private System.Windows.Forms.TabControl PropertyTabControl;
+        private System.Windows.Forms.TabPage tabPage3;
+        private System.Windows.Forms.TabPage tabPage4;
+        private System.Windows.Forms.PropertyGrid EasyPropertyGrid;
+    }
+}
+

+ 176 - 0
MediaTagger/MainForm.cs

@@ -0,0 +1,176 @@
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Design;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+using TagLib;
+using File = System.IO.File;
+
+namespace MediaTagger
+{
+    public partial class MainForm : Form
+    {
+        private TagLib.File _tagInstance;
+
+        public MainForm()
+        {
+            InitializeComponent();
+            MediaFileDragDropLabel.Tag = MediaFileDragDropLabel.Text;
+        }
+
+        private void Reset()
+        {
+            LyricsTextBox.DataBindings.Clear();
+            LyricsTextBox.Clear();
+            MediaFileDragDropLabel.Text = MediaFileDragDropLabel.Tag as string;
+            AdvPropertyGrid.SelectedObject = null;
+            EasyPropertyGrid.SelectedObject = null;
+            CoverPictureBox.Image = null;
+            _tagInstance = null;
+        }
+
+        private void Common_DragEnter(object sender, DragEventArgs e)
+        {
+            var bMatchDropFormat = e.Data.GetDataPresent(DataFormats.FileDrop);
+            var bMatchDataType = e.Data.GetData(DataFormats.FileDrop) is string[];
+            var bMatchFileCount = ((string[])e.Data.GetData(DataFormats.FileDrop)).Length == 1;
+            var bMatchExistFile = File.Exists(((string[])e.Data.GetData(DataFormats.FileDrop))[0]);
+
+            e.Effect = bMatchDropFormat && bMatchDataType && bMatchFileCount && bMatchExistFile
+                    ? DragDropEffects.Link
+                    : DragDropEffects.None;
+        }
+
+        private void MediaFileDragDropLabel_DragDrop(object sender, DragEventArgs e)
+        {
+            _tagInstance?.Dispose();
+            Reset();
+
+            var path = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
+            _tagInstance = TagLib.File.Create(path);
+
+            MediaFileDragDropLabel.Text = path
+                + Environment.NewLine + "TagType: " + _tagInstance.TagTypes;
+
+            AdvPropertyGrid.SelectedObject = _tagInstance.Tag;
+            EasyPropertyGrid.SelectedObject = new EasyTag(_tagInstance.Tag);
+            CoverPictureBox.Image = _tagInstance.Tag.Pictures.FirstOrDefault()?.Data.Data.LoadAsImage();
+
+            LyricsTextBox.DataBindings.Add("Text", _tagInstance.Tag, nameof(_tagInstance.Tag.Lyrics));
+        }
+
+        private void SaveButton_Click(object sender, EventArgs e)
+        {
+            _tagInstance.Save();
+
+            Reset();
+        }
+
+        private void DiscardButton_Click(object sender, EventArgs e)
+        {
+            _tagInstance.Dispose();
+            Reset();
+        }
+
+        private void CoverFileDragDropLabel_DragDrop(object sender, DragEventArgs e)
+        {
+            var path = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
+            try
+            {
+                var img = Image.FromFile(path);
+                if (false == System.Drawing.Imaging.ImageFormat.Jpeg.Equals(img.RawFormat))
+                {
+                    img.Dispose();
+                    throw new ArgumentException("No a JPEG file");
+                }
+
+                var orig = CoverPictureBox.Image;
+                CoverPictureBox.Image = img;
+                orig?.Dispose();
+
+                var bytes = File.ReadAllBytes(path);
+                var pic = new Picture //REF: stackoverflow.com/a/30285220/2430943
+                {
+                    Type = PictureType.FrontCover,
+                    Description = "Cover",
+                    MimeType = "image/jpeg",
+                    Data = bytes,
+                };
+                _tagInstance.Tag.Pictures = new IPicture[] { pic };
+            }
+            catch (Exception exception)
+            {
+                MessageBox.Show(exception.ToString(), "ERROR");
+            }
+        }
+
+        private void CoverPictureBox_Click(object sender, EventArgs e)
+        {
+            if (null == _tagInstance) return;
+            ;
+            var data = _tagInstance.Tag.Pictures.FirstOrDefault()?.Data.Data;
+            if (null == data)
+            {
+                MessageBox.Show("No cover embedded!");
+                return;
+            }
+
+            var dialog = new SaveFileDialog
+            {
+                Title = "Save cover",
+                Filter = "JPEG file(*.jpeg;*.jpg)|*.jpeg;*.jpg"
+            };
+
+            if (DialogResult.OK == dialog.ShowDialog(this))
+            {
+                File.WriteAllBytes(dialog.FileName, data);
+            }
+        }
+    }
+
+    internal class EasyTag
+    {
+        private readonly Tag _raw;
+
+        public EasyTag(Tag tag)
+        {
+            _raw = tag;
+        }
+
+        [Category("Frequently Used")]
+        public string Album { get => _raw.Album; set => _raw.Album = value; }
+
+        [Category("Frequently Used")]
+        public string Artists { get => string.Join(";", _raw.Performers); set => _raw.Performers = value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries); }
+
+        [Category("Frequently Used")]
+        public string Title { get => _raw.Title; set => _raw.Title = value; }
+
+        [Category("Other")]
+        public string Genres { get => string.Join(";", _raw.Genres); set => _raw.Genres = value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries); }
+
+        [Category("Other")]
+        public uint Track { get => _raw.Track; set => _raw.Track = value; }
+
+        [Category("Other")]
+        public uint Year { get => _raw.Year; set => _raw.Year = value; }
+
+        [Category("Other")]
+        [Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
+        public string Lyrics { get => _raw.Lyrics; set => _raw.Lyrics = value; }
+    }
+
+    internal static class ExtensionMethod
+    {
+        public static Image LoadAsImage(this byte[] data)
+        {
+            using (var ms = new MemoryStream(data))
+            {
+                return Image.FromStream(ms);
+            }
+        }
+    }
+}

+ 120 - 0
MediaTagger/MainForm.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 93 - 0
MediaTagger/MediaTagger.csproj

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="C:\NuGetLocalRepo\ILRepack.2.0.16\build\ILRepack.props" Condition="Exists('C:\NuGetLocalRepo\ILRepack.2.0.16\build\ILRepack.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{E23D02D8-44FF-48B6-AC00-E42C5143C19F}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>MediaTagger</RootNamespace>
+    <AssemblyName>MediaTagger</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="policy.2.0.taglib-sharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=db62eba44689b5b0, processorArchitecture=MSIL">
+      <HintPath>C:\NuGetLocalRepo\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Design" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+    <Reference Include="taglib-sharp, Version=2.1.0.0, Culture=neutral, PublicKeyToken=db62eba44689b5b0, processorArchitecture=MSIL">
+      <HintPath>C:\NuGetLocalRepo\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MainForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="MainForm.Designer.cs">
+      <DependentUpon>MainForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <EmbeddedResource Include="MainForm.resx">
+      <DependentUpon>MainForm.cs</DependentUpon>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PostBuildEvent>if $(ConfigurationName) == Release setlocal enabledelayedexpansion enableextensions
+    if $(ConfigurationName) == Release set DLL_LIST=
+    if $(ConfigurationName) == Release for %25%25x in ($(TargetDir)*.dll) do set DLL_LIST=!DLL_LIST! "%25%25x"
+    if $(ConfigurationName) == Release echo dlls: !DLL_LIST!
+
+if $(ConfigurationName) == Release if not exist "$(TargetDir)Packed" md "$(TargetDir)Packed"
+
+if $(ConfigurationName) == Release $(ILRepack) /ndebug "/out:$(TargetDir)Packed\$(TargetFileName)" "$(TargetPath)" !DLL_LIST!
+
+if $(ConfigurationName) == Release if exist "$(TargetDir)Packed\$(TargetFileName).config" del "$(TargetDir)Packed\$(TargetFileName).config"</PostBuildEvent>
+  </PropertyGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('C:\NuGetLocalRepo\ILRepack.2.0.16\build\ILRepack.props')" Text="$([System.String]::Format('$(ErrorText)', 'C:\NuGetLocalRepo\ILRepack.2.0.16\build\ILRepack.props'))" />
+  </Target>
+</Project>

+ 22 - 0
MediaTagger/Program.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace MediaTagger
+{
+    static class Program
+    {
+        /// <summary>
+        /// 应用程序的主入口点。
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new MainForm());
+        }
+    }
+}

+ 36 - 0
MediaTagger/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("MediaTagger")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("MediaTagger")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("e23d02d8-44ff-48b6-ac00-e42c5143c19f")]
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
+// 方法是按如下所示使用“*”: :
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 5 - 0
MediaTagger/packages.config

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="ILRepack" version="2.0.16" targetFramework="net461" />
+  <package id="taglib" version="2.1.0.0" targetFramework="net461" />
+</packages>

+ 11 - 0
MusicTool.sln

@@ -9,6 +9,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LrcTool", "LyricTools\LrcTo
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LrcToolNet4DebugLauncher", "LyricTools\LrcToolNet4DebugLauncher.csproj", "{4729633E-E50E-47F4-AC9B-38412BB5D693}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaTagger", "MediaTagger\MediaTagger.csproj", "{E23D02D8-44FF-48B6-AC00-E42C5143C19F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "@", "@", "{1F900AE3-F1C6-4CAA-82B4-E3F305DE9F03}"
+	ProjectSection(SolutionItems) = preProject
+		NuGet.config = NuGet.config
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -27,6 +34,10 @@ Global
 		{4729633E-E50E-47F4-AC9B-38412BB5D693}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4729633E-E50E-47F4-AC9B-38412BB5D693}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4729633E-E50E-47F4-AC9B-38412BB5D693}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E23D02D8-44FF-48B6-AC00-E42C5143C19F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E23D02D8-44FF-48B6-AC00-E42C5143C19F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E23D02D8-44FF-48B6-AC00-E42C5143C19F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E23D02D8-44FF-48B6-AC00-E42C5143C19F}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE