{S0}简介
本文演示了一个工具,它可以自动生成iTunes播放列表。我们可以创建播放列表,他们已被添加到iTunes资料库的文件夹结构相同。当我们看到后自动生成的iTunes播放列表,将创建的目录结构。此文件夹包含在您的硬盘的确切妆的所有歌曲。要求
使用从苹果公司最畅销的音乐播放器之一时,我发现很难找到设备的歌曲或音乐专辑。这是非常确定,如果我们有一个少数歌曲。但对于音乐随身携带的音乐文件吨,吨的怪胎,它的那种痛苦地发现一个特定的专辑或电影。此播放器提供了这么多的方法,查找和管理歌曲和专辑,但他们都变得有点无用相册计数时。毫无疑问,苹果公司的iPod是在世界上最流行的音乐播放器之一,我预计iTunes将提供一个很好的方法来处理选择 - 但他们不。解决方案
所有我的研究给我买了一个结论:至少在台式机用户的角度来看,iTunes中没有内置,同时考虑大罪状。回想当我用来听歌曲从桌面的天,我曾经安排在几个月甚至几年分组的文件夹中的专辑,也为不同类别,如音乐剧,专辑,电影歌曲,不同的语言等那些日子不同的文件夹,我用拖放到我最喜爱的的播放器Winamp的文件夹,并清除播放列表中,每当我想,或将其保存为播放列表,以备后用。幸运的是,此功能是部分在iTunes中。我可以创造一个"在去"播放列表的iPod上的飞行,并明确表示,只要是不需要。但仍然存在一个问题。我如何找到的专辑,从1000说,我想只找到一些在2005年1月发布的歌曲大集合?我需要创建一个工具,它可以创建在相同的确切顺序出现在我的硬盘中的歌曲。在我的研究发现的iTunes SDK使我的生活更轻松。感谢苹果。如何使用应用程序
应用程序UI包含五个主要元素。您可以使用在应用程序提供的默认设置。甚至有一些小的刀尖提供的帮助。
步骤:启动应用程序和根文件夹重命名,如果需要的话。点击"同步播放"按钮。等待几秒钟,开始操作。你可以看到iTunes的入门和指定的播放列表文件夹下创建新的文件夹和播放列表。如果你想中止操作,你可以在任何时候。迄今取得的进展将被保存在工作文件夹中的iTunes根。如果你喜欢保持它将会在备份文件夹中保存的备份,但是这将更新到您的iPod下次同步时。使用代码
完整的开发周期,我们将看到四个阶段。模拟iTunes文件夹子系统的设计。iTunes的接口系统的设计。用户界面的设计。性能优化和改善用户体验。1)设计模拟iTunes文件夹子系统 - 模型
当我开始调查我发现iTunes的子系统不处理我需要一棵树形成的观点的iTunes的架构。至少它不是暴露的API的一部分(或者更糟,我无法弄明白吗?)。对于每一步,我需要找到从集合中的元素,我没有看到一个可行的解决方案haivng主要的文件夹的名称,可邀请重复。为了解决这个问题,我需要在我的应用程序我自己的树子系统。
设计提取三类树中的元素,这实质上是一个基类在iTunes在iTunes子系统。
)文件夹,B)播放,C)轨道。
这里是实现这一目的的代码。 - FolderElement.cs
B - PlaylistElement.cs{C}C - TrackElement.csusing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTunesLib;
namespace ItnuesPlayListManager
{
public class FolderElement
{
public FolderElement(FolderElement parent,IITUserPlaylist itnuesfolder)
{
CreateNew(parent, itnuesfolder);
}
public FolderElement(FolderElement parent,string newfoldername)
{
IITUserPlaylist itnuesFolderSource = (
IITUserPlaylist)parent.ItnuesFolderSource.CreateFolder(newfoldername);
CreateNew(parent, itnuesFolderSource);
}
private bool CreateNew(FolderElement parent,IITUserPlaylist itnuesfolder)
{
ItnuesFolderSource = itnuesfolder;
this.Parent = parent;
SubFolders = new SortedDictionary<string, FolderElement>();
PlayLists = new SortedDictionary<string, PlayListElement>();
return true;
}
public FolderElement Parent
{
get; private set;
}
public IITUserPlaylist ItnuesFolderSource
{
get; private set;
}
public SortedDictionary<string, FolderElement> SubFolders
{
get; private set;
}
public SortedDictionary<string, PlayListElement> PlayLists
{
get; private set;
}
public bool MoveFolder(FolderElement destination)
{
if(destination.SubFolders.ContainsKey(this.ItnuesFolderSource.Name))
return false;
Parent = destination;
Parent.SubFolders.Remove(this.ItnuesFolderSource.Name);
destination.SubFolders.Add(this.ItnuesFolderSource.Name, this);
object val = destination.ItnuesFolderSource;
this.ItnuesFolderSource.set_Parent(ref val);
return true;
}
public bool DeleteSubFolder(FolderElement folder)
{
if (!this.SubFolders.ContainsKey(folder.ItnuesFolderSource.Name))
return false;
this.SubFolders.Remove(folder.ItnuesFolderSource.Name);
folder.ItnuesFolderSource.Delete();
return true;
}
}
}
2)iTunes的接口系统设计using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTunesLib;
namespace ItnuesPlayListManager
{
public class TrackElement
{
public TrackElement(PlayListElement playlistobject, IITTrack trackobject)
{
ItnuesTrackSource = trackobject;
object obj = (object)trackobject;
playlistobject.ItnuesFolderSource.AddTrack(ref obj);
Parent = playlistobject;
}
public PlayListElement Parent
{
get; private set;
}
public IITTrack ItnuesTrackSource
{
get; private set;
}
}
}
,模拟文件夹系统的设计完成后,我们应该提供一个接口来执行与iTunes进行通信的功能。下面是执行这些操作的类。ItnuesApp.cs
InteractionUtils.csusing System.Collections.Generic;
using iTunesLib;
namespace ItnuesPlayListManager
{
public static class ItnuesApp
{
static iTunesApp app;
public const string ROOTFOLDERNAME_BACKUP = "(Backup)";
public const string ROOTFOLDERNAME_WORKING = "(Working)";
public static iTunesApp Application
{
get
{
if(app ==null)
app = new iTunesAppClass();
return app;
}
}
public static bool Disconnect()
{
app = null;
return true;
}
public static PlayListElement GetPlaylist(FolderElement root, List<string> path)
{
PlayListElement leaf;
FolderElement currentfolder = root;
int i = 0;
for (i = 1; i < path.Count - 2; i++)
{
if (currentfolder.SubFolders.ContainsKey(path[i]) == false)
{
FolderElement folder = new FolderElement(currentfolder, path[i]);
currentfolder.SubFolders.Add(path[i], folder);
currentfolder = folder;
}
else
{
currentfolder = currentfolder.SubFolders[path[i]];
}
}
if (currentfolder.PlayLists.ContainsKey(path[i]) == true)
{
leaf = currentfolder.PlayLists[path[i]];
}
else
{
leaf = new PlayListElement(currentfolder, path[i]);
currentfolder.PlayLists.Add(path[i], leaf);
}
return leaf;
}
public static FolderElement GetWorkingRootFolder(string rootfoldername)
{
IITUserPlaylist rootworkingfolder = IsDuplicateSubFolderExists(null,
rootfoldername + ROOTFOLDERNAME_WORKING);
if (rootworkingfolder != null)
{
rootworkingfolder.Delete();
}
rootworkingfolder = (IITUserPlaylist)ItnuesApp.Application.CreateFolder(
rootfoldername + ROOTFOLDERNAME_WORKING);
return new FolderElement(null, rootworkingfolder);
}
public static bool ManageBackup(FolderElement newrootfolder, string rootfoldername,
bool keepabackup)
{
IITUserPlaylist rootfoldebackup = IsDuplicateSubFolderExists(null,
rootfoldername + ROOTFOLDERNAME_BACKUP);
IITUserPlaylist rootfoldercurrent = IsDuplicateSubFolderExists(null,
rootfoldername);
if (rootfoldebackup != null)
{
rootfoldebackup.Delete();
}
if (rootfoldercurrent != null)
{
if (keepabackup == true)
{
rootfoldercurrent.Name = rootfoldername + ROOTFOLDERNAME_BACKUP;
}
else
{
rootfoldercurrent.Delete();
}
}
newrootfolder.ItnuesFolderSource.Name = rootfoldername;
return true;
}
public static IITUserPlaylist IsDuplicateSubFolderExists(IITUserPlaylist play,
string foldername)
{
foreach (object item in ItnuesApp.Application.LibrarySource.Playlists)
{
if (item is IITUserPlaylist)
{
IITUserPlaylist itemIITUserPlaylist = (IITUserPlaylist)item;
if (itemIITUserPlaylist.Name.ToUpper() == foldername.ToUpper())
{
if (itemIITUserPlaylist.get_Parent() != null)
{
if (itemIITUserPlaylist.get_Parent().playlistID == play.playlistID)
return itemIITUserPlaylist;
}
else
return itemIITUserPlaylist;
}
}
}
return null;
}
}
}
3)用户界面设计using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Threading;
namespace ItnuesPlayListManager
{
public static class InteractionUtils
{
public static List<string> GetBrokenPaths(string path)
{
List<string> list = new List<string>();
string[] str = path.Split(new char[] { '\\', ':' }, 999,
StringSplitOptions.RemoveEmptyEntries);
list.AddRange(str.AsEnumerable());
return list;
}
public static void RemoteThreadUpdate(this Dispatcher ctrlControl,
Action mtdLambadaExpression)
{
if (ctrlControl.CheckAccess())
{
mtdLambadaExpression();
}
else
{
ctrlControl.BeginInvoke(DispatcherPriority.Normal, mtdLambadaExpression);
}
}
}
public class DataEventArgs<T> : EventArgs
{
public DataEventArgs(T data)
{
this.Data = data;
}
public T Data { get; set; }
}
}
最后,考虑UI界面设计时,它似乎使用MVC模式将是适当的UI功能似乎变得复杂。控制器包含iTunes界面和模拟文件夹系统之间的集成逻辑和最后提供输出到UI。本节由三部分组成背后仪表板,仪表板控制器和最后的板UI代码。DashBoardController.cs
DashBoard.xaml.csusing System;
using System.Collections.Generic;
using System.Linq;
using iTunesLib;
using System.Windows.Documents;
namespace ItnuesPlayListManager
{
public class DashBoardController
{
public class CreateEventArgs
{
public CreateEventArgs(string message,double progress,bool isErrorMessage)
{
Message = message;
Progress = progress;
IsErrorMessage = isErrorMessage;
}
public string Message { get; private set; }
public double Progress { get; private set; }
public bool IsErrorMessage { get; private set; }
}
public event EventHandler<DataEventArgs<CreateEventArgs>> ProgressStatus;
public event EventHandler<DataEventArgs<double>> BeforeCreate;
public event EventHandler<DataEventArgs<bool>> AfterCreate;
public FolderElement Root { get; private set; }
public void CreatePlaylistTree(object obj)
{
object[] objarray = (object[])obj;
bool val = CreatePlaylistTree((string)objarray[0], (bool)objarray[1],
(bool)objarray[2],(bool)objarray[3]);
if (AfterCreate != null) AfterCreate(this,new DataEventArgs<bool>(val));
}
public bool CreatePlaylistTree(string rootfoldername,bool deleteUnfoundtracks,
bool keepbackup,bool removeEmptyFolders)
{
List<IITFileOrCDTrack> trackstodelete = new List<IITFileOrCDTrack>();
var tracks = ItnuesApp.Application.LibraryPlaylist.Tracks;
var numTracks = tracks.Count;
int i=1;
PlayListElement element = null;
string lastlocation = string.Empty;
if(BeforeCreate!=null)
BeforeCreate(this, new DataEventArgs<double>(numTracks));
Root = ItnuesApp.GetWorkingRootFolder(rootfoldername);
//start create the playlists and folders
for (i = 1; i < numTracks; i++)
{
IITFileOrCDTrack currTrack = (IITFileOrCDTrack)tracks[i];
//temporary arrangement. skip the tracks of those the location is not
//found. TODO.need to find/remove/save the location of these files.
if (currTrack.Location != null)
{
string currentlocation = currTrack.Location.Substring(0,
currTrack.Location.LastIndexOf("\\"));
if (lastlocation == currentlocation)
{
element.SubTracks.Add(currTrack.Location.Substring(
currTrack.Location.LastIndexOf("\\")),new TrackElement(element,
tracks[i]));
}
else
{
List<string> patharray = InteractionUtils.GetBrokenPaths(
currTrack.Location);
element = ItnuesApp.GetPlaylist(Root, patharray);
element.SubTracks.Add(patharray[patharray.Count - 1],
new TrackElement(element, tracks[i]));
}
if(ProgressStatus!=null) ProgressStatus(this,
new DataEventArgs<CreateEventArgs>(new CreateEventArgs(
"Created [" + currTrack.Location + "] - [" + currTrack.Name + "]",
i, false)));
lastlocation = currentlocation;
}
else
{
if(deleteUnfoundtracks ==true)
{
if (ProgressStatus != null) ProgressStatus(this,
new DataEventArgs<CreateEventArgs>(new CreateEventArgs(
"Deleted the track [" + currTrack.Name + "]", i, true)));
}
}
}
//optionaly deletes the missing tracks.
foreach (var item in trackstodelete)
{
item.Delete();
}
//deletes the unrequired subfolders
if (removeEmptyFolders == true)
{
if (ProgressStatus != null) ProgressStatus(this,
new DataEventArgs<CreateEventArgs>(new CreateEventArgs(
"Removing unrequired folders", i, false)));
FolderElement singlefolder = Root;
while (singlefolder.SubFolders.Count <= 1)
{
singlefolder = singlefolder.SubFolders.ElementAt(0).Value;
}
if (singlefolder != Root)
{
FolderElement foldertodelete = Root.SubFolders.ElementAt(0).Value;
foreach (var item in singlefolder.SubFolders)
{
item.Value.MoveFolder(Root);
}
Root.DeleteSubFolder(foldertodelete);
}
}
ItnuesApp.ManageBackup(Root, rootfoldername, keepbackup);
return true;
}
}
}
4)性能优化和用户体验方面的改进using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Threading;
namespace ItnuesPlayListManager
{
public partial class DashBoard : Window
{
Thread navigatethread;
double MaxTracks = 0;
public DashBoard()
{
InitializeComponent();
//the scripting object will be locked even after utility terminates.
//so need to explicitily release those.
Application.Current.Exit+=new ExitEventHandler((x, y) =>
{
ItnuesApp.Disconnect();
ItnuesPlayListManager.Properties.Settings.Default.Save();
}
);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.chkRemoveUnfoundtracks.IsChecked =
ItnuesPlayListManager.Properties.Settings.Default.RemoveUnFoundTracks;
this.chkBackCurrent.IsChecked =
ItnuesPlayListManager.Properties.Settings.Default.KeepBackup;
this.txtRootFolderName.Text =
ItnuesPlayListManager.Properties.Settings.Default.AutoString;
this.chkRemoveUnusedRoots.IsChecked =
ItnuesPlayListManager.Properties.Settings.Default.RemoveUnusedRoots;
chkRemoveUnfoundtracks.Checked += new RoutedEventHandler((x, y) =>
{
if (MessageBox.Show("This will remove all the tracks from ITnues which" +
"the files cannot be located. Files from Removable Media or Network" +
" Locations also will be affected. " + Environment.NewLine +
Environment.NewLine + "Checking this option is recommended only" +
"if you have all the files in your local Hard Disk. Are You sure" +
" you want to check this option?", "File Removal",
MessageBoxButton.YesNo,MessageBoxImage.Question) == MessageBoxResult.No)
{
chkRemoveUnfoundtracks.IsChecked = false;
return;
}
ItnuesPlayListManager.Properties.Settings.Default.RemoveUnFoundTracks = true;
});
chkBackCurrent.Checked += new RoutedEventHandler((x, y) =>
{
ItnuesPlayListManager.Properties.Settings.Default.KeepBackup = true;
});
chkRemoveUnusedRoots.Checked+=new RoutedEventHandler((x,y) =>
{
ItnuesPlayListManager.Properties.Settings.Default.RemoveUnusedRoots = true;
});
chkRemoveUnfoundtracks.Unchecked += new RoutedEventHandler((x, y) =>
{
ItnuesPlayListManager.Properties.Settings.Default.RemoveUnFoundTracks = false;
});
chkBackCurrent.Unchecked += new RoutedEventHandler((x, y) =>
{
ItnuesPlayListManager.Properties.Settings.Default.KeepBackup = false;
});
chkRemoveUnusedRoots.Unchecked += new RoutedEventHandler((x, y) =>
{
ItnuesPlayListManager.Properties.Settings.Default.RemoveUnusedRoots = false;
});
txtRootFolderName.TextChanged += new TextChangedEventHandler((x, y) =>
{
ItnuesPlayListManager.Properties.Settings.Default.AutoString =
txtRootFolderName.Text;
});
}
private void btnsyncplaylist_Click(object sender, RoutedEventArgs e)
{
try
{
if (txtRootFolderName.Text.Trim().Length < 1)
{
MessageBox.Show("Root folder name cannot be empty.");
return;
}
if (navigatethread == null)
{
DashBoardController playlistmanager = new DashBoardController();
playlistmanager.ProgressStatus +=
new EventHandler<DataEventArgs<DashBoardController.CreateEventArgs>>(
playlistmanager_ProgressStatus);
playlistmanager.BeforeCreate +=
new EventHandler<DataEventArgs<double>>((x,
y) => { MaxTracks = y.Data; );
playlistmanager.AfterCreate += new EventHandler<DataEventArgs<bool>>(
playlistmanager_AfterCreate);
lstOutput.Items.Clear();
lstOutput.Items.Add("Connecting to ITnues Application..");
barProgress.Value = 0;
navigatethread = new Thread(new ParameterizedThreadStart(
playlistmanager.CreatePlaylistTree));
navigatethread.Start(new object[] {txtRootFolderName.Text,
chkRemoveUnfoundtracks.IsChecked.Value,chkBackCurrent.IsChecked.Value,
chkRemoveUnusedRoots.IsChecked.Value});
btnsyncplaylist.Content = "Abort";
}
else
{
navigatethread.Abort();
navigatethread = null;
btnsyncplaylist.Content = "Sync Playlist";
}
}
catch (Exception ex)
{
lstOutput.Items.Add(ex.Message);
}
}
void playlistmanager_AfterCreate(object sender, DataEventArgs<bool> e)
{
btnsyncplaylist.Dispatcher.RemoteThreadUpdate(() =>
{
btnsyncplaylist.Content = "Sync Playlist";
navigatethread = null;
});
}
void playlistmanager_ProgressStatus(object sender,
DataEventArgs<DashBoardController.CreateEventArgs> e)
{
lstOutput.Dispatcher.RemoteThreadUpdate(() =>
{
if (e.Data.IsErrorMessage == false)
lstOutput.Items.Insert(0, e.Data.Message);
else
{
ListBoxItem item = new ListBoxItem();
item.Foreground = Brushes.Red;
item.Content = e.Data.Message;
lstOutput.Items.Insert(0,item);
}
});
barProgress.Dispatcher.RemoteThreadUpdate(() =>
{
barProgress.Value = (e.Data.Progress / MaxTracks) * 100;
}
);
}
}
}
确定。最后,我们得到了应用程序的编译和运行。如果您尝试运行应用程序,你可以看到在硬盘上的歌曲文件夹中的结构相同iTunes的应用程序创建的子文件夹。显然,你需要配置iTunes中的iTunes文件夹复制到您的所有歌曲文件,因为你将无法控制您的文件的位置。这是你可以得到很多的音乐你的口袋里,并得到更好地控制你的音乐文件(至少对我的作品完美)的方式。
应用程序启动和正常运行并创建播放列表。但我想带出一些性能调整,并有一些用户体验的项目添加到应用程序。1)多线程组件 - 冗长的操作过程中停止冻结的用户界面
我们可以看到在iTunes的输出,但是,我们的应用程序没有响应,直到操作完成。这是因为UI和后台操作是在同一个线程,所以我们需要从主线程的iTunes操作。因此,我们共创另一个该线程。第1节)
这段代码,可以发现在InteractionUtils类。这个扩展方法的重视,分派对象,我们可以通过一个lambda表达式或Action实例在主线程中执行操作。
第2节) public static void RemoteThreadUpdate(this Dispatcher ctrlControl,
Action mtdLambadaExpression)
{
if (ctrlControl.CheckAccess())
{
mtdLambadaExpression();
}
else
{
ctrlControl.BeginInvoke(DispatcherPriority.Normal, mtdLambadaExpression);
}
}
一个新的线程被实例(对象发件人,发送RoutedEventArgs)内btnsyncplaylist_Click DashBorad.xaml.cs navigatethread = new Thread(new ParameterizedThreadStart(
playlistmanager.CreatePlaylistTree));
navigatethread.Start(new object[] {txtRootFolderName.Text,
chkRemoveUnfoundtracks.IsChecked.Value,chkBackCurrent.IsChecked.Value,
chkRemoveUnusedRoots.IsChecked.Value});
相关数据传递到业务层,DashBoardController.cs超载
第3节) public void CreatePlaylistTree(object obj)
{
object[] objarray = (object[])obj;
bool val = CreatePlaylistTree((string)objarray[0], (bool)objarray[1],
(bool)objarray[2],(bool)objarray[3]);
if (AfterCreate != null) AfterCreate(this,new DataEventArgs<bool>(val));
}
更新进步和操作日志的用户界面,需要从子线程的消息传递。这是通过在DashBoardController.cs一些事件。 public event EventHandler<DataEventArgs<CreateEventArgs>> ProgressStatus;
public event EventHandler<DataEventArgs<double>> BeforeCreate;
public event EventHandler<DataEventArgs<bool>> AfterCreate;
这些事件在Dashboard.xaml.cs订阅
2)循环优化第1节) void playlistmanager_AfterCreate(object sender, DataEventArgs<bool> e)
void playlistmanager_ProgressStatus(object sender,
DataEventArgs<DashBoardController.CreateEventArgs> e)
iTunes不提供一种机制,存在一个特定的文件夹内找到一个播放列表/文件夹/曲目。 iTunes的软件开发工具包提供了一些有限的搜索功能,但在高度执行的方式,我们需要建立我们自己的搜索算法实现这样的功能。创建的文件夹系统,解决了这个问题。 (最初的时候我没有斜坡;在iTunes上的SDK我注意到缺少此功能,所以我决定创建一个树图系统,应用程序将能够导航和通过树木)。第2节)
我们得到了一些改善左。如果你调试通过曲目列表(LibraryPlaylist.Tracks),你会发现,在大多数情况下,曲目是在连续的顺序,每个文件夹可以包含多个曲目。因此,有没有需要导航从根到叶子,如果在同一播放当前的曲目是最后一首曲目。
这是由一个简单的位置检查, if (lastlocation == currentlocation)
创建一个显着的,而这种优化运行的应用程序。在你可以看到小停顿在一个新的文件夹位于输出。3)选择性地删除缺少的轨道
的方法public bool CreatePlaylistTree(string rootfoldername,bool deleteUnfoundtracks,
bool keepbackup,bool removeEmptyFolders)
你可以看到这个代码删除哪个位置是空的轨道。我知道iTunes商店原来的位置soemwhere我不能findout。 Probabaly它不是通过COM API之外。
4)可以从根目录中删除空文件夹 if (currTrack.Location != null)
{
.....
}
else
{
if(deleteUnfoundtracks ==true)
{
if (ProgressStatus != null) ProgressStatus(this,
new DataEventArgs<CreateEventArgs>(new CreateEventArgs(
"Deleted the track [" + currTrack.Name + "]", i, true)));
trackstodelete.Add(currTrack);
}
}
....
foreach (var item in trackstodelete)
{
item.Delete();
}
此功能是有用的,如果主要的音乐文件夹开始像iTunes Music文件夹的子文件夹内的某个地方。我相信而通过iTunes naviagting人不想通过一系列空的播放列表文件夹导航到一个子文件夹达到。的方法,你可以找到这样的代码:
5)正确断开的iTunes private void RemoveEmptyFolders(bool removeEmptyFolders)
的iTunes instnce将被锁定,即使在该实用程序终止。出现这种情况,因为。NET不能释放COM资源,即使在应用程序终止。因此,我们需要释放那些explicitily。
例外该工具只考虑标题可以发现其中的物理路径。通常情况下,如果你打开iTunes,如果你看到一个感叹号(!)到您的轨道附近的标志,那些不会自动生成播放列表。我的工作后,这个,你可以期望未来的版本与此修复程序。建议在iTunes中设置的选项,所以它应该不会复制到iTunes中的本地文件夹添加到库中的所有文件。像那些被转换成几个文件确定。太多了,将结束在同样的情况发生默认的iTunes功能,在一个名为"我的音乐"文件夹中有太多的播放列表。系统需求 Application.Current.Exit+=new ExitEventHandler((x, y) =>
{
ItnuesApp.Disconnect();
ItnuesPlayListManager.Properties.Settings.Default.Save();
});
这个软件是已知的工作具有以下配置:微软视窗XP的SP1/Vista/Windows7微软。NET框架3.5
苹果公司的iTunes 9.0或更高版本
该软件可能工作在不同的配置,但是这并没有核实日期。结论
我希望本文帮助的人希望有一个小工具,他们可以摆脱在发现在他们的苹果音乐播放设备的音乐的痛苦。虽然有很多在MAC平台,我没有看到很多人做接口,在Windows平台上的iTunes。我想给那些希望有一个入门的例子休息。请添加下面在论坛上的意见,建议,和改善。