Ejemplo n.º 1
0
        private void DeleteEmptyDirectory(GLib.File directory)
        {
            // if the directory we're dealing with is not in the
            // F-Spot photos directory, don't delete anything,
            // even if it is empty
            string photo_uri    = SafeUri.UriToFilename(Global.PhotoUri.ToString());
            bool   path_matched = directory.Path.IndexOf(photo_uri) > -1;

            if (directory.Path.Equals(photo_uri) || !path_matched)
            {
                return;
            }

            if (DirectoryIsEmpty(directory))
            {
                try {
                    Log.DebugFormat("Removing empty directory: {0}", directory.Path);
                    directory.Delete();
                } catch (GLib.GException e) {
                    // silently log the exception, but don't re-throw it
                    // as to not annoy the user
                    Log.Exception(e);
                }
                // check to see if the parent is empty
                DeleteEmptyDirectory(directory.Parent);
            }
        }
Ejemplo n.º 2
0
        [Test] // https://bugzilla.gnome.org/show_bug.cgi?id=722731
        public void TestThatInvalidTracksShouldNotBecomeCurrentTrack()
        {
            var a_valid_track = "A_boy.ogg";
            var a_valid_uri   = new SafeUri(Paths.Combine(TestsDir, "data", a_valid_track));

            var a_track_that_doesnt_exit = "this_does_not_exist_in_the_data_folder.ogg";
            var an_invalid_track         = new SafeUri(Paths.Combine(TestsDir, "data", a_track_that_doesnt_exit));

            TrackInfo current_track = null;

            WaitUntil(PlayerEvent.Error, () => {
                service.Open(a_valid_uri);
                current_track = service.CurrentTrack;
                Assert.IsNotNull(current_track);
                Assert.IsTrue(current_track.Uri.AbsolutePath.EndsWith(a_valid_track));

                service.SetNextTrack(an_invalid_track);
                service.Play();
            });

            WaitUntil(PlayerEvent.StartOfStream, () => {
                service.Open(a_valid_uri);
                service.Play();
            });

            current_track = service.CurrentTrack;
            Assert.IsNotNull(current_track);
            var actual_filename = System.IO.Path.GetFileName(SafeUri.UriToFilename(current_track.Uri));

            Assert.AreEqual(a_valid_track, actual_filename);
        }
Ejemplo n.º 3
0
        private void OnImportFinished(object o, EventArgs args)
        {
            importer.Finished -= OnImportFinished;

            if (CanSyncPlaylists)
            {
                var insert_cmd = new Hyena.Data.Sqlite.HyenaSqliteCommand(
                    "INSERT INTO CorePlaylistEntries (PlaylistID, TrackID) VALUES (?, ?)");
                foreach (string playlist_path in PlaylistFiles)
                {
                    // playlist_path has a file:// prefix, and GetDirectoryName messes it up,
                    // so we need to convert it to a regular path
                    string base_folder = ms_device.RootPath != null ? BaseDirectory :
                                         System.IO.Path.GetDirectoryName(SafeUri.UriToFilename(playlist_path));

                    IPlaylistFormat loaded_playlist = PlaylistFileUtil.Load(playlist_path,
                                                                            new Uri(base_folder),
                                                                            ms_device.RootPath);
                    if (loaded_playlist == null)
                    {
                        continue;
                    }

                    string         name     = System.IO.Path.GetFileNameWithoutExtension(SafeUri.UriToFilename(playlist_path));
                    PlaylistSource playlist = new PlaylistSource(name, this);
                    playlist.Save();
                    //Hyena.Data.Sqlite.HyenaSqliteCommand.LogAll = true;
                    foreach (PlaylistElement element in loaded_playlist.Elements)
                    {
                        string track_path = element.Uri.LocalPath;
                        long   track_id   = DatabaseTrackInfo.GetTrackIdForUri(new SafeUri(track_path), DbId);
                        if (track_id == 0)
                        {
                            Log.DebugFormat("Failed to find track {0} in DAP library to load it into playlist {1}", track_path, playlist_path);
                        }
                        else
                        {
                            ServiceManager.DbConnection.Execute(insert_cmd, playlist.DbId, track_id);
                        }
                    }
                    //Hyena.Data.Sqlite.HyenaSqliteCommand.LogAll = false;
                    playlist.UpdateCounts();
                    AddChildSource(playlist);
                }
            }

            import_reset_event.Set();
        }
Ejemplo n.º 4
0
        protected override void OnImportRequested(string path)
        {
            try {
                DatabaseTrackInfo track = ImportTrack(path);
                if (track != null && track.TrackId > 0)
                {
                    UpdateProgress(String.Format("{0} - {1}",
                                                 track.DisplayArtistName, track.DisplayTrackTitle));
                }
                else
                {
                    UpdateProgress(null);
                }

                OnImportResult(track, path, null);
            } catch (Exception e) {
                LogError(SafeUri.UriToFilename(path), e);
                UpdateProgress(null);
                OnImportResult(null, path, e);
            }
        }
Ejemplo n.º 5
0
        public bool CopyToLibraryIfAppropriate(bool force_copy)
        {
            bool copy_success = true;

            LibrarySource library_source = PrimarySource as LibrarySource;

            if (library_source == null)
            {
                // Get out, not a local Library
                return(false);
            }

            SafeUri old_uri = this.Uri;

            if (old_uri == null)
            {
                // Get out quick, no URI set yet.
                return(copy_success);
            }

            bool in_library = old_uri.IsLocalPath ? old_uri.AbsolutePath.StartsWith(PrimarySource.BaseDirectoryWithSeparator) : false;

            if (!in_library && ((library_source.HasCopyOnImport && library_source.CopyOnImport) || force_copy))
            {
                string new_filename = PathPattern != null?PathPattern.BuildFull(PrimarySource.BaseDirectory, this, Path.GetExtension(old_uri.ToString()))
                                          : Path.Combine(PrimarySource.BaseDirectory, Path.GetFileName(SafeUri.UriToFilename(old_uri)));

                SafeUri new_uri = new SafeUri(new_filename);

                try {
                    if (Banshee.IO.File.Exists(new_uri))
                    {
                        if (Banshee.IO.File.GetSize(old_uri) == Banshee.IO.File.GetSize(new_uri))
                        {
                            Hyena.Log.DebugFormat("Not copying {0} to library because there is already a file of same size at {1}", old_uri, new_uri);
                            copy_success = false;
                            return(copy_success);
                        }
                        else
                        {
                            string extension       = Path.GetExtension(new_filename);
                            string filename_no_ext = new_filename.Remove(new_filename.Length - extension.Length);
                            int    duplicate_index = 1;
                            while (Banshee.IO.File.Exists(new_uri))
                            {
                                new_filename = String.Format("{0} ({1}){2}", filename_no_ext, duplicate_index, extension);
                                new_uri      = new SafeUri(new_filename);
                                duplicate_index++;
                            }
                        }
                    }

                    Banshee.IO.File.Copy(old_uri, new_uri, false);
                    Uri = new_uri;
                } catch (Exception e) {
                    Log.ErrorFormat("Exception copying into library: {0}", e);
                }
            }
            return(copy_success);
        }
        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);
            }
        }
        protected override void ImportCore()
        {
            LibraryImportManager  import_manager = ServiceManager.Get <LibraryImportManager> ();
            HyenaSqliteConnection conn;

            try {
                conn = new HyenaSqliteConnection(amarok_db_path);
            } catch (Exception e) {
                LogError(amarok_db_path, String.Format(
                             "Unable to open Amarok database: {0}", e.Message));
                return;
            }

            int count = 0;

            try {
                count = conn.Query <int> ("SELECT COUNT(*) FROM tags");
            } catch (Exception) {}

            try {
                HyenaSqliteCommand cmd = new HyenaSqliteCommand(@"
                    SELECT DISTINCT NULL,
                           tags.url,
                           tags.title,
                           artist.name,
                           genre.name,
                           album.name,
                           year.name,
                           tags.track,
                           tags.length
                     FROM  tags,
                           artist,
                           album,
                           genre,
                           year
                     WHERE tags.deviceid = -1
                       AND tags.artist = artist.id
                       AND tags.album = album.id
                       AND tags.genre = genre.id
                       AND tags.year = year.id"
                                                                );

                HyenaSqliteCommand stats_cmd = new HyenaSqliteCommand(@"
                                                     SELECT DISTINCT (rating+rating%2)/2, playcounter, createdate, accessdate
                                                     FROM   statistics
                                                     WHERE  url = ? AND deviceid = -1");

                int processed = 0;

                IDataReader reader = conn.Query(cmd);
                while (reader.Read())
                {
                    if (CheckForCanceled())
                    {
                        break;
                    }

                    processed++;

                    try {
                        string path = (string)reader[1];

                        SafeUri uri = null;
                        if (path.StartsWith("./"))
                        {
                            uri = new SafeUri(path.Substring(1));
                        }
                        else if (path.StartsWith("/"))
                        {
                            uri = new SafeUri(path);
                        }
                        else
                        {
                            continue;
                        }

                        string title  = (string)reader[2];
                        string artist = (string)reader[3];
                        //Console.WriteLine ("Amarok import has {0}/{1} - {2}", artist, title, uri);

                        // the following fields are not critical and can be skipped if something goes wrong
                        int  rating = 0, playcount = 0;
                        long created = 0, accessed = 0;

                        // Try to read stats
                        try {
                            IDataReader stats_reader = conn.Query(stats_cmd, path);

                            while (stats_reader.Read())
                            {
                                rating    = Convert.ToInt32(stats_reader[0]);
                                playcount = Convert.ToInt32(stats_reader[1]);
                                created   = Convert.ToInt64(stats_reader[2]);
                                accessed  = Convert.ToInt64(stats_reader[3]);
                            }
                            stats_reader.Dispose();
                        } catch (Exception) {}

                        UpdateUserJob(processed, count, artist, title);

                        try {
                            DatabaseTrackInfo track = import_manager.ImportTrack(uri);

                            if (track == null)
                            {
                                throw new Exception(String.Format(Catalog.GetString("Unable to import track: {0}"), uri.AbsoluteUri));
                            }

                            if (rating > 0 || playcount > 0 || created > 0 || accessed > 0)
                            {
                                track.Rating    = rating;
                                track.PlayCount = playcount;
                                if (created > 0)
                                {
                                    track.DateAdded = Hyena.DateTimeUtil.FromTimeT(created);
                                }
                                if (accessed > 0)
                                {
                                    track.LastPlayed = Hyena.DateTimeUtil.FromTimeT(accessed);
                                }
                                track.Save(false);
                            }
                        } catch (Exception e) {
                            LogError(SafeUri.UriToFilename(uri), e);
                        }
                    } catch (Exception e) {
                        Hyena.Log.Exception(e);
                        // something went wrong, skip entry
                    }
                }
                reader.Dispose();
                import_manager.NotifyAllSources();

                // TODO migrating more than the podcast subscriptions (eg whether to auto sync them etc) means 1) we need to have those features
                // and 2) we need to depend on Migo and/or the Podcast extension
                DBusCommandService cmd_service = ServiceManager.Get <DBusCommandService> ();
                if (cmd_service != null && ServiceManager.DbConnection.TableExists("PodcastSyndications"))
                {
                    foreach (string podcast_url in conn.QueryEnumerable <string> ("SELECT url FROM podcastchannels"))
                    {
                        cmd_service.PushFile(podcast_url.Replace("http:", "feed:"));
                    }
                }
            } catch (Exception e) {
                Hyena.Log.Exception(e);
                LogError(amarok_db_path, Catalog.GetString("Importing from Amarok failed"));
            } finally {
                conn.Dispose();
            }
        }
        protected override void ImportCore()
        {
            LibraryImportManager import_manager = ServiceManager.Get <LibraryImportManager> ();

            SafeUri db_uri = rhythmbox_db_uri;

            // Check if library is located in the old place (.gnome2/rhythmbox/rhythmdb.db)
            if (!Banshee.IO.File.Exists(rhythmbox_db_uri))
            {
                db_uri = rhythmbox_db_uri_old;
            }

            if (!IsValidXmlDocument(db_uri))
            {
                LogError(SafeUri.UriToFilename(db_uri), "Rhythmbox library is corrupted.");
                return;
            }

            // Load Rhythmbox library
            Stream      stream_db  = Banshee.IO.File.OpenRead(db_uri);
            XmlDocument xml_doc_db = new XmlDocument();

            xml_doc_db.Load(stream_db);
            XmlElement db_root = xml_doc_db.DocumentElement;

            stream_db.Close();

            if (db_root == null || !db_root.HasChildNodes || db_root.Name != "rhythmdb")
            {
                LogError(SafeUri.UriToFilename(db_uri), "Unable to open Rhythmbox library.");
                return;
            }

            count     = db_root.ChildNodes.Count;
            processed = 0;

            // Import Rhythmbox playlists if playlist file is available
            SafeUri rhythmbox_playlists_uri = new SafeUri(Hyena.Paths.Combine(
                                                              Environment.GetFolderPath(Environment.SpecialFolder.Personal),
                                                              ".local", "share", "rhythmbox", "playlists.xml"
                                                              ));

            bool playlists_available = Banshee.IO.File.Exists(rhythmbox_playlists_uri);

            // Look at the old location too
            if (!playlists_available)
            {
                rhythmbox_playlists_uri = new SafeUri(Hyena.Paths.Combine(
                                                          Environment.GetFolderPath(Environment.SpecialFolder.Personal),
                                                          ".gnome2", "rhythmbox", "playlists.xml"
                                                          ));

                playlists_available = Banshee.IO.File.Exists(rhythmbox_playlists_uri);
            }

            XmlElement playlists_root = null;

            if (playlists_available)
            {
                if (IsValidXmlDocument(rhythmbox_playlists_uri))
                {
                    Stream      stream_playlists  = Banshee.IO.File.OpenRead(rhythmbox_playlists_uri);
                    XmlDocument xml_doc_playlists = new XmlDocument();
                    xml_doc_playlists.Load(stream_playlists);
                    playlists_root = xml_doc_playlists.DocumentElement;
                    stream_playlists.Close();

                    if (playlists_root == null || !playlists_root.HasChildNodes || playlists_root.Name != "rhythmdb-playlists")
                    {
                        playlists_available = false;
                    }
                    else
                    {
                        count += playlists_root.ChildNodes.Count;
                    }
                }
                else
                {
                    LogError(SafeUri.UriToFilename(rhythmbox_playlists_uri), "Rhythmbox playlists are corrupted.");
                    playlists_available = false;
                }
            }

            ImportSongs(import_manager, db_root.SelectNodes("/rhythmdb/entry[@type='song']"));

            //ImportPodcasts (import_manager, db_root.SelectNodes ("/rhythmdb/entry[@type='podcast-post']"));

            if (playlists_available)
            {
                ImportStaticPlaylists(playlists_root.SelectNodes("/rhythmdb-playlists/playlist[@type='static']"));
            }

            import_manager.NotifyAllSources();
        }
        private void ImportSongs(LibraryImportManager manager, XmlNodeList songs)
        {
            foreach (XmlElement song in songs)
            {
                if (CheckForCanceled())
                {
                    break;
                }

                processed++;

                string title         = String.Empty,
                       genre         = String.Empty,
                       artist        = String.Empty,
                       album         = String.Empty;
                int year             = 0,
                    rating           = 0,
                    play_count       = 0,
                    track_number     = 0;
                DateTime date_added  = DateTime.Now,
                         last_played = DateTime.MinValue;
                SafeUri uri          = null;

                foreach (XmlElement child in song.ChildNodes)
                {
                    if (child == null || child.InnerText == null || child.InnerText == String.Empty)
                    {
                        continue;
                    }

                    try {
                        switch (child.Name)
                        {
                        case "title":
                            title = child.InnerText;
                            break;

                        case "genre":
                            genre = child.InnerText;
                            break;

                        case "artist":
                            artist = child.InnerText;
                            break;

                        case "album":
                            album = child.InnerText;
                            break;

                        case "track-number":
                            track_number = Int32.Parse(child.InnerText);
                            break;

                        case "location":
                            uri = new SafeUri(child.InnerText);
                            break;

                        case "date":
                            if (child.InnerText != "0")
                            {
                                year = (new DateTime(1, 1, 1).AddDays(Double.Parse(child.InnerText))).Year;
                            }
                            break;

                        case "rating":
                            rating = Int32.Parse(child.InnerText);
                            break;

                        case "play-count":
                            play_count = Int32.Parse(child.InnerText);
                            break;

                        case "last-played":
                            last_played = Hyena.DateTimeUtil.ToDateTime(Int64.Parse(child.InnerText));
                            break;

                        case "first-seen":
                            date_added = Hyena.DateTimeUtil.ToDateTime(Int64.Parse(child.InnerText));;
                            break;
                        }
                    } catch (Exception) {
                        // parsing InnerText failed
                    }
                }

                if (uri == null)
                {
                    continue;
                }

                UpdateUserJob(processed, count, artist, title);

                try {
                    DatabaseTrackInfo track = manager.ImportTrack(uri);

                    if (track == null)
                    {
                        LogError(SafeUri.UriToFilename(uri), Catalog.GetString("Unable to import song."));
                        continue;
                    }

                    track.TrackTitle  = title;
                    track.ArtistName  = artist;
                    track.Genre       = genre;
                    track.AlbumTitle  = album;
                    track.TrackNumber = track_number;
                    track.Year        = year;
                    track.DateAdded   = date_added;

                    track.Rating     = (rating >= 0 && rating <= 5) ? rating : 0;
                    track.PlayCount  = (play_count >= 0) ? play_count : 0;
                    track.LastPlayed = last_played;

                    track.Save(false);
                } catch (Exception e) {
                    LogError(SafeUri.UriToFilename(uri), e);
                }
            }
        }
        private void onStartSync(object o, EventArgs args)
        {
            Hyena.Log.Debug("Start of Sync triggered!");

            var options = View.GetOptions();

            // target directory to copy to
            Hyena.Log.DebugFormat("Target folder is set to: {0}", options.TargetFolder);

            // count all files for progress bar
            int totalFiles = 0;

            foreach (var playlist in options.SelectedPlaylists)
            {
                totalFiles += playlist.Tracks.Count();
            }
            View.Progress.Text = AddinManager.CurrentLocalizer.GetString("Preparing sync");
            var progress_step    = 1f / totalFiles;
            var current_progress = 0f;

            // begin sync worker thread
            ThreadStart syncStart = delegate()
            {
                Hyena.Log.Debug("Sync thread started!");
                // foreach playlist
                foreach (var playlist in options.SelectedPlaylists)
                {
                    Stream       m3u_stream = null;
                    StreamWriter m3u_writer = null;

                    if (options.CreateM3u)
                    {
                        var m3u_fileUri = new StringBuilder().Append(options.TargetFolder)
                                          .Append(Path.DirectorySeparatorChar).Append(playlist.Name)
                                          .Append(".m3u").ToString();

                        m3u_stream = Banshee.IO.File.OpenWrite(new SafeUri(m3u_fileUri), true);
                        Log.DebugFormat("opened m3u playlist for writing: {0}", m3u_fileUri);
                        m3u_writer = new StreamWriter(m3u_stream, System.Text.Encoding.UTF8);
                    }

                    // for each contained file
                    foreach (var track in playlist.Tracks)
                    {
                        // get filename part of path
                        var dest_path_suffix = track.GetFilepathSuffix(options.SubfolderDepth);

                        // we dont want %20 etc. in the m3u since some android devices delete
                        // playlists with that encoding in it (i.e. galaxy S)
                        Hyena.Log.DebugFormat("filename for m3u file is {0}", dest_path_suffix);

                        // assemble new Uri of target track
                        var destUri = new SafeUri(new StringBuilder().Append(options.TargetFolder)
                                                  .Append(Path.DirectorySeparatorChar)
                                                  .Append(dest_path_suffix).ToString());

                        // create subfolders if necessary
                        string dest_path = options.TargetFolder;
                        var    folders   = track.GetSubdirectories(options.SubfolderDepth);
                        try {
                            for (int i = 0; i < folders.Count(); i++)
                            {
                                dest_path += folders [i] + "/";
                                Hyena.Log.DebugFormat("creating folder {0}", dest_path);
                                if (!Banshee.IO.Directory.Exists(dest_path))
                                {
                                    Banshee.IO.Directory.Create(new SafeUri(dest_path));
                                }
                            }
                        } catch {
                            // folder creation failed, this is fatal, stop
                            // TODO display a error popup
                            break;
                        }

                        // copy file to selected folder
                        try {
                            var destExists = Banshee.IO.File.Exists(destUri);

                            if (options.OverwriteExisting || !destExists)
                            {
                                if (options.CreateSymLinks)
                                {
                                    var trackFilePath = SafeUri.UriToFilename(track.Uri);
                                    var destFilePath  = SafeUri.UriToFilename(destUri);

                                    if (track.Uri.IsLocalPath)
                                    {
                                        var target = new UnixFileInfo(trackFilePath);

                                        // symbolic links need manual deletion,
                                        // otherwise an exception is thrown in CreateSymbolicLink()
                                        if (destExists)
                                        {
                                            var dest = new UnixFileInfo(destFilePath);
                                            dest.Delete();
                                        }

                                        target.CreateSymbolicLink(destFilePath);
                                    }
                                    else
                                    {
                                        Hyena.Log.ErrorFormat("Cannot create symlink to remote path {0} in {1}, skipping", track.Uri, destFilePath);
                                    }
                                }
                                else
                                {
                                    Banshee.IO.File.Copy(track.Uri, destUri, true);
                                }
                                Hyena.Log.DebugFormat("Copying {0} to {1}", track.Uri, destUri);
                            }
                            else
                            {
                                Hyena.Log.Debug("Not overwriting existing file {0}", destUri);
                            }
                        } catch {
                            Hyena.Log.ErrorFormat("Error copying file {0} to {1}, skipping", track.Uri, destUri);
                        }

                        // increment the progressbar
                        current_progress += progress_step;
                        if (current_progress > 1.0f)
                        {
                            current_progress = 1.0f;
                        }

                        Gtk.Application.Invoke(delegate {
                            View.Progress.Fraction = current_progress;
                            View.Progress.Text     = AddinManager.CurrentLocalizer.GetString("Copying") + " " + track.Filepath;
                            Gtk.Main.IterationDo(false);
                        });

                        if (options.CreateM3u)
                        {
                            m3u_writer.Write(track.CreateM3uEntry(options.SubfolderDepth));
                        }
                    }
                    // close the m3u file before processing next playlist
                    if (options.CreateM3u)
                    {
                        Hyena.Log.Debug("closing m3u filedescriptor");
                        m3u_writer.Close();
                        m3u_writer.Dispose();
                    }
                    Hyena.Log.Debug("sync process finished");
                }

                Gtk.Application.Invoke(delegate {
                    View.Progress.Text     = AddinManager.CurrentLocalizer.GetString("Done!");
                    View.Progress.Fraction = 1f;
                    Gtk.Main.IterationDo(false);
                });
                Hyena.Log.Debug("sync DONE, returning");
                return;
            };

            // end of sync worker thread

            syncThread = new Thread(syncStart);
            syncThread.Start();
            return;
        }