duplicate remover script (fixes #21)
fix a scripting crash when one native object was registered more than once veto mechanism for inserting songs into playlist
This commit is contained in:
parent
b9b504be30
commit
a883630ab3
@ -7,3 +7,4 @@ function(install_script_files scriptname)
|
|||||||
endfunction(install_script_files)
|
endfunction(install_script_files)
|
||||||
|
|
||||||
add_subdirectory(digitallyimported-radio)
|
add_subdirectory(digitallyimported-radio)
|
||||||
|
add_subdirectory(remove-duplicates)
|
||||||
|
5
scripts/remove-duplicates/CMakeLists.txt
Normal file
5
scripts/remove-duplicates/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
install_script_files(remove-duplicates
|
||||||
|
icon.png
|
||||||
|
remove_duplicates.py
|
||||||
|
script.ini
|
||||||
|
)
|
BIN
scripts/remove-duplicates/icon.png
Normal file
BIN
scripts/remove-duplicates/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
70
scripts/remove-duplicates/remove_duplicates.py
Normal file
70
scripts/remove-duplicates/remove_duplicates.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import clementine
|
||||||
|
from clementine import SongInsertVetoListener
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveDuplicatesListener(SongInsertVetoListener):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
SongInsertVetoListener.__init__(self)
|
||||||
|
|
||||||
|
def init_listener(self):
|
||||||
|
for playlist in clementine.playlists.GetAllPlaylists():
|
||||||
|
playlist.AddSongInsertVetoListener(self)
|
||||||
|
|
||||||
|
clementine.playlists.PlaylistAdded.connect(self.playlist_added)
|
||||||
|
|
||||||
|
def remove_duplicates(self):
|
||||||
|
for playlist in clementine.playlists.GetAllPlaylists():
|
||||||
|
self.remove_duplicates_from(playlist)
|
||||||
|
|
||||||
|
def playlist_added(self, playlist_id):
|
||||||
|
playlist = clementine.playlists.playlist(playlist_id)
|
||||||
|
|
||||||
|
playlist.AddSongInsertVetoListener(self)
|
||||||
|
self.remove_duplicates_from(playlist)
|
||||||
|
|
||||||
|
def AboutToInsertSongs(self, old_songs, new_songs):
|
||||||
|
vetoed = []
|
||||||
|
used_urls = set()
|
||||||
|
|
||||||
|
songs = old_songs + new_songs
|
||||||
|
for song in songs:
|
||||||
|
url = self.url_for_song(song)
|
||||||
|
|
||||||
|
# don't veto songs without URL (possibly radios)
|
||||||
|
if len(url) > 0:
|
||||||
|
if url in used_urls:
|
||||||
|
vetoed.append(song)
|
||||||
|
used_urls.add(url)
|
||||||
|
|
||||||
|
return vetoed
|
||||||
|
|
||||||
|
def remove_duplicates_from(self, playlist):
|
||||||
|
indices = []
|
||||||
|
used_urls = set()
|
||||||
|
|
||||||
|
songs = playlist.GetAllSongs()
|
||||||
|
for i in range(0, len(songs)):
|
||||||
|
song = songs[i]
|
||||||
|
url = self.url_for_song(song)
|
||||||
|
|
||||||
|
# ignore songs without URL (possibly radios)
|
||||||
|
if len(url) > 0:
|
||||||
|
if url in used_urls:
|
||||||
|
indices.append(i)
|
||||||
|
used_urls.add(url)
|
||||||
|
|
||||||
|
if len(indices) > 0:
|
||||||
|
playlist.RemoveItemsWithoutUndo(indices)
|
||||||
|
|
||||||
|
def url_for_song(self, song):
|
||||||
|
if not song.filename() == "":
|
||||||
|
return song.filename() + ":" + str(song.beginning())
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
script = RemoveDuplicatesListener()
|
||||||
|
|
||||||
|
script.init_listener()
|
||||||
|
script.remove_duplicates()
|
9
scripts/remove-duplicates/script.ini
Normal file
9
scripts/remove-duplicates/script.ini
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[Script]
|
||||||
|
name=Duplicate remover
|
||||||
|
description=This script will prevent duplicates being added to your playlists.
|
||||||
|
author=Pawel Bara <keirangtp ( at ) gmail.com>
|
||||||
|
url=http://www.clementine-player.org
|
||||||
|
icon=icon.png
|
||||||
|
|
||||||
|
language=python
|
||||||
|
script_file=remove_duplicates.py
|
@ -758,6 +758,7 @@ if(HAVE_SCRIPTING_PYTHON)
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Song.cpp
|
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Song.cpp
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Subdirectory.cpp
|
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Subdirectory.cpp
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100TaskManagerTask.cpp
|
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100TaskManagerTask.cpp
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0101Playlist.cpp
|
||||||
)
|
)
|
||||||
endif(HAVE_SCRIPTING_PYTHON)
|
endif(HAVE_SCRIPTING_PYTHON)
|
||||||
|
|
||||||
|
@ -1154,3 +1154,9 @@ QFuture<bool> Song::BackgroundSave() const {
|
|||||||
QFuture<bool> future = QtConcurrent::run(&Song::Save, Song(*this));
|
QFuture<bool> future = QtConcurrent::run(&Song::Save, Song(*this));
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Song::operator==(const Song& other) const {
|
||||||
|
// TODO: this isn't working for radios
|
||||||
|
return filename() == other.filename() &&
|
||||||
|
beginning() == other.beginning();
|
||||||
|
}
|
||||||
|
@ -254,6 +254,8 @@ class Song {
|
|||||||
// Comparison functions
|
// Comparison functions
|
||||||
bool IsMetadataEqual(const Song& other) const;
|
bool IsMetadataEqual(const Song& other) const;
|
||||||
|
|
||||||
|
bool operator==(const Song& other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GuessFileType(TagLib::FileRef* fileref);
|
void GuessFileType(TagLib::FileRef* fileref);
|
||||||
static bool Save(const Song& song);
|
static bool Save(const Song& song);
|
||||||
|
@ -41,14 +41,15 @@
|
|||||||
#include "smartplaylists/generatorinserter.h"
|
#include "smartplaylists/generatorinserter.h"
|
||||||
#include "smartplaylists/generatormimedata.h"
|
#include "smartplaylists/generatormimedata.h"
|
||||||
|
|
||||||
#include <QtDebug>
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMimeData>
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QUndoStack>
|
#include <QFileInfo>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QMutableListIterator>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QUndoStack>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -122,13 +123,14 @@ Playlist::~Playlist() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void InsertSongItems(Playlist* playlist, const SongList& songs,
|
void Playlist::InsertSongItems(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
||||||
int pos, bool play_now, bool enqueue) {
|
|
||||||
PlaylistItemList items;
|
PlaylistItemList items;
|
||||||
|
|
||||||
foreach (const Song& song, songs) {
|
foreach (const Song& song, songs) {
|
||||||
items << PlaylistItemPtr(new T(song));
|
items << PlaylistItemPtr(new T(song));
|
||||||
}
|
}
|
||||||
playlist->InsertItems(items, pos, play_now, enqueue);
|
|
||||||
|
InsertItems(items, pos, play_now, enqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant Playlist::headerData(int section, Qt::Orientation, int role) const {
|
QVariant Playlist::headerData(int section, Qt::Orientation, int role) const {
|
||||||
@ -609,13 +611,13 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int ro
|
|||||||
// We want to check if these songs are from the actual local file backend,
|
// We want to check if these songs are from the actual local file backend,
|
||||||
// if they are we treat them differently.
|
// if they are we treat them differently.
|
||||||
if (song_data->backend && song_data->backend->songs_table() == Library::kSongsTable)
|
if (song_data->backend && song_data->backend->songs_table() == Library::kSongsTable)
|
||||||
InsertSongItems<LibraryPlaylistItem>(this, song_data->songs, row, play_now, enqueue_now);
|
InsertSongItems<LibraryPlaylistItem>(song_data->songs, row, play_now, enqueue_now);
|
||||||
else if (song_data->backend && song_data->backend->songs_table() == MagnatuneService::kSongsTable)
|
else if (song_data->backend && song_data->backend->songs_table() == MagnatuneService::kSongsTable)
|
||||||
InsertSongItems<MagnatunePlaylistItem>(this, song_data->songs, row, play_now, enqueue_now);
|
InsertSongItems<MagnatunePlaylistItem>(song_data->songs, row, play_now, enqueue_now);
|
||||||
else if (song_data->backend && song_data->backend->songs_table() == JamendoService::kSongsTable)
|
else if (song_data->backend && song_data->backend->songs_table() == JamendoService::kSongsTable)
|
||||||
InsertSongItems<JamendoPlaylistItem>(this, song_data->songs, row, play_now, enqueue_now);
|
InsertSongItems<JamendoPlaylistItem>(song_data->songs, row, play_now, enqueue_now);
|
||||||
else
|
else
|
||||||
InsertSongItems<SongPlaylistItem>(this, song_data->songs, row, play_now, enqueue_now);
|
InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now, enqueue_now);
|
||||||
} else if (const RadioMimeData* radio_data = qobject_cast<const RadioMimeData*>(data)) {
|
} else if (const RadioMimeData* radio_data = qobject_cast<const RadioMimeData*>(data)) {
|
||||||
// Dragged from the Radio pane
|
// Dragged from the Radio pane
|
||||||
InsertRadioStations(radio_data->model, radio_data->indexes,
|
InsertRadioStations(radio_data->model, radio_data->indexes,
|
||||||
@ -783,10 +785,44 @@ void Playlist::MoveItemsWithoutUndo(int start, const QList<int>& dest_rows) {
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::InsertItems(const PlaylistItemList& items, int pos, bool play_now, bool enqueue) {
|
void Playlist::InsertItems(const PlaylistItemList& itemsIn, int pos, bool play_now, bool enqueue) {
|
||||||
if (items.isEmpty())
|
if (itemsIn.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
PlaylistItemList items = itemsIn;
|
||||||
|
|
||||||
|
// exercise vetoes
|
||||||
|
SongList songs;
|
||||||
|
|
||||||
|
foreach(PlaylistItemPtr item, items) {
|
||||||
|
songs << item.get()->Metadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Song> vetoed;
|
||||||
|
foreach(SongInsertVetoListener* listener, veto_listeners_) {
|
||||||
|
foreach(const Song& song, listener->AboutToInsertSongs(GetAllSongs(), songs)) {
|
||||||
|
vetoed.append(song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!vetoed.isEmpty()) {
|
||||||
|
QMutableListIterator<PlaylistItemPtr> it(items);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
PlaylistItemPtr item = it.next();
|
||||||
|
const Song& current = item.get()->Metadata();
|
||||||
|
|
||||||
|
if(vetoed.contains(current)) {
|
||||||
|
vetoed.removeOne(current);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for empty items once again after veto
|
||||||
|
if(items.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const int start = pos == -1 ? items_.count() : pos;
|
const int start = pos == -1 ? items_.count() : pos;
|
||||||
undo_stack_->push(new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue));
|
undo_stack_->push(new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue));
|
||||||
|
|
||||||
@ -836,11 +872,11 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::InsertLibraryItems(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
void Playlist::InsertLibraryItems(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
||||||
InsertSongItems<LibraryPlaylistItem>(this, songs, pos, play_now, enqueue);
|
InsertSongItems<LibraryPlaylistItem>(songs, pos, play_now, enqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::InsertSongs(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
void Playlist::InsertSongs(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
||||||
InsertSongItems<SongPlaylistItem>(this, songs, pos, play_now, enqueue);
|
InsertSongItems<SongPlaylistItem>(songs, pos, play_now, enqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos, bool play_now, bool enqueue) {
|
||||||
@ -1109,6 +1145,31 @@ void Playlist::ItemsLoaded() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool DescendingIntLessThan(int a, int b) {
|
||||||
|
return a > b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playlist::RemoveItemsWithoutUndo(const QList<int>& indicesIn) {
|
||||||
|
// Sort the indices descending because removing elements 'backwards'
|
||||||
|
// is easier - indices don't 'move' in the process.
|
||||||
|
QList<int> indices = indicesIn;
|
||||||
|
qSort(indices.begin(), indices.end(), DescendingIntLessThan);
|
||||||
|
|
||||||
|
for(int j = 0; j < indices.count(); j++) {
|
||||||
|
int beginning = indices[j], end = indices[j];
|
||||||
|
|
||||||
|
// Splits the indices into sequences. For example this: [1, 2, 4],
|
||||||
|
// will get split into [1, 2] and [4].
|
||||||
|
while(j != indices.count() - 1 && indices[j] == indices[j + 1] + 1) {
|
||||||
|
beginning--;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the current sequence.
|
||||||
|
removeRows(beginning, end - beginning + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Playlist::removeRows(int row, int count, const QModelIndex& parent) {
|
bool Playlist::removeRows(int row, int count, const QModelIndex& parent) {
|
||||||
if (row < 0 || row >= items_.size() || row + count > items_.size()) {
|
if (row < 0 || row >= items_.size() || row + count > items_.size()) {
|
||||||
return false;
|
return false;
|
||||||
@ -1306,6 +1367,21 @@ void Playlist::RateSong(const QModelIndex& index, double rating) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Playlist::AddSongInsertVetoListener(SongInsertVetoListener* listener) {
|
||||||
|
veto_listeners_.append(listener);
|
||||||
|
connect(listener, SIGNAL(destroyed()), this, SLOT(SongInsertVetoListenerDestroyed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playlist::RemoveSongInsertVetoListener(SongInsertVetoListener* listener) {
|
||||||
|
disconnect(listener, SIGNAL(destroyed()), this, SLOT(SongInsertVetoListenerDestroyed()));
|
||||||
|
veto_listeners_.removeAll(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Playlist::SongInsertVetoListenerDestroyed() {
|
||||||
|
// qobject_cast returns NULL here for Python SIP listeners.
|
||||||
|
veto_listeners_.removeAll(static_cast<SongInsertVetoListener*>(sender()));
|
||||||
|
}
|
||||||
|
|
||||||
void Playlist::Shuffle() {
|
void Playlist::Shuffle() {
|
||||||
layoutAboutToBeChanged();
|
layoutAboutToBeChanged();
|
||||||
|
|
||||||
|
@ -48,6 +48,19 @@ typedef QMap<int, Qt::Alignment> ColumnAlignmentMap;
|
|||||||
Q_DECLARE_METATYPE(Qt::Alignment);
|
Q_DECLARE_METATYPE(Qt::Alignment);
|
||||||
Q_DECLARE_METATYPE(ColumnAlignmentMap);
|
Q_DECLARE_METATYPE(ColumnAlignmentMap);
|
||||||
|
|
||||||
|
// Objects that may prevent a song being added to the playlist. When there
|
||||||
|
// is something about to be inserted into it, Playlist notifies all of it's
|
||||||
|
// listeners about the fact and every one of them picks 'invalid' songs.
|
||||||
|
class SongInsertVetoListener : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Listener returns a list of 'invalid' songs. 'old_songs' are songs that are
|
||||||
|
// currently in the playlist and 'new_songs' are the songs about to be added if
|
||||||
|
// nobody exercises a veto.
|
||||||
|
virtual SongList AboutToInsertSongs(const SongList& old_songs, const SongList& new_songs) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class Playlist : public QAbstractListModel {
|
class Playlist : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -171,6 +184,8 @@ class Playlist : public QAbstractListModel {
|
|||||||
void InsertSongsOrLibraryItems(const SongList& items, int pos = -1, bool play_now = false, bool enqueue = false);
|
void InsertSongsOrLibraryItems(const SongList& items, int pos = -1, bool play_now = false, bool enqueue = false);
|
||||||
void InsertSmartPlaylist (smart_playlists::GeneratorPtr gen, int pos = -1, bool play_now = false, bool enqueue = false);
|
void InsertSmartPlaylist (smart_playlists::GeneratorPtr gen, int pos = -1, bool play_now = false, bool enqueue = false);
|
||||||
void InsertUrls (const QList<QUrl>& urls, int pos = -1, bool play_now = false, bool enqueue = false);
|
void InsertUrls (const QList<QUrl>& urls, int pos = -1, bool play_now = false, bool enqueue = false);
|
||||||
|
// Removes items with given indices from the playlist. This operation is not undoable.
|
||||||
|
void RemoveItemsWithoutUndo (const QList<int>& indices);
|
||||||
|
|
||||||
void StopAfter(int row);
|
void StopAfter(int row);
|
||||||
void ReloadItems(const QList<int>& rows);
|
void ReloadItems(const QList<int>& rows);
|
||||||
@ -178,6 +193,12 @@ class Playlist : public QAbstractListModel {
|
|||||||
// Changes rating of a song to the given value asynchronously
|
// Changes rating of a song to the given value asynchronously
|
||||||
void RateSong(const QModelIndex& index, double rating);
|
void RateSong(const QModelIndex& index, double rating);
|
||||||
|
|
||||||
|
// Registers an object which will get notifications when new songs
|
||||||
|
// are about to be inserted into this playlist.
|
||||||
|
void AddSongInsertVetoListener(SongInsertVetoListener* listener);
|
||||||
|
// Unregisters a SongInsertVetoListener object.
|
||||||
|
void RemoveSongInsertVetoListener(SongInsertVetoListener* listener);
|
||||||
|
|
||||||
// QAbstractListModel
|
// QAbstractListModel
|
||||||
int rowCount(const QModelIndex& = QModelIndex()) const { return items_.count(); }
|
int rowCount(const QModelIndex& = QModelIndex()) const { return items_.count(); }
|
||||||
int columnCount(const QModelIndex& = QModelIndex()) const { return ColumnCount; }
|
int columnCount(const QModelIndex& = QModelIndex()) const { return ColumnCount; }
|
||||||
@ -236,6 +257,9 @@ class Playlist : public QAbstractListModel {
|
|||||||
const QModelIndexList& items,
|
const QModelIndexList& items,
|
||||||
int pos, bool play_now, bool enqueue);
|
int pos, bool play_now, bool enqueue);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void InsertSongItems(const SongList& songs, int pos, bool play_now, bool enqueue);
|
||||||
|
|
||||||
// Modify the playlist without changing the undo stack. These are used by
|
// Modify the playlist without changing the undo stack. These are used by
|
||||||
// our friends in PlaylistUndoCommands
|
// our friends in PlaylistUndoCommands
|
||||||
void InsertItemsWithoutUndo(const PlaylistItemList& items, int pos,
|
void InsertItemsWithoutUndo(const PlaylistItemList& items, int pos,
|
||||||
@ -254,6 +278,7 @@ class Playlist : public QAbstractListModel {
|
|||||||
void SongSaveComplete();
|
void SongSaveComplete();
|
||||||
void ItemReloadComplete();
|
void ItemReloadComplete();
|
||||||
void ItemsLoaded();
|
void ItemsLoaded();
|
||||||
|
void SongInsertVetoListenerDestroyed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_loading_;
|
bool is_loading_;
|
||||||
@ -296,6 +321,8 @@ class Playlist : public QAbstractListModel {
|
|||||||
|
|
||||||
smart_playlists::GeneratorPtr dynamic_playlist_;
|
smart_playlists::GeneratorPtr dynamic_playlist_;
|
||||||
ColumnAlignmentMap column_alignments_;
|
ColumnAlignmentMap column_alignments_;
|
||||||
|
|
||||||
|
QList<SongInsertVetoListener*> veto_listeners_;
|
||||||
};
|
};
|
||||||
|
|
||||||
QDataStream& operator <<(QDataStream&, const Playlist*);
|
QDataStream& operator <<(QDataStream&, const Playlist*);
|
||||||
|
@ -70,6 +70,16 @@ void PlaylistManager::Init(LibraryBackend* library_backend,
|
|||||||
emit PlaylistManagerInitialized();
|
emit PlaylistManagerInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QList<Playlist*> PlaylistManager::GetAllPlaylists() const {
|
||||||
|
QList<Playlist*> result;
|
||||||
|
|
||||||
|
foreach(const Data& data, playlists_.values()) {
|
||||||
|
result.append(data.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Playlist* PlaylistManager::AddPlaylist(int id, const QString& name) {
|
Playlist* PlaylistManager::AddPlaylist(int id, const QString& name) {
|
||||||
Playlist* ret = new Playlist(playlist_backend_, task_manager_, library_backend_, id);
|
Playlist* ret = new Playlist(playlist_backend_, task_manager_, library_backend_, id);
|
||||||
ret->set_sequence(sequence_);
|
ret->set_sequence(sequence_);
|
||||||
|
@ -49,6 +49,9 @@ public:
|
|||||||
Playlist* current() const { return playlist(current_id()); }
|
Playlist* current() const { return playlist(current_id()); }
|
||||||
Playlist* active() const { return playlist(active_id()); }
|
Playlist* active() const { return playlist(active_id()); }
|
||||||
|
|
||||||
|
// Returns the collection of playlists managed by this PlaylistManager.
|
||||||
|
const QList<Playlist*> GetAllPlaylists() const;
|
||||||
|
|
||||||
const QItemSelection& selection(int id) const { return playlists_[id].selection; }
|
const QItemSelection& selection(int id) const { return playlists_[id].selection; }
|
||||||
const QItemSelection& current_selection() const { return selection(current_id()); }
|
const QItemSelection& current_selection() const { return selection(current_id()); }
|
||||||
const QItemSelection& active_selection() const { return selection(active_id()); }
|
const QItemSelection& active_selection() const { return selection(active_id()); }
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
%Include scriptinterface.sip
|
%Include scriptinterface.sip
|
||||||
%Include settingsdialog.sip
|
%Include settingsdialog.sip
|
||||||
%Include song.sip
|
%Include song.sip
|
||||||
|
%Include songinsertvetolistener.sip
|
||||||
%Include songloader.sip
|
%Include songloader.sip
|
||||||
%Include taskmanager.sip
|
%Include taskmanager.sip
|
||||||
%Include uiinterface.sip
|
%Include uiinterface.sip
|
||||||
|
@ -2,6 +2,7 @@ class Playlist : QAbstractListModel {
|
|||||||
|
|
||||||
%TypeHeaderCode
|
%TypeHeaderCode
|
||||||
#include "playlist/playlist.h"
|
#include "playlist/playlist.h"
|
||||||
|
#include "scripting/python/pythonengine.h"
|
||||||
%End
|
%End
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -107,12 +108,21 @@ public:
|
|||||||
void InsertSongsOrLibraryItems(const SongList& items, int pos = -1, bool play_now = false, bool enqueue = false);
|
void InsertSongsOrLibraryItems(const SongList& items, int pos = -1, bool play_now = false, bool enqueue = false);
|
||||||
// void InsertSmartPlaylist (smart_playlists::GeneratorPtr gen, int pos = -1, bool play_now = false, bool enqueue = false);
|
// void InsertSmartPlaylist (smart_playlists::GeneratorPtr gen, int pos = -1, bool play_now = false, bool enqueue = false);
|
||||||
void InsertUrls (const QList<QUrl>& urls, int pos = -1, bool play_now = false, bool enqueue = false);
|
void InsertUrls (const QList<QUrl>& urls, int pos = -1, bool play_now = false, bool enqueue = false);
|
||||||
|
void RemoveItemsWithoutUndo (const QList<int>& indicesIn);
|
||||||
|
|
||||||
void StopAfter(int row);
|
void StopAfter(int row);
|
||||||
void ReloadItems(const QList<int>& rows);
|
void ReloadItems(const QList<int>& rows);
|
||||||
|
|
||||||
// Changes rating of a song to the given value asynchronously
|
// Changes rating of a song to the given value asynchronously
|
||||||
void RateSong(const QModelIndex& index, double rating);
|
void RateSong(const QModelIndex& index, double rating);
|
||||||
|
|
||||||
|
void AddSongInsertVetoListener(SongInsertVetoListener* listener /Transfer/);
|
||||||
|
%MethodCode
|
||||||
|
sipCpp->AddSongInsertVetoListener(a0);
|
||||||
|
PythonEngine::instance()->RegisterNativeObject(a0);
|
||||||
|
%End
|
||||||
|
void RemoveSongInsertVetoListener(SongInsertVetoListener* listener);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void set_current_row(int index);
|
void set_current_row(int index);
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ public:
|
|||||||
Playlist* current() const;
|
Playlist* current() const;
|
||||||
Playlist* active() const;
|
Playlist* active() const;
|
||||||
|
|
||||||
|
const QList<Playlist*> GetAllPlaylists() const;
|
||||||
|
|
||||||
const QItemSelection& selection(int id) const;
|
const QItemSelection& selection(int id) const;
|
||||||
const QItemSelection& current_selection() const;
|
const QItemSelection& current_selection() const;
|
||||||
const QItemSelection& active_selection() const;
|
const QItemSelection& active_selection() const;
|
||||||
|
@ -26,6 +26,7 @@ class ScriptInterface : QObject {
|
|||||||
CLASS(RadioService),
|
CLASS(RadioService),
|
||||||
CLASS(ScriptInterface),
|
CLASS(ScriptInterface),
|
||||||
CLASS(SettingsDialog),
|
CLASS(SettingsDialog),
|
||||||
|
CLASS(SongInsertVetoListener),
|
||||||
CLASS(SongLoader),
|
CLASS(SongLoader),
|
||||||
CLASS(TaskManager),
|
CLASS(TaskManager),
|
||||||
CLASS(UIInterface),
|
CLASS(UIInterface),
|
||||||
|
8
src/scripting/python/songinsertvetolistener.sip
Normal file
8
src/scripting/python/songinsertvetolistener.sip
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class SongInsertVetoListener : QObject {
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "playlist/playlist.h"
|
||||||
|
%End
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual SongList AboutToInsertSongs(const SongList& old_songs, const SongList& new_songs) = 0;
|
||||||
|
};
|
@ -32,7 +32,9 @@ Script::~Script() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Script::AddNativeObject(QObject* object) {
|
void Script::AddNativeObject(QObject* object) {
|
||||||
native_objects_ << object;
|
if(!native_objects_.contains(object)) {
|
||||||
|
native_objects_ << object;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::RemoveNativeObject(QObject* object) {
|
void Script::RemoveNativeObject(QObject* object) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user