From a32010e03b3e894a522c262c8ab730120458a151 Mon Sep 17 00:00:00 2001 From: SamTShaw Date: Wed, 7 Aug 2019 23:13:40 +0800 Subject: [PATCH] Ipod Playlist Support (#220) * Ipod Playlist Support Copy a whole playlist to the ipod and create an entry in Playlists on the iPod * Fix formatting and indentation Fix indenting and formatting to be consistent --- src/core/musicstorage.h | 1 + src/device/gpoddevice.cpp | 13 +++++++++ src/organise/organise.cpp | 6 ++-- src/organise/organise.h | 3 +- src/organise/organisedialog.cpp | 9 ++++-- src/organise/organisedialog.h | 3 ++ src/playlist/playlistlistcontainer.cpp | 38 ++++++++++++++++++++++++++ src/playlist/playlistlistcontainer.h | 6 ++++ 8 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/core/musicstorage.h b/src/core/musicstorage.h index 047ded76b..86ae2b93b 100644 --- a/src/core/musicstorage.h +++ b/src/core/musicstorage.h @@ -65,6 +65,7 @@ class MusicStorage { QString cover_source_; QString cover_dest_; ProgressFunction progress_; + QString playlist_; }; struct DeleteJob { diff --git a/src/device/gpoddevice.cpp b/src/device/gpoddevice.cpp index 964a68e3b..b1852cea7 100644 --- a/src/device/gpoddevice.cpp +++ b/src/device/gpoddevice.cpp @@ -188,6 +188,19 @@ bool GPodDevice::CopyToStorage(const CopyJob &job) { return false; } + // Put the track in the playlist, if one is specified + if (!job.playlist_.isEmpty()) { + // Does the playlist already exist? + auto itdbPlaylist = itdb_playlist_by_name(db_, job.playlist_.toUtf8().data()); + if (itdbPlaylist == nullptr) { + // Create the playlist + itdbPlaylist = itdb_playlist_new(job.playlist_.toUtf8().data(), false); + itdb_playlist_add(db_, itdbPlaylist, -1); + } + // Playlist should exist so add the track to the playlist + itdb_playlist_add_track(itdbPlaylist, track, -1); + } + AddTrackToModel(track, url_.path()); // Remove the original if it was requested diff --git a/src/organise/organise.cpp b/src/organise/organise.cpp index 17f1ee4f4..cbc13714a 100644 --- a/src/organise/organise.cpp +++ b/src/organise/organise.cpp @@ -52,7 +52,7 @@ const int Organise::kBatchSize = 10; const int Organise::kTranscodeProgressInterval = 500; #endif -Organise::Organise(TaskManager *task_manager, std::shared_ptr destination, const OrganiseFormat &format, bool copy, bool overwrite, bool mark_as_listened, bool albumcover, const NewSongInfoList &songs_info, bool eject_after) +Organise::Organise(TaskManager *task_manager, std::shared_ptr destination, const OrganiseFormat &format, bool copy, bool overwrite, bool mark_as_listened, bool albumcover, const NewSongInfoList &songs_info, bool eject_after, const QString &playlist) : thread_(nullptr), task_manager_(task_manager), #ifdef HAVE_GSTREAMER @@ -66,10 +66,11 @@ Organise::Organise(TaskManager *task_manager, std::shared_ptr dest albumcover_(albumcover), eject_after_(eject_after), task_count_(songs_info.count()), + playlist_(playlist), tasks_complete_(0), started_(false), task_id_(0), - current_copy_progress_(0) { + current_copy_progress_(0){ original_thread_ = thread(); @@ -208,6 +209,7 @@ void Organise::ProcessSomeFiles() { job.mark_as_listened_ = mark_as_listened_; job.albumcover_ = albumcover_; job.remove_original_ = !copy_; + job.playlist_ = playlist_; if (task.song_info_.song_.art_manual_is_valid() && task.song_info_.song_.art_manual().path() != Song::kManuallyUnsetCover) { if (task.song_info_.song_.art_manual().isLocalFile() && QFile::exists(task.song_info_.song_.art_manual().toLocalFile())) { diff --git a/src/organise/organise.h b/src/organise/organise.h index ddc8e6a3c..620a11c60 100644 --- a/src/organise/organise.h +++ b/src/organise/organise.h @@ -61,7 +61,7 @@ class Organise : public QObject { }; typedef QList NewSongInfoList; - Organise(TaskManager *task_manager, std::shared_ptr destination, const OrganiseFormat &format, bool copy, bool overwrite, bool mark_as_listened, bool albumcover, const NewSongInfoList &songs, bool eject_after); + Organise(TaskManager *task_manager, std::shared_ptr destination, const OrganiseFormat &format, bool copy, bool overwrite, bool mark_as_listened, bool albumcover, const NewSongInfoList &songs, bool eject_after, const QString &playlist = QString()); ~Organise(); static const int kBatchSize; @@ -123,6 +123,7 @@ class Organise : public QObject { const bool albumcover_; const bool eject_after_; int task_count_; + const QString playlist_; #ifdef HAVE_GSTREAMER QBasicTimer transcode_progress_timer_; diff --git a/src/organise/organisedialog.cpp b/src/organise/organisedialog.cpp index 0e1cce9a7..634e0dec2 100644 --- a/src/organise/organisedialog.cpp +++ b/src/organise/organisedialog.cpp @@ -244,7 +244,12 @@ SongList OrganiseDialog::LoadSongsBlocking(const QStringList &filenames) { } void OrganiseDialog::SetCopy(bool copy) { - ui_->aftercopying->setCurrentIndex(copy ? 0 : 1); + ui_->aftercopying->setCurrentIndex(copy ? 0 : 1); +} + +void OrganiseDialog::SetPlaylist(const QString &playlist) +{ + playlist_ = playlist; } void OrganiseDialog::InsertTag(const QString &tag) { @@ -399,7 +404,7 @@ void OrganiseDialog::accept() { // It deletes itself when it's finished. const bool copy = ui_->aftercopying->currentIndex() == 0; - Organise *organise = new Organise(task_manager_, storage, format_, copy, ui_->overwrite->isChecked(), ui_->mark_as_listened->isChecked(), ui_->albumcover->isChecked(), new_songs_info_, ui_->eject_after->isChecked()); + Organise *organise = new Organise(task_manager_, storage, format_, copy, ui_->overwrite->isChecked(), ui_->mark_as_listened->isChecked(), ui_->albumcover->isChecked(), new_songs_info_, ui_->eject_after->isChecked(), playlist_); connect(organise, SIGNAL(Finished(QStringList, QStringList)), SLOT(OrganiseFinished(QStringList, QStringList))); connect(organise, SIGNAL(FileCopied(int)), this, SIGNAL(FileCopied(int))); if (backend_) diff --git a/src/organise/organisedialog.h b/src/organise/organisedialog.h index 1b9d159ec..b1bb9eeaa 100644 --- a/src/organise/organisedialog.h +++ b/src/organise/organisedialog.h @@ -74,6 +74,8 @@ class OrganiseDialog : public QDialog { void SetCopy(bool copy); static Organise::NewSongInfoList ComputeNewSongsFilenames(const SongList &songs, const OrganiseFormat &format); + + void SetPlaylist(const QString &playlist); signals: void FileCopied(int); @@ -114,6 +116,7 @@ class OrganiseDialog : public QDialog { SongList songs_; Organise::NewSongInfoList new_songs_info_; quint64 total_size_; + QString playlist_; std::unique_ptr error_dialog_; diff --git a/src/playlist/playlistlistcontainer.cpp b/src/playlist/playlistlistcontainer.cpp index aa40f9ce4..3f3bc8084 100644 --- a/src/playlist/playlistlistcontainer.cpp +++ b/src/playlist/playlistlistcontainer.cpp @@ -53,6 +53,11 @@ #include "playlistlistmodel.h" #include "playlistmanager.h" #include "ui_playlistlistcontainer.h" +#include "collection/collectionmodel.h" +#ifndef Q_OS_WIN +# include "device/devicemanager.h" +# include "device/devicestatefiltermodel.h" +#endif class PlaylistListSortFilterModel : public QSortFilterProxyModel { public: @@ -80,6 +85,7 @@ PlaylistListContainer::PlaylistListContainer(QWidget *parent) action_new_folder_(new QAction(this)), action_remove_(new QAction(this)), action_save_playlist_(new QAction(this)), + action_copy_to_device_(new QAction(this)), model_(new PlaylistListModel(this)), proxy_(new PlaylistListSortFilterModel(this)), loaded_icons_(false), @@ -92,6 +98,7 @@ PlaylistListContainer::PlaylistListContainer(QWidget *parent) action_new_folder_->setText(tr("New folder")); action_remove_->setText(tr("Delete")); action_save_playlist_->setText(tr("Save playlist", "Save playlist menu action.")); + action_copy_to_device_->setText(tr("Copy to device...")); ui_->new_folder->setDefaultAction(action_new_folder_); ui_->remove->setDefaultAction(action_remove_); @@ -100,6 +107,7 @@ PlaylistListContainer::PlaylistListContainer(QWidget *parent) connect(action_new_folder_, SIGNAL(triggered()), SLOT(NewFolderClicked())); connect(action_remove_, SIGNAL(triggered()), SLOT(DeleteClicked())); connect(action_save_playlist_, SIGNAL(triggered()), SLOT(SavePlaylist())); + connect(action_copy_to_device_, SIGNAL(triggered()), SLOT(CopyToDevice())); connect(model_, SIGNAL(PlaylistPathChanged(int, QString)), SLOT(PlaylistPathChanged(int, QString))); proxy_->setSourceModel(model_); @@ -127,6 +135,7 @@ void PlaylistListContainer::showEvent(QShowEvent *e) { action_new_folder_->setIcon(IconLoader::Load("folder-new")); action_remove_->setIcon(IconLoader::Load("edit-delete")); action_save_playlist_->setIcon(IconLoader::Load("document-save")); + action_copy_to_device_->setIcon(IconLoader::Load("device")); model_->SetIcons(IconLoader::Load("view-media-playlist"), IconLoader::Load("folder")); @@ -317,6 +326,33 @@ void PlaylistListContainer::ItemDoubleClicked(const QModelIndex &proxy_index) { } +void PlaylistListContainer::CopyToDevice() +{ +#ifndef Q_OS_WIN + // Reuse the organise dialog, but set the detail about the playlist name + if (!organise_dialog_) { + organise_dialog_.reset(new OrganiseDialog {app_->task_manager()}); + } + organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true); + organise_dialog_->SetCopy(true); + + const QModelIndex ¤t_index = proxy_->mapToSource(ui_->tree->currentIndex()); + + // Is it a playlist? + if (current_index.data(PlaylistListModel::Role_Type).toInt() == PlaylistListModel::Type_Playlist) { + const int playlist_id = current_index.data(PlaylistListModel::Role_PlaylistId).toInt(); + + QStandardItem *item = model_->PlaylistById(playlist_id); + QString playlist_name = item ? item->text() : tr("Playlist"); + organise_dialog_->SetPlaylist(playlist_name); + + // Get the songs in the playlist + organise_dialog_->SetSongs(app_->playlist_manager()->playlist(playlist_id)->GetAllSongs()); + organise_dialog_->show(); + } +#endif +} + void PlaylistListContainer::DeleteClicked() { QSet ids; @@ -386,6 +422,8 @@ void PlaylistListContainer::contextMenuEvent(QContextMenuEvent *e) { menu_->addAction(action_remove_); menu_->addSeparator(); menu_->addAction(action_save_playlist_); + menu_->addSeparator(); + menu_->addAction(action_copy_to_device_); } menu_->popup(e->globalPos()); diff --git a/src/playlist/playlistlistcontainer.h b/src/playlist/playlistlistcontainer.h index 3a2d73f4b..10679ff0d 100644 --- a/src/playlist/playlistlistcontainer.h +++ b/src/playlist/playlistlistcontainer.h @@ -35,6 +35,8 @@ #include #include +#include + class QModelIndex; class Application; class Playlist; @@ -59,6 +61,7 @@ private slots: void NewFolderClicked(); void DeleteClicked(); void ItemDoubleClicked(const QModelIndex &index); + void CopyToDevice(); // From the model void PlaylistPathChanged(int id, const QString &new_path); @@ -94,6 +97,7 @@ private: QAction *action_new_folder_; QAction *action_remove_; QAction *action_save_playlist_; + QAction *action_copy_to_device_; PlaylistListModel *model_; QSortFilterProxyModel *proxy_; @@ -102,6 +106,8 @@ private: QIcon padded_play_icon_; int active_playlist_id_; + + std::unique_ptr organise_dialog_; }; #endif // PLAYLISTLISTCONTAINER_H