[banshee] Add iTunes importer (bgo#441093)



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]