diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 5bc16ba4c..02c28db4c 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -1241,6 +1241,7 @@ void Playlist::UpdateItems(const SongList& songs) { } } } + connect(backend_,SIGNAL(PlaylistSaved(int)),SIGNAL(PlaylistSongsLoaded(int))); Save(); } diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index 833f311cd..4e947ad89 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -373,6 +373,9 @@ class Playlist : public QAbstractListModel { // Signals that the queue has changed, meaning that the remaining queued // items should update their position. void QueueChanged(); + // Signals that the playlist has finished asynchronously loading songs + // the playlist can now be safely processed or closed + void PlaylistSongsLoaded(int id); private: void SetCurrentIsPaused(bool paused); diff --git a/src/playlist/playlistbackend.cpp b/src/playlist/playlistbackend.cpp index 01596e804..ba5846f84 100644 --- a/src/playlist/playlistbackend.cpp +++ b/src/playlist/playlistbackend.cpp @@ -343,6 +343,7 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList& items, if (db_->CheckErrors(update)) return; transaction.Commit(); + emit PlaylistSaved(playlist); } int PlaylistBackend::CreatePlaylist(const QString& name, diff --git a/src/playlist/playlistbackend.h b/src/playlist/playlistbackend.h index f9d347c4e..2dcaa3fd7 100644 --- a/src/playlist/playlistbackend.h +++ b/src/playlist/playlistbackend.h @@ -79,7 +79,9 @@ class PlaylistBackend : public QObject { public slots: void SavePlaylist(int playlist, const PlaylistItemList& items, int last_played, smart_playlists::GeneratorPtr dynamic); - + signals: + void PlaylistSaved(int id); + private: struct NewSongFromQueryState { QHash cached_cues_; diff --git a/src/playlist/playlistlistcontainer.cpp b/src/playlist/playlistlistcontainer.cpp index 77ef82cee..f9d18b4ac 100644 --- a/src/playlist/playlistlistcontainer.cpp +++ b/src/playlist/playlistlistcontainer.cpp @@ -24,16 +24,22 @@ #include #include #include +#include +#include #include #include "core/application.h" #include "core/logging.h" #include "core/player.h" +#include "core/utilities.h" #include "playlist.h" #include "playlistlistmodel.h" #include "playlistmanager.h" #include "ui/iconloader.h" #include "ui_playlistlistcontainer.h" +#include "playlistparsers/playlistparser.h" + +const char* PlaylistListContainer::kSettingsGroup = "PlaylistList"; /* This filter proxy will: - Accept all ancestors if at least a single child matches @@ -146,6 +152,7 @@ PlaylistListContainer::PlaylistListContainer(QWidget* parent) action_new_folder_(new QAction(this)), action_remove_(new QAction(this)), action_save_playlist_(new QAction(this)), + action_bulk_import_playlists_(new QAction(this)), model_(new PlaylistListModel(this)), proxy_(new PlaylistListFilterProxyModel(this)), loaded_icons_(false), @@ -158,16 +165,20 @@ PlaylistListContainer::PlaylistListContainer(QWidget* parent) action_remove_->setText(tr("Delete")); action_save_playlist_->setText( tr("Save playlist", "Save playlist menu action.")); + action_bulk_import_playlists_->setText(tr("Bulk Import Playlists")); ui_->new_folder->setDefaultAction(action_new_folder_); ui_->remove->setDefaultAction(action_remove_); ui_->save_playlist->setDefaultAction(action_save_playlist_); + ui_->bulk_import_playlists->setDefaultAction(action_bulk_import_playlists_); + connect(action_new_folder_, SIGNAL(triggered()), SLOT(NewFolderClicked())); connect(action_remove_, SIGNAL(triggered()), SLOT(DeleteClicked())); connect(action_save_playlist_, SIGNAL(triggered()), SLOT(SavePlaylist())); connect(model_, SIGNAL(PlaylistPathChanged(int, QString)), SLOT(PlaylistPathChanged(int, QString))); + connect(action_bulk_import_playlists_, SIGNAL(triggered()), SLOT(BulkImportPlaylists())); proxy_->setSourceModel(model_); proxy_->setDynamicSortFilter(true); @@ -182,6 +193,9 @@ PlaylistListContainer::PlaylistListContainer(QWidget* parent) connect(ui_->search, SIGNAL(textChanged(QString)), SLOT(SearchTextEdited(QString))); + + //access to global settings using the QSettings object. Use this to get data about last opened folder etc. + settings_.beginGroup(kSettingsGroup); } PlaylistListContainer::~PlaylistListContainer() { delete ui_; } @@ -198,6 +212,8 @@ void PlaylistListContainer::showEvent(QShowEvent* e) { action_remove_->setIcon(IconLoader::Load("edit-delete", IconLoader::Base)); action_save_playlist_->setIcon( IconLoader::Load("document-save", IconLoader::Base)); + action_bulk_import_playlists_->setIcon( + IconLoader::Load("document-open-folder", IconLoader::Base)); model_->SetIcons(IconLoader::Load("view-media-playlist", IconLoader::Base), IconLoader::Load("folder", IconLoader::Base)); @@ -330,6 +346,86 @@ void PlaylistListContainer::SavePlaylist() { } } +/* +Open filepicker and Use QDirIterator to recursively find subdirectories +within the chosen directory. create an empty +*/ +void PlaylistListContainer::BulkImportPlaylists() { + QString base_path(settings_.value("last_path", Utilities::GetConfigPath( + Utilities::Path_DefaultMusicLibrary)).toString()); + base_path = QFileDialog::getExistingDirectory(this, tr("Add directory..."), base_path); + + if (base_path.isNull()) { + return; + } + + //Create Root Folder + QFileInfo child_info(base_path); + QStandardItem* root_folder = model_->NewFolder(child_info.fileName()); + model_->invisibleRootItem()->appendRow(root_folder); + + bulk_imported_id_list_ = new QList(); + bulk_imported_count_ = 0; + + RecursivelyCreateSubfolders(root_folder, base_path, child_info.baseName()); + + settings_.setValue("last_path", base_path); +} + +void PlaylistListContainer::RecursivelyCreateSubfolders(QStandardItem* parent_folder, QString path, QString ui_path){ + QStringList filters; + filters = app_->playlist_manager()->parser()->file_extensions(); + for(int i=0; iNewFolder(child_info.fileName()); + parent_folder->appendRow(folder); + RecursivelyCreateSubfolders(folder, child_info.absoluteFilePath(), ui_path + "/" + child_info.baseName()); + }else if(child_info.isFile()){ + if(QDir::match(filters, child_info.fileName())){ + bulk_imported_count_++; + int id = app_->playlist_manager()->LoadBulkPlaylists(child, ui_path); + Playlist* playlist = app_->playlist_manager()->playlist(id); + connect(playlist, SIGNAL(PlaylistSongsLoaded(int)), SLOT(BulkImportPlaylistsCallback(int))); + PlaylistPathChanged(id, ui_path); + } + } + } +} + +/* +* After the bulk imported playlist has asynchronously had its songs +* loaded, add it to the list of finished playlists +* once the quota has been filled, close the temporary tabs +*/ +void PlaylistListContainer::BulkImportPlaylistsCallback (int id){ + if(bulk_imported_count_ == 0){ + return; + } + if(bulk_imported_id_list_->contains(id)){ + return; + } + app_->playlist_manager()->BulkImportPlaylistsCallback(id); + const QString& name = app_->playlist_manager()->GetPlaylistName(id); + AddPlaylist(id, name, true); + bulk_imported_id_list_->append(id); + if(bulk_imported_id_list_->count() == bulk_imported_count_){ + //clean up tabs + for(qsizetype i =0; i < bulk_imported_id_list_->count(); i++){ + app_->playlist_manager()->Close(bulk_imported_id_list_->at(i)); + } + app_->playlist_manager()->ChangePlaylistOrder(app_->playlist_manager()->GetAllPlaylistIds()); + bulk_imported_count_ = 0; + bulk_imported_id_list_ = new QList(); + } +} + void PlaylistListContainer::PlaylistFavoriteStateChanged(int id, bool favorite) { if (favorite) { @@ -473,6 +569,7 @@ void PlaylistListContainer::contextMenuEvent(QContextMenuEvent* e) { menu_->addAction(action_remove_); menu_->addSeparator(); menu_->addAction(action_save_playlist_); + menu_->addAction(action_bulk_import_playlists_); } menu_->popup(e->globalPos()); } diff --git a/src/playlist/playlistlistcontainer.h b/src/playlist/playlistlistcontainer.h index ec11271f8..980721a8d 100644 --- a/src/playlist/playlistlistcontainer.h +++ b/src/playlist/playlistlistcontainer.h @@ -18,6 +18,7 @@ #ifndef PLAYLISTLISTCONTAINER_H #define PLAYLISTLISTCONTAINER_H +#include #include #include "playlistbackend.h" @@ -42,6 +43,8 @@ class PlaylistListContainer : public QWidget { void SetApplication(Application* app); + static const char* kSettingsGroup; + protected: void showEvent(QShowEvent* e); void contextMenuEvent(QContextMenuEvent* e); @@ -63,6 +66,9 @@ class PlaylistListContainer : public QWidget { const QString* ui_path = nullptr); void RemovePlaylist(int id); void SavePlaylist(); + void BulkImportPlaylists(); + void RecursivelyCreateSubfolders(QStandardItem* parent_folder, QString path, QString ui_path); + void BulkImportPlaylistsCallback (int id); void PlaylistFavoriteStateChanged(int id, bool favorite); void CurrentChanged(Playlist* new_playlist); void ActiveChanged(Playlist* new_playlist); @@ -89,6 +95,7 @@ class PlaylistListContainer : public QWidget { QAction* action_new_folder_; QAction* action_remove_; QAction* action_save_playlist_; + QAction* action_bulk_import_playlists_; PlaylistListModel* model_; PlaylistListFilterProxyModel* proxy_; @@ -97,6 +104,12 @@ class PlaylistListContainer : public QWidget { QIcon padded_play_icon_; int active_playlist_id_; + + QSettings settings_; + + QList* bulk_imported_id_list_; + int bulk_imported_count_; + int debug_count_; }; #endif // PLAYLISTLISTCONTAINER_H diff --git a/src/playlist/playlistlistcontainer.ui b/src/playlist/playlistlistcontainer.ui index 5539e535b..8cdd62a5f 100644 --- a/src/playlist/playlistlistcontainer.ui +++ b/src/playlist/playlistlistcontainer.ui @@ -72,6 +72,13 @@ + + + + Bulk Import Playlists + + + diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 0fc1be642..2ad2fe509 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -101,6 +101,16 @@ QList PlaylistManager::GetAllPlaylists() const { return result; } +QList PlaylistManager::GetAllPlaylistIds() const { + QList result; + + for (const int data : playlists_.keys()) { + result.append(data); + } + + return result; +} + QItemSelection PlaylistManager::selection(int id) const { QMap::const_iterator it = playlists_.find(id); return it->selection; @@ -196,6 +206,35 @@ void PlaylistManager::Save(int id, const QString& filename, } } +int PlaylistManager::LoadBulkPlaylists(const QString& filename, const QString& ui_path){ + QFileInfo info(filename); + + int id = playlist_backend_->CreatePlaylist(info.baseName(), QString()); + + if (id == -1) { + emit Error(tr("Couldn't create playlist")); + return id; + } + + Playlist* playlist = + AddPlaylist(id, info.baseName(), QString(), ui_path, false); + + QList urls; + playlist->InsertUrls(urls << QUrl::fromLocalFile(filename)); + + return id; +} + +void PlaylistManager::BulkImportPlaylistsCallback(int id){ + if (playlists_.contains(id)) { + // If playlists_ contains this playlist, its means it's opened: star or + // unstar it. + bool favorite = true; + playlist_backend_->FavoritePlaylist(id, favorite); + playlists_[id].p->set_favorite(favorite); + } +} + void PlaylistManager::ItemsLoadedForSavePlaylist(QFuture future, const QString& filename, Playlist::Path path_type) { diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index 7ea0e46e7..d5dee1810 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -55,6 +55,7 @@ class PlaylistManagerInterface : public QObject { // Returns the collection of playlists managed by this PlaylistManager. virtual QList GetAllPlaylists() const = 0; + virtual QList GetAllPlaylistIds() const = 0; // Grays out and reloads all deleted songs in all playlists. virtual void InvalidateDeletedSongs() = 0; // Removes all deleted songs from all playlists. @@ -151,6 +152,7 @@ class PlaylistManager : public PlaylistManagerInterface { // Returns the collection of playlists managed by this PlaylistManager. QList GetAllPlaylists() const; + QList GetAllPlaylistIds() const; // Grays out and reloads all deleted songs in all playlists. void InvalidateDeletedSongs(); // Removes all deleted songs from all playlists. @@ -185,6 +187,8 @@ class PlaylistManager : public PlaylistManagerInterface { const QString& special_type = QString()); void Load(const QString& filename); void Save(int id, const QString& filename, Playlist::Path path_type); + int LoadBulkPlaylists(const QString& filename, const QString& ui_path); + void BulkImportPlaylistsCallback(int id); // Display a file dialog to let user choose a file before saving the file void SaveWithUI(int id, const QString& playlist_name); void Rename(int id, const QString& new_name);