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

View File

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

View File

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

View File

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

View File

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

View File

@ -308,7 +308,7 @@ void Mpris2::ArtLoaded(const Song& song, const QString& art_uri) {
using mpris::AddMetadata;
AddMetadata("mpris:trackid", current_track_id(), &last_metadata_);
AddMetadata("xesam:url", song.filename(), &last_metadata_);
AddMetadata("xesam:url", song.url().toString(), &last_metadata_);
AddMetadata("xesam:title", song.PrettyTitle(), &last_metadata_);
AddMetadataAsList("xesam:artist", song.artist(), &last_metadata_);
AddMetadata("xesam:album", song.album(), &last_metadata_);

View File

@ -148,7 +148,7 @@ void Organise::ProcessSomeFiles() {
song.set_filetype(task.new_filetype_);
// Fiddle the filename extension as well to match the new type
song.set_filename(FiddleFileExtension(song.filename(), task.new_extension_));
song.set_url(QUrl::fromLocalFile(FiddleFileExtension(song.url().toLocalFile(), task.new_extension_)));
song.set_basefilename(FiddleFileExtension(song.basefilename(), task.new_extension_));
// Have to set this to the size of the new file or else funny stuff happens

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 == "bitrate") value = QString::number(song.bitrate());
else if (tag == "samplerate") value = QString::number(song.samplerate());
else if (tag == "extension") value = song.filename().section('.', -1, -1);
else if (tag == "extension") value = song.url().toLocalFile().section('.', -1, -1);
else if (tag == "artistinitial") {
value = song.albumartist().trimmed();
if (value.isEmpty()) value = song.artist().trimmed();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -215,7 +215,7 @@ QString iMobileDeviceConnection::GetUnusedFilename(
}
// Use the same file extension as the original file, default to mp3.
QString extension = metadata.filename().section('.', -1, -1).toLower();
QString extension = metadata.url().path().section('.', -1, -1).toLower();
if (extension.isEmpty())
extension = "mp3";

View File

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

View File

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

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

View File

@ -40,17 +40,17 @@ class LibraryBackendInterface : public QObject {
Album() {}
Album(const QString& _artist, const QString& _album_name,
const QString& _art_automatic, const QString& _art_manual,
const QString& _first_filename)
const QUrl& _first_url)
: artist(_artist), album_name(_album_name),
art_automatic(_art_automatic), art_manual(_art_manual),
first_filename(_first_filename) {}
first_url(_first_url) {}
QString artist;
QString album_name;
QString art_automatic;
QString art_manual;
QString first_filename;
QUrl first_url;
};
typedef QList<Album> AlbumList;
@ -63,7 +63,7 @@ class LibraryBackendInterface : public QObject {
virtual SongList FindSongsInDirectory(int id) = 0;
virtual SubdirectoryList SubdirsInDirectory(int id) = 0;
virtual DirectoryList GetAllDirectories() = 0;
virtual void ChangeDirPath(int id, const QString& new_path) = 0;
virtual void ChangeDirPath(int id, const QString& old_path, const QString& new_path) = 0;
virtual QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()) = 0;
virtual QStringList GetAllArtistsWithAlbums(const QueryOptions& opt = QueryOptions()) = 0;
@ -84,11 +84,11 @@ class LibraryBackendInterface : public QObject {
// Returns all sections of a song with the given filename. If there's just one section
// the resulting list will have it's size equal to 1.
virtual SongList GetSongsByFilename(const QString& filename) = 0;
virtual SongList GetSongsByUrl(const QUrl& url) = 0;
// Returns a section of a song with the given filename and beginning. If the section
// is not present in library, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByFilename(const QString& filename, qint64 beginning = 0) = 0;
virtual Song GetSongByUrl(const QUrl& url, qint64 beginning = 0) = 0;
virtual void AddDirectory(const QString& path) = 0;
virtual void RemoveDirectory(const Directory& dir) = 0;
@ -120,7 +120,7 @@ class LibraryBackend : public LibraryBackendInterface {
SongList FindSongsInDirectory(int id);
SubdirectoryList SubdirsInDirectory(int id);
DirectoryList GetAllDirectories();
void ChangeDirPath(int id, const QString& new_path);
void ChangeDirPath(int id, const QString& old_path, const QString& new_path);
QStringList GetAll(const QString& column, const QueryOptions& opt = QueryOptions());
QStringList GetAllArtists(const QueryOptions& opt = QueryOptions());
@ -143,8 +143,8 @@ class LibraryBackend : public LibraryBackendInterface {
SongList GetSongsByForeignId(const QStringList& ids, const QString& table,
const QString& column);
SongList GetSongsByFilename(const QString& filename);
Song GetSongByFilename(const QString& filename, qint64 beginning = 0);
SongList GetSongsByUrl(const QUrl& url);
Song GetSongByUrl(const QUrl& url, qint64 beginning = 0);
void AddDirectory(const QString& path);
void RemoveDirectory(const Directory& dir);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ void InsertItems::undo() {
bool InsertItems::UpdateItem(const PlaylistItemPtr& updated_item) {
for (int i=0; i<items_.size(); 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;
return true;
}

View File

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

View File

@ -40,16 +40,8 @@ SongList AsxIniParser::Load(QIODevice *device, const QString& playlist_path, con
QString value = line.mid(equals + 1);
if (key.startsWith("ref")) {
Song song;
if (!ParseTrackLocation(value, dir, &song))
qLog(Warning) << "Failed to parse location: " << value;
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(song.filename());
if (library_song.is_valid()) {
ret << library_song;
} else {
song.InitFromFile(song.filename(), -1);
Song song = LoadSong(value, 0, dir);
if (song.is_valid()) {
ret << song;
}
}
@ -64,7 +56,7 @@ void AsxIniParser::Save(const SongList &songs, QIODevice *device, const QDir &di
int n = 1;
foreach (const Song& song, songs) {
s << "Ref" << n << "=" << MakeRelativeTo(song.filename(), dir) << endl;
s << "Ref" << n << "=" << URLOrRelativeFilename(song.url(), dir) << endl;
++n;
}
}

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

View File

@ -31,11 +31,12 @@ class ASXParser : public XMLParser {
bool TryMagic(const QByteArray &data) const;
SongList Load(QIODevice *device, const QString& playlist_path = "", const QDir &dir = QDir()) const;
SongList Load(QIODevice *device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList &songs, QIODevice *device, const QDir &dir = QDir()) const;
private:
Song ParseTrack(QXmlStreamReader* reader) const;
Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const;
};
#endif

View File

@ -207,46 +207,37 @@ SongList CueParser::Load(QIODevice* device, const QString& playlist_path, const
for(int i = 0; i < entries.length(); i++) {
CueEntry entry = entries.at(i);
Song current;
if (!ParseTrackLocation(entry.file, dir, &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);
}
Song song = LoadSong(entry.file, IndexToMarker(entry.index), dir);
// cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
if(cue_mtime.isValid()) {
song.set_mtime(qMax(cue_mtime.toTime_t(), song.mtime()));
}
song.set_cue_path(playlist_path);
// overwrite the stuff, we may have read from the file or library, using
// the current .cue metadata
// set track number only in single-file mode
if(files == 1) {
song.set_track(i + 1);
}
// the last TRACK for every FILE gets it's 'end' marker from the media file's
// length
if(i + 1 < entries.size() && entries.at(i).file == entries.at(i + 1).file) {
// incorrect indices?
if(!UpdateSong(entry, entries.at(i + 1).index, &song)) {
continue;
}
} else {
// incorrect index?
if(!UpdateLastSong(entry, &song)) {
continue;
}
}
ret << song;
// cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
if(cue_mtime.isValid()) {
song.set_mtime(qMax(cue_mtime.toTime_t(), song.mtime()));
}
song.set_cue_path(playlist_path);
// overwrite the stuff, we may have read from the file or library, using
// the current .cue metadata
// set track number only in single-file mode
if(files == 1) {
song.set_track(i + 1);
}
// the last TRACK for every FILE gets it's 'end' marker from the media file's
// length
if(i + 1 < entries.size() && entries.at(i).file == entries.at(i + 1).file) {
// incorrect indices?
if(!UpdateSong(entry, entries.at(i + 1).index, &song)) {
continue;
}
} else {
// incorrect index?
if(!UpdateLastSong(entry, &song)) {
continue;
}
}
ret << song;
}
return ret;

View File

@ -56,29 +56,13 @@ SongList M3UParser::Load(QIODevice* device, const QString& playlist_path, const
}
}
} else if (!line.isEmpty()) {
Song song;
Song song = LoadSong(line, 0, dir);
song.set_title(current_metadata.title);
song.set_artist(current_metadata.artist);
song.set_length_nanosec(current_metadata.length);
ret << song;
// Track location.
if (!ParseTrackLocation(line, dir, &song)) {
qLog(Warning) << "Failed to parse location: " << line;
} else {
// Load the song from the library if it's there.
Song library_song = LoadLibrarySong(song.filename());
if (library_song.is_valid()) {
ret << library_song;
} else {
song.Init(current_metadata.title,
current_metadata.artist,
QString(), // Unknown album.
current_metadata.length);
song.InitFromFile(song.filename(), -1);
ret << song;
}
current_metadata.artist.clear();
current_metadata.title.clear();
current_metadata.length = -1;
}
current_metadata = Metadata();
}
if (buffer.atEnd()) {
break;
@ -115,14 +99,14 @@ bool M3UParser::ParseMetadata(const QString& line, M3UParser::Metadata* metadata
void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir) const {
device->write("#EXTM3U\n");
foreach (const Song& song, songs) {
if (song.filename().isEmpty()) {
if (song.url().isEmpty()) {
continue;
}
QString meta = QString("#EXTINF:%1,%2 - %3\n")
.arg(song.length_nanosec() / kNsecPerSec)
.arg(song.artist()).arg(song.title());
device->write(meta.toUtf8());
device->write(MakeRelativeTo(song.filename(), dir).toUtf8());
device->write(URLOrRelativeFilename(song.url(), dir).toUtf8());
device->write("\n");
}
}

View File

@ -28,67 +28,72 @@ ParserBase::ParserBase(LibraryBackendInterface* library, QObject *parent)
{
}
bool ParserBase::ParseTrackLocation(const QString& filename_or_url,
const QDir& dir, Song* song) const {
void ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning,
const QDir& dir, Song* song) const {
if (filename_or_url.isEmpty()) {
return;
}
QString filename = filename_or_url;
if (filename_or_url.contains(QRegExp("^[a-z]+://"))) {
// Looks like a url.
QUrl temp(filename_or_url);
if (temp.isValid()) {
song->set_filename(temp.toString());
QUrl url(filename_or_url);
if (url.scheme() == "file") {
filename = url.toLocalFile();
} else {
song->set_url(QUrl(filename_or_url));
song->set_filetype(Song::Type_Stream);
song->set_valid(true);
return true;
} else {
return false;
return;
}
}
// Should be a local path.
if (QDir::isAbsolutePath(filename_or_url)) {
// Absolute path.
// Fix windows \, eg. C:\foo -> C:/foo.
song->set_filename(QDir::fromNativeSeparators(filename_or_url));
} else {
// Relative path.
QString proper_path = QDir::fromNativeSeparators(filename_or_url);
QString absolute_path = dir.absoluteFilePath(proper_path);
song->set_filename(absolute_path);
// Convert native separators for Windows paths
filename = QDir::fromNativeSeparators(filename);
// Make the path absolute
if (!QDir::isAbsolutePath(filename)) {
filename = dir.absoluteFilePath(filename);
}
// Use the canonical path
if (QFile::exists(filename)) {
filename = QFileInfo(filename).canonicalFilePath();
}
const QUrl url = QUrl::fromLocalFile(filename);
// Search in the library
Song library_song;
if (library_) {
library_song = library_->GetSongByUrl(url, beginning);
}
// If it was found in the library then use it, otherwise load metadata from
// disk.
if (library_song.is_valid()) {
*song = library_song;
} else {
song->InitFromFile(filename, -1);
}
return true;
}
QString ParserBase::MakeRelativeTo(const QString& filename_or_url,
const QDir& dir) const {
if (filename_or_url.contains(QRegExp("^[a-z]+://")))
return filename_or_url;
Song ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir) const {
Song song;
LoadSong(filename_or_url, beginning, dir, &song);
return song;
}
if (QDir::isAbsolutePath(filename_or_url)) {
QString relative = dir.relativeFilePath(filename_or_url);
QString ParserBase::URLOrRelativeFilename(const QUrl& url, const QDir& dir) const {
if (url.scheme() != "file")
return url.toString();
const QString filename = url.toLocalFile();
if (QDir::isAbsolutePath(filename)) {
const QString relative = dir.relativeFilePath(filename);
if (!relative.contains(".."))
return relative;
}
return filename_or_url;
}
QString ParserBase::MakeUrl(const QString& filename_or_url) const {
if (filename_or_url.contains(QRegExp("^[a-z]+://"))) {
return filename_or_url;
}
return QUrl::fromLocalFile(filename_or_url).toString();
}
Song ParserBase::LoadLibrarySong(const QString& filename_or_url, qint64 beginning) const {
if (!library_)
return Song();
QFileInfo info;
if (filename_or_url.contains("://"))
info.setFile(QUrl(filename_or_url).path());
else
info.setFile(filename_or_url);
return library_->GetSongByFilename(info.canonicalFilePath(), beginning);
return filename;
}

View File

@ -48,22 +48,19 @@ public:
virtual void Save(const SongList& songs, QIODevice* device, const QDir& dir = QDir()) const = 0;
protected:
// Takes a URL, relative path or absolute path, and returns an absolute path.
// Resolves relative paths to "dir".
bool ParseTrackLocation(const QString& filename_or_url, const QDir& dir,
Song* song) const;
// Loads a song. If filename_or_url is a URL (with a scheme other than
// "file") then it is set on the song and the song marked as a stream.
// If it is a filename or a file:// URL then it is made absolute and canonical
// and set as a file:// url on the song. Also sets the song's metadata by
// searching in the Library, or loading from the file as a fallback.
// This function should always be used when loading a playlist.
Song LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir) const;
void LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir, Song* song) const;
// Takes a URL, relative path or absolute path, and in the case of absolute
// paths makes them relative to dir if they are subdirectories.
QString MakeRelativeTo(const QString& filename_or_url, const QDir& dir) const;
// Takes a URL or absolute path and returns a URL
QString MakeUrl(const QString& filename_or_url) const;
// Converts the URL or path to a canonical path and searches the library for
// a section of a song with that path and the given beginning. If one is found,
// returns it, otherwise returns an invalid song.
Song LoadLibrarySong(const QString& filename_or_url, qint64 beginning = 0) const;
// If the URL is a file:// URL then returns its path relative to the
// directory. Otherwise returns the URL as is.
// This function should always be used when saving a playlist.
QString URLOrRelativeFilename(const QUrl& url, const QDir& dir) const;
private:
LibraryBackendInterface* library_;

View File

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

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

View File

@ -36,11 +36,12 @@ class XSPFParser : public XMLParser {
bool TryMagic(const QByteArray &data) const;
SongList Load(QIODevice *device, const QString& playlist_path = "", const QDir &dir = QDir()) const;
SongList Load(QIODevice *device, const QString& playlist_path = "",
const QDir& dir = QDir()) const;
void Save(const SongList &songs, QIODevice *device, const QDir &dir = QDir()) const;
private:
Song ParseTrack(QXmlStreamReader* reader) const;
Song ParseTrack(QXmlStreamReader* reader, const QDir& dir) const;
};
#endif

View File

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

View File

@ -36,5 +36,5 @@ bool JamendoPlaylistItem::InitFromQuery(const SqlRow& query) {
}
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;
QString ogg_url = QString(kOggStreamUrl).arg(id_text);
song.set_filename(ogg_url);
song.set_url(QUrl(ogg_url));
song.set_art_automatic(album_cover);
song.set_valid(true);

View File

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

View File

@ -150,7 +150,7 @@ class LastFMService : public RadioService {
private:
QStandardItem* CreateStationItem(QStandardItem* parent,
const QString& name, const QString& icon, const QString& url,
const QString& name, const QString& icon, const QUrl& url,
const QString& title);
QString ErrorString(lastfm::ws::Error error) const;
bool InitScrobbler();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.
%End
const QString& filename() const;
const QUrl& url() const;
%Docstring
filename() -> str
The filename I{or URL} of this song.
url() -> L{PyQt4.QtCore.QUrl}
The URL of this song.
%End
const QString& basefilename() const;
%Docstring
basefilename() -> str
The filename of this song without any directory component.
%End
QUrl url() const;
%Docstring
url() -> L{PyQt4.QtCore.QUrl}
URL for this song which may point either to a file or to another type of stream.
%End
uint mtime() const;
@ -658,9 +652,9 @@ set_lastplayed(lastplayed)
set_score(score)
%End
void set_filename(const QString& v);
void set_url(const QUrl& v);
%Docstring
set_filename(filename)
set_url(url)
%End
void set_basefilename(const QString& v);

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ QMimeData* AlbumCoverManagerList::mimeData(const QList<QListWidgetItem*> items)
// Get URLs from the songs
QList<QUrl> urls;
foreach (const Song& song, songs) {
urls << QUrl::fromLocalFile(song.filename());
urls << song.url();
}
// 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()) {
// Try reloading the tags from file
Song copy(song);
copy.InitFromFile(copy.filename(), copy.directory_id());
copy.InitFromFile(copy.url().toLocalFile(), copy.directory_id());
if (copy.is_valid())
ret << Data(copy);
@ -424,7 +424,11 @@ void EditTagDialog::UpdateSummaryTab(const Song& song) {
QLocale::system().dateTimeFormat(QLocale::LongFormat)));
ui_->filesize->setText(Utilities::PrettySize(song.filesize()));
ui_->filetype->setText(song.TextForFiletype());
ui_->filename->setText(QDir::toNativeSeparators(song.filename()));
if (song.url().scheme() == "file")
ui_->filename->setText(QDir::toNativeSeparators(song.url().toLocalFile()));
else
ui_->filename->setText(song.url().toString());
album_cover_choice_controller_->search_for_cover_action()->setEnabled(CoverProviders::instance().HasAnyProviders());
}
@ -602,7 +606,7 @@ void EditTagDialog::SaveData(const QList<Data>& data) {
continue;
if (!ref.current_.Save()) {
emit Error(tr("An error occurred writing metadata to '%1'").arg(ref.current_.filename()));
emit Error(tr("An error occurred writing metadata to '%1'").arg(ref.current_.url().toLocalFile()));
}
}
}
@ -736,12 +740,12 @@ void EditTagDialog::FetchTag() {
}
void EditTagDialog::FetchTagSongChosen(const Song& original_song, const Song& new_metadata) {
const QString filename = original_song.filename();
const QString filename = original_song.url().toLocalFile();
// Find the song with this filename
for (int i=0 ; i<data_.count() ; ++i) {
Data* data = &data_[i];
if (data->original_.filename() != filename)
if (data->original_.url().toLocalFile() != filename)
continue;
// Is it currently being displayed in the UI?

View File

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

View File

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

View File

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

View File

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

View File

@ -45,7 +45,7 @@ TEST_F(ASXParserTest, ParsesOneTrackFromXML) {
const Song& song = songs[0];
EXPECT_EQ("Foo", song.title());
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());
}
@ -64,8 +64,8 @@ TEST_F(ASXParserTest, ParsesMoreThanOneTrackFromXML) {
ASXParser parser(NULL);
SongList songs = parser.Load(&buffer);
ASSERT_EQ(2, songs.length());
EXPECT_EQ("http://example.com/foo.mp3", songs[0].filename());
EXPECT_EQ("http://example.com/bar.mp3", songs[1].filename());
EXPECT_EQ(QUrl("http://example.com/foo.mp3"), songs[0].url());
EXPECT_EQ(QUrl("http://example.com/bar.mp3"), songs[1].url());
EXPECT_TRUE(songs[0].is_stream());
EXPECT_TRUE(songs[1].is_stream());
}
@ -90,7 +90,7 @@ TEST_F(ASXParserTest, ParsesBrokenXmlEntities) {
ASXParser parser(NULL);
SongList songs = parser.Load(&buffer);
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) {
@ -99,7 +99,7 @@ TEST_F(ASXParserTest, SavesSong) {
buffer.open(QIODevice::WriteOnly);
ASXParser parser(NULL);
Song one;
one.set_filename("http://www.example.com/foo.mp3");
one.set_url(QUrl("http://www.example.com/foo.mp3"));
one.set_filetype(Song::Type_Stream);
one.set_title("foo");
one.set_length_nanosec(123 * kNsecPerSec);
@ -123,5 +123,5 @@ TEST_F(ASXParserTest, ParsesSomaFM) {
ASSERT_EQ(4, songs.count());
EXPECT_EQ("SomaFM: Secret Agent", songs[0].title());
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 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("Album", first_song.album());
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(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("Album", second_song.album());
ASSERT_EQ("Zucchero himself", second_song.artist());
@ -189,7 +189,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
Song fourth_song = song_list.at(3);
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("Artist One Album", first_song.album());
ASSERT_EQ("Artist One", first_song.artist());
@ -199,7 +199,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
ASSERT_EQ(-1, first_song.track());
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("Artist One Album", second_song.album());
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(-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("Artist Two Album", third_song.album());
ASSERT_EQ("Artist X", third_song.artist());
@ -217,7 +217,7 @@ TEST_F(CueParserTest, AcceptsMultipleFileBasedCues) {
ASSERT_EQ(-1, third_song.track());
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("Artist Two Album", fourth_song.album());
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(-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("Artist Two Album", fifth_song.album());
ASSERT_EQ("Artist Two", fifth_song.artist());
@ -253,7 +253,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
Song fourth_song = song_list.at(3);
// 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 Album", first_song.album());
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(-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 Album", second_song.album());
ASSERT_EQ("A3", second_song.title());
@ -271,7 +271,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
// all B* songs are broken
// 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 Album", third_song.album());
ASSERT_EQ("C1", third_song.title());
@ -279,7 +279,7 @@ TEST_F(CueParserTest, SkipsBrokenSongsInMultipleFileBasedCues) {
ASSERT_EQ(-1, third_song.track());
// 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 Album", fourth_song.album());
ASSERT_EQ("D2", fourth_song.title());
@ -302,14 +302,14 @@ TEST_F(CueParserTest, SkipsDataFiles) {
Song first_song = song_list.at(0);
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 Album", first_song.album());
ASSERT_EQ("A1", first_song.title());
ASSERT_EQ(to_nanosec(0, 1, 0), first_song.beginning_nanosec());
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 Album", second_song.album());
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
Song ret;
ret.set_directory_id(directory_id);
ret.set_filename("foo.mp3");
ret.set_url(QUrl::fromLocalFile("foo.mp3"));
ret.set_mtime(1);
ret.set_ctime(1);
ret.set_filesize(1);
@ -109,7 +109,7 @@ TEST_F(LibraryBackendTest, AddInvalidSong) {
backend_->AddOrUpdateSongs(SongList() << s);
ASSERT_EQ(1, spy.count()); spy.takeFirst();
s.set_filename("foo");
s.set_url(QUrl::fromLocalFile("foo"));
backend_->AddOrUpdateSongs(SongList() << s);
ASSERT_EQ(1, spy.count()); spy.takeFirst();

View File

@ -52,7 +52,7 @@ class LibraryModelTest : public ::testing::Test {
song.set_directory_id(1);
if (song.mtime() == -1) song.set_mtime(1);
if (song.ctime() == -1) song.set_ctime(1);
if (song.filename().isNull()) song.set_filename("/tmp/foo");
if (song.url().isEmpty()) song.set_url(QUrl("file:///tmp/foo"));
if (song.filesize() == -1) song.set_filesize(1);
if (!added_dir_) {

View File

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

View File

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

View File

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

View File

@ -48,7 +48,7 @@ TEST_F(PLSParserTest, ParseOneTrack) {
SongList songs = parser_.Load(file.get(), "", QDir("/relative/to/"));
ASSERT_EQ(1, songs.length());
EXPECT_EQ("/relative/to/filename with spaces.mp3", songs[0].filename());
EXPECT_EQ(QUrl("file:///relative/to/filename with spaces.mp3"), songs[0].url());
EXPECT_EQ("Title", songs[0].title());
EXPECT_EQ(123 * kNsecPerSec, songs[0].length_nanosec());
}
@ -58,10 +58,10 @@ TEST_F(PLSParserTest, ParseSomaFM) {
SongList songs = parser_.Load(file.get());
ASSERT_EQ(4, songs.length());
EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1018", songs[0].filename());
EXPECT_EQ("http://streamer-mtc-aa03.somafm.com:80/stream/1018", songs[1].filename());
EXPECT_EQ("http://streamer-ntc-aa04.somafm.com:80/stream/1018", songs[2].filename());
EXPECT_EQ("http://ice.somafm.com/groovesalad", songs[3].filename());
EXPECT_EQ(QUrl("http://streamer-dtc-aa05.somafm.com:80/stream/1018"), songs[0].url());
EXPECT_EQ(QUrl("http://streamer-mtc-aa03.somafm.com:80/stream/1018"), songs[1].url());
EXPECT_EQ(QUrl("http://streamer-ntc-aa04.somafm.com:80/stream/1018"), songs[2].url());
EXPECT_EQ(QUrl("http://ice.somafm.com/groovesalad"), songs[3].url());
EXPECT_EQ("SomaFM: Groove Salad (#1 128k mp3): A nicely chilled plate of ambient beats and grooves.", songs[0].title());
EXPECT_EQ("SomaFM: Groove Salad (#2 128k mp3): A nicely chilled plate of ambient beats and grooves.", songs[1].title());
EXPECT_EQ("SomaFM: Groove Salad (#3 128k mp3): A nicely chilled plate of ambient beats and grooves.", songs[2].title());
@ -74,10 +74,10 @@ TEST_F(PLSParserTest, ParseSomaFM2) {
SongList songs = parser_.Load(file.get());
ASSERT_EQ(4, songs.length());
EXPECT_EQ("http://streamer-ntc-aa03.somafm.com:80/stream/1021", songs[0].filename());
EXPECT_EQ("http://streamer-mtc-aa04.somafm.com:80/stream/1021", songs[1].filename());
EXPECT_EQ("http://streamer-dtc-aa05.somafm.com:80/stream/1021", songs[2].filename());
EXPECT_EQ("http://ice.somafm.com/secretagent", songs[3].filename());
EXPECT_EQ(QUrl("http://streamer-ntc-aa03.somafm.com:80/stream/1021"), songs[0].url());
EXPECT_EQ(QUrl("http://streamer-mtc-aa04.somafm.com:80/stream/1021"), songs[1].url());
EXPECT_EQ(QUrl("http://streamer-dtc-aa05.somafm.com:80/stream/1021"), songs[2].url());
EXPECT_EQ(QUrl("http://ice.somafm.com/secretagent"), songs[3].url());
EXPECT_EQ("SomaFM: Secret Agent (#1 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!", songs[0].title());
EXPECT_EQ("SomaFM: Secret Agent (#2 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!", songs[1].title());
EXPECT_EQ("SomaFM: Secret Agent (#3 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!", songs[2].title());
@ -87,11 +87,11 @@ TEST_F(PLSParserTest, ParseSomaFM2) {
TEST_F(PLSParserTest, SaveAndLoad) {
Song one;
one.set_filename("http://www.example.com/foo.mp3");
one.set_url(QUrl("http://www.example.com/foo.mp3"));
one.set_title("Foo, with, some, commas");
Song two;
two.set_filename("relative/bar.mp3");
two.set_url(QUrl("relative/bar.mp3"));
two.set_title("Bar");
two.set_length_nanosec(123 * kNsecPerSec);
@ -106,8 +106,8 @@ TEST_F(PLSParserTest, SaveAndLoad) {
songs = parser_.Load(&temp, "", QDir("/meep"));
ASSERT_EQ(2, songs.count());
EXPECT_EQ(one.filename(), songs[0].filename());
EXPECT_EQ("/meep/relative/bar.mp3", songs[1].filename());
EXPECT_EQ(one.url(), songs[0].url());
EXPECT_EQ(QUrl("file:///meep/relative/bar.mp3"), songs[1].url());
EXPECT_EQ(one.title(), songs[0].title());
EXPECT_EQ(two.title(), songs[1].title());
EXPECT_EQ(one.length_nanosec(), songs[0].length_nanosec());

View File

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

View File

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

View File

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

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 ::QVariant& var, std::ostream& os);
void PrintTo(const ::QUrl& url, std::ostream& os);
#define EXPOSE_SIGNAL0(n) \
void Emit##n() { emit n(); }

View File

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