提取Windows Media Player播放列表中的歌曲
前些日子买了个mp3,听着挺过瘾。在电脑上我都是用Windows Media Player播放歌曲,还把比较喜欢的“精选金曲”添加到了播放列表中。但想把播放列表中的歌曲拷到mp3里的时候却遇到了困难:列表中的歌曲分散在几十个专辑目录中,一个目录一个目录的找岂不要累死,这种重复性的机械劳动可是我最讨厌的了!好在咱还会写两行代码,看能不能用程序把这些歌曲提取出来。
好了,从分析Windows Media Player的播放列表入手。在Windows Media Player的音乐存储目录下我发现了一个“My Playlists”目录,里面有若干个.wpl文件。不管三七二十一,用记事本打开看看。结构如下:
<?wpl version="1.0"?>
<smil>
<head>
<meta name="Generator" content="Microsoft Windows Media Player -- 9.0.0.3128"/>
<author/>
<title>yFavorite</title>
</head>
<body>
<seq>
<media src="..\MovieMusic03\My Sassy Girl(我的野蛮女友)\02.I Believe.mp3" tid="{D5B723B4-A11F-4DE9-86C4-58A5803BE024}"/>
<media src="..\MovieMusic03\My Sassy Girl(我的野蛮女友)\17.Episode 4 (Reg Time).mp3" tid="{79DE022B-0E10-464B-8E00-10FD3D200E1C}"/>
<media src="..\MovieMusic04\天空の城\时间の城.mp3" tid="{D01404B4-0DB7-444E-AA06-28716F740D78}"/>
...(后面省略)
呵呵,这差不多就是xml文件啊,播放列表中所有歌曲的名字和路径都在里面,好办。
思路:把.wpl文件中从第二行开始的信息作为xml片断获得后,提取<media>节点的“src”属性值,再转换为绝对路径,然后复制到目标目录里。
设计界面如下:
主要方法:
/// 以DOM方式读取xml数据,复制文件,并以进度条控件显示执行进度。
/// </summary>
private void CopySongsWithDoc( string wplFileName, string destDir )
{
string songName;
string songDir = wplFileName.Substring( 0, wplFileName.LastIndexOf( @"" ) );
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml( this.GetXmlFragment( wplFileName ) );
XmlNodeList songsList = xmlDoc.GetElementsByTagName( "media" );
this.progressBar1.Maximum = songsList.Count;
foreach( XmlNode xn in songsList )
{
// 歌曲文件完整的绝对路径
songName = this.RelativePathToAbsolutePath( xn.Attributes[ "src" ].Value, songDir );
if( File.Exists( songName ) )
File.Copy( songName, destDir + songName.Substring( songName.LastIndexOf( @"" ) ), true );
this.progressBar1.Value += 1;
}
MessageBox.Show( "复制完成。" );
}
/// <summary>
/// 把相对路径转换为绝对路径
/// </summary>
/// <param name="relativePath">相对路径</param>
/// <param name="currentDir">相对路径的当前目录</param>
/// <returns>对应的绝对路径</returns>
private string RelativePathToAbsolutePath( string relativePath, string currentDir )
{
if( relativePath.IndexOf( ":" ) > 0 ) // 相对路径在另一个逻辑盘分区里,直接返回即可。
return relativePath;
else
{
int lastSlashIndex = relativePath.LastIndexOf( @".." );
if( lastSlashIndex >= 0 ) // 如果从当前目录进入到相对路径所指示的目录需要先“向上”时
{
// 相对路径中应该“向上”的目录层数,即“..”的个数
int upLayerCount = relativePath.Substring( 0, lastSlashIndex ).Split( new char[] { '\' } ).Length;
string[] sArray = currentDir.Split( new char[] { '\' } );
// 当前目录的层数
int currentDirLayerCount = sArray.Length;
string dirAdded = "";
if( currentDirLayerCount > upLayerCount )
{
// 相对路径中每出现一次“..”,则 currentDir 就应该“向上”一级。
// leftDirLayerCount 指 currentDir 中应该保留的目录层数。
int leftDirLayerCount = currentDirLayerCount - upLayerCount;
for( int i = 0; i < leftDirLayerCount; i++ )
{
dirAdded += sArray[ i ] + @"";
}
}
return dirAdded + relativePath.Substring( lastSlashIndex + 3 );
}
else // 如果相对路径所指示的目录在当前目录内,但不在另一个逻辑盘分区里
return currentDir + @"" + relativePath;
}
}
/// <summary>
/// 从 WPL 文件中获取需要的 xml 片断。
/// </summary>
/// <param name="wplFileName">wpl文件名</param>
/// <returns>xml片断</returns>
private string GetXmlFragment( string wplFileName )
{
StreamReader sReader = new StreamReader( wplFileName, Encoding.UTF8 );
string xmlFragment = sReader.ReadToEnd();
// 把 wpl 文件的第一行去掉,从第二行开始取。
xmlFragment = xmlFragment.Remove( 0, xmlFragment.IndexOf( @"<", 1 ) );
sReader.Close();
return xmlFragment;
}
就这么简单,技术、方法都很初级,但自力更生的感觉还是很美妙的。 ^_^