From ccb9f8cf940939d9a8d146b8cd6d777086bb39ed Mon Sep 17 00:00:00 2001 From: David Sansome Date: Thu, 28 Apr 2011 12:27:53 +0000 Subject: [PATCH] Use URLs everywhere instead of filenames. Move the URL parsing and song loading code out of individual playlist parsers and into the base class. Fix the playlist parser unit tests. --- data/data.qrc | 1 + data/schema/schema-31.sql | 3 + .../digitallyimported-radio/servicebase.py | 4 +- src/core/database.cpp | 2 +- src/core/deletefiles.cpp | 2 +- src/core/filesystemmusicstorage.cpp | 4 +- src/core/mpris1.cpp | 2 +- src/core/mpris2.cpp | 2 +- src/core/organise.cpp | 2 +- src/core/organiseformat.cpp | 2 +- src/core/song.cpp | 50 ++++----- src/core/song.h | 14 +-- src/core/songloader.cpp | 6 +- src/covers/albumcoverloader.cpp | 2 +- src/devices/afcdevice.cpp | 2 +- src/devices/connecteddevice.cpp | 2 +- src/devices/deviceview.cpp | 2 +- src/devices/gpoddevice.cpp | 7 +- src/devices/gpodloader.cpp | 11 +- src/devices/imobiledeviceconnection.cpp | 2 +- src/devices/mtpdevice.cpp | 6 +- src/devices/mtploader.cpp | 3 +- src/library/librarybackend.cpp | 24 ++-- src/library/librarybackend.h | 18 +-- src/library/librarymodel.cpp | 6 +- src/library/libraryplaylistitem.cpp | 4 +- src/library/libraryview.cpp | 2 +- src/library/librarywatcher.cpp | 14 +-- src/musicbrainz/tagfetcher.cpp | 2 +- src/playlist/playlist.cpp | 16 +-- src/playlist/playlistbackend.cpp | 2 +- src/playlist/playlistundocommands.cpp | 2 +- src/playlist/songplaylistitem.cpp | 2 +- src/playlistparsers/asxiniparser.cpp | 14 +-- src/playlistparsers/asxparser.cpp | 45 +++----- src/playlistparsers/asxparser.h | 5 +- src/playlistparsers/cueparser.cpp | 67 +++++------- src/playlistparsers/m3uparser.cpp | 32 ++---- src/playlistparsers/parserbase.cpp | 103 +++++++++--------- src/playlistparsers/parserbase.h | 27 ++--- src/playlistparsers/plsparser.cpp | 19 ++-- src/playlistparsers/xspfparser.cpp | 52 ++++----- src/playlistparsers/xspfparser.h | 5 +- src/radio/icecastbackend.cpp | 2 +- src/radio/jamendoplaylistitem.cpp | 2 +- src/radio/jamendoservice.cpp | 2 +- src/radio/lastfmservice.cpp | 28 ++--- src/radio/lastfmservice.h | 2 +- src/radio/magnatuneplaylistitem.cpp | 2 +- src/radio/magnatuneservice.cpp | 2 +- src/radio/radioplaylistitem.cpp | 4 +- src/radio/somafmservice.cpp | 2 +- src/radio/spotifyservice.cpp | 4 +- src/scripting/python/librarybackend.sip | 10 +- src/scripting/python/song.sip | 16 +-- src/ui/albumcoverchoicecontroller.cpp | 6 +- src/ui/albumcovermanager.cpp | 6 +- src/ui/albumcovermanager.h | 2 +- src/ui/albumcovermanagerlist.cpp | 2 +- src/ui/edittagdialog.cpp | 14 ++- src/ui/organisedialog.cpp | 12 +- src/ui/organiseerrordialog.cpp | 2 +- src/ui/trackselectiondialog.cpp | 6 +- tests/asxiniparser_test.cpp | 6 +- tests/asxparser_test.cpp | 12 +- tests/cueparser_test.cpp | 26 ++--- tests/librarybackend_test.cpp | 4 +- tests/librarymodel_test.cpp | 2 +- tests/m3uparser_test.cpp | 30 +++-- tests/mock_librarybackend.h | 6 +- tests/organiseformat_test.cpp | 2 +- tests/plsparser_test.cpp | 26 ++--- tests/songloader_test.cpp | 10 +- tests/songplaylistitem_test.cpp | 2 +- tests/test_utils.cpp | 4 + tests/test_utils.h | 1 + tests/xspfparser_test.cpp | 10 +- 77 files changed, 401 insertions(+), 458 deletions(-) create mode 100644 data/schema/schema-31.sql diff --git a/data/data.qrc b/data/data.qrc index 30110af24..686bd8a31 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -312,5 +312,6 @@ icons/48x48/mail-message.png schema/schema-29.sql schema/schema-30.sql + schema/schema-31.sql diff --git a/data/schema/schema-31.sql b/data/schema/schema-31.sql new file mode 100644 index 000000000..fd1d9051e --- /dev/null +++ b/data/schema/schema-31.sql @@ -0,0 +1,3 @@ +UPDATE songs SET filename = "file://" || filename; + +UPDATE schema_version SET version=31; diff --git a/scripts/digitallyimported-radio/servicebase.py b/scripts/digitallyimported-radio/servicebase.py index 246ade687..9279ef43b 100644 --- a/scripts/digitallyimported-radio/servicebase.py +++ b/scripts/digitallyimported-radio/servicebase.py @@ -125,7 +125,7 @@ class DigitallyImportedServiceBase(clementine.RadioService): song = clementine.Song() song.set_title(stream["name"]) song.set_artist(self.SERVICE_DESCRIPTION) - song.set_filename("digitallyimported://%s" % stream["key"]) + song.set_url(QUrl("digitallyimported://%s" % stream["key"])) item = QStandardItem(QIcon(":last.fm/icon_radio.png"), stream["name"]) item.setData(stream["description"], PyQt4.QtCore.Qt.ToolTipRole) @@ -192,6 +192,6 @@ class DigitallyImportedServiceBase(clementine.RadioService): # Take the first track in the playlist result.type_ = clementine.PlaylistItem.SpecialLoadResult.TrackAvailable - result.media_url_ = QUrl(songs[0].filename()) + result.media_url_ = songs[0].url() self.AsyncLoadFinished.emit(result) diff --git a/src/core/database.cpp b/src/core/database.cpp index e9e3ab62c..b89b99185 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 = 30; +const int Database::kSchemaVersion = 31; const char* Database::kMagicAllSongsTables = "%allsongstables"; int Database::sNextConnectionId = 1; diff --git a/src/core/deletefiles.cpp b/src/core/deletefiles.cpp index d915014a2..6ff03e595 100644 --- a/src/core/deletefiles.cpp +++ b/src/core/deletefiles.cpp @@ -60,7 +60,7 @@ void DeleteFiles::Start(const QStringList& filenames) { SongList songs; foreach (const QString& filename, filenames) { Song song; - song.set_filename(filename); + song.set_url(QUrl::fromLocalFile(filename)); songs << song; } diff --git a/src/core/filesystemmusicstorage.cpp b/src/core/filesystemmusicstorage.cpp index ba7405495..cb04f089a 100644 --- a/src/core/filesystemmusicstorage.cpp +++ b/src/core/filesystemmusicstorage.cpp @@ -66,14 +66,14 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) { bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob& job) { #ifdef HAVE_GIO //convert QString to char - QByteArray ba = job.metadata_.filename().toLocal8Bit(); + QByteArray ba = job.metadata_.url().toLocalFile().toLocal8Bit(); const char *filepathChar = ba.data(); GFile *file = g_file_new_for_path (filepathChar); bool success = g_file_trash(file, NULL, NULL); g_object_unref(file); return success; #else - return QFile::remove(job.metadata_.filename()); + return QFile::remove(job.metadata_.url().toLocalFile()); #endif } diff --git a/src/core/mpris1.cpp b/src/core/mpris1.cpp index 26ed74c03..c11935a63 100644 --- a/src/core/mpris1.cpp +++ b/src/core/mpris1.cpp @@ -321,7 +321,7 @@ void Mpris1TrackList::PlayTrack(int index) { QVariantMap Mpris1::GetMetadata(const Song& song) { QVariantMap ret; - AddMetadata("location", song.filename(), &ret); + AddMetadata("location", song.url().toString(), &ret); AddMetadata("title", song.PrettyTitle(), &ret); AddMetadata("artist", song.artist(), &ret); AddMetadata("album", song.album(), &ret); diff --git a/src/core/mpris2.cpp b/src/core/mpris2.cpp index a93d86f1c..6154de3e9 100644 --- a/src/core/mpris2.cpp +++ b/src/core/mpris2.cpp @@ -308,7 +308,7 @@ void Mpris2::ArtLoaded(const Song& song, const QString& art_uri) { using mpris::AddMetadata; AddMetadata("mpris:trackid", current_track_id(), &last_metadata_); - AddMetadata("xesam:url", song.filename(), &last_metadata_); + AddMetadata("xesam:url", song.url().toString(), &last_metadata_); AddMetadata("xesam:title", song.PrettyTitle(), &last_metadata_); AddMetadataAsList("xesam:artist", song.artist(), &last_metadata_); AddMetadata("xesam:album", song.album(), &last_metadata_); diff --git a/src/core/organise.cpp b/src/core/organise.cpp index 98c631ef4..431b94768 100644 --- a/src/core/organise.cpp +++ b/src/core/organise.cpp @@ -148,7 +148,7 @@ void Organise::ProcessSomeFiles() { song.set_filetype(task.new_filetype_); // Fiddle the filename extension as well to match the new type - song.set_filename(FiddleFileExtension(song.filename(), task.new_extension_)); + song.set_url(QUrl::fromLocalFile(FiddleFileExtension(song.url().toLocalFile(), task.new_extension_))); song.set_basefilename(FiddleFileExtension(song.basefilename(), task.new_extension_)); // Have to set this to the size of the new file or else funny stuff happens diff --git a/src/core/organiseformat.cpp b/src/core/organiseformat.cpp index 2b4d350fa..50cd8230b 100644 --- a/src/core/organiseformat.cpp +++ b/src/core/organiseformat.cpp @@ -139,7 +139,7 @@ QString OrganiseFormat::TagValue(const QString &tag, const Song &song) const { else if (tag == "length") value = QString::number(song.length_nanosec() / kNsecPerSec); else if (tag == "bitrate") value = QString::number(song.bitrate()); else if (tag == "samplerate") value = QString::number(song.samplerate()); - else if (tag == "extension") value = song.filename().section('.', -1, -1); + else if (tag == "extension") value = song.url().toLocalFile().section('.', -1, -1); else if (tag == "artistinitial") { value = song.albumartist().trimmed(); if (value.isEmpty()) value = song.artist().trimmed(); diff --git a/src/core/song.cpp b/src/core/song.cpp index a98662743..2e46aa0c1 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -253,7 +253,7 @@ bool Song::HasProperMediaFile() const { #endif QMutexLocker l(&taglib_mutex_); - scoped_ptr fileref(factory_->GetFileRef(d->filename_)); + scoped_ptr fileref(factory_->GetFileRef(d->url_.toLocalFile())); return !fileref->isNull() && fileref->tag(); } @@ -266,7 +266,7 @@ void Song::InitFromFile(const QString& filename, int directory_id) { d->init_from_file_ = true; - d->filename_ = filename; + d->url_ = QUrl::fromLocalFile(filename); d->directory_id_ = directory_id; QFileInfo info(filename); @@ -502,6 +502,7 @@ void Song::InitFromQuery(const SqlRow& q, int col) { d->init_from_file_ = true; #define tostr(n) (q.value(n).isNull() ? QString::null : q.value(n).toString()) + #define tobytearray(n)(q.value(n).isNull() ? QByteArray() : q.value(n).toByteArray()) #define toint(n) (q.value(n).isNull() ? -1 : q.value(n).toInt()) #define tolonglong(n) (q.value(n).isNull() ? -1 : q.value(n).toLongLong()) #define tofloat(n) (q.value(n).isNull() ? -1 : q.value(n).toDouble()) @@ -524,8 +525,8 @@ void Song::InitFromQuery(const SqlRow& q, int col) { d->samplerate_ = toint(col + 14); d->directory_id_ = toint(col + 15); - d->filename_ = tostr(col + 16); - d->basefilename_ = QFileInfo(d->filename_).fileName(); + d->url_ = QUrl::fromEncoded(tobytearray(col + 16)); + d->basefilename_ = QFileInfo(d->url_.toLocalFile()).fileName(); d->mtime_ = toint(col + 17); d->ctime_ = toint(col + 18); d->filesize_ = toint(col + 19); @@ -562,7 +563,7 @@ void Song::InitFromQuery(const SqlRow& q, int col) { } void Song::InitFromFilePartial(const QString& filename) { - d->filename_ = filename; + d->url_ = QUrl::fromLocalFile(filename); // We currently rely on filename suffix to know if it's a music file or not. // TODO: I know this is not satisfying, but currently, we rely on TagLib // which seems to have the behavior (filename checks). Someday, it would be @@ -594,7 +595,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) { #endif // HAVE_LIBLASTFM #ifdef HAVE_LIBGPOD - void Song::InitFromItdb(const Itdb_Track* track) { + void Song::InitFromItdb(const Itdb_Track* track, const QString& prefix) { d->valid_ = true; d->title_ = QString::fromUtf8(track->title); @@ -621,9 +622,11 @@ void Song::InitFromLastFM(const lastfm::Track& track) { d->skipcount_ = track->skipcount; d->lastplayed_ = track->time_played; - d->filename_ = QString::fromLocal8Bit(track->ipod_path); - d->filename_.replace(':', '/'); - d->basefilename_ = QFileInfo(d->filename_).fileName(); + QString filename = QString::fromLocal8Bit(track->ipod_path); + filename.replace(':', '/'); + + d->url_ = QUrl::fromLocalFile(prefix + filename); + d->basefilename_ = QFileInfo(filename).fileName(); } void Song::ToItdb(Itdb_Track *track) const { @@ -656,7 +659,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) { #endif #ifdef HAVE_LIBMTP - void Song::InitFromMTP(const LIBMTP_track_t* track) { + void Song::InitFromMTP(const LIBMTP_track_t* track, const QString& host) { d->valid_ = true; d->title_ = QString::fromUtf8(track->title); @@ -664,8 +667,8 @@ void Song::InitFromLastFM(const lastfm::Track& track) { d->album_ = QString::fromUtf8(track->album); d->composer_ = QString::fromUtf8(track->composer); d->genre_ = QString::fromUtf8(track->genre); - d->filename_ = QString::number(track->item_id); - d->basefilename_ = d->filename_; + d->url_ = QUrl(QString("mtp://%1/%2").arg(host, track->item_id)); + d->basefilename_ = QString::number(track->item_id); d->track_ = track->tracknumber; set_length_nanosec(track->duration * kNsecPerMsec); @@ -839,7 +842,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) { d->bitrate_ = item_value.toInt(); else if (wcscmp(name, g_wszWMDMFileName) == 0) - d->filename_ = item_value.toString(); + d->url_ = QUrl::fromLocalFile(item_value.toString()); else if (wcscmp(name, g_wszWMDMDuration) == 0) set_length_nanosec(item_value.toULongLong() * 1e2); @@ -913,7 +916,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) { // Make a final guess based on the file extension { - QString ext = d->filename_.section('.', -1, -1).toLower(); + QString ext = d->url_.path().section('.', -1, -1).toLower(); if (ext == "mp3" || ext == "wma" || ext == "flac" || ext == "ogg" || ext == "spx" || ext == "mp4" || ext == "aac" || ext == "m4a") break; @@ -1007,7 +1010,7 @@ void Song::BindToQuery(QSqlQuery *query) const { query->bindValue(":samplerate", intval(d->samplerate_)); query->bindValue(":directory", notnullintval(d->directory_id_)); - query->bindValue(":filename", d->filename_); + query->bindValue(":filename", d->url_.toEncoded()); query->bindValue(":mtime", notnullintval(d->mtime_)); query->bindValue(":ctime", notnullintval(d->ctime_)); query->bindValue(":filesize", notnullintval(d->filesize_)); @@ -1066,12 +1069,6 @@ void Song::ToLastFM(lastfm::Track* track) const { } #endif // HAVE_LIBLASTFM -QUrl Song::url() const { - return QFile::exists(filename()) - ? QUrl::fromLocalFile(filename()) - : filename(); -} - QString Song::PrettyTitle() const { QString title(d->title_); @@ -1164,16 +1161,17 @@ TagLib::String QStringToTaglibString(const QString& s) { } bool Song::IsEditable() const { - return d->valid_ && !d->filename_.isNull() && !is_stream() && + return d->valid_ && !d->url_.isEmpty() && !is_stream() && d->filetype_ != Type_Unknown && !has_cue(); } bool Song::Save() const { - if (d->filename_.isNull()) + const QString filename = d->url_.toLocalFile(); + if (filename.isNull()) return false; QMutexLocker l(&taglib_mutex_); - scoped_ptr fileref(factory_->GetFileRef(d->filename_)); + scoped_ptr fileref(factory_->GetFileRef(filename)); if (!fileref || fileref->isNull()) // The file probably doesn't exist return false; @@ -1214,7 +1212,7 @@ bool Song::Save() const { if (ret) { // Linux: inotify doesn't seem to notice the change to the file unless we // change the timestamps as well. (this is what touch does) - utimensat(0, QFile::encodeName(d->filename_).constData(), NULL, 0); + utimensat(0, QFile::encodeName(filename).constData(), NULL, 0); } #endif // Q_OS_LINUX @@ -1232,7 +1230,7 @@ QFuture Song::BackgroundSave() const { bool Song::operator==(const Song& other) const { // TODO: this isn't working for radios - return filename() == other.filename() && + return url() == other.url() && beginning_nanosec() == other.beginning_nanosec(); } diff --git a/src/core/song.h b/src/core/song.h index 98487e6c6..3d808e000 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -136,12 +137,12 @@ class Song { void MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle& bundle); #ifdef HAVE_LIBGPOD - void InitFromItdb(const Itdb_Track* track); + void InitFromItdb(const Itdb_Track* track, const QString& prefix); void ToItdb(Itdb_Track* track) const; #endif #ifdef HAVE_LIBMTP - void InitFromMTP(const LIBMTP_track_t* track); + void InitFromMTP(const LIBMTP_track_t* track, const QString& host); void ToMTP(LIBMTP_track_t* track) const; #endif @@ -197,10 +198,7 @@ class Song { int samplerate() const { return d->samplerate_; } int directory_id() const { return d->directory_id_; } - const QString& filename() const { return d->filename_; } - // Returns this Song's URL which may point either to a file or to another type - // of stream. - QUrl url() const; + const QUrl& url() const { return d->url_; } const QString& basefilename() const { return d->basefilename_; } uint mtime() const { return d->mtime_; } uint ctime() const { return d->ctime_; } @@ -277,7 +275,7 @@ class Song { void set_cue_path(const QString& v) { d->cue_path_ = v; } // Setters that should only be used by tests - void set_filename(const QString& v) { d->filename_ = v; } + void set_url(const QUrl& v) { d->url_ = v; } void set_basefilename(const QString& v) { d->basefilename_ = v; } void set_directory_id(int v) { d->directory_id_ = v; } @@ -342,7 +340,7 @@ class Song { int samplerate_; int directory_id_; - QString filename_; + QUrl url_; QString basefilename_; int mtime_; int ctime_; diff --git a/src/core/songloader.cpp b/src/core/songloader.cpp index 8c33771c6..7793ca9fd 100644 --- a/src/core/songloader.cpp +++ b/src/core/songloader.cpp @@ -193,7 +193,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename, bool block, void SongLoader::EffectiveSongsLoad() { for (int i = 0; i < songs_.size(); i++) { Song& song = songs_[i]; - QString filename = song.filename(); + QString filename = song.url().toLocalFile(); QFileInfo info(filename); LibraryQuery query; @@ -233,7 +233,7 @@ static bool CompareSongs(const Song& left, const Song& right) { if (left.disc() > right.disc()) return false; if (left.track() < right.track()) return true; if (left.track() > right.track()) return false; - return left.filename() < right.filename(); + return left.url() < right.url(); } void SongLoader::LoadLocalDirectoryAndEmit(const QString& filename) { @@ -256,7 +256,7 @@ void SongLoader::AddAsRawStream() { Song song; song.set_valid(true); song.set_filetype(Song::Type_Stream); - song.set_filename(url_.toString()); + song.set_url(url_); song.set_title(url_.toString()); songs_ << song; } diff --git a/src/covers/albumcoverloader.cpp b/src/covers/albumcoverloader.cpp index 4f8814979..381435bb9 100644 --- a/src/covers/albumcoverloader.cpp +++ b/src/covers/albumcoverloader.cpp @@ -233,5 +233,5 @@ void AlbumCoverLoader::SetDefaultOutputImage(const QImage &image) { quint64 AlbumCoverLoader::LoadImageAsync(const Song &song) { return LoadImageAsync(song.art_automatic(), song.art_manual(), - song.filename(), song.image()); + song.url().toLocalFile(), song.image()); } diff --git a/src/devices/afcdevice.cpp b/src/devices/afcdevice.cpp index d0a9bc667..9c4c6300a 100644 --- a/src/devices/afcdevice.cpp +++ b/src/devices/afcdevice.cpp @@ -159,7 +159,7 @@ void AfcDevice::FinaliseDatabase() { } bool AfcDevice::DeleteFromStorage(const DeleteJob& job) { - const QString path = QUrl(job.metadata_.filename()).path(); + const QString path = job.metadata_.url().toLocalFile(); if (!RemoveTrackFromITunesDb(path)) return false; diff --git a/src/devices/connecteddevice.cpp b/src/devices/connecteddevice.cpp index a90842784..59fc8f38b 100644 --- a/src/devices/connecteddevice.cpp +++ b/src/devices/connecteddevice.cpp @@ -79,7 +79,7 @@ void ConnectedDevice::InitBackendDirectory( if (dir.path != mount_point) { // The directory is different, commence the munging. qLog(Info) << "Changing path from" << dir.path << "to" << mount_point; - backend_->ChangeDirPath(dir.id, mount_point); + backend_->ChangeDirPath(dir.id, dir.path, mount_point); } } diff --git a/src/devices/deviceview.cpp b/src/devices/deviceview.cpp index f51177791..6603782e3 100644 --- a/src/devices/deviceview.cpp +++ b/src/devices/deviceview.cpp @@ -402,7 +402,7 @@ void DeviceView::Organise() { SongList songs = GetSelectedSongs(); QStringList filenames; foreach (const Song& song, songs) { - filenames << song.filename(); + filenames << song.url().toLocalFile(); } organise_dialog_->SetCopy(true); diff --git a/src/devices/gpoddevice.cpp b/src/devices/gpoddevice.cpp index b7f41c42f..a8e57d776 100644 --- a/src/devices/gpoddevice.cpp +++ b/src/devices/gpoddevice.cpp @@ -99,9 +99,8 @@ Itdb_Track* GPodDevice::AddTrackToITunesDb(const Song& metadata) { void GPodDevice::AddTrackToModel(Itdb_Track* track, const QString& prefix) { // Add it to our LibraryModel Song metadata_on_device; - metadata_on_device.InitFromItdb(track); + metadata_on_device.InitFromItdb(track, prefix); metadata_on_device.set_directory_id(1); - metadata_on_device.set_filename(prefix + metadata_on_device.filename()); songs_to_add_ << metadata_on_device; } @@ -209,11 +208,11 @@ bool GPodDevice::RemoveTrackFromITunesDb(const QString& path, const QString& rel bool GPodDevice::DeleteFromStorage(const DeleteJob& job) { Q_ASSERT(db_); - if (!RemoveTrackFromITunesDb(job.metadata_.filename(), url_.path())) + if (!RemoveTrackFromITunesDb(job.metadata_.url().toLocalFile(), url_.path())) return false; // Remove the file - if (!QFile::remove(job.metadata_.filename())) + if (!QFile::remove(job.metadata_.url().toLocalFile())) return false; // Remove it from our library model diff --git a/src/devices/gpodloader.cpp b/src/devices/gpodloader.cpp index 4db90beed..730cff5b8 100644 --- a/src/devices/gpodloader.cpp +++ b/src/devices/gpodloader.cpp @@ -66,20 +66,17 @@ void GPodLoader::LoadDatabase() { } // Convert all the tracks from libgpod structs into Song classes + const QString prefix = path_prefix_.isEmpty() + ? QDir::fromNativeSeparators(mount_point_) : path_prefix_; + SongList songs; for (GList* tracks = db->tracks ; tracks != NULL ; tracks = tracks->next) { Itdb_Track* track = static_cast(tracks->data); Song song; - song.InitFromItdb(track); + song.InitFromItdb(track, prefix); song.set_directory_id(1); - QString filename = (path_prefix_.isEmpty() ? mount_point_ : path_prefix_) + - song.filename(); - if (path_prefix_.isEmpty()) - filename = QDir::cleanPath(QDir::fromNativeSeparators(filename)); - song.set_filename(filename); - if (type_ != Song::Type_Unknown) song.set_filetype(type_); songs << song; diff --git a/src/devices/imobiledeviceconnection.cpp b/src/devices/imobiledeviceconnection.cpp index 8a5c221b9..d98c908ea 100644 --- a/src/devices/imobiledeviceconnection.cpp +++ b/src/devices/imobiledeviceconnection.cpp @@ -215,7 +215,7 @@ QString iMobileDeviceConnection::GetUnusedFilename( } // Use the same file extension as the original file, default to mp3. - QString extension = metadata.filename().section('.', -1, -1).toLower(); + QString extension = metadata.url().path().section('.', -1, -1).toLower(); if (extension.isEmpty()) extension = "mp3"; diff --git a/src/devices/mtpdevice.cpp b/src/devices/mtpdevice.cpp index 1a06f957b..571655920 100644 --- a/src/devices/mtpdevice.cpp +++ b/src/devices/mtpdevice.cpp @@ -112,9 +112,8 @@ bool MtpDevice::CopyToStorage(const CopyJob& job) { // Add it to our LibraryModel Song metadata_on_device; - metadata_on_device.InitFromMTP(&track); + metadata_on_device.InitFromMTP(&track, url_.host()); metadata_on_device.set_directory_id(1); - metadata_on_device.set_filename("mtp://" + url_.host() + "/" + metadata_on_device.filename()); songs_to_add_ << metadata_on_device; // Remove the original if requested @@ -150,8 +149,7 @@ void MtpDevice::StartDelete() { bool MtpDevice::DeleteFromStorage(const DeleteJob& job) { // Extract the ID from the song's URL - QUrl url(job.metadata_.filename()); - QString filename = url.path(); + QString filename = job.metadata_.url().path(); filename.remove('/'); bool ok = false; diff --git a/src/devices/mtploader.cpp b/src/devices/mtploader.cpp index 07a467ff7..17015f18a 100644 --- a/src/devices/mtploader.cpp +++ b/src/devices/mtploader.cpp @@ -64,9 +64,8 @@ bool MtpLoader::TryLoad() { LIBMTP_track_t* track = tracks; Song song; - song.InitFromMTP(track); + song.InitFromMTP(track, url_.host()); song.set_directory_id(1); - song.set_filename("mtp://" + url_.host() + "/" + song.filename()); songs << song; tracks = tracks->next; diff --git a/src/library/librarybackend.cpp b/src/library/librarybackend.cpp index 8c80b06ef..7439769e7 100644 --- a/src/library/librarybackend.cpp +++ b/src/library/librarybackend.cpp @@ -89,7 +89,8 @@ void LibraryBackend::LoadDirectories() { } } -void LibraryBackend::ChangeDirPath(int id, const QString &new_path) { +void LibraryBackend::ChangeDirPath(int id, const QString& old_path, + const QString& new_path) { QMutexLocker l(db_->Mutex()); QSqlDatabase db(db_->Connect()); ScopedTransaction t(&db); @@ -101,12 +102,15 @@ void LibraryBackend::ChangeDirPath(int id, const QString &new_path) { q.exec(); if (db_->CheckErrors(q)) return; - const int path_len = new_path.length(); + const QByteArray old_url = QUrl::fromLocalFile(old_path).toEncoded(); + const QByteArray new_url = QUrl::fromLocalFile(new_path).toEncoded(); + + const int path_len = old_url.length(); // Do the subdirs table q = QSqlQuery(QString("UPDATE %1 SET path=:path || substr(path, %2)" " WHERE directory=:id").arg(subdirs_table_).arg(path_len), db); - q.bindValue(":path", new_path); + q.bindValue(":path", new_url); q.bindValue(":id", id); q.exec(); if (db_->CheckErrors(q)) return; @@ -114,7 +118,7 @@ void LibraryBackend::ChangeDirPath(int id, const QString &new_path) { // Do the songs table q = QSqlQuery(QString("UPDATE %1 SET filename=:path || substr(filename, %2)" " WHERE directory=:id").arg(songs_table_).arg(path_len), db); - q.bindValue(":path", new_path); + q.bindValue(":path", new_url); q.bindValue(":id", id); q.exec(); if (db_->CheckErrors(q)) return; @@ -564,10 +568,10 @@ SongList LibraryBackend::GetSongsById(const QStringList& ids, QSqlDatabase& db) return ret; } -Song LibraryBackend::GetSongByFilename(const QString& filename, qint64 beginning) { +Song LibraryBackend::GetSongByUrl(const QUrl& url, qint64 beginning) { LibraryQuery query; query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec); - query.AddWhere("filename", filename); + query.AddWhere("filename", url.toEncoded()); query.AddWhere("beginning", beginning); Song song; @@ -577,10 +581,10 @@ Song LibraryBackend::GetSongByFilename(const QString& filename, qint64 beginning return song; } -SongList LibraryBackend::GetSongsByFilename(const QString& filename) { +SongList LibraryBackend::GetSongsByUrl(const QUrl& url) { LibraryQuery query; query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec); - query.AddWhere("filename", filename); + query.AddWhere("filename", url.toEncoded()); SongList songlist; if (ExecQuery(&query)) { @@ -754,7 +758,7 @@ LibraryBackend::AlbumList LibraryBackend::GetAlbums(const QString& artist, info.album_name = query.Value(0).toString(); info.art_automatic = query.Value(4).toString(); info.art_manual = query.Value(5).toString(); - info.first_filename = query.Value(6).toString(); + info.first_url = QUrl::fromEncoded(query.Value(6).toByteArray()); if (info.artist == last_artist && info.album_name == last_album) continue; @@ -784,7 +788,7 @@ LibraryBackend::Album LibraryBackend::GetAlbumArt(const QString& artist, const Q if (query.Next()) { ret.art_automatic = query.Value(0).toString(); ret.art_manual = query.Value(1).toString(); - ret.first_filename = query.Value(2).toString(); + ret.first_url = QUrl::fromEncoded(query.Value(2).toByteArray()); } return ret; diff --git a/src/library/librarybackend.h b/src/library/librarybackend.h index 52c33cec0..90a562dd5 100644 --- a/src/library/librarybackend.h +++ b/src/library/librarybackend.h @@ -40,17 +40,17 @@ class LibraryBackendInterface : public QObject { Album() {} Album(const QString& _artist, const QString& _album_name, const QString& _art_automatic, const QString& _art_manual, - const QString& _first_filename) + const QUrl& _first_url) : artist(_artist), album_name(_album_name), art_automatic(_art_automatic), art_manual(_art_manual), - first_filename(_first_filename) {} + first_url(_first_url) {} QString artist; QString album_name; QString art_automatic; QString art_manual; - QString first_filename; + QUrl first_url; }; typedef QList AlbumList; @@ -63,7 +63,7 @@ class LibraryBackendInterface : public QObject { virtual SongList FindSongsInDirectory(int id) = 0; virtual SubdirectoryList SubdirsInDirectory(int id) = 0; virtual DirectoryList GetAllDirectories() = 0; - virtual void ChangeDirPath(int id, const QString& new_path) = 0; + virtual void ChangeDirPath(int id, const QString& old_path, const QString& new_path) = 0; virtual QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()) = 0; virtual QStringList GetAllArtistsWithAlbums(const QueryOptions& opt = QueryOptions()) = 0; @@ -84,11 +84,11 @@ class LibraryBackendInterface : public QObject { // Returns all sections of a song with the given filename. If there's just one section // the resulting list will have it's size equal to 1. - virtual SongList GetSongsByFilename(const QString& filename) = 0; + virtual SongList GetSongsByUrl(const QUrl& url) = 0; // Returns a section of a song with the given filename and beginning. If the section // is not present in library, returns invalid song. // Using default beginning value is suitable when searching for single-section songs. - virtual Song GetSongByFilename(const QString& filename, qint64 beginning = 0) = 0; + virtual Song GetSongByUrl(const QUrl& url, qint64 beginning = 0) = 0; virtual void AddDirectory(const QString& path) = 0; virtual void RemoveDirectory(const Directory& dir) = 0; @@ -120,7 +120,7 @@ class LibraryBackend : public LibraryBackendInterface { SongList FindSongsInDirectory(int id); SubdirectoryList SubdirsInDirectory(int id); DirectoryList GetAllDirectories(); - void ChangeDirPath(int id, const QString& new_path); + void ChangeDirPath(int id, const QString& old_path, const QString& new_path); QStringList GetAll(const QString& column, const QueryOptions& opt = QueryOptions()); QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()); @@ -143,8 +143,8 @@ class LibraryBackend : public LibraryBackendInterface { SongList GetSongsByForeignId(const QStringList& ids, const QString& table, const QString& column); - SongList GetSongsByFilename(const QString& filename); - Song GetSongByFilename(const QString& filename, qint64 beginning = 0); + SongList GetSongsByUrl(const QUrl& url); + Song GetSongByUrl(const QUrl& url, qint64 beginning = 0); void AddDirectory(const QString& path); void RemoveDirectory(const Directory& dir); diff --git a/src/library/librarymodel.cpp b/src/library/librarymodel.cpp index ba87f6d08..aebdaf809 100644 --- a/src/library/librarymodel.cpp +++ b/src/library/librarymodel.cpp @@ -392,7 +392,7 @@ QVariant LibraryModel::AlbumIcon(const QModelIndex& index, int role) const { if (!songs.isEmpty()) { const Song& s = songs.first(); QPixmap pixmap = AlbumCoverLoader::TryLoadPixmap( - s.art_automatic(), s.art_manual(), s.filename()); + s.art_automatic(), s.art_manual(), s.url().toLocalFile()); if (!pixmap.isNull()) { QImage image = pixmap.toImage().scaled( @@ -931,7 +931,7 @@ QString LibraryModel::SortTextForYear(int year) const { QString LibraryModel::SortTextForSong(const Song& song) const { QString ret = QString::number(qMax(0, song.disc()) * 1000 + qMax(0, song.track())); ret.prepend(QString("0").repeated(6 - ret.length())); - ret.append(song.filename()); + ret.append(song.url().toString()); return ret; } @@ -1012,7 +1012,7 @@ void LibraryModel::GetChildSongs(LibraryItem* item, QList* urls, } case LibraryItem::Type_Song: - urls->append(QUrl::fromLocalFile(item->metadata.filename())); + urls->append(item->metadata.url()); if (!song_ids->contains(item->metadata.id())) { songs->append(item->metadata); song_ids->insert(item->metadata.id()); diff --git a/src/library/libraryplaylistitem.cpp b/src/library/libraryplaylistitem.cpp index e166114a4..d478d229e 100644 --- a/src/library/libraryplaylistitem.cpp +++ b/src/library/libraryplaylistitem.cpp @@ -32,11 +32,11 @@ LibraryPlaylistItem::LibraryPlaylistItem(const Song& song) QUrl LibraryPlaylistItem::Url() const { - return QUrl::fromLocalFile(song_.filename()); + return song_.url(); } void LibraryPlaylistItem::Reload() { - song_.InitFromFile(song_.filename(), song_.directory_id()); + song_.InitFromFile(song_.url().toLocalFile(), song_.directory_id()); } bool LibraryPlaylistItem::InitFromQuery(const SqlRow& query) { diff --git a/src/library/libraryview.cpp b/src/library/libraryview.cpp index 80fdf6169..c395873cc 100644 --- a/src/library/libraryview.cpp +++ b/src/library/libraryview.cpp @@ -550,7 +550,7 @@ void LibraryView::EditSmartPlaylistFinished() { void LibraryView::ShowInBrowser() { QStringList filenames; foreach (const Song& song, GetSelectedSongs()) { - filenames << song.filename(); + filenames << song.url().toLocalFile(); } Utilities::OpenInFileBrowser(filenames); diff --git a/src/library/librarywatcher.cpp b/src/library/librarywatcher.cpp index 0ac6c0c57..4bd505df9 100644 --- a/src/library/librarywatcher.cpp +++ b/src/library/librarywatcher.cpp @@ -132,7 +132,7 @@ SongList LibraryWatcher::ScanTransaction::FindSongsInSubdirectory(const QString // TODO: Make this faster SongList ret; foreach (const Song& song, cached_songs_) { - if (song.filename().section('/', 0, -2) == path) + if (song.url().toLocalFile().section('/', 0, -2) == path) ret << song; } return ret; @@ -363,8 +363,8 @@ void LibraryWatcher::ScanSubdirectory( // Look for deleted songs foreach (const Song& song, songs_in_db) { - if (!files_on_disk.contains(song.filename())) { - qLog(Debug) << "Song deleted from disk:" << song.filename(); + if (!files_on_disk.contains(song.url().toLocalFile())) { + qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile(); t->deleted_songs << song; } } @@ -397,7 +397,7 @@ void LibraryWatcher::UpdateCueAssociatedSongs(const QString& file, const QString QFile cue(matching_cue); cue.open(QIODevice::ReadOnly); - SongList old_sections = backend_->GetSongsByFilename(file); + SongList old_sections = backend_->GetSongsByUrl(QUrl::fromLocalFile(file)); QHash sections_map; foreach(const Song& song, old_sections) { @@ -437,7 +437,7 @@ void LibraryWatcher::UpdateNonCueAssociatedSong(const QString& file, const Song& // 'raw' (cueless) song and we just remove the rest of the sections // from the library if(cue_deleted) { - foreach(const Song& song, backend_->GetSongsByFilename(file)) { + foreach(const Song& song, backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) { if(!song.IsMetadataEqual(matching_song)) { t->deleted_songs << song; } @@ -470,7 +470,7 @@ SongList LibraryWatcher::ScanNewFile(const QString& file, const QString& path, // media files. Playlist parser for CUEs considers every entry in sheet // valid and we don't want invalid media getting into library! foreach(const Song& cue_song, cue_parser_->Load(&cue, matching_cue, path)) { - if(cue_song.filename() == file && cue_song.HasProperMediaFile()) { + if(cue_song.url().toLocalFile() == file && cue_song.HasProperMediaFile()) { song_list << cue_song; } } @@ -553,7 +553,7 @@ void LibraryWatcher::RemoveDirectory(const Directory& dir) { bool LibraryWatcher::FindSongByPath(const SongList& list, const QString& path, Song* out) { // TODO: Make this faster foreach (const Song& song, list) { - if (song.filename() == path) { + if (song.url().toLocalFile() == path) { *out = song; return true; } diff --git a/src/musicbrainz/tagfetcher.cpp b/src/musicbrainz/tagfetcher.cpp index dd50b2fc2..ece742c3b 100644 --- a/src/musicbrainz/tagfetcher.cpp +++ b/src/musicbrainz/tagfetcher.cpp @@ -35,7 +35,7 @@ TagFetcher::TagFetcher(QObject* parent) } QString TagFetcher::GetFingerprint(const Song& song) { - return Fingerprinter(song.filename()).CreateFingerprint(); + return Fingerprinter(song.url().toLocalFile()).CreateFingerprint(); } void TagFetcher::StartFetch(const SongList& songs) { diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 67b762ad5..69dd79eae 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -274,7 +274,7 @@ QVariant Playlist::data(const QModelIndex& index, int role) const { case Column_BPM: return song.bpm(); case Column_Bitrate: return song.bitrate(); case Column_Samplerate: return song.samplerate(); - case Column_Filename: return song.filename(); + case Column_Filename: return song.url(); case Column_BaseFilename: return song.basefilename(); case Column_Filesize: return song.filesize(); case Column_Filetype: return song.filetype(); @@ -958,7 +958,7 @@ void Playlist::UpdateItems(const SongList& songs) { // Update current items list for (int i=0; iMetadata().filename() == song.filename()) { + if (item->Metadata().url() == song.url()) { PlaylistItemPtr new_item; if (song.id() == -1) { new_item = PlaylistItemPtr(new SongPlaylistItem(song)); @@ -1041,7 +1041,7 @@ bool Playlist::CompareItems(int column, Qt::SortOrder order, case Column_BPM: cmp(bpm); case Column_Bitrate: cmp(bitrate); case Column_Samplerate: cmp(samplerate); - case Column_Filename: cmp(filename); + case Column_Filename: cmp(url); case Column_BaseFilename: cmp(basefilename); case Column_Filesize: cmp(filesize); case Column_Filetype: cmp(filetype); @@ -1186,7 +1186,7 @@ void Playlist::ItemsLoaded() { while (it.hasNext()) { PlaylistItemPtr item = it.next(); - if (item->IsLocalLibraryItem() && item->Metadata().filename().isEmpty()) { + if (item->IsLocalLibraryItem() && item->Metadata().url().isEmpty()) { it.remove(); } } @@ -1684,7 +1684,7 @@ void Playlist::InvalidateDeletedSongs() { Song song = item->Metadata(); if(!song.is_stream()) { - bool exists = QFile::exists(song.filename()); + bool exists = QFile::exists(song.url().toLocalFile()); if(!exists && !item->HasForegroundColor(kInvalidSongPriority)) { // gray out the song if it's not there @@ -1707,7 +1707,7 @@ void Playlist::RemoveDeletedSongs() { PlaylistItemPtr item = items_[row]; Song song = item->Metadata(); - if(!song.is_stream() && !QFile::exists(song.filename())) { + if(!song.is_stream() && !QFile::exists(song.url().toLocalFile())) { rows_to_remove.append(row); } } @@ -1723,8 +1723,8 @@ bool Playlist::ApplyValidityOnCurrentSong(const QUrl& url, bool valid) { // if validity has changed, reload the item if(!current_song.is_stream() && - current_song.filename() == url.toLocalFile() && - current_song.is_valid() != QFile::exists(current_song.filename())) { + current_song.url() == url && + current_song.is_valid() != QFile::exists(current_song.url().toLocalFile())) { ReloadItems(QList() << current_row()); } diff --git a/src/playlist/playlistbackend.cpp b/src/playlist/playlistbackend.cpp index cfc0082c5..1da313dda 100644 --- a/src/playlist/playlistbackend.cpp +++ b/src/playlist/playlistbackend.cpp @@ -192,7 +192,7 @@ PlaylistItemPtr PlaylistBackend::RestoreCueData(PlaylistItemPtr item, boost::sha } foreach(const Song& from_list, song_list) { - if(from_list.filename() == song.filename() && + if(from_list.url() == song.url() && from_list.beginning_nanosec() == song.beginning_nanosec()) { // we found a matching section; replace the input // item with a new one containing CUE metadata diff --git a/src/playlist/playlistundocommands.cpp b/src/playlist/playlistundocommands.cpp index f0efc11f9..828b5ad59 100644 --- a/src/playlist/playlistundocommands.cpp +++ b/src/playlist/playlistundocommands.cpp @@ -50,7 +50,7 @@ void InsertItems::undo() { bool InsertItems::UpdateItem(const PlaylistItemPtr& updated_item) { for (int i=0; iMetadata().filename() == updated_item->Metadata().filename()) { + if (item->Metadata().url() == updated_item->Metadata().url()) { items_[i] = updated_item; return true; } diff --git a/src/playlist/songplaylistitem.cpp b/src/playlist/songplaylistitem.cpp index e230b4642..f3c446f51 100644 --- a/src/playlist/songplaylistitem.cpp +++ b/src/playlist/songplaylistitem.cpp @@ -52,7 +52,7 @@ QUrl SongPlaylistItem::Url() const { } void SongPlaylistItem::Reload() { - QString old_filename = song_.filename(); + QString old_filename = song_.url().toLocalFile(); int old_directory_id = song_.directory_id(); song_ = Song(); diff --git a/src/playlistparsers/asxiniparser.cpp b/src/playlistparsers/asxiniparser.cpp index bbf93ed1a..79e5a155c 100644 --- a/src/playlistparsers/asxiniparser.cpp +++ b/src/playlistparsers/asxiniparser.cpp @@ -40,16 +40,8 @@ SongList AsxIniParser::Load(QIODevice *device, const QString& playlist_path, con QString value = line.mid(equals + 1); if (key.startsWith("ref")) { - Song song; - if (!ParseTrackLocation(value, dir, &song)) - qLog(Warning) << "Failed to parse location: " << value; - - // Load the song from the library if it's there. - Song library_song = LoadLibrarySong(song.filename()); - if (library_song.is_valid()) { - ret << library_song; - } else { - song.InitFromFile(song.filename(), -1); + Song song = LoadSong(value, 0, dir); + if (song.is_valid()) { ret << song; } } @@ -64,7 +56,7 @@ void AsxIniParser::Save(const SongList &songs, QIODevice *device, const QDir &di int n = 1; foreach (const Song& song, songs) { - s << "Ref" << n << "=" << MakeRelativeTo(song.filename(), dir) << endl; + s << "Ref" << n << "=" << URLOrRelativeFilename(song.url(), dir) << endl; ++n; } } diff --git a/src/playlistparsers/asxparser.cpp b/src/playlistparsers/asxparser.cpp index fad080324..eb819cdcd 100644 --- a/src/playlistparsers/asxparser.cpp +++ b/src/playlistparsers/asxparser.cpp @@ -31,7 +31,8 @@ ASXParser::ASXParser(LibraryBackendInterface* library, QObject* parent) { } -SongList ASXParser::Load(QIODevice *device, const QString& playlist_path, const QDir&) const { +SongList ASXParser::Load(QIODevice *device, const QString& playlist_path, + const QDir& dir) const { // We have to load everything first so we can munge the "XML". QByteArray data = device->readAll(); @@ -70,7 +71,7 @@ SongList ASXParser::Load(QIODevice *device, const QString& playlist_path, const } while (!reader.atEnd() && ParseUntilElement(&reader, "entry")) { - Song song = ParseTrack(&reader); + Song song = ParseTrack(&reader, dir); if (song.is_valid()) { ret << song; } @@ -79,9 +80,9 @@ SongList ASXParser::Load(QIODevice *device, const QString& playlist_path, const } -Song ASXParser::ParseTrack(QXmlStreamReader* reader) const { - Song song; - QString title, artist, album; +Song ASXParser::ParseTrack(QXmlStreamReader* reader, const QDir& dir) const { + QString title, artist, album, ref; + while (!reader->atEnd()) { QXmlStreamReader::TokenType type = reader->readNext(); @@ -89,24 +90,7 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const { case QXmlStreamReader::StartElement: { QStringRef name = reader->name(); if (name == "ref") { - QUrl url(reader->attributes().value("href").toString()); - if (url.scheme() == "file") { - QString filename = url.toLocalFile(); - if (!QFile::exists(filename)) { - return Song(); - } - - // Load the song from the library if it's there. - Song library_song = LoadLibrarySong(filename); - if (library_song.is_valid()) - return library_song; - - song.InitFromFile(filename, -1); - return song; - } else { - song.set_filename(url.toString()); - song.set_filetype(Song::Type_Stream); - } + ref = reader->attributes().value("href").toString(); } else if (name == "title") { title = reader->readElementText(); } else if (name == "author") { @@ -116,8 +100,7 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const { } case QXmlStreamReader::EndElement: { if (reader->name() == "entry") { - song.Init(title, artist, album, -1); - return song; + goto return_song; } break; } @@ -125,8 +108,14 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const { break; } } - // At least make an effort if we never find a . - song.Init(title, artist, album, -1); + +return_song: + Song song = LoadSong(ref, 0, dir); + + // Override metadata with what was in the playlist + song.set_title(title); + song.set_artist(artist); + song.set_album(album); return song; } @@ -142,7 +131,7 @@ void ASXParser::Save(const SongList& songs, QIODevice* device, const QDir&) cons writer.writeTextElement("title", song.title()); { StreamElement ref("ref", &writer); - writer.writeAttribute("href", MakeUrl(song.filename())); + writer.writeAttribute("href", song.url().toString()); } if (!song.artist().isEmpty()) { writer.writeTextElement("author", song.artist()); diff --git a/src/playlistparsers/asxparser.h b/src/playlistparsers/asxparser.h index a151c4833..0d295dfa7 100644 --- a/src/playlistparsers/asxparser.h +++ b/src/playlistparsers/asxparser.h @@ -31,11 +31,12 @@ class ASXParser : public XMLParser { bool TryMagic(const QByteArray &data) const; - SongList Load(QIODevice *device, const QString& playlist_path = "", const QDir &dir = QDir()) const; + SongList Load(QIODevice *device, const QString& playlist_path = "", + const QDir& dir = QDir()) const; void Save(const SongList &songs, QIODevice *device, const QDir &dir = QDir()) const; private: - Song ParseTrack(QXmlStreamReader* reader) const; + Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const; }; #endif diff --git a/src/playlistparsers/cueparser.cpp b/src/playlistparsers/cueparser.cpp index 7acd72d1d..91c21e297 100644 --- a/src/playlistparsers/cueparser.cpp +++ b/src/playlistparsers/cueparser.cpp @@ -207,46 +207,37 @@ SongList CueParser::Load(QIODevice* device, const QString& playlist_path, const for(int i = 0; i < entries.length(); i++) { CueEntry entry = entries.at(i); - Song current; - if (!ParseTrackLocation(entry.file, dir, ¤t)) { - qLog(Warning) << "failed to parse location in .cue file from " << dir_path; - } else { - // look for the section in library - Song song = LoadLibrarySong(current.filename(), IndexToMarker(entry.index)); - if (!song.is_valid()) { - song.InitFromFile(current.filename(), -1); - } + Song song = LoadSong(entry.file, IndexToMarker(entry.index), dir); - // cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime) - if(cue_mtime.isValid()) { - song.set_mtime(qMax(cue_mtime.toTime_t(), song.mtime())); - } - song.set_cue_path(playlist_path); - - // overwrite the stuff, we may have read from the file or library, using - // the current .cue metadata - - // set track number only in single-file mode - if(files == 1) { - song.set_track(i + 1); - } - - // the last TRACK for every FILE gets it's 'end' marker from the media file's - // length - if(i + 1 < entries.size() && entries.at(i).file == entries.at(i + 1).file) { - // incorrect indices? - if(!UpdateSong(entry, entries.at(i + 1).index, &song)) { - continue; - } - } else { - // incorrect index? - if(!UpdateLastSong(entry, &song)) { - continue; - } - } - - ret << song; + // cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime) + if(cue_mtime.isValid()) { + song.set_mtime(qMax(cue_mtime.toTime_t(), song.mtime())); } + song.set_cue_path(playlist_path); + + // overwrite the stuff, we may have read from the file or library, using + // the current .cue metadata + + // set track number only in single-file mode + if(files == 1) { + song.set_track(i + 1); + } + + // the last TRACK for every FILE gets it's 'end' marker from the media file's + // length + if(i + 1 < entries.size() && entries.at(i).file == entries.at(i + 1).file) { + // incorrect indices? + if(!UpdateSong(entry, entries.at(i + 1).index, &song)) { + continue; + } + } else { + // incorrect index? + if(!UpdateLastSong(entry, &song)) { + continue; + } + } + + ret << song; } return ret; diff --git a/src/playlistparsers/m3uparser.cpp b/src/playlistparsers/m3uparser.cpp index 859a98f59..47cc9beb7 100644 --- a/src/playlistparsers/m3uparser.cpp +++ b/src/playlistparsers/m3uparser.cpp @@ -56,29 +56,13 @@ SongList M3UParser::Load(QIODevice* device, const QString& playlist_path, const } } } else if (!line.isEmpty()) { - Song song; + Song song = LoadSong(line, 0, dir); + song.set_title(current_metadata.title); + song.set_artist(current_metadata.artist); + song.set_length_nanosec(current_metadata.length); + ret << song; - // Track location. - if (!ParseTrackLocation(line, dir, &song)) { - qLog(Warning) << "Failed to parse location: " << line; - } else { - // Load the song from the library if it's there. - Song library_song = LoadLibrarySong(song.filename()); - if (library_song.is_valid()) { - ret << library_song; - } else { - song.Init(current_metadata.title, - current_metadata.artist, - QString(), // Unknown album. - current_metadata.length); - song.InitFromFile(song.filename(), -1); - ret << song; - } - - current_metadata.artist.clear(); - current_metadata.title.clear(); - current_metadata.length = -1; - } + current_metadata = Metadata(); } if (buffer.atEnd()) { break; @@ -115,14 +99,14 @@ bool M3UParser::ParseMetadata(const QString& line, M3UParser::Metadata* metadata void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const { device->write("#EXTM3U\n"); foreach (const Song& song, songs) { - if (song.filename().isEmpty()) { + if (song.url().isEmpty()) { continue; } QString meta = QString("#EXTINF:%1,%2 - %3\n") .arg(song.length_nanosec() / kNsecPerSec) .arg(song.artist()).arg(song.title()); device->write(meta.toUtf8()); - device->write(MakeRelativeTo(song.filename(), dir).toUtf8()); + device->write(URLOrRelativeFilename(song.url(), dir).toUtf8()); device->write("\n"); } } diff --git a/src/playlistparsers/parserbase.cpp b/src/playlistparsers/parserbase.cpp index b5671e564..613e6be57 100644 --- a/src/playlistparsers/parserbase.cpp +++ b/src/playlistparsers/parserbase.cpp @@ -28,67 +28,72 @@ ParserBase::ParserBase(LibraryBackendInterface* library, QObject *parent) { } -bool ParserBase::ParseTrackLocation(const QString& filename_or_url, - const QDir& dir, Song* song) const { +void ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning, + const QDir& dir, Song* song) const { + if (filename_or_url.isEmpty()) { + return; + } + + QString filename = filename_or_url; + if (filename_or_url.contains(QRegExp("^[a-z]+://"))) { - // Looks like a url. - QUrl temp(filename_or_url); - if (temp.isValid()) { - song->set_filename(temp.toString()); + QUrl url(filename_or_url); + if (url.scheme() == "file") { + filename = url.toLocalFile(); + } else { + song->set_url(QUrl(filename_or_url)); song->set_filetype(Song::Type_Stream); song->set_valid(true); - return true; - } else { - return false; + return; } } - // Should be a local path. - if (QDir::isAbsolutePath(filename_or_url)) { - // Absolute path. - // Fix windows \, eg. C:\foo -> C:/foo. - song->set_filename(QDir::fromNativeSeparators(filename_or_url)); - } else { - // Relative path. - QString proper_path = QDir::fromNativeSeparators(filename_or_url); - QString absolute_path = dir.absoluteFilePath(proper_path); - song->set_filename(absolute_path); + // Convert native separators for Windows paths + filename = QDir::fromNativeSeparators(filename); + + // Make the path absolute + if (!QDir::isAbsolutePath(filename)) { + filename = dir.absoluteFilePath(filename); + } + + // Use the canonical path + if (QFile::exists(filename)) { + filename = QFileInfo(filename).canonicalFilePath(); + } + + const QUrl url = QUrl::fromLocalFile(filename); + + // Search in the library + Song library_song; + if (library_) { + library_song = library_->GetSongByUrl(url, beginning); + } + + // If it was found in the library then use it, otherwise load metadata from + // disk. + if (library_song.is_valid()) { + *song = library_song; + } else { + song->InitFromFile(filename, -1); } - return true; } -QString ParserBase::MakeRelativeTo(const QString& filename_or_url, - const QDir& dir) const { - if (filename_or_url.contains(QRegExp("^[a-z]+://"))) - return filename_or_url; +Song ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir) const { + Song song; + LoadSong(filename_or_url, beginning, dir, &song); + return song; +} - if (QDir::isAbsolutePath(filename_or_url)) { - QString relative = dir.relativeFilePath(filename_or_url); +QString ParserBase::URLOrRelativeFilename(const QUrl& url, const QDir& dir) const { + if (url.scheme() != "file") + return url.toString(); + + const QString filename = url.toLocalFile(); + if (QDir::isAbsolutePath(filename)) { + const QString relative = dir.relativeFilePath(filename); if (!relative.contains("..")) return relative; } - return filename_or_url; -} - -QString ParserBase::MakeUrl(const QString& filename_or_url) const { - if (filename_or_url.contains(QRegExp("^[a-z]+://"))) { - return filename_or_url; - } - - return QUrl::fromLocalFile(filename_or_url).toString(); -} - -Song ParserBase::LoadLibrarySong(const QString& filename_or_url, qint64 beginning) const { - if (!library_) - return Song(); - - QFileInfo info; - - if (filename_or_url.contains("://")) - info.setFile(QUrl(filename_or_url).path()); - else - info.setFile(filename_or_url); - - return library_->GetSongByFilename(info.canonicalFilePath(), beginning); + return filename; } diff --git a/src/playlistparsers/parserbase.h b/src/playlistparsers/parserbase.h index 8c6f67443..793a42951 100644 --- a/src/playlistparsers/parserbase.h +++ b/src/playlistparsers/parserbase.h @@ -48,22 +48,19 @@ public: virtual void Save(const SongList& songs, QIODevice* device, const QDir& dir = QDir()) const = 0; protected: - // Takes a URL, relative path or absolute path, and returns an absolute path. - // Resolves relative paths to "dir". - bool ParseTrackLocation(const QString& filename_or_url, const QDir& dir, - Song* song) const; + // Loads a song. If filename_or_url is a URL (with a scheme other than + // "file") then it is set on the song and the song marked as a stream. + // If it is a filename or a file:// URL then it is made absolute and canonical + // and set as a file:// url on the song. Also sets the song's metadata by + // searching in the Library, or loading from the file as a fallback. + // This function should always be used when loading a playlist. + Song LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir) const; + void LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir, Song* song) const; - // Takes a URL, relative path or absolute path, and in the case of absolute - // paths makes them relative to dir if they are subdirectories. - QString MakeRelativeTo(const QString& filename_or_url, const QDir& dir) const; - - // Takes a URL or absolute path and returns a URL - QString MakeUrl(const QString& filename_or_url) const; - - // Converts the URL or path to a canonical path and searches the library for - // a section of a song with that path and the given beginning. If one is found, - // returns it, otherwise returns an invalid song. - Song LoadLibrarySong(const QString& filename_or_url, qint64 beginning = 0) const; + // If the URL is a file:// URL then returns its path relative to the + // directory. Otherwise returns the URL as is. + // This function should always be used when saving a playlist. + QString URLOrRelativeFilename(const QUrl& url, const QDir& dir) const; private: LibraryBackendInterface* library_; diff --git a/src/playlistparsers/plsparser.cpp b/src/playlistparsers/plsparser.cpp index 1685a7e1a..ef94ef38e 100644 --- a/src/playlistparsers/plsparser.cpp +++ b/src/playlistparsers/plsparser.cpp @@ -40,16 +40,15 @@ SongList PLSParser::Load(QIODevice *device, const QString& playlist_path, const int n = n_re.cap(0).toInt(); if (key.startsWith("file")) { - if (!ParseTrackLocation(value, dir, &songs[n])) - qLog(Warning) << "Failed to parse location: " << value; + Song song = LoadSong(value, 0, dir); - // Load the song from the library if it's there. - Song library_song = LoadLibrarySong(songs[n].filename()); - if (library_song.is_valid()) { - songs[n] = library_song; - } else { - songs[n].InitFromFile(songs[n].filename(), -1); - } + // Use the title and length we've already loaded if any + if (!songs[n].title().isEmpty()) + song.set_title(songs[n].title()); + if (!songs[n].length_nanosec() != -1) + song.set_length_nanosec(songs[n].length_nanosec()); + + songs[n] = song; } else if (key.startsWith("title")) { songs[n].set_title(value); } else if (key.startsWith("length")) { @@ -71,7 +70,7 @@ void PLSParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) int n = 1; foreach (const Song& song, songs) { - s << "File" << n << "=" << MakeRelativeTo(song.filename(), dir) << endl; + s << "File" << n << "=" << URLOrRelativeFilename(song.url(), dir) << endl; s << "Title" << n << "=" << song.title() << endl; s << "Length" << n << "=" << song.length_nanosec() / kNsecPerSec << endl; ++n; diff --git a/src/playlistparsers/xspfparser.cpp b/src/playlistparsers/xspfparser.cpp index 9f9f3257e..468fff829 100644 --- a/src/playlistparsers/xspfparser.cpp +++ b/src/playlistparsers/xspfparser.cpp @@ -29,7 +29,8 @@ XSPFParser::XSPFParser(LibraryBackendInterface* library, QObject* parent) { } -SongList XSPFParser::Load(QIODevice *device, const QString& playlist_path, const QDir&) const { +SongList XSPFParser::Load(QIODevice *device, const QString& playlist_path, + const QDir& dir) const { SongList ret; QXmlStreamReader reader(device); @@ -39,7 +40,7 @@ SongList XSPFParser::Load(QIODevice *device, const QString& playlist_path, const } while (!reader.atEnd() && ParseUntilElement(&reader, "track")) { - Song song = ParseTrack(&reader); + Song song = ParseTrack(&reader, dir); if (song.is_valid()) { ret << song; } @@ -47,34 +48,17 @@ SongList XSPFParser::Load(QIODevice *device, const QString& playlist_path, const return ret; } -Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const { - Song song; - QString title, artist, album; +Song XSPFParser::ParseTrack(QXmlStreamReader* reader, const QDir& dir) const { + QString title, artist, album, location; qint64 nanosec = -1; + while (!reader->atEnd()) { QXmlStreamReader::TokenType type = reader->readNext(); switch (type) { case QXmlStreamReader::StartElement: { QStringRef name = reader->name(); if (name == "location") { - QUrl url(reader->readElementText()); - if (url.scheme() == "file") { - QString filename = url.toLocalFile(); - if (!QFile::exists(filename)) { - return Song(); - } - - // Load the song from the library if it's there. - Song library_song = LoadLibrarySong(filename); - if (library_song.is_valid()) - return library_song; - - song.InitFromFile(filename, -1); - return song; - } else { - song.set_filename(url.toString()); - song.set_filetype(Song::Type_Stream); - } + location = reader->readElementText(); } else if (name == "title") { title = reader->readElementText(); } else if (name == "creator") { @@ -82,7 +66,7 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const { } else if (name == "album") { album = reader->readElementText(); } else if (name == "duration") { // in milliseconds. - const QString& duration = reader->readElementText(); + const QString duration = reader->readElementText(); bool ok = false; nanosec = duration.toInt(&ok) * kNsecPerMsec; if (!ok) { @@ -97,16 +81,22 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const { } case QXmlStreamReader::EndElement: { if (reader->name() == "track") { - song.Init(title, artist, album, nanosec); - return song; + goto return_song; } } default: break; } } - // At least make an effort if we never find a . - song.Init(title, artist, album, nanosec); + +return_song: + Song song = LoadSong(location, 0, dir); + + // Override metadata with what was in the playlist + song.set_title(title); + song.set_artist(artist); + song.set_album(album); + song.set_length_nanosec(nanosec); return song; } @@ -120,7 +110,7 @@ void XSPFParser::Save(const SongList& songs, QIODevice* device, const QDir&) con StreamElement tracklist("trackList", &writer); foreach (const Song& song, songs) { StreamElement track("track", &writer); - writer.writeTextElement("location", MakeUrl(song.filename())); + writer.writeTextElement("location", song.url().toString()); writer.writeTextElement("title", song.title()); if (!song.artist().isEmpty()) { writer.writeTextElement("creator", song.artist()); @@ -136,7 +126,9 @@ void XSPFParser::Save(const SongList& songs, QIODevice* device, const QDir&) con // Ignore images that are in our resource bundle. if (!art.startsWith(":") && !art.isEmpty()) { // Convert local files to URLs. - art = MakeUrl(art); + if (!art.contains("://")) { + art = QUrl::fromLocalFile(art).toString(); + } writer.writeTextElement("image", art); } } diff --git a/src/playlistparsers/xspfparser.h b/src/playlistparsers/xspfparser.h index 29f465fd4..14ea42446 100644 --- a/src/playlistparsers/xspfparser.h +++ b/src/playlistparsers/xspfparser.h @@ -36,11 +36,12 @@ class XSPFParser : public XMLParser { bool TryMagic(const QByteArray &data) const; - SongList Load(QIODevice *device, const QString& playlist_path = "", const QDir &dir = QDir()) const; + SongList Load(QIODevice *device, const QString& playlist_path = "", + const QDir& dir = QDir()) const; void Save(const SongList &songs, QIODevice *device, const QDir &dir = QDir()) const; private: - Song ParseTrack(QXmlStreamReader* reader) const; + Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const; }; #endif diff --git a/src/radio/icecastbackend.cpp b/src/radio/icecastbackend.cpp index d546604ce..4707484af 100644 --- a/src/radio/icecastbackend.cpp +++ b/src/radio/icecastbackend.cpp @@ -177,7 +177,7 @@ Song IcecastBackend::Station::ToSong() const { Song ret; ret.set_valid(true); ret.set_title(name); - ret.set_filename(url.toEncoded()); + ret.set_url(url); ret.set_bitrate(bitrate); ret.set_samplerate(samplerate); ret.set_genre(genre); diff --git a/src/radio/jamendoplaylistitem.cpp b/src/radio/jamendoplaylistitem.cpp index 8f8da9c66..01755e7e7 100644 --- a/src/radio/jamendoplaylistitem.cpp +++ b/src/radio/jamendoplaylistitem.cpp @@ -36,5 +36,5 @@ bool JamendoPlaylistItem::InitFromQuery(const SqlRow& query) { } QUrl JamendoPlaylistItem::Url() const { - return QUrl::fromEncoded(song_.filename().toAscii()); + return song_.url(); } diff --git a/src/radio/jamendoservice.cpp b/src/radio/jamendoservice.cpp index 5fcfc3ec8..bdacef636 100644 --- a/src/radio/jamendoservice.cpp +++ b/src/radio/jamendoservice.cpp @@ -364,7 +364,7 @@ Song JamendoService::ReadTrack(const QString& artist, continue; QString ogg_url = QString(kOggStreamUrl).arg(id_text); - song.set_filename(ogg_url); + song.set_url(QUrl(ogg_url)); song.set_art_automatic(album_cover); song.set_valid(true); diff --git a/src/radio/lastfmservice.cpp b/src/radio/lastfmservice.cpp index 29ac2ef49..f71cfa1aa 100644 --- a/src/radio/lastfmservice.cpp +++ b/src/radio/lastfmservice.cpp @@ -148,22 +148,22 @@ void LastFMService::LazyPopulate(QStandardItem* parent) { CreateStationItem(parent, tr("My Recommendations"), ":last.fm/recommended_radio.png", - "lastfm://user/USERNAME/recommended", + QUrl("lastfm://user/USERNAME/recommended"), tr("My Last.fm Recommended Radio")); CreateStationItem(parent, tr("My Radio Station"), ":last.fm/personal_radio.png", - "lastfm://user/USERNAME/library", + QUrl("lastfm://user/USERNAME/library"), tr("My Last.fm Library")); CreateStationItem(parent, tr("My Mix Radio"), ":last.fm/loved_radio.png", - "lastfm://user/USERNAME/mix", + QUrl("lastfm://user/USERNAME/mix"), tr("My Last.fm Mix Radio")); CreateStationItem(parent, tr("My Neighborhood"), ":last.fm/neighbour_radio.png", - "lastfm://user/USERNAME/neighbours", + QUrl("lastfm://user/USERNAME/neighbours"), tr("My Last.fm Neighborhood")); // Types that have children @@ -213,17 +213,17 @@ void LastFMService::LazyPopulate(QStandardItem* parent) { CreateStationItem(parent, tr("Last.fm Radio Station - %1").arg(parent->text()), ":last.fm/personal_radio.png", - "lastfm://user/" + parent->text() + "/library", + QUrl("lastfm://user/" + parent->text() + "/library"), tr("Last.fm Library - %1").arg(parent->text())); CreateStationItem(parent, tr("Last.fm Mix Radio - %1").arg(parent->text()), ":last.fm/loved_radio.png", - "lastfm://user/" + parent->text() + "/mix", + QUrl("lastfm://user/" + parent->text() + "/mix"), tr("Last.fm Mix Radio - %1").arg(parent->text())); CreateStationItem(parent, tr("Last.fm Neighbor Radio - %1").arg(parent->text()), ":last.fm/neighbour_radio.png", - "lastfm://user/" + parent->text() + "/neighbours", + QUrl("lastfm://user/" + parent->text() + "/neighbours"), tr("Last.fm Neighbor Radio - %1").arg(parent->text())); break; @@ -234,9 +234,9 @@ void LastFMService::LazyPopulate(QStandardItem* parent) { QStandardItem* LastFMService::CreateStationItem( QStandardItem* parent, const QString& name, const QString& icon, - const QString& url, const QString& title) { + const QUrl& url, const QString& title) { Song song; - song.set_filename(url); + song.set_url(url); song.set_title(title); QStandardItem* ret = new QStandardItem(QIcon(icon), name); @@ -625,7 +625,7 @@ void LastFMService::RefreshFriendsFinished() { foreach (const lastfm::User& f, friends) { Song song; - song.set_filename("lastfm://user/" + f.name() + "/library"); + song.set_url(QUrl("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()); @@ -657,7 +657,7 @@ void LastFMService::RefreshNeighboursFinished() { foreach (const lastfm::User& n, neighbours) { Song song; - song.set_filename("lastfm://user/" + n.name() + "/library"); + song.set_url(QUrl("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()); @@ -711,7 +711,7 @@ void LastFMService::AddArtistOrTag(const QString& name, url_content = content; Song song; - song.set_filename(url_pattern.arg(url_content)); + song.set_url(QUrl(url_pattern.arg(url_content))); song.set_title(title_pattern.arg(content)); QStandardItem* item = new QStandardItem(QIcon(icon), content); @@ -756,7 +756,7 @@ void LastFMService::RestoreList(const QString& name, url_content = content; Song song; - song.set_filename(url_pattern.arg(url_content)); + song.set_url(QUrl(url_pattern.arg(url_content))); song.set_title(title_pattern.arg(content)); QStandardItem* item = new QStandardItem(icon, content); @@ -881,7 +881,7 @@ PlaylistItemPtr LastFMService::PlaylistItemForUrl(const QUrl& url) { QStringList sections(url.path().split("/", QString::SkipEmptyParts)); Song song; - song.set_filename(url.toString()); + song.set_url(url); if (sections.count() == 2 && url.host() == "artist" && sections[1] == "similarartists") { song.set_title(tr(kTitleArtist).arg(sections[0])); diff --git a/src/radio/lastfmservice.h b/src/radio/lastfmservice.h index d5a4b373e..f45b595e1 100644 --- a/src/radio/lastfmservice.h +++ b/src/radio/lastfmservice.h @@ -150,7 +150,7 @@ class LastFMService : public RadioService { private: QStandardItem* CreateStationItem(QStandardItem* parent, - const QString& name, const QString& icon, const QString& url, + const QString& name, const QString& icon, const QUrl& url, const QString& title); QString ErrorString(lastfm::ws::Error error) const; bool InitScrobbler(); diff --git a/src/radio/magnatuneplaylistitem.cpp b/src/radio/magnatuneplaylistitem.cpp index 8a434e3ce..e7a8ccfe8 100644 --- a/src/radio/magnatuneplaylistitem.cpp +++ b/src/radio/magnatuneplaylistitem.cpp @@ -42,7 +42,7 @@ PlaylistItem::Options MagnatunePlaylistItem::options() const { } QUrl MagnatunePlaylistItem::Url() const { - return QUrl::fromEncoded(song_.filename().toAscii()); + return song_.url(); } PlaylistItem::SpecialLoadResult MagnatunePlaylistItem::StartLoading() { diff --git a/src/radio/magnatuneservice.cpp b/src/radio/magnatuneservice.cpp index 240fc3503..c07fc87c9 100644 --- a/src/radio/magnatuneservice.cpp +++ b/src/radio/magnatuneservice.cpp @@ -205,7 +205,7 @@ Song MagnatuneService::ReadTrack(QXmlStreamReader& reader) { if (name == "year") song.set_year(value.toInt()); if (name == "magnatunegenres") song.set_genre(value.section(',', 0, 0)); if (name == "seconds") song.set_length_nanosec(value.toInt() * kNsecPerSec); - if (name == "url") song.set_filename(value); + if (name == "url") song.set_url(QUrl(value)); if (name == "cover_small") song.set_art_automatic(value); if (name == "albumsku") song.set_comment(value); } diff --git a/src/radio/radioplaylistitem.cpp b/src/radio/radioplaylistitem.cpp index 2c52c8338..5670549c0 100644 --- a/src/radio/radioplaylistitem.cpp +++ b/src/radio/radioplaylistitem.cpp @@ -77,7 +77,7 @@ QVariant RadioPlaylistItem::DatabaseValue(DatabaseColumn column) const { void RadioPlaylistItem::InitMetadata() { if (metadata_.title().isEmpty()) - metadata_.set_title(metadata_.filename()); + metadata_.set_title(metadata_.url().toString()); metadata_.set_filetype(Song::Type_Stream); metadata_.set_valid(true); } @@ -108,7 +108,7 @@ PlaylistItem::SpecialLoadResult RadioPlaylistItem::LoadNext() { } QUrl RadioPlaylistItem::Url() const { - return QUrl(metadata_.filename()); + return metadata_.url(); } PlaylistItem::Options RadioPlaylistItem::options() const { diff --git a/src/radio/somafmservice.cpp b/src/radio/somafmservice.cpp index 79bcdb407..c2ad1ada8 100644 --- a/src/radio/somafmservice.cpp +++ b/src/radio/somafmservice.cpp @@ -181,7 +181,7 @@ void SomaFMService::ReadChannel(QXmlStreamReader& reader) { } else if (reader.name() == "dj") { song.set_artist(reader.readElementText()); } else if (reader.name() == "fastpls" && reader.attributes().value("format") == "mp3") { - song.set_filename(reader.readElementText()); + song.set_url(QUrl(reader.readElementText())); } else { ConsumeElement(reader); } diff --git a/src/radio/spotifyservice.cpp b/src/radio/spotifyservice.cpp index b1eeb7556..267f9a3bb 100644 --- a/src/radio/spotifyservice.cpp +++ b/src/radio/spotifyservice.cpp @@ -278,7 +278,7 @@ void SpotifyService::FillPlaylist(QStandardItem* item, const protobuf::LoadPlayl child->setData(Type_Track, RadioModel::Role_Type); child->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); child->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); - child->setData(QUrl(song.filename()), RadioModel::Role_Url); + child->setData(song.url(), RadioModel::Role_Url); item->appendRow(child); } @@ -293,7 +293,7 @@ void SpotifyService::SongFromProtobuf(const protobuf::Track& track, Song* song) song->set_disc(track.disc()); song->set_track(track.track()); song->set_year(track.year()); - song->set_filename(QStringFromStdString(track.uri())); + song->set_url(QUrl(QStringFromStdString(track.uri()))); QStringList artists; for (int i=0 ; i AlbumList; @@ -33,7 +33,7 @@ public: SongList FindSongsInDirectory(int id); SubdirectoryList SubdirsInDirectory(int id); DirectoryList GetAllDirectories(); - void ChangeDirPath(int id, const QString& new_path); + void ChangeDirPath(int id, const QString& old_path, const QString& new_path); QStringList GetAll(const QString& column, const QueryOptions& opt = QueryOptions()); QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()); @@ -56,8 +56,8 @@ public: SongList GetSongsByForeignId(const QStringList& ids, const QString& table, const QString& column); - SongList GetSongsByFilename(const QString& filename); - Song GetSongByFilename(const QString& filename, int beginning); + SongList GetSongsByUrl(const QUrl& url); + Song GetSongByUrl(const QUrl& url, int beginning); void AddDirectory(const QString& path); void RemoveDirectory(const Directory& dir); diff --git a/src/scripting/python/song.sip b/src/scripting/python/song.sip index d54548d3a..530a29cca 100644 --- a/src/scripting/python/song.sip +++ b/src/scripting/python/song.sip @@ -308,22 +308,16 @@ The ID in the L{LibraryBackend} of the directory containing this song, or -1 if the song is not from the library. %End - const QString& filename() const; + const QUrl& url() const; %Docstring -filename() -> str -The filename I{or URL} of this song. +url() -> L{PyQt4.QtCore.QUrl} +The URL of this song. %End const QString& basefilename() const; %Docstring basefilename() -> str The filename of this song without any directory component. -%End - - QUrl url() const; -%Docstring -url() -> L{PyQt4.QtCore.QUrl} -URL for this song which may point either to a file or to another type of stream. %End uint mtime() const; @@ -658,9 +652,9 @@ set_lastplayed(lastplayed) set_score(score) %End - void set_filename(const QString& v); + void set_url(const QUrl& v); %Docstring -set_filename(filename) +set_url(url) %End void set_basefilename(const QString& v); diff --git a/src/ui/albumcoverchoicecontroller.cpp b/src/ui/albumcoverchoicecontroller.cpp index 98608a8d0..d9c88fbc9 100644 --- a/src/ui/albumcoverchoicecontroller.cpp +++ b/src/ui/albumcoverchoicecontroller.cpp @@ -130,8 +130,8 @@ QString AlbumCoverChoiceController::GetInitialPathForFileDialog(const Song& song return song.art_automatic(); // if no automatic art, start in the song's folder - } else if (!song.filename().isEmpty() && song.filename().contains('/')) { - return song.filename().section('/', 0, -2) + filename; + } else if (!song.url().isEmpty() && song.url().toLocalFile().contains('/')) { + return song.url().toLocalFile().section('/', 0, -2) + filename; // fallback - start in home } else { @@ -189,7 +189,7 @@ void AlbumCoverChoiceController::ShowCover(const Song& song) { QLabel* label = new QLabel(dialog); label->setPixmap(AlbumCoverLoader::TryLoadPixmap( - song.art_automatic(), song.art_manual(), song.filename())); + song.art_automatic(), song.art_manual(), song.url().toLocalFile())); dialog->resize(label->pixmap()->size()); dialog->show(); diff --git a/src/ui/albumcovermanager.cpp b/src/ui/albumcovermanager.cpp index edc839678..d7b5a802b 100644 --- a/src/ui/albumcovermanager.cpp +++ b/src/ui/albumcovermanager.cpp @@ -293,14 +293,14 @@ void AlbumCoverManager::ArtistChanged(QListWidgetItem* current) { QListWidgetItem* item = new QListWidgetItem(no_cover_icon_, info.album_name, ui_->albums); item->setData(Role_ArtistName, info.artist); item->setData(Role_AlbumName, info.album_name); - item->setData(Role_FirstFilename, info.first_filename); + item->setData(Role_FirstUrl, info.first_url); item->setData(Qt::TextAlignmentRole, QVariant(Qt::AlignTop | Qt::AlignHCenter)); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); item->setToolTip(info.artist + " - " + info.album_name); if (!info.art_automatic.isEmpty() || !info.art_manual.isEmpty()) { quint64 id = cover_loader_->Worker()->LoadImageAsync( - info.art_automatic, info.art_manual, info.first_filename); + info.art_automatic, info.art_manual, info.first_url.toLocalFile()); item->setData(Role_PathAutomatic, info.art_automatic); item->setData(Role_PathManual, info.art_manual); cover_loading_tasks_[id] = item; @@ -487,7 +487,7 @@ Song AlbumCoverManager::ItemAsSong(QListWidgetItem* item) { result.set_artist(item->data(Role_ArtistName).toString()); result.set_album(item->data(Role_AlbumName).toString()); - result.set_filename(item->data(Role_FirstFilename).toString()); + result.set_url(item->data(Role_FirstUrl).toUrl()); result.set_art_automatic(item->data(Role_PathAutomatic).toString()); result.set_art_manual(item->data(Role_PathManual).toString()); diff --git a/src/ui/albumcovermanager.h b/src/ui/albumcovermanager.h index c8a321de2..c4c1c3170 100644 --- a/src/ui/albumcovermanager.h +++ b/src/ui/albumcovermanager.h @@ -110,7 +110,7 @@ class AlbumCoverManager : public QMainWindow { Role_AlbumName, Role_PathAutomatic, Role_PathManual, - Role_FirstFilename, + Role_FirstUrl, }; enum HideCovers { diff --git a/src/ui/albumcovermanagerlist.cpp b/src/ui/albumcovermanagerlist.cpp index 4314f1a67..0d54b58b8 100644 --- a/src/ui/albumcovermanagerlist.cpp +++ b/src/ui/albumcovermanagerlist.cpp @@ -43,7 +43,7 @@ QMimeData* AlbumCoverManagerList::mimeData(const QList items) // Get URLs from the songs QList urls; foreach (const Song& song, songs) { - urls << QUrl::fromLocalFile(song.filename()); + urls << song.url(); } // Get the QAbstractItemModel data so the picture works diff --git a/src/ui/edittagdialog.cpp b/src/ui/edittagdialog.cpp index 3223f4f6d..75075fe4d 100644 --- a/src/ui/edittagdialog.cpp +++ b/src/ui/edittagdialog.cpp @@ -191,7 +191,7 @@ QList EditTagDialog::LoadData(const SongList& songs) const if (song.IsEditable()) { // Try reloading the tags from file Song copy(song); - copy.InitFromFile(copy.filename(), copy.directory_id()); + copy.InitFromFile(copy.url().toLocalFile(), copy.directory_id()); if (copy.is_valid()) ret << Data(copy); @@ -424,7 +424,11 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) { QLocale::system().dateTimeFormat(QLocale::LongFormat))); ui_->filesize->setText(Utilities::PrettySize(song.filesize())); ui_->filetype->setText(song.TextForFiletype()); - ui_->filename->setText(QDir::toNativeSeparators(song.filename())); + + if (song.url().scheme() == "file") + ui_->filename->setText(QDir::toNativeSeparators(song.url().toLocalFile())); + else + ui_->filename->setText(song.url().toString()); album_cover_choice_controller_->search_for_cover_action()->setEnabled(CoverProviders::instance().HasAnyProviders()); } @@ -602,7 +606,7 @@ void EditTagDialog::SaveData(const QList& data) { continue; if (!ref.current_.Save()) { - emit Error(tr("An error occurred writing metadata to '%1'").arg(ref.current_.filename())); + emit Error(tr("An error occurred writing metadata to '%1'").arg(ref.current_.url().toLocalFile())); } } } @@ -736,12 +740,12 @@ void EditTagDialog::FetchTag() { } void EditTagDialog::FetchTagSongChosen(const Song& original_song, const Song& new_metadata) { - const QString filename = original_song.filename(); + const QString filename = original_song.url().toLocalFile(); // Find the song with this filename for (int i=0 ; ioriginal_.filename() != filename) + if (data->original_.url().toLocalFile() != filename) continue; // Is it currently being displayed in the UI? diff --git a/src/ui/organisedialog.cpp b/src/ui/organisedialog.cpp index 7bff6fa26..ea0324166 100644 --- a/src/ui/organisedialog.cpp +++ b/src/ui/organisedialog.cpp @@ -106,19 +106,13 @@ int OrganiseDialog::SetSongs(const SongList& songs) { preview_songs_.clear(); foreach (const Song& song, songs) { - const QString filename = song.filename(); - - if (filename.isEmpty()) + if (song.url().scheme() != "file") { continue; - if (filename.contains("://")) { - QUrl url(song.filename()); - if (!url.scheme().isEmpty() && url.scheme() != "file") - continue; - } + }; if (song.filesize() > 0) total_size_ += song.filesize(); - filenames_ << filename; + filenames_ << song.url().toLocalFile(); if (preview_songs_.count() < kNumberOfPreviews) preview_songs_ << song; diff --git a/src/ui/organiseerrordialog.cpp b/src/ui/organiseerrordialog.cpp index ccf12c02b..b67ee4d29 100644 --- a/src/ui/organiseerrordialog.cpp +++ b/src/ui/organiseerrordialog.cpp @@ -38,7 +38,7 @@ void OrganiseErrorDialog::Show( OperationType type, const SongList& songs_with_errors) { QStringList files; foreach (const Song& song, songs_with_errors) { - files << song.filename(); + files << song.url().toLocalFile(); } Show(type, files); } diff --git a/src/ui/trackselectiondialog.cpp b/src/ui/trackselectiondialog.cpp index 80d7e4b29..1222c4112 100644 --- a/src/ui/trackselectiondialog.cpp +++ b/src/ui/trackselectiondialog.cpp @@ -77,7 +77,7 @@ void TrackSelectionDialog::Init(const SongList& songs) { data_ << data; QListWidgetItem* item = new QListWidgetItem(ui_->song_list); - item->setText(QFileInfo(song.filename()).fileName()); + item->setText(QFileInfo(song.url().toLocalFile()).fileName()); item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); } @@ -94,7 +94,7 @@ void TrackSelectionDialog::FetchTagProgress(const Song& original_song, // Find the item with this filename int row = -1; for (int i=0 ; iAddOrUpdateSongs(SongList() << s); ASSERT_EQ(1, spy.count()); spy.takeFirst(); - s.set_filename("foo"); + s.set_url(QUrl::fromLocalFile("foo")); backend_->AddOrUpdateSongs(SongList() << s); ASSERT_EQ(1, spy.count()); spy.takeFirst(); diff --git a/tests/librarymodel_test.cpp b/tests/librarymodel_test.cpp index 3f6388193..bd7bb2441 100644 --- a/tests/librarymodel_test.cpp +++ b/tests/librarymodel_test.cpp @@ -52,7 +52,7 @@ class LibraryModelTest : public ::testing::Test { song.set_directory_id(1); if (song.mtime() == -1) song.set_mtime(1); if (song.ctime() == -1) song.set_ctime(1); - if (song.filename().isNull()) song.set_filename("/tmp/foo"); + if (song.url().isEmpty()) song.set_url(QUrl("file:///tmp/foo")); if (song.filesize() == -1) song.set_filesize(1); if (!added_dir_) { diff --git a/tests/m3uparser_test.cpp b/tests/m3uparser_test.cpp index 0d71549ae..d6020647f 100644 --- a/tests/m3uparser_test.cpp +++ b/tests/m3uparser_test.cpp @@ -56,10 +56,10 @@ TEST_F(M3UParserTest, ParsesTrackLocation) { taglib_.ExpectCall(temp.fileName(), "foo", "bar", "baz"); Song song(&taglib_); QString line(temp.fileName()); - ASSERT_TRUE(parser_.ParseTrackLocation(line, QDir(), &song)); - ASSERT_EQ(temp.fileName(), song.filename()); + parser_.LoadSong(line, 0, QDir(), &song); + ASSERT_EQ(QUrl::fromLocalFile(temp.fileName()), song.url()); - song.InitFromFile(song.filename(), -1); + song.InitFromFile(song.url().toLocalFile(), -1); EXPECT_EQ("foo", song.title()); EXPECT_EQ("bar", song.artist()); @@ -74,10 +74,10 @@ TEST_F(M3UParserTest, ParsesTrackLocationRelative) { M3UParser parser(NULL); QString line(info.fileName()); Song song(&taglib_); - ASSERT_TRUE(parser.ParseTrackLocation(line, info.dir(), &song)); - ASSERT_EQ(temp.fileName(), song.filename()); + parser.LoadSong(line, 0, info.dir(), &song); + ASSERT_EQ(QUrl::fromLocalFile(temp.fileName()), song.url()); - song.InitFromFile(song.filename(), -1); + song.InitFromFile(song.url().toLocalFile(), -1); EXPECT_EQ("foo", song.title()); } @@ -85,8 +85,8 @@ TEST_F(M3UParserTest, ParsesTrackLocationRelative) { TEST_F(M3UParserTest, ParsesTrackLocationHttp) { QString line("http://example.com/foo/bar.mp3"); Song song; - ASSERT_TRUE(parser_.ParseTrackLocation(line, QDir(), &song)); - EXPECT_EQ("http://example.com/foo/bar.mp3", song.filename()); + parser_.LoadSong(line, 0, QDir(), &song); + EXPECT_EQ(QUrl("http://example.com/foo/bar.mp3"), song.url()); } TEST_F(M3UParserTest, ParsesSongsFromDevice) { @@ -102,8 +102,7 @@ TEST_F(M3UParserTest, ParsesSongsFromDevice) { EXPECT_EQ("Some Artist", s.artist()); EXPECT_EQ("Some Title", s.title()); EXPECT_EQ(123 * kNsecPerSec, s.length_nanosec()); - EXPECT_PRED_FORMAT2(::testing::IsSubstring, - "http://foo.com/bar/somefile.mp3", s.filename().toStdString()); + EXPECT_EQ(QUrl("http://foo.com/bar/somefile.mp3"), s.url()); } TEST_F(M3UParserTest, ParsesNonExtendedM3U) { @@ -114,10 +113,8 @@ TEST_F(M3UParserTest, ParsesNonExtendedM3U) { M3UParser parser(NULL); SongList songs = parser.Load(&buffer, "", QDir("somedir")); ASSERT_EQ(2, songs.size()); - EXPECT_PRED_FORMAT2(::testing::IsSubstring, - "http://foo.com/bar/somefile.mp3", songs[0].filename().toStdString()); - EXPECT_PRED_FORMAT2(::testing::IsSubstring, - "http://baz.com/thing.mp3", songs[1].filename().toStdString()); + EXPECT_EQ(QUrl("http://foo.com/bar/somefile.mp3"), songs[0].url()); + EXPECT_EQ(QUrl("http://baz.com/thing.mp3"), songs[1].url()); EXPECT_EQ(-1, songs[0].length_nanosec()); EXPECT_EQ(-1, songs[1].length_nanosec()); EXPECT_TRUE(songs[0].artist().isEmpty()); @@ -144,7 +141,7 @@ TEST_F(M3UParserTest, SavesSong) { one.set_title("foo"); one.set_artist("bar"); one.set_length_nanosec(123 * kNsecPerSec); - one.set_filename("http://www.example.com/foo.mp3"); + one.set_url(QUrl("http://www.example.com/foo.mp3")); SongList songs; songs << one; M3UParser parser(NULL); @@ -167,5 +164,6 @@ TEST_F(M3UParserTest, ParsesUTF8) { EXPECT_EQ(11, songs[0].title().length()); EXPECT_EQ(QString::fromUtf8("Разные"), songs[0].artist()); EXPECT_EQ(QString::fromUtf8("исполнители"), songs[0].title()); - EXPECT_EQ(QString::fromUtf8("/foo/Разные/исполнители.mp3"), songs[0].filename()); + EXPECT_EQ(QUrl::fromLocalFile(QString::fromUtf8("/foo/Разные/исполнители.mp3")), + songs[0].url()); } diff --git a/tests/mock_librarybackend.h b/tests/mock_librarybackend.h index 9141af834..314f74c9a 100644 --- a/tests/mock_librarybackend.h +++ b/tests/mock_librarybackend.h @@ -33,7 +33,7 @@ class MockLibraryBackend : public LibraryBackendInterface { MOCK_METHOD1(FindSongsInDirectory, SongList(int)); MOCK_METHOD1(SubdirsInDirectory, SubdirectoryList(int)); MOCK_METHOD0(GetAllDirectories, DirectoryList()); - MOCK_METHOD2(ChangeDirPath, void(int, const QString&)); + MOCK_METHOD3(ChangeDirPath, void(int, const QString&, const QString&)); MOCK_METHOD1(GetAllArtists, QStringList(const QueryOptions&)); MOCK_METHOD1(GetAllArtistsWithAlbums, QStringList(const QueryOptions&)); @@ -51,8 +51,8 @@ class MockLibraryBackend : public LibraryBackendInterface { MOCK_METHOD1(GetSongById, Song(int)); - MOCK_METHOD1(GetSongsByFilename, SongList(const QString&)); - MOCK_METHOD2(GetSongByFilename, Song(const QString&, qint64)); + MOCK_METHOD1(GetSongsByUrl, SongList(const QUrl&)); + MOCK_METHOD2(GetSongByUrl, Song(const QUrl&, qint64)); MOCK_METHOD1(AddDirectory, void(const QString&)); MOCK_METHOD1(RemoveDirectory, void(const Directory&)); diff --git a/tests/organiseformat_test.cpp b/tests/organiseformat_test.cpp index 1e01c9ff9..8d7d58c0e 100644 --- a/tests/organiseformat_test.cpp +++ b/tests/organiseformat_test.cpp @@ -52,7 +52,7 @@ TEST_F(OrganiseFormatTest, BasicReplace) { } TEST_F(OrganiseFormatTest, Extension) { - song_.set_filename("/some/path/filename.mp3"); + song_.set_url(QUrl("file:///some/path/filename.mp3")); format_.set_format("%extension"); ASSERT_TRUE(format_.IsValid()); diff --git a/tests/plsparser_test.cpp b/tests/plsparser_test.cpp index 1815b1497..4d56211ae 100644 --- a/tests/plsparser_test.cpp +++ b/tests/plsparser_test.cpp @@ -48,7 +48,7 @@ TEST_F(PLSParserTest, ParseOneTrack) { SongList songs = parser_.Load(file.get(), "", QDir("/relative/to/")); ASSERT_EQ(1, songs.length()); - EXPECT_EQ("/relative/to/filename with spaces.mp3", songs[0].filename()); + EXPECT_EQ(QUrl("file:///relative/to/filename with spaces.mp3"), songs[0].url()); EXPECT_EQ("Title", songs[0].title()); EXPECT_EQ(123 * kNsecPerSec, songs[0].length_nanosec()); } @@ -58,10 +58,10 @@ TEST_F(PLSParserTest, ParseSomaFM) { SongList songs = parser_.Load(file.get()); ASSERT_EQ(4, songs.length()); - EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1018", songs[0].filename()); - EXPECT_EQ("http://streamer-mtc-aa03.somafm.com:80/stream/1018", songs[1].filename()); - EXPECT_EQ("http://streamer-ntc-aa04.somafm.com:80/stream/1018", songs[2].filename()); - EXPECT_EQ("http://ice.somafm.com/groovesalad", songs[3].filename()); + EXPECT_EQ(QUrl("http://streamer-dtc-aa05.somafm.com:80/stream/1018"), songs[0].url()); + EXPECT_EQ(QUrl("http://streamer-mtc-aa03.somafm.com:80/stream/1018"), songs[1].url()); + EXPECT_EQ(QUrl("http://streamer-ntc-aa04.somafm.com:80/stream/1018"), songs[2].url()); + EXPECT_EQ(QUrl("http://ice.somafm.com/groovesalad"), songs[3].url()); EXPECT_EQ("SomaFM: Groove Salad (#1 128k mp3): A nicely chilled plate of ambient beats and grooves.", songs[0].title()); EXPECT_EQ("SomaFM: Groove Salad (#2 128k mp3): A nicely chilled plate of ambient beats and grooves.", songs[1].title()); EXPECT_EQ("SomaFM: Groove Salad (#3 128k mp3): A nicely chilled plate of ambient beats and grooves.", songs[2].title()); @@ -74,10 +74,10 @@ TEST_F(PLSParserTest, ParseSomaFM2) { SongList songs = parser_.Load(file.get()); ASSERT_EQ(4, songs.length()); - EXPECT_EQ("http://streamer-ntc-aa03.somafm.com:80/stream/1021", songs[0].filename()); - EXPECT_EQ("http://streamer-mtc-aa04.somafm.com:80/stream/1021", songs[1].filename()); - EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1021", songs[2].filename()); - EXPECT_EQ("http://ice.somafm.com/secretagent", songs[3].filename()); + EXPECT_EQ(QUrl("http://streamer-ntc-aa03.somafm.com:80/stream/1021"), songs[0].url()); + EXPECT_EQ(QUrl("http://streamer-mtc-aa04.somafm.com:80/stream/1021"), songs[1].url()); + EXPECT_EQ(QUrl("http://streamer-dtc-aa05.somafm.com:80/stream/1021"), songs[2].url()); + EXPECT_EQ(QUrl("http://ice.somafm.com/secretagent"), songs[3].url()); EXPECT_EQ("SomaFM: Secret Agent (#1 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!", songs[0].title()); EXPECT_EQ("SomaFM: Secret Agent (#2 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!", songs[1].title()); EXPECT_EQ("SomaFM: Secret Agent (#3 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!", songs[2].title()); @@ -87,11 +87,11 @@ TEST_F(PLSParserTest, ParseSomaFM2) { TEST_F(PLSParserTest, SaveAndLoad) { Song one; - one.set_filename("http://www.example.com/foo.mp3"); + one.set_url(QUrl("http://www.example.com/foo.mp3")); one.set_title("Foo, with, some, commas"); Song two; - two.set_filename("relative/bar.mp3"); + two.set_url(QUrl("relative/bar.mp3")); two.set_title("Bar"); two.set_length_nanosec(123 * kNsecPerSec); @@ -106,8 +106,8 @@ TEST_F(PLSParserTest, SaveAndLoad) { songs = parser_.Load(&temp, "", QDir("/meep")); ASSERT_EQ(2, songs.count()); - EXPECT_EQ(one.filename(), songs[0].filename()); - EXPECT_EQ("/meep/relative/bar.mp3", songs[1].filename()); + EXPECT_EQ(one.url(), songs[0].url()); + EXPECT_EQ(QUrl("file:///meep/relative/bar.mp3"), songs[1].url()); EXPECT_EQ(one.title(), songs[0].title()); EXPECT_EQ(two.title(), songs[1].title()); EXPECT_EQ(one.length_nanosec(), songs[0].length_nanosec()); diff --git a/tests/songloader_test.cpp b/tests/songloader_test.cpp index eeada3a95..44913376b 100644 --- a/tests/songloader_test.cpp +++ b/tests/songloader_test.cpp @@ -55,7 +55,7 @@ protected: loader_->set_timeout(20000); // the thing we return is not really important - EXPECT_CALL(*library_.get(), GetSongByFilename(_, _)).WillRepeatedly(Return(Song())); + EXPECT_CALL(*library_.get(), GetSongByUrl(_, _)).WillRepeatedly(Return(Song())); } void LoadLocalDirectory(const QString& dir); @@ -191,7 +191,7 @@ TEST_F(SongLoaderTest, LoadRemoteMp3) { // Check the song got loaded ASSERT_EQ(1, loader_->songs().count()); - EXPECT_EQ(QString(kRemoteUrl) + "/beep.mp3", loader_->songs()[0].filename()); + EXPECT_EQ(QUrl(QString(kRemoteUrl) + "/beep.mp3"), loader_->songs()[0].url()); } TEST_F(SongLoaderTest, LoadRemote404) { @@ -231,7 +231,7 @@ TEST_F(SongLoaderTest, LoadRemotePls) { ASSERT_EQ(4, loader_->songs().count()); EXPECT_EQ("SomaFM: Groove Salad (#3 128k mp3): A nicely chilled plate of ambient beats and grooves.", loader_->songs()[2].title()); - EXPECT_EQ("http://ice.somafm.com/groovesalad", loader_->songs()[3].filename()); + EXPECT_EQ(QUrl("http://ice.somafm.com/groovesalad"), loader_->songs()[3].url()); } TEST_F(SongLoaderTest, LoadRemotePlainText) { @@ -268,8 +268,8 @@ TEST_F(SongLoaderTest, LoadRemotePlainM3U) { EXPECT_EQ(true, spy[0][0].toBool()); ASSERT_EQ(2, loader_->songs().count()); - EXPECT_EQ("http://www.example.com/one.mp3", loader_->songs()[0].filename()); - EXPECT_EQ("http://www.example.com/two.mp3", loader_->songs()[1].filename()); + EXPECT_EQ(QUrl("http://www.example.com/one.mp3"), loader_->songs()[0].url()); + EXPECT_EQ(QUrl("http://www.example.com/two.mp3"), loader_->songs()[1].url()); } TEST_F(SongLoaderTest, LoadLocalDirectory) { diff --git a/tests/songplaylistitem_test.cpp b/tests/songplaylistitem_test.cpp index 38daa5842..7dee583f7 100644 --- a/tests/songplaylistitem_test.cpp +++ b/tests/songplaylistitem_test.cpp @@ -38,7 +38,7 @@ class SongPlaylistItemTest : public ::testing::TestWithParam { absolute_file_name_ = QFileInfo(temp_file_.fileName()).absoluteFilePath(); song_.Init("Title", "Artist", "Album", 123); - song_.set_filename(absolute_file_name_); + song_.set_url(QUrl::fromLocalFile(absolute_file_name_)); item_.reset(new SongPlaylistItem(song_)); diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp index 9b0088b29..5a9610174 100644 --- a/tests/test_utils.cpp +++ b/tests/test_utils.cpp @@ -50,6 +50,10 @@ void PrintTo(const ::QVariant& var, std::ostream& os) { os << var.toString().toStdString(); } +void PrintTo(const ::QUrl& url, std::ostream& os) { + os << url.toString().toStdString(); +} + TemporaryResource::TemporaryResource(const QString& filename) { setFileTemplate(QDir::tempPath() + "/clementine_test-XXXXXX." + filename.section('.', -1, -1)); diff --git a/tests/test_utils.h b/tests/test_utils.h index a60a95b95..26cbab828 100644 --- a/tests/test_utils.h +++ b/tests/test_utils.h @@ -46,6 +46,7 @@ std::ostream& operator <<(std::ostream& stream, const QList& list) { void PrintTo(const ::QString& str, std::ostream& os); void PrintTo(const ::QVariant& var, std::ostream& os); +void PrintTo(const ::QUrl& url, std::ostream& os); #define EXPOSE_SIGNAL0(n) \ void Emit##n() { emit n(); } diff --git a/tests/xspfparser_test.cpp b/tests/xspfparser_test.cpp index d2e8dc623..8fe233ffa 100644 --- a/tests/xspfparser_test.cpp +++ b/tests/xspfparser_test.cpp @@ -49,7 +49,7 @@ TEST_F(XSPFParserTest, ParsesOneTrackFromXML) { EXPECT_EQ("Foo", song.title()); EXPECT_EQ("Bar", song.artist()); EXPECT_EQ("Baz", song.album()); - EXPECT_EQ("http://example.com/foo.mp3", song.filename()); + EXPECT_EQ(QUrl("http://example.com/foo.mp3"), song.url()); EXPECT_EQ(60 * kNsecPerSec, song.length_nanosec()); EXPECT_TRUE(song.is_valid()); } @@ -69,8 +69,8 @@ TEST_F(XSPFParserTest, ParsesMoreThanOneTrackFromXML) { XSPFParser parser(NULL); SongList songs = parser.Load(&buffer); ASSERT_EQ(2, songs.length()); - EXPECT_EQ("http://example.com/foo.mp3", songs[0].filename()); - EXPECT_EQ("http://example.com/bar.mp3", songs[1].filename()); + EXPECT_EQ(QUrl("http://example.com/foo.mp3"), songs[0].url()); + EXPECT_EQ(QUrl("http://example.com/bar.mp3"), songs[1].url()); EXPECT_TRUE(songs[0].is_stream()); EXPECT_TRUE(songs[1].is_stream()); } @@ -100,7 +100,7 @@ TEST_F(XSPFParserTest, SavesSong) { buffer.open(QIODevice::WriteOnly); XSPFParser parser(NULL); Song one; - one.set_filename("http://www.example.com/foo.mp3"); + one.set_url(QUrl("http://www.example.com/foo.mp3")); one.set_filetype(Song::Type_Stream); one.set_title("foo"); one.set_length_nanosec(123 * kNsecPerSec); @@ -121,7 +121,7 @@ TEST_F(XSPFParserTest, SavesLocalFile) { buffer.open(QIODevice::WriteOnly); XSPFParser parser(NULL); Song one; - one.set_filename("/bar/foo.mp3"); + one.set_url(QUrl("file:///bar/foo.mp3")); one.set_filetype(Song::Type_Mpeg); one.set_title("foo"); one.set_length_nanosec(123 * kNsecPerSec);