I am implementing a music player for Windows 10, and I am in a bit of a pickle at the library phase.
I am scanning for all the files in a music library (plus optional folders the user selects) and I need to get their tags (only need the title and artist; don't need complex details).
The problem is performance. Getting the music properties cost a LOT if you have a large media library. At the moment I am doing the following:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(async () =>
{
try
{
var files = await StorageHelper.GetFiles(KnownFolders.MusicLibrary);
List<LocalAudio> tempList = new List<LocalAudio>();
foreach (var file in files)
{
MusicProperties properties = await file.Properties.GetMusicPropertiesAsync();
var title = string.IsNullOrWhiteSpace(properties.Title) ? file.Name : properties.Title;
var artist = string.IsNullOrWhiteSpace(properties.Artist) ? file.Path : properties.Artist;
tempList.Add(new LocalAudio() { Title = title, FilePath = file.Path, Artist = artist, Duration = properties.Duration });
if (tempList.Count > 50)
{
await AddToLibrary(tempList);
tempList.Clear();
}
}
await AddToLibrary(tempList);
tempList.Clear();
}
catch (Exception ex)
{
//log exception here
}
});
}
private async Task AddToLibrary(List<LocalAudio> tempList)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
foreach (var item in tempList)
{
this.LocalFiles.Add(item);
}
});
}
Where LocalAudio
is my audio model and this.LocalFiles
is an ObservableCollection
. Also, the StorageHelper.GetFiles(StorageFolder)
method returns an IEnumerable
of StorageFiles
(music files) (it's a recursive method to go in depth).
I am adding batches of 50 files on the dispatcher thread so I won't block the UI (I know, even if it is an async method running in a task that I started by fire-and-forget it still blocks the UI if I add them continuously...).
How can I optimize this better? Any idea is good. I was thinking to scan them once and save the music properties (title, artist and duration) in a file and read that file on application start and just sync the filename with the data I have in that file (a dictionary or something and read the file and keep it in memory). Not sure how good this idea is.