2016/3/13 技术探讨

    最近一段时间,我都在努力的减肥,除了减少食量以外,还坚持每天至少跑半个小时(具体看下班时间了,不“被加班”就跑一个小时),跑完后去呜呜的群里签到然后再吃饭。但作为一个体重超过90KG的重量级选手来说,跑步真的是一件很累的活,如果就这么单纯的跑的话,根本就坚持不了十分钟。于是我去一些评书网,下载了一些“粤语古仔”,边听边跑。

    虽然边听边跑的效果很不错,但我却遇上了这么一个问题,那就是下载了的文件(mp3),虽然文件名还是以“001”、“002”、“0030”……的方式命名,但到了手机上,却成了如下图所示:

    这根本看不出到底谁是谁,更可悲的是,这个手机音乐还傻傻的无法判断“欲知后事如何,请听下回分解”的下回,到底是哪一回了,最终给我来了个随机播放。

    终究其原因,还是因为虽然我们的文件名是按照“001”、“002”……的方式明明,但手机音乐却完全不读取,它是根据mp3的title来确定此文件的命名,于是就悲催了。

    当然,我们可以通过文件属性的方式,把这个title进行修正,但一部的评书,短则百回,长则千回,逐个文件的修改确实不是明智之举,正确的姿势应该是利用程序,利用控制台帮我们逐个的进行修改。

    好,下面我们就开始讲解我们应该怎么做。

    首先,我们需要先从nuget下载两个东西,他们分别是:

    然后新建一个工具类,把下面的这堆代码放进去: 

MediaTags
复制代码
namespace Jhong
{
    using Microsoft.WindowsAPICodePack.Shell;
    using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
    using System;
    using System.Linq;

    public class MediaTags
    {
        #region Mp3文件属性

        /// <summary>
        /// 标题
        /// </summary>
        [MediaProperty("Title")]
        public string Title { get; set; }

        /// <summary>
        /// 子标题
        /// </summary>
        [MediaProperty("Media.SubTitle")]
        public string SubTitle { get; set; }

        /// <summary>
        /// 星级
        /// </summary>
        [MediaProperty("Rating")]
        public uint? Rating { get; set; }

        /// <summary>
        /// 备注
        /// </summary>
        [MediaProperty("Comment")]
        public string Comment { get; set; }

        /// <summary>
        /// 艺术家
        /// </summary>
        [MediaProperty("Author")]
        public string Author { get; set; }

        /// <summary>
        /// 唱片集
        /// </summary>
        [MediaProperty("Music.AlbumTitle")]
        public string AlbumTitle { get; set; }

        /// <summary>
        /// 唱片集艺术家
        /// </summary>
        [MediaProperty("Music.AlbumArtist")]
        public string AlbumArtist { get; set; }

        /// <summary>
        ////// </summary>
        [MediaProperty("Media.Year")]
        public uint? Year { get; set; }

        /// <summary>
        /// 流派
        /// </summary>
        [MediaProperty("Music.Genre")]
        public string Genre { get; set; }

        /// <summary>
        /// 流派
        /// </summary>
        [MediaProperty("Music.TrackNumber")]
        public uint? TrackNumber { get; set; }

        /// <summary>
        /// 流派
        /// </summary>
        [MediaProperty("Media.Duration")]
        public string Duration { get; private set; }

        /// <summary>
        /// 比特率
        /// </summary>
        [MediaProperty("Audio.EncodingBitrate")]
        public string BitRate { get; private set; }

        #endregion Mp3文件属性

        public MediaTags(string mediaPath)
        {
            Init(mediaPath);
        }

        private void Init(string mediaPath)
        {
            using (var obj = ShellObject.FromParsingName(mediaPath))
            {
                var mediaInfo = obj.Properties;
                foreach (var properItem in this.GetType().GetProperties())
                {
                    var mp3Att = properItem.GetCustomAttributes(typeof(MediaPropertyAttribute), false).FirstOrDefault();
                    var shellProper = mediaInfo.GetProperty("System." + mp3Att);
                    var value = shellProper == null ? null : shellProper.ValueAsObject;

                    if (value == null)
                    {
                        continue;
                    }

                    if (shellProper.ValueType == typeof(string[]))    //艺术家,流派等多值属性
                    {
                        properItem.SetValue(this, string.Join(";", value as string[]), null);
                    }
                    else if (properItem.PropertyType != shellProper.ValueType)    //一些只读属性,类型不是string,但作为string输出,避免转换 如播放时间,比特率等
                    {
                        properItem.SetValue(this, value == null ? "" : shellProper.FormatForDisplay(PropertyDescriptionFormatOptions.None), null);
                    }
                    else
                    {
                        properItem.SetValue(this, value, null);
                    }
                }
            }
        }

        /// <summary>
        /// 更新mp3信息
        /// </summary>
        /// <param name="mp3Path"></param>
        public void Commit(string mp3Path)
        {
            var old = new MediaTags(mp3Path);

            using (var obj = ShellObject.FromParsingName(mp3Path))
            {
                var mediaInfo = obj.Properties;
                foreach (var proper in this.GetType().GetProperties())
                {
                    var oldValue = proper.GetValue(old, null);
                    var newValue = proper.GetValue(this, null);

                    if (oldValue == null && newValue == null)
                    {
                        continue;
                    }

                    // 这里做了修改  郭红俊 20091202
                    // 原先在旧值存在,新值没有给出时,会有空对象引用的bug
                    //if (oldValue == null || !oldValue.Equals(newValue))

                    // 新的逻辑 新值存在时, 则替换旧值
                    if ((newValue != null) && (newValue.ToString().Trim().Length > 0) && (newValue != oldValue))
                    {
                        var mp3Att = proper.GetCustomAttributes(typeof(MediaPropertyAttribute), false).FirstOrDefault();
                        var shellProper = mediaInfo.GetProperty("System." + mp3Att);
                        Console.WriteLine(mp3Att);

                        if (newValue == null) newValue = string.Empty;

                        SetPropertyValue(shellProper, newValue);
                    }
                }
            }
        }

        #region SetPropertyValue

        private static void SetPropertyValue(IShellProperty prop, object value)
        {
            if (prop.ValueType == typeof(string[]))        //只读属性不会改变,故与实际类型不符的只有string[]这一种
            {
                string[] values = (value as string).Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                (prop as ShellProperty<string[]>).Value = values;
            }
            if (prop.ValueType == typeof(string))
            {
                (prop as ShellProperty<string>).Value = value as string;
            }
            else if (prop.ValueType == typeof(ushort?))
            {
                (prop as ShellProperty<ushort?>).Value = value as ushort?;
            }
            else if (prop.ValueType == typeof(short?))
            {
                (prop as ShellProperty<short?>).Value = value as short?;
            }
            else if (prop.ValueType == typeof(uint?))
            {
                (prop as ShellProperty<uint?>).Value = value as uint?;
            }
            else if (prop.ValueType == typeof(int?))
            {
                (prop as ShellProperty<int?>).Value = value as int?;
            }
            else if (prop.ValueType == typeof(ulong?))
            {
                (prop as ShellProperty<ulong?>).Value = value as ulong?;
            }
            else if (prop.ValueType == typeof(long?))
            {
                (prop as ShellProperty<long?>).Value = value as long?;
            }
            else if (prop.ValueType == typeof(DateTime?))
            {
                (prop as ShellProperty<DateTime?>).Value = value as DateTime?;
            }
            else if (prop.ValueType == typeof(double?))
            {
                (prop as ShellProperty<double?>).Value = value as double?;
            }
        }

        #endregion SetPropertyValue

        #region MediaPropertyAttribute

        [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
        private sealed class MediaPropertyAttribute : Attribute
        {
            public string PropertyKey { get; private set; }

            public MediaPropertyAttribute(string propertyKey)
            {
                this.PropertyKey = propertyKey;
            }

            public override string ToString()
            {
                return PropertyKey;
            }
        }

        #endregion MediaPropertyAttribute
    }
}
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

    使用方法就如下面的代码所示了,把你想修改的信息进行修改,然后再提交即可: 

使用方法
复制代码
namespace Jhong
{
    using System;
    using System.IO;
    using System.Linq;

    internal class Program
    {
        private static void Main(string[] args)
        {
            var path = @"E:\鹿鼎记";
            var dic = new System.IO.DirectoryInfo(path);
            MediaTags mt = new MediaTags(Environment.CurrentDirectory);

            var files = dic.GetFiles();
            for (int i = 0; i < files.Count(); i++)
            {
                var file = files[i];
                var fpath = Path.Combine(path, file.Name);
                mt.Title = "鹿鼎记_" + i.ToString().PadLeft(3, '0');
                mt.Commit(fpath);
            }
        }
    }
}
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

     最后,我们把控制台跑起来,批量进行修改:

     修改完成后,我们可以看到title已经全部被修改了

    手机上也已经OK了

    最后有一点要提醒的,修改这些文件,可能涉及权限的问题,如果你放C盘桌面之类的,可能会抛出Exception,所以建议把这些文件放到其他盘中。

  [ C# ]   [ mp3 ]   [ 信息修改 ]   [ Windows API Code Pack ]
知识共享许可协议 本作品由小蝶惊鸿创作,采用知识共享署名 4.0 国际许可协议进行许可,转载时请保留本文署名及链接。