Compare commits

...

3 Commits

Author SHA1 Message Date
Skulking Scavenger 0da199e00e
Merge 933d7da3ee into f76dbffa6b 2024-04-11 18:46:07 +02:00
Clementine Buildbot f76dbffa6b Automatic merge of translations from Transifex 2024-03-24 02:30:19 +00:00
Klaus von Nachtstein 933d7da3ee Add Bulk Import option for playlists 2023-04-28 00:48:45 -05:00
10 changed files with 177 additions and 9 deletions

View File

@ -1241,6 +1241,7 @@ void Playlist::UpdateItems(const SongList& songs) {
}
}
}
connect(backend_,SIGNAL(PlaylistSaved(int)),SIGNAL(PlaylistSongsLoaded(int)));
Save();
}

View File

@ -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);

View File

@ -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,

View File

@ -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<QString, SongList> cached_cues_;

View File

@ -24,16 +24,22 @@
#include <QPainter>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QFileDialog>
#include <QDirIterator>
#include <iostream>
#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<int>();
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; i<filters.count(); i++){
filters[i] = "*." + filters[i];
}
QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
while(it.hasNext()) {
QString child(it.next());
QFileInfo child_info(child);
if (child_info.isDir()) {
QStandardItem* folder = model_->NewFolder(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<int>();
}
}
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());
}

View File

@ -18,6 +18,7 @@
#ifndef PLAYLISTLISTCONTAINER_H
#define PLAYLISTLISTCONTAINER_H
#include <QSettings>
#include <QWidget>
#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<int>* bulk_imported_id_list_;
int bulk_imported_count_;
int debug_count_;
};
#endif // PLAYLISTLISTCONTAINER_H

View File

@ -72,6 +72,13 @@
<item>
<widget class="QToolButton" name="save_playlist"/>
</item>
<item>
<widget class="QToolButton" name="bulk_import_playlists">
<property name="toolTip">
<string>Bulk Import Playlists</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">

View File

@ -101,6 +101,16 @@ QList<Playlist*> PlaylistManager::GetAllPlaylists() const {
return result;
}
QList<int> PlaylistManager::GetAllPlaylistIds() const {
QList<int> result;
for (const int data : playlists_.keys()) {
result.append(data);
}
return result;
}
QItemSelection PlaylistManager::selection(int id) const {
QMap<int, Data>::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<QUrl> 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<SongList> future,
const QString& filename,
Playlist::Path path_type) {

View File

@ -55,6 +55,7 @@ class PlaylistManagerInterface : public QObject {
// Returns the collection of playlists managed by this PlaylistManager.
virtual QList<Playlist*> GetAllPlaylists() const = 0;
virtual QList<int> 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<Playlist*> GetAllPlaylists() const;
QList<int> 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);

View File

@ -42,6 +42,7 @@
# Martin Brodbeck <martin@brodbeck-online.de>, 2013
# Martin Herkt <lachs0r@hong-mailing.de>, 2011
# Martin Herkt <luck3r@phicode.de>, 2010
# Michael Kohler, 2024
# MCMicS, 2020
# Mohamed Sakhri, 2013
# Mohamed Sakhri, 2013
@ -70,7 +71,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-04-09 10:24+0000\n"
"PO-Revision-Date: 2011-10-27 18:53+0000\n"
"Last-Translator: 6543 <6543@obermui.de>, 2020\n"
"Last-Translator: Michael Kohler, 2024\n"
"Language-Team: German (http://app.transifex.com/davidsansome/clementine/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -219,7 +220,7 @@ msgid "1 track"
msgstr "1 Titel"
msgid "128K MP3"
msgstr ""
msgstr "128K MP3"
msgid "128k MP3"
msgstr "128k MP3"
@ -240,7 +241,7 @@ msgid "A Grooveshark Anywhere account is required."
msgstr ""
msgid "A Spotify Premium account is required."
msgstr ""
msgstr "Spotify Premium Zugang wird benötigt."
msgid ""
"A smart playlist is a dynamic list of songs that come from your library. "
@ -293,7 +294,7 @@ msgid "Action"
msgstr "Aktion"
msgid "Active/deactive Wiiremote"
msgstr ""
msgstr "Aktivieren/deaktivieren Wiiremote"
msgid "Add Stream"
msgstr "Datenstrom hinzufügen"
@ -326,7 +327,7 @@ msgid "Add folder..."
msgstr "Ordner hinzufügen …"
msgid "Add media"
msgstr ""
msgstr "Medien hinzufügen ..."
msgid "Add new folder..."
msgstr "Neuen Ordner hinzufügen …"
@ -386,7 +387,7 @@ msgid "Add wiimotedev action"
msgstr "Aktion für Wii-Fernbedienung hinzufügen"
msgid "Add..."
msgstr ""
msgstr "Hinzufügen …"
msgid "Added this month"
msgstr "Diesen Monat hinzugefügt"
@ -422,7 +423,7 @@ msgid "Album info on jamendo.com..."
msgstr "Albuminformationen auf jamendo.com …"
msgid "Albumartist"
msgstr ""
msgstr "Albumkünstler"
msgid "Albums with covers"
msgstr "Alben mit Titelbildern"
@ -633,7 +634,7 @@ msgid "CUE sheet support"
msgstr "Unterstützung von Cuesheets"
msgid "Cancel"
msgstr ""
msgstr "Abbrechen"
msgid "Change cover art"
msgstr "Titelbilder ändern"