Add source to songs and playlist_items
This commit is contained in:
parent
19b645d731
commit
6d686ee66a
@ -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>
|
||||||
|
@ -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
65
data/schema/schema-3.sql
Normal 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;
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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 */);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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++));
|
||||||
|
@ -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 {
|
||||||
|
@ -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_));
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]));
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -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_;
|
||||||
}
|
}
|
||||||
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
@ -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_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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_;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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_;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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_;
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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_;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user