Add source to songs and playlist_items

This commit is contained in:
Jonas Kvinge 2018-09-08 12:38:02 +02:00
parent 19b645d731
commit 6d686ee66a
48 changed files with 500 additions and 401 deletions

View File

@ -3,6 +3,7 @@
<file>schema/schema.sql</file> <file>schema/schema.sql</file>
<file>schema/schema-1.sql</file> <file>schema/schema-1.sql</file>
<file>schema/schema-2.sql</file> <file>schema/schema-2.sql</file>
<file>schema/schema-3.sql</file>
<file>schema/device-schema.sql</file> <file>schema/device-schema.sql</file>
<file>style/strawberry.css</file> <file>style/strawberry.css</file>
<file>misc/playing_tooltip.txt</file> <file>misc/playing_tooltip.txt</file>

View File

@ -11,8 +11,6 @@ CREATE TABLE device_%deviceid_subdirectories (
CREATE TABLE device_%deviceid_songs ( CREATE TABLE device_%deviceid_songs (
/* Metadata from taglib */
title TEXT NOT NULL, title TEXT NOT NULL,
album TEXT NOT NULL, album TEXT NOT NULL,
artist TEXT NOT NULL, artist TEXT NOT NULL,
@ -27,6 +25,7 @@ CREATE TABLE device_%deviceid_songs (
performer TEXT NOT NULL, performer TEXT NOT NULL,
grouping TEXT NOT NULL, grouping TEXT NOT NULL,
comment TEXT NOT NULL, comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0,
@ -35,8 +34,7 @@ CREATE TABLE device_%deviceid_songs (
samplerate INTEGER NOT NULL DEFAULT 0, samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0, bitdepth INTEGER NOT NULL DEFAULT 0,
/* Information about the file on disk */ source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL, directory_id INTEGER NOT NULL,
filename TEXT NOT NULL, filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
@ -45,8 +43,6 @@ CREATE TABLE device_%deviceid_songs (
ctime INTEGER NOT NULL, ctime INTEGER NOT NULL,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
/* Other */
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0, lastplayed INTEGER NOT NULL DEFAULT 0,

65
data/schema/schema-3.sql Normal file
View File

@ -0,0 +1,65 @@
ALTER TABLE songs ADD COLUMN source INTEGER NOT NULL DEFAULT 0;
UPDATE songs SET source = 2 WHERE source = 0;
DROP TABLE playlist_items;
CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL,
type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER,
url TEXT,
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER,
filename TEXT,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER,
mtime INTEGER,
ctime INTEGER,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
UPDATE schema_version SET version=3;

View File

@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
DELETE FROM schema_version; DELETE FROM schema_version;
INSERT INTO schema_version (version) VALUES (2); INSERT INTO schema_version (version) VALUES (3);
CREATE TABLE IF NOT EXISTS directories ( CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL, path TEXT NOT NULL,
@ -19,8 +19,6 @@ CREATE TABLE IF NOT EXISTS subdirectories (
CREATE TABLE IF NOT EXISTS songs ( CREATE TABLE IF NOT EXISTS songs (
/* Metadata from taglib */
title TEXT NOT NULL, title TEXT NOT NULL,
album TEXT NOT NULL, album TEXT NOT NULL,
artist TEXT NOT NULL, artist TEXT NOT NULL,
@ -44,8 +42,7 @@ CREATE TABLE IF NOT EXISTS songs (
samplerate INTEGER NOT NULL DEFAULT 0, samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0, bitdepth INTEGER NOT NULL DEFAULT 0,
/* Information about the file on disk */ source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL, directory_id INTEGER NOT NULL,
filename TEXT NOT NULL, filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
@ -54,8 +51,6 @@ CREATE TABLE IF NOT EXISTS songs (
ctime INTEGER NOT NULL, ctime INTEGER NOT NULL,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
/* Other */
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0, lastplayed INTEGER NOT NULL DEFAULT 0,
@ -89,13 +84,10 @@ CREATE TABLE IF NOT EXISTS playlists (
CREATE TABLE IF NOT EXISTS playlist_items ( CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL, playlist INTEGER NOT NULL,
type TEXT NOT NULL, type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER, collection_id INTEGER,
internet_service TEXT,
url TEXT, url TEXT,
/* Metadata from taglib */
title TEXT NOT NULL, title TEXT NOT NULL,
album TEXT NOT NULL, album TEXT NOT NULL,
artist TEXT NOT NULL, artist TEXT NOT NULL,
@ -119,8 +111,7 @@ CREATE TABLE IF NOT EXISTS playlist_items (
samplerate INTEGER NOT NULL DEFAULT 0, samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0, bitdepth INTEGER NOT NULL DEFAULT 0,
/* Information about the file on disk */ source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER, directory_id INTEGER,
filename TEXT, filename TEXT,
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
@ -129,8 +120,6 @@ CREATE TABLE IF NOT EXISTS playlist_items (
ctime INTEGER, ctime INTEGER,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
/* Other */
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0, lastplayed INTEGER NOT NULL DEFAULT 0,

View File

@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com> Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -474,27 +475,27 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
} }
pb::tagreader::SongMetadata_Type TagReader::GuessFileType(TagLib::FileRef *fileref) const { pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_WAV; if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_FLAC; if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_FLAC;
if (dynamic_cast<TagLib::WavPack::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_WAVPACK; if (dynamic_cast<TagLib::WavPack::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAVPACK;
if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGFLAC; if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGFLAC;
if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGVORBIS; if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGVORBIS;
if (dynamic_cast<TagLib::Ogg::Opus::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGOPUS; if (dynamic_cast<TagLib::Ogg::Opus::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGOPUS;
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGSPEEX; if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGSPEEX;
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPEG; if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MPEG;
if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MP4; if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MP4;
if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_ASF; if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_ASF;
if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_AIFF; if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_AIFF;
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPC; if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MPC;
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_TRUEAUDIO; if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_TRUEAUDIO;
#ifdef HAVE_TAGLIB_DSFFILE #ifdef HAVE_TAGLIB_DSFFILE
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_DSF; if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_DSF;
if (dynamic_cast<TagLib::DSDIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_DSDIFF; if (dynamic_cast<TagLib::DSDIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_DSDIFF;
#endif #endif
return pb::tagreader::SongMetadata_Type_UNKNOWN; return pb::tagreader::SongMetadata_FileType_UNKNOWN;
} }

View File

@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com> Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -64,7 +65,7 @@ class TagReader {
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const; void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const; void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
pb::tagreader::SongMetadata_Type GuessFileType(TagLib::FileRef *fileref) const; pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const; void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const; void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;

View File

@ -4,7 +4,7 @@ package pb.tagreader;
message SongMetadata { message SongMetadata {
enum Type { enum FileType {
UNKNOWN = 0; UNKNOWN = 0;
WAV = 1; WAV = 1;
FLAC = 2; FLAC = 2;
@ -51,7 +51,7 @@ message SongMetadata {
optional string url = 21; optional string url = 21;
optional string basefilename = 22; optional string basefilename = 22;
optional Type filetype = 23; optional FileType filetype = 23;
optional int32 filesize = 24; optional int32 filesize = 24;
optional int32 mtime = 25; optional int32 mtime = 25;
optional int32 ctime = 26; optional int32 ctime = 26;

View File

@ -63,7 +63,6 @@ SCollection::SCollection(Application *app, QObject *parent)
} }
SCollection::~SCollection() { SCollection::~SCollection() {
watcher_->deleteLater(); watcher_->deleteLater();
watcher_thread_->exit(); watcher_thread_->exit();
watcher_thread_->wait(5000 /* five seconds */); watcher_thread_->wait(5000 /* five seconds */);

View File

@ -720,7 +720,6 @@ SongList CollectionBackend::GetSongsByUrl(const QUrl &url) {
while (query.Next()) { while (query.Next()) {
Song song; Song song;
song.InitFromQuery(query, true); song.InitFromQuery(query, true);
songlist << song; songlist << song;
} }
} }

View File

@ -29,11 +29,13 @@
class SqlRow; class SqlRow;
CollectionPlaylistItem::CollectionPlaylistItem(const QString &type) CollectionPlaylistItem::CollectionPlaylistItem(const Song::Source &source)
: PlaylistItem(type) {} : PlaylistItem(source) {}
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) CollectionPlaylistItem::CollectionPlaylistItem(const Song &song)
: PlaylistItem("Collection"), song_(song) {} : PlaylistItem(Song::Source_Collection), song_(song) {
song_.set_source(Song::Source_Collection);
}
QUrl CollectionPlaylistItem::Url() const { return song_.url(); } QUrl CollectionPlaylistItem::Url() const { return song_.url(); }
@ -44,7 +46,6 @@ void CollectionPlaylistItem::Reload() {
bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) { bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) {
// Rows from the songs tables come first // Rows from the songs tables come first
song_.InitFromQuery(query, true); song_.InitFromQuery(query, true);
return song_.is_valid(); return song_.is_valid();
} }
@ -59,4 +60,3 @@ Song CollectionPlaylistItem::Metadata() const {
if (HasTemporaryMetadata()) return temp_metadata_; if (HasTemporaryMetadata()) return temp_metadata_;
return song_; return song_;
} }

View File

@ -36,7 +36,7 @@ class SqlRow;
class CollectionPlaylistItem : public PlaylistItem { class CollectionPlaylistItem : public PlaylistItem {
public: public:
CollectionPlaylistItem(const QString &type); CollectionPlaylistItem(const Song::Source &source);
CollectionPlaylistItem(const Song &song); CollectionPlaylistItem(const Song &song);
bool InitFromQuery(const SqlRow &query); bool InitFromQuery(const SqlRow &query);

View File

@ -134,7 +134,7 @@ CollectionWatcher::ScanTransaction::~ScanTransaction() {
if (watcher_->monitor_) { if (watcher_->monitor_) {
// Watch the new subdirectories // Watch the new subdirectories
for (const Subdirectory& subdir : new_subdirs) { for (const Subdirectory &subdir : new_subdirs) {
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path); watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path);
} }
} }
@ -197,8 +197,7 @@ SubdirectoryList CollectionWatcher::ScanTransaction::GetImmediateSubdirs(const Q
SubdirectoryList ret; SubdirectoryList ret;
for (const Subdirectory &subdir : known_subdirs_) { for (const Subdirectory &subdir : known_subdirs_) {
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && subdir.mtime != 0) {
subdir.mtime != 0) {
ret << subdir; ret << subdir;
} }
} }
@ -251,7 +250,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// Do not scan symlinked dirs that are already in collection // Do not scan symlinked dirs that are already in collection
if (path_info.isSymLink()) { if (path_info.isSymLink()) {
QString real_path = path_info.symLinkTarget(); QString real_path = path_info.symLinkTarget();
for (const Directory& dir : watched_dirs_) { for (const Directory &dir : watched_dirs_) {
if (real_path.startsWith(dir.path)) { if (real_path.startsWith(dir.path)) {
t->AddToProgress(1); t->AddToProgress(1);
return; return;
@ -278,7 +277,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist any more. // If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist any more.
// If one has been removed, "rescan" it to get the deleted songs // If one has been removed, "rescan" it to get the deleted songs
SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path); SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
for (const Subdirectory& subdir : previous_subdirs) { for (const Subdirectory &subdir : previous_subdirs) {
if (!QFile::exists(subdir.path) && subdir.path != path) { if (!QFile::exists(subdir.path) && subdir.path != path) {
t->AddToProgressMax(1); t->AddToProgressMax(1);
ScanSubdirectory(subdir.path, subdir, t, true); ScanSubdirectory(subdir.path, subdir, t, true);
@ -322,7 +321,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
QSet<QString> cues_processed; QSet<QString> cues_processed;
// Now compare the list from the database with the list of files on disk // Now compare the list from the database with the list of files on disk
for (const QString& file : files_on_disk) { for (const QString &file : files_on_disk) {
if (stop_requested_) return; if (stop_requested_) return;
// associated cue // associated cue
@ -389,16 +388,16 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
QString image = ImageForSong(file, album_art); QString image = ImageForSong(file, album_art);
for (Song song : song_list) { for (Song song : song_list) {
song.set_source(Song::Source_Collection);
song.set_directory_id(t->dir()); song.set_directory_id(t->dir());
if (song.art_automatic().isEmpty()) song.set_art_automatic(image); if (song.art_automatic().isEmpty()) song.set_art_automatic(image);
t->new_songs << song; t->new_songs << song;
} }
} }
} }
// Look for deleted songs // Look for deleted songs
for (const Song& song : songs_in_db) { for (const Song &song : songs_in_db) {
if (!song.is_unavailable() && !files_on_disk.contains(song.url().toLocalFile())) { if (!song.is_unavailable() && !files_on_disk.contains(song.url().toLocalFile())) {
qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile(); qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile();
t->deleted_songs << song; t->deleted_songs << song;
@ -420,7 +419,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// Recurse into the new subdirs that we found // Recurse into the new subdirs that we found
t->AddToProgressMax(my_new_subdirs.count()); t->AddToProgressMax(my_new_subdirs.count());
for (const Subdirectory& my_new_subdir : my_new_subdirs) { for (const Subdirectory &my_new_subdir : my_new_subdirs) {
if (stop_requested_) return; if (stop_requested_) return;
ScanSubdirectory(my_new_subdir.path, my_new_subdir, t, true); ScanSubdirectory(my_new_subdir.path, my_new_subdir, t, true);
} }
@ -435,7 +434,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
SongList old_sections = backend_->GetSongsByUrl(QUrl::fromLocalFile(file)); SongList old_sections = backend_->GetSongsByUrl(QUrl::fromLocalFile(file));
QHash<quint64, Song> sections_map; QHash<quint64, Song> sections_map;
for (const Song& song : old_sections) { for (const Song &song : old_sections) {
sections_map[song.beginning_nanosec()] = song; sections_map[song.beginning_nanosec()] = song;
} }
@ -443,6 +442,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
// Update every song that's in the cue and collection // Update every song that's in the cue and collection
for (Song cue_song : cue_parser_->Load(&cue, matching_cue, path)) { for (Song cue_song : cue_parser_->Load(&cue, matching_cue, path)) {
cue_song.set_source(Song::Source_Collection);
cue_song.set_directory_id(t->dir()); cue_song.set_directory_id(t->dir());
Song matching = sections_map[cue_song.beginning_nanosec()]; Song matching = sections_map[cue_song.beginning_nanosec()];
@ -450,7 +450,8 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
if (!matching.is_valid()) { if (!matching.is_valid()) {
t->new_songs << cue_song; t->new_songs << cue_song;
// changed section // changed section
} else { }
else {
PreserveUserSetData(file, image, matching, &cue_song, t); PreserveUserSetData(file, image, matching, &cue_song, t);
used_ids.insert(matching.id()); used_ids.insert(matching.id());
} }
@ -469,8 +470,7 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file, const So
// If a cue got deleted, we turn it's first section into the new 'raw' (cueless) song and we just remove the rest of the sections from the collection // If a cue got deleted, we turn it's first section into the new 'raw' (cueless) song and we just remove the rest of the sections from the collection
if (cue_deleted) { if (cue_deleted) {
for (const Song &song : for (const Song &song : backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
if (!song.IsMetadataEqual(matching_song)) { if (!song.IsMetadataEqual(matching_song)) {
t->deleted_songs << song; t->deleted_songs << song;
} }
@ -478,6 +478,7 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file, const So
} }
Song song_on_disk; Song song_on_disk;
song_on_disk.set_source(Song::Source_Collection);
song_on_disk.set_directory_id(t->dir()); song_on_disk.set_directory_id(t->dir());
TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk); TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk);
@ -504,7 +505,7 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
// Also, watch out for incorrect media files. // Also, watch out for incorrect media files.
// Playlist parser for CUEs considers every entry in sheet valid and we don't want invalid media getting into collection! // Playlist parser for CUEs considers every entry in sheet valid and we don't want invalid media getting into collection!
QString file_nfd = file.normalized(QString::NormalizationForm_D); QString file_nfd = file.normalized(QString::NormalizationForm_D);
for (const Song& cue_song : cue_parser_->Load(&cue, matching_cue, path)) { for (const Song &cue_song : cue_parser_->Load(&cue, matching_cue, path)) {
if (cue_song.url().toLocalFile().normalized(QString::NormalizationForm_D) == file_nfd) { if (cue_song.url().toLocalFile().normalized(QString::NormalizationForm_D) == file_nfd) {
if (TagReaderClient::Instance()->IsMediaFileBlocking(file)) { if (TagReaderClient::Instance()->IsMediaFileBlocking(file)) {
song_list << cue_song; song_list << cue_song;
@ -663,7 +664,7 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
for (const QString &filter_text : best_image_filters_) { for (const QString &filter_text : best_image_filters_) {
// The images in the images list are represented by a full path, so we need to isolate just the filename // The images in the images list are represented by a full path, so we need to isolate just the filename
for (const QString& image : images) { for (const QString &image : images) {
QFileInfo file_info(image); QFileInfo file_info(image);
QString filename(file_info.fileName()); QString filename(file_info.fileName());
if (filename.contains(filter_text, Qt::CaseInsensitive)) if (filename.contains(filter_text, Qt::CaseInsensitive))
@ -683,7 +684,7 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
int biggest_size = 0; int biggest_size = 0;
QString biggest_path; QString biggest_path;
for (const QString& path : filtered) { for (const QString &path : filtered) {
QImage image(path); QImage image(path);
if (image.isNull()) continue; if (image.isNull()) continue;

View File

@ -52,7 +52,7 @@
#include "scopedtransaction.h" #include "scopedtransaction.h"
const char *Database::kDatabaseFilename = "strawberry.db"; const char *Database::kDatabaseFilename = "strawberry.db";
const int Database::kSchemaVersion = 2; const int Database::kSchemaVersion = 3;
const char *Database::kMagicAllSongsTables = "%allsongstables"; const char *Database::kMagicAllSongsTables = "%allsongstables";
int Database::sNextConnectionId = 1; int Database::sNextConnectionId = 1;

View File

@ -1848,7 +1848,7 @@ void MainWindow::EditFileTags(const QList<QUrl> &urls) {
Song song; Song song;
song.set_url(url); song.set_url(url);
song.set_valid(true); song.set_valid(true);
song.set_filetype(Song::Type_MPEG); song.set_filetype(Song::FileType_MPEG);
songs << song; songs << song;
} }

View File

@ -71,7 +71,7 @@ class MusicStorage {
virtual QString LocalPath() const { return QString(); } virtual QString LocalPath() const { return QString(); }
virtual TranscodeMode GetTranscodeMode() const { return Transcode_Never; } virtual TranscodeMode GetTranscodeMode() const { return Transcode_Never; }
virtual Song::FileType GetTranscodeFormat() const { return Song::Type_Unknown; } virtual Song::FileType GetTranscodeFormat() const { return Song::FileType_Unknown; }
virtual bool GetSupportedFiletypes(QList<Song::FileType>* ret) { return true; } virtual bool GetSupportedFiletypes(QList<Song::FileType>* ret) { return true; }
virtual bool StartCopy(QList<Song::FileType>* supported_types) { return true;} virtual bool StartCopy(QList<Song::FileType>* supported_types) { return true;}

View File

@ -154,7 +154,7 @@ void Organise::ProcessSomeFiles() {
else { else {
// Figure out if we need to transcode it // Figure out if we need to transcode it
Song::FileType dest_type = CheckTranscode(song.filetype()); Song::FileType dest_type = CheckTranscode(song.filetype());
if (dest_type != Song::Type_Unknown) { if (dest_type != Song::FileType_Unknown) {
// Get the preset // Get the preset
TranscoderPreset preset = Transcoder::PresetForFileType(dest_type); TranscoderPreset preset = Transcoder::PresetForFileType(dest_type);
qLog(Debug) << "Transcoding with" << preset.name_; qLog(Debug) << "Transcoding with" << preset.name_;
@ -206,28 +206,28 @@ void Organise::ProcessSomeFiles() {
Song::FileType Organise::CheckTranscode(Song::FileType original_type) const { Song::FileType Organise::CheckTranscode(Song::FileType original_type) const {
//if (original_type == Song::Type_Stream) return Song::Type_Unknown; if (original_type == Song::FileType_Stream) return Song::FileType_Unknown;
const MusicStorage::TranscodeMode mode = destination_->GetTranscodeMode(); const MusicStorage::TranscodeMode mode = destination_->GetTranscodeMode();
const Song::FileType format = destination_->GetTranscodeFormat(); const Song::FileType format = destination_->GetTranscodeFormat();
switch (mode) { switch (mode) {
case MusicStorage::Transcode_Never: case MusicStorage::Transcode_Never:
return Song::Type_Unknown; return Song::FileType_Unknown;
case MusicStorage::Transcode_Always: case MusicStorage::Transcode_Always:
if (original_type == format) return Song::Type_Unknown; if (original_type == format) return Song::FileType_Unknown;
return format; return format;
case MusicStorage::Transcode_Unsupported: case MusicStorage::Transcode_Unsupported:
if (supported_filetypes_.isEmpty() || supported_filetypes_.contains(original_type)) return Song::Type_Unknown; if (supported_filetypes_.isEmpty() || supported_filetypes_.contains(original_type)) return Song::FileType_Unknown;
if (format != Song::Type_Unknown) return format; if (format != Song::FileType_Unknown) return format;
// The user hasn't visited the device properties page yet to set a preferred format for the device, so we have to pick the best available one. // The user hasn't visited the device properties page yet to set a preferred format for the device, so we have to pick the best available one.
return Transcoder::PickBestFormat(supported_filetypes_); return Transcoder::PickBestFormat(supported_filetypes_);
} }
return Song::Type_Unknown; return Song::FileType_Unknown;
} }

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -38,6 +39,7 @@
#include <QRegExp> #include <QRegExp>
#include <QUrl> #include <QUrl>
#include <QImage> #include <QImage>
#include <QIcon>
#include <QTextCodec> #include <QTextCodec>
#include <QSqlQuery> #include <QSqlQuery>
#include <QtDebug> #include <QtDebug>
@ -52,6 +54,7 @@
#include "core/logging.h" #include "core/logging.h"
#include "core/messagehandler.h" #include "core/messagehandler.h"
#include "core/iconloader.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "timeconstants.h" #include "timeconstants.h"
@ -86,6 +89,7 @@ const QStringList Song::kColumns = QStringList() << "title"
<< "samplerate" << "samplerate"
<< "bitdepth" << "bitdepth"
<< "source"
<< "directory_id" << "directory_id"
<< "filename" << "filename"
<< "filetype" << "filetype"
@ -167,6 +171,7 @@ struct Song::Private : public QSharedData {
int samplerate_; int samplerate_;
int bitdepth_; int bitdepth_;
Source source_;
int directory_id_; int directory_id_;
QString basefilename_; QString basefilename_;
QUrl url_; QUrl url_;
@ -210,9 +215,11 @@ Song::Private::Private()
end_(-1), end_(-1),
bitrate_(-1), bitrate_(-1),
samplerate_(-1), samplerate_(-1),
bitdepth_(-1),
source_(Source_Unknown),
directory_id_(-1), directory_id_(-1),
filetype_(Type_Unknown), filetype_(FileType_Unknown),
filesize_(-1), filesize_(-1),
mtime_(-1), mtime_(-1),
ctime_(-1), ctime_(-1),
@ -283,6 +290,7 @@ qint64 Song::length_nanosec() const { return d->end_ - d->beginning_; }
int Song::bitrate() const { return d->bitrate_; } int Song::bitrate() const { return d->bitrate_; }
int Song::samplerate() const { return d->samplerate_; } int Song::samplerate() const { return d->samplerate_; }
int Song::bitdepth() const { return d->bitdepth_; } int Song::bitdepth() const { return d->bitdepth_; }
Song::Source Song::source() const { return d->source_; }
int Song::directory_id() const { return d->directory_id_; } int Song::directory_id() const { return d->directory_id_; }
const QUrl &Song::url() const { return d->url_; } const QUrl &Song::url() const { return d->url_; }
const QString &Song::basefilename() const { return d->basefilename_; } const QString &Song::basefilename() const { return d->basefilename_; }
@ -290,8 +298,8 @@ uint Song::mtime() const { return d->mtime_; }
uint Song::ctime() const { return d->ctime_; } uint Song::ctime() const { return d->ctime_; }
int Song::filesize() const { return d->filesize_; } int Song::filesize() const { return d->filesize_; }
Song::FileType Song::filetype() const { return d->filetype_; } Song::FileType Song::filetype() const { return d->filetype_; }
bool Song::is_stream() const { return d->filetype_ == Type_Stream; } bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal; }
bool Song::is_cdda() const { return d->filetype_ == Type_CDDA; } bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
bool Song::is_collection_song() const { bool Song::is_collection_song() const {
return !is_cdda() && !is_stream() && id() != -1; return !is_cdda() && !is_stream() && id() != -1;
} }
@ -331,6 +339,7 @@ void Song::set_bitrate(int v) { d->bitrate_ = v; }
void Song::set_samplerate(int v) { d->samplerate_ = v; } void Song::set_samplerate(int v) { d->samplerate_ = v; }
void Song::set_bitdepth(int v) { d->bitdepth_ = v; } void Song::set_bitdepth(int v) { d->bitdepth_ = v; }
void Song::set_source(Source v) { d->source_ = v; }
void Song::set_directory_id(int v) { d->directory_id_ = v; } void Song::set_directory_id(int v) { d->directory_id_ = v; }
void Song::set_url(const QUrl &v) { void Song::set_url(const QUrl &v) {
if (Application::kIsPortable) { if (Application::kIsPortable) {
@ -366,39 +375,93 @@ QString Song::JoinSpec(const QString &table) {
return Utilities::Prepend(table + ".", kColumns).join(", "); return Utilities::Prepend(table + ".", kColumns).join(", ");
} }
QString Song::TextForFiletype(FileType type) { QString Song::TextForSource(Source source) {
switch (type) { switch (source) {
case Song::Type_WAV: return QObject::tr("Wav"); case Song::Source_LocalFile: return QObject::tr("File");
case Song::Type_FLAC: return QObject::tr("FLAC"); case Song::Source_Collection: return QObject::tr("Collection");
case Song::Type_WavPack: return QObject::tr("WavPack"); case Song::Source_CDDA: return QObject::tr("CD");
case Song::Type_OggFlac: return QObject::tr("Ogg FLAC"); case Song::Source_Device: return QObject::tr("Device");
case Song::Type_OggVorbis: return QObject::tr("Ogg Vorbis"); case Song::Source_Stream: return QObject::tr("Stream");
case Song::Type_OggOpus: return QObject::tr("Ogg Opus"); case Song::Source_Tidal: return QObject::tr("Tidal");
case Song::Type_OggSpeex: return QObject::tr("Ogg Speex");
case Song::Type_MPEG: return QObject::tr("MP3");
case Song::Type_MP4: return QObject::tr("MP4 AAC");
case Song::Type_ASF: return QObject::tr("Windows Media audio");
case Song::Type_AIFF: return QObject::tr("AIFF");
case Song::Type_MPC: return QObject::tr("MPC");
case Song::Type_TrueAudio: return QObject::tr("TrueAudio");
case Song::Type_DSF: return QObject::tr("DSF"); // .dsf
case Song::Type_DSDIFF: return QObject::tr("DSDIFF"); // .dff
case Song::Type_CDDA: return QObject::tr("CDDA");
case Song::Type_Stream: return QObject::tr("Stream");
case Song::Type_Unknown:
default: return QObject::tr("Unknown"); default: return QObject::tr("Unknown");
} }
} }
QIcon Song::IconForSource(Source source) {
switch (source) {
case Song::Source_LocalFile: return IconLoader::Load("folder-sound");
case Song::Source_Collection: return IconLoader::Load("vinyl");
case Song::Source_CDDA: return IconLoader::Load("cd");
case Song::Source_Device: return IconLoader::Load("device");
case Song::Source_Stream: return IconLoader::Load("applications-internet");
case Song::Source_Tidal: return IconLoader::Load("tidal");
default: return IconLoader::Load("edit-delete");
}
}
QString Song::TextForFiletype(FileType filetype) {
switch (filetype) {
case Song::FileType_WAV: return QObject::tr("Wav");
case Song::FileType_FLAC: return QObject::tr("FLAC");
case Song::FileType_WavPack: return QObject::tr("WavPack");
case Song::FileType_OggFlac: return QObject::tr("Ogg FLAC");
case Song::FileType_OggVorbis: return QObject::tr("Ogg Vorbis");
case Song::FileType_OggOpus: return QObject::tr("Ogg Opus");
case Song::FileType_OggSpeex: return QObject::tr("Ogg Speex");
case Song::FileType_MPEG: return QObject::tr("MP3");
case Song::FileType_MP4: return QObject::tr("MP4 AAC");
case Song::FileType_ASF: return QObject::tr("Windows Media audio");
case Song::FileType_AIFF: return QObject::tr("AIFF");
case Song::FileType_MPC: return QObject::tr("MPC");
case Song::FileType_TrueAudio: return QObject::tr("TrueAudio");
case Song::FileType_DSF: return QObject::tr("DSF");
case Song::FileType_DSDIFF: return QObject::tr("DSDIFF");
case Song::FileType_CDDA: return QObject::tr("CDDA");
case Song::FileType_Stream: return QObject::tr("Stream");
case Song::FileType_Unknown:
default: return QObject::tr("Unknown");
}
}
QIcon Song::IconForFiletype(FileType filetype) {
switch (filetype) {
case Song::FileType_WAV: return IconLoader::Load("wav");
case Song::FileType_FLAC: return IconLoader::Load("flac");
case Song::FileType_WavPack: return IconLoader::Load("wavpack");
case Song::FileType_OggFlac: return IconLoader::Load("flac");
case Song::FileType_OggVorbis: return IconLoader::Load("vorbis");
case Song::FileType_OggOpus: return IconLoader::Load("opus");
case Song::FileType_OggSpeex: return IconLoader::Load("speex");
case Song::FileType_MPEG: return IconLoader::Load("mp3");
case Song::FileType_MP4: return IconLoader::Load("mp4");
case Song::FileType_ASF: return IconLoader::Load("wma");
case Song::FileType_AIFF: return IconLoader::Load("aiff");
case Song::FileType_MPC: return IconLoader::Load("mpc");
case Song::FileType_TrueAudio: return IconLoader::Load("trueaudio");
case Song::FileType_DSF: return IconLoader::Load("dsf");
case Song::FileType_DSDIFF: return IconLoader::Load("dsd");
case Song::FileType_CDDA: return IconLoader::Load("cd");
case Song::FileType_Stream: return IconLoader::Load("applications-internet");
case Song::FileType_Unknown:
default: return IconLoader::Load("edit-delete");
}
}
bool Song::IsFileLossless() const { bool Song::IsFileLossless() const {
switch (filetype()) { switch (filetype()) {
case Song::Type_WAV: case Song::FileType_WAV:
case Song::Type_FLAC: case Song::FileType_FLAC:
case Song::Type_OggFlac: case Song::FileType_OggFlac:
case Song::Type_WavPack: case Song::FileType_WavPack:
case Song::Type_AIFF: case Song::FileType_AIFF:
return true; return true;
default: default:
return false; return false;
@ -407,20 +470,20 @@ bool Song::IsFileLossless() const {
Song::FileType Song::FiletypeByExtension(QString ext) { Song::FileType Song::FiletypeByExtension(QString ext) {
if (ext.toLower() == "wav" || ext.toLower() == "wave") return Song::Type_WAV; if (ext.toLower() == "wav" || ext.toLower() == "wave") return Song::FileType_WAV;
else if (ext.toLower() == "flac") return Song::Type_FLAC; else if (ext.toLower() == "flac") return Song::FileType_FLAC;
else if (ext.toLower() == "wavpack" || ext.toLower() == "wv") return Song::Type_WavPack; else if (ext.toLower() == "wavpack" || ext.toLower() == "wv") return Song::FileType_WavPack;
else if (ext.toLower() == "ogg" || ext.toLower() == "oga") return Song::Type_OggVorbis; else if (ext.toLower() == "ogg" || ext.toLower() == "oga") return Song::FileType_OggVorbis;
else if (ext.toLower() == "opus") return Song::Type_OggOpus; else if (ext.toLower() == "opus") return Song::FileType_OggOpus;
else if (ext.toLower() == "speex" || ext.toLower() == "spx") return Song::Type_OggSpeex; else if (ext.toLower() == "speex" || ext.toLower() == "spx") return Song::FileType_OggSpeex;
else if (ext.toLower() == "mp3") return Song::Type_MPEG; else if (ext.toLower() == "mp3") return Song::FileType_MPEG;
else if (ext.toLower() == "mp4" || ext.toLower() == "m4a" || ext.toLower() == "aac") return Song::Type_MP4; else if (ext.toLower() == "mp4" || ext.toLower() == "m4a" || ext.toLower() == "aac") return Song::FileType_MP4;
else if (ext.toLower() == "asf" || ext.toLower() == "wma") return Song::Type_ASF; else if (ext.toLower() == "asf" || ext.toLower() == "wma") return Song::FileType_ASF;
else if (ext.toLower() == "aiff" || ext.toLower() == "aif" || ext.toLower() == "aifc") return Song::Type_AIFF; else if (ext.toLower() == "aiff" || ext.toLower() == "aif" || ext.toLower() == "aifc") return Song::FileType_AIFF;
else if (ext.toLower() == "mpc" || ext.toLower() == "mp+" || ext.toLower() == "mpp") return Song::Type_MPC; else if (ext.toLower() == "mpc" || ext.toLower() == "mp+" || ext.toLower() == "mpp") return Song::FileType_MPC;
else if (ext.toLower() == "dsf") return Song::Type_DSF; else if (ext.toLower() == "dsf") return Song::FileType_DSF;
else if (ext.toLower() == "dsd" || ext.toLower() == "dff") return Song::Type_DSDIFF; else if (ext.toLower() == "dsd" || ext.toLower() == "dff") return Song::FileType_DSDIFF;
else return Song::Type_Unknown; else return Song::FileType_Unknown;
} }
@ -547,7 +610,7 @@ void Song::ToProtobuf(pb::tagreader::SongMetadata *pb) const {
pb->set_filesize(d->filesize_); pb->set_filesize(d->filesize_);
pb->set_suspicious_tags(d->suspicious_tags_); pb->set_suspicious_tags(d->suspicious_tags_);
pb->set_art_automatic(DataCommaSizeFromQString(d->art_automatic_)); pb->set_art_automatic(DataCommaSizeFromQString(d->art_automatic_));
pb->set_filetype(static_cast<pb::tagreader::SongMetadata_Type>(d->filetype_)); pb->set_filetype(static_cast<pb::tagreader::SongMetadata_FileType>(d->filetype_));
} }
#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())
@ -635,6 +698,9 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
d->bitdepth_ = toint(x); d->bitdepth_ = toint(x);
} }
else if (Song::kColumns.value(i) == "source") {
d->source_ = Source(q.value(x).toInt());
}
else if (Song::kColumns.value(i) == "directory_id") { else if (Song::kColumns.value(i) == "directory_id") {
d->directory_id_ = toint(x); d->directory_id_ = toint(x);
} }
@ -721,8 +787,10 @@ void Song::InitFromFilePartial(const QString &filename) {
QString suffix = info.suffix().toLower(); QString suffix = info.suffix().toLower();
TagLib::FileRef fileref(filename.toUtf8().constData()); TagLib::FileRef fileref(filename.toUtf8().constData());
//if (TagLib::FileRef::defaultFileExtensions().contains(suffix.toUtf8().constData())) { if (fileref.file()) {
if (fileref.file()) d->valid_ = true; d->valid_ = true;
d->source_ = Source_LocalFile;
}
else { else {
d->valid_ = false; d->valid_ = false;
qLog(Error) << "File" << filename << "is not recognized by TagLib as a valid audio file."; qLog(Error) << "File" << filename << "is not recognized by TagLib as a valid audio file.";
@ -772,6 +840,7 @@ void Song::InitFromItdb(const Itdb_Track *track, const QString &prefix) {
d->samplerate_ = track->samplerate; d->samplerate_ = track->samplerate;
d->bitdepth_ = -1; //track->bitdepth; d->bitdepth_ = -1; //track->bitdepth;
d->source_ = Source_Device;
QString filename = QString::fromLocal8Bit(track->ipod_path); QString filename = QString::fromLocal8Bit(track->ipod_path);
filename.replace(':', '/'); filename.replace(':', '/');
if (prefix.contains("://")) { if (prefix.contains("://")) {
@ -781,7 +850,7 @@ void Song::InitFromItdb(const Itdb_Track *track, const QString &prefix) {
} }
d->basefilename_ = QFileInfo(filename).fileName(); d->basefilename_ = QFileInfo(filename).fileName();
d->filetype_ = track->type2 ? Type_MPEG : Type_MP4; d->filetype_ = track->type2 ? FileType_MPEG : FileType_MP4;
d->filesize_ = track->size; d->filesize_ = track->size;
d->mtime_ = track->time_modified; d->mtime_ = track->time_modified;
d->ctime_ = track->time_added; d->ctime_ = track->time_added;
@ -814,7 +883,7 @@ void Song::ToItdb(Itdb_Track *track) const {
//track->bithdepth = d->bithdepth_; //track->bithdepth = d->bithdepth_;
track->type1 = 0; track->type1 = 0;
track->type2 = d->filetype_ == Type_MP4 ? 0 : 1; track->type2 = d->filetype_ == FileType_MP4 ? 0 : 1;
track->mediatype = 1; // Audio track->mediatype = 1; // Audio
track->size = d->filesize_; track->size = d->filesize_;
track->time_modified = d->mtime_; track->time_modified = d->mtime_;
@ -854,18 +923,20 @@ void Song::InitFromMTP(const LIBMTP_track_t *track, const QString &host) {
d->playcount_ = track->usecount; d->playcount_ = track->usecount;
switch (track->filetype) { switch (track->filetype) {
case LIBMTP_FILETYPE_WAV: d->filetype_ = Type_WAV; break; case LIBMTP_FILETYPE_WAV: d->filetype_ = FileType_WAV; break;
case LIBMTP_FILETYPE_MP3: d->filetype_ = Type_MPEG; break; case LIBMTP_FILETYPE_MP3: d->filetype_ = FileType_MPEG; break;
case LIBMTP_FILETYPE_WMA: d->filetype_ = Type_ASF; break; case LIBMTP_FILETYPE_WMA: d->filetype_ = FileType_ASF; break;
case LIBMTP_FILETYPE_OGG: d->filetype_ = Type_OggVorbis; break; case LIBMTP_FILETYPE_OGG: d->filetype_ = FileType_OggVorbis; break;
case LIBMTP_FILETYPE_MP4: d->filetype_ = Type_MP4; break; case LIBMTP_FILETYPE_MP4: d->filetype_ = FileType_MP4; break;
case LIBMTP_FILETYPE_AAC: d->filetype_ = Type_MP4; break; case LIBMTP_FILETYPE_AAC: d->filetype_ = FileType_MP4; break;
case LIBMTP_FILETYPE_FLAC: d->filetype_ = Type_OggFlac; break; case LIBMTP_FILETYPE_FLAC: d->filetype_ = FileType_OggFlac; break;
case LIBMTP_FILETYPE_MP2: d->filetype_ = Type_MPEG; break; case LIBMTP_FILETYPE_MP2: d->filetype_ = FileType_MPEG; break;
case LIBMTP_FILETYPE_M4A: d->filetype_ = Type_MP4; break; case LIBMTP_FILETYPE_M4A: d->filetype_ = FileType_MP4; break;
default: d->filetype_ = Type_Unknown; break; default: d->filetype_ = FileType_Unknown; break;
} }
d->source_ = Source_Device;
} }
void Song::ToMTP(LIBMTP_track_t *track) const { void Song::ToMTP(LIBMTP_track_t *track) const {
@ -897,14 +968,14 @@ void Song::ToMTP(LIBMTP_track_t *track) const {
track->usecount = d->playcount_; track->usecount = d->playcount_;
switch (d->filetype_) { switch (d->filetype_) {
case Type_ASF: track->filetype = LIBMTP_FILETYPE_ASF; break; case FileType_ASF: track->filetype = LIBMTP_FILETYPE_ASF; break;
case Type_MP4: track->filetype = LIBMTP_FILETYPE_MP4; break; case FileType_MP4: track->filetype = LIBMTP_FILETYPE_MP4; break;
case Type_MPEG: track->filetype = LIBMTP_FILETYPE_MP3; break; case FileType_MPEG: track->filetype = LIBMTP_FILETYPE_MP3; break;
case Type_FLAC: case FileType_FLAC:
case Type_OggFlac: track->filetype = LIBMTP_FILETYPE_FLAC; break; case FileType_OggFlac: track->filetype = LIBMTP_FILETYPE_FLAC; break;
case Type_OggSpeex: case FileType_OggSpeex:
case Type_OggVorbis: track->filetype = LIBMTP_FILETYPE_OGG; break; case FileType_OggVorbis: track->filetype = LIBMTP_FILETYPE_OGG; break;
case Type_WAV: track->filetype = LIBMTP_FILETYPE_WAV; break; case FileType_WAV: track->filetype = LIBMTP_FILETYPE_WAV; break;
default: track->filetype = LIBMTP_FILETYPE_UNDEF_AUDIO; break; default: track->filetype = LIBMTP_FILETYPE_UNDEF_AUDIO; break;
} }
@ -965,6 +1036,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
query->bindValue(":samplerate", intval(d->samplerate_)); query->bindValue(":samplerate", intval(d->samplerate_));
query->bindValue(":bitdepth", intval(d->bitdepth_)); query->bindValue(":bitdepth", intval(d->bitdepth_));
query->bindValue(":source", notnullintval(d->source_));
query->bindValue(":directory_id", notnullintval(d->directory_id_)); query->bindValue(":directory_id", notnullintval(d->directory_id_));
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(d->url_)) { if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(d->url_)) {
@ -1103,7 +1175,7 @@ bool Song::IsMetadataEqual(const Song &other) const {
} }
bool Song::IsEditable() const { bool Song::IsEditable() const {
return d->valid_ && !d->url_.isEmpty() && !is_stream() && d->filetype_ != Type_Unknown && !has_cue(); return d->valid_ && !d->url_.isEmpty() && !is_stream() && d->source_ != Source_Unknown && d->filetype_ != FileType_Unknown && !has_cue();
} }
bool Song::operator==(const Song &other) const { bool Song::operator==(const Song &other) const {

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -37,6 +38,7 @@
#include <QRegExp> #include <QRegExp>
#include <QUrl> #include <QUrl>
#include <QImage> #include <QImage>
#include <QIcon>
#include <QTextCodec> #include <QTextCodec>
#include <QSqlQuery> #include <QSqlQuery>
@ -87,29 +89,48 @@ class Song {
// Don't change these values - they're stored in the database, and defined in the tag reader protobuf. // Don't change these values - they're stored in the database, and defined in the tag reader protobuf.
// If a new lossless file is added, also add it to IsFileLossless(). // If a new lossless file is added, also add it to IsFileLossless().
enum FileType {
Type_Unknown = 0, enum Source {
Type_WAV = 1, Source_Unknown = 0,
Type_FLAC = 2, Source_LocalFile = 1,
Type_WavPack = 3, Source_Collection = 2,
Type_OggFlac = 4, Source_CDDA = 3,
Type_OggVorbis = 5, Source_Device = 4,
Type_OggOpus = 6, Source_Stream = 5,
Type_OggSpeex = 7, Source_Tidal = 6,
Type_MPEG = 8,
Type_MP4 = 9,
Type_ASF = 10,
Type_AIFF = 11,
Type_MPC = 12,
Type_TrueAudio = 13,
Type_DSF = 14,
Type_DSDIFF = 15,
Type_CDDA = 90,
Type_Stream = 91,
}; };
static QString TextForFiletype(FileType type); enum FileType {
FileType_Unknown = 0,
FileType_WAV = 1,
FileType_FLAC = 2,
FileType_WavPack = 3,
FileType_OggFlac = 4,
FileType_OggVorbis = 5,
FileType_OggOpus = 6,
FileType_OggSpeex = 7,
FileType_MPEG = 8,
FileType_MP4 = 9,
FileType_ASF = 10,
FileType_AIFF = 11,
FileType_MPC = 12,
FileType_TrueAudio = 13,
FileType_DSF = 14,
FileType_DSDIFF = 15,
FileType_CDDA = 90,
FileType_Stream = 91,
};
static QString TextForSource(Source source);
static QIcon IconForSource(Source source);
static QString TextForFiletype(FileType filetype);
QIcon IconForFiletype(FileType filetype);
QString TextForSource() const { return TextForSource(source()); }
QIcon IconForSource() const { return IconForSource(source()); }
QString TextForFiletype() const { return TextForFiletype(filetype()); } QString TextForFiletype() const { return TextForFiletype(filetype()); }
QIcon IconForFiletype(FileType filetype) const { return IconForFiletype(filetype); }
bool IsFileLossless() const; bool IsFileLossless() const;
static FileType FiletypeByExtension(QString ext); static FileType FiletypeByExtension(QString ext);
@ -182,6 +203,7 @@ class Song {
int samplerate() const; int samplerate() const;
int bitdepth() const; int bitdepth() const;
Source source() const;
int directory_id() const; int directory_id() const;
const QUrl &url() const; const QUrl &url() const;
const QString &basefilename() const; const QString &basefilename() const;
@ -261,6 +283,7 @@ class Song {
void set_samplerate(int v); void set_samplerate(int v);
void set_bitdepth(int v); void set_bitdepth(int v);
void set_source(Source v);
void set_directory_id(int v); void set_directory_id(int v);
void set_url(const QUrl &v); void set_url(const QUrl &v);
void set_basefilename(const QString &v); void set_basefilename(const QString &v);

View File

@ -276,7 +276,7 @@ void SongLoader::EffectiveSongLoad(Song *song) {
if (!song) return; if (!song) return;
if (song->filetype() != Song::Type_Unknown) { if (song->filetype() != Song::FileType_Unknown) {
// Maybe we loaded the metadata already, for example from a cuesheet. // Maybe we loaded the metadata already, for example from a cuesheet.
return; return;
} }
@ -335,7 +335,7 @@ void SongLoader::LoadLocalDirectory(const QString &filename) {
void SongLoader::AddAsRawStream() { 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::FileType_Stream);
song.set_url(url_); song.set_url(url_);
song.set_title(url_.toString()); song.set_title(url_.toString());
songs_ << song; songs_ << song;

View File

@ -110,7 +110,8 @@ void CddaSongLoader::LoadSongs() {
Song song; Song song;
song.set_id(track_number); song.set_id(track_number);
song.set_valid(true); song.set_valid(true);
song.set_filetype(Song::Type_CDDA); song.set_source(Song::Source_CDDA);
song.set_filetype(Song::FileType_CDDA);
song.set_url(GetUrlFromTrack(track_number)); song.set_url(GetUrlFromTrack(track_number));
song.set_title(QString("Track %1").arg(track_number)); song.set_title(QString("Track %1").arg(track_number));
song.set_track(track_number); song.set_track(track_number);
@ -207,7 +208,8 @@ void CddaSongLoader::AudioCDTagsLoaded(const QString &artist, const QString &alb
song.set_track(track_number); song.set_track(track_number);
song.set_year(ret.year_); song.set_year(ret.year_);
song.set_id(track_number); song.set_id(track_number);
song.set_filetype(Song::Type_CDDA); song.set_source(Song::Source_CDDA);
song.set_filetype(Song::FileType_CDDA);
song.set_valid(true); song.set_valid(true);
// We need to set url: that's how playlist will find the correct item to update // We need to set url: that's how playlist will find the correct item to update
song.set_url(GetUrlFromTrack(track_number++)); song.set_url(GetUrlFromTrack(track_number++));

View File

@ -100,7 +100,7 @@ const int DeviceManager::kDeviceIconOverlaySize = 16;
DeviceManager::DeviceInfo::DeviceInfo() DeviceManager::DeviceInfo::DeviceInfo()
: database_id_(-1), : database_id_(-1),
transcode_mode_(MusicStorage::Transcode_Unsupported), transcode_mode_(MusicStorage::Transcode_Unsupported),
transcode_format_(Song::Type_Unknown), transcode_format_(Song::FileType_Unknown),
task_percentage_(-1) {} task_percentage_(-1) {}
DeviceDatabaseBackend::Device DeviceManager::DeviceInfo::SaveToDb() const { DeviceDatabaseBackend::Device DeviceManager::DeviceInfo::SaveToDb() const {

View File

@ -308,7 +308,7 @@ void DeviceProperties::UpdateFormatsFinished(QFuture<bool> future) {
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
// Set the format combobox item // Set the format combobox item
TranscoderPreset preset = Transcoder::PresetForFileType(Song::FileType(index_.data(DeviceManager::Role_TranscodeFormat).toInt())); TranscoderPreset preset = Transcoder::PresetForFileType(Song::FileType(index_.data(DeviceManager::Role_TranscodeFormat).toInt()));
if (preset.type_ == Song::Type_Unknown) { if (preset.type_ == Song::FileType_Unknown) {
// The user hasn't chosen a format for this device yet, // The user hasn't chosen a format for this device yet,
// so work our way down a list of some preferred formats, picking the first one that is supported // so work our way down a list of some preferred formats, picking the first one that is supported
preset = Transcoder::PresetForFileType(Transcoder::PickBestFormat(supported_formats_)); preset = Transcoder::PresetForFileType(Transcoder::PickBestFormat(supported_formats_));

View File

@ -248,8 +248,8 @@ void GPodDevice::FinishDelete(bool success) {
} }
bool GPodDevice::GetSupportedFiletypes(QList<Song::FileType> *ret) { bool GPodDevice::GetSupportedFiletypes(QList<Song::FileType> *ret) {
*ret << Song::Type_MP4; *ret << Song::FileType_MP4;
*ret << Song::Type_MPEG; *ret << Song::FileType_MPEG;
return true; return true;
} }

View File

@ -39,7 +39,7 @@ GPodLoader::GPodLoader(const QString &mount_point, TaskManager *task_manager, Co
: QObject(nullptr), : QObject(nullptr),
device_(device), device_(device),
mount_point_(mount_point), mount_point_(mount_point),
type_(Song::Type_Unknown), type_(Song::FileType_Unknown),
task_manager_(task_manager), task_manager_(task_manager),
backend_(backend) { backend_(backend) {
original_thread_ = thread(); original_thread_ = thread();
@ -81,7 +81,7 @@ void GPodLoader::LoadDatabase() {
song.InitFromItdb(track, prefix); song.InitFromItdb(track, prefix);
song.set_directory_id(1); song.set_directory_id(1);
if (type_ != Song::Type_Unknown) song.set_filetype(type_); if (type_ != Song::FileType_Unknown) song.set_filetype(type_);
songs << song; songs << song;
} }

View File

@ -209,21 +209,21 @@ bool MtpDevice::GetSupportedFiletypes(QList<Song::FileType> *ret, LIBMTP_mtpdevi
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
switch (LIBMTP_filetype_t(list[i])) { switch (LIBMTP_filetype_t(list[i])) {
case LIBMTP_FILETYPE_WAV: *ret << Song::Type_WAV; break; case LIBMTP_FILETYPE_WAV: *ret << Song::FileType_WAV; break;
case LIBMTP_FILETYPE_MP2: case LIBMTP_FILETYPE_MP2:
case LIBMTP_FILETYPE_MP3: *ret << Song::Type_MPEG; break; case LIBMTP_FILETYPE_MP3: *ret << Song::FileType_MPEG; break;
case LIBMTP_FILETYPE_WMA: *ret << Song::Type_ASF; break; case LIBMTP_FILETYPE_WMA: *ret << Song::FileType_ASF; break;
case LIBMTP_FILETYPE_MP4: case LIBMTP_FILETYPE_MP4:
case LIBMTP_FILETYPE_M4A: case LIBMTP_FILETYPE_M4A:
case LIBMTP_FILETYPE_AAC: *ret << Song::Type_MP4; break; case LIBMTP_FILETYPE_AAC: *ret << Song::FileType_MP4; break;
case LIBMTP_FILETYPE_FLAC: case LIBMTP_FILETYPE_FLAC:
*ret << Song::Type_FLAC; *ret << Song::FileType_FLAC;
*ret << Song::Type_OggFlac; *ret << Song::FileType_OggFlac;
break; break;
case LIBMTP_FILETYPE_OGG: case LIBMTP_FILETYPE_OGG:
*ret << Song::Type_OggVorbis; *ret << Song::FileType_OggVorbis;
*ret << Song::Type_OggSpeex; *ret << Song::FileType_OggSpeex;
*ret << Song::Type_OggFlac; *ret << Song::FileType_OggFlac;
break; break;
default: default:
qLog(Error) << "Unknown MTP file format" << LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t(list[i])); qLog(Error) << "Unknown MTP file format" << LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t(list[i]));

View File

@ -32,13 +32,13 @@
#include "internetservice.h" #include "internetservice.h"
#include "tidal/tidalservice.h" #include "tidal/tidalservice.h"
QMap<QString, InternetService*>* InternetModel::sServices = nullptr; QMap<Song::Source, InternetService*>* InternetModel::sServices = nullptr;
InternetModel::InternetModel(Application *app, QObject *parent) InternetModel::InternetModel(Application *app, QObject *parent)
: QStandardItemModel(parent), : QStandardItemModel(parent),
app_(app) { app_(app) {
if (!sServices) sServices = new QMap<QString, InternetService*>; if (!sServices) sServices = new QMap<Song::Source, InternetService*>;
Q_ASSERT(sServices->isEmpty()); Q_ASSERT(sServices->isEmpty());
AddService(new TidalService(app, this)); AddService(new TidalService(app, this));
@ -47,7 +47,7 @@ InternetModel::InternetModel(Application *app, QObject *parent)
void InternetModel::AddService(InternetService *service) { void InternetModel::AddService(InternetService *service) {
qLog(Debug) << "Adding internet service:" << service->name(); qLog(Debug) << "Adding internet service:" << service->name();
sServices->insert(service->name(), service); sServices->insert(service->source(), service);
connect(service, SIGNAL(destroyed()), SLOT(ServiceDeleted())); connect(service, SIGNAL(destroyed()), SLOT(ServiceDeleted()));
if (service->has_initial_load_settings()) service->InitialLoadSettings(); if (service->has_initial_load_settings()) service->InitialLoadSettings();
else service->ReloadSettings(); else service->ReloadSettings();
@ -56,8 +56,8 @@ void InternetModel::AddService(InternetService *service) {
void InternetModel::RemoveService(InternetService *service) { void InternetModel::RemoveService(InternetService *service) {
if (!sServices->contains(service->name())) return; if (!sServices->contains(service->source())) return;
sServices->remove(service->name()); sServices->remove(service->source());
disconnect(service, 0, this, 0); disconnect(service, 0, this, 0);
} }
@ -69,9 +69,9 @@ void InternetModel::ServiceDeleted() {
} }
InternetService *InternetModel::ServiceByName(const QString &name) { InternetService *InternetModel::ServiceBySource(const Song::Source &source) {
if (sServices->contains(name)) return sServices->value(name); if (sServices->contains(source)) return sServices->value(source);
return nullptr; return nullptr;
} }

View File

@ -105,11 +105,13 @@ class InternetModel : public QStandardItemModel {
}; };
// Needs to be static for InternetPlaylistItem::restore // Needs to be static for InternetPlaylistItem::restore
static InternetService *ServiceByName(const QString &name); static InternetService *ServiceBySource(const Song::Source &source);
//static InternetService *ServiceByName(const QString &name);
template <typename T> template <typename T>
static T *Service() { static T *Service() {
return static_cast<T*>(ServiceByName(T::kServiceName)); //return static_cast<T*>(ServiceByName(T::kServiceName));
return static_cast<T*>(ServiceBySource(T::kSource));
} }
// Add and remove services. Ownership is not transferred and the service is not reparented. // Add and remove services. Ownership is not transferred and the service is not reparented.
@ -124,7 +126,8 @@ class InternetModel : public QStandardItemModel {
void ServiceDeleted(); void ServiceDeleted();
private: private:
static QMap<QString, InternetService*> *sServices; //static QMap<QString, InternetService*> *sServices;
static QMap<Song::Source, InternetService*> *sServices;
Application *app_; Application *app_;
}; };

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -33,72 +34,39 @@
#include "collection/sqlrow.h" #include "collection/sqlrow.h"
#include "playlist/playlistbackend.h" #include "playlist/playlistbackend.h"
InternetPlaylistItem::InternetPlaylistItem(const QString &type) InternetPlaylistItem::InternetPlaylistItem(const Song::Source &source)
: PlaylistItem(type), set_service_icon_(false) {} : PlaylistItem(source) {}
InternetPlaylistItem::InternetPlaylistItem(InternetService *service, const Song &metadata) InternetPlaylistItem::InternetPlaylistItem(InternetService *service, const Song &metadata)
: PlaylistItem("Internet"), : PlaylistItem(Song::Source_Stream),
service_name_(service->name()), source_(service->source()),
set_service_icon_(false),
metadata_(metadata) { metadata_(metadata) {
InitMetadata(); InitMetadata();
} }
bool InternetPlaylistItem::InitFromQuery(const SqlRow &query) { bool InternetPlaylistItem::InitFromQuery(const SqlRow &query) {
// The song tables gets joined first, plus one each for the song ROWIDs
const int row = (Song::kColumns.count() + 1) * PlaylistBackend::kSongTableJoins;
service_name_ = query.value(row + 1).toString();
metadata_.InitFromQuery(query, false, (Song::kColumns.count() + 1) * 1); metadata_.InitFromQuery(query, false, (Song::kColumns.count() + 1) * 1);
InitMetadata(); InitMetadata();
return true; return true;
} }
InternetService *InternetPlaylistItem::service() const { InternetService *InternetPlaylistItem::service() const {
InternetService *ret = InternetModel::ServiceBySource(source_);
InternetService *ret = InternetModel::ServiceByName(service_name_);
if (ret && !set_service_icon_) {
const_cast<InternetPlaylistItem*>(this)->set_service_icon_ = true;
QString icon = ret->Icon();
if (!icon.isEmpty()) {
const_cast<InternetPlaylistItem*>(this)->metadata_.set_art_manual(icon);
}
}
return ret; return ret;
} }
QVariant InternetPlaylistItem::DatabaseValue(DatabaseColumn column) const { QVariant InternetPlaylistItem::DatabaseValue(DatabaseColumn column) const {
switch (column) {
case Column_InternetService:
return service_name_;
default:
return PlaylistItem::DatabaseValue(column); return PlaylistItem::DatabaseValue(column);
}
} }
void InternetPlaylistItem::InitMetadata() { void InternetPlaylistItem::InitMetadata() {
if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString());
if (metadata_.title().isEmpty()) if (metadata_.source() == Song::Source_Unknown) metadata_.set_source(Song::Source_Stream);
metadata_.set_title(metadata_.url().toString()); if (metadata_.filetype() == Song::FileType_Unknown) metadata_.set_filetype(Song::FileType_Stream);
if (metadata_.filetype() == Song::Type_Unknown) metadata_.set_filetype(Song::Type_Stream);
metadata_.set_valid(true); metadata_.set_valid(true);
} }
Song InternetPlaylistItem::Metadata() const { Song InternetPlaylistItem::Metadata() const {
if (!set_service_icon_) {
// Get the icon if we don't have it already
service();
}
if (HasTemporaryMetadata()) return temp_metadata_; if (HasTemporaryMetadata()) return temp_metadata_;
return metadata_; return metadata_;
} }

View File

@ -35,7 +35,7 @@ class InternetService;
class InternetPlaylistItem : public PlaylistItem { class InternetPlaylistItem : public PlaylistItem {
public: public:
explicit InternetPlaylistItem(const QString &type); explicit InternetPlaylistItem(const Song::Source &type);
InternetPlaylistItem(InternetService *service, const Song &metadata); InternetPlaylistItem(InternetService *service, const Song &metadata);
bool InitFromQuery(const SqlRow &query); bool InitFromQuery(const SqlRow &query);
Song Metadata() const; Song Metadata() const;
@ -50,8 +50,7 @@ class InternetPlaylistItem : public PlaylistItem {
InternetService *service() const; InternetService *service() const;
private: private:
QString service_name_; Song::Source source_;
bool set_service_icon_;
Song metadata_; Song metadata_;
}; };

View File

@ -27,6 +27,6 @@
#include "internetmodel.h" #include "internetmodel.h"
#include "internetservice.h" #include "internetservice.h"
InternetService::InternetService(const QString &name, Application *app, InternetModel *model, QObject *parent) InternetService::InternetService(Song::Source source, const QString &name, Application *app, InternetModel *model, QObject *parent)
: QObject(parent), app_(app), model_(model), name_(name) { : QObject(parent), app_(app), model_(model), source_(source), name_(name) {
} }

View File

@ -27,8 +27,10 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QIcon>
#include "core/song.h" #include "core/song.h"
#include "core/iconloader.h"
#include "playlist/playlistitem.h" #include "playlist/playlistitem.h"
#include "settings/settingsdialog.h" #include "settings/settingsdialog.h"
@ -40,14 +42,15 @@ class InternetService : public QObject {
Q_OBJECT Q_OBJECT
public: public:
InternetService(const QString &name, Application *app, InternetModel *model, QObject *parent = nullptr); InternetService(Song::Source source, const QString &name, Application *app, InternetModel *model, QObject *parent = nullptr);
virtual ~InternetService() {} virtual ~InternetService() {}
Song::Source source() const { return source_; }
QString name() const { return name_; } QString name() const { return name_; }
InternetModel *model() const { return model_; } InternetModel *model() const { return model_; }
virtual bool has_initial_load_settings() const { return false; } virtual bool has_initial_load_settings() const { return false; }
virtual void InitialLoadSettings() {} virtual void InitialLoadSettings() {}
virtual void ReloadSettings() {} virtual void ReloadSettings() {}
virtual QString Icon() { return QString(); } virtual QIcon Icon() { return Song::IconForSource(source_); }
public slots: public slots:
virtual void ShowConfig() {} virtual void ShowConfig() {}
@ -56,6 +59,7 @@ class InternetService : public QObject {
Application *app_; Application *app_;
private: private:
InternetModel *model_; InternetModel *model_;
Song::Source source_;
QString name_; QString name_;
}; };

View File

@ -67,6 +67,7 @@
#include "core/logging.h" #include "core/logging.h"
#include "core/mimedata.h" #include "core/mimedata.h"
#include "core/tagreaderclient.h" #include "core/tagreaderclient.h"
#include "core/song.h"
#include "collection/collection.h" #include "collection/collection.h"
#include "collection/collectionbackend.h" #include "collection/collectionbackend.h"
#include "collection/collectionplaylistitem.h" #include "collection/collectionplaylistitem.h"
@ -308,7 +309,7 @@ QVariant Playlist::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) return song.comment().simplified(); if (role == Qt::DisplayRole) return song.comment().simplified();
return song.comment(); return song.comment();
case Column_Source: return item->Url(); case Column_Source: return song.source();
} }
@ -910,7 +911,7 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bo
items_.insert(i, item); items_.insert(i, item);
virtual_items_ << virtual_items_.count(); virtual_items_ << virtual_items_.count();
if (item->type() == "Collection") { if (item->source() == Song::Source_Collection) {
int id = item->Metadata().id(); int id = item->Metadata().id();
if (id != -1) { if (id != -1) {
collection_items_by_id_.insertMulti(id, item); collection_items_by_id_.insertMulti(id, item);
@ -990,16 +991,22 @@ void Playlist::UpdateItems(const SongList &songs) {
const Song &song = it.next(); const Song &song = it.next();
PlaylistItemPtr &item = items_[i]; PlaylistItemPtr &item = items_[i];
if (item->Metadata().url() == song.url() && if (item->Metadata().url() == song.url() &&
(item->Metadata().filetype() == Song::Type_Unknown || (
item->Metadata().source() == Song::Source_Unknown ||
item->Metadata().filetype() == Song::FileType_Unknown ||
// Stream may change and may need to be updated too // Stream may change and may need to be updated too
item->Metadata().filetype() == Song::Type_Stream || item->Metadata().source() == Song::Source_Stream ||
item->Metadata().source() == Song::Source_Tidal ||
// And CD tracks as well (tags are loaded in a second step) // And CD tracks as well (tags are loaded in a second step)
item->Metadata().filetype() == Song::Type_CDDA)) { item->Metadata().source() == Song::Source_CDDA
)
) {
PlaylistItemPtr new_item; PlaylistItemPtr new_item;
if (song.is_collection_song()) { if (song.is_collection_song()) {
new_item = PlaylistItemPtr(new CollectionPlaylistItem(song)); new_item = PlaylistItemPtr(new CollectionPlaylistItem(song));
collection_items_by_id_.insertMulti(song.id(), new_item); collection_items_by_id_.insertMulti(song.id(), new_item);
} else { }
else {
new_item = PlaylistItemPtr(new SongPlaylistItem(song)); new_item = PlaylistItemPtr(new SongPlaylistItem(song));
} }
items_[i] = new_item; items_[i] = new_item;
@ -1099,7 +1106,7 @@ bool Playlist::CompareItems(int column, Qt::SortOrder order, shared_ptr<Playlist
case Column_DateCreated: cmp(ctime); case Column_DateCreated: cmp(ctime);
case Column_Comment: strcmp(comment); case Column_Comment: strcmp(comment);
case Column_Source: cmp(url); case Column_Source: cmp(source);
} }
#undef cmp #undef cmp
@ -1395,7 +1402,7 @@ PlaylistItemList Playlist::RemoveItemsWithoutUndo(int row, int count) {
PlaylistItemPtr item(items_.takeAt(row)); PlaylistItemPtr item(items_.takeAt(row));
ret << item; ret << item;
if (item->type() == "Collection") { if (item->source() == Song::Source_Collection) {
int id = item->Metadata().id(); int id = item->Metadata().id();
if (id != -1) { if (id != -1) {
collection_items_by_id_.remove(id, item); collection_items_by_id_.remove(id, item);

View File

@ -145,7 +145,7 @@ QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) {
" p.ROWID, " + " p.ROWID, " +
Song::JoinSpec("p") + Song::JoinSpec("p") +
"," ","
" p.type, p.internet_service" " p.type"
" FROM playlist_items AS p" " FROM playlist_items AS p"
" LEFT JOIN songs" " LEFT JOIN songs"
" ON p.collection_id = songs.ROWID" " ON p.collection_id = songs.ROWID"
@ -198,7 +198,7 @@ PlaylistItemPtr PlaylistBackend::NewPlaylistItemFromQuery(const SqlRow &row, std
// The song tables get joined first, plus one each for the song ROWIDs // The song tables get joined first, plus one each for the song ROWIDs
const int playlist_row = (Song::kColumns.count() + 1) * kSongTableJoins; const int playlist_row = (Song::kColumns.count() + 1) * kSongTableJoins;
PlaylistItemPtr item(PlaylistItem::NewFromType(row.value(playlist_row).toString())); PlaylistItemPtr item(PlaylistItem::NewFromSource(Song::Source(row.value(playlist_row).toInt())));
if (item) { if (item) {
item->InitFromQuery(row); item->InitFromQuery(row);
return RestoreCueData(item, state); return RestoreCueData(item, state);
@ -219,8 +219,8 @@ Song PlaylistBackend::NewSongFromQuery(const SqlRow &row, std::shared_ptr<NewSon
PlaylistItemPtr PlaylistBackend::RestoreCueData(PlaylistItemPtr item, std::shared_ptr<NewSongFromQueryState> state) { PlaylistItemPtr PlaylistBackend::RestoreCueData(PlaylistItemPtr item, std::shared_ptr<NewSongFromQueryState> state) {
// we need collection to run a CueParser; also, this method applies only to file-type PlaylistItems // We need collection to run a CueParser; also, this method applies only to file-type PlaylistItems
if (item->type() != "File") return item; if (item->source() != Song::Source_LocalFile) return item;
CueParser cue_parser(app_->collection_backend()); CueParser cue_parser(app_->collection_backend());
@ -279,7 +279,7 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList &items,
QSqlQuery clear(db); QSqlQuery clear(db);
clear.prepare("DELETE FROM playlist_items WHERE playlist = :playlist"); clear.prepare("DELETE FROM playlist_items WHERE playlist = :playlist");
QSqlQuery insert(db); QSqlQuery insert(db);
insert.prepare("INSERT INTO playlist_items (playlist, type, collection_id, internet_service, " + Song::kColumnSpec + ") VALUES (:playlist, :type, :collection_id, :internet_service, " + Song::kBindSpec + ")"); insert.prepare("INSERT INTO playlist_items (playlist, type, collection_id, " + Song::kColumnSpec + ") VALUES (:playlist, :type, :collection_id, " + Song::kBindSpec + ")");
QSqlQuery update(db); QSqlQuery update(db);
update.prepare("UPDATE playlists SET last_played=:last_played WHERE ROWID=:playlist"); update.prepare("UPDATE playlists SET last_played=:last_played WHERE ROWID=:playlist");

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -348,12 +349,6 @@ QString FileTypeItemDelegate::displayText(const QVariant &value, const QLocale &
} }
QString SamplerateBitdepthItemDelegate::displayText(const QVariant &value, const QLocale &locale) const {
return value.toString();
}
QWidget *TextItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QWidget *TextItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
return new QLineEdit(parent); return new QLineEdit(parent);
@ -442,37 +437,16 @@ QString SongSourceDelegate::displayText(const QVariant &value, const QLocale&) c
return QString(); return QString();
} }
QPixmap SongSourceDelegate::LookupPixmap(const QUrl &url, const QSize &size) const { QPixmap SongSourceDelegate::LookupPixmap(const Song::Source &source, const QSize &size) const {
QPixmap pixmap; QPixmap pixmap;
if (cache_.find(url.scheme(), &pixmap)) { if (cache_.find(Song::TextForSource(source), &pixmap)) {
return pixmap; return pixmap;
} }
QIcon icon; QIcon icon(Song::IconForSource(source));
const UrlHandler *handler = player_->HandlerForUrl(url);
if (handler) {
icon = handler->icon();
}
else {
if (url.scheme() == "file") {
icon = IconLoader::Load("folder-sound");
}
else if (url.scheme() == "cdda") {
icon = IconLoader::Load("cd");
}
else if (url.scheme() == "http" || url.scheme() == "https") {
if (url.host().contains(QRegExp(".*.tidal.com")))
icon = IconLoader::Load("tidal");
else
icon = IconLoader::Load("download");
}
else {
icon = IconLoader::Load("folder-sound");
}
}
pixmap = icon.pixmap(size.height()); pixmap = icon.pixmap(size.height());
cache_.insert(url.scheme(), pixmap); cache_.insert(Song::TextForSource(source), pixmap);
return pixmap; return pixmap;
@ -486,9 +460,8 @@ void SongSourceDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
QStyleOptionViewItem option_copy(option); QStyleOptionViewItem option_copy(option);
initStyleOption(&option_copy, index); initStyleOption(&option_copy, index);
// Find the pixmap to use for this URL const Song::Source &source = Song::Source(index.data().toInt());
const QUrl &url = index.data().toUrl(); QPixmap pixmap = LookupPixmap(source, option_copy.decorationSize);
QPixmap pixmap = LookupPixmap(url, option_copy.decorationSize);
float device_pixel_ratio = 1.0f; float device_pixel_ratio = 1.0f;
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -125,12 +126,6 @@ class FileTypeItemDelegate : public PlaylistDelegateBase {
QString displayText(const QVariant &value, const QLocale &locale) const; QString displayText(const QVariant &value, const QLocale &locale) const;
}; };
class SamplerateBitdepthItemDelegate : public PlaylistDelegateBase {
public:
SamplerateBitdepthItemDelegate(QObject *parent) : PlaylistDelegateBase(parent) {}
QString displayText(const QVariant &value, const QLocale &locale) const;
};
class TextItemDelegate : public PlaylistDelegateBase { class TextItemDelegate : public PlaylistDelegateBase {
public: public:
TextItemDelegate(QObject *parent) : PlaylistDelegateBase(parent) {} TextItemDelegate(QObject *parent) : PlaylistDelegateBase(parent) {}
@ -182,7 +177,7 @@ class SongSourceDelegate : public PlaylistDelegateBase {
void paint(QPainter *paint, const QStyleOptionViewItem &option, const QModelIndex &index) const; void paint(QPainter *paint, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private: private:
QPixmap LookupPixmap(const QUrl &url, const QSize &size) const; QPixmap LookupPixmap(const Song::Source &type, const QSize &size) const;
Player *player_; Player *player_;
mutable QPixmapCache cache_; mutable QPixmapCache cache_;

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -42,20 +43,18 @@
PlaylistItem::~PlaylistItem() { PlaylistItem::~PlaylistItem() {
} }
PlaylistItem* PlaylistItem::NewFromType(const QString &type) { PlaylistItem *PlaylistItem::NewFromSource(const Song::Source &source) {
if (type == "Collection") return new CollectionPlaylistItem(type); switch (source) {
else if (type == "File") return new SongPlaylistItem(type); case Song::Source_Collection: return new CollectionPlaylistItem(source);
else if (type == "Internet") return new InternetPlaylistItem("Internet"); case Song::Source_Tidal:
else if (type == "Tidal") return new InternetPlaylistItem("Tidal"); case Song::Source_Stream: return new InternetPlaylistItem(source);
default: return new SongPlaylistItem(source);
qLog(Warning) << "Invalid PlaylistItem type:" << type; }
return nullptr;
} }
PlaylistItem* PlaylistItem::NewFromSongsTable(const QString &table, const Song &song) { PlaylistItem *PlaylistItem::NewFromSongsTable(const QString &table, const Song &song) {
if (table == SCollection::kSongsTable) if (table == SCollection::kSongsTable)
return new CollectionPlaylistItem(song); return new CollectionPlaylistItem(song);
@ -67,9 +66,8 @@ PlaylistItem* PlaylistItem::NewFromSongsTable(const QString &table, const Song &
void PlaylistItem::BindToQuery(QSqlQuery *query) const { void PlaylistItem::BindToQuery(QSqlQuery *query) const {
query->bindValue(":type", type()); query->bindValue(":type", source());
query->bindValue(":collection_id", DatabaseValue(Column_CollectionId)); query->bindValue(":collection_id", DatabaseValue(Column_CollectionId));
query->bindValue(":internet_service", DatabaseValue(Column_InternetService));
DatabaseSongMetadata().BindToQuery(query); DatabaseSongMetadata().BindToQuery(query);

View File

@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -46,11 +47,11 @@ class SqlRow;
class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> { class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
public: public:
PlaylistItem(const QString &type) : should_skip_(false), type_(type) {} PlaylistItem(const Song::Source &source) : should_skip_(false), source_(source) {}
virtual ~PlaylistItem(); virtual ~PlaylistItem();
static PlaylistItem* NewFromType(const QString &type); static PlaylistItem *NewFromSource(const Song::Source &source);
static PlaylistItem* NewFromSongsTable(const QString &table, const Song &song); static PlaylistItem *NewFromSongsTable(const QString &table, const Song &song);
enum Option { enum Option {
Default = 0x00, Default = 0x00,
@ -63,7 +64,7 @@ class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
}; };
Q_DECLARE_FLAGS(Options, Option); Q_DECLARE_FLAGS(Options, Option);
virtual QString type() const { return type_; } virtual Song::Source source() const { return source_; }
virtual Options options() const { return Default; } virtual Options options() const { return Default; }
@ -104,14 +105,14 @@ class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
protected: protected:
bool should_skip_; bool should_skip_;
enum DatabaseColumn { Column_CollectionId, Column_InternetService }; enum DatabaseColumn { Column_CollectionId };
virtual QVariant DatabaseValue(DatabaseColumn) const { virtual QVariant DatabaseValue(DatabaseColumn) const {
return QVariant(QVariant::String); return QVariant(QVariant::String);
} }
virtual Song DatabaseSongMetadata() const { return Song(); } virtual Song DatabaseSongMetadata() const { return Song(); }
QString type_; Song::Source source_;
Song temp_metadata_; Song temp_metadata_;

View File

@ -1057,7 +1057,6 @@ void PlaylistView::StretchChanged(bool stretch) {
bool PlaylistView::eventFilter(QObject *object, QEvent *event) { bool PlaylistView::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::Enter && (object == horizontalScrollBar() || object == verticalScrollBar())) { if (event->type() == QEvent::Enter && (object == horizontalScrollBar() || object == verticalScrollBar())) {
//RatingHoverOut();
return false; return false;
} }
return QObject::eventFilter(object, event); return QObject::eventFilter(object, event);

View File

@ -29,15 +29,13 @@
#include "playlistitem.h" #include "playlistitem.h"
#include "songplaylistitem.h" #include "songplaylistitem.h"
SongPlaylistItem::SongPlaylistItem(const QString &type) : PlaylistItem(type) {} SongPlaylistItem::SongPlaylistItem(const Song::Source &source) : PlaylistItem(source) {}
SongPlaylistItem::SongPlaylistItem(const Song &song) SongPlaylistItem::SongPlaylistItem(const Song &song)
: PlaylistItem("File"), song_(song) {} : PlaylistItem(Song::Source_LocalFile), song_(song) {}
bool SongPlaylistItem::InitFromQuery(const SqlRow &query) { bool SongPlaylistItem::InitFromQuery(const SqlRow &query) {
song_.InitFromQuery(query, false, (Song::kColumns.count()+1)); song_.InitFromQuery(query, false, (Song::kColumns.count()+1));
return true; return true;
} }
@ -45,7 +43,6 @@ QUrl SongPlaylistItem::Url() const { return song_.url(); }
void SongPlaylistItem::Reload() { void SongPlaylistItem::Reload() {
if (song_.url().scheme() != "file") return; if (song_.url().scheme() != "file") return;
TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_); TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
} }

View File

@ -33,7 +33,7 @@
class SongPlaylistItem : public PlaylistItem { class SongPlaylistItem : public PlaylistItem {
public: public:
SongPlaylistItem(const QString &type); SongPlaylistItem(const Song::Source &source);
SongPlaylistItem(const Song &song); SongPlaylistItem(const Song &song);
// Restores a stream- or file-related playlist item using query row. // Restores a stream- or file-related playlist item using query row.

View File

@ -140,6 +140,7 @@ class TidalSearch : public QObject {
Application *app_; Application *app_;
TidalService *service_; TidalService *service_;
Song::Source source_;
QString name_; QString name_;
QString id_; QString id_;
QIcon icon_; QIcon icon_;

View File

@ -53,6 +53,7 @@
#include "tidalsearch.h" #include "tidalsearch.h"
#include "settings/tidalsettingspage.h" #include "settings/tidalsettingspage.h"
const Song::Source TidalService::kSource = Song::Source_Tidal;
const char *TidalService::kServiceName = "Tidal"; const char *TidalService::kServiceName = "Tidal";
const char *TidalService::kApiUrl = "https://listen.tidal.com/v1"; const char *TidalService::kApiUrl = "https://listen.tidal.com/v1";
const char *TidalService::kAuthUrl = "https://listen.tidal.com/v1/login/username"; const char *TidalService::kAuthUrl = "https://listen.tidal.com/v1/login/username";
@ -62,7 +63,7 @@ const char *TidalService::kApiToken = "P5Xbeo5LFvESeDy6";
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
TidalService::TidalService(Application *app, InternetModel *parent) TidalService::TidalService(Application *app, InternetModel *parent)
: InternetService(kServiceName, app, parent, parent), : InternetService(kSource, kServiceName, app, parent, parent),
network_(new NetworkAccessManager(this)), network_(new NetworkAccessManager(this)),
timer_searchdelay_(new QTimer(this)), timer_searchdelay_(new QTimer(this)),
searchdelay_(1500), searchdelay_(1500),
@ -730,7 +731,7 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
} }
int album_id = json_album["id"].toInt(); int album_id = json_album["id"].toInt();
if (album_id_requested != 0 && album_id_requested != album_id) { if (album_id_requested != 0 && album_id_requested != album_id) {
qLog(Error) << "Tidal: Invalid Json reply, track album is wrong."; qLog(Error) << "Tidal: Invalid Json reply, track album id is wrong.";
qLog(Debug) << json_album; qLog(Debug) << json_album;
return song; return song;
} }
@ -745,6 +746,7 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
//qLog(Debug) << "id" << id << "track" << track << "disc" << disc << "title" << title << "album" << album << "artist" << artist << cover << allow_streaming << url; //qLog(Debug) << "id" << id << "track" << track << "disc" << disc << "title" << title << "album" << album << "artist" << artist << cover << allow_streaming << url;
song.set_source(Song::Source_Tidal);
song.set_id(song_id); song.set_id(song_id);
song.set_album_id(album_id); song.set_album_id(album_id);
song.set_artist(artist); song.set_artist(artist);
@ -819,9 +821,9 @@ void TidalService::GetStreamURLFinished(QNetworkReply *reply, const int search_i
QString codec = json_obj["codec"].toString().toLower(); QString codec = json_obj["codec"].toString().toLower();
song.set_filetype(Song::FiletypeByExtension(codec)); song.set_filetype(Song::FiletypeByExtension(codec));
if (song.filetype() == Song::Type_Unknown) { if (song.filetype() == Song::FileType_Unknown) {
qLog(Debug) << "Tidal: Unknown codec" << codec; qLog(Debug) << "Tidal: Unknown codec" << codec;
song.set_filetype(Song::Type_Stream); song.set_filetype(Song::FileType_Stream);
} }
song.set_valid(true); song.set_valid(true);

View File

@ -47,6 +47,7 @@ class TidalService : public InternetService {
TidalService(Application *app, InternetModel *parent); TidalService(Application *app, InternetModel *parent);
~TidalService(); ~TidalService();
static const Song::Source kSource;
static const char *kServiceName; static const char *kServiceName;
void ReloadSettings(); void ReloadSettings();

View File

@ -222,15 +222,15 @@ Transcoder::Transcoder(QObject *parent, const QString &settings_postfix)
QList<TranscoderPreset> Transcoder::GetAllPresets() { QList<TranscoderPreset> Transcoder::GetAllPresets() {
QList<TranscoderPreset> ret; QList<TranscoderPreset> ret;
ret << PresetForFileType(Song::Type_FLAC); ret << PresetForFileType(Song::FileType_FLAC);
ret << PresetForFileType(Song::Type_MP4); ret << PresetForFileType(Song::FileType_MP4);
ret << PresetForFileType(Song::Type_MPEG); ret << PresetForFileType(Song::FileType_MPEG);
ret << PresetForFileType(Song::Type_OggVorbis); ret << PresetForFileType(Song::FileType_OggVorbis);
ret << PresetForFileType(Song::Type_OggFlac); ret << PresetForFileType(Song::FileType_OggFlac);
ret << PresetForFileType(Song::Type_OggSpeex); ret << PresetForFileType(Song::FileType_OggSpeex);
ret << PresetForFileType(Song::Type_ASF); ret << PresetForFileType(Song::FileType_ASF);
ret << PresetForFileType(Song::Type_WAV); ret << PresetForFileType(Song::FileType_WAV);
ret << PresetForFileType(Song::Type_OggOpus); ret << PresetForFileType(Song::FileType_OggOpus);
return ret; return ret;
} }
@ -238,23 +238,23 @@ QList<TranscoderPreset> Transcoder::GetAllPresets() {
TranscoderPreset Transcoder::PresetForFileType(Song::FileType type) { TranscoderPreset Transcoder::PresetForFileType(Song::FileType type) {
switch (type) { switch (type) {
case Song::Type_FLAC: case Song::FileType_FLAC:
return TranscoderPreset(type, tr("FLAC"), "flac", "audio/x-flac"); return TranscoderPreset(type, tr("FLAC"), "flac", "audio/x-flac");
case Song::Type_MP4: case Song::FileType_MP4:
return TranscoderPreset(type, tr("M4A AAC"), "mp4", "audio/mpeg, mpegversion=(int)4", "audio/mp4"); return TranscoderPreset(type, tr("M4A AAC"), "mp4", "audio/mpeg, mpegversion=(int)4", "audio/mp4");
case Song::Type_MPEG: case Song::FileType_MPEG:
return TranscoderPreset(type, tr("MP3"), "mp3", "audio/mpeg, mpegversion=(int)1, layer=(int)3"); return TranscoderPreset(type, tr("MP3"), "mp3", "audio/mpeg, mpegversion=(int)1, layer=(int)3");
case Song::Type_OggVorbis: case Song::FileType_OggVorbis:
return TranscoderPreset(type, tr("Ogg Vorbis"), "ogg", "audio/x-vorbis", "application/ogg"); return TranscoderPreset(type, tr("Ogg Vorbis"), "ogg", "audio/x-vorbis", "application/ogg");
case Song::Type_OggFlac: case Song::FileType_OggFlac:
return TranscoderPreset(type, tr("Ogg FLAC"), "ogg", "audio/x-flac", "application/ogg"); return TranscoderPreset(type, tr("Ogg FLAC"), "ogg", "audio/x-flac", "application/ogg");
case Song::Type_OggSpeex: case Song::FileType_OggSpeex:
return TranscoderPreset(type, tr("Ogg Speex"), "spx", "audio/x-speex", "application/ogg"); return TranscoderPreset(type, tr("Ogg Speex"), "spx", "audio/x-speex", "application/ogg");
case Song::Type_OggOpus: case Song::FileType_OggOpus:
return TranscoderPreset(type, tr("Ogg Opus"), "opus", "audio/x-opus", "application/ogg"); return TranscoderPreset(type, tr("Ogg Opus"), "opus", "audio/x-opus", "application/ogg");
case Song::Type_ASF: case Song::FileType_ASF:
return TranscoderPreset(type, tr("Windows Media audio"), "wma", "audio/x-wma", "video/x-ms-asf"); return TranscoderPreset(type, tr("Windows Media audio"), "wma", "audio/x-wma", "video/x-ms-asf");
case Song::Type_WAV: case Song::FileType_WAV:
return TranscoderPreset(type, tr("Wav"), "wav", QString(), "audio/x-wav"); return TranscoderPreset(type, tr("Wav"), "wav", QString(), "audio/x-wav");
default: default:
qLog(Warning) << "Unsupported format in PresetForFileType:" << type; qLog(Warning) << "Unsupported format in PresetForFileType:" << type;
@ -265,12 +265,12 @@ TranscoderPreset Transcoder::PresetForFileType(Song::FileType type) {
Song::FileType Transcoder::PickBestFormat(QList<Song::FileType> supported) { Song::FileType Transcoder::PickBestFormat(QList<Song::FileType> supported) {
if (supported.isEmpty()) return Song::Type_Unknown; if (supported.isEmpty()) return Song::FileType_Unknown;
QList<Song::FileType> best_formats; QList<Song::FileType> best_formats;
best_formats << Song::Type_MPEG; best_formats << Song::FileType_MPEG;
best_formats << Song::Type_OggVorbis; best_formats << Song::FileType_OggVorbis;
best_formats << Song::Type_ASF; best_formats << Song::FileType_ASF;
for (Song::FileType type : best_formats) { for (Song::FileType type : best_formats) {
if (supported.isEmpty() || supported.contains(type)) return type; if (supported.isEmpty() || supported.contains(type)) return type;

View File

@ -41,7 +41,7 @@
#include "core/song.h" #include "core/song.h"
struct TranscoderPreset { struct TranscoderPreset {
TranscoderPreset() : type_(Song::Type_Unknown) {} TranscoderPreset() : type_(Song::FileType_Unknown) {}
TranscoderPreset(Song::FileType type, const QString &name, const QString &extension, const QString &codec_mimetype, const QString &muxer_mimetype_ = QString()); TranscoderPreset(Song::FileType type, const QString &name, const QString &extension, const QString &codec_mimetype, const QString &muxer_mimetype_ = QString());
Song::FileType type_; Song::FileType type_;

View File

@ -44,14 +44,14 @@ TranscoderOptionsDialog::TranscoderOptionsDialog(Song::FileType type, QWidget *p
ui_->setupUi(this); ui_->setupUi(this);
switch (type) { switch (type) {
case Song::Type_FLAC: case Song::FileType_FLAC:
case Song::Type_OggFlac: options_ = new TranscoderOptionsFlac(this); break; case Song::FileType_OggFlac: options_ = new TranscoderOptionsFlac(this); break;
case Song::Type_MP4: options_ = new TranscoderOptionsAAC(this); break; case Song::FileType_MP4: options_ = new TranscoderOptionsAAC(this); break;
case Song::Type_MPEG: options_ = new TranscoderOptionsMP3(this); break; case Song::FileType_MPEG: options_ = new TranscoderOptionsMP3(this); break;
case Song::Type_OggVorbis: options_ = new TranscoderOptionsVorbis(this); break; case Song::FileType_OggVorbis: options_ = new TranscoderOptionsVorbis(this); break;
case Song::Type_OggOpus: options_ = new TranscoderOptionsOpus(this); break; case Song::FileType_OggOpus: options_ = new TranscoderOptionsOpus(this); break;
case Song::Type_OggSpeex: options_ = new TranscoderOptionsSpeex(this); break; case Song::FileType_OggSpeex: options_ = new TranscoderOptionsSpeex(this); break;
case Song::Type_ASF: options_ = new TranscoderOptionsWma(this); break; case Song::FileType_ASF: options_ = new TranscoderOptionsWma(this); break;
default: default:
break; break;
} }