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.

This commit is contained in:
David Sansome 2011-04-28 12:27:53 +00:00
parent 9dd45dbe82
commit ccb9f8cf94
77 changed files with 401 additions and 458 deletions

View File

@ -312,5 +312,6 @@
<file>icons/48x48/mail-message.png</file> <file>icons/48x48/mail-message.png</file>
<file>schema/schema-29.sql</file> <file>schema/schema-29.sql</file>
<file>schema/schema-30.sql</file> <file>schema/schema-30.sql</file>
<file>schema/schema-31.sql</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -0,0 +1,3 @@
UPDATE songs SET filename = "file://" || filename;
UPDATE schema_version SET version=31;

View File

@ -125,7 +125,7 @@ class DigitallyImportedServiceBase(clementine.RadioService):
song = clementine.Song() song = clementine.Song()
song.set_title(stream["name"]) song.set_title(stream["name"])
song.set_artist(self.SERVICE_DESCRIPTION) 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 = QStandardItem(QIcon(":last.fm/icon_radio.png"), stream["name"])
item.setData(stream["description"], PyQt4.QtCore.Qt.ToolTipRole) item.setData(stream["description"], PyQt4.QtCore.Qt.ToolTipRole)
@ -192,6 +192,6 @@ class DigitallyImportedServiceBase(clementine.RadioService):
# Take the first track in the playlist # Take the first track in the playlist
result.type_ = clementine.PlaylistItem.SpecialLoadResult.TrackAvailable result.type_ = clementine.PlaylistItem.SpecialLoadResult.TrackAvailable
result.media_url_ = QUrl(songs[0].filename()) result.media_url_ = songs[0].url()
self.AsyncLoadFinished.emit(result) self.AsyncLoadFinished.emit(result)

View File

@ -32,7 +32,7 @@
#include <QVariant> #include <QVariant>
const char* Database::kDatabaseFilename = "clementine.db"; const char* Database::kDatabaseFilename = "clementine.db";
const int Database::kSchemaVersion = 30; const int Database::kSchemaVersion = 31;
const char* Database::kMagicAllSongsTables = "%allsongstables"; const char* Database::kMagicAllSongsTables = "%allsongstables";
int Database::sNextConnectionId = 1; int Database::sNextConnectionId = 1;

View File

@ -60,7 +60,7 @@ void DeleteFiles::Start(const QStringList& filenames) {
SongList songs; SongList songs;
foreach (const QString& filename, filenames) { foreach (const QString& filename, filenames) {
Song song; Song song;
song.set_filename(filename); song.set_url(QUrl::fromLocalFile(filename));
songs << song; songs << song;
} }

View File

@ -66,14 +66,14 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) {
bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob& job) { bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob& job) {
#ifdef HAVE_GIO #ifdef HAVE_GIO
//convert QString to char //convert QString to char
QByteArray ba = job.metadata_.filename().toLocal8Bit(); QByteArray ba = job.metadata_.url().toLocalFile().toLocal8Bit();
const char *filepathChar = ba.data(); const char *filepathChar = ba.data();
GFile *file = g_file_new_for_path (filepathChar); GFile *file = g_file_new_for_path (filepathChar);
bool success = g_file_trash(file, NULL, NULL); bool success = g_file_trash(file, NULL, NULL);
g_object_unref(file); g_object_unref(file);
return success; return success;
#else #else
return QFile::remove(job.metadata_.filename()); return QFile::remove(job.metadata_.url().toLocalFile());
#endif #endif
} }

View File

@ -321,7 +321,7 @@ void Mpris1TrackList::PlayTrack(int index) {
QVariantMap Mpris1::GetMetadata(const Song& song) { QVariantMap Mpris1::GetMetadata(const Song& song) {
QVariantMap ret; QVariantMap ret;
AddMetadata("location", song.filename(), &ret); AddMetadata("location", song.url().toString(), &ret);
AddMetadata("title", song.PrettyTitle(), &ret); AddMetadata("title", song.PrettyTitle(), &ret);
AddMetadata("artist", song.artist(), &ret); AddMetadata("artist", song.artist(), &ret);
AddMetadata("album", song.album(), &ret); AddMetadata("album", song.album(), &ret);

View File

@ -308,7 +308,7 @@ void Mpris2::ArtLoaded(const Song& song, const QString& art_uri) {
using mpris::AddMetadata; using mpris::AddMetadata;
AddMetadata("mpris:trackid", current_track_id(), &last_metadata_); 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_); AddMetadata("xesam:title", song.PrettyTitle(), &last_metadata_);
AddMetadataAsList("xesam:artist", song.artist(), &last_metadata_); AddMetadataAsList("xesam:artist", song.artist(), &last_metadata_);
AddMetadata("xesam:album", song.album(), &last_metadata_); AddMetadata("xesam:album", song.album(), &last_metadata_);

View File

@ -148,7 +148,7 @@ void Organise::ProcessSomeFiles() {
song.set_filetype(task.new_filetype_); song.set_filetype(task.new_filetype_);
// Fiddle the filename extension as well to match the new type // 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_)); 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 // Have to set this to the size of the new file or else funny stuff happens

View File

@ -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 == "length") value = QString::number(song.length_nanosec() / kNsecPerSec);
else if (tag == "bitrate") value = QString::number(song.bitrate()); else if (tag == "bitrate") value = QString::number(song.bitrate());
else if (tag == "samplerate") value = QString::number(song.samplerate()); 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") { else if (tag == "artistinitial") {
value = song.albumartist().trimmed(); value = song.albumartist().trimmed();
if (value.isEmpty()) value = song.artist().trimmed(); if (value.isEmpty()) value = song.artist().trimmed();

View File

@ -253,7 +253,7 @@ bool Song::HasProperMediaFile() const {
#endif #endif
QMutexLocker l(&taglib_mutex_); QMutexLocker l(&taglib_mutex_);
scoped_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(d->filename_)); scoped_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(d->url_.toLocalFile()));
return !fileref->isNull() && fileref->tag(); return !fileref->isNull() && fileref->tag();
} }
@ -266,7 +266,7 @@ void Song::InitFromFile(const QString& filename, int directory_id) {
d->init_from_file_ = true; d->init_from_file_ = true;
d->filename_ = filename; d->url_ = QUrl::fromLocalFile(filename);
d->directory_id_ = directory_id; d->directory_id_ = directory_id;
QFileInfo info(filename); QFileInfo info(filename);
@ -502,6 +502,7 @@ void Song::InitFromQuery(const SqlRow& q, int col) {
d->init_from_file_ = true; d->init_from_file_ = true;
#define tostr(n) (q.value(n).isNull() ? QString::null : q.value(n).toString()) #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 toint(n) (q.value(n).isNull() ? -1 : q.value(n).toInt())
#define tolonglong(n) (q.value(n).isNull() ? -1 : q.value(n).toLongLong()) #define tolonglong(n) (q.value(n).isNull() ? -1 : q.value(n).toLongLong())
#define tofloat(n) (q.value(n).isNull() ? -1 : q.value(n).toDouble()) #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->samplerate_ = toint(col + 14);
d->directory_id_ = toint(col + 15); d->directory_id_ = toint(col + 15);
d->filename_ = tostr(col + 16); d->url_ = QUrl::fromEncoded(tobytearray(col + 16));
d->basefilename_ = QFileInfo(d->filename_).fileName(); d->basefilename_ = QFileInfo(d->url_.toLocalFile()).fileName();
d->mtime_ = toint(col + 17); d->mtime_ = toint(col + 17);
d->ctime_ = toint(col + 18); d->ctime_ = toint(col + 18);
d->filesize_ = toint(col + 19); d->filesize_ = toint(col + 19);
@ -562,7 +563,7 @@ void Song::InitFromQuery(const SqlRow& q, int col) {
} }
void Song::InitFromFilePartial(const QString& filename) { 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. // 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 // 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 // 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 #endif // HAVE_LIBLASTFM
#ifdef HAVE_LIBGPOD #ifdef HAVE_LIBGPOD
void Song::InitFromItdb(const Itdb_Track* track) { void Song::InitFromItdb(const Itdb_Track* track, const QString& prefix) {
d->valid_ = true; d->valid_ = true;
d->title_ = QString::fromUtf8(track->title); d->title_ = QString::fromUtf8(track->title);
@ -621,9 +622,11 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
d->skipcount_ = track->skipcount; d->skipcount_ = track->skipcount;
d->lastplayed_ = track->time_played; d->lastplayed_ = track->time_played;
d->filename_ = QString::fromLocal8Bit(track->ipod_path); QString filename = QString::fromLocal8Bit(track->ipod_path);
d->filename_.replace(':', '/'); filename.replace(':', '/');
d->basefilename_ = QFileInfo(d->filename_).fileName();
d->url_ = QUrl::fromLocalFile(prefix + filename);
d->basefilename_ = QFileInfo(filename).fileName();
} }
void Song::ToItdb(Itdb_Track *track) const { void Song::ToItdb(Itdb_Track *track) const {
@ -656,7 +659,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
#endif #endif
#ifdef HAVE_LIBMTP #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->valid_ = true;
d->title_ = QString::fromUtf8(track->title); d->title_ = QString::fromUtf8(track->title);
@ -664,8 +667,8 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
d->album_ = QString::fromUtf8(track->album); d->album_ = QString::fromUtf8(track->album);
d->composer_ = QString::fromUtf8(track->composer); d->composer_ = QString::fromUtf8(track->composer);
d->genre_ = QString::fromUtf8(track->genre); d->genre_ = QString::fromUtf8(track->genre);
d->filename_ = QString::number(track->item_id); d->url_ = QUrl(QString("mtp://%1/%2").arg(host, track->item_id));
d->basefilename_ = d->filename_; d->basefilename_ = QString::number(track->item_id);
d->track_ = track->tracknumber; d->track_ = track->tracknumber;
set_length_nanosec(track->duration * kNsecPerMsec); set_length_nanosec(track->duration * kNsecPerMsec);
@ -839,7 +842,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
d->bitrate_ = item_value.toInt(); d->bitrate_ = item_value.toInt();
else if (wcscmp(name, g_wszWMDMFileName) == 0) 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) else if (wcscmp(name, g_wszWMDMDuration) == 0)
set_length_nanosec(item_value.toULongLong() * 1e2); 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 // 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" || if (ext == "mp3" || ext == "wma" || ext == "flac" || ext == "ogg" ||
ext == "spx" || ext == "mp4" || ext == "aac" || ext == "m4a") ext == "spx" || ext == "mp4" || ext == "aac" || ext == "m4a")
break; break;
@ -1007,7 +1010,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
query->bindValue(":samplerate", intval(d->samplerate_)); query->bindValue(":samplerate", intval(d->samplerate_));
query->bindValue(":directory", notnullintval(d->directory_id_)); 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(":mtime", notnullintval(d->mtime_));
query->bindValue(":ctime", notnullintval(d->ctime_)); query->bindValue(":ctime", notnullintval(d->ctime_));
query->bindValue(":filesize", notnullintval(d->filesize_)); query->bindValue(":filesize", notnullintval(d->filesize_));
@ -1066,12 +1069,6 @@ void Song::ToLastFM(lastfm::Track* track) const {
} }
#endif // HAVE_LIBLASTFM #endif // HAVE_LIBLASTFM
QUrl Song::url() const {
return QFile::exists(filename())
? QUrl::fromLocalFile(filename())
: filename();
}
QString Song::PrettyTitle() const { QString Song::PrettyTitle() const {
QString title(d->title_); QString title(d->title_);
@ -1164,16 +1161,17 @@ TagLib::String QStringToTaglibString(const QString& s) {
} }
bool Song::IsEditable() const { 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(); d->filetype_ != Type_Unknown && !has_cue();
} }
bool Song::Save() const { bool Song::Save() const {
if (d->filename_.isNull()) const QString filename = d->url_.toLocalFile();
if (filename.isNull())
return false; return false;
QMutexLocker l(&taglib_mutex_); QMutexLocker l(&taglib_mutex_);
scoped_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(d->filename_)); scoped_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (!fileref || fileref->isNull()) // The file probably doesn't exist if (!fileref || fileref->isNull()) // The file probably doesn't exist
return false; return false;
@ -1214,7 +1212,7 @@ bool Song::Save() const {
if (ret) { if (ret) {
// Linux: inotify doesn't seem to notice the change to the file unless we // Linux: inotify doesn't seem to notice the change to the file unless we
// change the timestamps as well. (this is what touch does) // 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 #endif // Q_OS_LINUX
@ -1232,7 +1230,7 @@ QFuture<bool> Song::BackgroundSave() const {
bool Song::operator==(const Song& other) const { bool Song::operator==(const Song& other) const {
// TODO: this isn't working for radios // TODO: this isn't working for radios
return filename() == other.filename() && return url() == other.url() &&
beginning_nanosec() == other.beginning_nanosec(); beginning_nanosec() == other.beginning_nanosec();
} }

View File

@ -26,6 +26,7 @@
#include <QSharedDataPointer> #include <QSharedDataPointer>
#include <QSqlQuery> #include <QSqlQuery>
#include <QString> #include <QString>
#include <QUrl>
#include <xiphcomment.h> #include <xiphcomment.h>
@ -136,12 +137,12 @@ class Song {
void MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle& bundle); void MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle& bundle);
#ifdef HAVE_LIBGPOD #ifdef HAVE_LIBGPOD
void InitFromItdb(const Itdb_Track* track); void InitFromItdb(const Itdb_Track* track, const QString& prefix);
void ToItdb(Itdb_Track* track) const; void ToItdb(Itdb_Track* track) const;
#endif #endif
#ifdef HAVE_LIBMTP #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; void ToMTP(LIBMTP_track_t* track) const;
#endif #endif
@ -197,10 +198,7 @@ class Song {
int samplerate() const { return d->samplerate_; } int samplerate() const { return d->samplerate_; }
int directory_id() const { return d->directory_id_; } int directory_id() const { return d->directory_id_; }
const QString& filename() const { return d->filename_; } const QUrl& url() const { return d->url_; }
// Returns this Song's URL which may point either to a file or to another type
// of stream.
QUrl url() const;
const QString& basefilename() const { return d->basefilename_; } const QString& basefilename() const { return d->basefilename_; }
uint mtime() const { return d->mtime_; } uint mtime() const { return d->mtime_; }
uint ctime() const { return d->ctime_; } uint ctime() const { return d->ctime_; }
@ -277,7 +275,7 @@ class Song {
void set_cue_path(const QString& v) { d->cue_path_ = v; } void set_cue_path(const QString& v) { d->cue_path_ = v; }
// Setters that should only be used by tests // 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_basefilename(const QString& v) { d->basefilename_ = v; }
void set_directory_id(int v) { d->directory_id_ = v; } void set_directory_id(int v) { d->directory_id_ = v; }
@ -342,7 +340,7 @@ class Song {
int samplerate_; int samplerate_;
int directory_id_; int directory_id_;
QString filename_; QUrl url_;
QString basefilename_; QString basefilename_;
int mtime_; int mtime_;
int ctime_; int ctime_;

View File

@ -193,7 +193,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename, bool block,
void SongLoader::EffectiveSongsLoad() { void SongLoader::EffectiveSongsLoad() {
for (int i = 0; i < songs_.size(); i++) { for (int i = 0; i < songs_.size(); i++) {
Song& song = songs_[i]; Song& song = songs_[i];
QString filename = song.filename(); QString filename = song.url().toLocalFile();
QFileInfo info(filename); QFileInfo info(filename);
LibraryQuery query; LibraryQuery query;
@ -233,7 +233,7 @@ static bool CompareSongs(const Song& left, const Song& right) {
if (left.disc() > right.disc()) return false; if (left.disc() > right.disc()) return false;
if (left.track() < right.track()) return true; if (left.track() < right.track()) return true;
if (left.track() > right.track()) return false; if (left.track() > right.track()) return false;
return left.filename() < right.filename(); return left.url() < right.url();
} }
void SongLoader::LoadLocalDirectoryAndEmit(const QString& filename) { void SongLoader::LoadLocalDirectoryAndEmit(const QString& filename) {
@ -256,7 +256,7 @@ void SongLoader::AddAsRawStream() {
Song song; Song song;
song.set_valid(true); song.set_valid(true);
song.set_filetype(Song::Type_Stream); song.set_filetype(Song::Type_Stream);
song.set_filename(url_.toString()); song.set_url(url_);
song.set_title(url_.toString()); song.set_title(url_.toString());
songs_ << song; songs_ << song;
} }

View File

@ -233,5 +233,5 @@ void AlbumCoverLoader::SetDefaultOutputImage(const QImage &image) {
quint64 AlbumCoverLoader::LoadImageAsync(const Song &song) { quint64 AlbumCoverLoader::LoadImageAsync(const Song &song) {
return LoadImageAsync(song.art_automatic(), song.art_manual(), return LoadImageAsync(song.art_automatic(), song.art_manual(),
song.filename(), song.image()); song.url().toLocalFile(), song.image());
} }

View File

@ -159,7 +159,7 @@ void AfcDevice::FinaliseDatabase() {
} }
bool AfcDevice::DeleteFromStorage(const DeleteJob& job) { bool AfcDevice::DeleteFromStorage(const DeleteJob& job) {
const QString path = QUrl(job.metadata_.filename()).path(); const QString path = job.metadata_.url().toLocalFile();
if (!RemoveTrackFromITunesDb(path)) if (!RemoveTrackFromITunesDb(path))
return false; return false;

View File

@ -79,7 +79,7 @@ void ConnectedDevice::InitBackendDirectory(
if (dir.path != mount_point) { if (dir.path != mount_point) {
// The directory is different, commence the munging. // The directory is different, commence the munging.
qLog(Info) << "Changing path from" << dir.path << "to" << mount_point; qLog(Info) << "Changing path from" << dir.path << "to" << mount_point;
backend_->ChangeDirPath(dir.id, mount_point); backend_->ChangeDirPath(dir.id, dir.path, mount_point);
} }
} }

View File

@ -402,7 +402,7 @@ void DeviceView::Organise() {
SongList songs = GetSelectedSongs(); SongList songs = GetSelectedSongs();
QStringList filenames; QStringList filenames;
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
filenames << song.filename(); filenames << song.url().toLocalFile();
} }
organise_dialog_->SetCopy(true); organise_dialog_->SetCopy(true);

View File

@ -99,9 +99,8 @@ Itdb_Track* GPodDevice::AddTrackToITunesDb(const Song& metadata) {
void GPodDevice::AddTrackToModel(Itdb_Track* track, const QString& prefix) { void GPodDevice::AddTrackToModel(Itdb_Track* track, const QString& prefix) {
// Add it to our LibraryModel // Add it to our LibraryModel
Song metadata_on_device; 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_directory_id(1);
metadata_on_device.set_filename(prefix + metadata_on_device.filename());
songs_to_add_ << metadata_on_device; 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) { bool GPodDevice::DeleteFromStorage(const DeleteJob& job) {
Q_ASSERT(db_); Q_ASSERT(db_);
if (!RemoveTrackFromITunesDb(job.metadata_.filename(), url_.path())) if (!RemoveTrackFromITunesDb(job.metadata_.url().toLocalFile(), url_.path()))
return false; return false;
// Remove the file // Remove the file
if (!QFile::remove(job.metadata_.filename())) if (!QFile::remove(job.metadata_.url().toLocalFile()))
return false; return false;
// Remove it from our library model // Remove it from our library model

View File

@ -66,20 +66,17 @@ void GPodLoader::LoadDatabase() {
} }
// Convert all the tracks from libgpod structs into Song classes // Convert all the tracks from libgpod structs into Song classes
const QString prefix = path_prefix_.isEmpty()
? QDir::fromNativeSeparators(mount_point_) : path_prefix_;
SongList songs; SongList songs;
for (GList* tracks = db->tracks ; tracks != NULL ; tracks = tracks->next) { for (GList* tracks = db->tracks ; tracks != NULL ; tracks = tracks->next) {
Itdb_Track* track = static_cast<Itdb_Track*>(tracks->data); Itdb_Track* track = static_cast<Itdb_Track*>(tracks->data);
Song song; Song song;
song.InitFromItdb(track); song.InitFromItdb(track, prefix);
song.set_directory_id(1); 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) if (type_ != Song::Type_Unknown)
song.set_filetype(type_); song.set_filetype(type_);
songs << song; songs << song;

View File

@ -215,7 +215,7 @@ QString iMobileDeviceConnection::GetUnusedFilename(
} }
// Use the same file extension as the original file, default to mp3. // 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()) if (extension.isEmpty())
extension = "mp3"; extension = "mp3";

View File

@ -112,9 +112,8 @@ bool MtpDevice::CopyToStorage(const CopyJob& job) {
// Add it to our LibraryModel // Add it to our LibraryModel
Song metadata_on_device; 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_directory_id(1);
metadata_on_device.set_filename("mtp://" + url_.host() + "/" + metadata_on_device.filename());
songs_to_add_ << metadata_on_device; songs_to_add_ << metadata_on_device;
// Remove the original if requested // Remove the original if requested
@ -150,8 +149,7 @@ void MtpDevice::StartDelete() {
bool MtpDevice::DeleteFromStorage(const DeleteJob& job) { bool MtpDevice::DeleteFromStorage(const DeleteJob& job) {
// Extract the ID from the song's URL // Extract the ID from the song's URL
QUrl url(job.metadata_.filename()); QString filename = job.metadata_.url().path();
QString filename = url.path();
filename.remove('/'); filename.remove('/');
bool ok = false; bool ok = false;

View File

@ -64,9 +64,8 @@ bool MtpLoader::TryLoad() {
LIBMTP_track_t* track = tracks; LIBMTP_track_t* track = tracks;
Song song; Song song;
song.InitFromMTP(track); song.InitFromMTP(track, url_.host());
song.set_directory_id(1); song.set_directory_id(1);
song.set_filename("mtp://" + url_.host() + "/" + song.filename());
songs << song; songs << song;
tracks = tracks->next; tracks = tracks->next;

View File

@ -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()); QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect()); QSqlDatabase db(db_->Connect());
ScopedTransaction t(&db); ScopedTransaction t(&db);
@ -101,12 +102,15 @@ void LibraryBackend::ChangeDirPath(int id, const QString &new_path) {
q.exec(); q.exec();
if (db_->CheckErrors(q)) return; 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 // Do the subdirs table
q = QSqlQuery(QString("UPDATE %1 SET path=:path || substr(path, %2)" q = QSqlQuery(QString("UPDATE %1 SET path=:path || substr(path, %2)"
" WHERE directory=:id").arg(subdirs_table_).arg(path_len), db); " 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.bindValue(":id", id);
q.exec(); q.exec();
if (db_->CheckErrors(q)) return; if (db_->CheckErrors(q)) return;
@ -114,7 +118,7 @@ void LibraryBackend::ChangeDirPath(int id, const QString &new_path) {
// Do the songs table // Do the songs table
q = QSqlQuery(QString("UPDATE %1 SET filename=:path || substr(filename, %2)" q = QSqlQuery(QString("UPDATE %1 SET filename=:path || substr(filename, %2)"
" WHERE directory=:id").arg(songs_table_).arg(path_len), db); " 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.bindValue(":id", id);
q.exec(); q.exec();
if (db_->CheckErrors(q)) return; if (db_->CheckErrors(q)) return;
@ -564,10 +568,10 @@ SongList LibraryBackend::GetSongsById(const QStringList& ids, QSqlDatabase& db)
return ret; return ret;
} }
Song LibraryBackend::GetSongByFilename(const QString& filename, qint64 beginning) { Song LibraryBackend::GetSongByUrl(const QUrl& url, qint64 beginning) {
LibraryQuery query; LibraryQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec); query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("filename", filename); query.AddWhere("filename", url.toEncoded());
query.AddWhere("beginning", beginning); query.AddWhere("beginning", beginning);
Song song; Song song;
@ -577,10 +581,10 @@ Song LibraryBackend::GetSongByFilename(const QString& filename, qint64 beginning
return song; return song;
} }
SongList LibraryBackend::GetSongsByFilename(const QString& filename) { SongList LibraryBackend::GetSongsByUrl(const QUrl& url) {
LibraryQuery query; LibraryQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec); query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("filename", filename); query.AddWhere("filename", url.toEncoded());
SongList songlist; SongList songlist;
if (ExecQuery(&query)) { if (ExecQuery(&query)) {
@ -754,7 +758,7 @@ LibraryBackend::AlbumList LibraryBackend::GetAlbums(const QString& artist,
info.album_name = query.Value(0).toString(); info.album_name = query.Value(0).toString();
info.art_automatic = query.Value(4).toString(); info.art_automatic = query.Value(4).toString();
info.art_manual = query.Value(5).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) if (info.artist == last_artist && info.album_name == last_album)
continue; continue;
@ -784,7 +788,7 @@ LibraryBackend::Album LibraryBackend::GetAlbumArt(const QString& artist, const Q
if (query.Next()) { if (query.Next()) {
ret.art_automatic = query.Value(0).toString(); ret.art_automatic = query.Value(0).toString();
ret.art_manual = query.Value(1).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; return ret;

View File

@ -40,17 +40,17 @@ class LibraryBackendInterface : public QObject {
Album() {} Album() {}
Album(const QString& _artist, const QString& _album_name, Album(const QString& _artist, const QString& _album_name,
const QString& _art_automatic, const QString& _art_manual, const QString& _art_automatic, const QString& _art_manual,
const QString& _first_filename) const QUrl& _first_url)
: artist(_artist), album_name(_album_name), : artist(_artist), album_name(_album_name),
art_automatic(_art_automatic), art_manual(_art_manual), art_automatic(_art_automatic), art_manual(_art_manual),
first_filename(_first_filename) {} first_url(_first_url) {}
QString artist; QString artist;
QString album_name; QString album_name;
QString art_automatic; QString art_automatic;
QString art_manual; QString art_manual;
QString first_filename; QUrl first_url;
}; };
typedef QList<Album> AlbumList; typedef QList<Album> AlbumList;
@ -63,7 +63,7 @@ class LibraryBackendInterface : public QObject {
virtual SongList FindSongsInDirectory(int id) = 0; virtual SongList FindSongsInDirectory(int id) = 0;
virtual SubdirectoryList SubdirsInDirectory(int id) = 0; virtual SubdirectoryList SubdirsInDirectory(int id) = 0;
virtual DirectoryList GetAllDirectories() = 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 GetAllArtists(const QueryOptions& opt = QueryOptions()) = 0;
virtual QStringList GetAllArtistsWithAlbums(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 // 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. // 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 // Returns a section of a song with the given filename and beginning. If the section
// is not present in library, returns invalid song. // is not present in library, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs. // 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 AddDirectory(const QString& path) = 0;
virtual void RemoveDirectory(const Directory& dir) = 0; virtual void RemoveDirectory(const Directory& dir) = 0;
@ -120,7 +120,7 @@ class LibraryBackend : public LibraryBackendInterface {
SongList FindSongsInDirectory(int id); SongList FindSongsInDirectory(int id);
SubdirectoryList SubdirsInDirectory(int id); SubdirectoryList SubdirsInDirectory(int id);
DirectoryList GetAllDirectories(); 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 GetAll(const QString& column, const QueryOptions& opt = QueryOptions());
QStringList GetAllArtists(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, SongList GetSongsByForeignId(const QStringList& ids, const QString& table,
const QString& column); const QString& column);
SongList GetSongsByFilename(const QString& filename); SongList GetSongsByUrl(const QUrl& url);
Song GetSongByFilename(const QString& filename, qint64 beginning = 0); Song GetSongByUrl(const QUrl& url, qint64 beginning = 0);
void AddDirectory(const QString& path); void AddDirectory(const QString& path);
void RemoveDirectory(const Directory& dir); void RemoveDirectory(const Directory& dir);

View File

@ -392,7 +392,7 @@ QVariant LibraryModel::AlbumIcon(const QModelIndex& index, int role) const {
if (!songs.isEmpty()) { if (!songs.isEmpty()) {
const Song& s = songs.first(); const Song& s = songs.first();
QPixmap pixmap = AlbumCoverLoader::TryLoadPixmap( QPixmap pixmap = AlbumCoverLoader::TryLoadPixmap(
s.art_automatic(), s.art_manual(), s.filename()); s.art_automatic(), s.art_manual(), s.url().toLocalFile());
if (!pixmap.isNull()) { if (!pixmap.isNull()) {
QImage image = pixmap.toImage().scaled( QImage image = pixmap.toImage().scaled(
@ -931,7 +931,7 @@ QString LibraryModel::SortTextForYear(int year) const {
QString LibraryModel::SortTextForSong(const Song& song) const { QString LibraryModel::SortTextForSong(const Song& song) const {
QString ret = QString::number(qMax(0, song.disc()) * 1000 + qMax(0, song.track())); QString ret = QString::number(qMax(0, song.disc()) * 1000 + qMax(0, song.track()));
ret.prepend(QString("0").repeated(6 - ret.length())); ret.prepend(QString("0").repeated(6 - ret.length()));
ret.append(song.filename()); ret.append(song.url().toString());
return ret; return ret;
} }
@ -1012,7 +1012,7 @@ void LibraryModel::GetChildSongs(LibraryItem* item, QList<QUrl>* urls,
} }
case LibraryItem::Type_Song: case LibraryItem::Type_Song:
urls->append(QUrl::fromLocalFile(item->metadata.filename())); urls->append(item->metadata.url());
if (!song_ids->contains(item->metadata.id())) { if (!song_ids->contains(item->metadata.id())) {
songs->append(item->metadata); songs->append(item->metadata);
song_ids->insert(item->metadata.id()); song_ids->insert(item->metadata.id());

View File

@ -32,11 +32,11 @@ LibraryPlaylistItem::LibraryPlaylistItem(const Song& song)
QUrl LibraryPlaylistItem::Url() const { QUrl LibraryPlaylistItem::Url() const {
return QUrl::fromLocalFile(song_.filename()); return song_.url();
} }
void LibraryPlaylistItem::Reload() { void LibraryPlaylistItem::Reload() {
song_.InitFromFile(song_.filename(), song_.directory_id()); song_.InitFromFile(song_.url().toLocalFile(), song_.directory_id());
} }
bool LibraryPlaylistItem::InitFromQuery(const SqlRow& query) { bool LibraryPlaylistItem::InitFromQuery(const SqlRow& query) {

View File

@ -550,7 +550,7 @@ void LibraryView::EditSmartPlaylistFinished() {
void LibraryView::ShowInBrowser() { void LibraryView::ShowInBrowser() {
QStringList filenames; QStringList filenames;
foreach (const Song& song, GetSelectedSongs()) { foreach (const Song& song, GetSelectedSongs()) {
filenames << song.filename(); filenames << song.url().toLocalFile();
} }
Utilities::OpenInFileBrowser(filenames); Utilities::OpenInFileBrowser(filenames);

View File

@ -132,7 +132,7 @@ SongList LibraryWatcher::ScanTransaction::FindSongsInSubdirectory(const QString
// TODO: Make this faster // TODO: Make this faster
SongList ret; SongList ret;
foreach (const Song& song, cached_songs_) { foreach (const Song& song, cached_songs_) {
if (song.filename().section('/', 0, -2) == path) if (song.url().toLocalFile().section('/', 0, -2) == path)
ret << song; ret << song;
} }
return ret; return ret;
@ -363,8 +363,8 @@ void LibraryWatcher::ScanSubdirectory(
// Look for deleted songs // Look for deleted songs
foreach (const Song& song, songs_in_db) { foreach (const Song& song, songs_in_db) {
if (!files_on_disk.contains(song.filename())) { if (!files_on_disk.contains(song.url().toLocalFile())) {
qLog(Debug) << "Song deleted from disk:" << song.filename(); qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile();
t->deleted_songs << song; t->deleted_songs << song;
} }
} }
@ -397,7 +397,7 @@ void LibraryWatcher::UpdateCueAssociatedSongs(const QString& file, const QString
QFile cue(matching_cue); QFile cue(matching_cue);
cue.open(QIODevice::ReadOnly); cue.open(QIODevice::ReadOnly);
SongList old_sections = backend_->GetSongsByFilename(file); SongList old_sections = backend_->GetSongsByUrl(QUrl::fromLocalFile(file));
QHash<quint64, Song> sections_map; QHash<quint64, Song> sections_map;
foreach(const Song& song, old_sections) { 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 // 'raw' (cueless) song and we just remove the rest of the sections
// from the library // from the library
if(cue_deleted) { if(cue_deleted) {
foreach(const Song& song, backend_->GetSongsByFilename(file)) { foreach(const Song& song, backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
if(!song.IsMetadataEqual(matching_song)) { if(!song.IsMetadataEqual(matching_song)) {
t->deleted_songs << 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 // media files. Playlist parser for CUEs considers every entry in sheet
// valid and we don't want invalid media getting into library! // valid and we don't want invalid media getting into library!
foreach(const Song& cue_song, cue_parser_->Load(&cue, matching_cue, path)) { 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; 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) { bool LibraryWatcher::FindSongByPath(const SongList& list, const QString& path, Song* out) {
// TODO: Make this faster // TODO: Make this faster
foreach (const Song& song, list) { foreach (const Song& song, list) {
if (song.filename() == path) { if (song.url().toLocalFile() == path) {
*out = song; *out = song;
return true; return true;
} }

View File

@ -35,7 +35,7 @@ TagFetcher::TagFetcher(QObject* parent)
} }
QString TagFetcher::GetFingerprint(const Song& song) { QString TagFetcher::GetFingerprint(const Song& song) {
return Fingerprinter(song.filename()).CreateFingerprint(); return Fingerprinter(song.url().toLocalFile()).CreateFingerprint();
} }
void TagFetcher::StartFetch(const SongList& songs) { void TagFetcher::StartFetch(const SongList& songs) {

View File

@ -274,7 +274,7 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
case Column_BPM: return song.bpm(); case Column_BPM: return song.bpm();
case Column_Bitrate: return song.bitrate(); case Column_Bitrate: return song.bitrate();
case Column_Samplerate: return song.samplerate(); 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_BaseFilename: return song.basefilename();
case Column_Filesize: return song.filesize(); case Column_Filesize: return song.filesize();
case Column_Filetype: return song.filetype(); case Column_Filetype: return song.filetype();
@ -958,7 +958,7 @@ void Playlist::UpdateItems(const SongList& songs) {
// Update current items list // Update current items list
for (int i=0; i<items_.size(); i++) { for (int i=0; i<items_.size(); i++) {
PlaylistItemPtr item = items_[i]; PlaylistItemPtr item = items_[i];
if (item->Metadata().filename() == song.filename()) { if (item->Metadata().url() == song.url()) {
PlaylistItemPtr new_item; PlaylistItemPtr new_item;
if (song.id() == -1) { if (song.id() == -1) {
new_item = PlaylistItemPtr(new SongPlaylistItem(song)); 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_BPM: cmp(bpm);
case Column_Bitrate: cmp(bitrate); case Column_Bitrate: cmp(bitrate);
case Column_Samplerate: cmp(samplerate); case Column_Samplerate: cmp(samplerate);
case Column_Filename: cmp(filename); case Column_Filename: cmp(url);
case Column_BaseFilename: cmp(basefilename); case Column_BaseFilename: cmp(basefilename);
case Column_Filesize: cmp(filesize); case Column_Filesize: cmp(filesize);
case Column_Filetype: cmp(filetype); case Column_Filetype: cmp(filetype);
@ -1186,7 +1186,7 @@ void Playlist::ItemsLoaded() {
while (it.hasNext()) { while (it.hasNext()) {
PlaylistItemPtr item = it.next(); PlaylistItemPtr item = it.next();
if (item->IsLocalLibraryItem() && item->Metadata().filename().isEmpty()) { if (item->IsLocalLibraryItem() && item->Metadata().url().isEmpty()) {
it.remove(); it.remove();
} }
} }
@ -1684,7 +1684,7 @@ void Playlist::InvalidateDeletedSongs() {
Song song = item->Metadata(); Song song = item->Metadata();
if(!song.is_stream()) { if(!song.is_stream()) {
bool exists = QFile::exists(song.filename()); bool exists = QFile::exists(song.url().toLocalFile());
if(!exists && !item->HasForegroundColor(kInvalidSongPriority)) { if(!exists && !item->HasForegroundColor(kInvalidSongPriority)) {
// gray out the song if it's not there // gray out the song if it's not there
@ -1707,7 +1707,7 @@ void Playlist::RemoveDeletedSongs() {
PlaylistItemPtr item = items_[row]; PlaylistItemPtr item = items_[row];
Song song = item->Metadata(); 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); 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 validity has changed, reload the item
if(!current_song.is_stream() && if(!current_song.is_stream() &&
current_song.filename() == url.toLocalFile() && current_song.url() == url &&
current_song.is_valid() != QFile::exists(current_song.filename())) { current_song.is_valid() != QFile::exists(current_song.url().toLocalFile())) {
ReloadItems(QList<int>() << current_row()); ReloadItems(QList<int>() << current_row());
} }

View File

@ -192,7 +192,7 @@ PlaylistItemPtr PlaylistBackend::RestoreCueData(PlaylistItemPtr item, boost::sha
} }
foreach(const Song& from_list, song_list) { 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()) { from_list.beginning_nanosec() == song.beginning_nanosec()) {
// we found a matching section; replace the input // we found a matching section; replace the input
// item with a new one containing CUE metadata // item with a new one containing CUE metadata

View File

@ -50,7 +50,7 @@ void InsertItems::undo() {
bool InsertItems::UpdateItem(const PlaylistItemPtr& updated_item) { bool InsertItems::UpdateItem(const PlaylistItemPtr& updated_item) {
for (int i=0; i<items_.size(); i++) { for (int i=0; i<items_.size(); i++) {
PlaylistItemPtr item = items_[i]; PlaylistItemPtr item = items_[i];
if (item->Metadata().filename() == updated_item->Metadata().filename()) { if (item->Metadata().url() == updated_item->Metadata().url()) {
items_[i] = updated_item; items_[i] = updated_item;
return true; return true;
} }

View File

@ -52,7 +52,7 @@ QUrl SongPlaylistItem::Url() const {
} }
void SongPlaylistItem::Reload() { void SongPlaylistItem::Reload() {
QString old_filename = song_.filename(); QString old_filename = song_.url().toLocalFile();
int old_directory_id = song_.directory_id(); int old_directory_id = song_.directory_id();
song_ = Song(); song_ = Song();

View File

@ -40,16 +40,8 @@ SongList AsxIniParser::Load(QIODevice *device, const QString& playlist_path, con
QString value = line.mid(equals + 1); QString value = line.mid(equals + 1);
if (key.startsWith("ref")) { if (key.startsWith("ref")) {
Song song; Song song = LoadSong(value, 0, dir);
if (!ParseTrackLocation(value, dir, &song)) if (song.is_valid()) {
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);
ret << song; ret << song;
} }
} }
@ -64,7 +56,7 @@ void AsxIniParser::Save(const SongList &songs, QIODevice *device, const QDir &di
int n = 1; int n = 1;
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
s << "Ref" << n << "=" << MakeRelativeTo(song.filename(), dir) << endl; s << "Ref" << n << "=" << URLOrRelativeFilename(song.url(), dir) << endl;
++n; ++n;
} }
} }

View File

@ -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". // We have to load everything first so we can munge the "XML".
QByteArray data = device->readAll(); QByteArray data = device->readAll();
@ -70,7 +71,7 @@ SongList ASXParser::Load(QIODevice *device, const QString& playlist_path, const
} }
while (!reader.atEnd() && ParseUntilElement(&reader, "entry")) { while (!reader.atEnd() && ParseUntilElement(&reader, "entry")) {
Song song = ParseTrack(&reader); Song song = ParseTrack(&reader, dir);
if (song.is_valid()) { if (song.is_valid()) {
ret << song; ret << song;
} }
@ -79,9 +80,9 @@ SongList ASXParser::Load(QIODevice *device, const QString& playlist_path, const
} }
Song ASXParser::ParseTrack(QXmlStreamReader* reader) const { Song ASXParser::ParseTrack(QXmlStreamReader* reader, const QDir& dir) const {
Song song; QString title, artist, album, ref;
QString title, artist, album;
while (!reader->atEnd()) { while (!reader->atEnd()) {
QXmlStreamReader::TokenType type = reader->readNext(); QXmlStreamReader::TokenType type = reader->readNext();
@ -89,24 +90,7 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
QStringRef name = reader->name(); QStringRef name = reader->name();
if (name == "ref") { if (name == "ref") {
QUrl url(reader->attributes().value("href").toString()); ref = 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);
}
} else if (name == "title") { } else if (name == "title") {
title = reader->readElementText(); title = reader->readElementText();
} else if (name == "author") { } else if (name == "author") {
@ -116,8 +100,7 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const {
} }
case QXmlStreamReader::EndElement: { case QXmlStreamReader::EndElement: {
if (reader->name() == "entry") { if (reader->name() == "entry") {
song.Init(title, artist, album, -1); goto return_song;
return song;
} }
break; break;
} }
@ -125,8 +108,14 @@ Song ASXParser::ParseTrack(QXmlStreamReader* reader) const {
break; break;
} }
} }
// At least make an effort if we never find a </entry>.
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; return song;
} }
@ -142,7 +131,7 @@ void ASXParser::Save(const SongList& songs, QIODevice* device, const QDir&) cons
writer.writeTextElement("title", song.title()); writer.writeTextElement("title", song.title());
{ {
StreamElement ref("ref", &writer); StreamElement ref("ref", &writer);
writer.writeAttribute("href", MakeUrl(song.filename())); writer.writeAttribute("href", song.url().toString());
} }
if (!song.artist().isEmpty()) { if (!song.artist().isEmpty()) {
writer.writeTextElement("author", song.artist()); writer.writeTextElement("author", song.artist());

View File

@ -31,11 +31,12 @@ class ASXParser : public XMLParser {
bool TryMagic(const QByteArray &data) const; 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; void Save(const SongList &songs, QIODevice *device, const QDir &dir = QDir()) const;
private: private:
Song ParseTrack(QXmlStreamReader* reader) const; Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const;
}; };
#endif #endif

View File

@ -207,46 +207,37 @@ SongList CueParser::Load(QIODevice* device, const QString& playlist_path, const
for(int i = 0; i < entries.length(); i++) { for(int i = 0; i < entries.length(); i++) {
CueEntry entry = entries.at(i); CueEntry entry = entries.at(i);
Song current; Song song = LoadSong(entry.file, IndexToMarker(entry.index), dir);
if (!ParseTrackLocation(entry.file, dir, &current)) {
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);
}
// cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime) // cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
if(cue_mtime.isValid()) { if(cue_mtime.isValid()) {
song.set_mtime(qMax(cue_mtime.toTime_t(), song.mtime())); 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;
} }
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; return ret;

View File

@ -56,29 +56,13 @@ SongList M3UParser::Load(QIODevice* device, const QString& playlist_path, const
} }
} }
} else if (!line.isEmpty()) { } 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. current_metadata = Metadata();
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;
}
} }
if (buffer.atEnd()) { if (buffer.atEnd()) {
break; 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 { void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
device->write("#EXTM3U\n"); device->write("#EXTM3U\n");
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
if (song.filename().isEmpty()) { if (song.url().isEmpty()) {
continue; continue;
} }
QString meta = QString("#EXTINF:%1,%2 - %3\n") QString meta = QString("#EXTINF:%1,%2 - %3\n")
.arg(song.length_nanosec() / kNsecPerSec) .arg(song.length_nanosec() / kNsecPerSec)
.arg(song.artist()).arg(song.title()); .arg(song.artist()).arg(song.title());
device->write(meta.toUtf8()); device->write(meta.toUtf8());
device->write(MakeRelativeTo(song.filename(), dir).toUtf8()); device->write(URLOrRelativeFilename(song.url(), dir).toUtf8());
device->write("\n"); device->write("\n");
} }
} }

View File

@ -28,67 +28,72 @@ ParserBase::ParserBase(LibraryBackendInterface* library, QObject *parent)
{ {
} }
bool ParserBase::ParseTrackLocation(const QString& filename_or_url, void ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning,
const QDir& dir, Song* song) const { 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]+://"))) { if (filename_or_url.contains(QRegExp("^[a-z]+://"))) {
// Looks like a url. QUrl url(filename_or_url);
QUrl temp(filename_or_url); if (url.scheme() == "file") {
if (temp.isValid()) { filename = url.toLocalFile();
song->set_filename(temp.toString()); } else {
song->set_url(QUrl(filename_or_url));
song->set_filetype(Song::Type_Stream); song->set_filetype(Song::Type_Stream);
song->set_valid(true); song->set_valid(true);
return true; return;
} else {
return false;
} }
} }
// Should be a local path. // Convert native separators for Windows paths
if (QDir::isAbsolutePath(filename_or_url)) { filename = QDir::fromNativeSeparators(filename);
// Absolute path.
// Fix windows \, eg. C:\foo -> C:/foo. // Make the path absolute
song->set_filename(QDir::fromNativeSeparators(filename_or_url)); if (!QDir::isAbsolutePath(filename)) {
} else { filename = dir.absoluteFilePath(filename);
// Relative path. }
QString proper_path = QDir::fromNativeSeparators(filename_or_url);
QString absolute_path = dir.absoluteFilePath(proper_path); // Use the canonical path
song->set_filename(absolute_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, Song ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir) const {
const QDir& dir) const { Song song;
if (filename_or_url.contains(QRegExp("^[a-z]+://"))) LoadSong(filename_or_url, beginning, dir, &song);
return filename_or_url; return song;
}
if (QDir::isAbsolutePath(filename_or_url)) { QString ParserBase::URLOrRelativeFilename(const QUrl& url, const QDir& dir) const {
QString relative = dir.relativeFilePath(filename_or_url); 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("..")) if (!relative.contains(".."))
return relative; return relative;
} }
return filename_or_url; return filename;
}
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);
} }

View File

@ -48,22 +48,19 @@ public:
virtual void Save(const SongList& songs, QIODevice* device, const QDir& dir = QDir()) const = 0; virtual void Save(const SongList& songs, QIODevice* device, const QDir& dir = QDir()) const = 0;
protected: protected:
// Takes a URL, relative path or absolute path, and returns an absolute path. // Loads a song. If filename_or_url is a URL (with a scheme other than
// Resolves relative paths to "dir". // "file") then it is set on the song and the song marked as a stream.
bool ParseTrackLocation(const QString& filename_or_url, const QDir& dir, // If it is a filename or a file:// URL then it is made absolute and canonical
Song* song) const; // 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 // If the URL is a file:// URL then returns its path relative to the
// paths makes them relative to dir if they are subdirectories. // directory. Otherwise returns the URL as is.
QString MakeRelativeTo(const QString& filename_or_url, const QDir& dir) const; // This function should always be used when saving a playlist.
QString URLOrRelativeFilename(const QUrl& 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;
private: private:
LibraryBackendInterface* library_; LibraryBackendInterface* library_;

View File

@ -40,16 +40,15 @@ SongList PLSParser::Load(QIODevice *device, const QString& playlist_path, const
int n = n_re.cap(0).toInt(); int n = n_re.cap(0).toInt();
if (key.startsWith("file")) { if (key.startsWith("file")) {
if (!ParseTrackLocation(value, dir, &songs[n])) Song song = LoadSong(value, 0, dir);
qLog(Warning) << "Failed to parse location: " << value;
// Load the song from the library if it's there. // Use the title and length we've already loaded if any
Song library_song = LoadLibrarySong(songs[n].filename()); if (!songs[n].title().isEmpty())
if (library_song.is_valid()) { song.set_title(songs[n].title());
songs[n] = library_song; if (!songs[n].length_nanosec() != -1)
} else { song.set_length_nanosec(songs[n].length_nanosec());
songs[n].InitFromFile(songs[n].filename(), -1);
} songs[n] = song;
} else if (key.startsWith("title")) { } else if (key.startsWith("title")) {
songs[n].set_title(value); songs[n].set_title(value);
} else if (key.startsWith("length")) { } else if (key.startsWith("length")) {
@ -71,7 +70,7 @@ void PLSParser::Save(const SongList &songs, QIODevice *device, const QDir &dir)
int n = 1; int n = 1;
foreach (const Song& song, songs) { 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 << "Title" << n << "=" << song.title() << endl;
s << "Length" << n << "=" << song.length_nanosec() / kNsecPerSec << endl; s << "Length" << n << "=" << song.length_nanosec() / kNsecPerSec << endl;
++n; ++n;

View File

@ -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; SongList ret;
QXmlStreamReader reader(device); QXmlStreamReader reader(device);
@ -39,7 +40,7 @@ SongList XSPFParser::Load(QIODevice *device, const QString& playlist_path, const
} }
while (!reader.atEnd() && ParseUntilElement(&reader, "track")) { while (!reader.atEnd() && ParseUntilElement(&reader, "track")) {
Song song = ParseTrack(&reader); Song song = ParseTrack(&reader, dir);
if (song.is_valid()) { if (song.is_valid()) {
ret << song; ret << song;
} }
@ -47,34 +48,17 @@ SongList XSPFParser::Load(QIODevice *device, const QString& playlist_path, const
return ret; return ret;
} }
Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const { Song XSPFParser::ParseTrack(QXmlStreamReader* reader, const QDir& dir) const {
Song song; QString title, artist, album, location;
QString title, artist, album;
qint64 nanosec = -1; qint64 nanosec = -1;
while (!reader->atEnd()) { while (!reader->atEnd()) {
QXmlStreamReader::TokenType type = reader->readNext(); QXmlStreamReader::TokenType type = reader->readNext();
switch (type) { switch (type) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
QStringRef name = reader->name(); QStringRef name = reader->name();
if (name == "location") { if (name == "location") {
QUrl url(reader->readElementText()); location = 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);
}
} else if (name == "title") { } else if (name == "title") {
title = reader->readElementText(); title = reader->readElementText();
} else if (name == "creator") { } else if (name == "creator") {
@ -82,7 +66,7 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const {
} else if (name == "album") { } else if (name == "album") {
album = reader->readElementText(); album = reader->readElementText();
} else if (name == "duration") { // in milliseconds. } else if (name == "duration") { // in milliseconds.
const QString& duration = reader->readElementText(); const QString duration = reader->readElementText();
bool ok = false; bool ok = false;
nanosec = duration.toInt(&ok) * kNsecPerMsec; nanosec = duration.toInt(&ok) * kNsecPerMsec;
if (!ok) { if (!ok) {
@ -97,16 +81,22 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader) const {
} }
case QXmlStreamReader::EndElement: { case QXmlStreamReader::EndElement: {
if (reader->name() == "track") { if (reader->name() == "track") {
song.Init(title, artist, album, nanosec); goto return_song;
return song;
} }
} }
default: default:
break; break;
} }
} }
// At least make an effort if we never find a </track>.
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; return song;
} }
@ -120,7 +110,7 @@ void XSPFParser::Save(const SongList& songs, QIODevice* device, const QDir&) con
StreamElement tracklist("trackList", &writer); StreamElement tracklist("trackList", &writer);
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
StreamElement track("track", &writer); StreamElement track("track", &writer);
writer.writeTextElement("location", MakeUrl(song.filename())); writer.writeTextElement("location", song.url().toString());
writer.writeTextElement("title", song.title()); writer.writeTextElement("title", song.title());
if (!song.artist().isEmpty()) { if (!song.artist().isEmpty()) {
writer.writeTextElement("creator", song.artist()); 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. // Ignore images that are in our resource bundle.
if (!art.startsWith(":") && !art.isEmpty()) { if (!art.startsWith(":") && !art.isEmpty()) {
// Convert local files to URLs. // Convert local files to URLs.
art = MakeUrl(art); if (!art.contains("://")) {
art = QUrl::fromLocalFile(art).toString();
}
writer.writeTextElement("image", art); writer.writeTextElement("image", art);
} }
} }

View File

@ -36,11 +36,12 @@ class XSPFParser : public XMLParser {
bool TryMagic(const QByteArray &data) const; 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; void Save(const SongList &songs, QIODevice *device, const QDir &dir = QDir()) const;
private: private:
Song ParseTrack(QXmlStreamReader* reader) const; Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const;
}; };
#endif #endif

View File

@ -177,7 +177,7 @@ Song IcecastBackend::Station::ToSong() const {
Song ret; Song ret;
ret.set_valid(true); ret.set_valid(true);
ret.set_title(name); ret.set_title(name);
ret.set_filename(url.toEncoded()); ret.set_url(url);
ret.set_bitrate(bitrate); ret.set_bitrate(bitrate);
ret.set_samplerate(samplerate); ret.set_samplerate(samplerate);
ret.set_genre(genre); ret.set_genre(genre);

View File

@ -36,5 +36,5 @@ bool JamendoPlaylistItem::InitFromQuery(const SqlRow& query) {
} }
QUrl JamendoPlaylistItem::Url() const { QUrl JamendoPlaylistItem::Url() const {
return QUrl::fromEncoded(song_.filename().toAscii()); return song_.url();
} }

View File

@ -364,7 +364,7 @@ Song JamendoService::ReadTrack(const QString& artist,
continue; continue;
QString ogg_url = QString(kOggStreamUrl).arg(id_text); 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_art_automatic(album_cover);
song.set_valid(true); song.set_valid(true);

View File

@ -148,22 +148,22 @@ void LastFMService::LazyPopulate(QStandardItem* parent) {
CreateStationItem(parent, CreateStationItem(parent,
tr("My Recommendations"), tr("My Recommendations"),
":last.fm/recommended_radio.png", ":last.fm/recommended_radio.png",
"lastfm://user/USERNAME/recommended", QUrl("lastfm://user/USERNAME/recommended"),
tr("My Last.fm Recommended Radio")); tr("My Last.fm Recommended Radio"));
CreateStationItem(parent, CreateStationItem(parent,
tr("My Radio Station"), tr("My Radio Station"),
":last.fm/personal_radio.png", ":last.fm/personal_radio.png",
"lastfm://user/USERNAME/library", QUrl("lastfm://user/USERNAME/library"),
tr("My Last.fm Library")); tr("My Last.fm Library"));
CreateStationItem(parent, CreateStationItem(parent,
tr("My Mix Radio"), tr("My Mix Radio"),
":last.fm/loved_radio.png", ":last.fm/loved_radio.png",
"lastfm://user/USERNAME/mix", QUrl("lastfm://user/USERNAME/mix"),
tr("My Last.fm Mix Radio")); tr("My Last.fm Mix Radio"));
CreateStationItem(parent, CreateStationItem(parent,
tr("My Neighborhood"), tr("My Neighborhood"),
":last.fm/neighbour_radio.png", ":last.fm/neighbour_radio.png",
"lastfm://user/USERNAME/neighbours", QUrl("lastfm://user/USERNAME/neighbours"),
tr("My Last.fm Neighborhood")); tr("My Last.fm Neighborhood"));
// Types that have children // Types that have children
@ -213,17 +213,17 @@ void LastFMService::LazyPopulate(QStandardItem* parent) {
CreateStationItem(parent, CreateStationItem(parent,
tr("Last.fm Radio Station - %1").arg(parent->text()), tr("Last.fm Radio Station - %1").arg(parent->text()),
":last.fm/personal_radio.png", ":last.fm/personal_radio.png",
"lastfm://user/" + parent->text() + "/library", QUrl("lastfm://user/" + parent->text() + "/library"),
tr("Last.fm Library - %1").arg(parent->text())); tr("Last.fm Library - %1").arg(parent->text()));
CreateStationItem(parent, CreateStationItem(parent,
tr("Last.fm Mix Radio - %1").arg(parent->text()), tr("Last.fm Mix Radio - %1").arg(parent->text()),
":last.fm/loved_radio.png", ":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())); tr("Last.fm Mix Radio - %1").arg(parent->text()));
CreateStationItem(parent, CreateStationItem(parent,
tr("Last.fm Neighbor Radio - %1").arg(parent->text()), tr("Last.fm Neighbor Radio - %1").arg(parent->text()),
":last.fm/neighbour_radio.png", ":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())); tr("Last.fm Neighbor Radio - %1").arg(parent->text()));
break; break;
@ -234,9 +234,9 @@ void LastFMService::LazyPopulate(QStandardItem* parent) {
QStandardItem* LastFMService::CreateStationItem( QStandardItem* LastFMService::CreateStationItem(
QStandardItem* parent, const QString& name, const QString& icon, QStandardItem* parent, const QString& name, const QString& icon,
const QString& url, const QString& title) { const QUrl& url, const QString& title) {
Song song; Song song;
song.set_filename(url); song.set_url(url);
song.set_title(title); song.set_title(title);
QStandardItem* ret = new QStandardItem(QIcon(icon), name); QStandardItem* ret = new QStandardItem(QIcon(icon), name);
@ -625,7 +625,7 @@ void LastFMService::RefreshFriendsFinished() {
foreach (const lastfm::User& f, friends) { foreach (const lastfm::User& f, friends) {
Song song; 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())); song.set_title(tr("Last.fm Library - %1").arg(f.name()));
QStandardItem* item = new QStandardItem(QIcon(":last.fm/icon_user.png"), 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) { foreach (const lastfm::User& n, neighbours) {
Song song; 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())); song.set_title(tr("Last.fm Library - %1").arg(n.name()));
QStandardItem* item = new QStandardItem(QIcon(":last.fm/user_purple.png"), 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; url_content = content;
Song song; 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)); song.set_title(title_pattern.arg(content));
QStandardItem* item = new QStandardItem(QIcon(icon), content); QStandardItem* item = new QStandardItem(QIcon(icon), content);
@ -756,7 +756,7 @@ void LastFMService::RestoreList(const QString& name,
url_content = content; url_content = content;
Song song; 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)); song.set_title(title_pattern.arg(content));
QStandardItem* item = new QStandardItem(icon, content); QStandardItem* item = new QStandardItem(icon, content);
@ -881,7 +881,7 @@ PlaylistItemPtr LastFMService::PlaylistItemForUrl(const QUrl& url) {
QStringList sections(url.path().split("/", QString::SkipEmptyParts)); QStringList sections(url.path().split("/", QString::SkipEmptyParts));
Song song; Song song;
song.set_filename(url.toString()); song.set_url(url);
if (sections.count() == 2 && url.host() == "artist" && sections[1] == "similarartists") { if (sections.count() == 2 && url.host() == "artist" && sections[1] == "similarartists") {
song.set_title(tr(kTitleArtist).arg(sections[0])); song.set_title(tr(kTitleArtist).arg(sections[0]));

View File

@ -150,7 +150,7 @@ class LastFMService : public RadioService {
private: private:
QStandardItem* CreateStationItem(QStandardItem* parent, 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); const QString& title);
QString ErrorString(lastfm::ws::Error error) const; QString ErrorString(lastfm::ws::Error error) const;
bool InitScrobbler(); bool InitScrobbler();

View File

@ -42,7 +42,7 @@ PlaylistItem::Options MagnatunePlaylistItem::options() const {
} }
QUrl MagnatunePlaylistItem::Url() const { QUrl MagnatunePlaylistItem::Url() const {
return QUrl::fromEncoded(song_.filename().toAscii()); return song_.url();
} }
PlaylistItem::SpecialLoadResult MagnatunePlaylistItem::StartLoading() { PlaylistItem::SpecialLoadResult MagnatunePlaylistItem::StartLoading() {

View File

@ -205,7 +205,7 @@ Song MagnatuneService::ReadTrack(QXmlStreamReader& reader) {
if (name == "year") song.set_year(value.toInt()); if (name == "year") song.set_year(value.toInt());
if (name == "magnatunegenres") song.set_genre(value.section(',', 0, 0)); if (name == "magnatunegenres") song.set_genre(value.section(',', 0, 0));
if (name == "seconds") song.set_length_nanosec(value.toInt() * kNsecPerSec); 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 == "cover_small") song.set_art_automatic(value);
if (name == "albumsku") song.set_comment(value); if (name == "albumsku") song.set_comment(value);
} }

View File

@ -77,7 +77,7 @@ QVariant RadioPlaylistItem::DatabaseValue(DatabaseColumn column) const {
void RadioPlaylistItem::InitMetadata() { void RadioPlaylistItem::InitMetadata() {
if (metadata_.title().isEmpty()) if (metadata_.title().isEmpty())
metadata_.set_title(metadata_.filename()); metadata_.set_title(metadata_.url().toString());
metadata_.set_filetype(Song::Type_Stream); metadata_.set_filetype(Song::Type_Stream);
metadata_.set_valid(true); metadata_.set_valid(true);
} }
@ -108,7 +108,7 @@ PlaylistItem::SpecialLoadResult RadioPlaylistItem::LoadNext() {
} }
QUrl RadioPlaylistItem::Url() const { QUrl RadioPlaylistItem::Url() const {
return QUrl(metadata_.filename()); return metadata_.url();
} }
PlaylistItem::Options RadioPlaylistItem::options() const { PlaylistItem::Options RadioPlaylistItem::options() const {

View File

@ -181,7 +181,7 @@ void SomaFMService::ReadChannel(QXmlStreamReader& reader) {
} else if (reader.name() == "dj") { } else if (reader.name() == "dj") {
song.set_artist(reader.readElementText()); song.set_artist(reader.readElementText());
} else if (reader.name() == "fastpls" && reader.attributes().value("format") == "mp3") { } else if (reader.name() == "fastpls" && reader.attributes().value("format") == "mp3") {
song.set_filename(reader.readElementText()); song.set_url(QUrl(reader.readElementText()));
} else { } else {
ConsumeElement(reader); ConsumeElement(reader);
} }

View File

@ -278,7 +278,7 @@ void SpotifyService::FillPlaylist(QStandardItem* item, const protobuf::LoadPlayl
child->setData(Type_Track, RadioModel::Role_Type); child->setData(Type_Track, RadioModel::Role_Type);
child->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata); child->setData(QVariant::fromValue(song), RadioModel::Role_SongMetadata);
child->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour); 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); item->appendRow(child);
} }
@ -293,7 +293,7 @@ void SpotifyService::SongFromProtobuf(const protobuf::Track& track, Song* song)
song->set_disc(track.disc()); song->set_disc(track.disc());
song->set_track(track.track()); song->set_track(track.track());
song->set_year(track.year()); song->set_year(track.year());
song->set_filename(QStringFromStdString(track.uri())); song->set_url(QUrl(QStringFromStdString(track.uri())));
QStringList artists; QStringList artists;
for (int i=0 ; i<track.artist_size() ; ++i) { for (int i=0 ; i<track.artist_size() ; ++i) {

View File

@ -9,14 +9,14 @@ public:
Album(); Album();
Album(const QString& _artist, const QString& _album_name, Album(const QString& _artist, const QString& _album_name,
const QString& _art_automatic, const QString& _art_manual, const QString& _art_automatic, const QString& _art_manual,
const QString& _first_filename); const QUrl& _first_url);
QString artist; QString artist;
QString album_name; QString album_name;
QString art_automatic; QString art_automatic;
QString art_manual; QString art_manual;
QString first_filename; QUrl first_url;
}; };
typedef QList<LibraryBackend::Album> AlbumList; typedef QList<LibraryBackend::Album> AlbumList;
@ -33,7 +33,7 @@ public:
SongList FindSongsInDirectory(int id); SongList FindSongsInDirectory(int id);
SubdirectoryList SubdirsInDirectory(int id); SubdirectoryList SubdirsInDirectory(int id);
DirectoryList GetAllDirectories(); 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 GetAll(const QString& column, const QueryOptions& opt = QueryOptions());
QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()); QStringList GetAllArtists(const QueryOptions& opt = QueryOptions());
@ -56,8 +56,8 @@ public:
SongList GetSongsByForeignId(const QStringList& ids, const QString& table, SongList GetSongsByForeignId(const QStringList& ids, const QString& table,
const QString& column); const QString& column);
SongList GetSongsByFilename(const QString& filename); SongList GetSongsByUrl(const QUrl& url);
Song GetSongByFilename(const QString& filename, int beginning); Song GetSongByUrl(const QUrl& url, int beginning);
void AddDirectory(const QString& path); void AddDirectory(const QString& path);
void RemoveDirectory(const Directory& dir); void RemoveDirectory(const Directory& dir);

View File

@ -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. the song is not from the library.
%End %End
const QString& filename() const; const QUrl& url() const;
%Docstring %Docstring
filename() -> str url() -> L{PyQt4.QtCore.QUrl}
The filename I{or URL} of this song. The URL of this song.
%End %End
const QString& basefilename() const; const QString& basefilename() const;
%Docstring %Docstring
basefilename() -> str basefilename() -> str
The filename of this song without any directory component. 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 %End
uint mtime() const; uint mtime() const;
@ -658,9 +652,9 @@ set_lastplayed(lastplayed)
set_score(score) set_score(score)
%End %End
void set_filename(const QString& v); void set_url(const QUrl& v);
%Docstring %Docstring
set_filename(filename) set_url(url)
%End %End
void set_basefilename(const QString& v); void set_basefilename(const QString& v);

View File

@ -130,8 +130,8 @@ QString AlbumCoverChoiceController::GetInitialPathForFileDialog(const Song& song
return song.art_automatic(); return song.art_automatic();
// if no automatic art, start in the song's folder // if no automatic art, start in the song's folder
} else if (!song.filename().isEmpty() && song.filename().contains('/')) { } else if (!song.url().isEmpty() && song.url().toLocalFile().contains('/')) {
return song.filename().section('/', 0, -2) + filename; return song.url().toLocalFile().section('/', 0, -2) + filename;
// fallback - start in home // fallback - start in home
} else { } else {
@ -189,7 +189,7 @@ void AlbumCoverChoiceController::ShowCover(const Song& song) {
QLabel* label = new QLabel(dialog); QLabel* label = new QLabel(dialog);
label->setPixmap(AlbumCoverLoader::TryLoadPixmap( 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->resize(label->pixmap()->size());
dialog->show(); dialog->show();

View File

@ -293,14 +293,14 @@ void AlbumCoverManager::ArtistChanged(QListWidgetItem* current) {
QListWidgetItem* item = new QListWidgetItem(no_cover_icon_, info.album_name, ui_->albums); QListWidgetItem* item = new QListWidgetItem(no_cover_icon_, info.album_name, ui_->albums);
item->setData(Role_ArtistName, info.artist); item->setData(Role_ArtistName, info.artist);
item->setData(Role_AlbumName, info.album_name); 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->setData(Qt::TextAlignmentRole, QVariant(Qt::AlignTop | Qt::AlignHCenter));
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
item->setToolTip(info.artist + " - " + info.album_name); item->setToolTip(info.artist + " - " + info.album_name);
if (!info.art_automatic.isEmpty() || !info.art_manual.isEmpty()) { if (!info.art_automatic.isEmpty() || !info.art_manual.isEmpty()) {
quint64 id = cover_loader_->Worker()->LoadImageAsync( 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_PathAutomatic, info.art_automatic);
item->setData(Role_PathManual, info.art_manual); item->setData(Role_PathManual, info.art_manual);
cover_loading_tasks_[id] = item; cover_loading_tasks_[id] = item;
@ -487,7 +487,7 @@ Song AlbumCoverManager::ItemAsSong(QListWidgetItem* item) {
result.set_artist(item->data(Role_ArtistName).toString()); result.set_artist(item->data(Role_ArtistName).toString());
result.set_album(item->data(Role_AlbumName).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_automatic(item->data(Role_PathAutomatic).toString());
result.set_art_manual(item->data(Role_PathManual).toString()); result.set_art_manual(item->data(Role_PathManual).toString());

View File

@ -110,7 +110,7 @@ class AlbumCoverManager : public QMainWindow {
Role_AlbumName, Role_AlbumName,
Role_PathAutomatic, Role_PathAutomatic,
Role_PathManual, Role_PathManual,
Role_FirstFilename, Role_FirstUrl,
}; };
enum HideCovers { enum HideCovers {

View File

@ -43,7 +43,7 @@ QMimeData* AlbumCoverManagerList::mimeData(const QList<QListWidgetItem*> items)
// Get URLs from the songs // Get URLs from the songs
QList<QUrl> urls; QList<QUrl> urls;
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
urls << QUrl::fromLocalFile(song.filename()); urls << song.url();
} }
// Get the QAbstractItemModel data so the picture works // Get the QAbstractItemModel data so the picture works

View File

@ -191,7 +191,7 @@ QList<EditTagDialog::Data> EditTagDialog::LoadData(const SongList& songs) const
if (song.IsEditable()) { if (song.IsEditable()) {
// Try reloading the tags from file // Try reloading the tags from file
Song copy(song); Song copy(song);
copy.InitFromFile(copy.filename(), copy.directory_id()); copy.InitFromFile(copy.url().toLocalFile(), copy.directory_id());
if (copy.is_valid()) if (copy.is_valid())
ret << Data(copy); ret << Data(copy);
@ -424,7 +424,11 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) {
QLocale::system().dateTimeFormat(QLocale::LongFormat))); QLocale::system().dateTimeFormat(QLocale::LongFormat)));
ui_->filesize->setText(Utilities::PrettySize(song.filesize())); ui_->filesize->setText(Utilities::PrettySize(song.filesize()));
ui_->filetype->setText(song.TextForFiletype()); 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()); album_cover_choice_controller_->search_for_cover_action()->setEnabled(CoverProviders::instance().HasAnyProviders());
} }
@ -602,7 +606,7 @@ void EditTagDialog::SaveData(const QList<Data>& data) {
continue; continue;
if (!ref.current_.Save()) { 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) { 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 // Find the song with this filename
for (int i=0 ; i<data_.count() ; ++i) { for (int i=0 ; i<data_.count() ; ++i) {
Data* data = &data_[i]; Data* data = &data_[i];
if (data->original_.filename() != filename) if (data->original_.url().toLocalFile() != filename)
continue; continue;
// Is it currently being displayed in the UI? // Is it currently being displayed in the UI?

View File

@ -106,19 +106,13 @@ int OrganiseDialog::SetSongs(const SongList& songs) {
preview_songs_.clear(); preview_songs_.clear();
foreach (const Song& song, songs) { foreach (const Song& song, songs) {
const QString filename = song.filename(); if (song.url().scheme() != "file") {
if (filename.isEmpty())
continue; continue;
if (filename.contains("://")) { };
QUrl url(song.filename());
if (!url.scheme().isEmpty() && url.scheme() != "file")
continue;
}
if (song.filesize() > 0) if (song.filesize() > 0)
total_size_ += song.filesize(); total_size_ += song.filesize();
filenames_ << filename; filenames_ << song.url().toLocalFile();
if (preview_songs_.count() < kNumberOfPreviews) if (preview_songs_.count() < kNumberOfPreviews)
preview_songs_ << song; preview_songs_ << song;

View File

@ -38,7 +38,7 @@ void OrganiseErrorDialog::Show(
OperationType type, const SongList& songs_with_errors) { OperationType type, const SongList& songs_with_errors) {
QStringList files; QStringList files;
foreach (const Song& song, songs_with_errors) { foreach (const Song& song, songs_with_errors) {
files << song.filename(); files << song.url().toLocalFile();
} }
Show(type, files); Show(type, files);
} }

View File

@ -77,7 +77,7 @@ void TrackSelectionDialog::Init(const SongList& songs) {
data_ << data; data_ << data;
QListWidgetItem* item = new QListWidgetItem(ui_->song_list); 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)); 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 // Find the item with this filename
int row = -1; int row = -1;
for (int i=0 ; i<data_.count() ; ++i) { for (int i=0 ; i<data_.count() ; ++i) {
if (data_[i].original_song_.filename() == original_song.filename()) { if (data_[i].original_song_.url() == original_song.url()) {
row = i; row = i;
break; break;
} }
@ -116,7 +116,7 @@ void TrackSelectionDialog::FetchTagFinished(const Song& original_song,
// Find the item with this filename // Find the item with this filename
int row = -1; int row = -1;
for (int i=0 ; i<data_.count() ; ++i) { for (int i=0 ; i<data_.count() ; ++i) {
if (data_[i].original_song_.filename() == original_song.filename()) { if (data_[i].original_song_.url() == original_song.url()) {
row = i; row = i;
break; break;
} }

View File

@ -37,8 +37,8 @@ TEST_F(AsxIniParserTest, ParsesBasicTrackList) {
SongList songs = parser_.Load(&file, "", QDir()); SongList songs = parser_.Load(&file, "", QDir());
ASSERT_EQ(2, songs.length()); ASSERT_EQ(2, songs.length());
EXPECT_EQ("http://195.245.168.21/antena3?MSWMExt=.asf", songs[0].filename()); EXPECT_EQ(QUrl("http://195.245.168.21/antena3?MSWMExt=.asf"), songs[0].url());
EXPECT_EQ("http://195.245.168.21:80/antena3?MSWMExt=.asf", songs[1].filename()); EXPECT_EQ(QUrl("http://195.245.168.21:80/antena3?MSWMExt=.asf"), songs[1].url());
EXPECT_TRUE(songs[0].is_valid()); EXPECT_TRUE(songs[0].is_valid());
EXPECT_TRUE(songs[1].is_valid()); EXPECT_TRUE(songs[1].is_valid());
} }
@ -56,7 +56,7 @@ TEST_F(AsxIniParserTest, WritesBasicTrackList) {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
Song song; Song song;
song.set_filename("http://www.example.com/foo.mp3"); song.set_url(QUrl("http://www.example.com/foo.mp3"));
SongList songs; SongList songs;
songs << song; songs << song;

View File

@ -45,7 +45,7 @@ TEST_F(ASXParserTest, ParsesOneTrackFromXML) {
const Song& song = songs[0]; const Song& song = songs[0];
EXPECT_EQ("Foo", song.title()); EXPECT_EQ("Foo", song.title());
EXPECT_EQ("Bar", song.artist()); EXPECT_EQ("Bar", song.artist());
EXPECT_EQ("http://example.com/foo.mp3", song.filename()); EXPECT_EQ(QUrl("http://example.com/foo.mp3"), song.url());
EXPECT_TRUE(song.is_valid()); EXPECT_TRUE(song.is_valid());
} }
@ -64,8 +64,8 @@ TEST_F(ASXParserTest, ParsesMoreThanOneTrackFromXML) {
ASXParser parser(NULL); ASXParser parser(NULL);
SongList songs = parser.Load(&buffer); SongList songs = parser.Load(&buffer);
ASSERT_EQ(2, songs.length()); ASSERT_EQ(2, songs.length());
EXPECT_EQ("http://example.com/foo.mp3", songs[0].filename()); EXPECT_EQ(QUrl("http://example.com/foo.mp3"), songs[0].url());
EXPECT_EQ("http://example.com/bar.mp3", songs[1].filename()); EXPECT_EQ(QUrl("http://example.com/bar.mp3"), songs[1].url());
EXPECT_TRUE(songs[0].is_stream()); EXPECT_TRUE(songs[0].is_stream());
EXPECT_TRUE(songs[1].is_stream()); EXPECT_TRUE(songs[1].is_stream());
} }
@ -90,7 +90,7 @@ TEST_F(ASXParserTest, ParsesBrokenXmlEntities) {
ASXParser parser(NULL); ASXParser parser(NULL);
SongList songs = parser.Load(&buffer); SongList songs = parser.Load(&buffer);
ASSERT_EQ(1, songs.length()); ASSERT_EQ(1, songs.length());
EXPECT_EQ("mms://72.26.204.105/classictrance128k?user=h&pass=xxxxxxxxxxxxxxx", songs[0].filename()); EXPECT_EQ(QUrl("mms://72.26.204.105/classictrance128k?user=h&pass=xxxxxxxxxxxxxxx"), songs[0].url());
} }
TEST_F(ASXParserTest, SavesSong) { TEST_F(ASXParserTest, SavesSong) {
@ -99,7 +99,7 @@ TEST_F(ASXParserTest, SavesSong) {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
ASXParser parser(NULL); ASXParser parser(NULL);
Song one; 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_filetype(Song::Type_Stream);
one.set_title("foo"); one.set_title("foo");
one.set_length_nanosec(123 * kNsecPerSec); one.set_length_nanosec(123 * kNsecPerSec);
@ -123,5 +123,5 @@ TEST_F(ASXParserTest, ParsesSomaFM) {
ASSERT_EQ(4, songs.count()); ASSERT_EQ(4, songs.count());
EXPECT_EQ("SomaFM: Secret Agent", songs[0].title()); EXPECT_EQ("SomaFM: Secret Agent", songs[0].title());
EXPECT_EQ("Keep us on the air! Click Support SomaFM above!", songs[0].artist()); EXPECT_EQ("Keep us on the air! Click Support SomaFM above!", songs[0].artist());
EXPECT_EQ("http://streamer-ntc-aa03.somafm.com:80/stream/1021", songs[0].filename()); EXPECT_EQ(QUrl("http://streamer-ntc-aa03.somafm.com:80/stream/1021"), songs[0].url());
} }

View File

@ -151,7 +151,7 @@ TEST_F(CueParserTest, UsesAllMetadataInformation) {
Song first_song = song_list.at(0); Song first_song = song_list.at(0);
Song second_song = song_list.at(1); Song second_song = song_list.at(1);
ASSERT_TRUE(first_song.filename().endsWith("a_file.mp3")); ASSERT_TRUE(first_song.url().toString().endsWith("a_file.mp3"));
ASSERT_EQ("Un soffio caldo", first_song.title()); ASSERT_EQ("Un soffio caldo", first_song.title());
ASSERT_EQ("Album", first_song.album()); ASSERT_EQ("Album", first_song.album());
ASSERT_EQ("Zucchero", first_song.artist()); ASSERT_EQ("Zucchero", first_song.artist());
@ -161,7 +161,7 @@ TEST_F(CueParserTest, UsesAllMetadataInformation) {
ASSERT_EQ(second_song.beginning_nanosec() - first_song.beginning_nanosec(), first_song.length_nanosec()); ASSERT_EQ(second_song.beginning_nanosec() - first_song.beginning_nanosec(), first_song.length_nanosec());
ASSERT_EQ(1, first_song.track()); ASSERT_EQ(1, first_song.track());
ASSERT_TRUE(second_song.filename().endsWith("a_file.mp3")); ASSERT_TRUE(second_song.url().toString().endsWith("a_file.mp3"));
ASSERT_EQ("Hey you!", second_song.title()); ASSERT_EQ("Hey you!", second_song.title());
ASSERT_EQ("Album", second_song.album()); ASSERT_EQ("Album", second_song.album());
ASSERT_EQ("Zucchero himself", second_song.artist()); ASSERT_EQ("Zucchero himself", second_song.artist());
@ -189,7 +189,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
Song fourth_song = song_list.at(3); Song fourth_song = song_list.at(3);
Song fifth_song = song_list.at(4); Song fifth_song = song_list.at(4);
ASSERT_TRUE(first_song.filename().endsWith("files/longer_one.mp3")); ASSERT_TRUE(first_song.url().toString().endsWith("files/longer_one.mp3"));
ASSERT_EQ("A1Song1", first_song.title()); ASSERT_EQ("A1Song1", first_song.title());
ASSERT_EQ("Artist One Album", first_song.album()); ASSERT_EQ("Artist One Album", first_song.album());
ASSERT_EQ("Artist One", first_song.artist()); ASSERT_EQ("Artist One", first_song.artist());
@ -199,7 +199,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
ASSERT_EQ(-1, first_song.track()); ASSERT_EQ(-1, first_song.track());
ASSERT_EQ("CUEPATH", first_song.cue_path()); ASSERT_EQ("CUEPATH", first_song.cue_path());
ASSERT_TRUE(second_song.filename().endsWith("files/longer_one.mp3")); ASSERT_TRUE(second_song.url().toString().endsWith("files/longer_one.mp3"));
ASSERT_EQ("A1Song2", second_song.title()); ASSERT_EQ("A1Song2", second_song.title());
ASSERT_EQ("Artist One Album", second_song.album()); ASSERT_EQ("Artist One Album", second_song.album());
ASSERT_EQ("Artist One", second_song.artist()); ASSERT_EQ("Artist One", second_song.artist());
@ -207,7 +207,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
ASSERT_EQ(to_nanosec(5, 3, 68), second_song.beginning_nanosec()); ASSERT_EQ(to_nanosec(5, 3, 68), second_song.beginning_nanosec());
ASSERT_EQ(-1, second_song.track()); ASSERT_EQ(-1, second_song.track());
ASSERT_TRUE(third_song.filename().endsWith("files/longer_two_p1.mp3")); ASSERT_TRUE(third_song.url().toString().endsWith("files/longer_two_p1.mp3"));
ASSERT_EQ("A2P1Song1", third_song.title()); ASSERT_EQ("A2P1Song1", third_song.title());
ASSERT_EQ("Artist Two Album", third_song.album()); ASSERT_EQ("Artist Two Album", third_song.album());
ASSERT_EQ("Artist X", third_song.artist()); ASSERT_EQ("Artist X", third_song.artist());
@ -217,7 +217,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
ASSERT_EQ(-1, third_song.track()); ASSERT_EQ(-1, third_song.track());
ASSERT_EQ("CUEPATH", third_song.cue_path()); ASSERT_EQ("CUEPATH", third_song.cue_path());
ASSERT_TRUE(fourth_song.filename().endsWith("files/longer_two_p1.mp3")); ASSERT_TRUE(fourth_song.url().toString().endsWith("files/longer_two_p1.mp3"));
ASSERT_EQ("A2P1Song2", fourth_song.title()); ASSERT_EQ("A2P1Song2", fourth_song.title());
ASSERT_EQ("Artist Two Album", fourth_song.album()); ASSERT_EQ("Artist Two Album", fourth_song.album());
ASSERT_EQ("Artist Two", fourth_song.artist()); ASSERT_EQ("Artist Two", fourth_song.artist());
@ -225,7 +225,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
ASSERT_EQ(to_nanosec(4, 0, 13), fourth_song.beginning_nanosec()); ASSERT_EQ(to_nanosec(4, 0, 13), fourth_song.beginning_nanosec());
ASSERT_EQ(-1, fourth_song.track()); ASSERT_EQ(-1, fourth_song.track());
ASSERT_TRUE(fifth_song.filename().endsWith("files/longer_two_p2.mp3")); ASSERT_TRUE(fifth_song.url().toString().endsWith("files/longer_two_p2.mp3"));
ASSERT_EQ("A2P2Song1", fifth_song.title()); ASSERT_EQ("A2P2Song1", fifth_song.title());
ASSERT_EQ("Artist Two Album", fifth_song.album()); ASSERT_EQ("Artist Two Album", fifth_song.album());
ASSERT_EQ("Artist Two", fifth_song.artist()); ASSERT_EQ("Artist Two", fifth_song.artist());
@ -253,7 +253,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
Song fourth_song = song_list.at(3); Song fourth_song = song_list.at(3);
// A* - broken song in the middle // A* - broken song in the middle
ASSERT_TRUE(first_song.filename().endsWith("file1.mp3")); ASSERT_TRUE(first_song.url().toString().endsWith("file1.mp3"));
ASSERT_EQ("Artist One", first_song.artist()); ASSERT_EQ("Artist One", first_song.artist());
ASSERT_EQ("Artist One Album", first_song.album()); ASSERT_EQ("Artist One Album", first_song.album());
ASSERT_EQ("A1", first_song.title()); ASSERT_EQ("A1", first_song.title());
@ -261,7 +261,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
ASSERT_EQ(second_song.beginning_nanosec() - first_song.beginning_nanosec(), first_song.length_nanosec()); ASSERT_EQ(second_song.beginning_nanosec() - first_song.beginning_nanosec(), first_song.length_nanosec());
ASSERT_EQ(-1, first_song.track()); ASSERT_EQ(-1, first_song.track());
ASSERT_TRUE(second_song.filename().endsWith("file1.mp3")); ASSERT_TRUE(second_song.url().toString().endsWith("file1.mp3"));
ASSERT_EQ("Artist One", second_song.artist()); ASSERT_EQ("Artist One", second_song.artist());
ASSERT_EQ("Artist One Album", second_song.album()); ASSERT_EQ("Artist One Album", second_song.album());
ASSERT_EQ("A3", second_song.title()); ASSERT_EQ("A3", second_song.title());
@ -271,7 +271,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
// all B* songs are broken // all B* songs are broken
// C* - broken song at the end // C* - broken song at the end
ASSERT_TRUE(third_song.filename().endsWith("file3.mp3")); ASSERT_TRUE(third_song.url().toString().endsWith("file3.mp3"));
ASSERT_EQ("Artist Three", third_song.artist()); ASSERT_EQ("Artist Three", third_song.artist());
ASSERT_EQ("Artist Three Album", third_song.album()); ASSERT_EQ("Artist Three Album", third_song.album());
ASSERT_EQ("C1", third_song.title()); ASSERT_EQ("C1", third_song.title());
@ -279,7 +279,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
ASSERT_EQ(-1, third_song.track()); ASSERT_EQ(-1, third_song.track());
// D* - broken song at the beginning // D* - broken song at the beginning
ASSERT_TRUE(fourth_song.filename().endsWith("file4.mp3")); ASSERT_TRUE(fourth_song.url().toString().endsWith("file4.mp3"));
ASSERT_EQ("Artist Four", fourth_song.artist()); ASSERT_EQ("Artist Four", fourth_song.artist());
ASSERT_EQ("Artist Four Album", fourth_song.album()); ASSERT_EQ("Artist Four Album", fourth_song.album());
ASSERT_EQ("D2", fourth_song.title()); ASSERT_EQ("D2", fourth_song.title());
@ -302,14 +302,14 @@ TEST_F(CueParserTest, SkipsDataFiles) {
Song first_song = song_list.at(0); Song first_song = song_list.at(0);
Song second_song = song_list.at(1); Song second_song = song_list.at(1);
ASSERT_TRUE(first_song.filename().endsWith("file1.mp3")); ASSERT_TRUE(first_song.url().toString().endsWith("file1.mp3"));
ASSERT_EQ("Artist One", first_song.artist()); ASSERT_EQ("Artist One", first_song.artist());
ASSERT_EQ("Artist One Album", first_song.album()); ASSERT_EQ("Artist One Album", first_song.album());
ASSERT_EQ("A1", first_song.title()); ASSERT_EQ("A1", first_song.title());
ASSERT_EQ(to_nanosec(0, 1, 0), first_song.beginning_nanosec()); ASSERT_EQ(to_nanosec(0, 1, 0), first_song.beginning_nanosec());
ASSERT_EQ(-1, first_song.track()); ASSERT_EQ(-1, first_song.track());
ASSERT_TRUE(second_song.filename().endsWith("file4.mp3")); ASSERT_TRUE(second_song.url().toString().endsWith("file4.mp3"));
ASSERT_EQ("Artist Four", second_song.artist()); ASSERT_EQ("Artist Four", second_song.artist());
ASSERT_EQ("Artist Four Album", second_song.album()); ASSERT_EQ("Artist Four Album", second_song.album());
ASSERT_EQ("D1", second_song.title()); ASSERT_EQ("D1", second_song.title());

View File

@ -46,7 +46,7 @@ class LibraryBackendTest : public ::testing::Test {
// Returns a valid song with all the required fields set // Returns a valid song with all the required fields set
Song ret; Song ret;
ret.set_directory_id(directory_id); ret.set_directory_id(directory_id);
ret.set_filename("foo.mp3"); ret.set_url(QUrl::fromLocalFile("foo.mp3"));
ret.set_mtime(1); ret.set_mtime(1);
ret.set_ctime(1); ret.set_ctime(1);
ret.set_filesize(1); ret.set_filesize(1);
@ -109,7 +109,7 @@ TEST_F(LibraryBackendTest, AddInvalidSong) {
backend_->AddOrUpdateSongs(SongList() << s); backend_->AddOrUpdateSongs(SongList() << s);
ASSERT_EQ(1, spy.count()); spy.takeFirst(); ASSERT_EQ(1, spy.count()); spy.takeFirst();
s.set_filename("foo"); s.set_url(QUrl::fromLocalFile("foo"));
backend_->AddOrUpdateSongs(SongList() << s); backend_->AddOrUpdateSongs(SongList() << s);
ASSERT_EQ(1, spy.count()); spy.takeFirst(); ASSERT_EQ(1, spy.count()); spy.takeFirst();

View File

@ -52,7 +52,7 @@ class LibraryModelTest : public ::testing::Test {
song.set_directory_id(1); song.set_directory_id(1);
if (song.mtime() == -1) song.set_mtime(1); if (song.mtime() == -1) song.set_mtime(1);
if (song.ctime() == -1) song.set_ctime(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 (song.filesize() == -1) song.set_filesize(1);
if (!added_dir_) { if (!added_dir_) {

View File

@ -56,10 +56,10 @@ TEST_F(M3UParserTest, ParsesTrackLocation) {
taglib_.ExpectCall(temp.fileName(), "foo", "bar", "baz"); taglib_.ExpectCall(temp.fileName(), "foo", "bar", "baz");
Song song(&taglib_); Song song(&taglib_);
QString line(temp.fileName()); QString line(temp.fileName());
ASSERT_TRUE(parser_.ParseTrackLocation(line, QDir(), &song)); parser_.LoadSong(line, 0, QDir(), &song);
ASSERT_EQ(temp.fileName(), song.filename()); 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("foo", song.title());
EXPECT_EQ("bar", song.artist()); EXPECT_EQ("bar", song.artist());
@ -74,10 +74,10 @@ TEST_F(M3UParserTest, ParsesTrackLocationRelative) {
M3UParser parser(NULL); M3UParser parser(NULL);
QString line(info.fileName()); QString line(info.fileName());
Song song(&taglib_); Song song(&taglib_);
ASSERT_TRUE(parser.ParseTrackLocation(line, info.dir(), &song)); parser.LoadSong(line, 0, info.dir(), &song);
ASSERT_EQ(temp.fileName(), song.filename()); 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("foo", song.title());
} }
@ -85,8 +85,8 @@ TEST_F(M3UParserTest, ParsesTrackLocationRelative) {
TEST_F(M3UParserTest, ParsesTrackLocationHttp) { TEST_F(M3UParserTest, ParsesTrackLocationHttp) {
QString line("http://example.com/foo/bar.mp3"); QString line("http://example.com/foo/bar.mp3");
Song song; Song song;
ASSERT_TRUE(parser_.ParseTrackLocation(line, QDir(), &song)); parser_.LoadSong(line, 0, QDir(), &song);
EXPECT_EQ("http://example.com/foo/bar.mp3", song.filename()); EXPECT_EQ(QUrl("http://example.com/foo/bar.mp3"), song.url());
} }
TEST_F(M3UParserTest, ParsesSongsFromDevice) { TEST_F(M3UParserTest, ParsesSongsFromDevice) {
@ -102,8 +102,7 @@ TEST_F(M3UParserTest, ParsesSongsFromDevice) {
EXPECT_EQ("Some Artist", s.artist()); EXPECT_EQ("Some Artist", s.artist());
EXPECT_EQ("Some Title", s.title()); EXPECT_EQ("Some Title", s.title());
EXPECT_EQ(123 * kNsecPerSec, s.length_nanosec()); EXPECT_EQ(123 * kNsecPerSec, s.length_nanosec());
EXPECT_PRED_FORMAT2(::testing::IsSubstring, EXPECT_EQ(QUrl("http://foo.com/bar/somefile.mp3"), s.url());
"http://foo.com/bar/somefile.mp3", s.filename().toStdString());
} }
TEST_F(M3UParserTest, ParsesNonExtendedM3U) { TEST_F(M3UParserTest, ParsesNonExtendedM3U) {
@ -114,10 +113,8 @@ TEST_F(M3UParserTest, ParsesNonExtendedM3U) {
M3UParser parser(NULL); M3UParser parser(NULL);
SongList songs = parser.Load(&buffer, "", QDir("somedir")); SongList songs = parser.Load(&buffer, "", QDir("somedir"));
ASSERT_EQ(2, songs.size()); ASSERT_EQ(2, songs.size());
EXPECT_PRED_FORMAT2(::testing::IsSubstring, EXPECT_EQ(QUrl("http://foo.com/bar/somefile.mp3"), songs[0].url());
"http://foo.com/bar/somefile.mp3", songs[0].filename().toStdString()); EXPECT_EQ(QUrl("http://baz.com/thing.mp3"), songs[1].url());
EXPECT_PRED_FORMAT2(::testing::IsSubstring,
"http://baz.com/thing.mp3", songs[1].filename().toStdString());
EXPECT_EQ(-1, songs[0].length_nanosec()); EXPECT_EQ(-1, songs[0].length_nanosec());
EXPECT_EQ(-1, songs[1].length_nanosec()); EXPECT_EQ(-1, songs[1].length_nanosec());
EXPECT_TRUE(songs[0].artist().isEmpty()); EXPECT_TRUE(songs[0].artist().isEmpty());
@ -144,7 +141,7 @@ TEST_F(M3UParserTest, SavesSong) {
one.set_title("foo"); one.set_title("foo");
one.set_artist("bar"); one.set_artist("bar");
one.set_length_nanosec(123 * kNsecPerSec); 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; SongList songs;
songs << one; songs << one;
M3UParser parser(NULL); M3UParser parser(NULL);
@ -167,5 +164,6 @@ TEST_F(M3UParserTest, ParsesUTF8) {
EXPECT_EQ(11, songs[0].title().length()); EXPECT_EQ(11, songs[0].title().length());
EXPECT_EQ(QString::fromUtf8("Разные"), songs[0].artist()); EXPECT_EQ(QString::fromUtf8("Разные"), songs[0].artist());
EXPECT_EQ(QString::fromUtf8("исполнители"), songs[0].title()); 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());
} }

View File

@ -33,7 +33,7 @@ class MockLibraryBackend : public LibraryBackendInterface {
MOCK_METHOD1(FindSongsInDirectory, SongList(int)); MOCK_METHOD1(FindSongsInDirectory, SongList(int));
MOCK_METHOD1(SubdirsInDirectory, SubdirectoryList(int)); MOCK_METHOD1(SubdirsInDirectory, SubdirectoryList(int));
MOCK_METHOD0(GetAllDirectories, DirectoryList()); 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(GetAllArtists, QStringList(const QueryOptions&));
MOCK_METHOD1(GetAllArtistsWithAlbums, QStringList(const QueryOptions&)); MOCK_METHOD1(GetAllArtistsWithAlbums, QStringList(const QueryOptions&));
@ -51,8 +51,8 @@ class MockLibraryBackend : public LibraryBackendInterface {
MOCK_METHOD1(GetSongById, Song(int)); MOCK_METHOD1(GetSongById, Song(int));
MOCK_METHOD1(GetSongsByFilename, SongList(const QString&)); MOCK_METHOD1(GetSongsByUrl, SongList(const QUrl&));
MOCK_METHOD2(GetSongByFilename, Song(const QString&, qint64)); MOCK_METHOD2(GetSongByUrl, Song(const QUrl&, qint64));
MOCK_METHOD1(AddDirectory, void(const QString&)); MOCK_METHOD1(AddDirectory, void(const QString&));
MOCK_METHOD1(RemoveDirectory, void(const Directory&)); MOCK_METHOD1(RemoveDirectory, void(const Directory&));

View File

@ -52,7 +52,7 @@ TEST_F(OrganiseFormatTest, BasicReplace) {
} }
TEST_F(OrganiseFormatTest, Extension) { TEST_F(OrganiseFormatTest, Extension) {
song_.set_filename("/some/path/filename.mp3"); song_.set_url(QUrl("file:///some/path/filename.mp3"));
format_.set_format("%extension"); format_.set_format("%extension");
ASSERT_TRUE(format_.IsValid()); ASSERT_TRUE(format_.IsValid());

View File

@ -48,7 +48,7 @@ TEST_F(PLSParserTest, ParseOneTrack) {
SongList songs = parser_.Load(file.get(), "", QDir("/relative/to/")); SongList songs = parser_.Load(file.get(), "", QDir("/relative/to/"));
ASSERT_EQ(1, songs.length()); 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("Title", songs[0].title());
EXPECT_EQ(123 * kNsecPerSec, songs[0].length_nanosec()); EXPECT_EQ(123 * kNsecPerSec, songs[0].length_nanosec());
} }
@ -58,10 +58,10 @@ TEST_F(PLSParserTest, ParseSomaFM) {
SongList songs = parser_.Load(file.get()); SongList songs = parser_.Load(file.get());
ASSERT_EQ(4, songs.length()); ASSERT_EQ(4, songs.length());
EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1018", songs[0].filename()); EXPECT_EQ(QUrl("http://streamer-dtc-aa05.somafm.com:80/stream/1018"), songs[0].url());
EXPECT_EQ("http://streamer-mtc-aa03.somafm.com:80/stream/1018", songs[1].filename()); EXPECT_EQ(QUrl("http://streamer-mtc-aa03.somafm.com:80/stream/1018"), songs[1].url());
EXPECT_EQ("http://streamer-ntc-aa04.somafm.com:80/stream/1018", songs[2].filename()); EXPECT_EQ(QUrl("http://streamer-ntc-aa04.somafm.com:80/stream/1018"), songs[2].url());
EXPECT_EQ("http://ice.somafm.com/groovesalad", songs[3].filename()); 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 (#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 (#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()); 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()); SongList songs = parser_.Load(file.get());
ASSERT_EQ(4, songs.length()); ASSERT_EQ(4, songs.length());
EXPECT_EQ("http://streamer-ntc-aa03.somafm.com:80/stream/1021", songs[0].filename()); EXPECT_EQ(QUrl("http://streamer-ntc-aa03.somafm.com:80/stream/1021"), songs[0].url());
EXPECT_EQ("http://streamer-mtc-aa04.somafm.com:80/stream/1021", songs[1].filename()); EXPECT_EQ(QUrl("http://streamer-mtc-aa04.somafm.com:80/stream/1021"), songs[1].url());
EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1021", songs[2].filename()); EXPECT_EQ(QUrl("http://streamer-dtc-aa05.somafm.com:80/stream/1021"), songs[2].url());
EXPECT_EQ("http://ice.somafm.com/secretagent", songs[3].filename()); 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 (#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 (#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()); 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) { TEST_F(PLSParserTest, SaveAndLoad) {
Song one; 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"); one.set_title("Foo, with, some, commas");
Song two; Song two;
two.set_filename("relative/bar.mp3"); two.set_url(QUrl("relative/bar.mp3"));
two.set_title("Bar"); two.set_title("Bar");
two.set_length_nanosec(123 * kNsecPerSec); two.set_length_nanosec(123 * kNsecPerSec);
@ -106,8 +106,8 @@ TEST_F(PLSParserTest, SaveAndLoad) {
songs = parser_.Load(&temp, "", QDir("/meep")); songs = parser_.Load(&temp, "", QDir("/meep"));
ASSERT_EQ(2, songs.count()); ASSERT_EQ(2, songs.count());
EXPECT_EQ(one.filename(), songs[0].filename()); EXPECT_EQ(one.url(), songs[0].url());
EXPECT_EQ("/meep/relative/bar.mp3", songs[1].filename()); EXPECT_EQ(QUrl("file:///meep/relative/bar.mp3"), songs[1].url());
EXPECT_EQ(one.title(), songs[0].title()); EXPECT_EQ(one.title(), songs[0].title());
EXPECT_EQ(two.title(), songs[1].title()); EXPECT_EQ(two.title(), songs[1].title());
EXPECT_EQ(one.length_nanosec(), songs[0].length_nanosec()); EXPECT_EQ(one.length_nanosec(), songs[0].length_nanosec());

View File

@ -55,7 +55,7 @@ protected:
loader_->set_timeout(20000); loader_->set_timeout(20000);
// the thing we return is not really important // 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); void LoadLocalDirectory(const QString& dir);
@ -191,7 +191,7 @@ TEST_F(SongLoaderTest, LoadRemoteMp3) {
// Check the song got loaded // Check the song got loaded
ASSERT_EQ(1, loader_->songs().count()); 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) { TEST_F(SongLoaderTest, LoadRemote404) {
@ -231,7 +231,7 @@ TEST_F(SongLoaderTest, LoadRemotePls) {
ASSERT_EQ(4, loader_->songs().count()); ASSERT_EQ(4, loader_->songs().count());
EXPECT_EQ("SomaFM: Groove Salad (#3 128k mp3): A nicely chilled plate of ambient beats and grooves.", EXPECT_EQ("SomaFM: Groove Salad (#3 128k mp3): A nicely chilled plate of ambient beats and grooves.",
loader_->songs()[2].title()); 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) { TEST_F(SongLoaderTest, LoadRemotePlainText) {
@ -268,8 +268,8 @@ TEST_F(SongLoaderTest, LoadRemotePlainM3U) {
EXPECT_EQ(true, spy[0][0].toBool()); EXPECT_EQ(true, spy[0][0].toBool());
ASSERT_EQ(2, loader_->songs().count()); ASSERT_EQ(2, loader_->songs().count());
EXPECT_EQ("http://www.example.com/one.mp3", loader_->songs()[0].filename()); EXPECT_EQ(QUrl("http://www.example.com/one.mp3"), loader_->songs()[0].url());
EXPECT_EQ("http://www.example.com/two.mp3", loader_->songs()[1].filename()); EXPECT_EQ(QUrl("http://www.example.com/two.mp3"), loader_->songs()[1].url());
} }
TEST_F(SongLoaderTest, LoadLocalDirectory) { TEST_F(SongLoaderTest, LoadLocalDirectory) {

View File

@ -38,7 +38,7 @@ class SongPlaylistItemTest : public ::testing::TestWithParam<const char*> {
absolute_file_name_ = QFileInfo(temp_file_.fileName()).absoluteFilePath(); absolute_file_name_ = QFileInfo(temp_file_.fileName()).absoluteFilePath();
song_.Init("Title", "Artist", "Album", 123); 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_)); item_.reset(new SongPlaylistItem(song_));

View File

@ -50,6 +50,10 @@ void PrintTo(const ::QVariant& var, std::ostream& os) {
os << var.toString().toStdString(); os << var.toString().toStdString();
} }
void PrintTo(const ::QUrl& url, std::ostream& os) {
os << url.toString().toStdString();
}
TemporaryResource::TemporaryResource(const QString& filename) { TemporaryResource::TemporaryResource(const QString& filename) {
setFileTemplate(QDir::tempPath() + "/clementine_test-XXXXXX." + setFileTemplate(QDir::tempPath() + "/clementine_test-XXXXXX." +
filename.section('.', -1, -1)); filename.section('.', -1, -1));

View File

@ -46,6 +46,7 @@ std::ostream& operator <<(std::ostream& stream, const QList<T>& list) {
void PrintTo(const ::QString& str, std::ostream& os); void PrintTo(const ::QString& str, std::ostream& os);
void PrintTo(const ::QVariant& var, std::ostream& os); void PrintTo(const ::QVariant& var, std::ostream& os);
void PrintTo(const ::QUrl& url, std::ostream& os);
#define EXPOSE_SIGNAL0(n) \ #define EXPOSE_SIGNAL0(n) \
void Emit##n() { emit n(); } void Emit##n() { emit n(); }

View File

@ -49,7 +49,7 @@ TEST_F(XSPFParserTest, ParsesOneTrackFromXML) {
EXPECT_EQ("Foo", song.title()); EXPECT_EQ("Foo", song.title());
EXPECT_EQ("Bar", song.artist()); EXPECT_EQ("Bar", song.artist());
EXPECT_EQ("Baz", song.album()); 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_EQ(60 * kNsecPerSec, song.length_nanosec());
EXPECT_TRUE(song.is_valid()); EXPECT_TRUE(song.is_valid());
} }
@ -69,8 +69,8 @@ TEST_F(XSPFParserTest, ParsesMoreThanOneTrackFromXML) {
XSPFParser parser(NULL); XSPFParser parser(NULL);
SongList songs = parser.Load(&buffer); SongList songs = parser.Load(&buffer);
ASSERT_EQ(2, songs.length()); ASSERT_EQ(2, songs.length());
EXPECT_EQ("http://example.com/foo.mp3", songs[0].filename()); EXPECT_EQ(QUrl("http://example.com/foo.mp3"), songs[0].url());
EXPECT_EQ("http://example.com/bar.mp3", songs[1].filename()); EXPECT_EQ(QUrl("http://example.com/bar.mp3"), songs[1].url());
EXPECT_TRUE(songs[0].is_stream()); EXPECT_TRUE(songs[0].is_stream());
EXPECT_TRUE(songs[1].is_stream()); EXPECT_TRUE(songs[1].is_stream());
} }
@ -100,7 +100,7 @@ TEST_F(XSPFParserTest, SavesSong) {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
XSPFParser parser(NULL); XSPFParser parser(NULL);
Song one; 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_filetype(Song::Type_Stream);
one.set_title("foo"); one.set_title("foo");
one.set_length_nanosec(123 * kNsecPerSec); one.set_length_nanosec(123 * kNsecPerSec);
@ -121,7 +121,7 @@ TEST_F(XSPFParserTest, SavesLocalFile) {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
XSPFParser parser(NULL); XSPFParser parser(NULL);
Song one; Song one;
one.set_filename("/bar/foo.mp3"); one.set_url(QUrl("file:///bar/foo.mp3"));
one.set_filetype(Song::Type_Mpeg); one.set_filetype(Song::Type_Mpeg);
one.set_title("foo"); one.set_title("foo");
one.set_length_nanosec(123 * kNsecPerSec); one.set_length_nanosec(123 * kNsecPerSec);