Add source to songs and playlist_items

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

View File

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

View File

@ -11,8 +11,6 @@ CREATE TABLE device_%deviceid_subdirectories (
CREATE TABLE device_%deviceid_songs (
/* Metadata from taglib */
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
@ -27,6 +25,7 @@ CREATE TABLE device_%deviceid_songs (
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,
@ -35,8 +34,7 @@ CREATE TABLE device_%deviceid_songs (
samplerate 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,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
@ -45,8 +43,6 @@ CREATE TABLE device_%deviceid_songs (
ctime INTEGER NOT NULL,
unavailable INTEGER DEFAULT 0,
/* Other */
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,

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

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

View File

@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
DELETE FROM schema_version;
INSERT INTO schema_version (version) VALUES (2);
INSERT INTO schema_version (version) VALUES (3);
CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL,
@ -19,8 +19,6 @@ CREATE TABLE IF NOT EXISTS subdirectories (
CREATE TABLE IF NOT EXISTS songs (
/* Metadata from taglib */
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
@ -44,8 +42,7 @@ CREATE TABLE IF NOT EXISTS songs (
samplerate 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,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
@ -54,8 +51,6 @@ CREATE TABLE IF NOT EXISTS songs (
ctime INTEGER NOT NULL,
unavailable INTEGER DEFAULT 0,
/* Other */
playcount INTEGER NOT NULL DEFAULT 0,
skipcount 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 (
playlist INTEGER NOT NULL,
type TEXT NOT NULL,
type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER,
internet_service TEXT,
url TEXT,
/* Metadata from taglib */
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
@ -119,8 +111,7 @@ CREATE TABLE IF NOT EXISTS playlist_items (
samplerate 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,
filename TEXT,
filetype INTEGER NOT NULL DEFAULT 0,
@ -129,8 +120,6 @@ CREATE TABLE IF NOT EXISTS playlist_items (
ctime INTEGER,
unavailable INTEGER DEFAULT 0,
/* Other */
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,

View File

@ -1,5 +1,6 @@
/* This file is part of Strawberry.
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
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::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_FLAC;
if (dynamic_cast<TagLib::WavPack::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_WAVPACK;
if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGFLAC;
if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGVORBIS;
if (dynamic_cast<TagLib::Ogg::Opus::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGOPUS;
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGSPEEX;
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPEG;
if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MP4;
if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_ASF;
if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_AIFF;
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPC;
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_TRUEAUDIO;
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_FileType_FLAC;
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_FileType_OGGFLAC;
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_FileType_OGGOPUS;
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_FileType_MPEG;
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_FileType_ASF;
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_FileType_MPC;
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_TRUEAUDIO;
#ifdef HAVE_TAGLIB_DSFFILE
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_DSF;
if (dynamic_cast<TagLib::DSDIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_DSDIFF;
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_FileType_DSDIFF;
#endif
return pb::tagreader::SongMetadata_Type_UNKNOWN;
return pb::tagreader::SongMetadata_FileType_UNKNOWN;
}

View File

@ -1,5 +1,6 @@
/* This file is part of Strawberry.
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
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 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 std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;

View File

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

View File

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

View File

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

View File

@ -29,11 +29,13 @@
class SqlRow;
CollectionPlaylistItem::CollectionPlaylistItem(const QString &type)
: PlaylistItem(type) {}
CollectionPlaylistItem::CollectionPlaylistItem(const Song::Source &source)
: PlaylistItem(source) {}
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(); }
@ -44,7 +46,6 @@ void CollectionPlaylistItem::Reload() {
bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) {
// Rows from the songs tables come first
song_.InitFromQuery(query, true);
return song_.is_valid();
}
@ -59,4 +60,3 @@ Song CollectionPlaylistItem::Metadata() const {
if (HasTemporaryMetadata()) return temp_metadata_;
return song_;
}

View File

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

View File

@ -134,7 +134,7 @@ CollectionWatcher::ScanTransaction::~ScanTransaction() {
if (watcher_->monitor_) {
// Watch the new subdirectories
for (const Subdirectory& subdir : new_subdirs) {
for (const Subdirectory &subdir : new_subdirs) {
watcher_->AddWatch(watcher_->watched_dirs_[dir_], subdir.path);
}
}
@ -197,8 +197,7 @@ SubdirectoryList CollectionWatcher::ScanTransaction::GetImmediateSubdirs(const Q
SubdirectoryList ret;
for (const Subdirectory &subdir : known_subdirs_) {
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path &&
subdir.mtime != 0) {
if (subdir.path.left(subdir.path.lastIndexOf(QDir::separator())) == path && subdir.mtime != 0) {
ret << subdir;
}
}
@ -251,7 +250,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// Do not scan symlinked dirs that are already in collection
if (path_info.isSymLink()) {
QString real_path = path_info.symLinkTarget();
for (const Directory& dir : watched_dirs_) {
for (const Directory &dir : watched_dirs_) {
if (real_path.startsWith(dir.path)) {
t->AddToProgress(1);
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 one has been removed, "rescan" it to get the deleted songs
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) {
t->AddToProgressMax(1);
ScanSubdirectory(subdir.path, subdir, t, true);
@ -322,7 +321,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
QSet<QString> cues_processed;
// 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;
// associated cue
@ -389,16 +388,16 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
QString image = ImageForSong(file, album_art);
for (Song song : song_list) {
song.set_source(Song::Source_Collection);
song.set_directory_id(t->dir());
if (song.art_automatic().isEmpty()) song.set_art_automatic(image);
t->new_songs << song;
}
}
}
// 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())) {
qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile();
t->deleted_songs << song;
@ -420,7 +419,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// Recurse into the new subdirs that we found
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;
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));
QHash<quint64, Song> sections_map;
for (const Song& song : old_sections) {
for (const Song &song : old_sections) {
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
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());
Song matching = sections_map[cue_song.beginning_nanosec()];
@ -450,7 +450,8 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
if (!matching.is_valid()) {
t->new_songs << cue_song;
// changed section
} else {
}
else {
PreserveUserSetData(file, image, matching, &cue_song, t);
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 (cue_deleted) {
for (const Song &song :
backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
for (const Song &song : backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
if (!song.IsMetadataEqual(matching_song)) {
t->deleted_songs << song;
}
@ -478,6 +478,7 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file, const So
}
Song song_on_disk;
song_on_disk.set_source(Song::Source_Collection);
song_on_disk.set_directory_id(t->dir());
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.
// 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);
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 (TagReaderClient::Instance()->IsMediaFileBlocking(file)) {
song_list << cue_song;
@ -663,7 +664,7 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
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
for (const QString& image : images) {
for (const QString &image : images) {
QFileInfo file_info(image);
QString filename(file_info.fileName());
if (filename.contains(filter_text, Qt::CaseInsensitive))
@ -683,7 +684,7 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
int biggest_size = 0;
QString biggest_path;
for (const QString& path : filtered) {
for (const QString &path : filtered) {
QImage image(path);
if (image.isNull()) continue;

View File

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

View File

@ -293,14 +293,14 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
// Icons
qLog(Debug) << "Creating UI";
// Help menu
ui_->action_about_strawberry->setIcon(IconLoader::Load("strawberry"));
ui_->action_about_qt->setIcon(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"));
// Music menu
ui_->action_open_file->setIcon(IconLoader::Load("document-open"));
ui_->action_open_cd->setIcon(IconLoader::Load("cd"));
ui_->action_previous_track->setIcon(IconLoader::Load("media-rewind"));
@ -309,9 +309,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ui_->action_stop_after_this_track->setIcon(IconLoader::Load("media-stop"));
ui_->action_next_track->setIcon(IconLoader::Load("media-forward"));
ui_->action_quit->setIcon(IconLoader::Load("application-exit"));
// Playlist
ui_->action_add_file->setIcon(IconLoader::Load("document-open"));
ui_->action_add_folder->setIcon(IconLoader::Load("document-open-folder"));
ui_->action_shuffle_mode->setIcon(IconLoader::Load("media-playlist-shuffle"));
@ -324,11 +324,11 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ui_->action_shuffle->setIcon(IconLoader::Load("media-playlist-shuffle"));
ui_->action_remove_duplicates->setIcon(IconLoader::Load("list-remove"));
ui_->action_remove_unavailable->setIcon(IconLoader::Load("list-remove"));
//ui_->action_remove_from_playlist->setIcon(IconLoader::Load("list-remove"));
// Configure
ui_->action_cover_manager->setIcon(IconLoader::Load("document-download"));
ui_->action_queue_manager->setIcon(IconLoader::Load("footsteps"));
ui_->action_edit_track->setIcon(IconLoader::Load("edit-rename"));
@ -452,9 +452,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->playlist->view(), SIGNAL(BackgroundPropertyChanged()), SLOT(RefreshStyleSheet()));
connect(ui_->track_slider, SIGNAL(ValueChangedSeconds(int)), app_->player(), SLOT(SeekTo(int)));
// Context connections
connect(context_view_->albums(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
// Collection connections
@ -1848,7 +1848,7 @@ void MainWindow::EditFileTags(const QList<QUrl> &urls) {
Song song;
song.set_url(url);
song.set_valid(true);
song.set_filetype(Song::Type_MPEG);
song.set_filetype(Song::FileType_MPEG);
songs << song;
}

View File

@ -71,7 +71,7 @@ class MusicStorage {
virtual QString LocalPath() const { return QString(); }
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 StartCopy(QList<Song::FileType>* supported_types) { return true;}

View File

@ -154,7 +154,7 @@ void Organise::ProcessSomeFiles() {
else {
// Figure out if we need to transcode it
Song::FileType dest_type = CheckTranscode(song.filetype());
if (dest_type != Song::Type_Unknown) {
if (dest_type != Song::FileType_Unknown) {
// Get the preset
TranscoderPreset preset = Transcoder::PresetForFileType(dest_type);
qLog(Debug) << "Transcoding with" << preset.name_;
@ -206,28 +206,28 @@ void Organise::ProcessSomeFiles() {
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 Song::FileType format = destination_->GetTranscodeFormat();
switch (mode) {
case MusicStorage::Transcode_Never:
return Song::Type_Unknown;
return Song::FileType_Unknown;
case MusicStorage::Transcode_Always:
if (original_type == format) return Song::Type_Unknown;
if (original_type == format) return Song::FileType_Unknown;
return format;
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.
return Transcoder::PickBestFormat(supported_filetypes_);
}
return Song::Type_Unknown;
return Song::FileType_Unknown;
}

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@ -38,6 +39,7 @@
#include <QRegExp>
#include <QUrl>
#include <QImage>
#include <QIcon>
#include <QTextCodec>
#include <QSqlQuery>
#include <QtDebug>
@ -52,6 +54,7 @@
#include "core/logging.h"
#include "core/messagehandler.h"
#include "core/iconloader.h"
#include "engine/enginebase.h"
#include "timeconstants.h"
@ -86,6 +89,7 @@ const QStringList Song::kColumns = QStringList() << "title"
<< "samplerate"
<< "bitdepth"
<< "source"
<< "directory_id"
<< "filename"
<< "filetype"
@ -167,6 +171,7 @@ struct Song::Private : public QSharedData {
int samplerate_;
int bitdepth_;
Source source_;
int directory_id_;
QString basefilename_;
QUrl url_;
@ -210,14 +215,16 @@ Song::Private::Private()
end_(-1),
bitrate_(-1),
samplerate_(-1),
bitdepth_(-1),
source_(Source_Unknown),
directory_id_(-1),
filetype_(Type_Unknown),
filetype_(FileType_Unknown),
filesize_(-1),
mtime_(-1),
ctime_(-1),
unavailable_(false),
playcount_(0),
skipcount_(0),
lastplayed_(-1),
@ -283,6 +290,7 @@ qint64 Song::length_nanosec() const { return d->end_ - d->beginning_; }
int Song::bitrate() const { return d->bitrate_; }
int Song::samplerate() const { return d->samplerate_; }
int Song::bitdepth() const { return d->bitdepth_; }
Song::Source Song::source() const { return d->source_; }
int Song::directory_id() const { return d->directory_id_; }
const QUrl &Song::url() const { return d->url_; }
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_; }
int Song::filesize() const { return d->filesize_; }
Song::FileType Song::filetype() const { return d->filetype_; }
bool Song::is_stream() const { return d->filetype_ == Type_Stream; }
bool Song::is_cdda() const { return d->filetype_ == Type_CDDA; }
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal; }
bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
bool Song::is_collection_song() const {
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_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_url(const QUrl &v) {
if (Application::kIsPortable) {
@ -366,39 +375,93 @@ QString Song::JoinSpec(const QString &table) {
return Utilities::Prepend(table + ".", kColumns).join(", ");
}
QString Song::TextForFiletype(FileType type) {
QString Song::TextForSource(Source source) {
switch (type) {
case Song::Type_WAV: return QObject::tr("Wav");
case Song::Type_FLAC: return QObject::tr("FLAC");
case Song::Type_WavPack: return QObject::tr("WavPack");
case Song::Type_OggFlac: return QObject::tr("Ogg FLAC");
case Song::Type_OggVorbis: return QObject::tr("Ogg Vorbis");
case Song::Type_OggOpus: return QObject::tr("Ogg Opus");
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");
switch (source) {
case Song::Source_LocalFile: return QObject::tr("File");
case Song::Source_Collection: return QObject::tr("Collection");
case Song::Source_CDDA: return QObject::tr("CD");
case Song::Source_Device: return QObject::tr("Device");
case Song::Source_Stream: return QObject::tr("Stream");
case Song::Source_Tidal: return QObject::tr("Tidal");
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 {
switch (filetype()) {
case Song::Type_WAV:
case Song::Type_FLAC:
case Song::Type_OggFlac:
case Song::Type_WavPack:
case Song::Type_AIFF:
case Song::FileType_WAV:
case Song::FileType_FLAC:
case Song::FileType_OggFlac:
case Song::FileType_WavPack:
case Song::FileType_AIFF:
return true;
default:
return false;
@ -407,20 +470,20 @@ bool Song::IsFileLossless() const {
Song::FileType Song::FiletypeByExtension(QString ext) {
if (ext.toLower() == "wav" || ext.toLower() == "wave") return Song::Type_WAV;
else if (ext.toLower() == "flac") return Song::Type_FLAC;
else if (ext.toLower() == "wavpack" || ext.toLower() == "wv") return Song::Type_WavPack;
else if (ext.toLower() == "ogg" || ext.toLower() == "oga") return Song::Type_OggVorbis;
else if (ext.toLower() == "opus") return Song::Type_OggOpus;
else if (ext.toLower() == "speex" || ext.toLower() == "spx") return Song::Type_OggSpeex;
else if (ext.toLower() == "mp3") return Song::Type_MPEG;
else if (ext.toLower() == "mp4" || ext.toLower() == "m4a" || ext.toLower() == "aac") return Song::Type_MP4;
else if (ext.toLower() == "asf" || ext.toLower() == "wma") return Song::Type_ASF;
else if (ext.toLower() == "aiff" || ext.toLower() == "aif" || ext.toLower() == "aifc") return Song::Type_AIFF;
else if (ext.toLower() == "mpc" || ext.toLower() == "mp+" || ext.toLower() == "mpp") return Song::Type_MPC;
else if (ext.toLower() == "dsf") return Song::Type_DSF;
else if (ext.toLower() == "dsd" || ext.toLower() == "dff") return Song::Type_DSDIFF;
else return Song::Type_Unknown;
if (ext.toLower() == "wav" || ext.toLower() == "wave") return Song::FileType_WAV;
else if (ext.toLower() == "flac") return Song::FileType_FLAC;
else if (ext.toLower() == "wavpack" || ext.toLower() == "wv") return Song::FileType_WavPack;
else if (ext.toLower() == "ogg" || ext.toLower() == "oga") return Song::FileType_OggVorbis;
else if (ext.toLower() == "opus") return Song::FileType_OggOpus;
else if (ext.toLower() == "speex" || ext.toLower() == "spx") return Song::FileType_OggSpeex;
else if (ext.toLower() == "mp3") return Song::FileType_MPEG;
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::FileType_ASF;
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::FileType_MPC;
else if (ext.toLower() == "dsf") return Song::FileType_DSF;
else if (ext.toLower() == "dsd" || ext.toLower() == "dff") return Song::FileType_DSDIFF;
else return Song::FileType_Unknown;
}
@ -547,7 +610,7 @@ void Song::ToProtobuf(pb::tagreader::SongMetadata *pb) const {
pb->set_filesize(d->filesize_);
pb->set_suspicious_tags(d->suspicious_tags_);
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())
@ -635,6 +698,9 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
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") {
d->directory_id_ = toint(x);
}
@ -721,8 +787,10 @@ void Song::InitFromFilePartial(const QString &filename) {
QString suffix = info.suffix().toLower();
TagLib::FileRef fileref(filename.toUtf8().constData());
//if (TagLib::FileRef::defaultFileExtensions().contains(suffix.toUtf8().constData())) {
if (fileref.file()) d->valid_ = true;
if (fileref.file()) {
d->valid_ = true;
d->source_ = Source_LocalFile;
}
else {
d->valid_ = false;
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->bitdepth_ = -1; //track->bitdepth;
d->source_ = Source_Device;
QString filename = QString::fromLocal8Bit(track->ipod_path);
filename.replace(':', '/');
if (prefix.contains("://")) {
@ -780,8 +849,8 @@ void Song::InitFromItdb(const Itdb_Track *track, const QString &prefix) {
set_url(QUrl::fromLocalFile(prefix + 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->mtime_ = track->time_modified;
d->ctime_ = track->time_added;
@ -814,7 +883,7 @@ void Song::ToItdb(Itdb_Track *track) const {
//track->bithdepth = d->bithdepth_;
track->type1 = 0;
track->type2 = d->filetype_ == Type_MP4 ? 0 : 1;
track->type2 = d->filetype_ == FileType_MP4 ? 0 : 1;
track->mediatype = 1; // Audio
track->size = d->filesize_;
track->time_modified = d->mtime_;
@ -854,18 +923,20 @@ void Song::InitFromMTP(const LIBMTP_track_t *track, const QString &host) {
d->playcount_ = track->usecount;
switch (track->filetype) {
case LIBMTP_FILETYPE_WAV: d->filetype_ = Type_WAV; break;
case LIBMTP_FILETYPE_MP3: d->filetype_ = Type_MPEG; break;
case LIBMTP_FILETYPE_WMA: d->filetype_ = Type_ASF; break;
case LIBMTP_FILETYPE_OGG: d->filetype_ = Type_OggVorbis; break;
case LIBMTP_FILETYPE_MP4: d->filetype_ = Type_MP4; break;
case LIBMTP_FILETYPE_AAC: d->filetype_ = Type_MP4; break;
case LIBMTP_FILETYPE_FLAC: d->filetype_ = Type_OggFlac; break;
case LIBMTP_FILETYPE_MP2: d->filetype_ = Type_MPEG; break;
case LIBMTP_FILETYPE_M4A: d->filetype_ = Type_MP4; break;
default: d->filetype_ = Type_Unknown; break;
case LIBMTP_FILETYPE_WAV: d->filetype_ = FileType_WAV; break;
case LIBMTP_FILETYPE_MP3: d->filetype_ = FileType_MPEG; break;
case LIBMTP_FILETYPE_WMA: d->filetype_ = FileType_ASF; break;
case LIBMTP_FILETYPE_OGG: d->filetype_ = FileType_OggVorbis; break;
case LIBMTP_FILETYPE_MP4: d->filetype_ = FileType_MP4; break;
case LIBMTP_FILETYPE_AAC: d->filetype_ = FileType_MP4; break;
case LIBMTP_FILETYPE_FLAC: d->filetype_ = FileType_OggFlac; break;
case LIBMTP_FILETYPE_MP2: d->filetype_ = FileType_MPEG; break;
case LIBMTP_FILETYPE_M4A: d->filetype_ = FileType_MP4; break;
default: d->filetype_ = FileType_Unknown; break;
}
d->source_ = Source_Device;
}
void Song::ToMTP(LIBMTP_track_t *track) const {
@ -897,15 +968,15 @@ void Song::ToMTP(LIBMTP_track_t *track) const {
track->usecount = d->playcount_;
switch (d->filetype_) {
case Type_ASF: track->filetype = LIBMTP_FILETYPE_ASF; break;
case Type_MP4: track->filetype = LIBMTP_FILETYPE_MP4; break;
case Type_MPEG: track->filetype = LIBMTP_FILETYPE_MP3; break;
case Type_FLAC:
case Type_OggFlac: track->filetype = LIBMTP_FILETYPE_FLAC; break;
case Type_OggSpeex:
case Type_OggVorbis: track->filetype = LIBMTP_FILETYPE_OGG; break;
case Type_WAV: track->filetype = LIBMTP_FILETYPE_WAV; break;
default: track->filetype = LIBMTP_FILETYPE_UNDEF_AUDIO; break;
case FileType_ASF: track->filetype = LIBMTP_FILETYPE_ASF; break;
case FileType_MP4: track->filetype = LIBMTP_FILETYPE_MP4; break;
case FileType_MPEG: track->filetype = LIBMTP_FILETYPE_MP3; break;
case FileType_FLAC:
case FileType_OggFlac: track->filetype = LIBMTP_FILETYPE_FLAC; break;
case FileType_OggSpeex:
case FileType_OggVorbis: track->filetype = LIBMTP_FILETYPE_OGG; break;
case FileType_WAV: track->filetype = LIBMTP_FILETYPE_WAV; 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(":bitdepth", intval(d->bitdepth_));
query->bindValue(":source", notnullintval(d->source_));
query->bindValue(":directory_id", notnullintval(d->directory_id_));
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(d->url_)) {
@ -1103,7 +1175,7 @@ bool Song::IsMetadataEqual(const Song &other) 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 {

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@ -37,6 +38,7 @@
#include <QRegExp>
#include <QUrl>
#include <QImage>
#include <QIcon>
#include <QTextCodec>
#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.
// If a new lossless file is added, also add it to IsFileLossless().
enum FileType {
Type_Unknown = 0,
Type_WAV = 1,
Type_FLAC = 2,
Type_WavPack = 3,
Type_OggFlac = 4,
Type_OggVorbis = 5,
Type_OggOpus = 6,
Type_OggSpeex = 7,
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,
enum Source {
Source_Unknown = 0,
Source_LocalFile = 1,
Source_Collection = 2,
Source_CDDA = 3,
Source_Device = 4,
Source_Stream = 5,
Source_Tidal = 6,
};
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()); }
QIcon IconForFiletype(FileType filetype) const { return IconForFiletype(filetype); }
bool IsFileLossless() const;
static FileType FiletypeByExtension(QString ext);
@ -182,6 +203,7 @@ class Song {
int samplerate() const;
int bitdepth() const;
Source source() const;
int directory_id() const;
const QUrl &url() const;
const QString &basefilename() const;
@ -260,7 +282,8 @@ class Song {
void set_bitrate(int v);
void set_samplerate(int v);
void set_bitdepth(int v);
void set_source(Source v);
void set_directory_id(int v);
void set_url(const QUrl &v);
void set_basefilename(const QString &v);

View File

@ -276,7 +276,7 @@ void SongLoader::EffectiveSongLoad(Song *song) {
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.
return;
}
@ -335,7 +335,7 @@ void SongLoader::LoadLocalDirectory(const QString &filename) {
void SongLoader::AddAsRawStream() {
Song song;
song.set_valid(true);
song.set_filetype(Song::Type_Stream);
song.set_filetype(Song::FileType_Stream);
song.set_url(url_);
song.set_title(url_.toString());
songs_ << song;
@ -481,7 +481,7 @@ GstPadProbeReturn SongLoader::DataReady(GstPad*, GstPadProbeInfo *info, gpointer
#ifdef HAVE_GSTREAMER
gboolean SongLoader::BusCallback(GstBus *, GstMessage *msg, gpointer self) {
SongLoader *instance = reinterpret_cast<SongLoader*>(self);
switch (GST_MESSAGE_TYPE(msg)) {

View File

@ -110,7 +110,8 @@ void CddaSongLoader::LoadSongs() {
Song song;
song.set_id(track_number);
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_title(QString("Track %1").arg(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_year(ret.year_);
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);
// We need to set url: that's how playlist will find the correct item to update
song.set_url(GetUrlFromTrack(track_number++));

View File

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

View File

@ -308,7 +308,7 @@ void DeviceProperties::UpdateFormatsFinished(QFuture<bool> future) {
#ifdef HAVE_GSTREAMER
// Set the format combobox item
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,
// 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_));

View File

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

View File

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

View File

@ -209,21 +209,21 @@ bool MtpDevice::GetSupportedFiletypes(QList<Song::FileType> *ret, LIBMTP_mtpdevi
for (int i = 0; i < length; ++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_MP3: *ret << Song::Type_MPEG; break;
case LIBMTP_FILETYPE_WMA: *ret << Song::Type_ASF; break;
case LIBMTP_FILETYPE_MP3: *ret << Song::FileType_MPEG; break;
case LIBMTP_FILETYPE_WMA: *ret << Song::FileType_ASF; break;
case LIBMTP_FILETYPE_MP4:
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:
*ret << Song::Type_FLAC;
*ret << Song::Type_OggFlac;
*ret << Song::FileType_FLAC;
*ret << Song::FileType_OggFlac;
break;
case LIBMTP_FILETYPE_OGG:
*ret << Song::Type_OggVorbis;
*ret << Song::Type_OggSpeex;
*ret << Song::Type_OggFlac;
*ret << Song::FileType_OggVorbis;
*ret << Song::FileType_OggSpeex;
*ret << Song::FileType_OggFlac;
break;
default:
qLog(Error) << "Unknown MTP file format" << LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t(list[i]));

View File

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

View File

@ -105,11 +105,13 @@ class InternetModel : public QStandardItemModel {
};
// 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>
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.
@ -124,7 +126,8 @@ class InternetModel : public QStandardItemModel {
void ServiceDeleted();
private:
static QMap<QString, InternetService*> *sServices;
//static QMap<QString, InternetService*> *sServices;
static QMap<Song::Source, InternetService*> *sServices;
Application *app_;
};

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@ -33,72 +34,39 @@
#include "collection/sqlrow.h"
#include "playlist/playlistbackend.h"
InternetPlaylistItem::InternetPlaylistItem(const QString &type)
: PlaylistItem(type), set_service_icon_(false) {}
InternetPlaylistItem::InternetPlaylistItem(const Song::Source &source)
: PlaylistItem(source) {}
InternetPlaylistItem::InternetPlaylistItem(InternetService *service, const Song &metadata)
: PlaylistItem("Internet"),
service_name_(service->name()),
set_service_icon_(false),
: PlaylistItem(Song::Source_Stream),
source_(service->source()),
metadata_(metadata) {
InitMetadata();
}
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);
InitMetadata();
return true;
}
InternetService *InternetPlaylistItem::service() const {
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);
}
}
InternetService *ret = InternetModel::ServiceBySource(source_);
return ret;
}
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() {
if (metadata_.title().isEmpty())
metadata_.set_title(metadata_.url().toString());
if (metadata_.filetype() == Song::Type_Unknown) metadata_.set_filetype(Song::Type_Stream);
if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString());
if (metadata_.source() == Song::Source_Unknown) metadata_.set_source(Song::Source_Stream);
if (metadata_.filetype() == Song::FileType_Unknown) metadata_.set_filetype(Song::FileType_Stream);
metadata_.set_valid(true);
}
Song InternetPlaylistItem::Metadata() const {
if (!set_service_icon_) {
// Get the icon if we don't have it already
service();
}
if (HasTemporaryMetadata()) return temp_metadata_;
return metadata_;
}

View File

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

View File

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

View File

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

View File

@ -67,6 +67,7 @@
#include "core/logging.h"
#include "core/mimedata.h"
#include "core/tagreaderclient.h"
#include "core/song.h"
#include "collection/collection.h"
#include "collection/collectionbackend.h"
#include "collection/collectionplaylistitem.h"
@ -249,11 +250,11 @@ bool Playlist::set_column_value(Song &song, Playlist::Column column, const QVari
break;
}
return true;
}
QVariant Playlist::data(const QModelIndex &index, int role) const {
switch (role) {
case Role_IsCurrent:
return current_item_index_.isValid() && index.row() == current_item_index_.row();
@ -275,40 +276,40 @@ QVariant Playlist::data(const QModelIndex &index, int role) const {
// Don't forget to change Playlist::CompareItems when adding new columns
switch (index.column()) {
case Column_Title: return song.PrettyTitle();
case Column_Artist: return song.artist();
case Column_Album: return song.album();
case Column_Length: return song.length_nanosec();
case Column_Track: return song.track();
case Column_Disc: return song.disc();
case Column_Year: return song.year();
case Column_OriginalYear: return song.effective_originalyear();
case Column_Genre: return song.genre();
case Column_AlbumArtist: return song.playlist_albumartist();
case Column_Composer: return song.composer();
case Column_Performer: return song.performer();
case Column_Grouping: return song.grouping();
case Column_Title: return song.PrettyTitle();
case Column_Artist: return song.artist();
case Column_Album: return song.album();
case Column_Length: return song.length_nanosec();
case Column_Track: return song.track();
case Column_Disc: return song.disc();
case Column_Year: return song.year();
case Column_OriginalYear: return song.effective_originalyear();
case Column_Genre: return song.genre();
case Column_AlbumArtist: return song.playlist_albumartist();
case Column_Composer: return song.composer();
case Column_Performer: return song.performer();
case Column_Grouping: return song.grouping();
case Column_PlayCount: return song.playcount();
case Column_SkipCount: return song.skipcount();
case Column_LastPlayed: return song.lastplayed();
case Column_PlayCount: return song.playcount();
case Column_SkipCount: return song.skipcount();
case Column_LastPlayed: return song.lastplayed();
case Column_Samplerate: return song.samplerate();
case Column_Bitdepth: return song.bitdepth();
case Column_Bitrate: return song.bitrate();
case Column_Samplerate: return song.samplerate();
case Column_Bitdepth: return song.bitdepth();
case Column_Bitrate: return song.bitrate();
case Column_Filename: return song.url();
case Column_BaseFilename: return song.basefilename();
case Column_Filesize: return song.filesize();
case Column_Filetype: return song.filetype();
case Column_DateModified: return song.mtime();
case Column_DateCreated: return song.ctime();
case Column_Filename: return song.url();
case Column_BaseFilename: return song.basefilename();
case Column_Filesize: return song.filesize();
case Column_Filetype: return song.filetype();
case Column_DateModified: return song.mtime();
case Column_DateCreated: return song.ctime();
case Column_Comment:
if (role == Qt::DisplayRole) return song.comment().simplified();
if (role == Qt::DisplayRole) return song.comment().simplified();
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);
virtual_items_ << virtual_items_.count();
if (item->type() == "Collection") {
if (item->source() == Song::Source_Collection) {
int id = item->Metadata().id();
if (id != -1) {
collection_items_by_id_.insertMulti(id, item);
@ -990,16 +991,22 @@ void Playlist::UpdateItems(const SongList &songs) {
const Song &song = it.next();
PlaylistItemPtr &item = items_[i];
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
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)
item->Metadata().filetype() == Song::Type_CDDA)) {
item->Metadata().source() == Song::Source_CDDA
)
) {
PlaylistItemPtr new_item;
if (song.is_collection_song()) {
new_item = PlaylistItemPtr(new CollectionPlaylistItem(song));
collection_items_by_id_.insertMulti(song.id(), new_item);
} else {
}
else {
new_item = PlaylistItemPtr(new SongPlaylistItem(song));
}
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_Comment: strcmp(comment);
case Column_Source: cmp(url);
case Column_Source: cmp(source);
}
#undef cmp
@ -1143,7 +1150,7 @@ QString Playlist::column_name(Column column) {
case Column_LastPlayed: return tr("Last played");
case Column_Samplerate: return tr("Sample rate");
case Column_Bitdepth: return tr("Bit depth");
case Column_Bitdepth: return tr("Bit depth");
case Column_Bitrate: return tr("Bitrate");
case Column_Filename: return tr("File name");
@ -1155,7 +1162,7 @@ QString Playlist::column_name(Column column) {
case Column_Comment: return tr("Comment");
case Column_Source: return tr("Source");
default: return QString();
default: return QString();
}
return "";
@ -1395,7 +1402,7 @@ PlaylistItemList Playlist::RemoveItemsWithoutUndo(int row, int count) {
PlaylistItemPtr item(items_.takeAt(row));
ret << item;
if (item->type() == "Collection") {
if (item->source() == Song::Source_Collection) {
int id = item->Metadata().id();
if (id != -1) {
collection_items_by_id_.remove(id, item);

View File

@ -145,7 +145,7 @@ QSqlQuery PlaylistBackend::GetPlaylistRows(int playlist) {
" p.ROWID, " +
Song::JoinSpec("p") +
","
" p.type, p.internet_service"
" p.type"
" FROM playlist_items AS p"
" LEFT JOIN songs"
" 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
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) {
item->InitFromQuery(row);
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) {
// we need collection to run a CueParser; also, this method applies only to file-type PlaylistItems
if (item->type() != "File") return item;
// We need collection to run a CueParser; also, this method applies only to file-type PlaylistItems
if (item->source() != Song::Source_LocalFile) return item;
CueParser cue_parser(app_->collection_backend());
@ -279,7 +279,7 @@ void PlaylistBackend::SavePlaylist(int playlist, const PlaylistItemList &items,
QSqlQuery clear(db);
clear.prepare("DELETE FROM playlist_items WHERE playlist = :playlist");
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);
update.prepare("UPDATE playlists SET last_played=:last_played WHERE ROWID=:playlist");

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* 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 {
return new QLineEdit(parent);
@ -442,37 +437,16 @@ QString SongSourceDelegate::displayText(const QVariant &value, const QLocale&) c
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;
if (cache_.find(url.scheme(), &pixmap)) {
if (cache_.find(Song::TextForSource(source), &pixmap)) {
return pixmap;
}
QIcon icon;
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");
}
}
QIcon icon(Song::IconForSource(source));
pixmap = icon.pixmap(size.height());
cache_.insert(url.scheme(), pixmap);
cache_.insert(Song::TextForSource(source), pixmap);
return pixmap;
@ -486,9 +460,8 @@ void SongSourceDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
QStyleOptionViewItem option_copy(option);
initStyleOption(&option_copy, index);
// Find the pixmap to use for this URL
const QUrl &url = index.data().toUrl();
QPixmap pixmap = LookupPixmap(url, option_copy.decorationSize);
const Song::Source &source = Song::Source(index.data().toInt());
QPixmap pixmap = LookupPixmap(source, option_copy.decorationSize);
float device_pixel_ratio = 1.0f;
#ifdef Q_OS_MACOS

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* 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;
};
class SamplerateBitdepthItemDelegate : public PlaylistDelegateBase {
public:
SamplerateBitdepthItemDelegate(QObject *parent) : PlaylistDelegateBase(parent) {}
QString displayText(const QVariant &value, const QLocale &locale) const;
};
class TextItemDelegate : public PlaylistDelegateBase {
public:
TextItemDelegate(QObject *parent) : PlaylistDelegateBase(parent) {}
@ -182,7 +177,7 @@ class SongSourceDelegate : public PlaylistDelegateBase {
void paint(QPainter *paint, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
QPixmap LookupPixmap(const QUrl &url, const QSize &size) const;
QPixmap LookupPixmap(const Song::Source &type, const QSize &size) const;
Player *player_;
mutable QPixmapCache cache_;

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* it under the terms of the GNU General Public License as published by
@ -42,20 +43,18 @@
PlaylistItem::~PlaylistItem() {
}
PlaylistItem* PlaylistItem::NewFromType(const QString &type) {
PlaylistItem *PlaylistItem::NewFromSource(const Song::Source &source) {
if (type == "Collection") return new CollectionPlaylistItem(type);
else if (type == "File") return new SongPlaylistItem(type);
else if (type == "Internet") return new InternetPlaylistItem("Internet");
else if (type == "Tidal") return new InternetPlaylistItem("Tidal");
qLog(Warning) << "Invalid PlaylistItem type:" << type;
return nullptr;
switch (source) {
case Song::Source_Collection: return new CollectionPlaylistItem(source);
case Song::Source_Tidal:
case Song::Source_Stream: return new InternetPlaylistItem(source);
default: return new SongPlaylistItem(source);
}
}
PlaylistItem* PlaylistItem::NewFromSongsTable(const QString &table, const Song &song) {
PlaylistItem *PlaylistItem::NewFromSongsTable(const QString &table, const Song &song) {
if (table == SCollection::kSongsTable)
return new CollectionPlaylistItem(song);
@ -67,9 +66,8 @@ PlaylistItem* PlaylistItem::NewFromSongsTable(const QString &table, const Song &
void PlaylistItem::BindToQuery(QSqlQuery *query) const {
query->bindValue(":type", type());
query->bindValue(":type", source());
query->bindValue(":collection_id", DatabaseValue(Column_CollectionId));
query->bindValue(":internet_service", DatabaseValue(Column_InternetService));
DatabaseSongMetadata().BindToQuery(query);

View File

@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* 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
* 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> {
public:
PlaylistItem(const QString &type) : should_skip_(false), type_(type) {}
PlaylistItem(const Song::Source &source) : should_skip_(false), source_(source) {}
virtual ~PlaylistItem();
static PlaylistItem* NewFromType(const QString &type);
static PlaylistItem* NewFromSongsTable(const QString &table, const Song &song);
static PlaylistItem *NewFromSource(const Song::Source &source);
static PlaylistItem *NewFromSongsTable(const QString &table, const Song &song);
enum Option {
Default = 0x00,
@ -63,7 +64,7 @@ class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
};
Q_DECLARE_FLAGS(Options, Option);
virtual QString type() const { return type_; }
virtual Song::Source source() const { return source_; }
virtual Options options() const { return Default; }
@ -104,14 +105,14 @@ class PlaylistItem : public std::enable_shared_from_this<PlaylistItem> {
protected:
bool should_skip_;
enum DatabaseColumn { Column_CollectionId, Column_InternetService };
enum DatabaseColumn { Column_CollectionId };
virtual QVariant DatabaseValue(DatabaseColumn) const {
return QVariant(QVariant::String);
}
virtual Song DatabaseSongMetadata() const { return Song(); }
QString type_;
Song::Source source_;
Song temp_metadata_;

View File

@ -1057,11 +1057,10 @@ void PlaylistView::StretchChanged(bool stretch) {
bool PlaylistView::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::Enter && (object == horizontalScrollBar() || object == verticalScrollBar())) {
//RatingHoverOut();
return false;
}
return QObject::eventFilter(object, event);
}
void PlaylistView::rowsInserted(const QModelIndex &parent, int start, int end) {

View File

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

View File

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

View File

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

View File

@ -53,6 +53,7 @@
#include "tidalsearch.h"
#include "settings/tidalsettingspage.h"
const Song::Source TidalService::kSource = Song::Source_Tidal;
const char *TidalService::kServiceName = "Tidal";
const char *TidalService::kApiUrl = "https://listen.tidal.com/v1";
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;
TidalService::TidalService(Application *app, InternetModel *parent)
: InternetService(kServiceName, app, parent, parent),
: InternetService(kSource, kServiceName, app, parent, parent),
network_(new NetworkAccessManager(this)),
timer_searchdelay_(new QTimer(this)),
searchdelay_(1500),
@ -730,7 +731,7 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val
}
int album_id = json_album["id"].toInt();
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;
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;
song.set_source(Song::Source_Tidal);
song.set_id(song_id);
song.set_album_id(album_id);
song.set_artist(artist);
@ -819,9 +821,9 @@ void TidalService::GetStreamURLFinished(QNetworkReply *reply, const int search_i
QString codec = json_obj["codec"].toString().toLower();
song.set_filetype(Song::FiletypeByExtension(codec));
if (song.filetype() == Song::Type_Unknown) {
if (song.filetype() == Song::FileType_Unknown) {
qLog(Debug) << "Tidal: Unknown codec" << codec;
song.set_filetype(Song::Type_Stream);
song.set_filetype(Song::FileType_Stream);
}
song.set_valid(true);

View File

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

View File

@ -222,15 +222,15 @@ Transcoder::Transcoder(QObject *parent, const QString &settings_postfix)
QList<TranscoderPreset> Transcoder::GetAllPresets() {
QList<TranscoderPreset> ret;
ret << PresetForFileType(Song::Type_FLAC);
ret << PresetForFileType(Song::Type_MP4);
ret << PresetForFileType(Song::Type_MPEG);
ret << PresetForFileType(Song::Type_OggVorbis);
ret << PresetForFileType(Song::Type_OggFlac);
ret << PresetForFileType(Song::Type_OggSpeex);
ret << PresetForFileType(Song::Type_ASF);
ret << PresetForFileType(Song::Type_WAV);
ret << PresetForFileType(Song::Type_OggOpus);
ret << PresetForFileType(Song::FileType_FLAC);
ret << PresetForFileType(Song::FileType_MP4);
ret << PresetForFileType(Song::FileType_MPEG);
ret << PresetForFileType(Song::FileType_OggVorbis);
ret << PresetForFileType(Song::FileType_OggFlac);
ret << PresetForFileType(Song::FileType_OggSpeex);
ret << PresetForFileType(Song::FileType_ASF);
ret << PresetForFileType(Song::FileType_WAV);
ret << PresetForFileType(Song::FileType_OggOpus);
return ret;
}
@ -238,23 +238,23 @@ QList<TranscoderPreset> Transcoder::GetAllPresets() {
TranscoderPreset Transcoder::PresetForFileType(Song::FileType type) {
switch (type) {
case Song::Type_FLAC:
case Song::FileType_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");
case Song::Type_MPEG:
case Song::FileType_MPEG:
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");
case Song::Type_OggFlac:
case Song::FileType_OggFlac:
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");
case Song::Type_OggOpus:
case Song::FileType_OggOpus:
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");
case Song::Type_WAV:
case Song::FileType_WAV:
return TranscoderPreset(type, tr("Wav"), "wav", QString(), "audio/x-wav");
default:
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) {
if (supported.isEmpty()) return Song::Type_Unknown;
if (supported.isEmpty()) return Song::FileType_Unknown;
QList<Song::FileType> best_formats;
best_formats << Song::Type_MPEG;
best_formats << Song::Type_OggVorbis;
best_formats << Song::Type_ASF;
best_formats << Song::FileType_MPEG;
best_formats << Song::FileType_OggVorbis;
best_formats << Song::FileType_ASF;
for (Song::FileType type : best_formats) {
if (supported.isEmpty() || supported.contains(type)) return type;

View File

@ -41,7 +41,7 @@
#include "core/song.h"
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());
Song::FileType type_;

View File

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