Add tidal support

This commit is contained in:
Jonas Kvinge 2018-08-09 18:10:03 +02:00
parent 26062bd07b
commit 820124f9e1
74 changed files with 5420 additions and 273 deletions

View File

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>schema/schema.sql</file>
<file>schema/schema-1.sql</file>
<file>schema/device-schema.sql</file>
<file>style/mainwindow.css</file>
<file>style/statusview.css</file>
@ -113,6 +114,7 @@
<file>icons/128x128/xine.png</file>
<file>icons/128x128/zoom-in.png</file>
<file>icons/128x128/zoom-out.png</file>
<file>icons/128x128/tidal.png</file>
<file>icons/64x64/albums.png</file>
<file>icons/64x64/alsa.png</file>
<file>icons/64x64/application-exit.png</file>
@ -201,6 +203,7 @@
<file>icons/64x64/xine.png</file>
<file>icons/64x64/zoom-in.png</file>
<file>icons/64x64/zoom-out.png</file>
<file>icons/64x64/tidal.png</file>
<file>icons/48x48/albums.png</file>
<file>icons/48x48/alsa.png</file>
<file>icons/48x48/application-exit.png</file>
@ -292,6 +295,7 @@
<file>icons/48x48/xine.png</file>
<file>icons/48x48/zoom-in.png</file>
<file>icons/48x48/zoom-out.png</file>
<file>icons/48x48/tidal.png</file>
<file>icons/32x32/albums.png</file>
<file>icons/32x32/alsa.png</file>
<file>icons/32x32/application-exit.png</file>
@ -384,6 +388,7 @@
<file>icons/32x32/xine.png</file>
<file>icons/32x32/zoom-in.png</file>
<file>icons/32x32/zoom-out.png</file>
<file>icons/32x32/tidal.png</file>
<file>icons/22x22/albums.png</file>
<file>icons/22x22/alsa.png</file>
<file>icons/22x22/application-exit.png</file>
@ -476,5 +481,6 @@
<file>icons/22x22/xine.png</file>
<file>icons/22x22/zoom-in.png</file>
<file>icons/22x22/zoom-out.png</file>
<file>icons/22x22/tidal.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
data/icons/22x22/tidal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

BIN
data/icons/32x32/tidal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
data/icons/48x48/tidal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
data/icons/64x64/tidal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
data/icons/full/tidal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

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

@ -0,0 +1,3 @@
ALTER TABLE playlist_items ADD COLUMN internet_service TEXT;
UPDATE schema_version SET version=1;

View File

@ -1,21 +1,21 @@
CREATE TABLE schema_version (
CREATE TABLE IF NOT EXISTS schema_version (
version INTEGER NOT NULL
);
DELETE FROM schema_version;
REPLACE INTO schema_version (version) VALUES (1);
INSERT INTO schema_version (version) VALUES (0);
CREATE TABLE directories (
CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL,
subdirs INTEGER NOT NULL
);
CREATE TABLE subdirectories (
CREATE TABLE IF NOT EXISTS subdirectories (
directory_id INTEGER NOT NULL,
path TEXT NOT NULL,
mtime INTEGER NOT NULL
);
CREATE TABLE songs (
CREATE TABLE IF NOT EXISTS songs (
/* Metadata from taglib */
@ -67,12 +67,12 @@ CREATE TABLE songs (
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE playlists (
CREATE TABLE IF NOT EXISTS playlists (
name TEXT NOT NULL,
last_played INTEGER NOT NULL DEFAULT -1,
@ -83,11 +83,12 @@ CREATE TABLE playlists (
);
CREATE TABLE playlist_items (
CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL,
type TEXT NOT NULL,
collection_id INTEGER,
internet_service TEXT,
url TEXT,
/* Metadata from taglib */
@ -145,7 +146,7 @@ CREATE TABLE playlist_items (
);
CREATE TABLE devices (
CREATE TABLE IF NOT EXISTS devices (
unique_id TEXT NOT NULL,
friendly_name TEXT,
size INTEGER,
@ -155,17 +156,17 @@ CREATE TABLE devices (
transcode_format NOT NULL DEFAULT 5
);
CREATE INDEX idx_filename ON songs (filename);
CREATE INDEX IF NOT EXISTS idx_filename ON songs (filename);
CREATE INDEX idx_comp_artist ON songs (compilation_effective, artist);
CREATE INDEX IF NOT EXISTS idx_comp_artist ON songs (compilation_effective, artist);
CREATE INDEX idx_album ON songs (album);
CREATE INDEX IF NOT EXISTS idx_album ON songs (album);
CREATE INDEX idx_title ON songs (title);
CREATE INDEX IF NOT EXISTS idx_title ON songs (title);
CREATE VIEW duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
CREATE VIEW IF NOT EXISTS duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
CREATE VIRTUAL TABLE songs_fts USING fts3(
CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts3(
ftstitle,
ftsalbum,
@ -180,7 +181,7 @@ CREATE VIRTUAL TABLE songs_fts USING fts3(
);
CREATE VIRTUAL TABLE playlist_items_fts_ USING fts3(
CREATE VIRTUAL TABLE IF NOT EXISTS playlist_items_fts_ USING fts3(
ftstitle,
ftsalbum,
@ -195,7 +196,7 @@ CREATE VIRTUAL TABLE playlist_items_fts_ USING fts3(
);
CREATE VIRTUAL TABLE %allsongstables_fts USING fts3(
CREATE VIRTUAL TABLE IF NOT EXISTS %allsongstables_fts USING fts3(
ftstitle,
ftsalbum,
@ -211,7 +212,7 @@ CREATE VIRTUAL TABLE %allsongstables_fts USING fts3(
);
INSERT INTO songs_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
INSERT INTO songs_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment FROM songs;
INSERT INTO %allsongstables_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)

View File

@ -207,6 +207,7 @@ set(SOURCES
settings/shortcutssettingspage.cpp
settings/appearancesettingspage.cpp
settings/notificationssettingspage.cpp
settings/tidalsettingspage.cpp
dialogs/about.cpp
dialogs/console.cpp
@ -246,6 +247,7 @@ set(SOURCES
widgets/tracksliderpopup.cpp
widgets/tracksliderslider.cpp
widgets/widgetfadehelper.cpp
widgets/loginstatewidget.cpp
musicbrainz/acoustidclient.cpp
musicbrainz/musicbrainzclient.cpp
@ -266,6 +268,16 @@ set(SOURCES
device/deviceviewcontainer.cpp
device/filesystemdevice.cpp
internet/internetmodel.cpp
internet/internetservice.cpp
internet/internetplaylistitem.cpp
tidal/tidalservice.cpp
tidal/tidalsearch.cpp
tidal/tidalsearchview.cpp
tidal/tidalsearchmodel.cpp
tidal/tidalsearchsortmodel.cpp
tidal/tidalsearchitemdelegate.cpp
)
set(HEADERS
@ -356,7 +368,7 @@ set(HEADERS
covermanager/amazoncoverprovider.h
covermanager/musicbrainzcoverprovider.h
covermanager/discogscoverprovider.h
settings/settingsdialog.h
settings/settingspage.h
settings/behavioursettingspage.h
@ -368,7 +380,8 @@ set(HEADERS
settings/shortcutssettingspage.h
settings/appearancesettingspage.h
settings/notificationssettingspage.h
settings/tidalsettingspage.h
dialogs/about.h
dialogs/errordialog.h
dialogs/console.h
@ -405,6 +418,7 @@ set(HEADERS
widgets/tracksliderpopup.h
widgets/tracksliderslider.h
widgets/widgetfadehelper.h
widgets/loginstatewidget.h
musicbrainz/acoustidclient.h
musicbrainz/musicbrainzclient.h
@ -424,6 +438,16 @@ set(HEADERS
device/deviceview.h
device/filesystemdevice.h
internet/internetmodel.h
internet/internetservice.h
internet/internetmimedata.h
internet/internetsongmimedata.h
tidal/tidalservice.h
tidal/tidalsearch.h
tidal/tidalsearchview.h
tidal/tidalsearchmodel.h
)
set(UI
@ -457,6 +481,7 @@ set(UI
settings/shortcutssettingspage.ui
settings/appearancesettingspage.ui
settings/notificationssettingspage.ui
settings/tidalsettingspage.ui
equalizer/equalizer.ui
equalizer/equalizerslider.ui
@ -470,12 +495,15 @@ set(UI
widgets/trackslider.ui
widgets/osdpretty.ui
widgets/fileview.ui
widgets/loginstatewidget.ui
device/deviceproperties.ui
device/deviceviewcontainer.ui
globalshortcuts/globalshortcutgrabber.ui
tidal/tidalsearchview.ui
)
set(RESOURCES ../data/data.qrc)

View File

@ -789,7 +789,7 @@ void CollectionBackend::UpdateCompilations() {
info.artists.insert(artist);
info.directories.insert(filename.left(last_separator));
if (compilation_detected) info.has_compilation_detected = true;
else info.has_not_compilation_detected = true;
else info.has_not_compilation_detected = true;
}
// Now mark the songs that we think are in compilations

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "config.h"
@ -52,6 +52,9 @@
#include "covermanager/discogscoverprovider.h"
#include "covermanager/musicbrainzcoverprovider.h"
#include "internet/internetmodel.h"
#include "tidal/tidalsearch.h"
bool Application::kIsPortable = false;
class ApplicationImpl {
@ -97,7 +100,9 @@ class ApplicationImpl {
app->MoveToNewThread(loader);
return loader;
}),
current_art_loader_([=]() { return new CurrentArtLoader(app, app); })
current_art_loader_([=]() { return new CurrentArtLoader(app, app); }),
internet_model_([=]() { return new InternetModel(app, app); }),
tidal_search_([=]() { return new TidalSearch(app, app); })
{ }
Lazy<TagReaderClient> tag_reader_client_;
@ -113,6 +118,8 @@ class ApplicationImpl {
Lazy<CoverProviders> cover_providers_;
Lazy<AlbumCoverLoader> album_cover_loader_;
Lazy<CurrentArtLoader> current_art_loader_;
Lazy<InternetModel> internet_model_;
Lazy<TidalSearch> tidal_search_;
};
@ -210,6 +217,13 @@ TaskManager *Application::task_manager() const {
}
EngineDevice *Application::enginedevice() const {
//qLog(Debug) << __PRETTY_FUNCTION__;
return p_->enginedevice_.get();
}
InternetModel* Application::internet_model() const {
return p_->internet_model_.get();
}
TidalSearch* Application::tidal_search() const {
return p_->tidal_search_.get();
}

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef APPLICATION_H_
@ -49,6 +49,8 @@ class DeviceManager;
class CoverProviders;
class AlbumCoverLoader;
class CurrentArtLoader;
class InternetModel;
class TidalSearch;
class Application : public QObject {
Q_OBJECT
@ -79,6 +81,9 @@ class Application : public QObject {
CollectionBackend *collection_backend() const;
CollectionModel *collection_model() const;
InternetModel *internet_model() const;
TidalSearch *tidal_search() const;
void MoveToNewThread(QObject *object);
void MoveToThread(QObject *object, QThread *thread);

View File

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

View File

@ -126,6 +126,8 @@
#include "settings/playlistsettingspage.h"
#include "settings/settingsdialog.h"
#include "tidal/tidalsearchview.h"
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
# include "musicbrainz/tagfetcher.h"
#endif
@ -186,6 +188,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
manager->SetPlaylistManager(app->playlist_manager());
return manager;
}),
tidal_search_view_(new TidalSearchView(app_, this)),
playlist_menu_(new QMenu(this)),
playlist_add_to_another_(nullptr),
playlistitem_actions_separator_(nullptr),
@ -218,7 +221,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ui_->volume->setValue(volume);
VolumeChanged(volume);
// Initialise the global search widget
// Initialise the tidal search widget
StyleHelper::setBaseColor(palette().color(QPalette::Highlight).darker());
// Add tabs to the fancy tab widget
@ -227,6 +230,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), tr("Files"));
ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist"), tr("Playlists"));
ui_->tabs->addTab(device_view_, IconLoader::Load("device"), tr("Devices"));
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal", "Tidal"));
//ui_->tabs->AddSpacer();
// Add the now playing widget to the fancy tab widget
@ -475,6 +479,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
collection_view_->filter()->AddMenuAction(separator);
collection_view_->filter()->AddMenuAction(collection_config_action);
// Tidal
connect(tidal_search_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
// Playlist menu
playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, SLOT(PlaylistPlay()));
playlist_menu_->addAction(ui_->action_stop);
@ -657,6 +664,12 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ReloadSettings();
// Tidal search shortcut
QAction *tidal_search_action = new QAction(this);
tidal_search_action->setShortcuts(QList<QKeySequence>() << QKeySequence("Ctrl+F") << QKeySequence("Ctrl+L"));
addAction(tidal_search_action);
connect(tidal_search_action, SIGNAL(triggered()), SLOT(FocusTidalSearchField()));
// Reload pretty OSD to avoid issues with fonts
osd_->ReloadPrettyOSDSettings();
@ -745,6 +758,7 @@ void MainWindow::ReloadAllSettings() {
osd_->ReloadSettings();
collection_view_->ReloadSettings();
ui_->playlist->view()->ReloadSettings();
tidal_search_view_->ReloadSettings();
}
@ -787,7 +801,7 @@ void MainWindow::MediaPaused() {
}
void MainWindow::MediaPlaying() {
ui_->action_stop->setEnabled(true);
ui_->action_stop_after_this_track->setEnabled(true);
ui_->action_play_pause->setIcon(IconLoader::Load("media-pause"));
@ -1789,7 +1803,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::Type_MPEG);
songs << song;
}
@ -2261,3 +2275,37 @@ void MainWindow::keyPressEvent(QKeyEvent *event) {
}
}
void MainWindow::FocusTidalSearchField() {
ui_->tabs->setCurrentWidget(tidal_search_view_);
tidal_search_view_->FocusSearchField();
}
void MainWindow::DoTidalSearch(const QString& query) {
FocusTidalSearchField();
tidal_search_view_->StartSearch(query);
}
void MainWindow::SearchForArtist() {
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(playlist_menu_index_.row()));
Song song = item->Metadata();
if (!song.albumartist().isEmpty()) {
DoTidalSearch(song.albumartist().simplified());
}
else if (!song.artist().isEmpty()) {
DoTidalSearch(song.artist().simplified());
}
}
void MainWindow::SearchForAlbum() {
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(playlist_menu_index_.row()));
Song song = item->Metadata();
if (!song.album().isEmpty()) {
DoTidalSearch(song.album().simplified());
}
}

View File

@ -84,6 +84,7 @@ class TranscodeDialog;
#endif
class Ui_MainWindow;
class Windows7ThumbBar;
class TidalSearchView;
class MainWindow : public QMainWindow, public PlatformInterface {
Q_OBJECT
@ -263,6 +264,11 @@ signals:
void ShowConsole();
void FocusTidalSearchField();
void DoTidalSearch(const QString& query);
void SearchForArtist();
void SearchForAlbum();
private:
void ConnectStatusView(StatusView *statusview);
@ -313,6 +319,8 @@ signals:
PlaylistItemList autocomplete_tag_items_;
#endif
TidalSearchView *tidal_search_view_;
QAction *collection_show_all_;
QAction *collection_show_duplicates_;
QAction *collection_show_untagged_;
@ -335,6 +343,9 @@ signals:
QAction *playlist_add_to_another_;
QList<QAction*> playlistitem_actions_;
QAction *playlistitem_actions_separator_;
QAction *search_for_artist_;
QAction *search_for_album_;
QModelIndex playlist_menu_index_;
QSortFilterProxyModel *collection_sort_model_;

View File

@ -60,6 +60,8 @@
# include "dbus/metatypes.h"
#endif
#include "tidal/tidalsearch.h"
void RegisterMetaTypes() {
qRegisterMetaType<const char*>("const char*");
@ -113,4 +115,7 @@ void RegisterMetaTypes() {
#endif
#endif
qRegisterMetaType<TidalSearch::ResultList>("TidalSearch::ResultList");
qRegisterMetaType<TidalSearch::Result>("TidalSearch::Result");
}

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef MIMEDATA_H
@ -52,6 +52,9 @@ class MimeData : public QMimeData {
// If this is set then the items are added to the queue after being inserted.
bool enqueue_now_;
// If this is set then the items are added to the beginning of the queue after being inserted.
bool enqueue_next_now_;
// If this is set then the items are inserted into a newly created playlist.
bool open_in_new_playlist_;

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "config.h"
@ -393,7 +393,7 @@ bool Mpris2::CanPause() const {
bool Mpris2::CanSeek() const { return CanSeek(app_->player()->GetState()); }
bool Mpris2::CanSeek(Engine::State state) const {
return app_->player()->GetCurrentItem() && state != Engine::Empty;
return app_->player()->GetCurrentItem() && state != Engine::Empty && !app_->player()->GetCurrentItem()->Metadata().is_stream();
}
bool Mpris2::CanControl() const { return true; }

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "config.h"

View File

@ -287,9 +287,10 @@ 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_cdda() const { return d->filetype_ == Type_Cdda; }
bool Song::is_stream() const { return d->filetype_ == Type_Stream; }
bool Song::is_cdda() const { return d->filetype_ == Type_CDDA; }
bool Song::is_collection_song() const {
return !is_cdda() && id() != -1;
return !is_cdda() && !is_stream() && id() != -1;
}
const QString &Song::art_automatic() const { return d->art_automatic_; }
const QString &Song::art_manual() const { return d->art_manual_; }
@ -329,10 +330,10 @@ void Song::set_bitdepth(int v) { d->bitdepth_ = v; }
void Song::set_directory_id(int v) { d->directory_id_ = v; }
void Song::set_url(const QUrl &v) {
if (Application::kIsPortable) {
QUrl base =
QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/");
QUrl base = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/");
d->url_ = base.resolved(v);
} else {
}
else {
d->url_ = v;
}
}
@ -364,36 +365,35 @@ QString Song::JoinSpec(const QString &table) {
QString Song::TextForFiletype(FileType type) {
switch (type) {
case Song::Type_Wav: return QObject::tr("Wav");
case Song::Type_Flac: return QObject::tr("FLAC");
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_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_Cdda: return QObject::tr("CDDA");
case Song::Type_CDDA: return QObject::tr("CDDA");
case Song::Type_Unknown:
default:
return QObject::tr("Unknown");
}
}
bool Song::IsFileLossless() const {
switch (filetype()) {
case Song::Type_Wav:
case Song::Type_Flac:
case Song::Type_WAV:
case Song::Type_FLAC:
case Song::Type_OggFlac:
case Song::Type_WavPack:
case Song::Type_Aiff:
case Song::Type_AIFF:
return true;
default:
return false;
@ -628,7 +628,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
else if (Song::kColumns.value(i) == "unavailable") {
d->unavailable_ = q.value(x).toBool();
}
else if (Song::kColumns.value(i) == "playcount") {
d->playcount_ = q.value(x).isNull() ? 0 : q.value(x).toInt();
}
@ -650,7 +650,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
}
else if (Song::kColumns.value(i) == "compilation_effective") {
}
else if (Song::kColumns.value(i) == "art_automatic") {
d->art_automatic_ = q.value(x).toString();
}
@ -662,11 +662,11 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
}
else if (Song::kColumns.value(i) == "effective_originalyear") {
}
else if (Song::kColumns.value(i) == "cue_path") {
d->cue_path_ = tostr(x);
}
else {
qLog(Error) << "Forgot to handle" << Song::kColumns.value(i);
}
@ -752,7 +752,7 @@ void Song::InitFromItdb(const Itdb_Track *track, const QString &prefix) {
}
d->basefilename_ = QFileInfo(filename).fileName();
d->filetype_ = track->type2 ? Type_Mpeg : Type_Mp4;
d->filetype_ = track->type2 ? Type_MPEG : Type_MP4;
d->filesize_ = track->size;
d->mtime_ = track->time_modified;
d->ctime_ = track->time_added;
@ -785,7 +785,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_ == Type_MP4 ? 0 : 1;
track->mediatype = 1; // Audio
track->size = d->filesize_;
track->time_modified = d->mtime_;
@ -825,15 +825,15 @@ 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_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_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;
case LIBMTP_FILETYPE_MP2: d->filetype_ = Type_MPEG; break;
case LIBMTP_FILETYPE_M4A: d->filetype_ = Type_MP4; break;
default: d->filetype_ = Type_Unknown; break;
}
@ -868,14 +868,14 @@ 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_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;
case Type_WAV: track->filetype = LIBMTP_FILETYPE_WAV; break;
default: track->filetype = LIBMTP_FILETYPE_UNDEF_AUDIO; break;
}
@ -927,7 +927,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
query->bindValue(":performer", strval(d->performer_));
query->bindValue(":grouping", strval(d->grouping_));
query->bindValue(":comment", strval(d->comment_));
query->bindValue(":beginning", d->beginning_);
query->bindValue(":length", intval(length_nanosec()));
@ -1037,7 +1037,8 @@ QString Song::TitleWithCompilationArtist() const {
}
QString Song::SampleRateBitDepthToText() const {
if (d->samplerate_ == -1) return QString("");
if (d->bitdepth_ == -1) return QString("%1 hz").arg(d->samplerate_);
return QString("%1 hz / %2 bit").arg(d->samplerate_).arg(d->bitdepth_);
@ -1071,7 +1072,7 @@ bool Song::IsMetadataEqual(const Song &other) const {
}
bool Song::IsEditable() const {
return d->valid_ && !d->url_.isEmpty() && d->filetype_ != Type_Unknown && !has_cue();
return d->valid_ && !d->url_.isEmpty() && !is_stream() && d->filetype_ != Type_Unknown && !has_cue();
}
bool Song::operator==(const Song &other) const {

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef SONG_H
@ -58,12 +58,6 @@ struct _Itdb_Track;
struct LIBMTP_track_struct;
#endif
#ifdef HAVE_LIBLASTFM
namespace lastfm {
class Track;
}
#endif
class SqlRow;
class Song {
@ -95,20 +89,20 @@ class Song {
// If a new lossless file is added, also add it to IsFileLossless().
enum FileType {
Type_Unknown = 0,
Type_Wav = 1,
Type_Flac = 2,
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_MPEG = 8,
Type_MP4 = 9,
Type_ASF = 10,
Type_AIFF = 11,
Type_MPC = 12,
Type_TrueAudio = 13,
Type_Cdda = 90,
Type_CDDA = 90,
Type_Stream = 91,
};
@ -127,9 +121,6 @@ class Song {
void InitFromQuery(const SqlRow &query, bool reliable_metadata, int col = 0);
void InitFromFilePartial(const QString &filename); // Just store the filename: incomplete but fast
void InitArtManual(); // Check if there is already a art in the cache and store the filename in art_manual
#ifdef HAVE_LIBLASTFM
void InitFromLastFM(const lastfm::Track &track);
#endif
void MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle);
@ -152,9 +143,6 @@ class Song {
// Save
void BindToQuery(QSqlQuery *query) const;
void BindToFtsQuery(QSqlQuery *query) const;
#ifdef HAVE_LIBLASTFM
void ToLastFM(lastfm::Track *track, bool prefer_album_artist) const;
#endif
void ToXesam(QVariantMap *map) const;
void ToProtobuf(pb::tagreader::SongMetadata *pb) const;
@ -210,6 +198,7 @@ class Song {
const QString &effective_albumartist() const;
bool is_collection_song() const;
bool is_stream() const;
bool is_cdda() const;
// Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "config.h"
@ -51,6 +51,8 @@
#include "song.h"
#include "songloader.h"
#include "tagreaderclient.h"
#include "engine/enginetype.h"
#include "engine/enginebase.h"
#include "collection/collectionbackend.h"
#include "collection/collectionquery.h"
#include "collection/sqlrow.h"
@ -78,6 +80,7 @@ SongLoader::SongLoader(CollectionBackendInterface *collection, const Player *pla
parser_(nullptr),
collection_(collection),
player_(player) {
if (sRawUriSchemes.isEmpty()) {
sRawUriSchemes << "udp"
<< "mms"
@ -97,7 +100,7 @@ SongLoader::SongLoader(CollectionBackendInterface *collection, const Player *pla
}
SongLoader::~SongLoader() {
#ifdef HAVE_GSTREAMER
if (pipeline_) {
state_ = Finished;
@ -121,24 +124,29 @@ SongLoader::Result SongLoader::Load(const QUrl &url) {
return Success;
}
if (player_->engine()->type() == Engine::GStreamer) {
#ifdef HAVE_GSTREAMER
preload_func_ = std::bind(&SongLoader::LoadRemote, this);
preload_func_ = std::bind(&SongLoader::LoadRemote, this);
return BlockingLoadRequired;
#else
return Error;
#endif
}
return BlockingLoadRequired;
return Success;
}
void SongLoader::LoadFilenamesBlocking() {
if (preload_func_) {
preload_func_();
}
}
SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
qLog(Debug) << "Fast Loading local file" << filename;
// First check to see if it's a directory - if so we can load all the songs inside right away.
if (QFileInfo(filename).isDir()) {
@ -149,7 +157,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
song.InitFromFilePartial(filename);
if (song.is_valid()) songs_ << song;
return Success;
}
SongLoader::Result SongLoader::LoadAudioCD() {
@ -208,6 +216,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
// It's not in the database, load it asynchronously.
preload_func_ = std::bind(&SongLoader::LoadLocalAsync, this, filename);
return BlockingLoadRequired;
}
void SongLoader::LoadLocalAsync(const QString &filename) {
@ -253,6 +262,7 @@ void SongLoader::LoadLocalAsync(const QString &filename) {
Song song;
song.InitFromFilePartial(filename);
if (song.is_valid()) songs_ << song;
}
void SongLoader::LoadMetadataBlocking() {
@ -274,7 +284,8 @@ void SongLoader::EffectiveSongLoad(Song *song) {
Song collection_song = collection_->GetSongByUrl(song->url());
if (collection_song.is_valid()) {
*song = collection_song;
} else {
}
else {
// it's a normal media file
QString filename = song->url().toLocalFile();
TagReaderClient::Instance()->ReadFileBlocking(filename, song);
@ -318,7 +329,15 @@ void SongLoader::LoadLocalDirectory(const QString &filename) {
// so if the user has the "Start playing when adding to playlist" preference behaviour set,
// it can enjoy the first song being played (seek it, have moodbar, etc.)
if (!songs_.isEmpty()) EffectiveSongLoad(&(*songs_.begin()));
}
void SongLoader::AddAsRawStream() {
Song song;
song.set_valid(true);
song.set_filetype(Song::Type_Stream);
song.set_url(url_);
song.set_title(url_.toString());
songs_ << song;
}
void SongLoader::Timeout() {
@ -348,10 +367,10 @@ void SongLoader::StopTypefind() {
}
else if (success_) {
//qLog(Debug) << "Loading" << url_ << "as raw stream";
qLog(Debug) << "Loading" << url_ << "as raw stream";
// It wasn't a playlist - just put the URL in as a stream
//AddAsRawStream();
AddAsRawStream();
}
emit LoadRemoteFinished();
@ -413,7 +432,7 @@ void SongLoader::LoadRemote() {
#ifdef HAVE_GSTREAMER
void SongLoader::TypeFound(GstElement *, uint, GstCaps *caps, void *self) {
SongLoader *instance = static_cast<SongLoader*>(self);
if (instance->state_ != WaitingForType) return;

View File

@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef SONGLOADER_H