[banshee] Add iTunes importer (bgo#441093)
- From: Alexander Kojevnikov <alexk src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [banshee] Add iTunes importer (bgo#441093)
- Date: Wed, 11 Nov 2009 05:15:56 +0000 (UTC)
commit 6919307cc7ebbc6dc41b6f527911f8f264b18f7a
Author: Alexander Kojevnikov <alexander kojevnikov com>
Date: Wed Nov 11 16:15:10 2009 +1100
Add iTunes importer (bgo#441093)
Banshee.sln | 7 +
build/build.environment.mk | 2 +-
.../Banshee.Library/ThreadPoolImportSource.cs | 9 +-
.../Banshee.PlayerMigration.addin.xml | 7 +-
.../Banshee.PlayerMigration.csproj | 79 ++
.../ItunesPlayerImportDialogs.cs | 217 ++++++
.../ItunesPlayerImportSource.cs | 818 ++++++++++++++++++++
src/Extensions/Banshee.PlayerMigration/Makefile.am | 5 +-
8 files changed, 1136 insertions(+), 8 deletions(-)
---
diff --git a/Banshee.sln b/Banshee.sln
index fd0f2a5..3fb6a39 100644
--- a/Banshee.sln
+++ b/Banshee.sln
@@ -65,6 +65,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.Lastfm", "src\Exten
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.Wikipedia", "src\Extensions\Banshee.Wikipedia\Banshee.Wikipedia.csproj", "{BF5D1722-269B-452E-B577-AEBA0CB894BA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.PlayerMigration", "src\Extensions\Banshee.PlayerMigration\Banshee.PlayerMigration.csproj", "{0AB92BF8-3A25-46AD-9748-1236471E9408}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.PlayQueue", "src\Extensions\Banshee.PlayQueue\Banshee.PlayQueue.csproj", "{74B2E4CC-2701-4C8B-A11D-6E4443F4B21B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.Moblin", "src\Extensions\Banshee.Moblin\Banshee.Moblin.csproj", "{4FBB954A-5CA9-44DC-97DA-7D549AC3EADB}"
@@ -187,6 +189,8 @@ Global
{6FF6F049-9DAB-48A7-BC4B-F7F3ED0EBA63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7271F1DF-1E15-4324-8102-E3D911A62BC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7271F1DF-1E15-4324-8102-E3D911A62BC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0AB92BF8-3A25-46AD-9748-1236471E9408}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0AB92BF8-3A25-46AD-9748-1236471E9408}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74B2E4CC-2701-4C8B-A11D-6E4443F4B21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74B2E4CC-2701-4C8B-A11D-6E4443F4B21B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E8D7EAD-3B7A-4F7D-8146-75AFCB9DEE83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -291,6 +295,8 @@ Global
{8E8D7EAD-3B7A-4F7D-8146-75AFCB9DEE83}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
{4FBB954A-5CA9-44DC-97DA-7D549AC3EADB}.Windows|Any CPU.Build.0 = Windows|Any CPU
{4FBB954A-5CA9-44DC-97DA-7D549AC3EADB}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
+ {0AB92BF8-3A25-46AD-9748-1236471E9408}.Windows|Any CPU.Build.0 = Windows|Any CPU
+ {0AB92BF8-3A25-46AD-9748-1236471E9408}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
{74B2E4CC-2701-4C8B-A11D-6E4443F4B21B}.Windows|Any CPU.Build.0 = Windows|Any CPU
{74B2E4CC-2701-4C8B-A11D-6E4443F4B21B}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
{02FD8195-9796-4AF5-A9D2-D310721963F4}.Windows|Any CPU.Build.0 = Windows|Any CPU
@@ -378,6 +384,7 @@ Global
{BFF64BCD-D7A7-4FB8-8147-7DEF7C3DC525} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{8E8D7EAD-3B7A-4F7D-8146-75AFCB9DEE83} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{4FBB954A-5CA9-44DC-97DA-7D549AC3EADB} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
+ {0AB92BF8-3A25-46AD-9748-1236471E9408} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{74B2E4CC-2701-4C8B-A11D-6E4443F4B21B} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{02FD8195-9796-4AF5-A9D2-D310721963F4} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{BF5D1722-269B-452E-B577-AEBA0CB894BA} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
diff --git a/build/build.environment.mk b/build/build.environment.mk
index 740240e..b4d71ae 100644
--- a/build/build.environment.mk
+++ b/build/build.environment.mk
@@ -130,7 +130,7 @@ REF_EXTENSION_MINIMODE = $(LINK_BANSHEE_THICKCLIENT_DEPS)
REF_EXTENSION_MOBLIN = $(LINK_BANSHEE_THICKCLIENT_DEPS)
REF_EXTENSION_MULTIMEDIAKEYS = $(LINK_BANSHEE_SERVICES_DEPS)
REF_EXTENSION_NOTIFICATIONAREA = $(LINK_BANSHEE_THICKCLIENT_DEPS)
-REF_EXTENSION_PLAYER_MIGRATION = $(LINK_BANSHEE_SERVICES_DEPS)
+REF_EXTENSION_PLAYER_MIGRATION = $(LINK_BANSHEE_THICKCLIENT_DEPS)
REF_EXTENSION_PLAYQUEUE = $(LINK_BANSHEE_THICKCLIENT_DEPS)
LINK_EXTENSION_PLAYQUEUE = -r:$(DIR_BIN)/Banshee.PlayQueue.dll
LINK_EXTENSION_PLAYQUEUE_DEPS = $(REF_EXTENSION_PLAYQUEUE) \
diff --git a/src/Core/Banshee.Services/Banshee.Library/ThreadPoolImportSource.cs b/src/Core/Banshee.Services/Banshee.Library/ThreadPoolImportSource.cs
index 68ed824..191c650 100644
--- a/src/Core/Banshee.Services/Banshee.Library/ThreadPoolImportSource.cs
+++ b/src/Core/Banshee.Services/Banshee.Library/ThreadPoolImportSource.cs
@@ -141,7 +141,7 @@ namespace Banshee.Library
public void Import ()
{
- if (importing) {
+ if (importing || !ConfirmImport ()) {
return;
}
@@ -158,7 +158,12 @@ namespace Banshee.Library
ImportCore ();
DestroyUserJob ();
}
-
+
+ protected virtual bool ConfirmImport ()
+ {
+ return true;
+ }
+
protected abstract void ImportCore ();
}
diff --git a/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.addin.xml b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.addin.xml
index 04087ff..1002c00 100644
--- a/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.addin.xml
+++ b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.addin.xml
@@ -4,10 +4,10 @@
version="1.0"
compatVersion="1.0"
copyright="Licensed under the MIT X11 license."
- name="Importers for Amarok and Rhythmbox"
+ name="Importers for Amarok, Rhythmbox and iTunes"
category="Core"
- description="Import your Amarok or Rhythmbox music library"
- author="Paul Lange, Sebastian Dröge"
+ description="Import your Amarok, Rhythmbox or iTunes music library"
+ author="Paul Lange, Sebastian Dröge, Scott Peterson, Alexander Kojevnikov"
url="http://banshee-project.org/"
defaultEnabled="true">
@@ -18,6 +18,7 @@
<Extension path="/Banshee/Library/ImportSource">
<ImportSource class="Banshee.PlayerMigration.RhythmboxPlayerImportSource"/>
<ImportSource class="Banshee.PlayerMigration.AmarokPlayerImportSource"/>
+ <ImportSource class="Banshee.PlayerMigration.ItunesPlayerImportSource"/>
</Extension>
</Addin>
diff --git a/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.csproj b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.csproj
new file mode 100644
index 0000000..f703072
--- /dev/null
+++ b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration.csproj
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.50727</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{0AB92BF8-3A25-46AD-9748-1236471E9408}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AssemblyName>Banshee.PlayerMigration</AssemblyName>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Banshee.PlayerMigration\AmarokPlayerImportSource.cs" />
+ <Compile Include="Banshee.PlayerMigration\RhythmboxPlayerImportSource.cs" />
+ <Compile Include="Banshee.PlayerMigration\ItunesPlayerImportSource.cs" />
+ <Compile Include="Banshee.PlayerMigration\ItunesPlayerImportDialogs.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <EmbeddedResource Include="Banshee.PlayerMigration.addin.xml" />
+ </ItemGroup>
+ <ProjectExtensions>
+ <MonoDevelop>
+ <Properties>
+ <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile.am" IsAutotoolsProject="true" RelativeConfigureInPath="../../..">
+ <BuildFilesVar Sync="true" Name="SOURCES" />
+ <DeployFilesVar />
+ <ResourcesVar Sync="true" Name="RESOURCES" />
+ <OthersVar />
+ <GacRefVar />
+ <AsmRefVar />
+ <ProjectRefVar />
+ <MessageRegex Name="Vala" />
+ </MonoDevelop.Autotools.MakefileInfo>
+ </Properties>
+ </MonoDevelop>
+ </ProjectExtensions>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Core\Banshee.Core\Banshee.Core.csproj">
+ <Project>{2ADB831A-A050-47D0-B6B9-9C19D60233BB}</Project>
+ <Name>Banshee.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Core\Banshee.Services\Banshee.Services.csproj">
+ <Project>{B28354F0-BA87-44E8-989F-B864A3C7C09F}</Project>
+ <Name>Banshee.Services</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Core\Banshee.ThickClient\Banshee.ThickClient.csproj">
+ <Project>{AC839523-7BDF-4AB6-8115-E17921B96EC6}</Project>
+ <Name>Banshee.ThickClient</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Core\Banshee.Widgets\Banshee.Widgets.csproj">
+ <Project>{A3701765-E571-413D-808C-9788A22791AF}</Project>
+ <Name>Banshee.Widgets</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration/ItunesPlayerImportDialogs.cs b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration/ItunesPlayerImportDialogs.cs
new file mode 100644
index 0000000..2d25d26
--- /dev/null
+++ b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration/ItunesPlayerImportDialogs.cs
@@ -0,0 +1,217 @@
+//
+// ItunesPlayerImportDialogs.cs
+//
+// Author:
+// Scott Peterson <lunchtimemama gmail com>
+//
+// Copyright (C) 2007 Scott Peterson
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using Gtk;
+using Mono.Unix;
+
+using Banshee.Base;
+
+namespace Banshee.PlayerMigration
+{
+ public class ItunesImportDialog : Dialog
+ {
+ private string library_uri;
+ protected readonly Button import_button;
+ protected readonly CheckButton ratings;
+ protected readonly CheckButton stats;
+ protected readonly CheckButton playlists;
+
+ public string LibraryUri
+ {
+ get { return library_uri; }
+ }
+
+ public bool Ratings
+ {
+ get { return ratings.Active; }
+ }
+ public bool Stats
+ {
+ get { return stats.Active; }
+ }
+ public bool Playliststs
+ {
+ get { return playlists.Active; }
+ }
+
+ public ItunesImportDialog () : base ()
+ {
+ // TODO add Help button and dialog/tooltip
+
+ Title = Catalog.GetString ("iTunes Importer");
+ Resizable = false;
+ VBox.BorderWidth = 8;
+ VBox.Spacing = 8;
+
+ Button cancel_button = new Button (Stock.Cancel);
+ cancel_button.Clicked += delegate { Respond (ResponseType.Cancel); };
+ cancel_button.ShowAll ();
+ AddActionWidget (cancel_button, ResponseType.Cancel);
+ cancel_button.CanDefault = true;
+ cancel_button.GrabFocus ();
+ DefaultResponse = ResponseType.Cancel;
+
+ import_button = new Button ();
+ import_button.Label = Catalog.GetString ("_Import");
+ import_button.UseUnderline = true;
+ import_button.Image = Image.NewFromIconName (Stock.Open, IconSize.Button);
+ import_button.Clicked += delegate { Respond (ResponseType.Ok); };
+ import_button.ShowAll ();
+ AddActionWidget (import_button, ResponseType.Ok);
+
+ VBox vbox = new VBox ();
+ ratings = new CheckButton (Catalog.GetString ("Import song ratings"));
+ ratings.Active = true;
+ vbox.PackStart (ratings);
+ stats = new CheckButton (Catalog.GetString ("Import play statistics (playcount, etc.)"));
+ stats.Active = true;
+ vbox.PackStart (stats);
+ playlists = new CheckButton (Catalog.GetString ("Import playlists"));
+ playlists.Active = true;
+ vbox.PackStart (playlists);
+
+ PackCheckboxes (vbox);
+
+ VBox.ShowAll ();
+ }
+
+ protected virtual void PackCheckboxes (VBox vbox)
+ {
+ string possible_location = System.IO.Path.Combine (System.IO.Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), "iTunes"),
+ ItunesPlayerImportSource.LibraryFilename);
+
+ if (Banshee.IO.File.Exists (new SafeUri (possible_location))) {
+ library_uri = possible_location;
+ } else {
+ HBox hbox = new HBox ();
+ hbox.Spacing = 8;
+ Image image = new Image (IconTheme.Default.LoadIcon ("gtk-open", 18, 0));
+ hbox.PackStart (image);
+ Label label1 = new Label ();
+ label1.Markup = String.Format ("<b>{0}</b>", GLib.Markup.EscapeText(
+ String.Format( Catalog.GetString (@"Locate your ""{0}"" file..."),
+ ItunesPlayerImportSource.LibraryFilename)));
+ label1.SetAlignment (0.0f, 0.5f);
+ hbox.PackStart (label1);
+ Button browse_button = new Button (hbox);
+ browse_button.Clicked += OnBrowseButtonClicked;
+ VBox.PackStart (browse_button);
+
+ ratings.Sensitive = stats.Sensitive = playlists.Sensitive = import_button.Sensitive = false;
+ }
+
+ VBox.PackStart (vbox);
+ }
+
+ private void OnBrowseButtonClicked (object o, EventArgs args)
+ {
+ Button browse_button = o as Button;
+ using(FileChooserDialog file_chooser = new FileChooserDialog(
+ String.Format (Catalog.GetString (@"Locate ""{0}"""), ItunesPlayerImportSource.LibraryFilename),
+ this, FileChooserAction.Open,
+ Stock.Cancel, ResponseType.Cancel,
+ Stock.Open, ResponseType.Ok)) {
+
+ FileFilter filter = new FileFilter ();
+ filter.AddPattern ("*" + ItunesPlayerImportSource.LibraryFilename);
+ filter.Name = ItunesPlayerImportSource.LibraryFilename;
+ file_chooser.AddFilter (filter);
+ if (file_chooser.Run () == (int)ResponseType.Ok) {
+ browse_button.Sensitive = false;
+ ratings.Sensitive = stats.Sensitive = playlists.Sensitive = import_button.Sensitive = true;
+ library_uri = file_chooser.Filename;
+ }
+ file_chooser.Destroy ();
+ }
+ }
+ }
+
+ public class ItunesMusicDirectoryDialog : Dialog
+ {
+ private FileChooserWidget chooser;
+
+ public string UserMusicDirectory {
+ get { return chooser.Filename; }
+ }
+
+ public ItunesMusicDirectoryDialog (string itunes_music_directory) : base ()
+ {
+ Title = Catalog.GetString ("Locate iTunes Music Directory");
+ HeightRequest = 650;
+ WidthRequest = 814;
+
+ Button cancel_button = new Button (Stock.Cancel);
+ cancel_button.Clicked += delegate { Respond (ResponseType.Cancel); };
+ cancel_button.ShowAll ();
+ AddActionWidget (cancel_button, ResponseType.Cancel);
+ cancel_button.CanDefault = true;
+ cancel_button.GrabFocus ();
+
+ Button ok_button = new Button (Stock.Ok);
+ ok_button.Clicked += delegate { Respond (ResponseType.Ok); };
+ ok_button.ShowAll ();
+ AddActionWidget (ok_button, ResponseType.Ok);
+
+ VBox vbox = new VBox ();
+ vbox.BorderWidth = 8;
+ vbox.Spacing = 10;
+
+ HBox hbox = new HBox ();
+ hbox.Spacing = 10;
+
+ Image image = new Image (Stock.DialogWarning, IconSize.Dialog);
+ hbox.PackStart (image, false, true, 0);
+
+ Label message = new Label ();
+ message.Markup = String.Format ("<b>{0}</b>", GLib.Markup.EscapeText(
+ String.Format (Catalog.GetString(
+ "The iTunes library refers to your music directory as \"{0}\" but " +
+ "Banshee was not able to infer the location of this directory. Please locate it."),
+ itunes_music_directory)));
+ message.Justify = Justification.Left;
+ message.WidthRequest = 750;
+ message.LineWrap = true;
+ hbox.PackStart (message, true, true, 0);
+
+ vbox.PackStart (hbox, false, true, 0);
+
+ chooser = new FileChooserWidget (FileChooserAction.SelectFolder);
+ chooser.ShowAll ();
+ vbox.PackStart (chooser, true, true, 0);
+
+ VBox.PackStart (vbox);
+
+ DefaultResponse = ResponseType.Cancel;
+
+ VBox.ShowAll ();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration/ItunesPlayerImportSource.cs b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration/ItunesPlayerImportSource.cs
new file mode 100644
index 0000000..39bd1ef
--- /dev/null
+++ b/src/Extensions/Banshee.PlayerMigration/Banshee.PlayerMigration/ItunesPlayerImportSource.cs
@@ -0,0 +1,818 @@
+//
+// ItunesPlayerImportSource.cs
+//
+// Authors:
+// Scott Peterson <lunchtimemama gmail com>
+// Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2007 Scott Peterson
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Xml;
+using Mono.Unix;
+using Gtk;
+
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+using Banshee.IO;
+using Banshee.Library;
+using Banshee.Playlist;
+using Banshee.ServiceStack;
+using Banshee.Sources;
+using Banshee.Widgets;
+using Hyena.Data.Sqlite;
+
+namespace Banshee.PlayerMigration
+{
+ public sealed class ItunesPlayerImportSource : ThreadPoolImportSource
+ {
+ // This is its own class so that we don't always load this stuff into memory
+ private class ItunesImportData
+ {
+ public string library_uri, default_query, local_prefix, fallback_dir;
+ public string[] query_dirs;
+ public bool get_ratings, get_stats, get_playlists, user_provided_prefix, empty_library;
+ public int total_songs, total_processed;
+ public Dictionary<int, int> track_ids = new Dictionary<int, int> (); // key=itunes_id, value=banshee_id
+ }
+
+ private readonly object mutex = new object ();
+ private volatile bool ok;
+
+ public const string LibraryFilename = "iTunes Music Library.xml";
+
+ public override string Name {
+ get { return Catalog.GetString ("iTunes Media Player"); }
+ }
+
+ public override bool CanImport {
+ get { return true; }
+ }
+
+ private ItunesImportData data;
+
+ protected override bool ConfirmImport ()
+ {
+ if (data == null) {
+ data = new ItunesImportData ();
+ var dialog = new ItunesImportDialog ();
+ if (!HandleImportDialog (dialog, delegate { data.library_uri = dialog.LibraryUri; })) {
+ data = null;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private delegate void ImportDialogHandler (ItunesImportDialog dialog);
+
+ private bool HandleImportDialog (ItunesImportDialog dialog, ImportDialogHandler code)
+ {
+ try {
+ if (dialog.Run () == (int)ResponseType.Ok) {
+ if(code != null) {
+ code (dialog);
+ }
+ data.get_ratings = dialog.Ratings;
+ data.get_stats = dialog.Stats;
+ data.get_playlists = dialog.Playliststs;
+ } else {
+ return false;
+ }
+ } finally {
+ dialog.Destroy ();
+ dialog.Dispose ();
+ }
+
+ if (String.IsNullOrEmpty (data.library_uri)) {
+ return false;
+ }
+
+ // Make sure the library version is supported (version 1.1)
+ string message = null;
+ bool prompt = false;
+ using (var xml_reader = new XmlTextReader (data.library_uri))
+ {
+ xml_reader.ReadToFollowing ("key");
+ do {
+ xml_reader.Read ();
+ string key = xml_reader.ReadContentAsString ();
+ if (key == "Major Version" || key == "Minor Version") {
+ xml_reader.Read ();
+ xml_reader.Read ();
+ if(xml_reader.ReadContentAsString () != "1") {
+ message = Catalog.GetString (
+ "Banshee is not familiar with this version of the iTunes library format." +
+ " Importing may or may not work as expected, or at all. Would you like to attempt to import anyway?");
+ prompt = true;
+ break;
+ }
+ }
+ } while (xml_reader.ReadToNextSibling ("key"));
+ }
+
+ if (prompt) {
+ bool proceed = false;
+ using (var message_dialog = new MessageDialog (null, 0, MessageType.Question, ButtonsType.YesNo, message)) {
+ if (message_dialog.Run () == (int)ResponseType.Yes) {
+ proceed = true;
+ }
+ message_dialog.Destroy ();
+ }
+ if (!proceed) {
+ LogError (data.library_uri, "Unsupported version");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected override void ImportCore ()
+ {
+ try {
+ CountSongs ();
+ data.empty_library = ServiceManager.SourceManager.MusicLibrary.TrackModel.Count == 0;
+
+ var import_manager = ServiceManager.Get<LibraryImportManager> ();
+ using (var xml_reader = new XmlTextReader (data.library_uri)) {
+ ProcessLibraryXml (import_manager, xml_reader);
+ }
+ import_manager.NotifyAllSources ();
+ } finally {
+ data = null;
+ }
+ }
+
+ private void CountSongs ()
+ {
+ using (var xml_reader = new XmlTextReader (data.library_uri)) {
+ xml_reader.ReadToDescendant("dict");
+ xml_reader.ReadToDescendant("dict");
+ xml_reader.ReadToDescendant("dict");
+ do {
+ data.total_songs++;
+ } while (xml_reader.ReadToNextSibling ("dict"));
+ }
+ }
+
+ private void ProcessLibraryXml (LibraryImportManager import_manager, XmlReader xml_reader)
+ {
+ while (xml_reader.ReadToFollowing ("key") && !CheckForCanceled ()) {
+ xml_reader.Read ();
+ string key = xml_reader.ReadContentAsString ();
+ xml_reader.Read ();
+ xml_reader.Read ();
+
+ switch (key) {
+ case "Music Folder":
+ if (!ProcessMusicFolderPath (xml_reader.ReadContentAsString ())) {
+ return;
+ }
+ break;
+ case "Tracks":
+ ProcessSongs (import_manager, xml_reader.ReadSubtree ());
+ break;
+ case "Playlists":
+ if (data.get_playlists) {
+ ProcessPlaylists (xml_reader.ReadSubtree ());
+ }
+ break;
+ }
+ }
+ }
+
+ private bool ProcessMusicFolderPath(string path)
+ {
+ string[] itunes_music_uri_parts = ConvertToLocalUriFormat (path).Split (Path.DirectorySeparatorChar);
+ string[] library_uri_parts = Path.GetDirectoryName (data.library_uri).Split (Path.DirectorySeparatorChar);
+
+ string itunes_dir_name = library_uri_parts[library_uri_parts.Length - 1];
+ int i = 0;
+ bool found = false;
+
+ for (i = itunes_music_uri_parts.Length - 1; i >= 0; i--) {
+ if (itunes_music_uri_parts[i] == itunes_dir_name) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ var builder = new StringBuilder (path.Length - 17);
+ for (int j = 3; j < itunes_music_uri_parts.Length; j++) {
+ string part = itunes_music_uri_parts[j];
+ builder.Append (part);
+ if (part.Length > 0) {
+ builder.Append (Path.DirectorySeparatorChar);
+ }
+ }
+
+ string local_path = builder.ToString ();
+
+ System.Threading.Monitor.Enter (mutex);
+
+ ThreadAssist.ProxyToMain (delegate {
+ System.Threading.Monitor.Enter (mutex);
+ using (var dialog = new ItunesMusicDirectoryDialog (local_path)) {
+ if (dialog.Run () == (int)ResponseType.Ok) {
+ data.local_prefix = dialog.UserMusicDirectory;
+ data.user_provided_prefix = true;
+ data.default_query = local_path;
+ ok = true;
+ } else {
+ ok = false;
+ }
+ dialog.Destroy ();
+ System.Threading.Monitor.Pulse (mutex);
+ System.Threading.Monitor.Exit (mutex);
+ }
+ });
+
+ System.Threading.Monitor.Wait (mutex);
+ System.Threading.Monitor.Exit (mutex);
+
+ if (ok) {
+ return true;
+ } else {
+ LogError (data.library_uri, "Unable to locate iTunes directory from iTunes URI");
+ return false;
+ }
+ }
+
+ string[] tmp_query_dirs = new string[itunes_music_uri_parts.Length];
+ string upstream_uri;
+ string tmp_upstream_uri = null;
+ int step = 0;
+ string root = Path.GetPathRoot (data.library_uri);
+ bool same_root = library_uri_parts[0] == root.Split (Path.DirectorySeparatorChar)[0];
+ do {
+ upstream_uri = tmp_upstream_uri;
+ tmp_upstream_uri = root;
+ for (int j = same_root ? 1 : 0; j < library_uri_parts.Length - step - 1; j++) {
+ tmp_upstream_uri = Path.Combine (tmp_upstream_uri, library_uri_parts[j]);
+ }
+ tmp_upstream_uri = Path.Combine (tmp_upstream_uri, itunes_music_uri_parts[i - step]);
+ data.fallback_dir = tmp_query_dirs[step] = itunes_music_uri_parts[i - step];
+ step++;
+ } while (Banshee.IO.Directory.Exists (tmp_upstream_uri));
+ if (upstream_uri == null) {
+ LogError (data.library_uri, "Unable to resolve iTunes URIs to local URIs");
+ return false;
+ }
+ data.query_dirs = new string[step - 2];
+ data.default_query = string.Empty;
+
+ for (int j = step - 2; j >= 0; j--) {
+ if (j > 0) {
+ data.query_dirs[j - 1] = tmp_query_dirs[j];
+ }
+ data.default_query += tmp_query_dirs[j] + Path.DirectorySeparatorChar;
+
+ }
+
+ data.local_prefix = string.Empty;
+ for (int j = 0; j <= library_uri_parts.Length - step; j++) {
+ data.local_prefix += library_uri_parts[j] + Path.DirectorySeparatorChar;
+ }
+
+ return true;
+ }
+
+ private void ProcessSongs (LibraryImportManager import_manager, XmlReader xml_reader)
+ {
+ using (xml_reader) {
+ xml_reader.ReadToFollowing ("dict");
+ while (xml_reader.ReadToFollowing ("dict") && !CheckForCanceled ()) {
+ ProcessSong (import_manager, xml_reader.ReadSubtree ());
+ }
+ }
+ }
+
+ private void ProcessPlaylists (XmlReader xml_reader)
+ {
+ using (xml_reader) {
+ while(xml_reader.ReadToFollowing ("dict") && !CheckForCanceled ()) {
+ ProcessPlaylist (xml_reader.ReadSubtree ());
+ }
+ }
+ }
+
+ private void ProcessSong (LibraryImportManager import_manager, XmlReader xml_reader)
+ {
+ data.total_processed++;
+
+ var itunes_id = 0;
+ var title = String.Empty;
+ var title_sort = String.Empty;
+ var genre = String.Empty;
+ var artist = String.Empty;
+ var artist_sort = String.Empty;
+ var album_artist = String.Empty;
+ var album_artist_sort = String.Empty;
+ var composer = String.Empty;
+ var album = String.Empty;
+ var album_sort = String.Empty;
+ var grouping = String.Empty;
+ var year = 0;
+ var rating = 0;
+ var play_count = 0;
+ var track_number = 0;
+ var date_added = DateTime.Now;
+ var last_played = DateTime.MinValue;
+
+ SafeUri uri = null;
+
+ using (xml_reader) {
+ while (xml_reader.ReadToFollowing ("key")) {
+ xml_reader.Read();
+ string key = xml_reader.ReadContentAsString ();
+ xml_reader.Read ();
+ xml_reader.Read ();
+
+ try {
+ switch (key) {
+ case "Track ID":
+ itunes_id = Int32.Parse (xml_reader.ReadContentAsString ());
+ break;
+ case "Name":
+ title = xml_reader.ReadContentAsString ();
+ break;
+ case "Sort Name":
+ title_sort = xml_reader.ReadContentAsString ();
+ break;
+ case "Genre":
+ genre = xml_reader.ReadContentAsString ();
+ break;
+ case "Artist":
+ artist = xml_reader.ReadContentAsString ();
+ break;
+ case "Sort Artist":
+ artist_sort = xml_reader.ReadContentAsString ();
+ break;
+ case "Album Artist":
+ album_artist = xml_reader.ReadContentAsString ();
+ break;
+ case "Sort Album Artist":
+ album_artist_sort = xml_reader.ReadContentAsString ();
+ break;
+ case "Composer":
+ composer = xml_reader.ReadContentAsString ();
+ break;
+ case "Album":
+ album = xml_reader.ReadContentAsString ();
+ break;
+ case "Sort Album":
+ album_sort = xml_reader.ReadContentAsString ();
+ break;
+ case "Grouping":
+ grouping = xml_reader.ReadContentAsString ();
+ break;
+ case "Year":
+ year = Int32.Parse (xml_reader.ReadContentAsString ());
+ break;
+ case "Rating":
+ rating = Int32.Parse (xml_reader.ReadContentAsString ()) / 20;
+ break;
+ case "Play Count":
+ play_count = Int32.Parse (xml_reader.ReadContentAsString ());
+ break;
+ case "Track Number":
+ track_number = Int32.Parse (xml_reader.ReadContentAsString ());
+ break;
+ case "Date Added":
+ date_added = DateTime.Parse (xml_reader.ReadContentAsString (),
+ DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal);
+ break;
+ case "Play Date UTC":
+ last_played = DateTime.Parse (xml_reader.ReadContentAsString (),
+ DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal);
+ break;
+ case "Location":
+ uri = ConvertToLocalUri (xml_reader.ReadContentAsString ());
+ break;
+ }
+ } catch {
+ }
+ }
+ }
+
+ if (uri == null) {
+ return;
+ }
+
+ UpdateUserJob (data.total_processed, data.total_songs, artist, title);
+
+ try {
+ DatabaseTrackInfo track = import_manager.ImportTrack (uri);
+
+ if (track == null) {
+ LogError (SafeUri.UriToFilename (uri), Catalog.GetString ("Unable to import song."));
+ return;
+ }
+
+ if (!String.IsNullOrEmpty (title)) {
+ track.TrackTitle = title;
+ }
+ if (!String.IsNullOrEmpty (title_sort)) {
+ track.TrackTitleSort = title_sort;
+ }
+ if (!String.IsNullOrEmpty (artist)) {
+ track.ArtistName = artist;
+ }
+ if (!String.IsNullOrEmpty (artist_sort)) {
+ track.ArtistNameSort = artist_sort;
+ }
+ if (!String.IsNullOrEmpty (genre)) {
+ track.Genre = genre;
+ }
+ if (!String.IsNullOrEmpty (album_artist)) {
+ track.AlbumArtist = album_artist;
+ }
+ if (!String.IsNullOrEmpty (album_artist_sort)) {
+ track.AlbumArtistSort = album_artist_sort;
+ }
+ if (!String.IsNullOrEmpty (composer)) {
+ track.Composer = composer;
+ }
+ if (!String.IsNullOrEmpty (album)) {
+ track.AlbumTitle = album;
+ }
+ if (!String.IsNullOrEmpty (album_sort)) {
+ track.AlbumTitleSort = album_sort;
+ }
+ if (!String.IsNullOrEmpty (grouping)) {
+ track.Grouping = grouping;
+ }
+ if (year > 0) {
+ track.Year = year;
+ }
+ if (data.get_ratings && rating > 0 && rating <= 5) {
+ track.Rating = rating;
+ }
+ if (data.get_stats && play_count > 0) {
+ track.PlayCount = play_count;
+ }
+ if (track_number > 0) {
+ track.TrackNumber = track_number;
+ }
+ if (data.get_stats) {
+ track.DateAdded = date_added;
+ }
+ if (data.get_stats && last_played > DateTime.MinValue) {
+ track.LastPlayed = last_played;
+ }
+
+ data.track_ids.Add (itunes_id, track.TrackId);
+
+ track.Save (false);
+ } catch (Exception e) {
+ LogError (SafeUri.UriToFilename (uri), e);
+ }
+ }
+
+ private void ProcessPlaylist (XmlReader xml_reader)
+ {
+ string name = string.Empty;
+ bool skip = false;
+ bool processed = false;
+
+ using (xml_reader) {
+ while (xml_reader.ReadToFollowing ("key")) {
+ xml_reader.Read ();
+ string key = xml_reader.ReadContentAsString ();
+ xml_reader.Read ();
+
+ switch (key) {
+ case "Name":
+ xml_reader.Read ();
+ name = xml_reader.ReadContentAsString ();
+ if (name == "Library" ||
+ name == "Music Videos" ||
+ name == "Audiobooks" ||
+ name == "Music" ||
+ name == "Movies" ||
+ name == "Party Shuffle" ||
+ name == "Podcasts" ||
+ name == "Party Shuffle" ||
+ name == "Purchased Music" ||
+ name == "Genius" ||
+ name == "TV Shows") {
+ skip = true;
+ }
+ break;
+ case "Smart Info":
+ skip = true;
+ break;
+ case "Smart Criteria":
+ skip = true;
+ break;
+ case "Playlist Items":
+ xml_reader.Read ();
+ if(!skip) {
+ ProcessPlaylist (name, xml_reader.ReadSubtree ());
+ processed = true;
+ }
+ break;
+ }
+ }
+ }
+
+ // Empty playlist
+ if (!processed && !skip) {
+ ProcessPlaylist (name, null);
+ }
+ }
+
+ private void ProcessPlaylist (string name, XmlReader xml_reader)
+ {
+ UpdateUserJob (1, 1, Catalog.GetString("Playlists"), name);
+
+ ProcessRegularPlaylist (name, xml_reader);
+ if (xml_reader != null) {
+ xml_reader.Close ();
+ }
+ }
+
+ private void ProcessRegularPlaylist (string name, XmlReader xml_reader)
+ {
+ var playlist_source = new PlaylistSource (name, ServiceManager.SourceManager.MusicLibrary);
+ playlist_source.Save ();
+ ServiceManager.SourceManager.MusicLibrary.AddChildSource (playlist_source);
+
+ // Get the songs in the playlists
+ if (xml_reader != null) {
+ while (xml_reader.ReadToFollowing ("integer") && !CheckForCanceled ()) {
+ xml_reader.Read ();
+ int itunes_id = Int32.Parse (xml_reader.ReadContentAsString ());
+ int track_id;
+ if (data.track_ids.TryGetValue (itunes_id, out track_id)) {
+ try {
+ ServiceManager.DbConnection.Execute (
+ "INSERT INTO CorePlaylistEntries (PlaylistID, TrackID) VALUES (?, ?)",
+ playlist_source.DbId, track_id);
+ } catch {
+ }
+ }
+ }
+ playlist_source.Reload ();
+ playlist_source.NotifyUser ();
+ }
+ }
+
+ private SafeUri ConvertToLocalUri (string raw_uri)
+ {
+ if (raw_uri == null) {
+ return null;
+ }
+
+ string uri = ConvertToLocalUriFormat (raw_uri);
+ int index = uri.IndexOf (data.default_query);
+
+ if (data.user_provided_prefix && index != -1) {
+ index += data.default_query.Length;
+ } else if (index == -1 && data.query_dirs.Length > 0) {
+ int count = 0;
+ string path = data.query_dirs[data.query_dirs.Length - 1];
+ do {
+ for (int k = data.query_dirs.Length - 2; k >= count; k--) {
+ path = Path.Combine (path, data.query_dirs[k]);
+ }
+ index = uri.IndexOf (path);
+ count++;
+ } while(index == -1 && count < data.query_dirs.Length);
+ if (index == -1) {
+ index = uri.IndexOf(data.fallback_dir);
+ if (index != -1) {
+ index += data.fallback_dir.Length + 1;
+ }
+ }
+ }
+
+ if (index == -1) {
+ if (data.empty_library) {
+ LogError (uri, "Unable to map iTunes URI to local URI");
+ }
+ return null;
+ }
+ SafeUri safe_uri = CreateSafeUri (Path.Combine(
+ data.local_prefix, uri.Substring (index, uri.Length - index)), data.empty_library);
+
+ if (safe_uri == null && !data.empty_library) {
+ string local_uri = string.Empty;
+ string lower_uri = raw_uri.ToLower (CultureInfo.InvariantCulture);
+ int i = lower_uri.Length;
+ while (true) {
+ i = lower_uri.LastIndexOf (Path.DirectorySeparatorChar, i - 1);
+ if (i == -1) {
+ break;
+ }
+ try {
+ using (var reader = ServiceManager.DbConnection.Query (String.Format (
+ @"SELECT Uri FROM CoreTracks WHERE lower(Uri) LIKE ""%{0}""", lower_uri.Substring (i + 1)))) {
+ bool found = false;
+ local_uri = string.Empty;
+ while (reader.Read ()) {
+ if (found) {
+ local_uri = string.Empty;
+ break;
+ }
+ found = true;
+ local_uri = (string)reader[0];
+ }
+ if (!found || local_uri.Length > 0) {
+ break;
+ }
+ }
+ } catch {
+ break;
+ }
+ }
+ if (local_uri.Length > 0) {
+ safe_uri = CreateSafeUri (local_uri, true);
+ } else {
+ LogError (uri, "Unable to map iTunes URI to local URI");
+ }
+ }
+
+ return safe_uri;
+ }
+
+ private SafeUri CreateSafeUri (string uri, bool complain)
+ {
+ SafeUri safe_uri;
+ try {
+ safe_uri = new SafeUri (uri);
+ } catch {
+ if (complain) {
+ LogError (uri, "URI is not a local file path");
+ }
+ return null;
+ }
+
+ safe_uri = FindFile (safe_uri);
+ if (safe_uri == null) {
+ if (complain) {
+ LogError (uri, "File does not exist");
+ }
+ return null;
+ }
+
+ return safe_uri;
+ }
+
+ // URIs are UTF-8 percent-encoded. Deconding with System.Web.HttpServerUtility
+ // involves too much overhead, so we do it cheap here.
+ private static string ConvertToLocalUriFormat (string input)
+ {
+ var builder = new StringBuilder (input.Length);
+ byte[] buffer = new byte[2];
+ bool using_buffer = false;
+ for (int i = 0; i < input.Length; i++) {
+ // If it's a '%', treat the two subsiquent characters as a UTF-8 byte in hex.
+ if (input[i] == '%') {
+ byte code = Byte.Parse (input.Substring(i + 1, 2),
+ System.Globalization.NumberStyles.HexNumber);
+ // If it's a non-ascii character, or there are already some non-ascii
+ // characters in the buffer, then queue it for UTF-8 decoding.
+ if (using_buffer || (code & 0x80) != 0) {
+ if (using_buffer) {
+ if (buffer[1] == 0) {
+ buffer[1] = code;
+ } else {
+ byte[] new_buffer = new byte[buffer.Length + 1];
+ for (int j = 0; j < buffer.Length; j++) {
+ new_buffer[j] = buffer[j];
+ }
+ buffer = new_buffer;
+ buffer[buffer.Length - 1] = code;
+ }
+ } else {
+ buffer[0] = code;
+ using_buffer = true;
+ }
+ }
+ // If it's a lone ascii character, there's no need for fancy UTF-8 decoding.
+ else {
+ builder.Append ((char)code);
+ }
+ i += 2;
+ } else {
+ // If we have something in the buffer, decode it.
+ if (using_buffer) {
+ builder.Append (Encoding.UTF8.GetString (buffer));
+ if (buffer.Length > 2) {
+ buffer = new byte[2];
+ } else {
+ buffer[1] = 0;
+ }
+ using_buffer = false;
+ }
+ // And add our regular characters and convert to local directory separator char.
+ if (input[i] == '/') {
+ builder.Append (Path.DirectorySeparatorChar);
+ } else {
+ builder.Append (input[i]);
+ }
+ }
+ }
+ return builder.ToString ();
+ }
+
+ private static SafeUri FindFile (SafeUri uri)
+ {
+ // URIs kept by iTunes often contain characters in a case different from the actual
+ // files and directories. This method tries to find the real file URI.
+
+ if (Banshee.IO.File.Exists (uri)) {
+ return uri;
+ }
+
+ string path = uri.AbsolutePath;
+ string file = Path.GetFileName (path);
+ string directory = Path.GetDirectoryName (path);
+ directory = FindDirectory (directory);
+ if (directory == null) {
+ return null;
+ }
+
+ uri = new SafeUri (Path.Combine (directory, file), false);
+ if (Banshee.IO.File.Exists (uri)) {
+ return uri;
+ }
+
+ foreach (string item in Banshee.IO.Directory.GetFiles (directory)) {
+ string name = Path.GetFileName (item);
+ if (0 != String.Compare (file, name, true)) {
+ continue;
+ }
+ return new SafeUri (Path.Combine (directory, name), false);
+ }
+
+ return null;
+ }
+
+ private static string FindDirectory (string directory)
+ {
+ if (Banshee.IO.Directory.Exists (directory)) {
+ return directory;
+ }
+
+ string current = Path.GetFileName (directory);
+ directory = Path.GetDirectoryName (directory);
+ if (String.IsNullOrEmpty (directory)) {
+ return null;
+ }
+
+ directory = FindDirectory (directory);
+ if (String.IsNullOrEmpty (directory)) {
+ return null;
+ }
+
+ foreach (string item in Banshee.IO.Directory.GetDirectories (directory)) {
+ string name = Path.GetFileName (item);
+ if (0 != String.Compare (current, name, true)) {
+ continue;
+ }
+ return Path.Combine (directory, name);
+ }
+
+ return null;
+ }
+
+ public override string [] IconNames {
+ get { return new string [] { "itunes", "system-search" }; }
+ }
+
+ public override int SortOrder {
+ get { return 40; }
+ }
+ }
+}
diff --git a/src/Extensions/Banshee.PlayerMigration/Makefile.am b/src/Extensions/Banshee.PlayerMigration/Makefile.am
index 384723a..cbc8fa0 100644
--- a/src/Extensions/Banshee.PlayerMigration/Makefile.am
+++ b/src/Extensions/Banshee.PlayerMigration/Makefile.am
@@ -5,10 +5,11 @@ INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
SOURCES = \
Banshee.PlayerMigration/AmarokPlayerImportSource.cs \
+ Banshee.PlayerMigration/ItunesPlayerImportDialogs.cs \
+ Banshee.PlayerMigration/ItunesPlayerImportSource.cs \
Banshee.PlayerMigration/RhythmboxPlayerImportSource.cs
-RESOURCES = \
- Banshee.PlayerMigration.addin.xml
+RESOURCES = Banshee.PlayerMigration.addin.xml
include $(top_srcdir)/build/build.mk
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]