From 3d6677fe6d03cd864fdc5e47d9a6c9217fb957bf Mon Sep 17 00:00:00 2001 From: David Sansome Date: Tue, 26 Apr 2011 22:06:58 +0000 Subject: [PATCH] Allow radio services (Spotify) to store whole songs in playlist items --- data/data.qrc | 1 + data/schema/schema-29.sql | 59 +++++++++++++++++++ .../digitallyimported-radio/servicebase.py | 9 ++- src/core/database.cpp | 23 +++++--- src/core/database.h | 6 +- src/devices/devicedatabasebackend.cpp | 2 +- src/playlist/playlist.cpp | 4 +- src/playlist/playlistbackend.cpp | 17 +++--- src/playlist/playlistbackend.h | 2 + src/playlist/playlistitem.cpp | 16 ++--- src/playlist/playlistitem.h | 8 +-- src/playlist/songplaylistitem.cpp | 43 +------------- src/playlist/songplaylistitem.h | 2 +- src/radio/lastfmservice.cpp | 52 ++++++++++------ src/radio/radiomodel.h | 10 ++-- src/radio/radioplaylistitem.cpp | 37 +++++------- src/radio/radioplaylistitem.h | 7 +-- src/radio/somafmservice.cpp | 24 ++++---- src/radio/spotifyservice.cpp | 2 +- src/radio/spotifyservice.h | 1 - src/scripting/python/radiomodel.sip | 3 +- 21 files changed, 177 insertions(+), 151 deletions(-) create mode 100644 data/schema/schema-29.sql diff --git a/data/data.qrc b/data/data.qrc index e7e5e42c6..bb65203f0 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -310,5 +310,6 @@ icons/22x22/mail-message.png icons/32x32/mail-message.png icons/48x48/mail-message.png + schema/schema-29.sql diff --git a/data/schema/schema-29.sql b/data/schema/schema-29.sql new file mode 100644 index 000000000..2f53528c5 --- /dev/null +++ b/data/schema/schema-29.sql @@ -0,0 +1,59 @@ +ALTER TABLE playlist_items ADD COLUMN albumartist TEXT; + +ALTER TABLE playlist_items ADD COLUMN composer TEXT; + +ALTER TABLE playlist_items ADD COLUMN track INTEGER; + +ALTER TABLE playlist_items ADD COLUMN disc INTEGER; + +ALTER TABLE playlist_items ADD COLUMN bpm REAL; + +ALTER TABLE playlist_items ADD COLUMN year INTEGER; + +ALTER TABLE playlist_items ADD COLUMN genre TEXT; + +ALTER TABLE playlist_items ADD COLUMN comment TEXT; + +ALTER TABLE playlist_items ADD COLUMN compilation INTEGER; + +ALTER TABLE playlist_items ADD COLUMN bitrate INTEGER; + +ALTER TABLE playlist_items ADD COLUMN samplerate INTEGER; + +ALTER TABLE playlist_items ADD COLUMN directory INTEGER; + +ALTER TABLE playlist_items ADD COLUMN filename TEXT; + +ALTER TABLE playlist_items ADD COLUMN mtime INTEGER; + +ALTER TABLE playlist_items ADD COLUMN ctime INTEGER; + +ALTER TABLE playlist_items ADD COLUMN filesize INTEGER; + +ALTER TABLE playlist_items ADD COLUMN sampler INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN art_automatic TEXT; + +ALTER TABLE playlist_items ADD COLUMN art_manual TEXT; + +ALTER TABLE playlist_items ADD COLUMN filetype INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN playcount INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN lastplayed INTEGER; + +ALTER TABLE playlist_items ADD COLUMN rating INTEGER; + +ALTER TABLE playlist_items ADD COLUMN forced_compilation_on INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN forced_compilation_off INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN effective_compilation NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN skipcount INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE playlist_items ADD COLUMN score INTEGER NOT NULL DEFAULT 0; + +UPDATE playlist_items SET filename = url; + +UPDATE schema_version SET version=29; diff --git a/scripts/digitallyimported-radio/servicebase.py b/scripts/digitallyimported-radio/servicebase.py index 9ec3947b1..246ade687 100644 --- a/scripts/digitallyimported-radio/servicebase.py +++ b/scripts/digitallyimported-radio/servicebase.py @@ -122,12 +122,15 @@ class DigitallyImportedServiceBase(clementine.RadioService): self.root.removeRows(0, self.root.rowCount()) for stream in streams: + song = clementine.Song() + song.set_title(stream["name"]) + song.set_artist(self.SERVICE_DESCRIPTION) + song.set_filename("digitallyimported://%s" % stream["key"]) + item = QStandardItem(QIcon(":last.fm/icon_radio.png"), stream["name"]) item.setData(stream["description"], PyQt4.QtCore.Qt.ToolTipRole) - item.setData("digitallyimported://%s" % stream["key"], clementine.RadioModel.Role_Url) item.setData(clementine.RadioModel.PlayBehaviour_SingleItem, clementine.RadioModel.Role_PlayBehaviour) - item.setData(stream["name"], clementine.RadioModel.Role_Title) - item.setData(self.SERVICE_DESCRIPTION, clementine.RadioModel.Role_Artist) + item.setData(song, clementine.RadioModel.Role_SongMetadata) self.root.appendRow(item) def playlistitem_options(self): diff --git a/src/core/database.cpp b/src/core/database.cpp index f4fbfd6be..64d3369c2 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -32,7 +32,7 @@ #include const char* Database::kDatabaseFilename = "clementine.db"; -const int Database::kSchemaVersion = 28; +const int Database::kSchemaVersion = 29; const char* Database::kMagicAllSongsTables = "%allsongstables"; int Database::sNextConnectionId = 1; @@ -439,7 +439,7 @@ QSqlDatabase Database::Connect() { " WHERE type='table'").arg(key), db); if (!q.exec() || !q.next()) { ScopedTransaction t(&db); - ExecFromFile(attached_databases_[key].schema_, db); + ExecFromFile(attached_databases_[key].schema_, db, 0); t.Commit(); } } @@ -508,25 +508,27 @@ void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) { filename = QString(":/schema/schema-%1.sql").arg(version); ScopedTransaction t(&db); - ExecFromFile(filename, db); + ExecFromFile(filename, db, version - 1); t.Commit(); } -void Database::ExecFromFile(const QString &filename, QSqlDatabase &db) { +void Database::ExecFromFile(const QString &filename, QSqlDatabase &db, + int schema_version) { // Open and read the database schema QFile schema_file(filename); if (!schema_file.open(QIODevice::ReadOnly)) qFatal("Couldn't open schema file %s", filename.toUtf8().constData()); - ExecCommands(QString::fromUtf8(schema_file.readAll()), db); + ExecCommands(QString::fromUtf8(schema_file.readAll()), db, schema_version); } -void Database::ExecCommands(const QString &schema, QSqlDatabase &db) { +void Database::ExecCommands(const QString& schema, QSqlDatabase& db, + int schema_version) { // Run each command QStringList commands(schema.split(";\n\n")); // We don't want this list to reflect possible DB schema changes // so we initialize it before executing any statements. - QStringList tables = SongsTables(db); + QStringList tables = SongsTables(db, schema_version); foreach (const QString& command, commands) { // There are now lots of "songs" tables that need to have the same schema: @@ -549,7 +551,7 @@ void Database::ExecCommands(const QString &schema, QSqlDatabase &db) { } } -QStringList Database::SongsTables(QSqlDatabase& db) const { +QStringList Database::SongsTables(QSqlDatabase& db, int schema_version) const { QStringList ret; // look for the tables in the main db @@ -570,6 +572,11 @@ QStringList Database::SongsTables(QSqlDatabase& db) const { } } + if (schema_version > 29) { + // The playlist_items table became a songs table in version 29. + ret << "playlist_items"; + } + return ret; } diff --git a/src/core/database.h b/src/core/database.h index 1ab1c327f..1718291ab 100644 --- a/src/core/database.h +++ b/src/core/database.h @@ -54,8 +54,8 @@ class Database : public QObject { QMutex* Mutex() { return &mutex_; } void RecreateAttachedDb(const QString& database_name); - void ExecFromFile(const QString& filename, QSqlDatabase &db); - void ExecCommands(const QString& commands, QSqlDatabase &db); + void ExecFromFile(const QString& filename, QSqlDatabase &db, int schema_version); + void ExecCommands(const QString& commands, QSqlDatabase &db, int schema_version); int startup_schema_version() const { return startup_schema_version_; } int current_schema_version() const { return kSchemaVersion; } @@ -67,7 +67,7 @@ class Database : public QObject { void UpdateMainSchema(QSqlDatabase* db); void UpdateDatabaseSchema(int version, QSqlDatabase& db); - QStringList SongsTables(QSqlDatabase& db) const; + QStringList SongsTables(QSqlDatabase& db, int schema_version) const; struct AttachedDatabase { AttachedDatabase() {} diff --git a/src/devices/devicedatabasebackend.cpp b/src/devices/devicedatabasebackend.cpp index 9f2bbb6d8..1b452ee16 100644 --- a/src/devices/devicedatabasebackend.cpp +++ b/src/devices/devicedatabasebackend.cpp @@ -90,7 +90,7 @@ int DeviceDatabaseBackend::AddDevice(const Device& device) { QString schema = QString::fromUtf8(schema_file.readAll()); schema.replace("%deviceid", QString::number(id)); - db_->ExecCommands(schema, db); + db_->ExecCommands(schema, db, 0); t.Commit(); return id; diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 6fb179b16..67b762ad5 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -935,9 +935,7 @@ void Playlist::InsertRadioStations(const RadioModel* model, case RadioModel::PlayBehaviour_SingleItem: playlist_items << shared_ptr(new RadioPlaylistItem( model->ServiceForIndex(item), - item.data(RadioModel::Role_Url).toUrl(), - item.data(RadioModel::Role_Title).toString(), - item.data(RadioModel::Role_Artist).toString())); + item.data(RadioModel::Role_SongMetadata).value())); break; case RadioModel::PlayBehaviour_UseSongLoader: diff --git a/src/playlist/playlistbackend.cpp b/src/playlist/playlistbackend.cpp index 8f8dc7bce..cfc0082c5 100644 --- a/src/playlist/playlistbackend.cpp +++ b/src/playlist/playlistbackend.cpp @@ -38,6 +38,8 @@ using smart_playlists::GeneratorPtr; using boost::shared_ptr; +const int PlaylistBackend::kSongTableJoins = 4; + PlaylistBackend::PlaylistBackend(QObject* parent) : QObject(parent), library_(NULL) @@ -109,8 +111,8 @@ QFuture PlaylistBackend::GetPlaylistItems(int playlist) { QString query = "SELECT songs.ROWID, " + Song::JoinSpec("songs") + "," " magnatune_songs.ROWID, " + Song::JoinSpec("magnatune_songs") + "," " jamendo_songs.ROWID, " + Song::JoinSpec("jamendo_songs") + "," - " p.type, p.url, p.title, p.artist, p.album, p.length," - " p.radio_service, p.beginning, p.cue_path" + " p.ROWID, " + Song::JoinSpec("p") + "," + " p.type, p.radio_service" " FROM playlist_items AS p" " LEFT JOIN songs" " ON p.library_id = songs.ROWID" @@ -140,7 +142,7 @@ QFuture PlaylistBackend::GetPlaylistItems(int playlist) { PlaylistItemPtr PlaylistBackend::NewSongFromQuery(const SqlRow& row, boost::shared_ptr state) { // The song tables get joined first, plus one each for the song ROWIDs - const int playlist_row = (Song::kColumns.count() + 1) * 3; + const int playlist_row = (Song::kColumns.count() + 1) * kSongTableJoins; PlaylistItemPtr item(PlaylistItem::NewFromType(row.value(playlist_row).toString())); if (item) { @@ -219,9 +221,10 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList& items, QSqlQuery clear("DELETE FROM playlist_items WHERE playlist = :playlist", db); QSqlQuery insert("INSERT INTO playlist_items" - " (playlist, type, library_id, url, title, artist, album," - " length, radio_service, beginning, cue_path)" - " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", db); + " (playlist, type, library_id, radio_service, " + + Song::kColumnSpec + ")" + " VALUES (:playlist, :type, :library_id, :radio_service, " + + Song::kBindSpec + ")", db); QSqlQuery update("UPDATE playlists SET " " last_played=:last_played," " dynamic_playlist_type=:dynamic_type," @@ -239,7 +242,7 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList& items, // Save the new ones foreach (PlaylistItemPtr item, items) { - insert.bindValue(0, playlist); + insert.bindValue(":playlist", playlist); item->BindToQuery(&insert); insert.exec(); diff --git a/src/playlist/playlistbackend.h b/src/playlist/playlistbackend.h index ae3cd0e3b..0f3bea83b 100644 --- a/src/playlist/playlistbackend.h +++ b/src/playlist/playlistbackend.h @@ -50,6 +50,8 @@ class PlaylistBackend : public QObject { typedef QList PlaylistList; typedef QFuture PlaylistItemFuture; + static const int kSongTableJoins; + PlaylistList GetAllPlaylists(); Playlist GetPlaylist(int id); PlaylistItemFuture GetPlaylistItems(int playlist); diff --git a/src/playlist/playlistitem.cpp b/src/playlist/playlistitem.cpp index e87c5c6ab..9742281d9 100644 --- a/src/playlist/playlistitem.cpp +++ b/src/playlist/playlistitem.cpp @@ -18,6 +18,7 @@ #include "playlistitem.h" #include "songplaylistitem.h" #include "core/logging.h" +#include "core/song.h" #include "library/library.h" #include "library/libraryplaylistitem.h" #include "radio/jamendoplaylistitem.h" @@ -64,16 +65,11 @@ PlaylistItem* PlaylistItem::NewFromSongsTable(const QString& table, const Song& } void PlaylistItem::BindToQuery(QSqlQuery* query) const { - query->bindValue(1, type()); - query->bindValue(2, DatabaseValue(Column_LibraryId)); - query->bindValue(3, DatabaseValue(Column_Url)); - query->bindValue(4, DatabaseValue(Column_Title)); - query->bindValue(5, DatabaseValue(Column_Artist)); - query->bindValue(6, DatabaseValue(Column_Album)); - query->bindValue(7, DatabaseValue(Column_Length)); - query->bindValue(8, DatabaseValue(Column_RadioService)); - query->bindValue(9, DatabaseValue(Column_Beginning)); - query->bindValue(10, DatabaseValue(Column_CuePath)); + query->bindValue(":type", type()); + query->bindValue(":library_id", DatabaseValue(Column_LibraryId)); + query->bindValue(":radio_service", DatabaseValue(Column_RadioService)); + + DatabaseSongMetadata().BindToQuery(query); } void PlaylistItem::SetTemporaryMetadata(const Song& metadata) { diff --git a/src/playlist/playlistitem.h b/src/playlist/playlistitem.h index c2486e057..5d806334f 100644 --- a/src/playlist/playlistitem.h +++ b/src/playlist/playlistitem.h @@ -138,18 +138,12 @@ class PlaylistItem : public boost::enable_shared_from_this { protected: enum DatabaseColumn { Column_LibraryId, - Column_Url, - Column_Title, - Column_Artist, - Column_Album, - Column_Length, Column_RadioService, - Column_Beginning, - Column_CuePath, }; virtual QVariant DatabaseValue(DatabaseColumn) const { return QVariant(QVariant::String); } + virtual Song DatabaseSongMetadata() const { return Song(); } QString type_; diff --git a/src/playlist/songplaylistitem.cpp b/src/playlist/songplaylistitem.cpp index 7a9d4dfd7..e230b4642 100644 --- a/src/playlist/songplaylistitem.cpp +++ b/src/playlist/songplaylistitem.cpp @@ -15,6 +15,7 @@ along with Clementine. If not, see . */ +#include "playlistbackend.h" #include "songplaylistitem.h" #include "library/sqlrow.h" @@ -35,55 +36,17 @@ SongPlaylistItem::SongPlaylistItem(const Song& song) } bool SongPlaylistItem::InitFromQuery(const SqlRow& query) { - // The song tables get joined first, plus one each for the song ROWIDs - const int row = (Song::kColumns.count() + 1) * 3; - - QString filename(query.value(row + 1).toString()); - QString title(query.value(row + 2).toString()); - QString artist(query.value(row + 3).toString()); - QString album(query.value(row + 4).toString()); - qint64 length(query.value(row + 5).toLongLong()); + song_.InitFromQuery(query, (Song::kColumns.count() + 1) * 3); if (type() == "Stream") { - song_.set_filename(filename); song_.set_filetype(Song::Type_Stream); - - song_.Init(title, artist, album, length); } else { - song_.InitFromFile(filename, -1); - - qint64 beginning(query.value(row + 7).toLongLong()); - QString cue_path(query.value(row + 8).toString()); - - // If the song was part of a cuesheet then keep the title, artist etc. that - // was loaded last time. - if (!cue_path.isEmpty()) { - song_.set_title(title); - song_.set_artist(artist); - song_.set_album(album); - song_.set_length_nanosec(length); - } - - song_.set_beginning_nanosec(beginning); - song_.set_cue_path(cue_path); + song_.set_directory_id(-1); } return true; } -QVariant SongPlaylistItem::DatabaseValue(DatabaseColumn column) const { - switch (column) { - case Column_Url: return song_.filename(); - case Column_Title: return song_.title(); - case Column_Artist: return song_.artist(); - case Column_Album: return song_.album(); - case Column_Length: return song_.length_nanosec(); - case Column_Beginning: return song_.beginning_nanosec(); - case Column_CuePath: return song_.cue_path(); - default: return PlaylistItem::DatabaseValue(column); - } -} - QUrl SongPlaylistItem::Url() const { return song_.url(); } diff --git a/src/playlist/songplaylistitem.h b/src/playlist/songplaylistitem.h index f5a5595a7..9ad0b42da 100644 --- a/src/playlist/songplaylistitem.h +++ b/src/playlist/songplaylistitem.h @@ -37,7 +37,7 @@ class SongPlaylistItem : public PlaylistItem { QUrl Url() const; protected: - QVariant DatabaseValue(DatabaseColumn) const; + Song DatabaseSongMetadata() const { return song_; } private: Song song_; diff --git a/src/radio/lastfmservice.cpp b/src/radio/lastfmservice.cpp index 8493caf4a..d597e350d 100644 --- a/src/radio/lastfmservice.cpp +++ b/src/radio/lastfmservice.cpp @@ -234,9 +234,12 @@ void LastFMService::LazyPopulate(QStandardItem* parent) { QStandardItem* LastFMService::CreateStationItem( QStandardItem* parent, const QString& name, const QString& icon, const QString& url, const QString& title) { + Song song; + song.set_filename(url); + song.set_title(title); + QStandardItem* ret = new QStandardItem(QIcon(icon), name); - ret->setData(url, RadioModel::Role_Url); - ret->setData(title, RadioModel::Role_Title); + ret->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); ret->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); parent->appendRow(ret); return ret; @@ -606,9 +609,12 @@ void LastFMService::RefreshFriendsFinished() { } foreach (const lastfm::User& f, friends) { + Song song; + song.set_filename("lastfm://user/" + f.name() + "/library"); + song.set_title(tr("Last.fm Library - %1").arg(f.name())); + QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_user.png"), f.name()); - item->setData(QUrl("lastfm://user/" + f.name() + "/library"), RadioModel::Role_Url); - item->setData(tr("Last.fm Library - %1").arg(f.name()), RadioModel::Role_Title); + item->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); item->setData(true, RadioModel::Role_CanLazyLoad); item->setData(Type_OtherUser, RadioModel::Role_Type); item->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); @@ -635,9 +641,12 @@ void LastFMService::RefreshNeighboursFinished() { } foreach (const lastfm::User& n, neighbours) { + Song song; + song.set_filename("lastfm://user/" + n.name() + "/library"); + song.set_title(tr("Last.fm Library - %1").arg(n.name())); + QStandardItem* item = new QStandardItem(QIcon(":last.fm/user_purple.png"), n.name()); - item->setData(QUrl("lastfm://user/" + n.name() + "/library"), RadioModel::Role_Url); - item->setData(tr("Last.fm Library - %1").arg(n.name()), RadioModel::Role_Title); + item->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); item->setData(true, RadioModel::Role_CanLazyLoad); item->setData(Type_OtherUser, RadioModel::Role_Type); item->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); @@ -686,9 +695,12 @@ void LastFMService::AddArtistOrTag(const QString& name, else url_content = content; + Song song; + song.set_filename(url_pattern.arg(url_content)); + song.set_title(title_pattern.arg(content)); + QStandardItem* item = new QStandardItem(QIcon(icon), content); - item->setData(url_pattern.arg(url_content), RadioModel::Role_Url); - item->setData(title_pattern.arg(content), RadioModel::Role_Title); + item->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); item->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); list->appendRow(item); emit AddItemToPlaylist(item->index(), AddMode_Append); @@ -728,9 +740,12 @@ void LastFMService::RestoreList(const QString& name, else url_content = content; + Song song; + song.set_filename(url_pattern.arg(url_content)); + song.set_title(title_pattern.arg(content)); + QStandardItem* item = new QStandardItem(icon, content); - item->setData(url_pattern.arg(url_content), RadioModel::Role_Url); - item->setData(title_pattern.arg(content), RadioModel::Role_Title); + item->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); item->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); parent->appendRow(item); } @@ -845,22 +860,23 @@ PlaylistItemPtr LastFMService::PlaylistItemForUrl(const QUrl& url) { // This is a bit of a hack, it's only used by the artist/song info tag // widgets for tag radio and similar artists radio. - PlaylistItemPtr ret; - if (url.scheme() != "lastfm") - return ret; + return PlaylistItemPtr(); QStringList sections(url.path().split("/", QString::SkipEmptyParts)); + Song song; + song.set_filename(url.toString()); + if (sections.count() == 2 && url.host() == "artist" && sections[1] == "similarartists") { - ret.reset(new RadioPlaylistItem(this, url, - tr(kTitleArtist).arg(sections[0]), QString())); + song.set_title(tr(kTitleArtist).arg(sections[0])); } else if (sections.count() == 1 && url.host() == "globaltags") { - ret.reset(new RadioPlaylistItem(this, url, - tr(kTitleTag).arg(sections[0]), QString())); + song.set_title(tr(kTitleTag).arg(sections[0])); + } else { + return PlaylistItemPtr(); } - return ret; + return PlaylistItemPtr(new RadioPlaylistItem(this, song)); } void LastFMService::ToggleScrobbling() { diff --git a/src/radio/radiomodel.h b/src/radio/radiomodel.h index 37aeaf9c3..f88b1eff5 100644 --- a/src/radio/radiomodel.h +++ b/src/radio/radiomodel.h @@ -54,14 +54,12 @@ public: Role_PlayBehaviour, // The URL of the media for this item. This is required if the - // PlayBehaviour is set to something other than None. How the URL is - // used depends on the PlayBehaviour. + // PlayBehaviour is set to PlayBehaviour_UseSongLoader. Role_Url, - // These fields are used to populate the playlist columns for this item - // only when using PlayBehaviour_SingleItem. They are ignored otherwise - Role_Title, - Role_Artist, + // The metadata used in the item that is added to the playlist if the + // PlayBehaviour is set to PlayBehaviour_SingleItem. Ignored otherwise. + Role_SongMetadata, // If this is set to true then the model will call the service's // LazyPopulate method when this item is expanded. Use this if your item's diff --git a/src/radio/radioplaylistitem.cpp b/src/radio/radioplaylistitem.cpp index e893e3771..2c52c8338 100644 --- a/src/radio/radioplaylistitem.cpp +++ b/src/radio/radioplaylistitem.cpp @@ -20,6 +20,7 @@ #include "radiomodel.h" #include "core/settingsprovider.h" #include "library/sqlrow.h" +#include "playlist/playlistbackend.h" #include #include @@ -31,28 +32,24 @@ RadioPlaylistItem::RadioPlaylistItem(const QString& type) { } -RadioPlaylistItem::RadioPlaylistItem(RadioService* service, const QUrl& url, - const QString& title, const QString& artist) +RadioPlaylistItem::RadioPlaylistItem(RadioService* service, const Song& metadata) : PlaylistItem("Radio"), - url_(url), - title_(title), - artist_(artist), service_name_(service->name()), - set_service_icon_(false) + set_service_icon_(false), + metadata_(metadata) { InitMetadata(); } bool RadioPlaylistItem::InitFromQuery(const SqlRow& query) { // The song tables gets joined first, plus one each for the song ROWIDs - const int row = (Song::kColumns.count() + 1) * 3; + const int row = (Song::kColumns.count() + 1) * PlaylistBackend::kSongTableJoins; - url_ = query.value(row + 1).toString(); - title_ = query.value(row + 2).toString(); - artist_ = query.value(row + 3).toString(); - service_name_ = query.value(row + 6).toString(); + service_name_ = query.value(row + 1).toString(); + metadata_.InitFromQuery(query, (Song::kColumns.count() + 1) * 3); InitMetadata(); + return true; } @@ -73,22 +70,16 @@ RadioService* RadioPlaylistItem::service() const { QVariant RadioPlaylistItem::DatabaseValue(DatabaseColumn column) const { switch (column) { - case Column_Url: return url_.toString(); - case Column_Title: return title_; - case Column_Artist: return artist_; case Column_RadioService: return service_name_; default: return PlaylistItem::DatabaseValue(column); } } void RadioPlaylistItem::InitMetadata() { - if (!title_.isEmpty()) - metadata_.set_title(title_); - else - metadata_.set_title(url_.toString()); - - metadata_.set_artist(artist_); + if (metadata_.title().isEmpty()) + metadata_.set_title(metadata_.filename()); metadata_.set_filetype(Song::Type_Stream); + metadata_.set_valid(true); } Song RadioPlaylistItem::Metadata() const { @@ -106,18 +97,18 @@ PlaylistItem::SpecialLoadResult RadioPlaylistItem::StartLoading() { RadioService* s = service(); if (!s) return SpecialLoadResult(); - return s->StartLoading(url_); + return s->StartLoading(Url()); } PlaylistItem::SpecialLoadResult RadioPlaylistItem::LoadNext() { RadioService* s = service(); if (!s) return SpecialLoadResult(); - return s->LoadNext(url_); + return s->LoadNext(Url()); } QUrl RadioPlaylistItem::Url() const { - return url_; + return QUrl(metadata_.filename()); } PlaylistItem::Options RadioPlaylistItem::options() const { diff --git a/src/radio/radioplaylistitem.h b/src/radio/radioplaylistitem.h index ee2d36033..d264f1b1b 100644 --- a/src/radio/radioplaylistitem.h +++ b/src/radio/radioplaylistitem.h @@ -28,8 +28,7 @@ class RadioService; class RadioPlaylistItem : public PlaylistItem { public: RadioPlaylistItem(const QString& type); - RadioPlaylistItem(RadioService* service, const QUrl& url, - const QString& title, const QString& artist); + RadioPlaylistItem(RadioService* service, const Song& metadata); Options options() const; @@ -44,15 +43,13 @@ class RadioPlaylistItem : public PlaylistItem { protected: QVariant DatabaseValue(DatabaseColumn) const; + Song DatabaseSongMetadata() const { return metadata_; } private: void InitMetadata(); RadioService* service() const; private: - QUrl url_; - QString title_; - QString artist_; QString service_name_; bool set_service_icon_; diff --git a/src/radio/somafmservice.cpp b/src/radio/somafmservice.cpp index 23b20d041..79bcdb407 100644 --- a/src/radio/somafmservice.cpp +++ b/src/radio/somafmservice.cpp @@ -158,28 +158,30 @@ void SomaFMService::RefreshChannelsFinished() { } void SomaFMService::ReadChannel(QXmlStreamReader& reader) { - QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_radio.png"), QString()); - item->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); + Song song; while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::EndElement: - if (item->data(RadioModel::Role_Url).toString().isNull()) { - // Didn't find a URL - delete item; - } else { + if (!song.url().isEmpty()) { + QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_radio.png"), QString()); + item->setText(song.title()); + + song.set_title("SomaFM " + song.title()); + item->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); + item->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); + root_->appendRow(item); } return; case QXmlStreamReader::StartElement: if (reader.name() == "title") { - item->setText(reader.readElementText()); - item->setData("SomaFM " + item->text(), RadioModel::Role_Title); + song.set_title(reader.readElementText()); } else if (reader.name() == "dj") { - item->setData(reader.readElementText(), RadioModel::Role_Artist); + song.set_artist(reader.readElementText()); } else if (reader.name() == "fastpls" && reader.attributes().value("format") == "mp3") { - item->setData(reader.readElementText(), RadioModel::Role_Url); + song.set_filename(reader.readElementText()); } else { ConsumeElement(reader); } @@ -189,8 +191,6 @@ void SomaFMService::ReadChannel(QXmlStreamReader& reader) { break; } } - - delete item; } void SomaFMService::ConsumeElement(QXmlStreamReader& reader) { diff --git a/src/radio/spotifyservice.cpp b/src/radio/spotifyservice.cpp index db9b0de89..3ec4c2623 100644 --- a/src/radio/spotifyservice.cpp +++ b/src/radio/spotifyservice.cpp @@ -194,7 +194,7 @@ void SpotifyService::FillPlaylist(QStandardItem* item, const protobuf::LoadPlayl QStandardItem* child = new QStandardItem(song.PrettyTitleWithArtist()); child->setData(Type_Track, RadioModel::Role_Type); - child->setData(QVariant::fromValue(song), Role_Metadata); + child->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); child->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); child->setData(QUrl(song.filename()), RadioModel::Role_Url); diff --git a/src/radio/spotifyservice.h b/src/radio/spotifyservice.h index 69be9224b..c81a03717 100644 --- a/src/radio/spotifyservice.h +++ b/src/radio/spotifyservice.h @@ -26,7 +26,6 @@ public: enum Role { Role_UserPlaylistIndex = RadioModel::RoleCount, - Role_Metadata, }; virtual QStandardItem* CreateRootItem(); diff --git a/src/scripting/python/radiomodel.sip b/src/scripting/python/radiomodel.sip index 2bbbe605a..a901f5356 100644 --- a/src/scripting/python/radiomodel.sip +++ b/src/scripting/python/radiomodel.sip @@ -10,8 +10,7 @@ public: Role_Type, Role_PlayBehaviour, Role_Url, - Role_Title, - Role_Artist, + Role_SongMetadata, Role_CanLazyLoad, Role_Service, };