Refactoring

This commit is contained in:
Jonas Kvinge 2024-10-22 18:12:33 +02:00
parent dfcf715291
commit 8da2b9cd94
623 changed files with 9071 additions and 5126 deletions

View File

@ -176,9 +176,11 @@ if(UNIX AND NOT APPLE)
endif() endif()
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0) pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) if(UNIX AND NOT APPLE)
if(GIO_FOUND AND UNIX) pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
if(GIO_FOUND AND UNIX)
pkg_check_modules(GIO_UNIX IMPORTED_TARGET gio-unix-2.0) pkg_check_modules(GIO_UNIX IMPORTED_TARGET gio-unix-2.0)
endif()
endif() endif()
pkg_check_modules(LIBCDIO IMPORTED_TARGET libcdio) pkg_check_modules(LIBCDIO IMPORTED_TARGET libcdio)
pkg_check_modules(GSTREAMER REQUIRED IMPORTED_TARGET gstreamer-1.0) pkg_check_modules(GSTREAMER REQUIRED IMPORTED_TARGET gstreamer-1.0)
@ -327,12 +329,10 @@ optional_component(UDISKS2 ON "Devices: UDisks2 backend"
optional_component(GIO ON "Devices: GIO device backend" optional_component(GIO ON "Devices: GIO device backend"
DEPENDS "libgio" GIO_FOUND DEPENDS "libgio" GIO_FOUND
DEPENDS "Unix or Windows" "NOT APPLE"
) )
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)" optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
DEPENDS "libgio-unix" GIO_UNIX_FOUND DEPENDS "libgio-unix" GIO_UNIX_FOUND
DEPENDS "Unix or Windows" "NOT APPLE"
) )
optional_component(AUDIOCD ON "Devices: Audio CD support" optional_component(AUDIOCD ON "Devices: Audio CD support"
@ -406,9 +406,11 @@ set(SOURCES
src/core/logging.cpp src/core/logging.cpp
src/core/mainwindow.cpp src/core/mainwindow.cpp
src/core/application.cpp src/core/application.cpp
src/core/playerinterface.cpp
src/core/player.cpp src/core/player.cpp
src/core/commandlineoptions.cpp src/core/commandlineoptions.cpp
src/core/database.cpp src/core/database.cpp
src/core/memorydatabase.cpp
src/core/sqlquery.cpp src/core/sqlquery.cpp
src/core/sqlrow.cpp src/core/sqlrow.cpp
src/core/metatypes.cpp src/core/metatypes.cpp
@ -433,14 +435,18 @@ set(SOURCES
src/core/taskmanager.cpp src/core/taskmanager.cpp
src/core/thread.cpp src/core/thread.cpp
src/core/urlhandler.cpp src/core/urlhandler.cpp
src/core/urlhandlers.cpp
src/core/iconloader.cpp src/core/iconloader.cpp
src/core/standarditemiconloader.cpp src/core/standarditemiconloader.cpp
src/core/scopedtransaction.cpp src/core/scopedtransaction.cpp
src/core/translations.cpp src/core/translations.cpp
src/core/systemtrayicon.cpp
src/core/localredirectserver.cpp src/core/localredirectserver.cpp
src/core/mimedata.cpp src/core/mimedata.cpp
src/core/temporaryfile.cpp src/core/temporaryfile.cpp
src/core/enginemetadata.cpp
src/core/songmimedata.cpp
src/core/platforminterface.cpp
src/utilities/strutils.cpp src/utilities/strutils.cpp
src/utilities/envutils.cpp src/utilities/envutils.cpp
src/utilities/colorutils.cpp src/utilities/colorutils.cpp
@ -459,6 +465,7 @@ set(SOURCES
src/utilities/coverutils.cpp src/utilities/coverutils.cpp
src/utilities/screenutils.cpp src/utilities/screenutils.cpp
src/utilities/textencodingutils.cpp src/utilities/textencodingutils.cpp
src/utilities/coveroptions.cpp
src/tagreader/tagreaderclient.cpp src/tagreader/tagreaderclient.cpp
src/tagreader/tagreaderresult.cpp src/tagreader/tagreaderresult.cpp
@ -483,12 +490,45 @@ set(SOURCES
src/filterparser/filterparser.cpp src/filterparser/filterparser.cpp
src/filterparser/filtertree.cpp src/filterparser/filtertree.cpp
src/filterparser/filtertreeand.cpp
src/filterparser/filtertreecolumnterm.cpp
src/filterparser/filtertreenop.cpp
src/filterparser/filtertreenot.cpp
src/filterparser/filtertreeor.cpp
src/filterparser/filtertreeterm.cpp
src/filterparser/filterparserfloateqcomparator.cpp
src/filterparser/filterparserfloatgecomparator.cpp
src/filterparser/filterparserfloatgtcomparator.cpp
src/filterparser/filterparserfloatlecomparator.cpp
src/filterparser/filterparserfloatltcomparator.cpp
src/filterparser/filterparserfloatnecomparator.cpp
src/filterparser/filterparserint64eqcomparator.cpp
src/filterparser/filterparserint64gecomparator.cpp
src/filterparser/filterparserint64gtcomparator.cpp
src/filterparser/filterparserint64lecomparator.cpp
src/filterparser/filterparserint64ltcomparator.cpp
src/filterparser/filterparserint64necomparator.cpp
src/filterparser/filterparserinteqcomparator.cpp
src/filterparser/filterparserintgecomparator.cpp
src/filterparser/filterparserintgtcomparator.cpp
src/filterparser/filterparserintlecomparator.cpp
src/filterparser/filterparserintltcomparator.cpp
src/filterparser/filterparserintnecomparator.cpp
src/filterparser/filterparsersearchtermcomparator.cpp
src/filterparser/filterparsertextcontainscomparator.cpp
src/filterparser/filterparsertexteqcomparator.cpp
src/filterparser/filterparsertextnecomparator.cpp
src/filterparser/filterparseruinteqcomparator.cpp
src/filterparser/filterparseruintgecomparator.cpp
src/filterparser/filterparseruintgtcomparator.cpp
src/filterparser/filterparseruintlecomparator.cpp
src/filterparser/filterparseruintltcomparator.cpp
src/filterparser/filterparseruintnecomparator.cpp
src/engine/enginebase.cpp src/engine/enginebase.cpp
src/engine/enginedevice.cpp src/engine/enginedevice.cpp
src/engine/devicefinders.cpp src/engine/devicefinders.cpp
src/engine/devicefinder.cpp src/engine/devicefinder.cpp
src/engine/enginemetadata.cpp
src/engine/gststartup.cpp src/engine/gststartup.cpp
src/engine/gstengine.cpp src/engine/gstengine.cpp
src/engine/gstenginepipeline.cpp src/engine/gstenginepipeline.cpp
@ -509,11 +549,12 @@ set(SOURCES
src/context/contextview.cpp src/context/contextview.cpp
src/context/contextalbum.cpp src/context/contextalbum.cpp
src/collection/collection.cpp src/collection/collectionlibrary.cpp
src/collection/collectionmodel.cpp src/collection/collectionmodel.cpp
src/collection/collectionbackend.cpp src/collection/collectionbackend.cpp
src/collection/collectionwatcher.cpp src/collection/collectionwatcher.cpp
src/collection/collectionview.cpp src/collection/collectionview.cpp
src/collection/collectionitem.cpp
src/collection/collectionitemdelegate.cpp src/collection/collectionitemdelegate.cpp
src/collection/collectionviewcontainer.cpp src/collection/collectionviewcontainer.cpp
src/collection/collectiondirectorymodel.cpp src/collection/collectiondirectorymodel.cpp
@ -539,17 +580,23 @@ set(SOURCES
src/playlist/playlistlistmodel.cpp src/playlist/playlistlistmodel.cpp
src/playlist/playlistlistsortfiltermodel.cpp src/playlist/playlistlistsortfiltermodel.cpp
src/playlist/playlistlistview.cpp src/playlist/playlistlistview.cpp
src/playlist/playlistmanagerinterface.cpp
src/playlist/playlistmanager.cpp src/playlist/playlistmanager.cpp
src/playlist/playlistsaveoptionsdialog.cpp src/playlist/playlistsaveoptionsdialog.cpp
src/playlist/playlistsequence.cpp src/playlist/playlistsequence.cpp
src/playlist/playlisttabbar.cpp src/playlist/playlisttabbar.cpp
src/playlist/playlistundocommands.cpp
src/playlist/playlistview.cpp src/playlist/playlistview.cpp
src/playlist/playlistproxystyle.cpp src/playlist/playlistproxystyle.cpp
src/playlist/songmimedata.cpp
src/playlist/songloaderinserter.cpp src/playlist/songloaderinserter.cpp
src/playlist/songplaylistitem.cpp src/playlist/songplaylistitem.cpp
src/playlist/dynamicplaylistcontrols.cpp src/playlist/dynamicplaylistcontrols.cpp
src/playlist/playlistundocommandbase.cpp
src/playlist/playlistundocommandinsertitems.cpp
src/playlist/playlistundocommandremoveitems.cpp
src/playlist/playlistundocommandmoveitems.cpp
src/playlist/playlistundocommandreorderitems.cpp
src/playlist/playlistundocommandsortitems.cpp
src/playlist/playlistundocommandshuffleitems.cpp
src/queue/queue.cpp src/queue/queue.cpp
src/queue/queueview.cpp src/queue/queueview.cpp
@ -633,6 +680,7 @@ set(SOURCES
src/settings/settingsdialog.cpp src/settings/settingsdialog.cpp
src/settings/settingspage.cpp src/settings/settingspage.cpp
src/settings/settingsitemdelegate.cpp
src/settings/behavioursettingspage.cpp src/settings/behavioursettingspage.cpp
src/settings/collectionsettingspage.cpp src/settings/collectionsettingspage.cpp
src/settings/collectionsettingsdirectorymodel.cpp src/settings/collectionsettingsdirectorymodel.cpp
@ -667,8 +715,6 @@ set(SOURCES
src/widgets/fancytabbar.cpp src/widgets/fancytabbar.cpp
src/widgets/fancytabdata.cpp src/widgets/fancytabdata.cpp
src/widgets/favoritewidget.cpp src/widgets/favoritewidget.cpp
src/widgets/fileview.cpp
src/widgets/fileviewlist.cpp
src/widgets/forcescrollperpixel.cpp src/widgets/forcescrollperpixel.cpp
src/widgets/freespacebar.cpp src/widgets/freespacebar.cpp
src/widgets/groupediconview.cpp src/widgets/groupediconview.cpp
@ -719,7 +765,7 @@ set(SOURCES
src/radios/radiomimedata.cpp src/radios/radiomimedata.cpp
src/scrobbler/audioscrobbler.cpp src/scrobbler/audioscrobbler.cpp
src/scrobbler/scrobblersettings.cpp src/scrobbler/scrobblersettingsservice.cpp
src/scrobbler/scrobblerservice.cpp src/scrobbler/scrobblerservice.cpp
src/scrobbler/scrobblercache.cpp src/scrobbler/scrobblercache.cpp
src/scrobbler/scrobblercacheitem.cpp src/scrobbler/scrobblercacheitem.cpp
@ -749,6 +795,22 @@ set(SOURCES
src/transcoder/transcoderoptionsaac.cpp src/transcoder/transcoderoptionsaac.cpp
src/transcoder/transcoderoptionsasf.cpp src/transcoder/transcoderoptionsasf.cpp
src/transcoder/transcoderoptionsmp3.cpp src/transcoder/transcoderoptionsmp3.cpp
src/systemtrayicon/systemtrayicon.cpp
src/fileview/fileview.cpp
src/fileview/fileviewlist.cpp
src/device/devicemanager.cpp
src/device/devicelister.cpp
src/device/devicedatabasebackend.cpp
src/device/deviceinfo.cpp
src/device/deviceproperties.cpp
src/device/filesystemdevice.cpp
src/device/connecteddevice.cpp
src/device/devicestatefiltermodel.cpp
src/device/deviceviewcontainer.cpp
src/device/deviceview.cpp
) )
set(HEADERS set(HEADERS
@ -756,7 +818,9 @@ set(HEADERS
src/core/mainwindow.h src/core/mainwindow.h
src/core/application.h src/core/application.h
src/core/player.h src/core/player.h
src/core/playerinterface.h
src/core/database.h src/core/database.h
src/core/memorydatabase.h
src/core/deletefiles.h src/core/deletefiles.h
src/core/filesystemwatcherinterface.h src/core/filesystemwatcherinterface.h
src/core/mergedproxymodel.h src/core/mergedproxymodel.h
@ -769,11 +833,13 @@ set(HEADERS
src/core/songloader.h src/core/songloader.h
src/core/taskmanager.h src/core/taskmanager.h
src/core/thread.h src/core/thread.h
src/core/urlhandlers.h
src/core/urlhandler.h src/core/urlhandler.h
src/core/standarditemiconloader.h src/core/standarditemiconloader.h
src/core/mimedata.h src/core/mimedata.h
src/core/stylesheetloader.h src/core/stylesheetloader.h
src/core/localredirectserver.h src/core/localredirectserver.h
src/core/songmimedata.h
src/tagreader/tagreaderclient.h src/tagreader/tagreaderclient.h
src/tagreader/tagreaderreply.h src/tagreader/tagreaderreply.h
@ -802,7 +868,7 @@ set(HEADERS
src/context/contextview.h src/context/contextview.h
src/context/contextalbum.h src/context/contextalbum.h
src/collection/collection.h src/collection/collectionlibrary.h
src/collection/collectionmodel.h src/collection/collectionmodel.h
src/collection/collectionbackend.h src/collection/collectionbackend.h
src/collection/collectionwatcher.h src/collection/collectionwatcher.h
@ -825,6 +891,7 @@ set(HEADERS
src/playlist/playlistlistmodel.h src/playlist/playlistlistmodel.h
src/playlist/playlistlistview.h src/playlist/playlistlistview.h
src/playlist/playlistlistsortfiltermodel.h src/playlist/playlistlistsortfiltermodel.h
src/playlist/playlistmanagerinterface.h
src/playlist/playlistmanager.h src/playlist/playlistmanager.h
src/playlist/playlistsaveoptionsdialog.h src/playlist/playlistsaveoptionsdialog.h
src/playlist/playlistsequence.h src/playlist/playlistsequence.h
@ -833,7 +900,6 @@ set(HEADERS
src/playlist/playlistproxystyle.h src/playlist/playlistproxystyle.h
src/playlist/playlistitemmimedata.h src/playlist/playlistitemmimedata.h
src/playlist/songloaderinserter.h src/playlist/songloaderinserter.h
src/playlist/songmimedata.h
src/playlist/dynamicplaylistcontrols.h src/playlist/dynamicplaylistcontrols.h
src/queue/queue.h src/queue/queue.h
@ -910,6 +976,7 @@ set(HEADERS
src/settings/settingsdialog.h src/settings/settingsdialog.h
src/settings/settingspage.h src/settings/settingspage.h
src/settings/settingsitemdelegate.h
src/settings/behavioursettingspage.h src/settings/behavioursettingspage.h
src/settings/collectionsettingspage.h src/settings/collectionsettingspage.h
src/settings/collectionsettingsdirectorymodel.h src/settings/collectionsettingsdirectorymodel.h
@ -944,8 +1011,6 @@ set(HEADERS
src/widgets/fancytabbar.h src/widgets/fancytabbar.h
src/widgets/fancytabdata.h src/widgets/fancytabdata.h
src/widgets/favoritewidget.h src/widgets/favoritewidget.h
src/widgets/fileview.h
src/widgets/fileviewlist.h
src/widgets/freespacebar.h src/widgets/freespacebar.h
src/widgets/groupediconview.h src/widgets/groupediconview.h
src/widgets/lineedit.h src/widgets/lineedit.h
@ -993,7 +1058,7 @@ set(HEADERS
src/radios/radioparadiseservice.h src/radios/radioparadiseservice.h
src/scrobbler/audioscrobbler.h src/scrobbler/audioscrobbler.h
src/scrobbler/scrobblersettings.h src/scrobbler/scrobblersettingsservice.h
src/scrobbler/scrobblerservice.h src/scrobbler/scrobblerservice.h
src/scrobbler/scrobblercache.h src/scrobbler/scrobblercache.h
src/scrobbler/scrobblingapi20.h src/scrobbler/scrobblingapi20.h
@ -1020,6 +1085,19 @@ set(HEADERS
src/transcoder/transcoderoptionsaac.h src/transcoder/transcoderoptionsaac.h
src/transcoder/transcoderoptionsasf.h src/transcoder/transcoderoptionsasf.h
src/transcoder/transcoderoptionsmp3.h src/transcoder/transcoderoptionsmp3.h
src/fileview/fileview.h
src/fileview/fileviewlist.h
src/device/devicemanager.h
src/device/devicelister.h
src/device/devicedatabasebackend.h
src/device/deviceproperties.h
src/device/filesystemdevice.h
src/device/connecteddevice.h
src/device/devicestatefiltermodel.h
src/device/deviceviewcontainer.h
src/device/deviceview.h
) )
set(UI set(UI
@ -1081,7 +1159,6 @@ set(UI
src/dialogs/saveplaylistsdialog.ui src/dialogs/saveplaylistsdialog.ui
src/widgets/trackslider.ui src/widgets/trackslider.ui
src/widgets/fileview.ui
src/widgets/loginstatewidget.ui src/widgets/loginstatewidget.ui
src/osd/osdpretty.ui src/osd/osdpretty.ui
@ -1106,6 +1183,11 @@ set(UI
src/transcoder/transcoderoptionsspeex.ui src/transcoder/transcoderoptionsspeex.ui
src/transcoder/transcoderoptionsasf.ui src/transcoder/transcoderoptionsasf.ui
src/transcoder/transcoderoptionsmp3.ui src/transcoder/transcoderoptionsmp3.ui
src/fileview/fileview.ui
src/device/deviceproperties.ui
src/device/deviceviewcontainer.ui
) )
if(APPLE) if(APPLE)
@ -1114,54 +1196,80 @@ if(APPLE)
src/utilities/macosutils.mm src/utilities/macosutils.mm
src/core/scoped_nsautorelease_pool.mm src/core/scoped_nsautorelease_pool.mm
src/core/mac_startup.mm src/core/mac_startup.mm
src/core/macsystemtrayicon.mm src/systemtrayicon/macsystemtrayicon.mm
src/osd/osdmac.mm src/osd/osdmac.mm
src/widgets/searchfield_mac.mm src/widgets/searchfield_mac.mm
src/engine/macosdevicefinder.cpp src/engine/macosdevicefinder.cpp
src/device/macosdevicelister.mm src/device/macosdevicelister.mm
HEADERS HEADERS
src/core/macsystemtrayicon.h src/systemtrayicon/macsystemtrayicon.h
src/osd/osdmac.h src/osd/osdmac.h
src/device/macosdevicelister.h src/device/macosdevicelister.h
) )
else() else()
list(APPEND SOURCES src/core/qtsystemtrayicon.cpp src/widgets/searchfield_qt.cpp src/widgets/searchfield_qt_private.cpp) list(APPEND SOURCES src/systemtrayicon/qtsystemtrayicon.cpp src/widgets/searchfield_qt.cpp src/widgets/searchfield_qt_private.cpp)
list(APPEND HEADERS src/core/qtsystemtrayicon.h src/widgets/searchfield_qt_private.h) list(APPEND HEADERS src/systemtrayicon/qtsystemtrayicon.h src/widgets/searchfield_qt_private.h)
endif() endif()
optional_source(WIN32 optional_source(WIN32
SOURCES SOURCES
src/utilities/scopedwchararray.cpp
src/utilities/winutils.cpp src/utilities/winutils.cpp
src/engine/directsounddevicefinder.cpp src/engine/directsounddevicefinder.cpp
src/engine/mmdevicefinder.cpp src/engine/mmdevicefinder.cpp
src/core/scopedwchararray.cpp
src/core/windows7thumbbar.cpp src/core/windows7thumbbar.cpp
HEADERS HEADERS
src/core/windows7thumbbar.h src/core/windows7thumbbar.h
) )
if(HAVE_GLOBALSHORTCUTS) if(HAVE_GLOBALSHORTCUTS)
optional_source(HAVE_GLOBALSHORTCUTS optional_source(HAVE_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsmanager.cpp src/globalshortcuts/globalshortcutsbackend.cpp src/globalshortcuts/globalshortcutgrabber.cpp src/settings/globalshortcutssettingspage.cpp SOURCES src/globalshortcuts/globalshortcutsmanager.cpp src/globalshortcuts/globalshortcutsbackend.cpp src/globalshortcuts/globalshortcutgrabber.cpp src/settings/globalshortcutssettingspage.cpp
HEADERS src/globalshortcuts/globalshortcutsmanager.h src/globalshortcuts/globalshortcutsbackend.h src/globalshortcuts/globalshortcutgrabber.h src/settings/globalshortcutssettingspage.h HEADERS src/globalshortcuts/globalshortcutsmanager.h src/globalshortcuts/globalshortcutsbackend.h src/globalshortcuts/globalshortcutgrabber.h src/settings/globalshortcutssettingspage.h
UI src/globalshortcuts/globalshortcutgrabber.ui src/settings/globalshortcutssettingspage.ui UI src/globalshortcuts/globalshortcutgrabber.ui src/settings/globalshortcutssettingspage.ui
) )
if(HAVE_KDE_GLOBALSHORTCUTS)
optional_source(HAVE_KDE_GLOBALSHORTCUTS optional_source(HAVE_KDE_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsbackend-kde.cpp src/globalshortcuts/globalshortcutsbackend-gnome.cpp src/globalshortcuts/globalshortcutsbackend-mate.cpp SOURCES src/globalshortcuts/globalshortcutsbackend-kde.cpp
HEADERS src/globalshortcuts/globalshortcutsbackend-kde.h src/globalshortcuts/globalshortcutsbackend-gnome.h src/globalshortcuts/globalshortcutsbackend-mate.h HEADERS src/globalshortcuts/globalshortcutsbackend-kde.h
) )
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.kde.KGlobalAccel.xml kglobalaccel)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.kde.KGlobalAccel.Component.xml kglobalaccelcomponent)
endif()
if(HAVE_GNOME_GLOBALSHORTCUTS)
optional_source(HAVE_GNOME_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsbackend-gnome.cpp
HEADERS src/globalshortcuts/globalshortcutsbackend-gnome.h
)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.gnome.SettingsDaemon.MediaKeys.xml gnomesettingsdaemon)
endif()
if(HAVE_MATE_GLOBALSHORTCUTS)
optional_source(HAVE_MATE_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsbackend-mate.cpp
HEADERS src/globalshortcuts/globalshortcutsbackend-mate.h
)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.mate.SettingsDaemon.MediaKeys.xml matesettingsdaemon)
endif()
optional_source(HAVE_X11_GLOBALSHORTCUTS optional_source(HAVE_X11_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsbackend-x11.cpp src/globalshortcuts/globalshortcut.cpp src/globalshortcuts/globalshortcut-x11.cpp SOURCES src/globalshortcuts/globalshortcutsbackend-x11.cpp src/globalshortcuts/globalshortcut.cpp src/globalshortcuts/globalshortcut-x11.cpp
HEADERS src/globalshortcuts/globalshortcutsbackend-x11.h src/globalshortcuts/globalshortcut.h HEADERS src/globalshortcuts/globalshortcutsbackend-x11.h src/globalshortcuts/globalshortcut.h
) )
optional_source(APPLE optional_source(APPLE
SOURCES src/globalshortcuts/globalshortcutsbackend-macos.mm src/globalshortcuts/globalshortcutgrabber.mm SOURCES src/globalshortcuts/globalshortcutsbackend-macos.mm src/globalshortcuts/globalshortcutgrabber.mm
HEADERS src/globalshortcuts/globalshortcutsbackend-macos.h src/globalshortcuts/globalshortcutgrabber.h HEADERS src/globalshortcuts/globalshortcutsbackend-macos.h src/globalshortcuts/globalshortcutgrabber.h
) )
optional_source(WIN32 optional_source(WIN32
SOURCES src/globalshortcuts/globalshortcutsbackend-win.cpp src/globalshortcuts/globalshortcut.cpp src/globalshortcuts/globalshortcut-win.cpp SOURCES src/globalshortcuts/globalshortcutsbackend-win.cpp src/globalshortcuts/globalshortcut.cpp src/globalshortcuts/globalshortcut-win.cpp
HEADERS src/globalshortcuts/globalshortcutsbackend-win.h src/globalshortcuts/globalshortcut.h HEADERS src/globalshortcuts/globalshortcutsbackend-win.h src/globalshortcuts/globalshortcut.h
) )
endif() endif()
optional_source(HAVE_ALSA SOURCES src/engine/alsadevicefinder.cpp src/engine/alsapcmdevicefinder.cpp) optional_source(HAVE_ALSA SOURCES src/engine/alsadevicefinder.cpp src/engine/alsapcmdevicefinder.cpp)
@ -1207,8 +1315,8 @@ optional_source(HAVE_MOODBAR
src/moodbar/moodbarpipeline.cpp src/moodbar/moodbarpipeline.cpp
src/moodbar/moodbarproxystyle.cpp src/moodbar/moodbarproxystyle.cpp
src/moodbar/moodbarrenderer.cpp src/moodbar/moodbarrenderer.cpp
src/moodbar/gstfastspectrumplugin.cpp src/engine/gstfastspectrumplugin.cpp
src/moodbar/gstfastspectrum.cpp src/engine/gstfastspectrum.cpp
src/settings/moodbarsettingspage.cpp src/settings/moodbarsettingspage.cpp
HEADERS HEADERS
src/moodbar/moodbarcontroller.h src/moodbar/moodbarcontroller.h
@ -1221,57 +1329,17 @@ optional_source(HAVE_MOODBAR
src/settings/moodbarsettingspage.ui src/settings/moodbarsettingspage.ui
) )
if(HAVE_KDE_GLOBALSHORTCUTS)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.kde.KGlobalAccel.xml kglobalaccel)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.kde.KGlobalAccel.Component.xml kglobalaccelcomponent)
endif()
if(HAVE_GNOME_GLOBALSHORTCUTS)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.gnome.SettingsDaemon.MediaKeys.xml gnomesettingsdaemon)
endif()
if(HAVE_MATE_GLOBALSHORTCUTS)
qt_add_dbus_interface(SOURCES src/globalshortcuts/org.mate.SettingsDaemon.MediaKeys.xml matesettingsdaemon)
endif()
optional_source(UNIX
SOURCES
src/device/connecteddevice.cpp
src/device/devicedatabasebackend.cpp
src/device/devicelister.cpp
src/device/devicemanager.cpp
src/device/devicestatefiltermodel.cpp
src/device/filesystemdevice.cpp
src/device/deviceviewcontainer.cpp
src/device/deviceview.cpp
src/device/deviceproperties.cpp
src/device/deviceinfo.cpp
HEADERS
src/device/connecteddevice.h
src/device/devicedatabasebackend.h
src/device/devicelister.h
src/device/devicemanager.h
src/device/devicestatefiltermodel.h
src/device/filesystemdevice.h
src/device/deviceviewcontainer.h
src/device/deviceview.h
src/device/deviceproperties.h
UI
src/device/deviceproperties.ui
src/device/deviceviewcontainer.ui
)
if(UNIX) if(UNIX)
optional_source(HAVE_GIO SOURCES src/device/giolister.cpp HEADERS src/device/giolister.h) optional_source(HAVE_GIO SOURCES src/device/giolister.cpp HEADERS src/device/giolister.h)
endif() endif()
if(HAVE_UDISKS2) if(HAVE_UDISKS2)
optional_source(HAVE_UDISKS2 SOURCES src/device/udisks2lister.cpp HEADERS src/device/udisks2lister.h) optional_source(HAVE_UDISKS2 SOURCES src/device/udisks2lister.cpp HEADERS src/device/udisks2lister.h)
set_source_files_properties(src/device/org.freedesktop.DBus.ObjectManager.xml PROPERTIES NO_NAMESPACE objectmanager INCLUDE core/dbus_metatypes.h) set_source_files_properties(src/device/org.freedesktop.DBus.ObjectManager.xml PROPERTIES NO_NAMESPACE objectmanager INCLUDE includes/dbus_metatypes.h)
set_source_files_properties(src/device/org.freedesktop.UDisks2.Filesystem.xml PROPERTIES NO_NAMESPACE udisks2filesystem INCLUDE core/dbus_metatypes.h) set_source_files_properties(src/device/org.freedesktop.UDisks2.Filesystem.xml PROPERTIES NO_NAMESPACE udisks2filesystem INCLUDE includes/dbus_metatypes.h)
set_source_files_properties(src/device/org.freedesktop.UDisks2.Block.xml PROPERTIES NO_NAMESPACE udisks2block INCLUDE core/dbus_metatypes.h) set_source_files_properties(src/device/org.freedesktop.UDisks2.Block.xml PROPERTIES NO_NAMESPACE udisks2block INCLUDE includes/dbus_metatypes.h)
set_source_files_properties(src/device/org.freedesktop.UDisks2.Drive.xml PROPERTIES NO_NAMESPACE udisks2drive INCLUDE core/dbus_metatypes.h) set_source_files_properties(src/device/org.freedesktop.UDisks2.Drive.xml PROPERTIES NO_NAMESPACE udisks2drive INCLUDE includes/dbus_metatypes.h)
set_source_files_properties(src/device/org.freedesktop.UDisks2.Job.xml PROPERTIES NO_NAMESPACE udisks2job INCLUDE core/dbus_metatypes.h) set_source_files_properties(src/device/org.freedesktop.UDisks2.Job.xml PROPERTIES NO_NAMESPACE udisks2job INCLUDE includes/dbus_metatypes.h)
qt_add_dbus_interface(SOURCES src/device/org.freedesktop.DBus.ObjectManager.xml objectmanager) qt_add_dbus_interface(SOURCES src/device/org.freedesktop.DBus.ObjectManager.xml objectmanager)
qt_add_dbus_interface(SOURCES src/device/org.freedesktop.UDisks2.Filesystem.xml udisks2filesystem) qt_add_dbus_interface(SOURCES src/device/org.freedesktop.UDisks2.Filesystem.xml udisks2filesystem)
qt_add_dbus_interface(SOURCES src/device/org.freedesktop.UDisks2.Block.xml udisks2block) qt_add_dbus_interface(SOURCES src/device/org.freedesktop.UDisks2.Block.xml udisks2block)
@ -1420,7 +1488,7 @@ if(LINUX AND LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
endif() endif()
if(HAVE_TRANSLATIONS) if(HAVE_TRANSLATIONS)
qt_add_lupdate(strawberry TS_FILES "${CMAKE_SOURCE_DIR}/src/translations/strawberry_en_US.ts" OPTIONS -locations none -no-ui-lines -no-obsolete) qt_add_lupdate(strawberry_lib TS_FILES "${CMAKE_SOURCE_DIR}/src/translations/strawberry_en_US.ts" OPTIONS -locations none -no-ui-lines -no-obsolete)
file(GLOB_RECURSE ts_files ${CMAKE_SOURCE_DIR}/src/translations/*.ts) file(GLOB_RECURSE ts_files ${CMAKE_SOURCE_DIR}/src/translations/*.ts)
set_source_files_properties(${ts_files} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/data") set_source_files_properties(${ts_files} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/data")
qt_add_lrelease(strawberry TS_FILES ${ts_files} QM_FILES_OUTPUT_VARIABLE INSTALL_TRANSLATIONS_FILES) qt_add_lrelease(strawberry TS_FILES ${ts_files} QM_FILES_OUTPUT_VARIABLE INSTALL_TRANSLATIONS_FILES)
@ -1429,14 +1497,7 @@ if(HAVE_TRANSLATIONS)
endif() endif()
endif() endif()
target_include_directories(strawberry PRIVATE target_include_directories(strawberry_lib PUBLIC
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
)
target_include_directories(strawberry_lib PRIVATE
${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src
@ -1444,14 +1505,14 @@ target_include_directories(strawberry_lib PRIVATE
) )
if(SINGLEAPPLICATION_INCLUDE_DIRS) if(SINGLEAPPLICATION_INCLUDE_DIRS)
target_include_directories(strawberry SYSTEM PRIVATE ${SINGLEAPPLICATION_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PUBLIC ${SINGLEAPPLICATION_INCLUDE_DIRS})
endif() endif()
if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H) if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PUBLIC ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
endif() endif()
target_link_libraries(strawberry_lib PRIVATE target_link_libraries(strawberry_lib PUBLIC
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
PkgConfig::GLIB PkgConfig::GLIB
PkgConfig::GOBJECT PkgConfig::GOBJECT
@ -1487,10 +1548,11 @@ target_link_libraries(strawberry_lib PRIVATE
$<$<BOOL:${FREEBSD}>:execinfo> $<$<BOOL:${FREEBSD}>:execinfo>
$<$<BOOL:${WIN32}>:dsound dwmapi getopt-win::getopt> $<$<BOOL:${WIN32}>:dsound dwmapi getopt-win::getopt>
$<$<BOOL:${MSVC}>:WindowsApp> $<$<BOOL:${MSVC}>:WindowsApp>
${SINGLEAPPLICATION_LIBRARIES}
) )
if(APPLE) if(APPLE)
target_link_libraries(strawberry_lib PRIVATE target_link_libraries(strawberry_lib PUBLIC
"-framework Foundation" "-framework Foundation"
"-framework AppKit" "-framework AppKit"
"-framework Carbon" "-framework Carbon"
@ -1502,19 +1564,7 @@ if(APPLE)
) )
endif() endif()
target_link_libraries(strawberry PRIVATE target_link_libraries(strawberry PUBLIC strawberry_lib)
${CMAKE_THREAD_LIBS_INIT}
PkgConfig::GLIB
PkgConfig::GOBJECT
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Sql
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
${SINGLEAPPLICATION_LIBRARIES}
strawberry_lib
)
if(NOT APPLE) if(NOT APPLE)
install(TARGETS strawberry RUNTIME DESTINATION bin) install(TARGETS strawberry RUNTIME DESTINATION bin)

View File

@ -36,7 +36,7 @@
#include <QString> #include <QString>
#include <QPainter> #include <QPainter>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "analyzer/fht.h" #include "analyzer/fht.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"

View File

@ -44,8 +44,8 @@
#include "waverubberanalyzer.h" #include "waverubberanalyzer.h"
#include "rainbowanalyzer.h" #include "rainbowanalyzer.h"
#include "includes/shared_ptr.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/shared_ptr.h"
#include "core/settings.h" #include "core/settings.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"

View File

@ -30,7 +30,7 @@
#include <QAction> #include <QAction>
#include <QActionGroup> #include <QActionGroup>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
class QTimer; class QTimer;

View File

@ -45,13 +45,11 @@
#include <QSqlQuery> #include <QSqlQuery>
#include <QSqlError> #include <QSqlError>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/database.h" #include "core/database.h"
#include "core/scopedtransaction.h" #include "core/scopedtransaction.h"
#include "core/song.h" #include "core/song.h"
#include "core/sqlrow.h"
#include "smartplaylists/smartplaylistsearch.h"
#include "collectiondirectory.h" #include "collectiondirectory.h"
#include "collectionbackend.h" #include "collectionbackend.h"
@ -1932,37 +1930,26 @@ void CollectionBackend::DeleteAll() {
} }
SongList CollectionBackend::SmartPlaylistsFindSongs(const SmartPlaylistSearch &search) { SongList CollectionBackend::ExecuteQuery(const QString &sql) {
QMutexLocker l(db_->Mutex()); QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect()); QSqlDatabase db(db_->Connect());
// Build the query
QString sql = search.ToSql(songs_table());
// Run the query
SongList ret;
SqlQuery query(db); SqlQuery query(db);
query.prepare(sql); query.prepare(sql);
if (!query.Exec()) { if (!query.Exec()) {
db_->ReportErrors(query); db_->ReportErrors(query);
return ret; return SongList();
} }
// Read the results SongList songs;
while (query.next()) { while (query.next()) {
Song song; Song song;
song.InitFromQuery(query, true); song.InitFromQuery(query, true);
ret << song; songs << song;
} }
return ret;
} return songs;
SongList CollectionBackend::SmartPlaylistsGetAllSongs() {
// Get all the songs!
return SmartPlaylistsFindSongs(SmartPlaylistSearch(SmartPlaylistSearch::SearchType::All, SmartPlaylistSearch::TermList(), SmartPlaylistSearch::SortType::FieldAsc, SmartPlaylistSearchTerm::Field::Artist, -1));
} }

View File

@ -36,7 +36,7 @@
#include <QUrl> #include <QUrl>
#include <QSqlDatabase> #include <QSqlDatabase>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/song.h" #include "core/song.h"
#include "collectionfilteroptions.h" #include "collectionfilteroptions.h"
#include "collectionquery.h" #include "collectionquery.h"
@ -45,7 +45,6 @@
class QThread; class QThread;
class TaskManager; class TaskManager;
class Database; class Database;
class SmartPlaylistSearch;
class CollectionBackendInterface : public QObject { class CollectionBackendInterface : public QObject {
Q_OBJECT Q_OBJECT
@ -227,8 +226,7 @@ class CollectionBackend : public CollectionBackendInterface {
SongList GetSongsByFingerprint(const QString &fingerprint) override; SongList GetSongsByFingerprint(const QString &fingerprint) override;
SongList SmartPlaylistsGetAllSongs(); SongList ExecuteQuery(const QString &sql);
SongList SmartPlaylistsFindSongs(const SmartPlaylistSearch &search);
void AddOrUpdateSongsAsync(const SongList &songs); void AddOrUpdateSongsAsync(const SongList &songs);
void UpdateSongsBySongIDAsync(const SongMap &new_songs); void UpdateSongsBySongIDAsync(const SongMap &new_songs);

View File

@ -29,7 +29,7 @@
#include <QVariant> #include <QVariant>
#include <QString> #include <QString>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/filesystemmusicstorage.h" #include "core/filesystemmusicstorage.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "core/musicstorage.h" #include "core/musicstorage.h"

View File

@ -33,7 +33,7 @@
#include <QStringList> #include <QStringList>
#include <QIcon> #include <QIcon>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "collectiondirectory.h" #include "collectiondirectory.h"
class QModelIndex; class QModelIndex;

View File

@ -28,10 +28,9 @@
#include <QUrl> #include <QUrl>
#include "core/song.h" #include "core/song.h"
#include "core/songmimedata.h"
#include "filterparser/filterparser.h" #include "filterparser/filterparser.h"
#include "filterparser/filtertree.h" #include "filterparser/filtertree.h"
#include "playlist/songmimedata.h"
#include "playlist/playlistmanager.h"
#include "collectionbackend.h" #include "collectionbackend.h"
#include "collectionfilter.h" #include "collectionfilter.h"
#include "collectionmodel.h" #include "collectionmodel.h"
@ -95,7 +94,7 @@ QMimeData *CollectionFilter::mimeData(const QModelIndexList &indexes) const {
} }
data->setUrls(urls); data->setUrls(urls);
data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs); data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
return data; return data;

View File

@ -55,8 +55,8 @@
#include "groupbydialog.h" #include "groupbydialog.h"
#include "ui_collectionfilterwidget.h" #include "ui_collectionfilterwidget.h"
#include "widgets/searchfield.h" #include "widgets/searchfield.h"
#include "settings/collectionsettingspage.h" #include "constants/collectionsettings.h"
#include "settings/appearancesettingspage.h" #include "constants/appearancesettings.h"
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
@ -204,8 +204,8 @@ void CollectionFilterWidget::setFilter(CollectionFilter *filter) {
void CollectionFilterWidget::ReloadSettings() { void CollectionFilterWidget::ReloadSettings() {
Settings s; Settings s;
s.beginGroup(AppearanceSettingsPage::kSettingsGroup); s.beginGroup(AppearanceSettings::kSettingsGroup);
int iconsize = s.value(AppearanceSettingsPage::kIconSizeConfigureButtons, 20).toInt(); int iconsize = s.value(AppearanceSettings::kIconSizeConfigureButtons, 20).toInt();
s.endGroup(); s.endGroup();
ui_->options->setIconSize(QSize(iconsize, iconsize)); ui_->options->setIconSize(QSize(iconsize, iconsize));
ui_->search_field->setIconSize(iconsize); ui_->search_field->setIconSize(iconsize);
@ -345,7 +345,7 @@ void CollectionFilterWidget::SaveGroupBy() {
qLog(Debug) << "Saving current grouping to" << name; qLog(Debug) << "Saving current grouping to" << name;
Settings s; Settings s;
if (settings_group_.isEmpty() || settings_group_ == QLatin1String(CollectionSettingsPage::kSettingsGroup)) { if (settings_group_.isEmpty() || settings_group_ == QLatin1String(CollectionSettings::kSettingsGroup)) {
s.beginGroup(SavedGroupingManager::kSavedGroupingsSettingsGroup); s.beginGroup(SavedGroupingManager::kSavedGroupingsSettingsGroup);
} }
else { else {

View File

@ -0,0 +1,32 @@
/*
* Strawberry Music Player
* Copyright 2018-2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "collectionitem.h"
CollectionItem::CollectionItem(SimpleTreeModel<CollectionItem> *_model)
: SimpleTreeItem<CollectionItem>(_model),
type(Type::Root),
container_level(-1),
compilation_artist_node_(nullptr) {}
CollectionItem::CollectionItem(const Type _type, CollectionItem *_parent)
: SimpleTreeItem<CollectionItem>(_parent),
type(_type),
container_level(-1),
compilation_artist_node_(nullptr) {}

View File

@ -1,8 +1,6 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,8 +20,6 @@
#ifndef COLLECTIONITEM_H #ifndef COLLECTIONITEM_H
#define COLLECTIONITEM_H #define COLLECTIONITEM_H
#include "config.h"
#include "core/simpletreeitem.h" #include "core/simpletreeitem.h"
#include "core/song.h" #include "core/song.h"
@ -37,17 +33,8 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
LoadingIndicator, LoadingIndicator,
}; };
explicit CollectionItem(SimpleTreeModel<CollectionItem> *_model) explicit CollectionItem(SimpleTreeModel<CollectionItem> *_model);
: SimpleTreeItem<CollectionItem>(_model), explicit CollectionItem(const Type _type, CollectionItem *_parent = nullptr);
type(Type::Root),
container_level(-1),
compilation_artist_node_(nullptr) {}
explicit CollectionItem(const Type _type, CollectionItem *_parent = nullptr)
: SimpleTreeItem<CollectionItem>(_parent),
type(_type),
container_level(-1),
compilation_artist_node_(nullptr) {}
Type type; Type type;
int container_level; int container_level;

View File

@ -30,7 +30,6 @@
#include <QSettings> #include <QSettings>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include "core/application.h"
#include "core/taskmanager.h" #include "core/taskmanager.h"
#include "core/database.h" #include "core/database.h"
#include "core/thread.h" #include "core/thread.h"
@ -39,22 +38,26 @@
#include "core/settings.h" #include "core/settings.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "utilities/threadutils.h" #include "utilities/threadutils.h"
#include "collection.h" #include "collectionlibrary.h"
#include "collectionwatcher.h" #include "collectionwatcher.h"
#include "collectionbackend.h" #include "collectionbackend.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "scrobbler/lastfmimport.h" #include "constants/collectionsettings.h"
#include "settings/collectionsettingspage.h"
using std::make_shared; using std::make_shared;
const char *SCollection::kSongsTable = "songs"; const char *CollectionLibrary::kSongsTable = "songs";
const char *SCollection::kDirsTable = "directories"; const char *CollectionLibrary::kDirsTable = "directories";
const char *SCollection::kSubdirsTable = "subdirectories"; const char *CollectionLibrary::kSubdirsTable = "subdirectories";
SCollection::SCollection(Application *app, QObject *parent) CollectionLibrary::CollectionLibrary(const SharedPtr<Database> database,
const SharedPtr<TaskManager> task_manager,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<AlbumCoverLoader> albumcover_loader,
QObject *parent)
: QObject(parent), : QObject(parent),
app_(app), task_manager_(task_manager),
tagreader_client_(tagreader_client),
backend_(nullptr), backend_(nullptr),
model_(nullptr), model_(nullptr),
watcher_(nullptr), watcher_(nullptr),
@ -68,18 +71,18 @@ SCollection::SCollection(Application *app, QObject *parent)
original_thread_ = thread(); original_thread_ = thread();
backend_ = make_shared<CollectionBackend>(); backend_ = make_shared<CollectionBackend>();
backend()->moveToThread(app->database()->thread()); backend()->moveToThread(database->thread());
qLog(Debug) << &*backend_ << "moved to thread" << app->database()->thread(); qLog(Debug) << &*backend_ << "moved to thread" << database->thread();
backend_->Init(app->database(), app->task_manager(), Song::Source::Collection, QLatin1String(kSongsTable), QLatin1String(kDirsTable), QLatin1String(kSubdirsTable)); backend_->Init(database, task_manager, Song::Source::Collection, QLatin1String(kSongsTable), QLatin1String(kDirsTable), QLatin1String(kSubdirsTable));
model_ = new CollectionModel(backend_, app_, this); model_ = new CollectionModel(backend_, albumcover_loader, this);
ReloadSettings(); ReloadSettings();
} }
SCollection::~SCollection() { CollectionLibrary::~CollectionLibrary() {
if (watcher_) { if (watcher_) {
watcher_->Abort(); watcher_->Abort();
@ -92,9 +95,9 @@ SCollection::~SCollection() {
} }
void SCollection::Init() { void CollectionLibrary::Init() {
watcher_ = new CollectionWatcher(Song::Source::Collection); watcher_ = new CollectionWatcher(Song::Source::Collection, task_manager_, tagreader_client_, backend_);
watcher_thread_ = new Thread(this); watcher_thread_ = new Thread(this);
watcher_thread_->setObjectName(watcher_->objectName()); watcher_thread_->setObjectName(watcher_->objectName());
@ -106,14 +109,11 @@ void SCollection::Init() {
watcher_thread_->start(QThread::IdlePriority); watcher_thread_->start(QThread::IdlePriority);
watcher_->set_backend(backend_); QObject::connect(&*backend_, &CollectionBackend::Error, this, &CollectionLibrary::Error);
watcher_->set_task_manager(app_->task_manager());
QObject::connect(&*backend_, &CollectionBackend::Error, this, &SCollection::Error);
QObject::connect(&*backend_, &CollectionBackend::DirectoryAdded, watcher_, &CollectionWatcher::AddDirectory); QObject::connect(&*backend_, &CollectionBackend::DirectoryAdded, watcher_, &CollectionWatcher::AddDirectory);
QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory); QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
QObject::connect(&*backend_, &CollectionBackend::SongsRatingChanged, this, &SCollection::SongsRatingChanged); QObject::connect(&*backend_, &CollectionBackend::SongsRatingChanged, this, &CollectionLibrary::SongsRatingChanged);
QObject::connect(&*backend_, &CollectionBackend::SongsStatisticsChanged, this, &SCollection::SongsPlaycountChanged); QObject::connect(&*backend_, &CollectionBackend::SongsStatisticsChanged, this, &CollectionLibrary::SongsPlaycountChanged);
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, &*backend_, &CollectionBackend::AddOrUpdateSongs); QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, &*backend_, &CollectionBackend::AddOrUpdateSongs);
QObject::connect(watcher_, &CollectionWatcher::SongsMTimeUpdated, &*backend_, &CollectionBackend::UpdateMTimesOnly); QObject::connect(watcher_, &CollectionWatcher::SongsMTimeUpdated, &*backend_, &CollectionBackend::UpdateMTimesOnly);
@ -125,30 +125,27 @@ void SCollection::Init() {
QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, &*backend_, &CollectionBackend::CompilationsNeedUpdating); QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, &*backend_, &CollectionBackend::CompilationsNeedUpdating);
QObject::connect(watcher_, &CollectionWatcher::UpdateLastSeen, &*backend_, &CollectionBackend::UpdateLastSeen); QObject::connect(watcher_, &CollectionWatcher::UpdateLastSeen, &*backend_, &CollectionBackend::UpdateLastSeen);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*backend_, &CollectionBackend::UpdateLastPlayed);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*backend_, &CollectionBackend::UpdatePlayCount);
// This will start the watcher checking for updates // This will start the watcher checking for updates
backend_->LoadDirectoriesAsync(); backend_->LoadDirectoriesAsync();
} }
void SCollection::Exit() { void CollectionLibrary::Exit() {
wait_for_exit_ << &*backend_ << watcher_; wait_for_exit_ << &*backend_ << watcher_;
QObject::disconnect(&*backend_, nullptr, watcher_, nullptr); QObject::disconnect(&*backend_, nullptr, watcher_, nullptr);
QObject::disconnect(watcher_, nullptr, &*backend_, nullptr); QObject::disconnect(watcher_, nullptr, &*backend_, nullptr);
QObject::connect(&*backend_, &CollectionBackend::ExitFinished, this, &SCollection::ExitReceived); QObject::connect(&*backend_, &CollectionBackend::ExitFinished, this, &CollectionLibrary::ExitReceived);
QObject::connect(watcher_, &CollectionWatcher::ExitFinished, this, &SCollection::ExitReceived); QObject::connect(watcher_, &CollectionWatcher::ExitFinished, this, &CollectionLibrary::ExitReceived);
backend_->ExitAsync(); backend_->ExitAsync();
watcher_->Abort(); watcher_->Abort();
watcher_->ExitAsync(); watcher_->ExitAsync();
} }
void SCollection::ExitReceived() { void CollectionLibrary::ExitReceived() {
QObject *obj = sender(); QObject *obj = sender();
QObject::disconnect(obj, nullptr, this, nullptr); QObject::disconnect(obj, nullptr, this, nullptr);
@ -158,13 +155,13 @@ void SCollection::ExitReceived() {
} }
void SCollection::IncrementalScan() { watcher_->IncrementalScanAsync(); } void CollectionLibrary::IncrementalScan() { watcher_->IncrementalScanAsync(); }
void SCollection::FullScan() { watcher_->FullScanAsync(); } void CollectionLibrary::FullScan() { watcher_->FullScanAsync(); }
void SCollection::StopScan() { watcher_->Stop(); } void CollectionLibrary::StopScan() { watcher_->Stop(); }
void SCollection::Rescan(const SongList &songs) { void CollectionLibrary::Rescan(const SongList &songs) {
qLog(Debug) << "Rescan" << songs.size() << "songs"; qLog(Debug) << "Rescan" << songs.size() << "songs";
if (!songs.isEmpty()) { if (!songs.isEmpty()) {
@ -173,58 +170,58 @@ void SCollection::Rescan(const SongList &songs) {
} }
void SCollection::PauseWatcher() { watcher_->SetRescanPausedAsync(true); } void CollectionLibrary::PauseWatcher() { watcher_->SetRescanPausedAsync(true); }
void SCollection::ResumeWatcher() { watcher_->SetRescanPausedAsync(false); } void CollectionLibrary::ResumeWatcher() { watcher_->SetRescanPausedAsync(false); }
void SCollection::ReloadSettings() { void CollectionLibrary::ReloadSettings() {
watcher_->ReloadSettingsAsync(); watcher_->ReloadSettingsAsync();
model_->ReloadSettings(); model_->ReloadSettings();
Settings s; Settings s;
s.beginGroup(CollectionSettingsPage::kSettingsGroup); s.beginGroup(CollectionSettings::kSettingsGroup);
save_playcounts_to_files_ = s.value("save_playcounts", false).toBool(); save_playcounts_to_files_ = s.value(CollectionSettings::kSavePlayCounts, false).toBool();
save_ratings_to_files_ = s.value("save_ratings", false).toBool(); save_ratings_to_files_ = s.value(CollectionSettings::kSaveRatings, false).toBool();
s.endGroup(); s.endGroup();
} }
void SCollection::SyncPlaycountAndRatingToFilesAsync() { void CollectionLibrary::SyncPlaycountAndRatingToFilesAsync() {
(void)QtConcurrent::run(&SCollection::SyncPlaycountAndRatingToFiles, this); (void)QtConcurrent::run(&CollectionLibrary::SyncPlaycountAndRatingToFiles, this);
} }
void SCollection::SyncPlaycountAndRatingToFiles() { void CollectionLibrary::SyncPlaycountAndRatingToFiles() {
const int task_id = app_->task_manager()->StartTask(tr("Saving playcounts and ratings")); const int task_id = task_manager_->StartTask(tr("Saving playcounts and ratings"));
app_->task_manager()->SetTaskBlocksCollectionScans(task_id); task_manager_->SetTaskBlocksCollectionScans(task_id);
const SongList songs = backend_->GetAllSongs(); const SongList songs = backend_->GetAllSongs();
const qint64 nb_songs = songs.size(); const qint64 nb_songs = songs.size();
int i = 0; int i = 0;
for (const Song &song : songs) { for (const Song &song : songs) {
(void)TagReaderClient::Instance()->SaveSongPlaycountBlocking(song.url().toLocalFile(), song.playcount()); (void)tagreader_client_->SaveSongPlaycountBlocking(song.url().toLocalFile(), song.playcount());
(void)TagReaderClient::Instance()->SaveSongRatingBlocking(song.url().toLocalFile(), song.rating()); (void)tagreader_client_->SaveSongRatingBlocking(song.url().toLocalFile(), song.rating());
app_->task_manager()->SetTaskProgress(task_id, ++i, nb_songs); task_manager_->SetTaskProgress(task_id, ++i, nb_songs);
} }
app_->task_manager()->SetTaskFinished(task_id); task_manager_->SetTaskFinished(task_id);
} }
void SCollection::SongsPlaycountChanged(const SongList &songs, const bool save_tags) { void CollectionLibrary::SongsPlaycountChanged(const SongList &songs, const bool save_tags) const {
if (save_tags || save_playcounts_to_files_) { if (save_tags || save_playcounts_to_files_) {
app_->tag_reader_client()->SaveSongsPlaycountAsync(songs); tagreader_client_->SaveSongsPlaycountAsync(songs);
} }
} }
void SCollection::SongsRatingChanged(const SongList &songs, const bool save_tags) { void CollectionLibrary::SongsRatingChanged(const SongList &songs, const bool save_tags) const {
if (save_tags || save_ratings_to_files_) { if (save_tags || save_ratings_to_files_) {
app_->tag_reader_client()->SaveSongsRatingAsync(songs); tagreader_client_->SaveSongsRatingAsync(songs);
} }
} }

View File

@ -29,22 +29,30 @@
#include <QHash> #include <QHash>
#include <QString> #include <QString>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/song.h" #include "core/song.h"
class QThread; class QThread;
class Application;
class Thread; class Thread;
class Database;
class TaskManager;
class TagReaderClient;
class CollectionBackend; class CollectionBackend;
class CollectionModel; class CollectionModel;
class CollectionWatcher; class CollectionWatcher;
class AlbumCoverLoader;
class SCollection : public QObject { class CollectionLibrary : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit SCollection(Application *app, QObject *parent = nullptr); explicit CollectionLibrary(const SharedPtr<Database> database,
~SCollection() override; const SharedPtr<TaskManager> task_manager,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<AlbumCoverLoader> albumcover_loader,
QObject *parent = nullptr);
~CollectionLibrary() override;
static const char *kSongsTable; static const char *kSongsTable;
static const char *kFtsTable; static const char *kFtsTable;
@ -78,15 +86,17 @@ class SCollection : public QObject {
private Q_SLOTS: private Q_SLOTS:
void ExitReceived(); void ExitReceived();
void SongsPlaycountChanged(const SongList &songs, const bool save_tags = false); void SongsPlaycountChanged(const SongList &songs, const bool save_tags = false) const;
void SongsRatingChanged(const SongList &songs, const bool save_tags = false); void SongsRatingChanged(const SongList &songs, const bool save_tags = false) const;
Q_SIGNALS: Q_SIGNALS:
void Error(const QString &error); void Error(const QString &error);
void ExitFinished(); void ExitFinished();
private: private:
Application *app_; const SharedPtr<TaskManager> task_manager_;
const SharedPtr<TagReaderClient> tagreader_client_;
SharedPtr<CollectionBackend> backend_; SharedPtr<CollectionBackend> backend_;
CollectionModel *model_; CollectionModel *model_;

View File

@ -53,14 +53,13 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QTimer> #include <QTimer>
#include "core/scoped_ptr.h" #include "includes/scoped_ptr.h"
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/application.h"
#include "core/database.h" #include "core/database.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/sqlrow.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/songmimedata.h"
#include "collectionfilteroptions.h" #include "collectionfilteroptions.h"
#include "collectionquery.h" #include "collectionquery.h"
#include "collectionbackend.h" #include "collectionbackend.h"
@ -69,12 +68,10 @@
#include "collectionmodel.h" #include "collectionmodel.h"
#include "collectionmodelupdate.h" #include "collectionmodelupdate.h"
#include "collectionfilter.h" #include "collectionfilter.h"
#include "playlist/playlistmanager.h"
#include "playlist/songmimedata.h"
#include "covermanager/albumcoverloaderoptions.h" #include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h" #include "covermanager/albumcoverloaderresult.h"
#include "covermanager/albumcoverloader.h" #include "covermanager/albumcoverloader.h"
#include "settings/collectionsettingspage.h" #include "constants/collectionsettings.h"
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
@ -85,12 +82,10 @@ constexpr char kPixmapDiskCacheDir[] = "pixmapcache";
constexpr char kVariousArtists[] = QT_TR_NOOP("Various artists"); constexpr char kVariousArtists[] = QT_TR_NOOP("Various artists");
} // namespace } // namespace
QNetworkDiskCache *CollectionModel::sIconCache = nullptr; CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, const SharedPtr<AlbumCoverLoader> albumcover_loader, QObject *parent)
CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Application *app, QObject *parent)
: SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent), : SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
backend_(backend), backend_(backend),
app_(app), albumcover_loader_(albumcover_loader),
dir_model_(new CollectionDirectoryModel(backend, this)), dir_model_(new CollectionDirectoryModel(backend, this)),
filter_(new CollectionFilter(this)), filter_(new CollectionFilter(this)),
timer_reload_(new QTimer(this)), timer_reload_(new QTimer(this)),
@ -100,7 +95,8 @@ CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Applicati
total_song_count_(0), total_song_count_(0),
total_artist_count_(0), total_artist_count_(0),
total_album_count_(0), total_album_count_(0),
loading_(false) { loading_(false),
icon_disk_cache_(nullptr) {
setObjectName(backend_->source() == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(backend_->source()), QLatin1String(metaObject()->className()))); setObjectName(backend_->source() == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(backend_->source()), QLatin1String(metaObject()->className())));
@ -108,8 +104,8 @@ CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Applicati
filter_->setSortRole(Role_SortText); filter_->setSortRole(Role_SortText);
filter_->sort(0); filter_->sort(0);
if (app_) { if (albumcover_loader_) {
QObject::connect(&*app_->album_cover_loader(), &AlbumCoverLoader::AlbumCoverLoaded, this, &CollectionModel::AlbumCoverLoaded); QObject::connect(&*albumcover_loader_, &AlbumCoverLoader::AlbumCoverLoaded, this, &CollectionModel::AlbumCoverLoaded);
} }
QIcon nocover = IconLoader::Load(u"cdcase"_s); QIcon nocover = IconLoader::Load(u"cdcase"_s);
@ -118,10 +114,9 @@ CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Applicati
pixmap_no_cover_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); pixmap_no_cover_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} }
if (app_ && !sIconCache) { if (!qgetenv("DISPLAY").isEmpty() && !icon_disk_cache_) {
sIconCache = new QNetworkDiskCache(this); icon_disk_cache_ = new QNetworkDiskCache(this);
sIconCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/' + QLatin1String(kPixmapDiskCacheDir)); icon_disk_cache_->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/' + QLatin1String(kPixmapDiskCacheDir) + u'-' + Song::TextForSource(backend_->source()));
QObject::connect(app_, &Application::ClearPixmapDiskCache, this, &CollectionModel::ClearDiskCache);
} }
QObject::connect(&*backend_, &CollectionBackend::SongsAdded, this, &CollectionModel::AddReAddOrUpdate); QObject::connect(&*backend_, &CollectionBackend::SongsAdded, this, &CollectionModel::AddReAddOrUpdate);
@ -230,16 +225,16 @@ void CollectionModel::ScheduleReset() {
void CollectionModel::ReloadSettings() { void CollectionModel::ReloadSettings() {
Settings settings; Settings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup); settings.beginGroup(CollectionSettings::kSettingsGroup);
const bool show_pretty_covers = settings.value("pretty_covers", true).toBool(); const bool show_pretty_covers = settings.value(CollectionSettings::kPrettyCovers, true).toBool();
const bool show_dividers= settings.value("show_dividers", true).toBool(); const bool show_dividers= settings.value(CollectionSettings::kShowDividers, true).toBool();
const bool show_various_artists = settings.value("various_artists", true).toBool(); const bool show_various_artists = settings.value(CollectionSettings::kVariousArtists, true).toBool();
const bool sort_skips_articles = settings.value("sort_skips_articles", true).toBool(); const bool sort_skips_articles = settings.value(CollectionSettings::kSortSkipsArticles, true).toBool();
use_disk_cache_ = settings.value(CollectionSettingsPage::kSettingsDiskCacheEnable, false).toBool(); use_disk_cache_ = settings.value(CollectionSettings::kSettingsDiskCacheEnable, false).toBool();
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettingsPage::kSettingsCacheSize, CollectionSettingsPage::kSettingsCacheSizeUnit, CollectionSettingsPage::kSettingsCacheSizeDefault) / 1024)); QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettings::kSettingsCacheSize, CollectionSettings::kSettingsCacheSizeUnit, CollectionSettings::kSettingsCacheSizeDefault) / 1024));
if (sIconCache) { if (icon_disk_cache_) {
sIconCache->setMaximumCacheSize(MaximumCacheSize(&settings, CollectionSettingsPage::kSettingsDiskCacheSize, CollectionSettingsPage::kSettingsDiskCacheSizeUnit, CollectionSettingsPage::kSettingsDiskCacheSizeDefault)); icon_disk_cache_->setMaximumCacheSize(MaximumCacheSize(&settings, CollectionSettings::kSettingsDiskCacheSize, CollectionSettings::kSettingsDiskCacheSizeUnit, CollectionSettings::kSettingsDiskCacheSizeDefault));
} }
settings.endGroup(); settings.endGroup();
@ -258,7 +253,7 @@ void CollectionModel::ReloadSettings() {
} }
if (!use_disk_cache_) { if (!use_disk_cache_) {
ClearDiskCache(); ClearIconDiskCache();
} }
} }
@ -427,7 +422,7 @@ QMimeData *CollectionModel::mimeData(const QModelIndexList &indexes) const {
} }
data->setUrls(urls); data->setUrls(urls);
data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs); data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
return data; return data;
@ -778,7 +773,7 @@ void CollectionModel::CreateSongItem(const Song &song, CollectionItem *parent) {
} }
void CollectionModel::SetSongItemData(CollectionItem *item, const Song &song) { void CollectionModel::SetSongItemData(CollectionItem *item, const Song &song) const {
item->display_text = song.TitleWithCompilationArtist(); item->display_text = song.TitleWithCompilationArtist();
item->sort_text = HasParentAlbumGroupBy(item->parent) ? SortTextForSong(song) : SortText(song.title()); item->sort_text = HasParentAlbumGroupBy(item->parent) ? SortTextForSong(song) : SortText(song.title());
@ -879,7 +874,7 @@ void CollectionModel::ClearItemPixmapCache(CollectionItem *item) {
// Remove from pixmap cache // Remove from pixmap cache
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(item)); const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(item));
QPixmapCache::remove(cache_key); QPixmapCache::remove(cache_key);
if (use_disk_cache_ && sIconCache) sIconCache->remove(AlbumIconPixmapDiskCacheKey(cache_key)); if (use_disk_cache_ && icon_disk_cache_) icon_disk_cache_->remove(AlbumIconPixmapDiskCacheKey(cache_key));
if (pending_cache_keys_.contains(cache_key)) { if (pending_cache_keys_.contains(cache_key)) {
pending_cache_keys_.remove(cache_key); pending_cache_keys_.remove(cache_key);
} }
@ -910,8 +905,8 @@ QVariant CollectionModel::AlbumIcon(const QModelIndex &idx) {
} }
// Try to load it from the disk cache // Try to load it from the disk cache
if (use_disk_cache_ && sIconCache) { if (use_disk_cache_ && icon_disk_cache_) {
ScopedPtr<QIODevice> disk_cache_img(sIconCache->data(AlbumIconPixmapDiskCacheKey(cache_key))); ScopedPtr<QIODevice> disk_cache_img(icon_disk_cache_->data(AlbumIconPixmapDiskCacheKey(cache_key)));
if (disk_cache_img) { if (disk_cache_img) {
QImage cached_image; QImage cached_image;
if (cached_image.load(&*disk_cache_img, "XPM")) { if (cached_image.load(&*disk_cache_img, "XPM")) {
@ -932,7 +927,7 @@ QVariant CollectionModel::AlbumIcon(const QModelIndex &idx) {
AlbumCoverLoaderOptions cover_loader_options(AlbumCoverLoaderOptions::Option::ScaledImage | AlbumCoverLoaderOptions::Option::PadScaledImage); AlbumCoverLoaderOptions cover_loader_options(AlbumCoverLoaderOptions::Option::ScaledImage | AlbumCoverLoaderOptions::Option::PadScaledImage);
cover_loader_options.desired_scaled_size = QSize(kPrettyCoverSize, kPrettyCoverSize); cover_loader_options.desired_scaled_size = QSize(kPrettyCoverSize, kPrettyCoverSize);
cover_loader_options.types = cover_types_; cover_loader_options.types = cover_types_;
const quint64 id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options, songs.first()); const quint64 id = albumcover_loader_->LoadImageAsync(cover_loader_options, songs.first());
pending_art_[id] = ItemAndCacheKey(item, cache_key); pending_art_[id] = ItemAndCacheKey(item, cache_key);
pending_cache_keys_.insert(cache_key); pending_cache_keys_.insert(cache_key);
} }
@ -965,19 +960,19 @@ void CollectionModel::AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderR
} }
// If we have a valid cover not already in the disk cache // If we have a valid cover not already in the disk cache
if (use_disk_cache_ && sIconCache && result.success && !result.image_scaled.isNull()) { if (use_disk_cache_ && icon_disk_cache_ && result.success && !result.image_scaled.isNull()) {
const QUrl disk_cache_key = AlbumIconPixmapDiskCacheKey(cache_key); const QUrl disk_cache_key = AlbumIconPixmapDiskCacheKey(cache_key);
ScopedPtr<QIODevice> disk_cache_img(sIconCache->data(disk_cache_key)); ScopedPtr<QIODevice> disk_cache_img(icon_disk_cache_->data(disk_cache_key));
if (!disk_cache_img) { if (!disk_cache_img) {
QNetworkCacheMetaData disk_cache_metadata; QNetworkCacheMetaData disk_cache_metadata;
disk_cache_metadata.setSaveToDisk(true); disk_cache_metadata.setSaveToDisk(true);
disk_cache_metadata.setUrl(disk_cache_key); disk_cache_metadata.setUrl(disk_cache_key);
// Qt 6 now ignores any entry without headers, so add a fake header. // Qt 6 now ignores any entry without headers, so add a fake header.
disk_cache_metadata.setRawHeaders(QNetworkCacheMetaData::RawHeaderList() << qMakePair(QByteArray("collection-thumbnail"), cache_key.toUtf8())); disk_cache_metadata.setRawHeaders(QNetworkCacheMetaData::RawHeaderList() << qMakePair(QByteArray("collection-thumbnail"), cache_key.toUtf8()));
QIODevice *device_iconcache = sIconCache->prepare(disk_cache_metadata); QIODevice *device_iconcache = icon_disk_cache_->prepare(disk_cache_metadata);
if (device_iconcache) { if (device_iconcache) {
result.image_scaled.save(device_iconcache, "XPM"); result.image_scaled.save(device_iconcache, "XPM");
sIconCache->insert(device_iconcache); icon_disk_cache_->insert(device_iconcache);
} }
} }
} }
@ -1450,7 +1445,7 @@ bool CollectionModel::HasParentAlbumGroupBy(CollectionItem *item) const {
qint64 CollectionModel::MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default) { qint64 CollectionModel::MaximumCacheSize(Settings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default) {
qint64 size = s->value(size_id, cache_size_default).toInt(); qint64 size = s->value(size_id, cache_size_default).toInt();
int unit = s->value(size_unit_id, static_cast<int>(CollectionSettingsPage::CacheSizeUnit::MB)).toInt() + 1; int unit = s->value(size_unit_id, static_cast<int>(CollectionSettings::CacheSizeUnit::MB)).toInt() + 1;
do { do {
size *= 1024; size *= 1024;
@ -1553,8 +1548,11 @@ void CollectionModel::TotalAlbumCountUpdatedSlot(const int count) {
} }
void CollectionModel::ClearDiskCache() { void CollectionModel::ClearIconDiskCache() {
if (sIconCache) sIconCache->clear();
if (icon_disk_cache_) icon_disk_cache_->clear();
QPixmapCache::clear();
} }
void CollectionModel::RowsInserted(const QModelIndex &parent, const int first, const int last) { void CollectionModel::RowsInserted(const QModelIndex &parent, const int first, const int last) {

View File

@ -44,10 +44,9 @@
#include <QNetworkDiskCache> #include <QNetworkDiskCache>
#include <QQueue> #include <QQueue>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/simpletreemodel.h" #include "core/simpletreemodel.h"
#include "core/song.h" #include "core/song.h"
#include "core/sqlrow.h"
#include "covermanager/albumcoverloaderoptions.h" #include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h" #include "covermanager/albumcoverloaderresult.h"
#include "collectionmodelupdate.h" #include "collectionmodelupdate.h"
@ -57,16 +56,16 @@
class QTimer; class QTimer;
class Settings; class Settings;
class Application;
class CollectionBackend; class CollectionBackend;
class CollectionDirectoryModel; class CollectionDirectoryModel;
class CollectionFilter; class CollectionFilter;
class AlbumCoverLoader;
class CollectionModel : public SimpleTreeModel<CollectionItem> { class CollectionModel : public SimpleTreeModel<CollectionItem> {
Q_OBJECT Q_OBJECT
public: public:
explicit CollectionModel(SharedPtr<CollectionBackend> backend, Application *app, QObject *parent = nullptr); explicit CollectionModel(const SharedPtr<CollectionBackend> backend, const SharedPtr<AlbumCoverLoader> albumcover_loader, QObject *parent = nullptr);
~CollectionModel() override; ~CollectionModel() override;
static const int kPrettyCoverSize; static const int kPrettyCoverSize;
@ -156,7 +155,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
int total_artist_count() const { return total_artist_count_; } int total_artist_count() const { return total_artist_count_; }
int total_album_count() const { return total_album_count_; } int total_album_count() const { return total_album_count_; }
quint64 icon_cache_disk_size() { return sIconCache->cacheSize(); } quint64 icon_disk_cache_size() { return icon_disk_cache_->cacheSize(); }
const CollectionModel::Grouping GetGroupBy() const { return options_current_.group_by; } const CollectionModel::Grouping GetGroupBy() const { return options_current_.group_by; }
void SetGroupBy(const CollectionModel::Grouping g, const std::optional<bool> separate_albums_by_grouping = std::optional<bool>()); void SetGroupBy(const CollectionModel::Grouping g, const std::optional<bool> separate_albums_by_grouping = std::optional<bool>());
@ -218,6 +217,8 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
void AddReAddOrUpdate(const SongList &songs); void AddReAddOrUpdate(const SongList &songs);
void RemoveSongs(const SongList &songs); void RemoveSongs(const SongList &songs);
void ClearIconDiskCache();
private: private:
void Clear(); void Clear();
void BeginReset(); void BeginReset();
@ -238,7 +239,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
void CreateDividerItem(const QString &divider_key, const QString &display_text, CollectionItem *parent); void CreateDividerItem(const QString &divider_key, const QString &display_text, CollectionItem *parent);
CollectionItem *CreateContainerItem(const GroupBy group_by, const int container_level, const QString &container_key, const Song &song, CollectionItem *parent); CollectionItem *CreateContainerItem(const GroupBy group_by, const int container_level, const QString &container_key, const Song &song, CollectionItem *parent);
void CreateSongItem(const Song &song, CollectionItem *parent); void CreateSongItem(const Song &song, CollectionItem *parent);
void SetSongItemData(CollectionItem *item, const Song &song); void SetSongItemData(CollectionItem *item, const Song &song) const;
CollectionItem *CreateCompilationArtistNode(CollectionItem *parent); CollectionItem *CreateCompilationArtistNode(CollectionItem *parent);
void LoadSongsFromSqlAsync(); void LoadSongsFromSqlAsync();
@ -267,15 +268,12 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
void TotalArtistCountUpdatedSlot(const int count); void TotalArtistCountUpdatedSlot(const int count);
void TotalAlbumCountUpdatedSlot(const int count); void TotalAlbumCountUpdatedSlot(const int count);
static void ClearDiskCache();
void RowsInserted(const QModelIndex &parent, const int first, const int last); void RowsInserted(const QModelIndex &parent, const int first, const int last);
void RowsRemoved(const QModelIndex &parent, const int first, const int last); void RowsRemoved(const QModelIndex &parent, const int first, const int last);
private: private:
static QNetworkDiskCache *sIconCache; const SharedPtr<CollectionBackend> backend_;
SharedPtr<CollectionBackend> backend_; const SharedPtr<AlbumCoverLoader> albumcover_loader_;
Application *app_;
CollectionDirectoryModel *dir_model_; CollectionDirectoryModel *dir_model_;
CollectionFilter *filter_; CollectionFilter *filter_;
QTimer *timer_reload_; QTimer *timer_reload_;
@ -310,6 +308,8 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
using ItemAndCacheKey = QPair<CollectionItem*, QString>; using ItemAndCacheKey = QPair<CollectionItem*, QString>;
QMap<quint64, ItemAndCacheKey> pending_art_; QMap<quint64, ItemAndCacheKey> pending_art_;
QSet<QString> pending_cache_keys_; QSet<QString> pending_cache_keys_;
QNetworkDiskCache *icon_disk_cache_;
}; };
Q_DECLARE_METATYPE(CollectionModel::Grouping) Q_DECLARE_METATYPE(CollectionModel::Grouping)

View File

@ -19,7 +19,7 @@
#include <QString> #include <QString>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/taskmanager.h" #include "core/taskmanager.h"
#include "collectiontask.h" #include "collectiontask.h"

View File

@ -23,7 +23,7 @@
#include <QtGlobal> #include <QtGlobal>
#include <QString> #include <QString>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
class TaskManager; class TaskManager;

View File

@ -50,20 +50,20 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include "core/application.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "core/mimedata.h" #include "core/mimedata.h"
#include "core/musicstorage.h" #include "core/musicstorage.h"
#include "core/deletefiles.h" #include "core/deletefiles.h"
#include "core/settings.h" #include "core/settings.h"
#include "utilities/filemanagerutils.h" #include "utilities/filemanagerutils.h"
#include "collection.h" #include "collectionlibrary.h"
#include "collectionbackend.h" #include "collectionbackend.h"
#include "collectiondirectorymodel.h" #include "collectiondirectorymodel.h"
#include "collectionmodel.h"
#include "collectionfilter.h"
#include "collectionfilterwidget.h" #include "collectionfilterwidget.h"
#include "collectionitem.h" #include "collectionitem.h"
#include "collectionitemdelegate.h" #include "collectionitemdelegate.h"
#include "collectionmodel.h"
#include "collectionview.h" #include "collectionview.h"
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
# include "device/devicemanager.h" # include "device/devicemanager.h"
@ -73,15 +73,16 @@
#include "dialogs/deleteconfirmationdialog.h" #include "dialogs/deleteconfirmationdialog.h"
#include "organize/organizedialog.h" #include "organize/organizedialog.h"
#include "organize/organizeerrordialog.h" #include "organize/organizeerrordialog.h"
#include "settings/collectionsettingspage.h" #include "constants/collectionsettings.h"
using std::make_unique; using std::make_unique;
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
CollectionView::CollectionView(QWidget *parent) CollectionView::CollectionView(QWidget *parent)
: AutoExpandingTreeView(parent), : AutoExpandingTreeView(parent),
app_(nullptr), model_(nullptr),
filter_(nullptr), filter_(nullptr),
filter_widget_(nullptr),
total_song_count_(-1), total_song_count_(-1),
total_artist_count_(-1), total_artist_count_(-1),
total_album_count_(-1), total_album_count_(-1),
@ -123,6 +124,35 @@ CollectionView::CollectionView(QWidget *parent)
CollectionView::~CollectionView() = default; CollectionView::~CollectionView() = default;
void CollectionView::Init(const SharedPtr<TaskManager> task_manager,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<NetworkAccessManager> network,
const SharedPtr<AlbumCoverLoader> albumcover_loader,
const SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader,
const SharedPtr<CoverProviders> cover_providers,
const SharedPtr<LyricsProviders> lyrics_providers,
const SharedPtr<CollectionLibrary> collection,
const SharedPtr<DeviceManager> device_manager,
const SharedPtr<StreamingServices> streaming_services) {
task_manager_ = task_manager;
tagreader_client_ = tagreader_client;
network_ = network;
albumcover_loader_ = albumcover_loader;
current_albumcover_loader_ = current_albumcover_loader;
cover_providers_ = cover_providers;
lyrics_providers_ = lyrics_providers;
collection_ = collection;
device_manager_ = device_manager;
streaming_services_ = streaming_services;
backend_ = collection_->backend();
model_ = collection_->model();
filter_ = collection_->model()->filter();
ReloadSettings();
}
void CollectionView::SaveFocus() { void CollectionView::SaveFocus() {
const QModelIndex current = currentIndex(); const QModelIndex current = currentIndex();
@ -142,8 +172,8 @@ void CollectionView::SaveFocus() {
switch (item_type) { switch (item_type) {
case CollectionItem::Type::Song:{ case CollectionItem::Type::Song:{
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current); QModelIndex index = filter_->mapToSource(current);
SongList songs = app_->collection_model()->GetChildSongs(index); SongList songs = model_->GetChildSongs(index);
if (!songs.isEmpty()) { if (!songs.isEmpty()) {
last_selected_song_ = songs.last(); last_selected_song_ = songs.last();
} }
@ -210,8 +240,8 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
break; break;
case CollectionItem::Type::Song: case CollectionItem::Type::Song:
if (!last_selected_song_.url().isEmpty()) { if (!last_selected_song_.url().isEmpty()) {
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current); QModelIndex index = filter_->mapToSource(current);
const SongList songs = app_->collection_model()->GetChildSongs(index); const SongList songs = model_->GetChildSongs(index);
if (std::any_of(songs.begin(), songs.end(), [this](const Song &song) { return song == last_selected_song_; })) { if (std::any_of(songs.begin(), songs.end(), [this](const Song &song) { return song == last_selected_song_; })) {
setCurrentIndex(current); setCurrentIndex(current);
return true; return true;
@ -241,6 +271,7 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
} }
} }
} }
return false; return false;
} }
@ -248,22 +279,14 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
void CollectionView::ReloadSettings() { void CollectionView::ReloadSettings() {
Settings settings; Settings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup); settings.beginGroup(CollectionSettings::kSettingsGroup);
SetAutoOpen(settings.value("auto_open", false).toBool()); SetAutoOpen(settings.value(CollectionSettings::kAutoOpen, false).toBool());
delete_files_ = settings.value("delete_files", false).toBool(); delete_files_ = settings.value(CollectionSettings::kDeleteFiles, false).toBool();
settings.endGroup(); settings.endGroup();
} }
void CollectionView::SetApplication(Application *app) { void CollectionView::SetFilterWidget(CollectionFilterWidget *filter_widget) { filter_widget_ = filter_widget; }
app_ = app;
ReloadSettings();
}
void CollectionView::SetFilter(CollectionFilterWidget *filter) { filter_ = filter; }
void CollectionView::TotalSongCountUpdated(const int count) { void CollectionView::TotalSongCountUpdated(const int count) {
@ -353,7 +376,7 @@ void CollectionView::mouseReleaseEvent(QMouseEvent *e) {
QTreeView::mouseReleaseEvent(e); QTreeView::mouseReleaseEvent(e);
if (total_song_count_ == 0) { if (total_song_count_ == 0) {
Q_EMIT ShowConfigDialog(); Q_EMIT ShowSettingsDialog();
} }
} }
@ -414,11 +437,11 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_->addSeparator(); context_menu_->addSeparator();
context_menu_->addMenu(filter_->menu()); context_menu_->addMenu(filter_widget_->menu());
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
action_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0); action_copy_to_device_->setDisabled(device_manager_->connected_devices_model()->rowCount() == 0);
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled); QObject::connect(device_manager_->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
#endif #endif
} }
@ -426,16 +449,16 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_index_ = indexAt(e->pos()); context_menu_index_ = indexAt(e->pos());
if (!context_menu_index_.isValid()) return; if (!context_menu_index_.isValid()) return;
context_menu_index_ = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(context_menu_index_); context_menu_index_ = filter_->mapToSource(context_menu_index_);
const QModelIndexList selected_indexes = qobject_cast<QSortFilterProxyModel*>(model())->mapSelectionToSource(selectionModel()->selection()).indexes(); const QModelIndexList selected_indexes = filter_->mapSelectionToSource(selectionModel()->selection()).indexes();
int regular_elements = 0; int regular_elements = 0;
int regular_editable = 0; int regular_editable = 0;
for (const QModelIndex &idx : selected_indexes) { for (const QModelIndex &idx : selected_indexes) {
++regular_elements; ++regular_elements;
if (app_->collection_model()->data(idx, CollectionModel::Role_Editable).toBool()) { if (model_->data(idx, CollectionModel::Role_Editable).toBool()) {
++regular_editable; ++regular_editable;
} }
} }
@ -502,7 +525,7 @@ void CollectionView::SetShowInVarious(const bool on) {
if (on && albums.keys().count() == 1) { if (on && albums.keys().count() == 1) {
const QStringList albums_list = albums.keys(); const QStringList albums_list = albums.keys();
const QString album = albums_list.first(); const QString album = albums_list.first();
const SongList all_of_album = app_->collection_backend()->GetSongsByAlbum(album); const SongList all_of_album = backend_->GetSongsByAlbum(album);
QSet<QString> other_artists; QSet<QString> other_artists;
for (const Song &s : all_of_album) { for (const Song &s : all_of_album) {
if (!albums.contains(album, s.artist()) && !other_artists.contains(s.artist())) { if (!albums.contains(album, s.artist()) && !other_artists.contains(s.artist())) {
@ -520,7 +543,7 @@ void CollectionView::SetShowInVarious(const bool on) {
const QSet<QString> albums_set = QSet<QString>(albums.keyBegin(), albums.keyEnd()); const QSet<QString> albums_set = QSet<QString>(albums.keyBegin(), albums.keyEnd());
for (const QString &album : albums_set) { for (const QString &album : albums_set) {
app_->collection_backend()->ForceCompilation(album, albums.values(album), on); backend_->ForceCompilation(album, albums.values(album), on);
} }
} }
@ -584,11 +607,12 @@ void CollectionView::SearchForThis() {
return; return;
} }
QString search; QString search;
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
QModelIndex index = filter_->mapToSource(current);
switch (item_type) { switch (item_type) {
case CollectionItem::Type::Song:{ case CollectionItem::Type::Song:{
SongList songs = app_->collection_model()->GetChildSongs(index); SongList songs = model_->GetChildSongs(index);
if (!songs.isEmpty()) { if (!songs.isEmpty()) {
last_selected_song_ = songs.last(); last_selected_song_ = songs.last();
} }
@ -601,8 +625,8 @@ void CollectionView::SearchForThis() {
} }
case CollectionItem::Type::Container:{ case CollectionItem::Type::Container:{
CollectionItem *item = app_->collection_model()->IndexToItem(index); CollectionItem *item = model_->IndexToItem(index);
const CollectionModel::GroupBy group_by = app_->collection_model()->GetGroupBy()[item->container_level]; const CollectionModel::GroupBy group_by = model_->GetGroupBy()[item->container_level];
while (!item->children.isEmpty()) { while (!item->children.isEmpty()) {
item = item->children.constFirst(); item = item->children.constFirst();
} }
@ -663,7 +687,7 @@ void CollectionView::SearchForThis() {
return; return;
} }
filter_->ShowInCollection(search); filter_widget_->ShowInCollection(search);
} }
@ -688,18 +712,18 @@ void CollectionView::scrollTo(const QModelIndex &idx, ScrollHint hint) {
SongList CollectionView::GetSelectedSongs() const { SongList CollectionView::GetSelectedSongs() const {
QModelIndexList selected_indexes = qobject_cast<QSortFilterProxyModel*>(model())->mapSelectionToSource(selectionModel()->selection()).indexes(); QModelIndexList selected_indexes = filter_->mapSelectionToSource(selectionModel()->selection()).indexes();
return app_->collection_model()->GetChildSongs(selected_indexes); return model_->GetChildSongs(selected_indexes);
} }
void CollectionView::Organize() { void CollectionView::Organize() {
if (!organize_dialog_) { if (!organize_dialog_) {
organize_dialog_ = make_unique<OrganizeDialog>(app_->task_manager(), app_->collection_backend(), this); organize_dialog_ = make_unique<OrganizeDialog>(task_manager_, tagreader_client_, backend_, this);
} }
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model()); organize_dialog_->SetDestinationModel(model_->directory_model());
organize_dialog_->SetCopy(false); organize_dialog_->SetCopy(false);
const SongList songs = GetSelectedSongs(); const SongList songs = GetSelectedSongs();
if (organize_dialog_->SetSongs(songs)) { if (organize_dialog_->SetSongs(songs)) {
@ -714,7 +738,7 @@ void CollectionView::Organize() {
void CollectionView::EditTracks() { void CollectionView::EditTracks() {
if (!edit_tag_dialog_) { if (!edit_tag_dialog_) {
edit_tag_dialog_ = make_unique<EditTagDialog>(app_, this); edit_tag_dialog_ = make_unique<EditTagDialog>(network_, tagreader_client_, backend_, albumcover_loader_, current_albumcover_loader_, cover_providers_, lyrics_providers_, streaming_services_, this);
QObject::connect(&*edit_tag_dialog_, &EditTagDialog::Error, this, &CollectionView::EditTagError); QObject::connect(&*edit_tag_dialog_, &EditTagDialog::Error, this, &CollectionView::EditTagError);
} }
const SongList songs = GetSelectedSongs(); const SongList songs = GetSelectedSongs();
@ -729,7 +753,7 @@ void CollectionView::EditTagError(const QString &message) {
void CollectionView::RescanSongs() { void CollectionView::RescanSongs() {
app_->collection()->Rescan(GetSelectedSongs()); collection_->Rescan(GetSelectedSongs());
} }
@ -737,10 +761,10 @@ void CollectionView::CopyToDevice() {
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
if (!organize_dialog_) { if (!organize_dialog_) {
organize_dialog_ = make_unique<OrganizeDialog>(app_->task_manager(), nullptr, this); organize_dialog_ = make_unique<OrganizeDialog>(task_manager_, tagreader_client_, nullptr, this);
} }
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true); organize_dialog_->SetDestinationModel(device_manager_->connected_devices_model(), true);
organize_dialog_->SetCopy(true); organize_dialog_->SetCopy(true);
organize_dialog_->SetSongs(GetSelectedSongs()); organize_dialog_->SetSongs(GetSelectedSongs());
organize_dialog_->show(); organize_dialog_->show();
@ -812,9 +836,9 @@ void CollectionView::Delete() {
if (DeleteConfirmationDialog::warning(files) != QDialogButtonBox::Yes) return; if (DeleteConfirmationDialog::warning(files) != QDialogButtonBox::Yes) return;
// We can cheat and always take the storage of the first directory, since they'll all be FilesystemMusicStorage in a collection and deleting doesn't check the actual directory. // We can cheat and always take the storage of the first directory, since they'll all be FilesystemMusicStorage in a collection and deleting doesn't check the actual directory.
SharedPtr<MusicStorage> storage = app_->collection_model()->directory_model()->index(0, 0).data(MusicStorage::Role_Storage).value<SharedPtr<MusicStorage>>(); SharedPtr<MusicStorage> storage = model_->directory_model()->index(0, 0).data(MusicStorage::Role_Storage).value<SharedPtr<MusicStorage>>();
DeleteFiles *delete_files = new DeleteFiles(app_->task_manager(), storage, true); DeleteFiles *delete_files = new DeleteFiles(task_manager_, storage, true);
QObject::connect(delete_files, &DeleteFiles::Finished, this, &CollectionView::DeleteFilesFinished); QObject::connect(delete_files, &DeleteFiles::Finished, this, &CollectionView::DeleteFilesFinished);
delete_files->Start(songs); delete_files->Start(songs);

View File

@ -30,10 +30,12 @@
#include <QPixmap> #include <QPixmap>
#include <QSet> #include <QSet>
#include "core/scoped_ptr.h" #include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "core/song.h" #include "core/song.h"
#include "widgets/autoexpandingtreeview.h" #include "widgets/autoexpandingtreeview.h"
class QSortFilterProxyModel;
class QMenu; class QMenu;
class QAction; class QAction;
class QContextMenuEvent; class QContextMenuEvent;
@ -41,8 +43,20 @@ class QMouseEvent;
class QPaintEvent; class QPaintEvent;
class QKeyEvent; class QKeyEvent;
class Application; class TaskManager;
class TagReaderClient;
class NetworkAccessManager;
class CollectionLibrary;
class CollectionBackend;
class CollectionModel;
class CollectionFilter;
class CollectionFilterWidget; class CollectionFilterWidget;
class DeviceManager;
class StreamingServices;
class AlbumCoverLoader;
class CurrentAlbumCoverLoader;
class CoverProviders;
class LyricsProviders;
class EditTagDialog; class EditTagDialog;
class OrganizeDialog; class OrganizeDialog;
@ -57,8 +71,18 @@ class CollectionView : public AutoExpandingTreeView {
// Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs. // Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs.
SongList GetSelectedSongs() const; SongList GetSelectedSongs() const;
void SetApplication(Application *app); void Init(const SharedPtr<TaskManager> task_manager,
void SetFilter(CollectionFilterWidget *filter); const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<NetworkAccessManager> network,
const SharedPtr<AlbumCoverLoader> albumcover_loader,
const SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader,
const SharedPtr<CoverProviders> cover_providers,
const SharedPtr<LyricsProviders> lyrics_providers,
const SharedPtr<CollectionLibrary> collection,
const SharedPtr<DeviceManager> device_manager,
const SharedPtr<StreamingServices> streaming_services);
void SetFilterWidget(CollectionFilterWidget *filter_widget);
// QTreeView // QTreeView
void keyboardSearch(const QString &search) override; void keyboardSearch(const QString &search) override;
@ -82,7 +106,7 @@ class CollectionView : public AutoExpandingTreeView {
void EditTagError(const QString &message); void EditTagError(const QString &message);
Q_SIGNALS: Q_SIGNALS:
void ShowConfigDialog(); void ShowSettingsDialog();
void TotalSongCountUpdated_(); void TotalSongCountUpdated_();
void TotalArtistCountUpdated_(); void TotalArtistCountUpdated_();
@ -120,8 +144,21 @@ class CollectionView : public AutoExpandingTreeView {
void SaveContainerPath(const QModelIndex &child); void SaveContainerPath(const QModelIndex &child);
private: private:
Application *app_; SharedPtr<TaskManager> task_manager_;
CollectionFilterWidget *filter_; SharedPtr<TagReaderClient> tagreader_client_;
SharedPtr<NetworkAccessManager> network_;
SharedPtr<DeviceManager> device_manager_;
SharedPtr<AlbumCoverLoader> albumcover_loader_;
SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader_;
SharedPtr<CollectionLibrary> collection_;
SharedPtr<CoverProviders> cover_providers_;
SharedPtr<LyricsProviders> lyrics_providers_;
SharedPtr<StreamingServices> streaming_services_;
SharedPtr<CollectionBackend> backend_;
CollectionModel *model_;
CollectionFilter *filter_;
CollectionFilterWidget *filter_widget_;
int total_song_count_; int total_song_count_;
int total_artist_count_; int total_artist_count_;

View File

@ -31,7 +31,7 @@
CollectionViewContainer::CollectionViewContainer(QWidget *parent) : QWidget(parent), ui_(new Ui_CollectionViewContainer) { CollectionViewContainer::CollectionViewContainer(QWidget *parent) : QWidget(parent), ui_(new Ui_CollectionViewContainer) {
ui_->setupUi(this); ui_->setupUi(this);
view()->SetFilter(filter_widget()); view()->SetFilterWidget(filter_widget());
QObject::connect(filter_widget(), &CollectionFilterWidget::UpPressed, view(), &CollectionView::UpAndFocus); QObject::connect(filter_widget(), &CollectionFilterWidget::UpPressed, view(), &CollectionView::UpAndFocus);
QObject::connect(filter_widget(), &CollectionFilterWidget::DownPressed, view(), &CollectionView::DownAndFocus); QObject::connect(filter_widget(), &CollectionFilterWidget::DownPressed, view(), &CollectionView::DownAndFocus);

View File

@ -50,13 +50,13 @@
#include "core/taskmanager.h" #include "core/taskmanager.h"
#include "core/settings.h" #include "core/settings.h"
#include "utilities/imageutils.h" #include "utilities/imageutils.h"
#include "utilities/timeconstants.h" #include "constants/timeconstants.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "collectiondirectory.h" #include "collectiondirectory.h"
#include "collectionbackend.h" #include "collectionbackend.h"
#include "collectionwatcher.h" #include "collectionwatcher.h"
#include "playlistparsers/cueparser.h" #include "playlistparsers/cueparser.h"
#include "settings/collectionsettingspage.h" #include "constants/collectionsettings.h"
#include "engine/ebur128measures.h" #include "engine/ebur128measures.h"
#ifdef HAVE_SONGFINGERPRINTING #ifdef HAVE_SONGFINGERPRINTING
# include "engine/chromaprinter.h" # include "engine/chromaprinter.h"
@ -75,11 +75,16 @@ using namespace Qt::Literals::StringLiterals;
QStringList CollectionWatcher::sValidImages = QStringList() << u"jpg"_s << u"png"_s << u"gif"_s << u"jpeg"_s; QStringList CollectionWatcher::sValidImages = QStringList() << u"jpg"_s << u"png"_s << u"gif"_s << u"jpeg"_s;
CollectionWatcher::CollectionWatcher(const Song::Source source, QObject *parent) CollectionWatcher::CollectionWatcher(const Song::Source source,
const SharedPtr<TaskManager> task_manager,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<CollectionBackend> backend,
QObject *parent)
: QObject(parent), : QObject(parent),
source_(source), source_(source),
backend_(nullptr), task_manager_(task_manager),
task_manager_(nullptr), tagreader_client_(tagreader_client),
backend_(backend),
fs_watcher_(FileSystemWatcherInterface::Create(this)), fs_watcher_(FileSystemWatcherInterface::Create(this)),
original_thread_(nullptr), original_thread_(nullptr),
scan_on_startup_(true), scan_on_startup_(true),
@ -96,7 +101,7 @@ CollectionWatcher::CollectionWatcher(const Song::Source source, QObject *parent)
periodic_scan_timer_(new QTimer(this)), periodic_scan_timer_(new QTimer(this)),
rescan_paused_(false), rescan_paused_(false),
total_watches_(0), total_watches_(0),
cue_parser_(new CueParser(backend_, this)), cue_parser_(new CueParser(tagreader_client, backend, this)),
last_scan_time_(0) { last_scan_time_(0) {
setObjectName(source_ == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(source_), QLatin1String(metaObject()->className()))); setObjectName(source_ == Song::Source::Collection ? QLatin1String(metaObject()->className()) : QStringLiteral("%1%2").arg(Song::DescriptionForSource(source_), QLatin1String(metaObject()->className())));
@ -196,29 +201,29 @@ void CollectionWatcher::ReloadSettings() {
const bool was_monitoring_before = monitor_; const bool was_monitoring_before = monitor_;
Settings s; Settings s;
s.beginGroup(CollectionSettingsPage::kSettingsGroup); s.beginGroup(CollectionSettings::kSettingsGroup);
if (source_ == Song::Source::Collection) { if (source_ == Song::Source::Collection) {
scan_on_startup_ = s.value("startup_scan", true).toBool(); scan_on_startup_ = s.value(CollectionSettings::kStartupScan, true).toBool();
monitor_ = s.value("monitor", true).toBool(); monitor_ = s.value(CollectionSettings::kMonitor, true).toBool();
} }
else { else {
scan_on_startup_ = true; scan_on_startup_ = true;
monitor_ = true; monitor_ = true;
} }
const QStringList filters = s.value("cover_art_patterns", QStringList() << u"front"_s << u"cover"_s).toStringList(); const QStringList filters = s.value(CollectionSettings::kCoverArtPatterns, QStringList() << u"front"_s << u"cover"_s).toStringList();
if (source_ == Song::Source::Collection) { if (source_ == Song::Source::Collection) {
song_tracking_ = s.value("song_tracking", false).toBool(); song_tracking_ = s.value(CollectionSettings::kSongTracking, false).toBool();
song_ebur128_loudness_analysis_ = s.value("song_ebur128_loudness_analysis", false).toBool(); song_ebur128_loudness_analysis_ = s.value(CollectionSettings::kSongENUR128LoudnessAnalysis, false).toBool();
mark_songs_unavailable_ = song_tracking_ ? true : s.value("mark_songs_unavailable", true).toBool(); mark_songs_unavailable_ = song_tracking_ ? true : s.value(CollectionSettings::kMarkSongsUnavailable, true).toBool();
} }
else { else {
song_tracking_ = false; song_tracking_ = false;
song_ebur128_loudness_analysis_ = false; song_ebur128_loudness_analysis_ = false;
mark_songs_unavailable_ = false; mark_songs_unavailable_ = false;
} }
expire_unavailable_songs_days_ = s.value("expire_unavailable_songs", 60).toInt(); expire_unavailable_songs_days_ = s.value(CollectionSettings::kExpireUnavailableSongs, 60).toInt();
overwrite_playcount_ = s.value("overwrite_playcount", false).toBool(); overwrite_playcount_ = s.value(CollectionSettings::kOverwritePlaycount, false).toBool();
overwrite_rating_ = s.value("overwrite_rating", false).toBool(); overwrite_rating_ = s.value(CollectionSettings::kOverwriteRating, false).toBool();
s.endGroup(); s.endGroup();
best_art_filters_.clear(); best_art_filters_.clear();
@ -575,7 +580,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
album_art[dir_part] << child; album_art[dir_part] << child;
t->AddToProgress(1); t->AddToProgress(1);
} }
else if (TagReaderClient::Instance()->IsMediaFileBlocking(child)) { else if (tagreader_client_->IsMediaFileBlocking(child)) {
files_on_disk << child; files_on_disk << child;
} }
else { else {
@ -874,7 +879,7 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file,
} }
Song song_on_disk(source_); Song song_on_disk(source_);
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk); const TagReaderResult result = tagreader_client_->ReadFileBlocking(file, &song_on_disk);
if (result.success() && song_on_disk.is_valid()) { if (result.success() && song_on_disk.is_valid()) {
song_on_disk.set_source(source_); song_on_disk.set_source(source_);
song_on_disk.set_directory_id(t->dir()); song_on_disk.set_directory_id(t->dir());
@ -927,7 +932,7 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
} }
else { // It's a normal media file else { // It's a normal media file
Song song(source_); Song song(source_);
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(file, &song); const TagReaderResult result = tagreader_client_->ReadFileBlocking(file, &song);
if (result.success() && song.is_valid()) { if (result.success() && song.is_valid()) {
song.set_source(source_); song.set_source(source_);
PerformEBUR128Analysis(song); PerformEBUR128Analysis(song);

View File

@ -36,28 +36,32 @@
#include <QMutex> #include <QMutex>
#include "collectiondirectory.h" #include "collectiondirectory.h"
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/song.h" #include "core/song.h"
class QThread; class QThread;
class QTimer; class QTimer;
class TaskManager;
class TagReaderClient;
class CollectionBackend; class CollectionBackend;
class FileSystemWatcherInterface; class FileSystemWatcherInterface;
class TaskManager;
class CueParser; class CueParser;
class CollectionWatcher : public QObject { class CollectionWatcher : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit CollectionWatcher(const Song::Source source, QObject *parent = nullptr); explicit CollectionWatcher(const Song::Source source,
const SharedPtr<TaskManager> task_manager,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<CollectionBackend> backend,
QObject *parent = nullptr);
~CollectionWatcher(); ~CollectionWatcher();
Song::Source source() { return source_; } Song::Source source() const { return source_; }
void set_backend(SharedPtr<CollectionBackend> backend) { backend_ = backend; }
void set_task_manager(SharedPtr<TaskManager> task_manager) { task_manager_ = task_manager; }
void set_device_name(const QString &device_name) { device_name_ = device_name; } void set_device_name(const QString &device_name) { device_name_ = device_name; }
void IncrementalScanAsync(); void IncrementalScanAsync();
@ -213,9 +217,12 @@ class CollectionWatcher : public QObject {
QString FindCueFilename(const QString &filename); QString FindCueFilename(const QString &filename);
private: private:
Song::Source source_; const Song::Source source_;
SharedPtr<CollectionBackend> backend_;
SharedPtr<TaskManager> task_manager_; const SharedPtr<TaskManager> task_manager_;
const SharedPtr<TagReaderClient> tagreader_client_;
const SharedPtr<CollectionBackend> backend_;
QString device_name_; QString device_name_;
FileSystemWatcherInterface *fs_watcher_; FileSystemWatcherInterface *fs_watcher_;

View File

@ -28,13 +28,13 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include "core/scoped_ptr.h" #include "includes/scoped_ptr.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "ui_groupbydialog.h"
class QWidget; class QWidget;
class GroupByDialogPrivate; class GroupByDialogPrivate;
class Ui_GroupByDialog;
class GroupByDialog : public QDialog { class GroupByDialog : public QDialog {
Q_OBJECT Q_OBJECT

View File

@ -39,7 +39,7 @@
#include "core/logging.h" #include "core/logging.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "core/settings.h" #include "core/settings.h"
#include "settings/collectionsettingspage.h" #include "constants/collectionsettings.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "savedgroupingmanager.h" #include "savedgroupingmanager.h"
#include "ui_savedgroupingmanager.h" #include "ui_savedgroupingmanager.h"
@ -77,7 +77,7 @@ SavedGroupingManager::~SavedGroupingManager() {
QString SavedGroupingManager::GetSavedGroupingsSettingsGroup(const QString &settings_group) { QString SavedGroupingManager::GetSavedGroupingsSettingsGroup(const QString &settings_group) {
if (settings_group.isEmpty() || settings_group == QLatin1String(CollectionSettingsPage::kSettingsGroup)) { if (settings_group.isEmpty() || settings_group == QLatin1String(CollectionSettings::kSettingsGroup)) {
return QLatin1String(kSavedGroupingsSettingsGroup); return QLatin1String(kSavedGroupingsSettingsGroup);
} }

View File

@ -0,0 +1,75 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef APPEARANCESETTINGS_H
#define APPEARANCESETTINGS_H
namespace AppearanceSettings {
constexpr char kSettingsGroup[] = "Appearance";
constexpr char kStyle[] = "style";
constexpr char kSystemThemeIcons[] = "system_icons";
constexpr char kBackgroundImageType[] = "background_image_type";
constexpr char kBackgroundImageFilename[] = "background_image_file";
constexpr char kBackgroundImagePosition[] = "background_image_position";
constexpr char kBackgroundImageStretch[] = "background_image_stretch";
constexpr char kBackgroundImageDoNotCut[] = "background_image_do_not_cut";
constexpr char kBackgroundImageKeepAspectRatio[] = "background_image_keep_aspect_ratio";
constexpr char kBackgroundImageMaxSize[] = "background_image_max_size";
constexpr char kBlurRadius[] = "blur_radius";
constexpr char kOpacityLevel[] = "opacity_level";
constexpr int kDefaultBlurRadius = 0;
constexpr int kDefaultOpacityLevel = 40;
constexpr char kTabBarSystemColor[] = "tab_system_color";
constexpr char kTabBarGradient[] = "tab_gradient";
constexpr char kTabBarColor[] = "tab_color";
constexpr char kIconSizeTabbarSmallMode[] = "icon_size_tabbar_small_mode";
constexpr char kIconSizeTabbarLargeMode[] = "icon_size_tabbar_large_mode";
constexpr char kIconSizePlayControlButtons[] = "icon_size_play_control_buttons";
constexpr char kIconSizePlaylistButtons[] = "icon_size_playlist_buttons";
constexpr char kIconSizeLeftPanelButtons[] = "icon_size_left_panel_buttons";
constexpr char kIconSizeConfigureButtons[] = "icon_size_configure_buttons";
constexpr char kPlaylistPlayingSongColor[] = "playlist_playing_song_color";
enum class BackgroundImageType {
Default,
None,
Custom,
Album,
Strawbs
};
enum class BackgroundImagePosition {
UpperLeft = 1,
UpperRight = 2,
Middle = 3,
BottomLeft = 4,
BottomRight = 5
};
} // namespace
#endif // APPEARANCESETTINGS_H

View File

@ -0,0 +1,64 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDSETTINGS_H
#define BACKENDSETTINGS_H
#include <QtGlobal>
namespace BackendSettings {
constexpr char kSettingsGroup[] = "Backend";
constexpr char kEngine[] = "Engine";
constexpr char kOutput[] = "Output";
constexpr char kDevice[] = "Device";
constexpr char kALSAPlugin[] = "alsaplugin";
constexpr char kExclusiveMode[] = "exclusive_mode";
constexpr char kVolumeControl[] = "volume_control";
constexpr char kChannelsEnabled[] = "channels_enabled";
constexpr char kChannels[] = "channels";
constexpr char kBS2B[] = "bs2b";
constexpr char kHTTP2[] = "http2";
constexpr char kStrictSSL[] = "strict_ssl";
constexpr char kBufferDuration[] = "bufferduration";
constexpr char kBufferLowWatermark[] = "bufferlowwatermark";
constexpr char kBufferHighWatermark[] = "bufferhighwatermark";
constexpr char kRgEnabled[] = "rgenabled";
constexpr char kRgMode[] = "rgmode";
constexpr char kRgPreamp[] = "rgpreamp";
constexpr char kRgFallbackGain[] = "rgfallbackgain";
constexpr char kRgCompression[] = "rgcompression";
constexpr char kEBUR128LoudnessNormalization[] = "ebur128_loudness_normalization";
constexpr char kEBUR128TargetLevelLUFS[] = "ebur128_target_level_lufs";
constexpr char kFadeoutEnabled[] = "FadeoutEnabled";
constexpr char kCrossfadeEnabled[] = "CrossfadeEnabled";
constexpr char kAutoCrossfadeEnabled[] = "AutoCrossfadeEnabled";
constexpr char kNoCrossfadeSameAlbum[] = "NoCrossfadeSameAlbum";
constexpr char kFadeoutPauseEnabled[] = "FadeoutPauseEnabled";
constexpr char kFadeoutDuration[] = "FadeoutDuration";
constexpr char kFadeoutPauseDuration[] = "FadeoutPauseDuration";
constexpr qint64 kDefaultBufferDuration = 4000;
constexpr double kDefaultBufferLowWatermark = 0.33;
constexpr double kDefaultBufferHighWatermark = 0.99;
} // namespace
#endif // BACKENDSETTINGS_H

View File

@ -0,0 +1,76 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BEHAVIOURSETTINGS_H
#define BEHAVIOURSETTINGS_H
namespace BehaviourSettings {
constexpr char kSettingsGroup[] = "Behaviour";
enum class StartupBehaviour {
Remember = 1,
Show = 2,
Hide = 3,
ShowMaximized = 4,
ShowMinimized = 5
};
enum class PlayBehaviour {
Never = 1,
IfStopped = 2,
Always = 3
};
enum class PreviousBehaviour {
DontRestart = 1,
Restart = 2
};
enum class AddBehaviour {
Append = 1,
Enqueue = 2,
Load = 3,
OpenInNew = 4
};
enum class PlaylistAddBehaviour {
Play = 1,
Enqueue = 2
};
constexpr char kKeepRunning[] = "keeprunning";
constexpr char kShowTrayIcon[] = "showtrayicon";
constexpr char kTrayIconProgress[] = "trayicon_progress";
constexpr char kTaskbarProgress[] = "taskbar_progress";
constexpr char kResumePlayback[] = "resumeplayback";
constexpr char kPlayingWidget[] = "playing_widget";
constexpr char kStartupBehaviour[] = "startupbehaviour";
constexpr char kLanguage[] = "language";
constexpr char kMenuPlayMode[] = "menu_playmode";
constexpr char kMenuPreviousMode[] = "menu_previousmode";
constexpr char kDoubleClickAddMode[] = "doubleclick_addmode";
constexpr char kDoubleClickPlayMode[] = "doubleclick_playmode";
constexpr char kDoubleClickPlaylistAddMode[] = "doubleclick_playlist_addmode";
constexpr char kSeekStepSec[] = "seek_step_sec";
constexpr char kVolumeIncrement[] = "volume_increment";
} // namespace
#endif // BEHAVIOURSETTINGS_H

View File

@ -0,0 +1,62 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef COLLECTIONSETTINGS_H
#define COLLECTIONSETTINGS_H
namespace CollectionSettings {
constexpr char kSettingsGroup[] = "Collection";
constexpr char kAutoOpen[] = "auto_open";
constexpr char kShowDividers[] = "show_dividers";
constexpr char kPrettyCovers[] = "pretty_covers";
constexpr char kVariousArtists[] = "various_artists";
constexpr char kSortSkipsArticles[] = "sort_skips_articles";
constexpr char kStartupScan[] = "startup_scan";
constexpr char kMonitor[] = "monitor";
constexpr char kSongTracking[] = "song_tracking";
constexpr char kSongENUR128LoudnessAnalysis[] = "song_ebur128_loudness_analysis";
constexpr char kMarkSongsUnavailable[] = "mark_songs_unavailable";
constexpr char kExpireUnavailableSongs[] = "expire_unavailable_songs";
constexpr char kCoverArtPatterns[] = "cover_art_patterns";
constexpr char kSettingsCacheSize[] = "cache_size";
constexpr char kSettingsCacheSizeUnit[] = "cache_size_unit";
constexpr char kSettingsDiskCacheEnable[] = "disk_cache_enable";
constexpr char kSettingsDiskCacheSize[] = "disk_cache_size";
constexpr char kSettingsDiskCacheSizeUnit[] = "disk_cache_size_unit";
constexpr int kSettingsCacheSizeDefault = 160;
constexpr int kSettingsDiskCacheSizeDefault = 360;
constexpr char kSavePlayCounts[] = "save_playcounts";
constexpr char kSaveRatings[] = "save_ratings";
constexpr char kOverwritePlaycount[] = "overwrite_playcount";
constexpr char kOverwriteRating[] = "overwrite_rating";
constexpr char kDeleteFiles[] = "delete_files";
constexpr char kLastPath[] = "last_path";
enum class CacheSizeUnit {
KB,
MB,
GB,
TB
};
} // namespace
#endif // COLLECTIONSETTINGS_H

View File

@ -0,0 +1,48 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CONTEXTSETTINGS_H
#define CONTEXTSETTINGS_H
#include <QtGlobal>
namespace ContextSettings {
constexpr char kSettingsGroup[] = "Context";
constexpr char kAlbum[] = "AlbumEnable";
constexpr char kTechnicalData[] = "TechnicalDataEnable";
constexpr char kSongLyrics[] = "SongLyricsEnable";
constexpr char kSearchCover[] = "SearchCoverEnable";
constexpr char kSearchLyrics[] = "SearchLyricsEnable";
constexpr char kFontHeadline[] = "font_headline";
constexpr char kFontNormal[] = "font_normal";
constexpr char kFontSizeHeadline[] = "font_size_headline";
constexpr char kFontSizeNormal[] = "font_size_normal";
constexpr char kSettingsTitleFmt[] = "TitleFmt";
constexpr char kSettingsSummaryFmt[] = "SummaryFmt";
constexpr char kDefaultFontFamily[] = "Noto Sans";
constexpr qreal kDefaultFontSizeHeadline = 11;
} // namespace
#endif // CONTEXTSETTINGS_H

View File

@ -0,0 +1,37 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef COVERSSETTINGS_H
#define COVERSSETTINGS_H
namespace CoversSettings {
constexpr char kSettingsGroup[] = "Covers";
constexpr char kProviders[] = "providers";
constexpr char kTypes[] = "types";
constexpr char kSaveType[] = "save_type";
constexpr char kSaveFilename[] = "save_filename";
constexpr char kSavePattern[] = "save_pattern";
constexpr char kSaveOverwrite[] = "save_overwrite";
constexpr char kSaveLowercase[] = "save_lowercase";
constexpr char kSaveReplaceSpaces[] = "save_replace_spaces";
} // namespace
#endif // COVERSSETTINGS_H

View File

@ -0,0 +1,39 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef FILEFILTERCONSTANTS_H
#define FILEFILTERCONSTANTS_H
#include <QtGlobal>
constexpr char kAllFilesFilterSpec[] = QT_TRANSLATE_NOOP("FileFilter", "All Files (*)");
constexpr char kFileFilter[] =
"*.wav *.flac *.wv *.ogg *.oga *.opus *.spx *.ape *.mpc "
"*.mp2 *.mp3 *.m4a *.mp4 *.aac *.asf *.asx *.wma "
"*.aif *.aiff *.mka *.tta *.dsf *.dsd "
"*.cue *.m3u *.m3u8 *.pls *.xspf *.asxini "
"*.ac3 *.dts "
"*.mod *.s3m *.xm *.it"
"*.spc *.vgm";
constexpr char kLoadImageFileFilter[] = QT_TRANSLATE_NOOP("FileFilter", "Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm)");
constexpr char kSaveImageFileFilter[] = QT_TRANSLATE_NOOP("FileFilter", "Images (*.png *.jpg *.jpeg *.bmp *.xpm *.pbm *.ppm *.xbm)");
#endif // FILEFILTERCONSTANTS_H

View File

@ -20,7 +20,7 @@
#ifndef FILENAMECONSTANTS_H #ifndef FILENAMECONSTANTS_H
#define FILENAMECONSTANTS_H #define FILENAMECONSTANTS_H
#include "core/arraysize.h" #include "includes/arraysize.h"
constexpr char kProblematicCharactersRegex[] = "[:?*\"<>|]"; constexpr char kProblematicCharactersRegex[] = "[:?*\"<>|]";
constexpr char kInvalidFatCharactersRegex[] = "[^a-zA-Z0-9!#\\$%&'()\\-@\\^_`{}~/. ]"; constexpr char kInvalidFatCharactersRegex[] = "[^a-zA-Z0-9!#\\$%&'()\\-@\\^_`{}~/. ]";

View File

@ -0,0 +1,33 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GLOBALSHORTCUTSSETTINGS_H
#define GLOBALSHORTCUTSSETTINGS_H
namespace GlobalShortcutsSettings {
constexpr char kSettingsGroup[] = "GlobalShortcuts";
constexpr char kUseKDE[] = "use_kde";
constexpr char kUseGnome[] = "use_gnome";
constexpr char kUseMate[] = "use_mate";
constexpr char kUseX11[] = "use_x11";
} // namespace
#endif // GLOBALSHORTCUTSSETTINGS_H

View File

@ -0,0 +1,30 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LYRICSSETTINGS_H
#define LYRICSSETTINGS_H
namespace LyricsSettings {
constexpr char kSettingsGroup[] = "Lyrics";
constexpr char kProviders[] = "providers";
} // namespace
#endif // LYRICSSETTINGS_H

View File

@ -0,0 +1,37 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MAINWINDOWSETTINGS_H
#define MAINWINDOWSETTINGS_H
namespace MainWindowSettings {
constexpr char kSettingsGroup[] = "MainWindow";
constexpr char kSearchForCoverAuto[] = "search_for_cover_auto";
constexpr char kShowSidebar[] = "show_sidebar";
constexpr char kMaximized[] = "maximized";
constexpr char kMinimized[] = "minimized";
constexpr char kHidden[] = "hidden";
constexpr char kGeometry[] = "geometry";
constexpr char kSplitterState[] = "splitter_state";
constexpr char kDoNotShowSponsorMessage[] = "do_not_show_sponsor_message";
} // namespace
#endif // MAINWINDOWSETTINGS_H

View File

@ -0,0 +1,43 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOODBARSETTINGS_H
#define MOODBARSETTINGS_H
namespace MoodbarSettings {
constexpr char kSettingsGroup[] = "Moodbar";
enum class Style {
Normal = 0,
Angry,
Frozen,
Happy,
SystemPalette,
StyleCount
};
constexpr char kEnabled[] = "enabled";
constexpr char kShow[] = "show";
constexpr char kStyle[] = "style";
constexpr char kSave[] = "save";
} // namespace
#endif // MOODBARSETTINGS_H

View File

@ -0,0 +1,37 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NETWORKPROXYSETTINGS_H
#define NETWORKPROXYSETTINGS_H
namespace NetworkProxySettings {
constexpr char kSettingsGroup[] = "NetworkProxy";
constexpr char kType[] = "type";
constexpr char kHostname[] = "hostname";
constexpr char kPort[] = "port";
constexpr char kUseAuthentication[] = "use_authentication";
constexpr char kUsername[] = "username";
constexpr char kPassword[] = "password";
constexpr char kEngine[] = "engine";
} // namespace
#endif // NETWORKPROXYSETTINGS_H

View File

@ -0,0 +1,68 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NOTIFICATIONSSETTINGS_H
#define NOTIFICATIONSSETTINGS_H
#include <QRgb>
namespace OSDSettings {
constexpr char kSettingsGroup[] = "OSD";
constexpr char kType[] = "Behaviour";
enum class Type {
Disabled = 0,
Native,
TrayPopup,
Pretty
};
constexpr char kTimeout[] = "Timeout";
constexpr char kShowOnVolumeChange[] = "ShowOnVolumeChange";
constexpr char kShowOnPlayModeChange[] = "ShowOnPlayModeChange";
constexpr char kShowOnPausePlayback[] = "ShowOnPausePlayback";
constexpr char kShowOnResumePlayback[] = "ShowOnResumePlayback";
constexpr char kShowArt[] = "ShowArt";
constexpr char kCustomTextEnabled[] = "CustomTextEnabled";
constexpr char kCustomText1[] = "CustomText1";
constexpr char kCustomText2[] = "CustomText2";
} // namespace
namespace OSDPrettySettings {
constexpr char kSettingsGroup[] = "OSDPretty";
constexpr char kForegroundColor[] = "foreground_color";
constexpr char kBackgroundColor[] = "background_color";
constexpr char kBackgroundOpacity[] = "background_opacity";
constexpr char kPopupScreen[] = "popup_screen";
constexpr char kPopupPos[] = "popup_pos";
constexpr char kFont[] = "font";
constexpr char kDisableDuration[] = "disable_duration";
constexpr char kFading[] = "fading";
constexpr QRgb kPresetBlue = qRgb(102, 150, 227);
constexpr QRgb kPresetRed = qRgb(202, 22, 16);
} // namespace
#endif // NOTIFICATIONSSETTINGS_H

View File

@ -0,0 +1,70 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PLAYLISTSETTINGS_H
#define PLAYLISTSETTINGS_H
#include <QtGlobal>
namespace PlaylistSettings {
constexpr char kSettingsGroup[] = "Playlist";
enum class PathType {
Automatic = 0, // Automatically select path type
Absolute, // Always use absolute paths
Relative, // Always use relative paths
Ask_User // Only used in preferences: to ask user which of the previous values he wants to use.
};
constexpr char kAlternatingRowColors[] = "alternating_row_colors";
constexpr char kShowBars[] = "show_bars";
constexpr char kGlowEffect[] = "glow_effect";
constexpr char kWarnClosePlaylist[] = "warn_close_playlist";
constexpr char kContinueOnError[] = "continue_on_error";
constexpr char kGreyoutSongsStartup[] = "greyout_songs_startup";
constexpr char kGreyoutSongsPlay[] = "greyout_songs_play";
constexpr char kSelectTrack[] = "select_track";
constexpr char kShowToolbar[] = "show_toolbar";
constexpr char kPlaylistClear[] = "playlist_clear";
constexpr char kAutoSort[] = "auto_sort";
constexpr char kPathType[] = "path_type";
constexpr char kEditMetadataInline[] = "editmetadatainline";
constexpr char kWriteMetadata[] = "write_metadata";
constexpr char kDeleteFiles[] = "delete_files";
constexpr char kStateVersion[] = "state_version";
constexpr char kState[] = "state";
constexpr char kColumnAlignments[] = "column_alignments";
constexpr char kRatingLocked[] = "rating_locked";
constexpr char kLastSaveFilter[] = "last_save_filter";
constexpr char kLastSavePath[] = "last_save_path";
constexpr char kLastSaveExtension[] = "last_save_extension";
constexpr char kLastSaveAllPath[] = "last_save_all_path";
constexpr char kLastSaveAllExtension[] = "last_save_all_extension";
} // namespace
Q_DECLARE_METATYPE(PlaylistSettings::PathType)
#endif // PLAYLISTSETTINGS_H

View File

@ -0,0 +1,47 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef QOBUZSETTINGS_H
#define QOBUZSETTINGS_H
namespace QobuzSettings {
constexpr char kSettingsGroup[] = "Qobuz";
constexpr char kEnabled[] = "enabled";
constexpr char kAppId[] = "app_id";
constexpr char kAppSecret[] = "app_secret";
constexpr char kUsername[] = "username";
constexpr char kPassword[] = "password";
constexpr char kFormat[] = "format";
constexpr char kSearchDelay[] = "searchdelay";
constexpr char kArtistsSearchLimit[] = "artistssearchlimit";
constexpr char kAlbumsSearchLimit[] = "albumssearchlimit";
constexpr char kSongsSearchLimit[] = "songssearchlimit";
constexpr char kBase64Secret[] = "base64secret";
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
constexpr char kUserId[] = "user_id";
constexpr char kCredentialsId[] = "credentials_id";
constexpr char kDeviceId[] = "device_id";
constexpr char kUserAuthToken[] = "user_auth_token";
} // namespace
#endif // QOBUZSETTINGS_H

View File

@ -0,0 +1,40 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCROBBLERSETTINGS_H
#define SCROBBLERSETTINGS_H
namespace ScrobblerSettings {
constexpr char kSettingsGroup[] = "Scrobbler";
constexpr char kEnabled[] = "enabled";
constexpr char kScrobbleButton[] = "scrobble_button";
constexpr char kLoveButton[] = "love_button";
constexpr char kOffline[] = "offline";
constexpr char kSubmit[] = "submit";
constexpr char kAlbumArtist[] = "albumartist";
constexpr char kShowErrorDialog[] = "show_error_dialog";
constexpr char kStripRemastered[] = "strip_remastered";
constexpr char kSources[] = "sources";
constexpr char kUserToken[] = "user_token";
} // namespace
#endif // SCROBBLERSETTINGS_H

View File

@ -0,0 +1,42 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SPOTIFYSETTINGS_H
#define SPOTIFYSETTINGS_H
namespace SpotifySettings {
constexpr char kSettingsGroup[] = "Spotify";
constexpr char kEnabled[] = "enabled";
constexpr char kSearchDelay[] = "searchdelay";
constexpr char kArtistsSearchLimit[] = "artistssearchlimit";
constexpr char kAlbumsSearchLimit[] = "albumssearchlimit";
constexpr char kSongsSearchLimit[] = "songssearchlimit";
constexpr char kFetchAlbums[] = "fetchalbums";
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
constexpr char kAccessToken[] = "access_token";
constexpr char kRefreshToken[] = "refresh_token";
constexpr char kExpiresIn[] = "expires_in";
constexpr char kLoginTime[] = "login_time";
} // namespace
#endif // SPOTIFYSETTINGS_H

View File

@ -0,0 +1,45 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SUBSONICETTINGS_H
#define SUBSONICETTINGS_H
namespace SubsonicSettings {
constexpr char kSettingsGroup[] = "Subsonic";
enum class AuthMethod {
Hex,
MD5
};
constexpr char kEnabled[] = "enabled";
constexpr char kUrl[] = "url";
constexpr char kUsername[] = "username";
constexpr char kPassword[] = "password";
constexpr char kHTTP2[] = "http2";
constexpr char kVerifyCertificate[] = "verifycertificate";
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
constexpr char kServerSideScrobbling[] = "serversidescrobbling";
constexpr char kAuthMethod[] = "authmethod";
} // namespace
#endif // SUBSONICETTINGS_H

View File

@ -0,0 +1,52 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TIDALSETTINGS_H
#define TIDALSETTINGS_H
namespace TidalSettings {
constexpr char kSettingsGroup[] = "Tidal";
constexpr char kEnabled[] = "enabled";
constexpr char kOAuth[] = "oauth";
constexpr char kClientId[] = "client_id";
constexpr char kApiToken[] = "api_token";
constexpr char kUsername[] = "username";
constexpr char kPassword[] = "password";
constexpr char kQuality[] = "quality";
constexpr char kSearchDelay[] = "searchdelay";
constexpr char kArtistsSearchLimit[] = "artistssearchlimit";
constexpr char kAlbumsSearchLimit[] = "albumssearchlimit";
constexpr char kSongsSearchLimit[] = "songssearchlimit";
constexpr char kFetchAlbums[] = "fetchalbums";
constexpr char kDownloadAlbumCovers[] = "downloadalbumcovers";
constexpr char kCoverSize[] = "coversize";
constexpr char kStreamUrl[] = "streamurl";
constexpr char kAlbumExplicit[] = "album_explicit";
enum class StreamUrlMethod {
StreamUrl,
UrlPostPaywall,
PlaybackInfoPostPaywall
};
}
#endif // TIDALSETTINGS_H

View File

@ -0,0 +1,34 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it wiLL be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TIMECONSTANTS_H
#define TIMECONSTANTS_H
#include <QtGlobal>
constexpr qint64 kMsecPerSec = 1000LL;
constexpr qint64 kUsecPerMsec = 1000LL;
constexpr qint64 kUsecPerSec = 1000000LL;
constexpr qint64 kNsecPerUsec = 1000LL;
constexpr qint64 kNsecPerMsec = 1000000LL;
constexpr qint64 kNsecPerSec = 1000000000LL;
constexpr qint64 kSecsPerDay = 24 * 60 * 60;
#endif // TIMECONSTANTS_H

View File

@ -0,0 +1,29 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it wiLL be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TRANSCODERSETTINGS_H
#define TRANSCODERSETTINGS_H
namespace TranscoderSettings {
constexpr char kSettingsGroup[] = "Transcoder";
}
#endif // TRANSCODERSETTINGS_H

View File

@ -38,7 +38,7 @@
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QPaintEvent> #include <QPaintEvent>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "utilities/imageutils.h" #include "utilities/imageutils.h"
#include "covermanager/albumcoverchoicecontroller.h" #include "covermanager/albumcoverchoicecontroller.h"

View File

@ -33,8 +33,8 @@
#include <QPixmap> #include <QPixmap>
#include <QMovie> #include <QMovie>
#include "core/scoped_ptr.h" #include "includes/scoped_ptr.h"
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
class QMenu; class QMenu;
class QTimeLine; class QTimeLine;

View File

@ -50,8 +50,6 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QDropEvent> #include <QDropEvent>
#include "core/application.h"
#include "core/player.h"
#include "core/song.h" #include "core/song.h"
#include "core/settings.h" #include "core/settings.h"
#include "utilities/strutils.h" #include "utilities/strutils.h"
@ -60,7 +58,7 @@
#include "collection/collectionview.h" #include "collection/collectionview.h"
#include "covermanager/albumcoverchoicecontroller.h" #include "covermanager/albumcoverchoicecontroller.h"
#include "lyrics/lyricsfetcher.h" #include "lyrics/lyricsfetcher.h"
#include "settings/contextsettingspage.h" #include "constants/contextsettings.h"
#include "contextview.h" #include "contextview.h"
#include "contextalbum.h" #include "contextalbum.h"
@ -69,11 +67,10 @@ using namespace Qt::Literals::StringLiterals;
namespace { namespace {
constexpr int kWidgetSpacing = 50; constexpr int kWidgetSpacing = 50;
} } // namespace
ContextView::ContextView(QWidget *parent) ContextView::ContextView(QWidget *parent)
: QWidget(parent), : QWidget(parent),
app_(nullptr),
collectionview_(nullptr), collectionview_(nullptr),
album_cover_choice_controller_(nullptr), album_cover_choice_controller_(nullptr),
lyrics_fetcher_(nullptr), lyrics_fetcher_(nullptr),
@ -241,14 +238,13 @@ ContextView::ContextView(QWidget *parent)
} }
void ContextView::Init(Application *app, CollectionView *collectionview, AlbumCoverChoiceController *album_cover_choice_controller) { void ContextView::Init(CollectionView *collectionview, AlbumCoverChoiceController *album_cover_choice_controller, SharedPtr<LyricsProviders> lyrics_providers) {
app_ = app;
collectionview_ = collectionview; collectionview_ = collectionview;
album_cover_choice_controller_ = album_cover_choice_controller; album_cover_choice_controller_ = album_cover_choice_controller;
widget_album_->Init(this, album_cover_choice_controller_); widget_album_->Init(this, album_cover_choice_controller_);
lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this); lyrics_fetcher_ = new LyricsFetcher(lyrics_providers, this);
QObject::connect(collectionview_, &CollectionView::TotalSongCountUpdated_, this, &ContextView::UpdateNoSong); QObject::connect(collectionview_, &CollectionView::TotalSongCountUpdated_, this, &ContextView::UpdateNoSong);
QObject::connect(collectionview_, &CollectionView::TotalArtistCountUpdated_, this, &ContextView::UpdateNoSong); QObject::connect(collectionview_, &CollectionView::TotalArtistCountUpdated_, this, &ContextView::UpdateNoSong);
@ -295,27 +291,27 @@ void ContextView::AddActions() {
void ContextView::ReloadSettings() { void ContextView::ReloadSettings() {
QString default_font; QString default_font;
if (QFontDatabase::families().contains(QLatin1String(ContextSettingsPage::kDefaultFontFamily))) { if (QFontDatabase::families().contains(QLatin1String(ContextSettings::kDefaultFontFamily))) {
default_font = QLatin1String(ContextSettingsPage::kDefaultFontFamily); default_font = QLatin1String(ContextSettings::kDefaultFontFamily);
} }
else { else {
default_font = font().family(); default_font = font().family();
} }
Settings s; Settings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup); s.beginGroup(ContextSettings::kSettingsGroup);
title_fmt_ = s.value(ContextSettingsPage::kSettingsTitleFmt, u"%title% - %artist%"_s).toString(); title_fmt_ = s.value(ContextSettings::kSettingsTitleFmt, u"%title% - %artist%"_s).toString();
summary_fmt_ = s.value(ContextSettingsPage::kSettingsSummaryFmt, u"%album%"_s).toString(); summary_fmt_ = s.value(ContextSettings::kSettingsSummaryFmt, u"%album%"_s).toString();
action_show_album_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::ALBUM)], true).toBool()); action_show_album_->setChecked(s.value(ContextSettings::kAlbum, true).toBool());
action_show_data_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA)], false).toBool()); action_show_data_->setChecked(s.value(ContextSettings::kTechnicalData, false).toBool());
action_show_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS)], true).toBool()); action_show_lyrics_->setChecked(s.value(ContextSettings::kSongLyrics, true).toBool());
action_search_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SEARCH_LYRICS)], true).toBool()); action_search_lyrics_->setChecked(s.value(ContextSettings::kSearchLyrics, true).toBool());
font_headline_.setFamily(s.value("font_headline", default_font).toString()); font_headline_.setFamily(s.value(ContextSettings::kFontHeadline, default_font).toString());
font_headline_.setPointSizeF(s.value("font_size_headline", ContextSettingsPage::kDefaultFontSizeHeadline).toReal()); font_headline_.setPointSizeF(s.value(ContextSettings::kFontSizeHeadline, ContextSettings::kDefaultFontSizeHeadline).toReal());
font_nosong_.setFamily(font_headline_.family()); font_nosong_.setFamily(font_headline_.family());
font_nosong_.setPointSizeF(font_headline_.pointSizeF() * 1.6F); font_nosong_.setPointSizeF(font_headline_.pointSizeF() * 1.6F);
font_normal_.setFamily(s.value("font_normal", default_font).toString()); font_normal_.setFamily(s.value(ContextSettings::kFontNormal, default_font).toString());
font_normal_.setPointSizeF(s.value("font_size_normal", font().pointSizeF()).toReal()); font_normal_.setPointSizeF(s.value(ContextSettings::kFontSizeNormal, font().pointSizeF()).toReal());
s.endGroup(); s.endGroup();
UpdateFonts(); UpdateFonts();
@ -697,9 +693,10 @@ void ContextView::AlbumCoverLoaded(const Song &song, const QImage &image) {
void ContextView::ActionShowAlbum() { void ContextView::ActionShowAlbum() {
Settings s; Settings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup); s.beginGroup(ContextSettings::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::ALBUM)], action_show_album_->isChecked()); s.setValue(ContextSettings::kAlbum, action_show_album_->isChecked());
s.endGroup(); s.endGroup();
if (song_playing_.is_valid()) SetSong(); if (song_playing_.is_valid()) SetSong();
} }
@ -707,9 +704,10 @@ void ContextView::ActionShowAlbum() {
void ContextView::ActionShowData() { void ContextView::ActionShowData() {
Settings s; Settings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup); s.beginGroup(ContextSettings::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA)], action_show_data_->isChecked()); s.setValue(ContextSettings::kTechnicalData, action_show_data_->isChecked());
s.endGroup(); s.endGroup();
if (song_playing_.is_valid()) SetSong(); if (song_playing_.is_valid()) SetSong();
} }
@ -717,8 +715,8 @@ void ContextView::ActionShowData() {
void ContextView::ActionShowLyrics() { void ContextView::ActionShowLyrics() {
Settings s; Settings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup); s.beginGroup(ContextSettings::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS)], action_show_lyrics_->isChecked()); s.setValue(ContextSettings::kSongLyrics, action_show_lyrics_->isChecked());
s.endGroup(); s.endGroup();
if (song_playing_.is_valid()) SetSong(); if (song_playing_.is_valid()) SetSong();
@ -730,8 +728,8 @@ void ContextView::ActionShowLyrics() {
void ContextView::ActionSearchLyrics() { void ContextView::ActionSearchLyrics() {
Settings s; Settings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup); s.beginGroup(ContextSettings::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[static_cast<int>(ContextSettingsPage::ContextSettingsOrder::SEARCH_LYRICS)], action_search_lyrics_->isChecked()); s.setValue(ContextSettings::kSearchLyrics, action_search_lyrics_->isChecked());
s.endGroup(); s.endGroup();
if (song_playing_.is_valid()) SetSong(); if (song_playing_.is_valid()) SetSong();

View File

@ -46,9 +46,9 @@ class QDragEnterEvent;
class QDropEvent; class QDropEvent;
class ResizableTextEdit; class ResizableTextEdit;
class Application;
class CollectionView; class CollectionView;
class AlbumCoverChoiceController; class AlbumCoverChoiceController;
class LyricsProviders;
class LyricsFetcher; class LyricsFetcher;
class ContextView : public QWidget { class ContextView : public QWidget {
@ -57,7 +57,7 @@ class ContextView : public QWidget {
public: public:
explicit ContextView(QWidget *parent = nullptr); explicit ContextView(QWidget *parent = nullptr);
void Init(Application *app, CollectionView *collectionview, AlbumCoverChoiceController *album_cover_choice_controller); void Init(CollectionView *collectionview, AlbumCoverChoiceController *album_cover_choice_controller, SharedPtr<LyricsProviders> lyrics_providers);
ContextAlbum *album_widget() const { return widget_album_; } ContextAlbum *album_widget() const { return widget_album_; }
bool album_enabled() const { return action_show_album_->isChecked(); } bool album_enabled() const { return action_show_album_->isChecked(); }
@ -101,7 +101,6 @@ class ContextView : public QWidget {
void AlbumCoverLoaded(const Song &song, const QImage &image); void AlbumCoverLoaded(const Song &song, const QImage &image);
private: private:
Application *app_;
CollectionView *collectionview_; CollectionView *collectionview_;
AlbumCoverChoiceController *album_cover_choice_controller_; AlbumCoverChoiceController *album_cover_choice_controller_;
LyricsFetcher *lyrics_fetcher_; LyricsFetcher *lyrics_fetcher_;

View File

@ -34,18 +34,17 @@
#include "core/logging.h" #include "core/logging.h"
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "lazy.h" #include "includes/lazy.h"
#include "database.h" #include "core/database.h"
#include "taskmanager.h" #include "core/taskmanager.h"
#include "player.h" #include "core/networkaccessmanager.h"
#include "networkaccessmanager.h" #include "core/player.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "engine/devicefinders.h" #include "engine/devicefinders.h"
#ifndef Q_OS_WIN #include "core/urlhandlers.h"
# include "device/devicemanager.h" #include "device/devicemanager.h"
#endif #include "collection/collectionlibrary.h"
#include "collection/collection.h"
#include "playlist/playlistbackend.h" #include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h" #include "playlist/playlistmanager.h"
#include "covermanager/albumcoverloader.h" #include "covermanager/albumcoverloader.h"
@ -114,58 +113,57 @@ using namespace std::chrono_literals;
class ApplicationImpl { class ApplicationImpl {
public: public:
explicit ApplicationImpl(Application *app) : explicit ApplicationImpl(Application *app) :
tag_reader_client_([app](){ tagreader_client_([app](){
TagReaderClient *client = new TagReaderClient(); TagReaderClient *client = new TagReaderClient();
app->MoveToNewThread(client); app->MoveToNewThread(client);
return client; return client;
}), }),
database_([app]() { database_([app]() {
Database *db = new Database(app); Database *database = new Database(app->task_manager());
app->MoveToNewThread(db); app->MoveToNewThread(database);
QTimer::singleShot(30s, db, &Database::DoBackup); QTimer::singleShot(30s, database, &Database::DoBackup);
return db; return database;
}), }),
task_manager_([]() { return new TaskManager(); }), task_manager_([]() { return new TaskManager(); }),
player_([app]() { return new Player(app); }), player_([app]() { return new Player(app->task_manager(), app->url_handlers(), app->playlist_manager()); }),
network_([]() { return new NetworkAccessManager(); }), network_([]() { return new NetworkAccessManager(); }),
device_finders_([]() { return new DeviceFinders(); }), device_finders_([]() { return new DeviceFinders(); }),
#ifndef Q_OS_WIN url_handlers_([]() { return new UrlHandlers(); }),
device_manager_([app]() { return new DeviceManager(app); }), device_manager_([app]() { return new DeviceManager(app->task_manager(), app->database(), app->tagreader_client(), app->albumcover_loader()); }),
#endif collection_([app]() { return new CollectionLibrary(app->database(), app->task_manager(), app->tagreader_client(), app->albumcover_loader()); }),
collection_([app]() { return new SCollection(app); }),
playlist_backend_([this, app]() { playlist_backend_([this, app]() {
PlaylistBackend *backend = new PlaylistBackend(app); PlaylistBackend *playlist_backend = new PlaylistBackend(app->database(), app->tagreader_client(), app->collection_backend());
app->MoveToThread(backend, database_->thread()); app->MoveToThread(playlist_backend, database_->thread());
return backend; return playlist_backend;
}), }),
playlist_manager_([app]() { return new PlaylistManager(app); }), playlist_manager_([app]() { return new PlaylistManager(app->task_manager(), app->tagreader_client(), app->url_handlers(), app->playlist_backend(), app->collection_backend(), app->current_albumcover_loader()); }),
cover_providers_([app]() { cover_providers_([app]() {
CoverProviders *cover_providers = new CoverProviders(); CoverProviders *cover_providers = new CoverProviders();
// Initialize the repository of cover providers. // Initialize the repository of cover providers.
cover_providers->AddProvider(new LastFmCoverProvider(app, app->network())); cover_providers->AddProvider(new LastFmCoverProvider(app->network()));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app, app->network())); cover_providers->AddProvider(new MusicbrainzCoverProvider(app->network()));
cover_providers->AddProvider(new DiscogsCoverProvider(app, app->network())); cover_providers->AddProvider(new DiscogsCoverProvider(app->network()));
cover_providers->AddProvider(new DeezerCoverProvider(app, app->network())); cover_providers->AddProvider(new DeezerCoverProvider(app->network()));
cover_providers->AddProvider(new MusixmatchCoverProvider(app, app->network())); cover_providers->AddProvider(new MusixmatchCoverProvider(app->network()));
cover_providers->AddProvider(new OpenTidalCoverProvider(app, app->network())); cover_providers->AddProvider(new OpenTidalCoverProvider(app->network()));
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
cover_providers->AddProvider(new TidalCoverProvider(app, app->network())); cover_providers->AddProvider(new TidalCoverProvider(app->streaming_services()->Service<TidalService>(), app->network()));
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
cover_providers->AddProvider(new SpotifyCoverProvider(app, app->network())); cover_providers->AddProvider(new SpotifyCoverProvider(app->streaming_services()->Service<SpotifyService>(), app->network()));
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
cover_providers->AddProvider(new QobuzCoverProvider(app, app->network())); cover_providers->AddProvider(new QobuzCoverProvider(app->streaming_services()->Service<QobuzService>(), app->network()));
#endif #endif
cover_providers->ReloadSettings(); cover_providers->ReloadSettings();
return cover_providers; return cover_providers;
}), }),
album_cover_loader_([app]() { albumcover_loader_([app]() {
AlbumCoverLoader *loader = new AlbumCoverLoader(); AlbumCoverLoader *loader = new AlbumCoverLoader(app->tagreader_client());
app->MoveToNewThread(loader); app->MoveToNewThread(loader);
return loader; return loader;
}), }),
current_albumcover_loader_([app]() { return new CurrentAlbumCoverLoader(app); }), current_albumcover_loader_([app]() { return new CurrentAlbumCoverLoader(app->albumcover_loader()); }),
lyrics_providers_([app]() { lyrics_providers_([app]() {
LyricsProviders *lyrics_providers = new LyricsProviders(app); LyricsProviders *lyrics_providers = new LyricsProviders(app);
// Initialize the repository of lyrics providers. // Initialize the repository of lyrics providers.
@ -185,51 +183,50 @@ class ApplicationImpl {
streaming_services_([app]() { streaming_services_([app]() {
StreamingServices *streaming_services = new StreamingServices(); StreamingServices *streaming_services = new StreamingServices();
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
streaming_services->AddService(make_shared<SubsonicService>(app)); streaming_services->AddService(make_shared<SubsonicService>(app->task_manager(), app->database(), app->url_handlers(), app->albumcover_loader()));
#endif #endif
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
streaming_services->AddService(make_shared<TidalService>(app)); streaming_services->AddService(make_shared<TidalService>(app->task_manager(), app->database(), app->network(), app->url_handlers(), app->albumcover_loader()));
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
streaming_services->AddService(make_shared<SpotifyService>(app)); streaming_services->AddService(make_shared<SpotifyService>(app->task_manager(), app->database(), app->network(), app->albumcover_loader()));
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
streaming_services->AddService(make_shared<QobuzService>(app)); streaming_services->AddService(make_shared<QobuzService>(app->task_manager(), app->database(), app->network(), app->url_handlers(), app->albumcover_loader()));
#endif #endif
return streaming_services; return streaming_services;
}), }),
radio_services_([app]() { return new RadioServices(app); }), radio_services_([app]() { return new RadioServices(app->task_manager(), app->network(), app->database(), app->albumcover_loader()); }),
scrobbler_([app]() { scrobbler_([app]() {
AudioScrobbler *scrobbler = new AudioScrobbler(app); AudioScrobbler *scrobbler = new AudioScrobbler(app);
scrobbler->AddService(make_shared<LastFMScrobbler>(scrobbler->settings(), app->network())); scrobbler->AddService(make_shared<LastFMScrobbler>(scrobbler->settings(), app->network()));
scrobbler->AddService(make_shared<LibreFMScrobbler>(scrobbler->settings(), app->network())); scrobbler->AddService(make_shared<LibreFMScrobbler>(scrobbler->settings(), app->network()));
scrobbler->AddService(make_shared<ListenBrainzScrobbler>(scrobbler->settings(), app->network())); scrobbler->AddService(make_shared<ListenBrainzScrobbler>(scrobbler->settings(), app->network()));
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
scrobbler->AddService(make_shared<SubsonicScrobbler>(scrobbler->settings(), app)); scrobbler->AddService(make_shared<SubsonicScrobbler>(scrobbler->settings(), app->streaming_services()->Service<SubsonicService>(), app));
#endif #endif
return scrobbler; return scrobbler;
}), }),
#ifdef HAVE_MOODBAR #ifdef HAVE_MOODBAR
moodbar_loader_([app]() { return new MoodbarLoader(app); }), moodbar_loader_([app]() { return new MoodbarLoader(app); }),
moodbar_controller_([app]() { return new MoodbarController(app); }), moodbar_controller_([app]() { return new MoodbarController(app->player(), app->moodbar_loader()); }),
#endif #endif
lastfm_import_([app]() { return new LastFMImport(app->network()); }) lastfm_import_([app]() { return new LastFMImport(app->network()); })
{} {}
Lazy<TagReaderClient> tag_reader_client_; Lazy<TagReaderClient> tagreader_client_;
Lazy<Database> database_; Lazy<Database> database_;
Lazy<TaskManager> task_manager_; Lazy<TaskManager> task_manager_;
Lazy<Player> player_; Lazy<Player> player_;
Lazy<NetworkAccessManager> network_; Lazy<NetworkAccessManager> network_;
Lazy<DeviceFinders> device_finders_; Lazy<DeviceFinders> device_finders_;
#ifndef Q_OS_WIN Lazy<UrlHandlers> url_handlers_;
Lazy<DeviceManager> device_manager_; Lazy<DeviceManager> device_manager_;
#endif Lazy<CollectionLibrary> collection_;
Lazy<SCollection> collection_;
Lazy<PlaylistBackend> playlist_backend_; Lazy<PlaylistBackend> playlist_backend_;
Lazy<PlaylistManager> playlist_manager_; Lazy<PlaylistManager> playlist_manager_;
Lazy<CoverProviders> cover_providers_; Lazy<CoverProviders> cover_providers_;
Lazy<AlbumCoverLoader> album_cover_loader_; Lazy<AlbumCoverLoader> albumcover_loader_;
Lazy<CurrentAlbumCoverLoader> current_albumcover_loader_; Lazy<CurrentAlbumCoverLoader> current_albumcover_loader_;
Lazy<LyricsProviders> lyrics_providers_; Lazy<LyricsProviders> lyrics_providers_;
Lazy<StreamingServices> streaming_services_; Lazy<StreamingServices> streaming_services_;
@ -250,9 +247,7 @@ Application::Application(QObject *parent)
device_finders()->Init(); device_finders()->Init();
collection()->Init(); collection()->Init();
tag_reader_client(); tagreader_client();
QObject::connect(&*database(), &Database::Error, this, &Application::ErrorAdded);
} }
@ -294,32 +289,28 @@ void Application::MoveToThread(QObject *object, QThread *thread) {
void Application::Exit() { void Application::Exit() {
wait_for_exit_ << &*tag_reader_client() wait_for_exit_ << &*tagreader_client()
<< &*collection() << &*collection()
<< &*playlist_backend() << &*playlist_backend()
<< &*album_cover_loader() << &*albumcover_loader()
#ifndef Q_OS_WIN
<< &*device_manager() << &*device_manager()
#endif
<< &*streaming_services() << &*streaming_services()
<< &*radio_services()->radio_backend(); << &*radio_services()->radio_backend();
QObject::connect(&*tag_reader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived); QObject::connect(&*tagreader_client(), &TagReaderClient::ExitFinished, this, &Application::ExitReceived);
tag_reader_client()->ExitAsync(); tagreader_client()->ExitAsync();
QObject::connect(&*collection(), &SCollection::ExitFinished, this, &Application::ExitReceived); QObject::connect(&*collection(), &CollectionLibrary::ExitFinished, this, &Application::ExitReceived);
collection()->Exit(); collection()->Exit();
QObject::connect(&*playlist_backend(), &PlaylistBackend::ExitFinished, this, &Application::ExitReceived); QObject::connect(&*playlist_backend(), &PlaylistBackend::ExitFinished, this, &Application::ExitReceived);
playlist_backend()->ExitAsync(); playlist_backend()->ExitAsync();
QObject::connect(&*album_cover_loader(), &AlbumCoverLoader::ExitFinished, this, &Application::ExitReceived); QObject::connect(&*albumcover_loader(), &AlbumCoverLoader::ExitFinished, this, &Application::ExitReceived);
album_cover_loader()->ExitAsync(); albumcover_loader()->ExitAsync();
#ifndef Q_OS_WIN
QObject::connect(&*device_manager(), &DeviceManager::ExitFinished, this, &Application::ExitReceived); QObject::connect(&*device_manager(), &DeviceManager::ExitFinished, this, &Application::ExitReceived);
device_manager()->Exit(); device_manager()->Exit();
#endif
QObject::connect(&*streaming_services(), &StreamingServices::ExitFinished, this, &Application::ExitReceived); QObject::connect(&*streaming_services(), &StreamingServices::ExitFinished, this, &Application::ExitReceived);
streaming_services()->Exit(); streaming_services()->Exit();
@ -345,23 +336,18 @@ void Application::ExitReceived() {
} }
void Application::AddError(const QString &message) { Q_EMIT ErrorAdded(message); } SharedPtr<TagReaderClient> Application::tagreader_client() const { return p_->tagreader_client_.ptr(); }
void Application::ReloadSettings() { Q_EMIT SettingsChanged(); }
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { Q_EMIT SettingsDialogRequested(page); }
SharedPtr<TagReaderClient> Application::tag_reader_client() const { return p_->tag_reader_client_.ptr(); }
SharedPtr<Database> Application::database() const { return p_->database_.ptr(); } SharedPtr<Database> Application::database() const { return p_->database_.ptr(); }
SharedPtr<TaskManager> Application::task_manager() const { return p_->task_manager_.ptr(); } SharedPtr<TaskManager> Application::task_manager() const { return p_->task_manager_.ptr(); }
SharedPtr<Player> Application::player() const { return p_->player_.ptr(); } SharedPtr<Player> Application::player() const { return p_->player_.ptr(); }
SharedPtr<NetworkAccessManager> Application::network() const { return p_->network_.ptr(); } SharedPtr<NetworkAccessManager> Application::network() const { return p_->network_.ptr(); }
SharedPtr<DeviceFinders> Application::device_finders() const { return p_->device_finders_.ptr(); } SharedPtr<DeviceFinders> Application::device_finders() const { return p_->device_finders_.ptr(); }
#ifndef Q_OS_WIN SharedPtr<UrlHandlers> Application::url_handlers() const { return p_->url_handlers_.ptr(); }
SharedPtr<DeviceManager> Application::device_manager() const { return p_->device_manager_.ptr(); } SharedPtr<DeviceManager> Application::device_manager() const { return p_->device_manager_.ptr(); }
#endif SharedPtr<CollectionLibrary> Application::collection() const { return p_->collection_.ptr(); }
SharedPtr<SCollection> Application::collection() const { return p_->collection_.ptr(); }
SharedPtr<CollectionBackend> Application::collection_backend() const { return collection()->backend(); } SharedPtr<CollectionBackend> Application::collection_backend() const { return collection()->backend(); }
CollectionModel *Application::collection_model() const { return collection()->model(); } CollectionModel *Application::collection_model() const { return collection()->model(); }
SharedPtr<AlbumCoverLoader> Application::album_cover_loader() const { return p_->album_cover_loader_.ptr(); } SharedPtr<AlbumCoverLoader> Application::albumcover_loader() const { return p_->albumcover_loader_.ptr(); }
SharedPtr<CoverProviders> Application::cover_providers() const { return p_->cover_providers_.ptr(); } SharedPtr<CoverProviders> Application::cover_providers() const { return p_->cover_providers_.ptr(); }
SharedPtr<CurrentAlbumCoverLoader> Application::current_albumcover_loader() const { return p_->current_albumcover_loader_.ptr(); } SharedPtr<CurrentAlbumCoverLoader> Application::current_albumcover_loader() const { return p_->current_albumcover_loader_.ptr(); }
SharedPtr<LyricsProviders> Application::lyrics_providers() const { return p_->lyrics_providers_.ptr(); } SharedPtr<LyricsProviders> Application::lyrics_providers() const { return p_->lyrics_providers_.ptr(); }

View File

@ -29,10 +29,8 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include "scoped_ptr.h" #include "includes/scoped_ptr.h"
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "settings/settingsdialog.h"
class QThread; class QThread;
@ -41,16 +39,15 @@ class ApplicationImpl;
class TagReaderClient; class TagReaderClient;
class Database; class Database;
class DeviceFinders; class DeviceFinders;
class UrlHandlers;
class Player; class Player;
class NetworkAccessManager; class NetworkAccessManager;
class SCollection; class CollectionLibrary;
class CollectionBackend; class CollectionBackend;
class CollectionModel; class CollectionModel;
class PlaylistBackend; class PlaylistBackend;
class PlaylistManager; class PlaylistManager;
#ifndef Q_OS_WIN
class DeviceManager; class DeviceManager;
#endif
class CoverProviders; class CoverProviders;
class AlbumCoverLoader; class AlbumCoverLoader;
class CurrentAlbumCoverLoader; class CurrentAlbumCoverLoader;
@ -72,17 +69,16 @@ class Application : public QObject {
explicit Application(QObject *parent = nullptr); explicit Application(QObject *parent = nullptr);
~Application() override; ~Application() override;
SharedPtr<TagReaderClient> tag_reader_client() const; SharedPtr<TagReaderClient> tagreader_client() const;
SharedPtr<Database> database() const; SharedPtr<Database> database() const;
SharedPtr<TaskManager> task_manager() const; SharedPtr<TaskManager> task_manager() const;
SharedPtr<Player> player() const; SharedPtr<Player> player() const;
SharedPtr<NetworkAccessManager> network() const; SharedPtr<NetworkAccessManager> network() const;
SharedPtr<DeviceFinders> device_finders() const; SharedPtr<DeviceFinders> device_finders() const;
#ifndef Q_OS_WIN SharedPtr<UrlHandlers> url_handlers() const;
SharedPtr<DeviceManager> device_manager() const; SharedPtr<DeviceManager> device_manager() const;
#endif
SharedPtr<SCollection> collection() const; SharedPtr<CollectionLibrary> collection() const;
SharedPtr<CollectionBackend> collection_backend() const; SharedPtr<CollectionBackend> collection_backend() const;
CollectionModel *collection_model() const; CollectionModel *collection_model() const;
@ -90,7 +86,7 @@ class Application : public QObject {
SharedPtr<PlaylistManager> playlist_manager() const; SharedPtr<PlaylistManager> playlist_manager() const;
SharedPtr<CoverProviders> cover_providers() const; SharedPtr<CoverProviders> cover_providers() const;
SharedPtr<AlbumCoverLoader> album_cover_loader() const; SharedPtr<AlbumCoverLoader> albumcover_loader() const;
SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader() const; SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader() const;
SharedPtr<LyricsProviders> lyrics_providers() const; SharedPtr<LyricsProviders> lyrics_providers() const;
@ -115,23 +111,13 @@ class Application : public QObject {
private Q_SLOTS: private Q_SLOTS:
void ExitReceived(); void ExitReceived();
public Q_SLOTS:
void AddError(const QString &message);
void ReloadSettings();
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
Q_SIGNALS: Q_SIGNALS:
void ErrorAdded(const QString &message);
void SettingsChanged();
void SettingsDialogRequested(const SettingsDialog::Page page);
void ExitFinished(); void ExitFinished();
void ClearPixmapDiskCache();
private: private:
ScopedPtr<ApplicationImpl> p_; ScopedPtr<ApplicationImpl> p_;
QList<QThread*> threads_; QList<QThread*> threads_;
QList<QObject*> wait_for_exit_; QList<QObject*> wait_for_exit_;
}; };
#endif // APPLICATION_H #endif // APPLICATION_H

View File

@ -45,7 +45,6 @@
#include "core/logging.h" #include "core/logging.h"
#include "taskmanager.h" #include "taskmanager.h"
#include "database.h" #include "database.h"
#include "application.h"
#include "sqlquery.h" #include "sqlquery.h"
#include "scopedtransaction.h" #include "scopedtransaction.h"
@ -62,9 +61,9 @@ constexpr char kMagicAllSongsTables[] = "%allsongstables";
int Database::sNextConnectionId = 1; int Database::sNextConnectionId = 1;
QMutex Database::sNextConnectionIdMutex; QMutex Database::sNextConnectionIdMutex;
Database::Database(Application *app, QObject *parent, const QString &database_name) : Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const QString &database_name) :
QObject(parent), QObject(parent),
app_(app), task_manager_(task_manager),
injected_database_name_(database_name), injected_database_name_(database_name),
query_hash_(0), query_hash_(0),
startup_schema_version_(-1), startup_schema_version_(-1),
@ -145,7 +144,7 @@ QSqlDatabase Database::Connect() {
} }
if (!db.open()) { if (!db.open()) {
app_->AddError(u"Database: "_s + db.lastError().text()); Q_EMIT Error(u"Database: "_s + db.lastError().text());
return db; return db;
} }
@ -492,7 +491,7 @@ void Database::ReportErrors(const SqlQuery &query) {
bool Database::IntegrityCheck(const QSqlDatabase &db) { bool Database::IntegrityCheck(const QSqlDatabase &db) {
qLog(Debug) << "Starting database integrity check"; qLog(Debug) << "Starting database integrity check";
const int task_id = app_->task_manager()->StartTask(tr("Integrity check")); const int task_id = task_manager_->StartTask(tr("Integrity check"));
bool ok = false; bool ok = false;
// Ask for 10 error messages at most. // Ask for 10 error messages at most.
@ -509,8 +508,8 @@ bool Database::IntegrityCheck(const QSqlDatabase &db) {
break; break;
} }
else { else {
if (!error_reported) { app_->AddError(tr("Database corruption detected.")); } if (!error_reported) { Q_EMIT Error(tr("Database corruption detected.")); }
app_->AddError(u"Database: "_s + message); Q_EMIT Error(u"Database: "_s + message);
error_reported = true; error_reported = true;
} }
} }
@ -519,7 +518,7 @@ bool Database::IntegrityCheck(const QSqlDatabase &db) {
ReportErrors(q); ReportErrors(q);
} }
app_->task_manager()->SetTaskFinished(task_id); task_manager_->SetTaskFinished(task_id);
return ok; return ok;
@ -563,7 +562,7 @@ void Database::BackupFile(const QString &filename) {
qLog(Debug) << "Starting database backup"; qLog(Debug) << "Starting database backup";
QString dest_filename = QStringLiteral("%1.bak").arg(filename); QString dest_filename = QStringLiteral("%1.bak").arg(filename);
const int task_id = app_->task_manager()->StartTask(tr("Backing up database")); const int task_id = task_manager_->StartTask(tr("Backing up database"));
sqlite3 *source_connection = nullptr; sqlite3 *source_connection = nullptr;
sqlite3 *dest_connection = nullptr; sqlite3 *dest_connection = nullptr;
@ -575,7 +574,7 @@ void Database::BackupFile(const QString &filename) {
if (dest_connection) { if (dest_connection) {
sqlite3_close(dest_connection); sqlite3_close(dest_connection);
} }
app_->task_manager()->SetTaskFinished(task_id); task_manager_->SetTaskFinished(task_id);
}); });
bool success = OpenDatabase(filename, &source_connection); bool success = OpenDatabase(filename, &source_connection);
@ -599,7 +598,7 @@ void Database::BackupFile(const QString &filename) {
do { do {
ret = sqlite3_backup_step(backup, 16); ret = sqlite3_backup_step(backup, 16);
const int page_count = sqlite3_backup_pagecount(backup); const int page_count = sqlite3_backup_pagecount(backup);
app_->task_manager()->SetTaskProgress(task_id, page_count - sqlite3_backup_remaining(backup), page_count); task_manager_->SetTaskProgress(task_id, page_count - sqlite3_backup_remaining(backup), page_count);
} }
while (ret == SQLITE_OK); while (ret == SQLITE_OK);

View File

@ -36,16 +36,17 @@
#include <QStringList> #include <QStringList>
#include <QRecursiveMutex> #include <QRecursiveMutex>
#include "includes/shared_ptr.h"
#include "sqlquery.h" #include "sqlquery.h"
class QThread; class QThread;
class Application; class TaskManager;
class Database : public QObject { class Database : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit Database(Application *app, QObject *parent = nullptr, const QString &database_name = QString()); explicit Database(SharedPtr<TaskManager> task_manager, QObject *parent = nullptr, const QString &database_name = QString());
~Database() override; ~Database() override;
static const int kSchemaVersion; static const int kSchemaVersion;
@ -102,7 +103,7 @@ class Database : public QObject {
void BackupFile(const QString &filename); void BackupFile(const QString &filename);
static bool OpenDatabase(const QString &filename, sqlite3 **connection); static bool OpenDatabase(const QString &filename, sqlite3 **connection);
Application *app_; SharedPtr<TaskManager> task_manager_;
// Alias -> filename // Alias -> filename
QMap<QString, AttachedDatabase> attached_databases_; QMap<QString, AttachedDatabase> attached_databases_;
@ -130,16 +131,4 @@ class Database : public QObject {
}; };
class MemoryDatabase : public Database {
Q_OBJECT
public:
explicit MemoryDatabase(Application *app, QObject *parent = nullptr)
: Database(app, parent, QStringLiteral(":memory:")) {}
~MemoryDatabase() override {
// Make sure Qt doesn't reuse the same database
QSqlDatabase::removeDatabase(Connect().connectionName());
}
};
#endif // DATABASE_H #endif // DATABASE_H

View File

@ -28,7 +28,7 @@
#include <QUrl> #include <QUrl>
#include <QMetaObject> #include <QMetaObject>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "taskmanager.h" #include "taskmanager.h"
#include "song.h" #include "song.h"
#include "deletefiles.h" #include "deletefiles.h"

View File

@ -27,7 +27,7 @@
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "song.h" #include "song.h"
class QThread; class QThread;

View File

@ -30,11 +30,11 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QSettings> #include <QSettings>
#include "core/logging.h" #include "logging.h"
#include "settings.h" #include "settings.h"
#include "iconmapper.h" #include "includes/iconmapper.h"
#include "settings/appearancesettingspage.h"
#include "iconloader.h" #include "iconloader.h"
#include "constants/appearancesettings.h"
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
@ -45,7 +45,7 @@ void IconLoader::Init() {
#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN) #if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
Settings s; Settings s;
s.beginGroup(AppearanceSettingsPage::kSettingsGroup); s.beginGroup(AppearanceSettings::kSettingsGroup);
system_icons_ = s.value("system_icons", false).toBool(); system_icons_ = s.value("system_icons", false).toBool();
s.endGroup(); s.endGroup();
#endif #endif

View File

@ -45,15 +45,6 @@
#include "config.h" #include "config.h"
#include "platforminterface.h"
#include "mac_delegate.h"
#include "mac_startup.h"
#include "scoped_cftyperef.h"
#include "core/logging.h"
#include "scoped_nsautorelease_pool.h"
#include "globalshortcuts/globalshortcutsmanager.h"
#include "globalshortcuts/globalshortcutsbackend-macos.h"
#include <QApplication> #include <QApplication>
#include <QCoreApplication> #include <QCoreApplication>
#include <QWidget> #include <QWidget>
@ -61,7 +52,14 @@
#include <QEvent> #include <QEvent>
#include <QFile> #include <QFile>
#include <QtDebug> #include "includes/mac_delegate.h"
#include "includes/scoped_cftyperef.h"
#include "core/scoped_nsautorelease_pool.h"
#include "core/logging.h"
#include "core/platforminterface.h"
#include "mac_startup.h"
#include "globalshortcuts/globalshortcutsmanager.h"
#include "globalshortcuts/globalshortcutsbackend-macos.h"
QDebug operator<<(QDebug dbg, NSObject *object) { QDebug operator<<(QDebug dbg, NSObject *object) {

View File

@ -84,52 +84,46 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "shared_ptr.h" #include "constants/filefilterconstants.h"
#include "commandlineoptions.h" #include "constants/timeconstants.h"
#include "mimedata.h" #include "constants/mainwindowsettings.h"
#include "iconloader.h" #include "includes/shared_ptr.h"
#include "taskmanager.h" #include "core/commandlineoptions.h"
#include "song.h" #include "core/mimedata.h"
#include "stylehelper.h" #include "core/iconloader.h"
#include "stylesheetloader.h" #include "core/taskmanager.h"
#include "core/song.h"
#include "core/stylehelper.h"
#include "core/stylesheetloader.h"
#include "application.h" #include "application.h"
#include "database.h" #include "core/database.h"
#include "player.h" #include "core/filesystemmusicstorage.h"
#include "filesystemmusicstorage.h" #include "core/deletefiles.h"
#include "deletefiles.h" #include "core/settings.h"
#ifdef Q_OS_MACOS #include "core/player.h"
# include "mac_startup.h"
# include "macsystemtrayicon.h"
# include "utilities/macosutils.h"
#else
# include "qtsystemtrayicon.h"
#endif
#include "networkaccessmanager.h"
#include "settings.h"
#include "utilities/envutils.h" #include "utilities/envutils.h"
#include "utilities/filemanagerutils.h" #include "utilities/filemanagerutils.h"
#include "utilities/timeconstants.h"
#include "utilities/screenutils.h" #include "utilities/screenutils.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "dialogs/errordialog.h" #include "dialogs/errordialog.h"
#include "dialogs/about.h" #include "dialogs/about.h"
#include "dialogs/console.h" #include "dialogs/console.h"
#include "dialogs/trackselectiondialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/addstreamdialog.h" #include "dialogs/addstreamdialog.h"
#include "dialogs/deleteconfirmationdialog.h" #include "dialogs/deleteconfirmationdialog.h"
#include "dialogs/lastfmimportdialog.h" #include "dialogs/lastfmimportdialog.h"
#include "dialogs/snapdialog.h" #include "dialogs/snapdialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/trackselectiondialog.h"
#include "organize/organizedialog.h" #include "organize/organizedialog.h"
#include "widgets/fancytabwidget.h" #include "widgets/fancytabwidget.h"
#include "widgets/playingwidget.h" #include "widgets/playingwidget.h"
#include "widgets/volumeslider.h" #include "widgets/volumeslider.h"
#include "widgets/fileview.h"
#include "widgets/multiloadingindicator.h" #include "widgets/multiloadingindicator.h"
#include "widgets/trackslider.h" #include "widgets/trackslider.h"
#include "fileview/fileview.h"
#include "osd/osdbase.h" #include "osd/osdbase.h"
#include "context/contextview.h" #include "context/contextview.h"
#include "collection/collection.h" #include "collection/collectionlibrary.h"
#include "collection/collectionbackend.h" #include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h" #include "collection/collectiondirectorymodel.h"
#include "collection/collectionviewcontainer.h" #include "collection/collectionviewcontainer.h"
@ -160,30 +154,31 @@
#include "covermanager/coverproviders.h" #include "covermanager/coverproviders.h"
#include "covermanager/albumcoverimageresult.h" #include "covermanager/albumcoverimageresult.h"
#include "lyrics/lyricsproviders.h" #include "lyrics/lyricsproviders.h"
#include "device/devicemanager.h"
#include "device/devicestatefiltermodel.h"
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
# include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
# include "device/deviceview.h" # include "device/deviceview.h"
# include "device/deviceviewcontainer.h" # include "device/deviceviewcontainer.h"
#endif #endif
#include "transcoder/transcodedialog.h" #include "transcoder/transcodedialog.h"
#include "settings/settingsdialog.h" #include "settings/settingsdialog.h"
#include "settings/behavioursettingspage.h" #include "constants/behavioursettings.h"
#include "settings/backendsettingspage.h" #include "constants/appearancesettings.h"
#include "settings/collectionsettingspage.h" #include "constants/backendsettings.h"
#include "settings/playlistsettingspage.h" #include "constants/collectionsettings.h"
#include "constants/playlistsettings.h"
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
# include "settings/subsonicsettingspage.h" # include "constants/subsonicsettings.h"
#endif #endif
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
# include "tidal/tidalservice.h" # include "tidal/tidalservice.h"
# include "settings/tidalsettingspage.h" # include "constants/tidalsettings.h"
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
# include "settings/spotifysettingspage.h" # include "constants/spotifysettings.h"
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
# include "settings/qobuzsettingspage.h" # include "constants/qobuzsettings.h"
#endif #endif
#include "streaming/streamingservices.h" #include "streaming/streamingservices.h"
@ -205,6 +200,7 @@
#ifdef HAVE_MOODBAR #ifdef HAVE_MOODBAR
# include "moodbar/moodbarcontroller.h" # include "moodbar/moodbarcontroller.h"
# include "moodbar/moodbarloader.h"
# include "moodbar/moodbarproxystyle.h" # include "moodbar/moodbarproxystyle.h"
#endif #endif
@ -213,7 +209,15 @@
#include "organize/organizeerrordialog.h" #include "organize/organizeerrordialog.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
# include "windows7thumbbar.h" # include "core/windows7thumbbar.h"
#endif
#ifdef Q_OS_MACOS
# include "core/mac_startup.h"
# include "systemtrayicon/macsystemtrayicon.h"
# include "utilities/macosutils.h"
#else
# include "systemtrayicon/qtsystemtrayicon.h"
#endif #endif
#ifdef HAVE_QTSPARKLE #ifdef HAVE_QTSPARKLE
@ -225,9 +229,6 @@ using std::make_shared;
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
const char *MainWindow::kSettingsGroup = "MainWindow";
const char *MainWindow::kAllFilesFilterSpec = QT_TR_NOOP("All Files (*)");
namespace { namespace {
const int kTrackSliderUpdateTimeMs = 200; const int kTrackSliderUpdateTimeMs = 200;
const int kTrackPositionUpdateTimeMs = 1000; const int kTrackPositionUpdateTimeMs = 1000;
@ -258,8 +259,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
app_(app), app_(app),
tray_icon_(tray_icon), tray_icon_(tray_icon),
osd_(osd), osd_(osd),
console_([app]() { console_([app, this]() {
Console *console = new Console(app); Console *console = new Console(app->database());
QObject::connect(console, &Console::Error, this, &MainWindow::ShowErrorDialog);
return console; return console;
}), }),
edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)), edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)),
@ -277,7 +279,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
queue_view_(new QueueView(this)), queue_view_(new QueueView(this)),
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)), settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
cover_manager_([this, app]() { cover_manager_([this, app]() {
AlbumCoverManager *cover_manager = new AlbumCoverManager(app, app->collection_backend(), this); AlbumCoverManager *cover_manager = new AlbumCoverManager(app->network(), app->collection_backend(), app->tagreader_client(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services(), this);
cover_manager->Init(); cover_manager->Init();
// Cover manager connections // Cover manager connections
@ -287,7 +289,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
}), }),
equalizer_(new Equalizer), equalizer_(new Equalizer),
organize_dialog_([this, app]() { organize_dialog_([this, app]() {
OrganizeDialog *dialog = new OrganizeDialog(app->task_manager(), app->collection_backend(), this); OrganizeDialog *dialog = new OrganizeDialog(app->task_manager(), app->tagreader_client(), app->collection_backend(), this);
dialog->SetDestinationModel(app->collection()->model()->directory_model()); dialog->SetDestinationModel(app->collection()->model()->directory_model());
return dialog; return dialog;
}), }),
@ -300,18 +302,25 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(add_stream_dialog, &AddStreamDialog::accepted, this, &MainWindow::AddStreamAccepted); QObject::connect(add_stream_dialog, &AddStreamDialog::accepted, this, &MainWindow::AddStreamAccepted);
return add_stream_dialog; return add_stream_dialog;
}), }),
smartplaylists_view_(new SmartPlaylistsViewContainer(app, this)), smartplaylists_view_(new SmartPlaylistsViewContainer(app->player(),
app->playlist_manager(),
app->collection_backend(),
#ifdef HAVE_MOODBAR
app->moodbar_loader(),
#endif
app->current_albumcover_loader(),
this)),
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
subsonic_view_(new StreamingSongsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Subsonic), QLatin1String(SubsonicSettingsPage::kSettingsGroup), SettingsDialog::Page::Subsonic, this)), subsonic_view_(new StreamingSongsView(app->streaming_services()->ServiceBySource(Song::Source::Subsonic), QLatin1String(SubsonicSettings::kSettingsGroup), this)),
#endif #endif
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
tidal_view_(new StreamingTabsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Tidal), QLatin1String(TidalSettingsPage::kSettingsGroup), SettingsDialog::Page::Tidal, this)), tidal_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Tidal), app->albumcover_loader(), QLatin1String(TidalSettings::kSettingsGroup), this)),
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
spotify_view_(new StreamingTabsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Spotify), QLatin1String(SpotifySettingsPage::kSettingsGroup), SettingsDialog::Page::Spotify, this)), spotify_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Spotify), app->albumcover_loader(), QLatin1String(SpotifySettings::kSettingsGroup), this)),
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
qobuz_view_(new StreamingTabsView(app_, app->streaming_services()->ServiceBySource(Song::Source::Qobuz), QLatin1String(QobuzSettingsPage::kSettingsGroup), SettingsDialog::Page::Qobuz, this)), qobuz_view_(new StreamingTabsView(app->streaming_services()->ServiceBySource(Song::Source::Qobuz), app->albumcover_loader(), QLatin1String(QobuzSettings::kSettingsGroup), this)),
#endif #endif
radio_view_(new RadioViewContainer(this)), radio_view_(new RadioViewContainer(this)),
lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)), lastfm_import_dialog_(new LastFMImportDialog(app_->lastfm_import(), this)),
@ -345,10 +354,10 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
taskbar_progress_(false), taskbar_progress_(false),
#endif #endif
doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour::Append), doubleclick_addmode_(BehaviourSettings::AddBehaviour::Append),
doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour::Never), doubleclick_playmode_(BehaviourSettings::PlayBehaviour::Never),
doubleclick_playlist_addmode_(BehaviourSettingsPage::PlaylistAddBehaviour::Play), doubleclick_playlist_addmode_(BehaviourSettings::PlaylistAddBehaviour::Play),
menu_playmode_(BehaviourSettingsPage::PlayBehaviour::Never), menu_playmode_(BehaviourSettings::PlayBehaviour::Never),
initialized_(false), initialized_(false),
was_maximized_(true), was_maximized_(true),
was_minimized_(false), was_minimized_(false),
@ -358,19 +367,18 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
qLog(Debug) << "Starting"; qLog(Debug) << "Starting";
QObject::connect(app, &Application::ErrorAdded, this, &MainWindow::ShowErrorDialog);
QObject::connect(app, &Application::SettingsDialogRequested, this, &MainWindow::OpenSettingsDialogAtPage);
// Initialize the UI // Initialize the UI
ui_->setupUi(this); ui_->setupUi(this);
setWindowIcon(IconLoader::Load(u"strawberry"_s)); setWindowIcon(IconLoader::Load(u"strawberry"_s));
album_cover_choice_controller_->Init(app); QObject::connect(&*app->database(), &Database::Error, this, &MainWindow::ShowErrorDialog);
album_cover_choice_controller_->Init(app->network(), app->tagreader_client(), app->collection()->backend(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->streaming_services());
ui_->multi_loading_indicator->SetTaskManager(app_->task_manager()); ui_->multi_loading_indicator->SetTaskManager(app_->task_manager());
context_view_->Init(app_, collection_view_->view(), album_cover_choice_controller_); context_view_->Init(collection_view_->view(), album_cover_choice_controller_, app_->lyrics_providers());
ui_->widget_playing->Init(app_, album_cover_choice_controller_); ui_->widget_playing->Init(album_cover_choice_controller_);
// Initialize the search widget // Initialize the search widget
StyleHelper::setBaseColor(palette().color(QPalette::Highlight).darker()); StyleHelper::setBaseColor(palette().color(QPalette::Highlight).darker());
@ -402,7 +410,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
// Add the playing widget to the fancy tab widget // Add the playing widget to the fancy tab widget
ui_->tabs->AddBottomWidget(ui_->widget_playing); ui_->tabs->AddBottomWidget(ui_->widget_playing);
ui_->tabs->SetBackgroundPixmap(QPixmap(u":/pictures/sidebar-background.png"_s)); ui_->tabs->SetBackgroundPixmap(QPixmap(u":/pictures/sidebar-background.png"_s));
ui_->tabs->LoadSettings(QLatin1String(kSettingsGroup)); ui_->tabs->LoadSettings(QLatin1String(MainWindowSettings::kSettingsGroup));
track_position_timer_->setInterval(kTrackPositionUpdateTimeMs); track_position_timer_->setInterval(kTrackPositionUpdateTimeMs);
QObject::connect(track_position_timer_, &QTimer::timeout, this, &MainWindow::UpdateTrackPosition); QObject::connect(track_position_timer_, &QTimer::timeout, this, &MainWindow::UpdateTrackPosition);
@ -423,14 +431,20 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
ui_->playlist->SetManager(app_->playlist_manager()); ui_->playlist->SetManager(app_->playlist_manager());
ui_->playlist->view()->Init(app_); ui_->playlist->view()->Init(app_->player(),
app_->playlist_manager(),
app_->collection_backend(),
#ifdef HAVE_MOODBAR
app_->moodbar_loader(),
#endif
app_->current_albumcover_loader());
collection_view_->view()->setModel(app_->collection()->model()->filter()); collection_view_->view()->setModel(app_->collection()->model()->filter());
collection_view_->view()->SetApplication(app_); collection_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->network(), app->albumcover_loader(), app->current_albumcover_loader(), app->cover_providers(), app->lyrics_providers(), app->collection(), app->device_manager(), app->streaming_services());
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
device_view_->view()->SetApplication(app_); device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
#endif #endif
playlist_list_->SetApplication(app_); playlist_list_->Init(app_->task_manager(), app->tagreader_client(), app_->playlist_manager(), app_->playlist_backend(), app_->device_manager());
organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model()); organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model());
@ -541,9 +555,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->action_equalizer, &QAction::triggered, this, &MainWindow::ShowEqualizer); QObject::connect(ui_->action_equalizer, &QAction::triggered, this, &MainWindow::ShowEqualizer);
QObject::connect(ui_->action_transcoder, &QAction::triggered, this, &MainWindow::ShowTranscodeDialog); QObject::connect(ui_->action_transcoder, &QAction::triggered, this, &MainWindow::ShowTranscodeDialog);
QObject::connect(ui_->action_jump, &QAction::triggered, ui_->playlist->view(), &PlaylistView::JumpToCurrentlyPlayingTrack); QObject::connect(ui_->action_jump, &QAction::triggered, ui_->playlist->view(), &PlaylistView::JumpToCurrentlyPlayingTrack);
QObject::connect(ui_->action_update_collection, &QAction::triggered, &*app_->collection(), &SCollection::IncrementalScan); QObject::connect(ui_->action_update_collection, &QAction::triggered, &*app_->collection(), &CollectionLibrary::IncrementalScan);
QObject::connect(ui_->action_full_collection_scan, &QAction::triggered, &*app_->collection(), &SCollection::FullScan); QObject::connect(ui_->action_full_collection_scan, &QAction::triggered, &*app_->collection(), &CollectionLibrary::FullScan);
QObject::connect(ui_->action_stop_collection_scan, &QAction::triggered, &*app_->collection(), &SCollection::StopScan); QObject::connect(ui_->action_stop_collection_scan, &QAction::triggered, &*app_->collection(), &CollectionLibrary::StopScan);
QObject::connect(ui_->action_add_files_to_transcoder, &QAction::triggered, this, &MainWindow::AddFilesToTranscoder); QObject::connect(ui_->action_add_files_to_transcoder, &QAction::triggered, this, &MainWindow::AddFilesToTranscoder);
ui_->action_add_files_to_transcoder->setIcon(IconLoader::Load(u"tools-wizard"_s)); ui_->action_add_files_to_transcoder->setIcon(IconLoader::Load(u"tools-wizard"_s));
@ -597,6 +611,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(&*app_->player(), &Player::Stopped, ui_->playlist, &PlaylistContainer::ActiveStopped); QObject::connect(&*app_->player(), &Player::Stopped, ui_->playlist, &PlaylistContainer::ActiveStopped);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, osd_, &OSDBase::SongChanged); QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, osd_, &OSDBase::SongChanged);
QObject::connect(&*app_->player(), &Player::Paused, osd_, &OSDBase::Paused); QObject::connect(&*app_->player(), &Player::Paused, osd_, &OSDBase::Paused);
QObject::connect(&*app_->player(), &Player::Resumed, osd_, &OSDBase::Resumed); QObject::connect(&*app_->player(), &Player::Resumed, osd_, &OSDBase::Resumed);
QObject::connect(&*app_->player(), &Player::Stopped, osd_, &OSDBase::Stopped); QObject::connect(&*app_->player(), &Player::Stopped, osd_, &OSDBase::Stopped);
@ -605,6 +620,16 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(&*app_->player(), &Player::VolumeChanged, ui_->volume, &VolumeSlider::SetValue); QObject::connect(&*app_->player(), &Player::VolumeChanged, ui_->volume, &VolumeSlider::SetValue);
QObject::connect(&*app_->player(), &Player::ForceShowOSD, this, &MainWindow::ForceShowOSD); QObject::connect(&*app_->player(), &Player::ForceShowOSD, this, &MainWindow::ForceShowOSD);
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::ThumbnailLoaded, osd_, &OSDBase::AlbumCoverLoaded);
QObject::connect(&*app_->player(), &Player::Paused, &*app_->playlist_manager(), &PlaylistManager::SetActivePaused);
QObject::connect(&*app_->player(), &Player::Playing, &*app_->playlist_manager(), &PlaylistManager::SetActivePlaying);
QObject::connect(&*app_->player(), &Player::Stopped, &*app_->playlist_manager(), &PlaylistManager::SetActiveStopped);
QObject::connect(&*app_->player(), &Player::Paused, playlist_list_, &PlaylistListContainer::ActivePaused);
QObject::connect(&*app_->player(), &Player::Playing, playlist_list_, &PlaylistListContainer::ActivePlaying);
QObject::connect(&*app_->player(), &Player::Stopped, playlist_list_, &PlaylistListContainer::ActiveStopped);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::AllPlaylistsLoaded, &*app->player(), &Player::PlaylistsLoaded); QObject::connect(&*app_->playlist_manager(), &PlaylistManager::AllPlaylistsLoaded, &*app->player(), &Player::PlaylistsLoaded);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, this, &MainWindow::SongChanged); QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, this, &MainWindow::SongChanged);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->player(), &Player::CurrentMetadataChanged); QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->player(), &Player::CurrentMetadataChanged);
@ -628,9 +653,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->track_slider, &TrackSlider::Next, &*app_->player(), &Player::Next); QObject::connect(ui_->track_slider, &TrackSlider::Next, &*app_->player(), &Player::Next);
// Collection connections // Collection connections
QObject::connect(&*app_->collection(), &SCollection::Error, this, &MainWindow::ShowErrorDialog); QObject::connect(&*app_->collection(), &CollectionLibrary::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(collection_view_->view(), &CollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(collection_view_->view(), &CollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(collection_view_->view(), &CollectionView::ShowConfigDialog, this, &MainWindow::ShowCollectionConfig); QObject::connect(collection_view_->view(), &CollectionView::ShowSettingsDialog, this, &MainWindow::OpenCollectionSettingsDialog);
QObject::connect(collection_view_->view(), &CollectionView::Error, this, &MainWindow::ShowErrorDialog); QObject::connect(collection_view_->view(), &CollectionView::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(app_->collection_model(), &CollectionModel::TotalSongCountUpdated, collection_view_->view(), &CollectionView::TotalSongCountUpdated); QObject::connect(app_->collection_model(), &CollectionModel::TotalSongCountUpdated, collection_view_->view(), &CollectionView::TotalSongCountUpdated);
QObject::connect(app_->collection_model(), &CollectionModel::TotalArtistCountUpdated, collection_view_->view(), &CollectionView::TotalArtistCountUpdated); QObject::connect(app_->collection_model(), &CollectionModel::TotalArtistCountUpdated, collection_view_->view(), &CollectionView::TotalArtistCountUpdated);
@ -638,9 +663,10 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(app_->collection_model(), &CollectionModel::modelAboutToBeReset, collection_view_->view(), &CollectionView::SaveFocus); QObject::connect(app_->collection_model(), &CollectionModel::modelAboutToBeReset, collection_view_->view(), &CollectionView::SaveFocus);
QObject::connect(app_->collection_model(), &CollectionModel::modelReset, collection_view_->view(), &CollectionView::RestoreFocus); QObject::connect(app_->collection_model(), &CollectionModel::modelReset, collection_view_->view(), &CollectionView::RestoreFocus);
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &SCollection::PauseWatcher); QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &SCollection::ResumeWatcher); QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &CollectionLibrary::ResumeWatcher);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::LoadAlbumCover);
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded); QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded);
QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog); QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog);
QObject::connect(album_cover_choice_controller_->cover_from_file_action(), &QAction::triggered, this, &MainWindow::LoadCoverFromFile); QObject::connect(album_cover_choice_controller_->cover_from_file_action(), &QAction::triggered, this, &MainWindow::LoadCoverFromFile);
@ -674,8 +700,8 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(collection_view_group, &QActionGroup::triggered, this, &MainWindow::ChangeCollectionFilterMode); QObject::connect(collection_view_group, &QActionGroup::triggered, this, &MainWindow::ChangeCollectionFilterMode);
QAction *collection_config_action = new QAction(IconLoader::Load(u"configure"_s), tr("Configure collection..."), this); QAction *collection_config_action = new QAction(IconLoader::Load(u"configure"_s), tr("Configure collection..."), this);
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::ShowCollectionConfig); QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::OpenCollectionSettingsDialog);
collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettingsPage::kSettingsGroup)); collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettings::kSettingsGroup));
collection_view_->filter_widget()->Init(app_->collection()->model(), app_->collection()->model()->filter()); collection_view_->filter_widget()->Init(app_->collection()->model(), app_->collection()->model()->filter());
QAction *separator = new QAction(this); QAction *separator = new QAction(this);
@ -688,13 +714,16 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
collection_view_->filter_widget()->AddMenuAction(collection_config_action); collection_view_->filter_widget()->AddMenuAction(collection_config_action);
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
QObject::connect(subsonic_view_, &StreamingSongsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(subsonic_view_->view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(subsonic_view_->view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
#endif #endif
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
QObject::connect(tidal_view_, &StreamingTabsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(tidal_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(tidal_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(tidal_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(tidal_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(tidal_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(tidal_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(tidal_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(tidal_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist); QObject::connect(tidal_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
if (TidalServicePtr tidalservice = app_->streaming_services()->Service<TidalService>()) { if (TidalServicePtr tidalservice = app_->streaming_services()->Service<TidalService>()) {
QObject::connect(this, &MainWindow::AuthorizationUrlReceived, &*tidalservice, &TidalService::AuthorizationUrlReceived); QObject::connect(this, &MainWindow::AuthorizationUrlReceived, &*tidalservice, &TidalService::AuthorizationUrlReceived);
@ -702,16 +731,20 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
QObject::connect(qobuz_view_, &StreamingTabsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(qobuz_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(qobuz_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(qobuz_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(qobuz_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(qobuz_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(qobuz_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(qobuz_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(qobuz_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist); QObject::connect(qobuz_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
QObject::connect(spotify_view_, &StreamingTabsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(spotify_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(spotify_view_->artists_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(spotify_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(spotify_view_->albums_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(spotify_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist); QObject::connect(spotify_view_->songs_collection_view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(spotify_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist); QObject::connect(spotify_view_->search_view(), &StreamingSearchView::AddToPlaylist, this, &MainWindow::AddToPlaylist);
#endif #endif
@ -773,14 +806,15 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->playlist, &PlaylistContainer::UndoRedoActionsChanged, this, &MainWindow::PlaylistUndoRedoChanged); QObject::connect(ui_->playlist, &PlaylistContainer::UndoRedoActionsChanged, this, &MainWindow::PlaylistUndoRedoChanged);
#ifndef Q_OS_WIN QObject::connect(&*app_->device_manager(), &DeviceManager::DeviceError, this, &MainWindow::ShowErrorDialog);
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0); #ifndef WIN32
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, playlist_copy_to_device_, &QAction::setDisabled); QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, playlist_copy_to_device_, &QAction::setDisabled);
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
#endif #endif
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged); QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged); QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::LoveButtonVisibilityChanged, this, &MainWindow::LoveButtonVisibilityChanged); QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::LoveButtonVisibilityChanged, this, &MainWindow::LoveButtonVisibilityChanged);
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
mac::SetApplicationHandler(this); mac::SetApplicationHandler(this);
@ -849,11 +883,14 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
ui_->status_bar_stack->setCurrentWidget(ui_->playlist_summary_page); ui_->status_bar_stack->setCurrentWidget(ui_->playlist_summary_page);
QObject::connect(ui_->multi_loading_indicator, &MultiLoadingIndicator::TaskCountChange, this, &MainWindow::TaskCountChanged); QObject::connect(ui_->multi_loading_indicator, &MultiLoadingIndicator::TaskCountChange, this, &MainWindow::TaskCountChanged);
ui_->track_slider->SetApplication(app); ui_->track_slider->Init();
#ifdef HAVE_MOODBAR #ifdef HAVE_MOODBAR
// Moodbar connections // Moodbar connections
QObject::connect(&*app_->moodbar_controller(), &MoodbarController::CurrentMoodbarDataChanged, ui_->track_slider->moodbar_style(), &MoodbarProxyStyle::SetMoodbarData); QObject::connect(&*app_->moodbar_controller(), &MoodbarController::CurrentMoodbarDataChanged, ui_->track_slider->moodbar_proxy_style(), &MoodbarProxyStyle::SetMoodbarData);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->moodbar_controller(), &MoodbarController::CurrentSongChanged);
QObject::connect(&*app_->player(), &Player::Stopped, &*app_->moodbar_controller(), &MoodbarController::PlaybackStopped);
QObject::connect(ui_->track_slider->moodbar_proxy_style(), &MoodbarProxyStyle::StyleChanged, &*app_->moodbar_loader(), &MoodbarLoader::StyleChanged);
#endif #endif
// Playing widget // Playing widget
@ -875,7 +912,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
css_loader->SetStyleSheet(this, u":/style/strawberry.css"_s); css_loader->SetStyleSheet(this, u":/style/strawberry.css"_s);
// Load playlists // Load playlists
app_->playlist_manager()->Init(app_->collection_backend(), app_->playlist_backend(), ui_->playlist_sequence, ui_->playlist); app_->playlist_manager()->Init(ui_->playlist_sequence, ui_->playlist);
queue_view_->SetPlaylistManager(app_->playlist_manager()); queue_view_->SetPlaylistManager(app_->playlist_manager());
@ -902,10 +939,12 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(&*app_->lastfm_import(), &LastFMImport::FinishedWithError, lastfm_import_dialog_, &LastFMImportDialog::FinishedWithError); QObject::connect(&*app_->lastfm_import(), &LastFMImport::FinishedWithError, lastfm_import_dialog_, &LastFMImportDialog::FinishedWithError);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateTotal, lastfm_import_dialog_, &LastFMImportDialog::UpdateTotal); QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateTotal, lastfm_import_dialog_, &LastFMImportDialog::UpdateTotal);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateProgress, lastfm_import_dialog_, &LastFMImportDialog::UpdateProgress); QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateProgress, lastfm_import_dialog_, &LastFMImportDialog::UpdateProgress);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, &*app_->collection_backend(), &CollectionBackend::UpdateLastPlayed);
QObject::connect(&*app_->lastfm_import(), &LastFMImport::UpdatePlayCount, &*app_->collection_backend(), &CollectionBackend::UpdatePlayCount);
// Load settings // Load settings
qLog(Debug) << "Loading settings"; qLog(Debug) << "Loading settings";
settings_.beginGroup(kSettingsGroup); settings_.beginGroup(MainWindowSettings::kSettingsGroup);
// Set last used geometry to position window on the correct monitor // Set last used geometry to position window on the correct monitor
// Set window state only if the window was last maximized // Set window state only if the window was last maximized
@ -913,7 +952,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
restoreGeometry(settings_.value("geometry").toByteArray()); restoreGeometry(settings_.value("geometry").toByteArray());
} }
if (!settings_.contains("splitter_state") || !ui_->splitter->restoreState(settings_.value("splitter_state").toByteArray())) { if (!settings_.contains(MainWindowSettings::kSplitterState) || !ui_->splitter->restoreState(settings_.value(MainWindowSettings::kSplitterState).toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20)); ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20));
} }
@ -941,40 +980,40 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef Q_OS_MACOS // Always show the mainwindow on startup for macOS #ifdef Q_OS_MACOS // Always show the mainwindow on startup for macOS
show(); show();
#else #else
BehaviourSettingsPage::StartupBehaviour startupbehaviour = BehaviourSettingsPage::StartupBehaviour::Remember; BehaviourSettings::StartupBehaviour startupbehaviour = BehaviourSettings::StartupBehaviour::Remember;
{ {
Settings s; Settings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup); s.beginGroup(BehaviourSettings::kSettingsGroup);
startupbehaviour = static_cast<BehaviourSettingsPage::StartupBehaviour>(s.value("startupbehaviour", static_cast<int>(BehaviourSettingsPage::StartupBehaviour::Remember)).toInt()); startupbehaviour = static_cast<BehaviourSettings::StartupBehaviour>(s.value(BehaviourSettings::kStartupBehaviour, static_cast<int>(BehaviourSettings::StartupBehaviour::Remember)).toInt());
s.endGroup(); s.endGroup();
} }
switch (startupbehaviour) { switch (startupbehaviour) {
case BehaviourSettingsPage::StartupBehaviour::Show: case BehaviourSettings::StartupBehaviour::Show:
show(); show();
break; break;
case BehaviourSettingsPage::StartupBehaviour::ShowMaximized: case BehaviourSettings::StartupBehaviour::ShowMaximized:
setWindowState(windowState() | Qt::WindowMaximized); setWindowState(windowState() | Qt::WindowMaximized);
show(); show();
break; break;
case BehaviourSettingsPage::StartupBehaviour::ShowMinimized: case BehaviourSettings::StartupBehaviour::ShowMinimized:
setWindowState(windowState() | Qt::WindowMinimized); setWindowState(windowState() | Qt::WindowMinimized);
show(); show();
break; break;
case BehaviourSettingsPage::StartupBehaviour::Hide: case BehaviourSettings::StartupBehaviour::Hide:
if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible()) { if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible()) {
break; break;
} }
[[fallthrough]]; [[fallthrough]];
case BehaviourSettingsPage::StartupBehaviour::Remember: case BehaviourSettings::StartupBehaviour::Remember:
default:{ default:{
was_maximized_ = settings_.value("maximized", true).toBool(); was_maximized_ = settings_.value(MainWindowSettings::kMaximized, true).toBool();
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized); if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
was_minimized_ = settings_.value("minimized", false).toBool(); was_minimized_ = settings_.value(MainWindowSettings::kMinimized, false).toBool();
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized); if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
if (!tray_icon_->IsSystemTrayAvailable() || !tray_icon_->isVisible() || !settings_.value("hidden", false).toBool()) { if (!tray_icon_->IsSystemTrayAvailable() || !tray_icon_->isVisible() || !settings_.value(MainWindowSettings::kHidden, false).toBool()) {
show(); show();
} }
break; break;
@ -982,7 +1021,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
} }
#endif #endif
bool show_sidebar = settings_.value("show_sidebar", true).toBool(); bool show_sidebar = settings_.value(MainWindowSettings::kShowSidebar, true).toBool();
ui_->sidebar_layout->setVisible(show_sidebar); ui_->sidebar_layout->setVisible(show_sidebar);
ui_->action_toggle_show_sidebar->setChecked(show_sidebar); ui_->action_toggle_show_sidebar->setChecked(show_sidebar);
@ -1016,7 +1055,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
if (!Utilities::GetEnv(u"SNAP"_s).isEmpty() && !Utilities::GetEnv(u"SNAP_NAME"_s).isEmpty()) { if (!Utilities::GetEnv(u"SNAP"_s).isEmpty() && !Utilities::GetEnv(u"SNAP_NAME"_s).isEmpty()) {
Settings s; Settings s;
s.beginGroup(kSettingsGroup); s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool ignore_snap = s.value("ignore_snap", false).toBool(); const bool ignore_snap = s.value("ignore_snap", false).toBool();
s.endGroup(); s.endGroup();
if (!ignore_snap) { if (!ignore_snap) {
@ -1030,12 +1069,12 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#if defined(Q_OS_MACOS) #if defined(Q_OS_MACOS)
if (Utilities::ProcessTranslated()) { if (Utilities::ProcessTranslated()) {
Settings s; Settings s;
s.beginGroup(kSettingsGroup); s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool ignore_rosetta = s.value("ignore_rosetta", false).toBool(); const bool ignore_rosetta = s.value("ignore_rosetta", false).toBool();
s.endGroup(); s.endGroup();
if (!ignore_rosetta) { if (!ignore_rosetta) {
MessageDialog *rosetta_message = new MessageDialog(this); MessageDialog *rosetta_message = new MessageDialog(this);
rosetta_message->set_settings_group(QLatin1String(kSettingsGroup)); rosetta_message->set_settings_group(QLatin1String(MainWindowSettings::kSettingsGroup));
rosetta_message->set_do_not_show_message_again(u"ignore_rosetta"_s); rosetta_message->set_do_not_show_message_again(u"ignore_rosetta"_s);
rosetta_message->setAttribute(Qt::WA_DeleteOnClose); rosetta_message->setAttribute(Qt::WA_DeleteOnClose);
rosetta_message->ShowMessage(tr("Strawberry running under Rosetta"), tr("You are running Strawberry under Rosetta. Running Strawberry under Rosetta is unsupported and known to have issues. You should download Strawberry for the correct CPU architecture from %1").arg(QLatin1String("<a href=\"https://downloads.strawberrymusicplayer.org/\">downloads.strawberrymusicplayer.org</a>")), IconLoader::Load(u"dialog-warning"_s)); rosetta_message->ShowMessage(tr("Strawberry running under Rosetta"), tr("You are running Strawberry under Rosetta. Running Strawberry under Rosetta is unsupported and known to have issues. You should download Strawberry for the correct CPU architecture from %1").arg(QLatin1String("<a href=\"https://downloads.strawberrymusicplayer.org/\">downloads.strawberrymusicplayer.org</a>")), IconLoader::Load(u"dialog-warning"_s));
@ -1052,14 +1091,13 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
s.endGroup(); s.endGroup();
#endif #endif
if (asked_permission) { if (asked_permission) {
s.beginGroup(kSettingsGroup); s.beginGroup(MainWindowSettings::kSettingsGroup);
constexpr char do_not_show_sponsor_message_key[] = "do_not_show_sponsor_message"; const bool do_not_show_sponsor_message = s.value(MainWindowSettings::kDoNotShowSponsorMessage, false).toBool();
const bool do_not_show_sponsor_message = s.value(do_not_show_sponsor_message_key, false).toBool();
s.endGroup(); s.endGroup();
if (!do_not_show_sponsor_message) { if (!do_not_show_sponsor_message) {
MessageDialog *sponsor_message = new MessageDialog(this); MessageDialog *sponsor_message = new MessageDialog(this);
sponsor_message->set_settings_group(QLatin1String(kSettingsGroup)); sponsor_message->set_settings_group(QLatin1String(MainWindowSettings::kSettingsGroup));
sponsor_message->set_do_not_show_message_again(QLatin1String(do_not_show_sponsor_message_key)); sponsor_message->set_do_not_show_message_again(QLatin1String(MainWindowSettings::kDoNotShowSponsorMessage));
sponsor_message->setAttribute(Qt::WA_DeleteOnClose); sponsor_message->setAttribute(Qt::WA_DeleteOnClose);
sponsor_message->ShowMessage(tr("Sponsoring Strawberry"), tr("Strawberry is free and open source software. If you like Strawberry, please consider sponsoring the project. For more information about sponsorship see our website %1").arg(u"<a href= \"https://www.strawberrymusicplayer.org/\">www.strawberrymusicplayer.org</a>"_s), IconLoader::Load(u"dialog-information"_s)); sponsor_message->ShowMessage(tr("Sponsoring Strawberry"), tr("Strawberry is free and open source software. If you like Strawberry, please consider sponsoring the project. For more information about sponsorship see our website %1").arg(u"<a href= \"https://www.strawberrymusicplayer.org/\">www.strawberrymusicplayer.org</a>"_s), IconLoader::Load(u"dialog-information"_s));
} }
@ -1083,8 +1121,8 @@ void MainWindow::ReloadSettings() {
constexpr bool keeprunning_available = true; constexpr bool keeprunning_available = true;
#else #else
const bool systemtray_available = tray_icon_->IsSystemTrayAvailable(); const bool systemtray_available = tray_icon_->IsSystemTrayAvailable();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup); s.beginGroup(BehaviourSettings::kSettingsGroup);
const bool showtrayicon = s.value("showtrayicon", systemtray_available).toBool(); const bool showtrayicon = s.value(BehaviourSettings::kShowTrayIcon, systemtray_available).toBool();
s.endGroup(); s.endGroup();
const bool keeprunning_available = systemtray_available && showtrayicon; const bool keeprunning_available = systemtray_available && showtrayicon;
if (systemtray_available) { if (systemtray_available) {
@ -1095,22 +1133,22 @@ void MainWindow::ReloadSettings() {
} }
#endif #endif
s.beginGroup(BehaviourSettingsPage::kSettingsGroup); s.beginGroup(BehaviourSettings::kSettingsGroup);
keep_running_ = keeprunning_available && s.value("keeprunning", false).toBool(); keep_running_ = keeprunning_available && s.value(BehaviourSettings::kKeepRunning, false).toBool();
playing_widget_ = s.value("playing_widget", true).toBool(); playing_widget_ = s.value(BehaviourSettings::kPlayingWidget, true).toBool();
bool trayicon_progress = s.value("trayicon_progress", false).toBool(); bool trayicon_progress = s.value(BehaviourSettings::kTrayIconProgress, false).toBool();
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
const bool taskbar_progress = s.value("taskbar_progress", true).toBool(); const bool taskbar_progress = s.value(BehaviourSettings::kTaskbarProgress, true).toBool();
#endif #endif
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched(); if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
doubleclick_addmode_ = static_cast<BehaviourSettingsPage::AddBehaviour>(s.value("doubleclick_addmode", static_cast<int>(BehaviourSettingsPage::AddBehaviour::Append)).toInt()); doubleclick_addmode_ = static_cast<BehaviourSettings::AddBehaviour>(s.value(BehaviourSettings::kDoubleClickAddMode, static_cast<int>(BehaviourSettings::AddBehaviour::Append)).toInt());
doubleclick_playmode_ = static_cast<BehaviourSettingsPage::PlayBehaviour>(s.value("doubleclick_playmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt()); doubleclick_playmode_ = static_cast<BehaviourSettings::PlayBehaviour>(s.value(BehaviourSettings::kDoubleClickPlayMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
doubleclick_playlist_addmode_ = static_cast<BehaviourSettingsPage::PlaylistAddBehaviour>(s.value("doubleclick_playlist_addmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt()); doubleclick_playlist_addmode_ = static_cast<BehaviourSettings::PlaylistAddBehaviour>(s.value(BehaviourSettings::kDoubleClickPlaylistAddMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
menu_playmode_ = static_cast<BehaviourSettingsPage::PlayBehaviour>(s.value("menu_playmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt()); menu_playmode_ = static_cast<BehaviourSettings::PlayBehaviour>(s.value(BehaviourSettings::kMenuPlayMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
s.endGroup(); s.endGroup();
s.beginGroup(AppearanceSettingsPage::kSettingsGroup); s.beginGroup(AppearanceSettings::kSettingsGroup);
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlayControlButtons, 32).toInt(); int iconsize = s.value(AppearanceSettings::kIconSizePlayControlButtons, 32).toInt();
s.endGroup(); s.endGroup();
tray_icon_->SetTrayiconProgress(trayicon_progress); tray_icon_->SetTrayiconProgress(trayicon_progress);
@ -1128,7 +1166,7 @@ void MainWindow::ReloadSettings() {
ui_->forward_button->setIconSize(QSize(iconsize, iconsize)); ui_->forward_button->setIconSize(QSize(iconsize, iconsize));
ui_->button_love->setIconSize(QSize(iconsize, iconsize)); ui_->button_love->setIconSize(QSize(iconsize, iconsize));
s.beginGroup(BackendSettingsPage::kSettingsGroup); s.beginGroup(BackendSettings::kSettingsGroup);
bool volume_control = s.value("volume_control", true).toBool(); bool volume_control = s.value("volume_control", true).toBool();
s.endGroup(); s.endGroup();
if (volume_control != ui_->volume->isEnabled()) { if (volume_control != ui_->volume->isEnabled()) {
@ -1143,17 +1181,17 @@ void MainWindow::ReloadSettings() {
} }
} }
s.beginGroup(PlaylistSettingsPage::kSettingsGroup); s.beginGroup(PlaylistSettings::kSettingsGroup);
delete_files_ = s.value("delete_files", false).toBool(); delete_files_ = s.value(PlaylistSettings::kDeleteFiles, false).toBool();
s.endGroup(); s.endGroup();
osd_->ReloadSettings(); osd_->ReloadSettings();
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value("search_for_cover_auto", true).toBool()); album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value(MainWindowSettings::kSearchForCoverAuto, true).toBool());
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
s.beginGroup(SubsonicSettingsPage::kSettingsGroup); s.beginGroup(SubsonicSettings::kSettingsGroup);
bool enable_subsonic = s.value("enabled", false).toBool(); bool enable_subsonic = s.value(SubsonicSettings::kEnabled, false).toBool();
s.endGroup(); s.endGroup();
if (enable_subsonic) { if (enable_subsonic) {
ui_->tabs->EnableTab(subsonic_view_); ui_->tabs->EnableTab(subsonic_view_);
@ -1164,8 +1202,8 @@ void MainWindow::ReloadSettings() {
#endif #endif
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
s.beginGroup(TidalSettingsPage::kSettingsGroup); s.beginGroup(TidalSettings::kSettingsGroup);
bool enable_tidal = s.value("enabled", false).toBool(); bool enable_tidal = s.value(TidalSettings::kEnabled, false).toBool();
s.endGroup(); s.endGroup();
if (enable_tidal) { if (enable_tidal) {
ui_->tabs->EnableTab(tidal_view_); ui_->tabs->EnableTab(tidal_view_);
@ -1176,8 +1214,8 @@ void MainWindow::ReloadSettings() {
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
s.beginGroup(SpotifySettingsPage::kSettingsGroup); s.beginGroup(SpotifySettings::kSettingsGroup);
bool enable_spotify = s.value("enabled", false).toBool(); bool enable_spotify = s.value(SpotifySettings::kEnabled, false).toBool();
s.endGroup(); s.endGroup();
if (enable_spotify) { if (enable_spotify) {
ui_->tabs->EnableTab(spotify_view_); ui_->tabs->EnableTab(spotify_view_);
@ -1188,8 +1226,8 @@ void MainWindow::ReloadSettings() {
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
s.beginGroup(QobuzSettingsPage::kSettingsGroup); s.beginGroup(QobuzSettings::kSettingsGroup);
bool enable_qobuz = s.value("enabled", false).toBool(); bool enable_qobuz = s.value(QobuzSettings::kEnabled, false).toBool();
s.endGroup(); s.endGroup();
if (enable_qobuz) { if (enable_qobuz) {
ui_->tabs->EnableTab(qobuz_view_); ui_->tabs->EnableTab(qobuz_view_);
@ -1208,7 +1246,6 @@ void MainWindow::ReloadAllSettings() {
ReloadSettings(); ReloadSettings();
// Other settings // Other settings
app_->ReloadSettings();
app_->collection()->ReloadSettings(); app_->collection()->ReloadSettings();
app_->player()->ReloadSettings(); app_->player()->ReloadSettings();
collection_view_->ReloadSettings(); collection_view_->ReloadSettings();
@ -1228,18 +1265,23 @@ void MainWindow::ReloadAllSettings() {
app_->lyrics_providers()->ReloadSettings(); app_->lyrics_providers()->ReloadSettings();
#ifdef HAVE_MOODBAR #ifdef HAVE_MOODBAR
app_->moodbar_controller()->ReloadSettings(); app_->moodbar_controller()->ReloadSettings();
app_->moodbar_loader()->ReloadSettings();
ui_->track_slider->moodbar_proxy_style()->ReloadSettings();
#endif #endif
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
subsonic_view_->ReloadSettings(); subsonic_view_->ReloadSettings();
#endif #endif
#ifdef HAVE_TIDAL #ifdef HAVE_TIDAL
tidal_view_->ReloadSettings(); tidal_view_->ReloadSettings();
tidal_view_->search_view()->ReloadSettings();
#endif #endif
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
spotify_view_->ReloadSettings(); spotify_view_->ReloadSettings();
spotify_view_->search_view()->ReloadSettings();
#endif #endif
#ifdef HAVE_QOBUZ #ifdef HAVE_QOBUZ
qobuz_view_->ReloadSettings(); qobuz_view_->ReloadSettings();
qobuz_view_->search_view()->ReloadSettings();
#endif #endif
} }
@ -1255,12 +1297,12 @@ void MainWindow::SaveSettings() {
SaveGeometry(); SaveGeometry();
app_->player()->SaveVolume(); app_->player()->SaveVolume();
app_->player()->SavePlaybackStatus(); app_->player()->SavePlaybackStatus();
ui_->tabs->SaveSettings(QLatin1String(kSettingsGroup)); ui_->tabs->SaveSettings(QLatin1String(MainWindowSettings::kSettingsGroup));
ui_->playlist->view()->SaveSettings(); ui_->playlist->view()->SaveSettings();
app_->scrobbler()->WriteCache(); app_->scrobbler()->WriteCache();
settings_.setValue("show_sidebar", ui_->action_toggle_show_sidebar->isChecked()); settings_.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->isChecked());
settings_.setValue("search_for_cover_auto", album_cover_choice_controller_->search_cover_auto_action()->isChecked()); settings_.setValue(MainWindowSettings::kSearchForCoverAuto, album_cover_choice_controller_->search_cover_auto_action()->isChecked());
} }
@ -1500,23 +1542,23 @@ void MainWindow::ToggleSidebar(const bool checked) {
ui_->sidebar_layout->setVisible(checked); ui_->sidebar_layout->setVisible(checked);
TabSwitched(); TabSwitched();
settings_.setValue("show_sidebar", checked); settings_.setValue(MainWindowSettings::kShowSidebar, checked);
} }
void MainWindow::ToggleSearchCoverAuto(const bool checked) { void MainWindow::ToggleSearchCoverAuto(const bool checked) {
settings_.setValue("search_for_cover_auto", checked); settings_.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
} }
void MainWindow::SaveGeometry() { void MainWindow::SaveGeometry() {
if (!initialized_) return; if (!initialized_) return;
settings_.setValue("maximized", isMaximized()); settings_.setValue(MainWindowSettings::kMaximized, isMaximized());
settings_.setValue("minimized", isMinimized()); settings_.setValue(MainWindowSettings::kMinimized, isMinimized());
settings_.setValue("hidden", isHidden()); settings_.setValue(MainWindowSettings::kHidden, isHidden());
settings_.setValue("geometry", saveGeometry()); settings_.setValue(MainWindowSettings::kGeometry, saveGeometry());
settings_.setValue("splitter_state", ui_->splitter->saveState()); settings_.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState());
} }
@ -1546,12 +1588,12 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &idx) {
} }
switch (doubleclick_playlist_addmode_) { switch (doubleclick_playlist_addmode_) {
case BehaviourSettingsPage::PlaylistAddBehaviour::Play: case BehaviourSettings::PlaylistAddBehaviour::Play:
app_->playlist_manager()->SetActiveToCurrent(); app_->playlist_manager()->SetActiveToCurrent();
app_->player()->PlayAt(source_idx.row(), false, 0, EngineBase::TrackChangeType::Manual, Playlist::AutoScroll::Never, true, true); app_->player()->PlayAt(source_idx.row(), false, 0, EngineBase::TrackChangeType::Manual, Playlist::AutoScroll::Never, true, true);
break; break;
case BehaviourSettingsPage::PlaylistAddBehaviour::Enqueue: case BehaviourSettings::PlaylistAddBehaviour::Enqueue:
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << source_idx); app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << source_idx);
if (app_->player()->GetState() != EngineBase::State::Playing) { if (app_->player()->GetState() != EngineBase::State::Playing) {
app_->playlist_manager()->SetActiveToCurrent(); app_->playlist_manager()->SetActiveToCurrent();
@ -1724,42 +1766,42 @@ void MainWindow::UpdateTaskbarProgress(const bool visible, const double progress
} }
#endif #endif
void MainWindow::ApplyAddBehaviour(const BehaviourSettingsPage::AddBehaviour b, MimeData *mimedata) { void MainWindow::ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata) {
switch (b) { switch (b) {
case BehaviourSettingsPage::AddBehaviour::Append: case BehaviourSettings::AddBehaviour::Append:
mimedata->clear_first_ = false; mimedata->clear_first_ = false;
mimedata->enqueue_now_ = false; mimedata->enqueue_now_ = false;
break; break;
case BehaviourSettingsPage::AddBehaviour::Enqueue: case BehaviourSettings::AddBehaviour::Enqueue:
mimedata->clear_first_ = false; mimedata->clear_first_ = false;
mimedata->enqueue_now_ = true; mimedata->enqueue_now_ = true;
break; break;
case BehaviourSettingsPage::AddBehaviour::Load: case BehaviourSettings::AddBehaviour::Load:
mimedata->clear_first_ = true; mimedata->clear_first_ = true;
mimedata->enqueue_now_ = false; mimedata->enqueue_now_ = false;
break; break;
case BehaviourSettingsPage::AddBehaviour::OpenInNew: case BehaviourSettings::AddBehaviour::OpenInNew:
mimedata->open_in_new_playlist_ = true; mimedata->open_in_new_playlist_ = true;
break; break;
} }
} }
void MainWindow::ApplyPlayBehaviour(const BehaviourSettingsPage::PlayBehaviour b, MimeData *mimedata) const { void MainWindow::ApplyPlayBehaviour(const BehaviourSettings::PlayBehaviour b, MimeData *mimedata) const {
switch (b) { switch (b) {
case BehaviourSettingsPage::PlayBehaviour::Always: case BehaviourSettings::PlayBehaviour::Always:
mimedata->play_now_ = true; mimedata->play_now_ = true;
break; break;
case BehaviourSettingsPage::PlayBehaviour::Never: case BehaviourSettings::PlayBehaviour::Never:
mimedata->play_now_ = false; mimedata->play_now_ = false;
break; break;
case BehaviourSettingsPage::PlayBehaviour::IfStopped: case BehaviourSettings::PlayBehaviour::IfStopped:
mimedata->play_now_ = !(app_->player()->GetState() == EngineBase::State::Playing); mimedata->play_now_ = !(app_->player()->GetState() == EngineBase::State::Playing);
break; break;
} }
@ -1814,7 +1856,7 @@ void MainWindow::AddToPlaylistFromAction(QAction *action) {
// Save the current playlist to reactivate it // Save the current playlist to reactivate it
const int current_id = app_->playlist_manager()->current_id(); const int current_id = app_->playlist_manager()->current_id();
// Get the name from selection // Get the name from selection
app_->playlist_manager()->New(app_->playlist_manager()->GetNameForNewPlaylist(songs)); app_->playlist_manager()->New(Song::GetNameForNewPlaylist(songs));
if (app_->playlist_manager()->current()->id() != current_id) { if (app_->playlist_manager()->current()->id() != current_id) {
// I'm sure the new playlist was created and is selected, so I can just insert items // I'm sure the new playlist was created and is selected, so I can just insert items
app_->playlist_manager()->current()->InsertItems(items); app_->playlist_manager()->current()->InsertItems(items);
@ -2161,7 +2203,7 @@ void MainWindow::RenumberTracks() {
Song song = item->OriginalMetadata(); Song song = item->OriginalMetadata();
if (song.IsEditable()) { if (song.IsEditable()) {
song.set_track(track); song.set_track(track);
TagReaderReplyPtr reply = TagReaderClient::Instance()->WriteFileAsync(song.url().toLocalFile(), song); TagReaderReplyPtr reply = app_->tagreader_client()->WriteFileAsync(song.url().toLocalFile(), song);
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index); QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection); QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
} }
@ -2192,7 +2234,7 @@ void MainWindow::SelectionSetValue() {
Song song = item->OriginalMetadata(); Song song = item->OriginalMetadata();
if (!song.is_valid()) continue; if (!song.is_valid()) continue;
if (song.url().isLocalFile() && Playlist::set_column_value(song, column, column_value)) { if (song.url().isLocalFile() && Playlist::set_column_value(song, column, column_value)) {
TagReaderReplyPtr reply = TagReaderClient::Instance()->WriteFileAsync(song.url().toLocalFile(), song); TagReaderReplyPtr reply = app_->tagreader_client()->WriteFileAsync(song.url().toLocalFile(), song);
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index); QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection); QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
} }
@ -2228,10 +2270,10 @@ void MainWindow::AddFile() {
// Last used directory // Last used directory
QString directory = settings_.value("add_media_path", QDir::currentPath()).toString(); QString directory = settings_.value("add_media_path", QDir::currentPath()).toString();
PlaylistParser parser(app_->collection_backend()); PlaylistParser parser(app_->tagreader_client(), app_->collection_backend());
// Show dialog // Show dialog
const QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Add file"), directory, QStringLiteral("%1 (%2);;%3;;%4").arg(tr("Music"), QLatin1String(FileView::kFileFilter), parser.filters(PlaylistParser::Type::Load), tr(kAllFilesFilterSpec))); const QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Add file"), directory, QStringLiteral("%1 (%2);;%3;;%4").arg(tr("Music"), QLatin1String(kFileFilter), parser.filters(PlaylistParser::Type::Load), tr(kAllFilesFilterSpec)));
if (filenames.isEmpty()) return; if (filenames.isEmpty()) return;
@ -2470,7 +2512,7 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
break; break;
case CommandlineOptions::UrlListAction::CreateNew: case CommandlineOptions::UrlListAction::CreateNew:
mimedata->name_for_new_playlist_ = options.playlist_name(); mimedata->name_for_new_playlist_ = options.playlist_name();
ApplyAddBehaviour(BehaviourSettingsPage::AddBehaviour::OpenInNew, mimedata); ApplyAddBehaviour(BehaviourSettings::AddBehaviour::OpenInNew, mimedata);
break; break;
} }
@ -2563,10 +2605,34 @@ void MainWindow::AddFilesToTranscoder() {
} }
void MainWindow::ShowCollectionConfig() { void MainWindow::OpenCollectionSettingsDialog() {
settings_dialog_->OpenAtPage(SettingsDialog::Page::Collection); settings_dialog_->OpenAtPage(SettingsDialog::Page::Collection);
} }
void MainWindow::OpenServiceSettingsDialog(const Song::Source source) {
switch (source) {
case Song::Source::Collection:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Collection);
break;
case Song::Source::Subsonic:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Subsonic);
break;
case Song::Source::Tidal:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Tidal);
break;
case Song::Source::Qobuz:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Qobuz);
break;
case Song::Source::Spotify:
settings_dialog_->OpenAtPage(SettingsDialog::Page::Spotify);
break;
default:
break;
}
}
void MainWindow::TaskCountChanged(const int count) { void MainWindow::TaskCountChanged(const int count) {
if (count == 0) { if (count == 0) {
@ -2810,10 +2876,18 @@ void MainWindow::ShowEqualizer() {
SettingsDialog *MainWindow::CreateSettingsDialog() { SettingsDialog *MainWindow::CreateSettingsDialog() {
SettingsDialog *settings_dialog = new SettingsDialog(app_, osd_, this); SettingsDialog *settings_dialog = new SettingsDialog(app_->player(),
app_->device_finders(),
app_->collection(),
app_->cover_providers(),
app_->lyrics_providers(),
app_->scrobbler(),
app_->streaming_services(),
#ifdef HAVE_GLOBALSHORTCUTS #ifdef HAVE_GLOBALSHORTCUTS
settings_dialog->SetGlobalShortcutManager(globalshortcuts_manager_); globalshortcuts_manager_,
#endif #endif
osd_,
this);
// Settings // Settings
QObject::connect(settings_dialog, &SettingsDialog::ReloadSettings, this, &MainWindow::ReloadAllSettings); QObject::connect(settings_dialog, &SettingsDialog::ReloadSettings, this, &MainWindow::ReloadAllSettings);
@ -2838,7 +2912,7 @@ void MainWindow::OpenSettingsDialogAtPage(SettingsDialog::Page page) {
EditTagDialog *MainWindow::CreateEditTagDialog() { EditTagDialog *MainWindow::CreateEditTagDialog() {
EditTagDialog *edit_tag_dialog = new EditTagDialog(app_); EditTagDialog *edit_tag_dialog = new EditTagDialog(app_->network(), app_->tagreader_client(), app_->collection_backend(), app_->albumcover_loader(), app_->current_albumcover_loader(), app_->cover_providers(), app_->lyrics_providers(), app_->streaming_services());
QObject::connect(edit_tag_dialog, &EditTagDialog::accepted, this, &MainWindow::EditTagDialogAccepted); QObject::connect(edit_tag_dialog, &EditTagDialog::accepted, this, &MainWindow::EditTagDialogAccepted);
QObject::connect(edit_tag_dialog, &EditTagDialog::Error, this, &MainWindow::ShowErrorDialog); QObject::connect(edit_tag_dialog, &EditTagDialog::Error, this, &MainWindow::ShowErrorDialog);
return edit_tag_dialog; return edit_tag_dialog;
@ -2938,7 +3012,7 @@ void MainWindow::AutoCompleteTags() {
// Create the tag fetching stuff if it hasn't been already // Create the tag fetching stuff if it hasn't been already
if (!tag_fetcher_) { if (!tag_fetcher_) {
tag_fetcher_ = make_unique<TagFetcher>(app_->network()); tag_fetcher_ = make_unique<TagFetcher>(app_->network());
track_selection_dialog_ = make_unique<TrackSelectionDialog>(); track_selection_dialog_ = make_unique<TrackSelectionDialog>(app_->tagreader_client());
track_selection_dialog_->set_save_on_close(true); track_selection_dialog_->set_save_on_close(true);
QObject::connect(&*tag_fetcher_, &TagFetcher::ResultAvailable, &*track_selection_dialog_, &TrackSelectionDialog::FetchTagFinished, Qt::QueuedConnection); QObject::connect(&*tag_fetcher_, &TagFetcher::ResultAvailable, &*track_selection_dialog_, &TrackSelectionDialog::FetchTagFinished, Qt::QueuedConnection);
@ -2986,7 +3060,7 @@ void MainWindow::AutoCompleteTagsAccepted() {
} }
void MainWindow::HandleNotificationPreview(const OSDBase::Behaviour type, const QString &line1, const QString &line2) { void MainWindow::HandleNotificationPreview(const OSDSettings::Type type, const QString &line1, const QString &line2) {
if (!app_->playlist_manager()->current()->GetAllSongs().isEmpty()) { if (!app_->playlist_manager()->current()->GetAllSongs().isEmpty()) {
// Show a preview notification for the first song in the current playlist // Show a preview notification for the first song in the current playlist

View File

@ -46,19 +46,19 @@
#include <QSettings> #include <QSettings>
#include <QtEvents> #include <QtEvents>
#include "scoped_ptr.h" #include "includes/scoped_ptr.h"
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "lazy.h" #include "includes/lazy.h"
#include "platforminterface.h" #include "core/platforminterface.h"
#include "song.h" #include "core/song.h"
#include "settings.h" #include "core/settings.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "osd/osdbase.h" #include "osd/osdbase.h"
#include "playlist/playlist.h" #include "playlist/playlist.h"
#include "playlist/playlistitem.h" #include "playlist/playlistitem.h"
#include "settings/settingsdialog.h" #include "settings/settingsdialog.h"
#include "settings/behavioursettingspage.h" #include "constants/behavioursettings.h"
#include "covermanager/albumcoverloaderresult.h" #include "covermanager/albumcoverloaderresult.h"
#include "covermanager/albumcoverimageresult.h" #include "covermanager/albumcoverimageresult.h"
@ -107,9 +107,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
explicit MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OSDBase *osd, const CommandlineOptions &options, QWidget *parent = nullptr); explicit MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OSDBase *osd, const CommandlineOptions &options, QWidget *parent = nullptr);
~MainWindow() override; ~MainWindow() override;
static const char *kSettingsGroup;
static const char *kAllFilesFilterSpec;
void SetHiddenInTray(const bool hidden); void SetHiddenInTray(const bool hidden);
void CommandlineOptionsReceived(const CommandlineOptions &options); void CommandlineOptionsReceived(const CommandlineOptions &options);
@ -202,7 +199,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void TaskCountChanged(const int count); void TaskCountChanged(const int count);
void ShowCollectionConfig(); void OpenCollectionSettingsDialog();
void OpenServiceSettingsDialog(const Song::Source source);
void ReloadSettings(); void ReloadSettings();
void ReloadAllSettings(); void ReloadAllSettings();
void RefreshStyleSheet(); void RefreshStyleSheet();
@ -237,7 +236,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void Exit(); void Exit();
void DoExit(); void DoExit();
void HandleNotificationPreview(const OSDBase::Behaviour type, const QString &line1, const QString &line2); void HandleNotificationPreview(const OSDSettings::Type type, const QString &line1, const QString &line2);
void ShowConsole(); void ShowConsole();
@ -274,8 +273,8 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void SaveSettings(); void SaveSettings();
static void ApplyAddBehaviour(const BehaviourSettingsPage::AddBehaviour b, MimeData *mimedata); static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata);
void ApplyPlayBehaviour(const BehaviourSettingsPage::PlayBehaviour b, MimeData *mimedata) const; void ApplyPlayBehaviour(const BehaviourSettings::PlayBehaviour b, MimeData *mimedata) const;
void CheckFullRescanRevisions(); void CheckFullRescanRevisions();
@ -385,10 +384,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
bool taskbar_progress_; bool taskbar_progress_;
#endif #endif
BehaviourSettingsPage::AddBehaviour doubleclick_addmode_; BehaviourSettings::AddBehaviour doubleclick_addmode_;
BehaviourSettingsPage::PlayBehaviour doubleclick_playmode_; BehaviourSettings::PlayBehaviour doubleclick_playmode_;
BehaviourSettingsPage::PlaylistAddBehaviour doubleclick_playlist_addmode_; BehaviourSettings::PlaylistAddBehaviour doubleclick_playlist_addmode_;
BehaviourSettingsPage::PlayBehaviour menu_playmode_; BehaviourSettings::PlayBehaviour menu_playmode_;
bool initialized_; bool initialized_;
bool was_maximized_; bool was_maximized_;

View File

@ -0,0 +1,30 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "memorydatabase.h"
using namespace Qt::Literals::StringLiterals;
MemoryDatabase::MemoryDatabase(SharedPtr<TaskManager> task_manager, QObject *parent)
: Database(task_manager, parent, u":memory:"_s) {}
MemoryDatabase::~MemoryDatabase() {
// Make sure Qt doesn't reuse the same database
Close();
}

37
src/core/memorydatabase.h Normal file
View File

@ -0,0 +1,37 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MEMORYDATABASE_H
#define MEMORYDATABASE_H
#include "includes/shared_ptr.h"
class TaskManager;
#include "database.h"
class MemoryDatabase : public Database {
Q_OBJECT
public:
explicit MemoryDatabase(SharedPtr<TaskManager> task_manager, QObject *parent = nullptr);
~MemoryDatabase() override;
};
#endif // MEMORYDATABASE_H

View File

@ -35,7 +35,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include "core/scoped_ptr.h" #include "includes/scoped_ptr.h"
class QMimeData; class QMimeData;

View File

@ -43,10 +43,9 @@
# include <QDBusArgument> # include <QDBusArgument>
#endif #endif
#include "song.h" #include "core/song.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "engine/gstenginepipeline.h" #include "engine/gstenginepipeline.h"
#include "collection/collectiondirectory.h" #include "collection/collectiondirectory.h"
#include "playlist/playlistitem.h" #include "playlist/playlistitem.h"
@ -57,7 +56,7 @@
#include "equalizer/equalizer.h" #include "equalizer/equalizer.h"
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
# include "dbus_metatypes.h" # include "includes/dbus_metatypes.h"
#endif #endif
#ifdef HAVE_MPRIS2 #ifdef HAVE_MPRIS2
@ -74,7 +73,7 @@
# include "device/mtpconnection.h" # include "device/mtpconnection.h"
#endif #endif
#include "settings/playlistsettingspage.h" #include "constants/playlistsettings.h"
#include "smartplaylists/smartplaylistsearchterm.h" #include "smartplaylists/smartplaylistsearchterm.h"
#include "smartplaylists/smartplaylistsitem.h" #include "smartplaylists/smartplaylistsitem.h"
@ -153,7 +152,7 @@ void RegisterMetaTypes() {
qRegisterMetaType<MtpConnection*>("MtpConnection*"); qRegisterMetaType<MtpConnection*>("MtpConnection*");
#endif #endif
qRegisterMetaType<PlaylistSettingsPage::PathType>("PlaylistSettingsPage::PathType"); qRegisterMetaType<PlaylistSettings::PathType>("PlaylistSettings::PathType");
qRegisterMetaType<PlaylistGeneratorPtr>("PlaylistGeneratorPtr"); qRegisterMetaType<PlaylistGeneratorPtr>("PlaylistGeneratorPtr");
qRegisterMetaType<SmartPlaylistSearchTerm::Field>("SmartPlaylistSearchTerm::Field"); qRegisterMetaType<SmartPlaylistSearchTerm::Field>("SmartPlaylistSearchTerm::Field");

View File

@ -35,7 +35,7 @@
#include <QList> #include <QList>
#include <QImage> #include <QImage>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "song.h" #include "song.h"
class MusicStorage { class MusicStorage {

View File

@ -0,0 +1,26 @@
/*
* Strawberry Music Player
* Copyright 2018-2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <QObject>
#include <QString>
#include "platforminterface.h"
PlatformInterface::PlatformInterface() = default;
PlatformInterface::~PlatformInterface() = default;

View File

@ -1,7 +1,6 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -26,8 +25,8 @@
class PlatformInterface { class PlatformInterface {
public: public:
PlatformInterface() = default; explicit PlatformInterface();
virtual ~PlatformInterface() {} virtual ~PlatformInterface();
// Called when the application should show itself. // Called when the application should show itself.
virtual void Activate() = 0; virtual void Activate() = 0;

View File

@ -37,18 +37,21 @@
#include <QTimer> #include <QTimer>
#include <QSettings> #include <QSettings>
#include "constants/behavioursettings.h"
#include "constants/backendsettings.h"
#include "constants/playlistsettings.h"
#include "constants/timeconstants.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/settings.h" #include "core/settings.h"
#include "utilities/timeconstants.h" #include "core/song.h"
#include "core/urlhandlers.h"
#include "scoped_ptr.h" #include "core/urlhandler.h"
#include "shared_ptr.h" #include "core/enginemetadata.h"
#include "song.h"
#include "urlhandler.h"
#include "application.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "engine/gstengine.h" #include "engine/gstengine.h"
#include "engine/gststartup.h" #include "engine/gststartup.h"
@ -60,18 +63,22 @@
#include "playlist/playlistsequence.h" #include "playlist/playlistsequence.h"
#include "equalizer/equalizer.h" #include "equalizer/equalizer.h"
#include "analyzer/analyzercontainer.h" #include "analyzer/analyzercontainer.h"
#include "settings/backendsettingspage.h"
#include "settings/behavioursettingspage.h"
#include "settings/playlistsettingspage.h"
using namespace std::chrono_literals; using namespace std::chrono_literals;
using std::make_shared;
const char *Player::kSettingsGroup = "Player"; namespace {
constexpr char kSettingsGroup[] = "Player";
constexpr char kVolume[] = "volume";
constexpr char kPlaybackState[] = "playback_state";
constexpr char kPlaybackPlaylist[] = "playback_playlist";
constexpr char kPlaybackPosition[] = "playback_position";
} // namespace
Player::Player(Application *app, QObject *parent) Player::Player(const SharedPtr<TaskManager> task_manager, const SharedPtr<UrlHandlers> url_handlers, const SharedPtr<PlaylistManager> playlist_manager, QObject *parent)
: PlayerInterface(parent), : PlayerInterface(parent),
app_(app), task_manager_(task_manager),
url_handlers_(url_handlers),
playlist_manager_(playlist_manager),
engine_(nullptr), engine_(nullptr),
gst_startup_(new GstStartup(this)), gst_startup_(new GstStartup(this)),
analyzer_(nullptr), analyzer_(nullptr),
@ -89,7 +96,7 @@ Player::Player(Application *app, QObject *parent)
last_pressed_previous_(QDateTime::currentDateTime()), last_pressed_previous_(QDateTime::currentDateTime()),
continue_on_error_(false), continue_on_error_(false),
greyout_(true), greyout_(true),
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour::DontRestart), menu_previousmode_(BehaviourSettings::PreviousBehaviour::DontRestart),
seek_step_sec_(10), seek_step_sec_(10),
volume_increment_(5), volume_increment_(5),
play_offset_nanosec_(0) { play_offset_nanosec_(0) {
@ -97,8 +104,8 @@ Player::Player(Application *app, QObject *parent)
setObjectName(QLatin1String(metaObject()->className())); setObjectName(QLatin1String(metaObject()->className()));
Settings s; Settings s;
s.beginGroup(BackendSettingsPage::kSettingsGroup); s.beginGroup(BackendSettings::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value("engine", EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower()); EngineBase::Type enginetype = EngineBase::TypeFromName(s.value(BackendSettings::kEngine, EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.endGroup(); s.endGroup();
CreateEngine(enginetype); CreateEngine(enginetype);
@ -107,6 +114,8 @@ Player::Player(Application *app, QObject *parent)
timer_save_volume_->setInterval(5s); timer_save_volume_->setInterval(5s);
QObject::connect(timer_save_volume_, &QTimer::timeout, this, &Player::SaveVolume); QObject::connect(timer_save_volume_, &QTimer::timeout, this, &Player::SaveVolume);
QObject::connect(&*url_handlers, &UrlHandlers::Registered, this, &Player::UrlHandlerRegistered);
} }
EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) { EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
@ -118,7 +127,7 @@ EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
case EngineBase::Type::None: case EngineBase::Type::None:
case EngineBase::Type::GStreamer:{ case EngineBase::Type::GStreamer:{
use_enginetype=EngineBase::Type::GStreamer; use_enginetype=EngineBase::Type::GStreamer;
ScopedPtr<GstEngine> gst_engine(new GstEngine(app_->task_manager())); ScopedPtr<GstEngine> gst_engine(new GstEngine(task_manager_));
gst_engine->SetStartup(gst_startup_); gst_engine->SetStartup(gst_startup_);
engine_.reset(gst_engine.release()); engine_.reset(gst_engine.release());
break; break;
@ -134,10 +143,10 @@ EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
if (use_enginetype != enginetype) { // Engine was set to something else. Reset output and device. if (use_enginetype != enginetype) { // Engine was set to something else. Reset output and device.
Settings s; Settings s;
s.beginGroup(BackendSettingsPage::kSettingsGroup); s.beginGroup(BackendSettings::kSettingsGroup);
s.setValue("engine", EngineBase::Name(use_enginetype)); s.setValue(BackendSettings::kEngine, EngineBase::Name(use_enginetype));
s.setValue("output", engine_->DefaultOutput()); s.setValue(BackendSettings::kOutput, engine_->DefaultOutput());
s.setValue("device", QVariant()); s.setValue(BackendSettings::kDevice, QVariant());
s.endGroup(); s.endGroup();
} }
@ -156,7 +165,7 @@ void Player::Init() {
Settings s; Settings s;
if (!engine_) { if (!engine_) {
s.beginGroup(BackendSettingsPage::kSettingsGroup); s.beginGroup(BackendSettings::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value("engine", EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower()); EngineBase::Type enginetype = EngineBase::TypeFromName(s.value("engine", EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.endGroup(); s.endGroup();
CreateEngine(enginetype); CreateEngine(enginetype);
@ -179,10 +188,10 @@ void Player::Init() {
QObject::connect(&*engine_, &EngineBase::VolumeChanged, this, &Player::SetVolumeFromEngine); QObject::connect(&*engine_, &EngineBase::VolumeChanged, this, &Player::SetVolumeFromEngine);
// Equalizer // Equalizer
QObject::connect(&*equalizer_, &Equalizer::StereoBalancerEnabledChanged, &*app_->player()->engine(), &EngineBase::SetStereoBalancerEnabled); QObject::connect(&*equalizer_, &Equalizer::StereoBalancerEnabledChanged, &*engine_, &EngineBase::SetStereoBalancerEnabled);
QObject::connect(&*equalizer_, &Equalizer::StereoBalanceChanged, &*app_->player()->engine(), &EngineBase::SetStereoBalance); QObject::connect(&*equalizer_, &Equalizer::StereoBalanceChanged, &*engine_, &EngineBase::SetStereoBalance);
QObject::connect(&*equalizer_, &Equalizer::EqualizerEnabledChanged, &*app_->player()->engine(), &EngineBase::SetEqualizerEnabled); QObject::connect(&*equalizer_, &Equalizer::EqualizerEnabledChanged, &*engine_, &EngineBase::SetEqualizerEnabled);
QObject::connect(&*equalizer_, &Equalizer::EqualizerParametersChanged, &*app_->player()->engine(), &EngineBase::SetEqualizerParameters); QObject::connect(&*equalizer_, &Equalizer::EqualizerParametersChanged, &*engine_, &EngineBase::SetEqualizerParameters);
engine_->SetStereoBalancerEnabled(equalizer_->is_stereo_balancer_enabled()); engine_->SetStereoBalancerEnabled(equalizer_->is_stereo_balancer_enabled());
engine_->SetStereoBalance(equalizer_->stereo_balance()); engine_->SetStereoBalance(equalizer_->stereo_balance());
@ -199,26 +208,32 @@ void Player::ReloadSettings() {
Settings s; Settings s;
s.beginGroup(PlaylistSettingsPage::kSettingsGroup); s.beginGroup(PlaylistSettings::kSettingsGroup);
continue_on_error_ = s.value("continue_on_error", false).toBool(); continue_on_error_ = s.value("continue_on_error", false).toBool();
greyout_ = s.value("greyout_songs_play", true).toBool(); greyout_ = s.value("greyout_songs_play", true).toBool();
s.endGroup(); s.endGroup();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup); s.beginGroup(BehaviourSettings::kSettingsGroup);
menu_previousmode_ = static_cast<BehaviourSettingsPage::PreviousBehaviour>(s.value("menu_previousmode", static_cast<int>(BehaviourSettingsPage::PreviousBehaviour::DontRestart)).toInt()); menu_previousmode_ = static_cast<BehaviourSettings::PreviousBehaviour>(s.value(BehaviourSettings::kMenuPreviousMode, static_cast<int>(BehaviourSettings::PreviousBehaviour::DontRestart)).toInt());
seek_step_sec_ = s.value("seek_step_sec", 10).toInt(); seek_step_sec_ = s.value(BehaviourSettings::kSeekStepSec, 10).toInt();
volume_increment_ = s.value("volume_increment", 5).toUInt(); volume_increment_ = s.value(BehaviourSettings::kVolumeIncrement, 5).toUInt();
s.endGroup(); s.endGroup();
engine_->ReloadSettings(); engine_->ReloadSettings();
} }
void Player::UrlHandlerRegistered(UrlHandler *url_handler) const {
QObject::connect(url_handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
void Player::LoadVolume() { void Player::LoadVolume() {
Settings s; Settings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
const uint volume = s.value("volume", 100).toInt(); const uint volume = s.value(kVolume, 100).toInt();
s.endGroup(); s.endGroup();
SetVolume(volume); SetVolume(volume);
@ -229,7 +244,7 @@ void Player::SaveVolume() {
Settings s; Settings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("volume", volume_); s.setValue(kVolume, volume_);
s.endGroup(); s.endGroup();
} }
@ -239,14 +254,14 @@ void Player::SavePlaybackStatus() {
Settings s; Settings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("playback_state", static_cast<int>(app_->player()->GetState())); s.setValue(kPlaybackState, static_cast<int>(GetState()));
if (app_->player()->GetState() == EngineBase::State::Playing || app_->player()->GetState() == EngineBase::State::Paused) { if (GetState() == EngineBase::State::Playing || GetState() == EngineBase::State::Paused) {
s.setValue("playback_playlist", app_->playlist_manager()->active()->id()); s.setValue(kPlaybackPlaylist, playlist_manager_->active()->id());
s.setValue("playback_position", app_->player()->engine()->position_nanosec() / kNsecPerSec); s.setValue(kPlaybackPosition, engine_->position_nanosec() / kNsecPerSec);
} }
else { else {
s.setValue("playback_playlist", -1); s.setValue(kPlaybackPlaylist, -1);
s.setValue("playback_position", 0); s.setValue(kPlaybackPosition, 0);
} }
s.endGroup(); s.endGroup();
@ -258,12 +273,12 @@ void Player::PlaylistsLoaded() {
Settings s; Settings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup); s.beginGroup(BehaviourSettings::kSettingsGroup);
const bool resume_playback = s.value("resumeplayback", false).toBool(); const bool resume_playback = s.value("resumeplayback", false).toBool();
s.endGroup(); s.endGroup();
s.beginGroup(Player::kSettingsGroup); s.beginGroup(kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value("playback_state", static_cast<int>(EngineBase::State::Empty)).toInt()); const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value(kPlaybackState, static_cast<int>(EngineBase::State::Empty)).toInt());
s.endGroup(); s.endGroup();
if (resume_playback && (playback_state == EngineBase::State::Playing || playback_state == EngineBase::State::Paused)) { if (resume_playback && (playback_state == EngineBase::State::Playing || playback_state == EngineBase::State::Paused)) {
@ -283,14 +298,14 @@ void Player::ResumePlayback() {
Settings s; Settings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value("playback_state", static_cast<int>(EngineBase::State::Empty)).toInt()); const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value(kPlaybackState, static_cast<int>(EngineBase::State::Empty)).toInt());
const int playback_playlist = s.value("playback_playlist", -1).toInt(); const int playback_playlist = s.value(kPlaybackPlaylist, -1).toInt();
const int playback_position = s.value("playback_position", 0).toInt(); const int playback_position = s.value(kPlaybackPosition, 0).toInt();
s.endGroup(); s.endGroup();
if (playback_playlist == app_->playlist_manager()->current()->id()) { if (playback_playlist == playlist_manager_->current()->id()) {
// Set active to current to resume playback on correct playlist. // Set active to current to resume playback on correct playlist.
app_->playlist_manager()->SetActiveToCurrent(); playlist_manager_->SetActiveToCurrent();
if (playback_state == EngineBase::State::Playing) { if (playback_state == EngineBase::State::Playing) {
Play(playback_position * kNsecPerSec); Play(playback_position * kNsecPerSec);
} }
@ -301,9 +316,9 @@ void Player::ResumePlayback() {
// Reset saved playback status so we don't resume again from the same position. // Reset saved playback status so we don't resume again from the same position.
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("playback_state", static_cast<int>(EngineBase::State::Empty)); s.setValue(kPlaybackState, static_cast<int>(EngineBase::State::Empty));
s.setValue("playback_playlist", -1); s.setValue(kPlaybackPlaylist, -1);
s.setValue("playback_position", 0); s.setValue(kPlaybackPosition, 0);
s.endGroup(); s.endGroup();
} }
@ -315,19 +330,19 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
} }
// Might've been an async load, so check we're still on the same item // Might've been an async load, so check we're still on the same item
const int current_row = app_->playlist_manager()->active()->current_row(); const int current_row = playlist_manager_->active()->current_row();
if (current_row == -1) { if (current_row == -1) {
return; return;
} }
PlaylistItemPtr current_item = app_->playlist_manager()->active()->current_item(); PlaylistItemPtr current_item = playlist_manager_->active()->current_item();
if (!current_item) { if (!current_item) {
return; return;
} }
int next_row = app_->playlist_manager()->active()->next_row(); int next_row = playlist_manager_->active()->next_row();
const bool has_next_row = next_row != -1; const bool has_next_row = next_row != -1;
PlaylistItemPtr next_item; PlaylistItemPtr next_item;
if (has_next_row) { if (has_next_row) {
next_item = app_->playlist_manager()->active()->item_at(next_row); next_item = playlist_manager_->active()->item_at(next_row);
} }
bool is_current = false; bool is_current = false;
@ -408,10 +423,10 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
if (update) { if (update) {
if (is_current) { if (is_current) {
app_->playlist_manager()->active()->UpdateItemMetadata(current_row, current_item, song, true); playlist_manager_->active()->UpdateItemMetadata(current_row, current_item, song, true);
} }
else if (is_next) { else if (is_next) {
app_->playlist_manager()->active()->UpdateItemMetadata(next_row, next_item, song, true); playlist_manager_->active()->UpdateItemMetadata(next_row, next_item, song, true);
} }
} }
@ -457,13 +472,13 @@ void Player::NextItem(const EngineBase::TrackChangeFlags change, const Playlist:
pause_time_ = QDateTime(); pause_time_ = QDateTime();
play_offset_nanosec_ = 0; play_offset_nanosec_ = 0;
Playlist *active_playlist = app_->playlist_manager()->active(); Playlist *active_playlist = playlist_manager_->active();
// If we received too many errors in auto change, with repeat enabled, we stop // If we received too many errors in auto change, with repeat enabled, we stop
if (change & EngineBase::TrackChangeType::Auto) { if (change & EngineBase::TrackChangeType::Auto) {
const PlaylistSequence::RepeatMode repeat_mode = active_playlist->RepeatMode(); const PlaylistSequence::RepeatMode repeat_mode = active_playlist->RepeatMode();
if (repeat_mode != PlaylistSequence::RepeatMode::Off) { if (repeat_mode != PlaylistSequence::RepeatMode::Off) {
if ((repeat_mode == PlaylistSequence::RepeatMode::Track && nb_errors_received_ >= 3) || (nb_errors_received_ >= app_->playlist_manager()->active()->filter()->rowCount())) { if ((repeat_mode == PlaylistSequence::RepeatMode::Track && nb_errors_received_ >= 3) || (nb_errors_received_ >= playlist_manager_->active()->filter()->rowCount())) {
// We received too many "Error" state changes: probably looping over a playlist which contains only unavailable elements: stop now. // We received too many "Error" state changes: probably looping over a playlist which contains only unavailable elements: stop now.
nb_errors_received_ = 0; nb_errors_received_ = 0;
Stop(); Stop();
@ -482,8 +497,8 @@ void Player::NextItem(const EngineBase::TrackChangeFlags change, const Playlist:
int i = active_playlist->next_row(ignore_repeat_track); int i = active_playlist->next_row(ignore_repeat_track);
if (i == -1) { if (i == -1) {
app_->playlist_manager()->active()->set_current_row(i); playlist_manager_->active()->set_current_row(i);
app_->playlist_manager()->active()->reset_last_played(); playlist_manager_->active()->reset_last_played();
Q_EMIT PlaylistFinished(); Q_EMIT PlaylistFinished();
Stop(); Stop();
return; return;
@ -503,9 +518,9 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
play_offset_nanosec_ = 0; play_offset_nanosec_ = 0;
Playlist *playlist = nullptr; Playlist *playlist = nullptr;
const QList<Playlist*> playlists = app_->playlist_manager()->GetAllPlaylists(); const QList<Playlist*> playlists = playlist_manager_->GetAllPlaylists();
for (Playlist *p : playlists) { for (Playlist *p : playlists) {
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) { if (playlist_name == playlist_manager_->GetPlaylistName(p->id())) {
playlist = p; playlist = p;
break; break;
} }
@ -516,12 +531,12 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
return; return;
} }
app_->playlist_manager()->SetActivePlaylist(playlist->id()); playlist_manager_->SetActivePlaylist(playlist->id());
app_->playlist_manager()->SetCurrentPlaylist(playlist->id()); playlist_manager_->SetCurrentPlaylist(playlist->id());
if (playlist->rowCount() == 0) return; if (playlist->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row(); int i = playlist_manager_->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row(); if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0; if (i == -1) i = 0;
PlayAt(i, false, 0, change, autoscroll, true); PlayAt(i, false, 0, change, autoscroll, true);
@ -530,14 +545,14 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) { bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
if (app_->playlist_manager()->active()->stop_after_current()) { if (playlist_manager_->active()->stop_after_current()) {
// Find what the next track would've been, and mark that one as current, so it plays next time the user presses Play. // Find what the next track would've been, and mark that one as current, so it plays next time the user presses Play.
const int next_row = app_->playlist_manager()->active()->next_row(); const int next_row = playlist_manager_->active()->next_row();
if (next_row != -1) { if (next_row != -1) {
app_->playlist_manager()->active()->set_current_row(next_row, autoscroll, true); playlist_manager_->active()->set_current_row(next_row, autoscroll, true);
} }
app_->playlist_manager()->active()->StopAfter(-1); playlist_manager_->active()->StopAfter(-1);
Stop(true); Stop(true);
return true; return true;
@ -550,7 +565,7 @@ bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
void Player::TrackEnded() { void Player::TrackEnded() {
if (current_item_ && current_item_->IsLocalCollectionItem() && current_item_->Metadata().id() != -1) { if (current_item_ && current_item_->IsLocalCollectionItem() && current_item_->Metadata().id() != -1) {
app_->playlist_manager()->collection_backend()->IncrementPlayCountAsync(current_item_->Metadata().id()); playlist_manager_->collection_backend()->IncrementPlayCountAsync(current_item_->Metadata().id());
} }
if (HandleStopAfter(Playlist::AutoScroll::Maybe)) return; if (HandleStopAfter(Playlist::AutoScroll::Maybe)) return;
@ -584,10 +599,10 @@ void Player::PlayPause(const quint64 offset_nanosec, const Playlist::AutoScroll
case EngineBase::State::Idle:{ case EngineBase::State::Idle:{
pause_time_ = QDateTime(); pause_time_ = QDateTime();
play_offset_nanosec_ = offset_nanosec; play_offset_nanosec_ = offset_nanosec;
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id()); playlist_manager_->SetActivePlaylist(playlist_manager_->current_id());
if (app_->playlist_manager()->active()->rowCount() == 0) break; if (playlist_manager_->active()->rowCount() == 0) break;
int i = app_->playlist_manager()->active()->current_row(); int i = playlist_manager_->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row(); if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0; if (i == -1) i = 0;
PlayAt(i, false, offset_nanosec, EngineBase::TrackChangeType::First, autoscroll, true); PlayAt(i, false, offset_nanosec, EngineBase::TrackChangeType::First, autoscroll, true);
break; break;
@ -600,12 +615,12 @@ void Player::UnPause() {
if (current_item_ && pause_time_.isValid()) { if (current_item_ && pause_time_.isValid()) {
const Song &song = current_item_->Metadata(); const Song &song = current_item_->Metadata();
if (url_handlers_.contains(song.url().scheme()) && song.stream_url_can_expire()) { if (url_handlers_->CanHandle(song.url()) && song.stream_url_can_expire()) {
const quint64 time = QDateTime::currentSecsSinceEpoch() - pause_time_.toSecsSinceEpoch(); const quint64 time = QDateTime::currentSecsSinceEpoch() - pause_time_.toSecsSinceEpoch();
if (time >= 30) { // Stream URL might be expired. if (time >= 30) { // Stream URL might be expired.
qLog(Debug) << "Re-requesting stream URL for" << song.url(); qLog(Debug) << "Re-requesting stream URL for" << song.url();
play_offset_nanosec_ = engine_->position_nanosec(); play_offset_nanosec_ = engine_->position_nanosec();
UrlHandler *url_handler = url_handlers_.value(song.url().scheme()); UrlHandler *url_handler = url_handlers_->GetUrlHandler(song.url());
HandleLoadResult(url_handler->StartLoading(song.url())); HandleLoadResult(url_handler->StartLoading(song.url()));
return; return;
} }
@ -636,8 +651,8 @@ void Player::RestartOrPrevious() {
void Player::Stop(const bool stop_after) { void Player::Stop(const bool stop_after) {
engine_->Stop(stop_after); engine_->Stop(stop_after);
app_->playlist_manager()->active()->set_current_row(-1); playlist_manager_->active()->set_current_row(-1);
app_->playlist_manager()->active()->reset_played_indexes(); playlist_manager_->active()->reset_played_indexes();
current_item_.reset(); current_item_.reset();
pause_time_ = QDateTime(); pause_time_ = QDateTime();
play_offset_nanosec_ = 0; play_offset_nanosec_ = 0;
@ -645,13 +660,13 @@ void Player::Stop(const bool stop_after) {
} }
void Player::StopAfterCurrent() { void Player::StopAfterCurrent() {
app_->playlist_manager()->active()->StopAfter(app_->playlist_manager()->active()->current_row()); playlist_manager_->active()->StopAfter(playlist_manager_->active()->current_row());
} }
bool Player::PreviousWouldRestartTrack() const { bool Player::PreviousWouldRestartTrack() const {
// Check if it has been over two seconds since previous button was pressed // Check if it has been over two seconds since previous button was pressed
return menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour::Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2; return menu_previousmode_ == BehaviourSettings::PreviousBehaviour::Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
} }
@ -664,19 +679,19 @@ void Player::PreviousItem(const EngineBase::TrackChangeFlags change) {
const bool ignore_repeat_track = change & EngineBase::TrackChangeType::Manual; const bool ignore_repeat_track = change & EngineBase::TrackChangeType::Manual;
if (menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour::Restart) { if (menu_previousmode_ == BehaviourSettings::PreviousBehaviour::Restart) {
// Check if it has been over two seconds since previous button was pressed // Check if it has been over two seconds since previous button was pressed
QDateTime now = QDateTime::currentDateTime(); QDateTime now = QDateTime::currentDateTime();
if (last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(now) >= 2) { if (last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(now) >= 2) {
last_pressed_previous_ = now; last_pressed_previous_ = now;
PlayAt(app_->playlist_manager()->active()->current_row(), false, 0, change, Playlist::AutoScroll::Always, false, true); PlayAt(playlist_manager_->active()->current_row(), false, 0, change, Playlist::AutoScroll::Always, false, true);
return; return;
} }
last_pressed_previous_ = now; last_pressed_previous_ = now;
} }
int i = app_->playlist_manager()->active()->previous_row(ignore_repeat_track); int i = playlist_manager_->active()->previous_row(ignore_repeat_track);
app_->playlist_manager()->active()->set_current_row(i, Playlist::AutoScroll::Always, false); playlist_manager_->active()->set_current_row(i, Playlist::AutoScroll::Always, false);
if (i == -1) { if (i == -1) {
Stop(); Stop();
PlayAt(i, false, 0, change, Playlist::AutoScroll::Always, true); PlayAt(i, false, 0, change, Playlist::AutoScroll::Always, true);
@ -790,22 +805,22 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
Q_EMIT TrackSkipped(current_item_); Q_EMIT TrackSkipped(current_item_);
} }
if (current_item_ && app_->playlist_manager()->active()->has_item_at(index) && current_item_->Metadata().IsOnSameAlbum(app_->playlist_manager()->active()->item_at(index)->Metadata())) { if (current_item_ && playlist_manager_->active()->has_item_at(index) && current_item_->Metadata().IsOnSameAlbum(playlist_manager_->active()->item_at(index)->Metadata())) {
change |= EngineBase::TrackChangeType::SameAlbum; change |= EngineBase::TrackChangeType::SameAlbum;
} }
if (reshuffle) app_->playlist_manager()->active()->ReshuffleIndices(); if (reshuffle) playlist_manager_->active()->ReshuffleIndices();
app_->playlist_manager()->active()->set_current_row(index, autoscroll, false, force_inform); playlist_manager_->active()->set_current_row(index, autoscroll, false, force_inform);
if (app_->playlist_manager()->active()->current_row() == -1) { if (playlist_manager_->active()->current_row() == -1) {
// Maybe index didn't exist in the playlist. // Maybe index didn't exist in the playlist.
return; return;
} }
current_item_ = app_->playlist_manager()->active()->current_item(); current_item_ = playlist_manager_->active()->current_item();
const QUrl url = current_item_->StreamUrl(); const QUrl url = current_item_->StreamUrl();
if (url_handlers_.contains(url.scheme())) { if (url_handlers_->CanHandle(url)) {
// It's already loading // It's already loading
if (loading_async_.contains(url)) { if (loading_async_.contains(url)) {
return; return;
@ -814,7 +829,7 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
pause_ = pause; pause_ = pause;
stream_change_type_ = change; stream_change_type_ = change;
autoscroll_ = autoscroll; autoscroll_ = autoscroll;
UrlHandler *url_handler = url_handlers_.value(url.scheme()); UrlHandler *url_handler = url_handlers_->GetUrlHandler(url);
HandleLoadResult(url_handler->StartLoading(url)); HandleLoadResult(url_handler->StartLoading(url));
} }
else { else {
@ -844,12 +859,12 @@ void Player::SeekTo(const quint64 seconds) {
engine_->Seek(nanosec); engine_->Seek(nanosec);
qLog(Debug) << "Track seeked to" << nanosec << "ns - updating scrobble point"; qLog(Debug) << "Track seeked to" << nanosec << "ns - updating scrobble point";
app_->playlist_manager()->active()->UpdateScrobblePoint(nanosec); playlist_manager_->active()->UpdateScrobblePoint(nanosec);
Q_EMIT Seeked(nanosec / 1000); Q_EMIT Seeked(nanosec / 1000);
if (seconds == 0) { if (seconds == 0) {
app_->playlist_manager()->active()->InformOfCurrentSongChange(false); playlist_manager_->active()->InformOfCurrentSongChange(false);
} }
} }
@ -865,26 +880,26 @@ void Player::SeekBackward() {
void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) { void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) {
if (engine_metadata.type == EngineMetadata::Type::Any || engine_metadata.type == EngineMetadata::Type::Current) { if (engine_metadata.type == EngineMetadata::Type::Any || engine_metadata.type == EngineMetadata::Type::Current) {
const int current_row = app_->playlist_manager()->active()->current_row(); const int current_row = playlist_manager_->active()->current_row();
if (current_row != -1) { if (current_row != -1) {
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item(); PlaylistItemPtr item = playlist_manager_->active()->current_item();
if (item && engine_metadata.media_url == item->Url()) { if (item && engine_metadata.media_url == item->Url()) {
Song song = item->Metadata(); Song song = item->Metadata();
song.MergeFromEngineMetadata(engine_metadata); song.MergeFromEngineMetadata(engine_metadata);
app_->playlist_manager()->active()->UpdateItemMetadata(current_row, item, song, true); playlist_manager_->active()->UpdateItemMetadata(current_row, item, song, true);
return; return;
} }
} }
} }
if (engine_metadata.type == EngineMetadata::Type::Any || engine_metadata.type == EngineMetadata::Type::Next) { if (engine_metadata.type == EngineMetadata::Type::Any || engine_metadata.type == EngineMetadata::Type::Next) {
const int next_row = app_->playlist_manager()->active()->next_row(); const int next_row = playlist_manager_->active()->next_row();
if (next_row != -1) { if (next_row != -1) {
PlaylistItemPtr next_item = app_->playlist_manager()->active()->item_at(next_row); PlaylistItemPtr next_item = playlist_manager_->active()->item_at(next_row);
if (engine_metadata.media_url == next_item->Url()) { if (engine_metadata.media_url == next_item->Url()) {
Song song = next_item->Metadata(); Song song = next_item->Metadata();
song.MergeFromEngineMetadata(engine_metadata); song.MergeFromEngineMetadata(engine_metadata);
app_->playlist_manager()->active()->UpdateItemMetadata(next_row, next_item, song, true); playlist_manager_->active()->UpdateItemMetadata(next_row, next_item, song, true);
} }
} }
} }
@ -893,9 +908,9 @@ void Player::EngineMetadataReceived(const EngineMetadata &engine_metadata) {
PlaylistItemPtr Player::GetItemAt(const int pos) const { PlaylistItemPtr Player::GetItemAt(const int pos) const {
if (pos < 0 || pos >= app_->playlist_manager()->active()->rowCount()) if (pos < 0 || pos >= playlist_manager_->active()->rowCount())
return PlaylistItemPtr(); return PlaylistItemPtr();
return app_->playlist_manager()->active()->item_at(pos); return playlist_manager_->active()->item_at(pos);
} }
@ -940,10 +955,10 @@ void Player::PlayWithPause(const quint64 offset_nanosec) {
pause_time_ = QDateTime(); pause_time_ = QDateTime();
play_offset_nanosec_ = offset_nanosec; play_offset_nanosec_ = offset_nanosec;
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id()); playlist_manager_->SetActivePlaylist(playlist_manager_->current_id());
if (app_->playlist_manager()->active()->rowCount() == 0) return; if (playlist_manager_->active()->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row(); int i = playlist_manager_->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row(); if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0; if (i == -1) i = 0;
PlayAt(i, true, offset_nanosec, EngineBase::TrackChangeType::First, Playlist::AutoScroll::Always, true); PlayAt(i, true, offset_nanosec, EngineBase::TrackChangeType::First, Playlist::AutoScroll::Always, true);
@ -959,11 +974,11 @@ void Player::TogglePrettyOSD() {
void Player::TrackAboutToEnd() { void Player::TrackAboutToEnd() {
const bool has_next_row = app_->playlist_manager()->active()->next_row() != -1; const bool has_next_row = playlist_manager_->active()->next_row() != -1;
PlaylistItemPtr next_item; PlaylistItemPtr next_item;
if (has_next_row) { if (has_next_row) {
next_item = app_->playlist_manager()->active()->item_at(app_->playlist_manager()->active()->next_row()); next_item = playlist_manager_->active()->item_at(playlist_manager_->active()->next_row());
} }
if (engine_->is_autocrossfade_enabled()) { if (engine_->is_autocrossfade_enabled()) {
@ -989,10 +1004,10 @@ void Player::TrackAboutToEnd() {
QUrl url = next_item->StreamUrl(); QUrl url = next_item->StreamUrl();
// Get the actual track URL rather than the stream URL. // Get the actual track URL rather than the stream URL.
if (url_handlers_.contains(url.scheme())) { if (url_handlers_->CanHandle(url)) {
if (loading_async_.contains(url)) return; if (loading_async_.contains(url)) return;
autoscroll_ = Playlist::AutoScroll::Maybe; autoscroll_ = Playlist::AutoScroll::Maybe;
UrlHandler *url_handler = url_handlers_.value(url.scheme()); UrlHandler *url_handler = url_handlers_->GetUrlHandler(url);
const UrlHandler::LoadResult result = url_handler->StartLoading(url); const UrlHandler::LoadResult result = url_handler->StartLoading(url);
switch (result.type_) { switch (result.type_) {
case UrlHandler::LoadResult::Type::Error: case UrlHandler::LoadResult::Type::Error:
@ -1045,57 +1060,6 @@ void Player::InvalidSongRequested(const QUrl &url) {
} }
void Player::RegisterUrlHandler(UrlHandler *handler) {
const QString scheme = handler->scheme();
if (url_handlers_.contains(scheme)) {
qLog(Warning) << "Tried to register a URL handler for" << scheme << "but one was already registered";
return;
}
qLog(Info) << "Registered URL handler for" << scheme;
url_handlers_.insert(scheme, handler);
QObject::connect(handler, &UrlHandler::destroyed, this, &Player::UrlHandlerDestroyed);
QObject::connect(handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
void Player::UnregisterUrlHandler(UrlHandler *handler) {
const QString scheme = url_handlers_.key(handler);
if (scheme.isEmpty()) {
qLog(Warning) << "Tried to unregister a URL handler for" << handler->scheme() << "that wasn't registered";
return;
}
qLog(Info) << "Unregistered URL handler for" << scheme;
url_handlers_.remove(scheme);
QObject::disconnect(handler, &UrlHandler::destroyed, this, &Player::UrlHandlerDestroyed);
QObject::disconnect(handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
const UrlHandler *Player::HandlerForUrl(const QUrl &url) const {
QMap<QString, UrlHandler*>::const_iterator it = url_handlers_.constFind(url.scheme());
if (it == url_handlers_.constEnd()) {
return nullptr;
}
return *it;
}
void Player::UrlHandlerDestroyed(QObject *object) {
UrlHandler *handler = static_cast<UrlHandler*>(object);
const QString scheme = url_handlers_.key(handler);
if (!scheme.isEmpty()) {
url_handlers_.remove(scheme);
}
}
void Player::HandleAuthentication() { void Player::HandleAuthentication() {
Q_EMIT Authenticated(); Q_EMIT Authenticated();
} }

View File

@ -31,110 +31,29 @@
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "urlhandler.h" #include "core/urlhandler.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "playlist/playlist.h" #include "playlist/playlist.h"
#include "playlist/playlistitem.h" #include "playlist/playlistitem.h"
#include "settings/behavioursettingspage.h" #include "constants/behavioursettings.h"
#include "playerinterface.h"
class QTimer; class QTimer;
class Application;
class Song; class Song;
class TaskManager;
class UrlHandlers;
class PlaylistManager;
class AnalyzerContainer; class AnalyzerContainer;
class Equalizer; class Equalizer;
class GstStartup; class GstStartup;
class PlayerInterface : public QObject {
Q_OBJECT
public:
explicit PlayerInterface(QObject *parent = nullptr) : QObject(parent) {}
virtual SharedPtr<EngineBase> engine() const = 0;
virtual EngineBase::State GetState() const = 0;
virtual uint GetVolume() const = 0;
virtual PlaylistItemPtr GetCurrentItem() const = 0;
virtual PlaylistItemPtr GetItemAt(const int pos) const = 0;
virtual void RegisterUrlHandler(UrlHandler *handler) = 0;
virtual void UnregisterUrlHandler(UrlHandler *handler) = 0;
public Q_SLOTS:
virtual void ReloadSettings() = 0;
virtual void LoadVolume() = 0;
virtual void SaveVolume() = 0;
virtual void SavePlaybackStatus() = 0;
virtual void PlaylistsLoaded() = 0;
// Manual track change to the specified track
virtual void PlayAt(const int index, const bool pause, const quint64 offset_nanosec, EngineBase::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) = 0;
// If there's currently a song playing, pause it, otherwise play the track that was playing last, or the first one on the playlist
virtual void PlayPause(const quint64 offset_nanosec = 0, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll::Always) = 0;
virtual void PlayPauseHelper() = 0;
virtual void RestartOrPrevious() = 0;
// Skips this track. Might load more of the current radio station.
virtual void Next() = 0;
virtual void Previous() = 0;
virtual void PlayPlaylist(const QString &playlist_name) = 0;
virtual void SetVolumeFromEngine(const uint volume) = 0;
virtual void SetVolumeFromSlider(const int value) = 0;
virtual void SetVolume(const uint volume) = 0;
virtual void VolumeUp() = 0;
virtual void VolumeDown() = 0;
virtual void SeekTo(const quint64 seconds) = 0;
// Moves the position of the currently playing song five seconds forward.
virtual void SeekForward() = 0;
// Moves the position of the currently playing song five seconds backwards.
virtual void SeekBackward() = 0;
virtual void CurrentMetadataChanged(const Song &metadata) = 0;
virtual void Mute() = 0;
virtual void Pause() = 0;
virtual void Stop(const bool stop_after = false) = 0;
virtual void Play(const quint64 offset_nanosec = 0) = 0;
virtual void PlayWithPause(const quint64 offset_nanosec) = 0;
virtual void PlayHelper() = 0;
virtual void ShowOSD() = 0;
Q_SIGNALS:
void Playing();
void Paused();
// Emitted only when playback is manually resumed
void Resumed();
void Stopped();
void Error(const QString &message = QString());
void PlaylistFinished();
void VolumeEnabled(const bool volume_enabled);
void VolumeChanged(const uint volume);
void TrackSkipped(PlaylistItemPtr old_track);
// Emitted when there's a manual change to the current's track position.
void Seeked(const qint64 microseconds);
// Emitted when Player has processed a request to play another song.
// This contains the URL of the song and a flag saying whether it was able to play the song.
void SongChangeRequestProcessed(const QUrl &url, const bool valid);
// The toggle parameter is true when user requests to toggle visibility for Pretty OSD
void ForceShowOSD(const Song &song, const bool toggle);
void Authenticated();
};
class Player : public PlayerInterface { class Player : public PlayerInterface {
Q_OBJECT Q_OBJECT
public: public:
explicit Player(Application *app, QObject *parent = nullptr); explicit Player(const SharedPtr<TaskManager> task_manager, const SharedPtr<UrlHandlers> url_handlers, const SharedPtr<PlaylistManager> playlist_manager, QObject *parent = nullptr);
static const char *kSettingsGroup;
EngineBase::Type CreateEngine(EngineBase::Type Type); EngineBase::Type CreateEngine(EngineBase::Type Type);
void Init(); void Init();
@ -146,11 +65,6 @@ class Player : public PlayerInterface {
PlaylistItemPtr GetCurrentItem() const override { return current_item_; } PlaylistItemPtr GetCurrentItem() const override { return current_item_; }
PlaylistItemPtr GetItemAt(const int pos) const override; PlaylistItemPtr GetItemAt(const int pos) const override;
void RegisterUrlHandler(UrlHandler *handler) override;
void UnregisterUrlHandler(UrlHandler *handler) override;
const UrlHandler *HandlerForUrl(const QUrl &url) const;
bool PreviousWouldRestartTrack() const; bool PreviousWouldRestartTrack() const;
void SetAnalyzer(AnalyzerContainer *analyzer) { analyzer_ = analyzer; } void SetAnalyzer(AnalyzerContainer *analyzer) { analyzer_ = analyzer; }
@ -158,6 +72,7 @@ class Player : public PlayerInterface {
public Q_SLOTS: public Q_SLOTS:
void ReloadSettings() override; void ReloadSettings() override;
void LoadVolume() override; void LoadVolume() override;
void SaveVolume() override; void SaveVolume() override;
void SavePlaybackStatus() override; void SavePlaybackStatus() override;
@ -197,6 +112,8 @@ class Player : public PlayerInterface {
void EngineChanged(const EngineBase::Type Type); void EngineChanged(const EngineBase::Type Type);
private Q_SLOTS: private Q_SLOTS:
void UrlHandlerRegistered(UrlHandler *url_handler) const;
void EngineStateChanged(const EngineBase::State); void EngineStateChanged(const EngineBase::State);
void EngineMetadataReceived(const EngineMetadata &engine_metadata); void EngineMetadataReceived(const EngineMetadata &engine_metadata);
void TrackAboutToEnd(); void TrackAboutToEnd();
@ -212,7 +129,6 @@ class Player : public PlayerInterface {
void ValidSongRequested(const QUrl&); void ValidSongRequested(const QUrl&);
void InvalidSongRequested(const QUrl&); void InvalidSongRequested(const QUrl&);
void UrlHandlerDestroyed(QObject *object);
void HandleLoadResult(const UrlHandler::LoadResult &result); void HandleLoadResult(const UrlHandler::LoadResult &result);
private: private:
@ -224,7 +140,9 @@ class Player : public PlayerInterface {
void UnPause(); void UnPause();
private: private:
Application *app_; const SharedPtr<TaskManager> task_manager_;
const SharedPtr<UrlHandlers> url_handlers_;
const SharedPtr<PlaylistManager> playlist_manager_;
SharedPtr<EngineBase> engine_; SharedPtr<EngineBase> engine_;
GstStartup *gst_startup_; GstStartup *gst_startup_;
AnalyzerContainer *analyzer_; AnalyzerContainer *analyzer_;
@ -242,8 +160,6 @@ class Player : public PlayerInterface {
EngineBase::State last_state_; EngineBase::State last_state_;
int nb_errors_received_; int nb_errors_received_;
QMap<QString, UrlHandler*> url_handlers_;
QList<QUrl> loading_async_; QList<QUrl> loading_async_;
uint volume_; uint volume_;
uint volume_before_mute_; uint volume_before_mute_;
@ -251,13 +167,12 @@ class Player : public PlayerInterface {
bool continue_on_error_; bool continue_on_error_;
bool greyout_; bool greyout_;
BehaviourSettingsPage::PreviousBehaviour menu_previousmode_; BehaviourSettings::PreviousBehaviour menu_previousmode_;
int seek_step_sec_; int seek_step_sec_;
uint volume_increment_; uint volume_increment_;
QDateTime pause_time_; QDateTime pause_time_;
quint64 play_offset_nanosec_; quint64 play_offset_nanosec_;
}; };
#endif // PLAYER_H #endif // PLAYER_H

View File

@ -0,0 +1,24 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "playerinterface.h"
PlayerInterface::PlayerInterface(QObject *parent) : QObject(parent) {}

117
src/core/playerinterface.h Normal file
View File

@ -0,0 +1,117 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PLAYERINTERFACE_H
#define PLAYERINTERFACE_H
#include "config.h"
#include <QObject>
#include <QString>
#include <QUrl>
#include "includes/shared_ptr.h"
#include "engine/enginebase.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
class QTimer;
class Song;
class PlayerInterface : public QObject {
Q_OBJECT
public:
explicit PlayerInterface(QObject *parent = nullptr);
virtual SharedPtr<EngineBase> engine() const = 0;
virtual EngineBase::State GetState() const = 0;
virtual uint GetVolume() const = 0;
virtual PlaylistItemPtr GetCurrentItem() const = 0;
virtual PlaylistItemPtr GetItemAt(const int pos) const = 0;
public Q_SLOTS:
virtual void ReloadSettings() = 0;
virtual void LoadVolume() = 0;
virtual void SaveVolume() = 0;
virtual void SavePlaybackStatus() = 0;
virtual void PlaylistsLoaded() = 0;
// Manual track change to the specified track
virtual void PlayAt(const int index, const bool pause, const quint64 offset_nanosec, EngineBase::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) = 0;
// If there's currently a song playing, pause it, otherwise play the track that was playing last, or the first one on the playlist
virtual void PlayPause(const quint64 offset_nanosec = 0, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll::Always) = 0;
virtual void PlayPauseHelper() = 0;
virtual void RestartOrPrevious() = 0;
// Skips this track. Might load more of the current radio station.
virtual void Next() = 0;
virtual void Previous() = 0;
virtual void PlayPlaylist(const QString &playlist_name) = 0;
virtual void SetVolumeFromEngine(const uint volume) = 0;
virtual void SetVolumeFromSlider(const int value) = 0;
virtual void SetVolume(const uint volume) = 0;
virtual void VolumeUp() = 0;
virtual void VolumeDown() = 0;
virtual void SeekTo(const quint64 seconds) = 0;
// Moves the position of the currently playing song five seconds forward.
virtual void SeekForward() = 0;
// Moves the position of the currently playing song five seconds backwards.
virtual void SeekBackward() = 0;
virtual void CurrentMetadataChanged(const Song &metadata) = 0;
virtual void Mute() = 0;
virtual void Pause() = 0;
virtual void Stop(const bool stop_after = false) = 0;
virtual void Play(const quint64 offset_nanosec = 0) = 0;
virtual void PlayWithPause(const quint64 offset_nanosec) = 0;
virtual void PlayHelper() = 0;
virtual void ShowOSD() = 0;
Q_SIGNALS:
void Playing();
void Paused();
// Emitted only when playback is manually resumed
void Resumed();
void Stopped();
void Error(const QString &message = QString());
void PlaylistFinished();
void VolumeEnabled(const bool volume_enabled);
void VolumeChanged(const uint volume);
void TrackSkipped(PlaylistItemPtr old_track);
// Emitted when there's a manual change to the current's track position.
void Seeked(const qint64 microseconds);
// Emitted when Player has processed a request to play another song.
// This contains the URL of the song and a flag saying whether it was able to play the song.
void SongChangeRequestProcessed(const QUrl &url, const bool valid);
// The toggle parameter is true when user requests to toggle visibility for Pretty OSD
void ForceShowOSD(const Song &song, const bool toggle);
void Authenticated();
};
#endif // PLAYERINTERFACE_H

View File

@ -51,11 +51,11 @@
#include <taglib/tstring.h> #include <taglib/tstring.h>
#include "core/iconloader.h" #include "core/iconloader.h"
#include "engine/enginemetadata.h" #include "core/enginemetadata.h"
#include "utilities/strutils.h" #include "utilities/strutils.h"
#include "utilities/timeutils.h" #include "utilities/timeutils.h"
#include "utilities/coverutils.h" #include "utilities/coverutils.h"
#include "utilities/timeconstants.h" #include "constants/timeconstants.h"
#include "utilities/sqlhelper.h" #include "utilities/sqlhelper.h"
#include "song.h" #include "song.h"
@ -1960,3 +1960,43 @@ QString Song::TitleRemoveMisc(const QString &title) {
return StripRegexList(title, kTitleMisc); return StripRegexList(title, kTitleMisc);
} }
QString Song::GetNameForNewPlaylist(const SongList &songs) {
if (songs.isEmpty()) {
return QObject::tr("Playlist");
}
QSet<QString> artists;
QSet<QString> albums;
artists.reserve(songs.count());
albums.reserve(songs.count());
for (const Song &song : songs) {
artists << (song.effective_albumartist().isEmpty() ? QObject::tr("Unknown") : song.effective_albumartist());
albums << (song.album().isEmpty() ? QObject::tr("Unknown") : song.album());
if (artists.size() > 1) {
break;
}
}
bool various_artists = artists.size() > 1;
QString result;
if (various_artists) {
result = QObject::tr("Various artists");
}
else {
QStringList artist_names = artists.values();
result = artist_names.first();
}
if (!various_artists && albums.size() == 1) {
QStringList album_names = albums.values();
result += " - "_L1 + album_names.first();
}
return result;
}

View File

@ -509,6 +509,8 @@ class Song {
static QString AlbumRemoveDiscMisc(const QString &album); static QString AlbumRemoveDiscMisc(const QString &album);
static QString TitleRemoveMisc(const QString &title); static QString TitleRemoveMisc(const QString &title);
static QString GetNameForNewPlaylist(const QList<Song> &songs);
static inline QString TagLibStringToQString(const TagLib::String &s) { static inline QString TagLibStringToQString(const TagLib::String &s) {
return QString::fromUtf8((s).toCString(true)); return QString::fromUtf8((s).toCString(true));
} }

View File

@ -39,15 +39,13 @@
#include <QUrl> #include <QUrl>
#include <QEventLoop> #include <QEventLoop>
#include "includes/shared_ptr.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/signalchecker.h"
#include "shared_ptr.h" #include "core/song.h"
#include "signalchecker.h" #include "core/database.h"
#include "player.h" #include "core/urlhandlers.h"
#include "song.h"
#include "songloader.h" #include "songloader.h"
#include "database.h"
#include "engine/enginebase.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "collection/collectionbackend.h" #include "collection/collectionbackend.h"
#include "playlistparsers/cueparser.h" #include "playlistparsers/cueparser.h"
@ -66,13 +64,17 @@ constexpr int kDefaultTimeout = 5000;
QSet<QString> SongLoader::sRawUriSchemes; QSet<QString> SongLoader::sRawUriSchemes;
SongLoader::SongLoader(SharedPtr<CollectionBackendInterface> collection_backend, const SharedPtr<Player> player, QObject *parent) SongLoader::SongLoader(const SharedPtr<UrlHandlers> url_handlers,
const SharedPtr<CollectionBackendInterface> collection_backend,
const SharedPtr<TagReaderClient> tagreader_client,
QObject *parent)
: QObject(parent), : QObject(parent),
player_(player), url_handlers_(url_handlers),
collection_backend_(collection_backend), collection_backend_(collection_backend),
tagreader_client_(tagreader_client),
timeout_timer_(new QTimer(this)), timeout_timer_(new QTimer(this)),
playlist_parser_(new PlaylistParser(collection_backend, this)), playlist_parser_(new PlaylistParser(tagreader_client, collection_backend, this)),
cue_parser_(new CueParser(collection_backend, this)), cue_parser_(new CueParser(tagreader_client, collection_backend, this)),
parser_(nullptr), parser_(nullptr),
state_(State::WaitingForType), state_(State::WaitingForType),
timeout_(kDefaultTimeout), timeout_(kDefaultTimeout),
@ -114,23 +116,16 @@ SongLoader::Result SongLoader::Load(const QUrl &url) {
return LoadLocal(url_.toLocalFile()); return LoadLocal(url_.toLocalFile());
} }
if (sRawUriSchemes.contains(url_.scheme()) || player_->HandlerForUrl(url)) { if (sRawUriSchemes.contains(url_.scheme()) || url_handlers_->CanHandle(url)) {
// The URI scheme indicates that it can't possibly be a playlist, // The URI scheme indicates that it can't possibly be a playlist,
// or we have a custom handler for the URL, so add it as a raw stream. // or we have a custom handler for the URL, so add it as a raw stream.
AddAsRawStream(); AddAsRawStream();
return Result::Success; return Result::Success;
} }
if (player_->engine()->type() == EngineBase::Type::GStreamer) {
preload_func_ = std::bind(&SongLoader::LoadRemote, this); preload_func_ = std::bind(&SongLoader::LoadRemote, this);
return Result::BlockingLoadRequired;
}
else {
errors_ << tr("You need GStreamer for this URL.");
return Result::Error;
}
return Result::Success; return Result::BlockingLoadRequired;
} }
@ -165,7 +160,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
// Assume it's just a normal file // Assume it's just a normal file
if (!Song::kRejectedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive) && if (!Song::kRejectedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive) &&
(TagReaderClient::Instance()->IsMediaFileBlocking(filename) || (tagreader_client_->IsMediaFileBlocking(filename) ||
Song::kAcceptedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive))) { Song::kAcceptedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive))) {
Song song(Song::Source::LocalFile); Song song(Song::Source::LocalFile);
song.InitFromFilePartial(filename, fileinfo); song.InitFromFilePartial(filename, fileinfo);
@ -183,19 +178,14 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
SongLoader::Result SongLoader::LoadAudioCD() { SongLoader::Result SongLoader::LoadAudioCD() {
#ifdef HAVE_AUDIOCD #ifdef HAVE_AUDIOCD
if (player_->engine()->type() == EngineBase::Type::GStreamer) {
CddaSongLoader *cdda_song_loader = new CddaSongLoader(QUrl(), this); CddaSongLoader *cdda_song_loader = new CddaSongLoader(QUrl(), this);
QObject::connect(cdda_song_loader, &CddaSongLoader::SongsDurationLoaded, this, &SongLoader::AudioCDTracksLoadFinishedSlot); QObject::connect(cdda_song_loader, &CddaSongLoader::SongsDurationLoaded, this, &SongLoader::AudioCDTracksLoadFinishedSlot);
QObject::connect(cdda_song_loader, &CddaSongLoader::SongsMetadataLoaded, this, &SongLoader::AudioCDTracksTagsLoaded); QObject::connect(cdda_song_loader, &CddaSongLoader::SongsMetadataLoaded, this, &SongLoader::AudioCDTracksTagsLoaded);
cdda_song_loader->LoadSongs(); cdda_song_loader->LoadSongs();
return Result::Success; return Result::Success;
} #else
else { errors_ << tr("Missing CDDA playback.");
#endif
errors_ << tr("CD playback is only available with the GStreamer engine.");
return Result::Error; return Result::Error;
#ifdef HAVE_AUDIOCD
}
#endif #endif
} }
@ -306,7 +296,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
// Assume it's just a normal file // Assume it's just a normal file
if (!Song::kRejectedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive) && if (!Song::kRejectedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive) &&
(TagReaderClient::Instance()->IsMediaFileBlocking(filename) || (tagreader_client_->IsMediaFileBlocking(filename) ||
Song::kAcceptedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive))) { Song::kAcceptedExtensions.contains(fileinfo.suffix(), Qt::CaseInsensitive))) {
Song song(Song::Source::LocalFile); Song song(Song::Source::LocalFile);
song.InitFromFilePartial(filename, fileinfo); song.InitFromFilePartial(filename, fileinfo);
@ -346,7 +336,7 @@ void SongLoader::EffectiveSongLoad(Song *song) {
else { else {
// It's a normal media file // It's a normal media file
const QString filename = song->url().toLocalFile(); const QString filename = song->url().toLocalFile();
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(filename, song); const TagReaderResult result = tagreader_client_->ReadFileBlocking(filename, song);
if (!result.success()) { if (!result.success()) {
qLog(Error) << "Could not read file" << song->url() << result.error_string(); qLog(Error) << "Could not read file" << song->url() << result.error_string();
} }

View File

@ -38,11 +38,12 @@
#include <QStringList> #include <QStringList>
#include <QUrl> #include <QUrl>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "song.h" #include "core/song.h"
class QTimer; class QTimer;
class Player; class UrlHandlers;
class TagReaderClient;
class CollectionBackendInterface; class CollectionBackendInterface;
class PlaylistParser; class PlaylistParser;
class ParserBase; class ParserBase;
@ -56,7 +57,11 @@ class SongLoader : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit SongLoader(SharedPtr<CollectionBackendInterface> collection_backend, const SharedPtr<Player> player, QObject *parent = nullptr); explicit SongLoader(const SharedPtr<UrlHandlers> url_handlers,
const SharedPtr<CollectionBackendInterface> collection_backend,
const SharedPtr<TagReaderClient> tagreader_client,
QObject *parent = nullptr);
~SongLoader() override; ~SongLoader() override;
enum class Result { enum class Result {
@ -137,8 +142,9 @@ class SongLoader : public QObject {
QUrl url_; QUrl url_;
SongList songs_; SongList songs_;
const SharedPtr<Player> player_; const SharedPtr<UrlHandlers> url_handlers_;
SharedPtr<CollectionBackendInterface> collection_backend_; const SharedPtr<CollectionBackendInterface> collection_backend_;
const SharedPtr<TagReaderClient> tagreader_client_;
QTimer *timeout_timer_; QTimer *timeout_timer_;
PlaylistParser *playlist_parser_; PlaylistParser *playlist_parser_;
CueParser *cue_parser_; CueParser *cue_parser_;

View File

@ -24,8 +24,8 @@
#include "config.h" #include "config.h"
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/mimedata.h" #include "mimedata.h"
#include "core/song.h" #include "core/song.h"
class CollectionBackendInterface; class CollectionBackendInterface;

View File

@ -35,8 +35,8 @@
#include <QPalette> #include <QPalette>
#include <QEvent> #include <QEvent>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/logging.h" #include "logging.h"
#include "stylesheetloader.h" #include "stylesheetloader.h"
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;

View File

@ -30,7 +30,7 @@
#include <QPalette> #include <QPalette>
#include <QString> #include <QString>
#include "shared_ptr.h" #include "includes/shared_ptr.h"
class QWidget; class QWidget;
class QEvent; class QEvent;

View File

@ -30,7 +30,7 @@
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
Translations::Translations() {} Translations::Translations() = default;
Translations::~Translations() { Translations::~Translations() {

View File

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

View File

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

95
src/core/urlhandlers.cpp Normal file
View File

@ -0,0 +1,95 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <QString>
#include "core/logging.h"
#include "urlhandlers.h"
#include "urlhandler.h"
UrlHandlers::UrlHandlers(QObject *parent) : QObject(parent) {}
void UrlHandlers::Register(UrlHandler *url_handler) {
const QString scheme = url_handler->scheme();
if (url_handlers_.contains(scheme)) {
qLog(Warning) << "Tried to register a URL handler for" << scheme << "but one was already registered";
return;
}
qLog(Info) << "Registered URL handler for" << scheme;
url_handlers_.insert(scheme, url_handler);
QObject::connect(url_handler, &UrlHandler::destroyed, this, &UrlHandlers::Destroyed);
Q_EMIT Registered(url_handler);
}
void UrlHandlers::Unregister(UrlHandler *url_handler) {
const QString scheme = url_handlers_.key(url_handler);
if (scheme.isEmpty()) {
qLog(Warning) << "Tried to unregister a URL handler for" << url_handler->scheme() << "that wasn't registered";
return;
}
qLog(Info) << "Unregistered URL handler for" << scheme;
url_handlers_.remove(scheme);
QObject::disconnect(url_handler, &UrlHandler::destroyed, this, &UrlHandlers::Destroyed);
QObject::disconnect(url_handler, &UrlHandler::AsyncLoadComplete, nullptr, nullptr);
}
void UrlHandlers::Destroyed(QObject *object) {
UrlHandler *handler = static_cast<UrlHandler*>(object);
const QString scheme = url_handlers_.key(handler);
if (!scheme.isEmpty()) {
url_handlers_.remove(scheme);
}
}
bool UrlHandlers::CanHandle(const QString &scheme) const {
return url_handlers_.contains(scheme);
}
bool UrlHandlers::CanHandle(const QUrl &url) const {
return url_handlers_.contains(url.scheme());
}
UrlHandler *UrlHandlers::GetUrlHandler(const QString &scheme) const {
if (!CanHandle(scheme)) return nullptr;
return url_handlers_.value(scheme);
}
UrlHandler *UrlHandlers::GetUrlHandler(const QUrl &url) const {
return GetUrlHandler(url.scheme());
}

56
src/core/urlhandlers.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Strawberry Music Player
* Copyright 2024, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef URLHANDLERS_H
#define URLHANDLERS_H
#include "config.h"
#include <QObject>
#include <QMap>
#include <QString>
#include <QUrl>
class UrlHandler;
class UrlHandlers : public QObject {
Q_OBJECT
public:
explicit UrlHandlers(QObject *parent = nullptr);
void Register(UrlHandler *url_handler);
void Unregister(UrlHandler *url_handler);
void Destroyed(QObject *object);
bool CanHandle(const QString &scheme) const;
bool CanHandle(const QUrl &url) const;
UrlHandler *GetUrlHandler(const QString &scheme) const;
UrlHandler *GetUrlHandler(const QUrl &url) const;
Q_SIGNALS:
void Registered(UrlHandler *url_handler);
void UnRegistered(UrlHandler *url_handler);
private:
QMap<QString, UrlHandler*> url_handlers_;
};
#endif // URLHANDLERS_H

View File

@ -54,20 +54,21 @@
#include <QSettings> #include <QSettings>
#include <QtEvents> #include <QtEvents>
#include "utilities/filenameconstants.h" #include "constants/filenameconstants.h"
#include "constants/filefilterconstants.h"
#include "constants/coverssettings.h"
#include "utilities/strutils.h" #include "utilities/strutils.h"
#include "utilities/mimeutils.h" #include "utilities/mimeutils.h"
#include "utilities/coveroptions.h" #include "utilities/coveroptions.h"
#include "utilities/coverutils.h" #include "utilities/coverutils.h"
#include "utilities/screenutils.h" #include "utilities/screenutils.h"
#include "core/application.h" #include "core/logging.h"
#include "core/song.h" #include "core/song.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "core/settings.h" #include "core/settings.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "collection/collectionfilteroptions.h" #include "collection/collectionfilteroptions.h"
#include "collection/collectionbackend.h" #include "collection/collectionbackend.h"
#include "settings/coverssettingspage.h"
#include "streaming/streamingservices.h" #include "streaming/streamingservices.h"
#include "streaming/streamingservice.h" #include "streaming/streamingservice.h"
#include "albumcoverchoicecontroller.h" #include "albumcoverchoicecontroller.h"
@ -80,15 +81,10 @@
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
const char *AlbumCoverChoiceController::kLoadImageFileFilter = QT_TR_NOOP("Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm)");
const char *AlbumCoverChoiceController::kSaveImageFileFilter = QT_TR_NOOP("Images (*.png *.jpg *.jpeg *.bmp *.xpm *.pbm *.ppm *.xbm)");
const char *AlbumCoverChoiceController::kAllFilesFilter = QT_TR_NOOP("All files (*)");
QSet<QString> *AlbumCoverChoiceController::sImageExtensions = nullptr; QSet<QString> *AlbumCoverChoiceController::sImageExtensions = nullptr;
AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent) AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent)
: QWidget(parent), : QWidget(parent),
app_(nullptr),
cover_searcher_(nullptr), cover_searcher_(nullptr),
cover_fetcher_(nullptr), cover_fetcher_(nullptr),
save_file_dialog_(nullptr), save_file_dialog_(nullptr),
@ -130,12 +126,22 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent)
AlbumCoverChoiceController::~AlbumCoverChoiceController() = default; AlbumCoverChoiceController::~AlbumCoverChoiceController() = default;
void AlbumCoverChoiceController::Init(Application *app) { void AlbumCoverChoiceController::Init(const SharedPtr<NetworkAccessManager> network,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<CollectionBackend> collection_backend,
const SharedPtr<AlbumCoverLoader> albumcover_loader,
const SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader,
const SharedPtr<CoverProviders> cover_providers,
const SharedPtr<StreamingServices> streaming_services) {
app_ = app; network_ = network;
tagreader_client_ = tagreader_client;
collection_backend_ = collection_backend;
current_albumcover_loader_ = current_albumcover_loader;
streaming_services_ = streaming_services;
cover_fetcher_ = new AlbumCoverFetcher(app_->cover_providers(), app->network(), this); cover_fetcher_ = new AlbumCoverFetcher(cover_providers, network, this);
cover_searcher_ = new AlbumCoverSearcher(QIcon(u":/pictures/cdcase.png"_s), app, this); cover_searcher_ = new AlbumCoverSearcher(QIcon(u":/pictures/cdcase.png"_s), albumcover_loader, this);
cover_searcher_->Init(cover_fetcher_); cover_searcher_->Init(cover_fetcher_);
QObject::connect(cover_fetcher_, &AlbumCoverFetcher::AlbumCoverFetched, this, &AlbumCoverChoiceController::AlbumCoverFetched); QObject::connect(cover_fetcher_, &AlbumCoverFetcher::AlbumCoverFetched, this, &AlbumCoverChoiceController::AlbumCoverFetched);
@ -145,13 +151,13 @@ void AlbumCoverChoiceController::Init(Application *app) {
void AlbumCoverChoiceController::ReloadSettings() { void AlbumCoverChoiceController::ReloadSettings() {
Settings s; Settings s;
s.beginGroup(CoversSettingsPage::kSettingsGroup); s.beginGroup(CoversSettings::kSettingsGroup);
cover_options_.cover_type = static_cast<CoverOptions::CoverType>(s.value(CoversSettingsPage::kSaveType, static_cast<int>(CoverOptions::CoverType::Cache)).toInt()); cover_options_.cover_type = static_cast<CoverOptions::CoverType>(s.value(CoversSettings::kSaveType, static_cast<int>(CoverOptions::CoverType::Cache)).toInt());
cover_options_.cover_filename = static_cast<CoverOptions::CoverFilename>(s.value(CoversSettingsPage::kSaveFilename, static_cast<int>(CoverOptions::CoverFilename::Pattern)).toInt()); cover_options_.cover_filename = static_cast<CoverOptions::CoverFilename>(s.value(CoversSettings::kSaveFilename, static_cast<int>(CoverOptions::CoverFilename::Pattern)).toInt());
cover_options_.cover_pattern = s.value(CoversSettingsPage::kSavePattern, u"%albumartist-%album"_s).toString(); cover_options_.cover_pattern = s.value(CoversSettings::kSavePattern, u"%albumartist-%album"_s).toString();
cover_options_.cover_overwrite = s.value(CoversSettingsPage::kSaveOverwrite, false).toBool(); cover_options_.cover_overwrite = s.value(CoversSettings::kSaveOverwrite, false).toBool();
cover_options_.cover_lowercase = s.value(CoversSettingsPage::kSaveLowercase, false).toBool(); cover_options_.cover_lowercase = s.value(CoversSettings::kSaveLowercase, false).toBool();
cover_options_.cover_replace_spaces = s.value(CoversSettingsPage::kSaveReplaceSpaces, false).toBool(); cover_options_.cover_replace_spaces = s.value(CoversSettings::kSaveReplaceSpaces, false).toBool();
s.endGroup(); s.endGroup();
cover_types_ = AlbumCoverLoaderOptions::LoadTypes(); cover_types_ = AlbumCoverLoaderOptions::LoadTypes();
@ -179,7 +185,7 @@ AlbumCoverImageResult AlbumCoverChoiceController::LoadImageFromFile(Song *song)
return AlbumCoverImageResult(); return AlbumCoverImageResult();
} }
QString cover_file = QFileDialog::getOpenFileName(this, tr("Load cover from disk"), GetInitialPathForFileDialog(*song, QString()), tr(kLoadImageFileFilter) + u";;"_s + tr(kAllFilesFilter)); QString cover_file = QFileDialog::getOpenFileName(this, tr("Load cover from disk"), GetInitialPathForFileDialog(*song, QString()), tr(kLoadImageFileFilter) + u";;"_s + tr(kAllFilesFilterSpec));
if (cover_file.isEmpty()) return AlbumCoverImageResult(); if (cover_file.isEmpty()) return AlbumCoverImageResult();
QFile file(cover_file); QFile file(cover_file);
@ -210,7 +216,7 @@ QUrl AlbumCoverChoiceController::LoadCoverFromFile(Song *song) {
if (!song->url().isValid() || !song->url().isLocalFile() || song->effective_albumartist().isEmpty() || song->album().isEmpty()) return QUrl(); if (!song->url().isValid() || !song->url().isLocalFile() || song->effective_albumartist().isEmpty() || song->album().isEmpty()) return QUrl();
QString cover_file = QFileDialog::getOpenFileName(this, tr("Load cover from disk"), GetInitialPathForFileDialog(*song, QString()), tr(kLoadImageFileFilter) + u";;"_s + tr(kAllFilesFilter)); QString cover_file = QFileDialog::getOpenFileName(this, tr("Load cover from disk"), GetInitialPathForFileDialog(*song, QString()), tr(kLoadImageFileFilter) + u";;"_s + tr(kAllFilesFilterSpec));
if (cover_file.isEmpty() || QImage(cover_file).isNull()) return QUrl(); if (cover_file.isEmpty() || QImage(cover_file).isNull()) return QUrl();
switch (get_save_album_cover_type()) { switch (get_save_album_cover_type()) {
@ -246,7 +252,7 @@ void AlbumCoverChoiceController::SaveCoverToFileManual(const Song &song, const A
static const QRegularExpression regex_invalid_fat_characters(QLatin1String(kInvalidFatCharactersRegex), QRegularExpression::CaseInsensitiveOption); static const QRegularExpression regex_invalid_fat_characters(QLatin1String(kInvalidFatCharactersRegex), QRegularExpression::CaseInsensitiveOption);
initial_file_name.remove(regex_invalid_fat_characters); initial_file_name.remove(regex_invalid_fat_characters);
QString save_filename = QFileDialog::getSaveFileName(this, tr("Save album cover"), GetInitialPathForFileDialog(song, initial_file_name), tr(kSaveImageFileFilter) + u";;"_s + tr(kAllFilesFilter)); QString save_filename = QFileDialog::getSaveFileName(this, tr("Save album cover"), GetInitialPathForFileDialog(song, initial_file_name), tr(kSaveImageFileFilter) + u";;"_s + tr(kAllFilesFilterSpec));
if (save_filename.isEmpty()) return; if (save_filename.isEmpty()) return;
@ -316,7 +322,7 @@ void AlbumCoverChoiceController::LoadCoverFromURL(Song *song) {
AlbumCoverImageResult AlbumCoverChoiceController::LoadImageFromURL() { AlbumCoverImageResult AlbumCoverChoiceController::LoadImageFromURL() {
if (!cover_from_url_dialog_) { cover_from_url_dialog_ = new CoverFromURLDialog(app_->network(), this); } if (!cover_from_url_dialog_) { cover_from_url_dialog_ = new CoverFromURLDialog(network_, this); }
return cover_from_url_dialog_->Exec(); return cover_from_url_dialog_->Exec();
@ -446,7 +452,7 @@ void AlbumCoverChoiceController::ShowCover(const Song &song, const QImage &image
case AlbumCoverLoaderOptions::Type::Embedded:{ case AlbumCoverLoaderOptions::Type::Embedded:{
if (song.art_embedded() && !song.url().isEmpty() && song.url().isValid() && song.url().isLocalFile()) { if (song.art_embedded() && !song.url().isEmpty() && song.url().isValid() && song.url().isLocalFile()) {
QImage image_embedded_cover; QImage image_embedded_cover;
const TagReaderResult result = TagReaderClient::Instance()->LoadCoverImageBlocking(song.url().toLocalFile(), image_embedded_cover); const TagReaderResult result = tagreader_client_->LoadCoverImageBlocking(song.url().toLocalFile(), image_embedded_cover);
if (result.success() && !image_embedded_cover.isNull()) { if (result.success() && !image_embedded_cover.isNull()) {
QPixmap pixmap = QPixmap::fromImage(image_embedded_cover); QPixmap pixmap = QPixmap::fromImage(image_embedded_cover);
if (!pixmap.isNull()) { if (!pixmap.isNull()) {
@ -548,11 +554,11 @@ void AlbumCoverChoiceController::SaveArtEmbeddedToSong(Song *song, const bool ar
song->set_art_unset(false); song->set_art_unset(false);
if (song->source() == Song::Source::Collection) { if (song->source() == Song::Source::Collection) {
app_->collection_backend()->UpdateEmbeddedAlbumArtAsync(song->effective_albumartist(), song->album(), art_embedded); collection_backend_->UpdateEmbeddedAlbumArtAsync(song->effective_albumartist(), song->album(), art_embedded);
} }
if (*song == app_->current_albumcover_loader()->last_song()) { if (*song == current_albumcover_loader_->last_song()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song); current_albumcover_loader_->LoadAlbumCover(*song);
} }
} }
@ -567,7 +573,7 @@ void AlbumCoverChoiceController::SaveArtManualToSong(Song *song, const QUrl &art
// Update the backends. // Update the backends.
switch (song->source()) { switch (song->source()) {
case Song::Source::Collection: case Song::Source::Collection:
app_->collection_backend()->UpdateManualAlbumArtAsync(song->effective_albumartist(), song->album(), art_manual); collection_backend_->UpdateManualAlbumArtAsync(song->effective_albumartist(), song->album(), art_manual);
break; break;
case Song::Source::LocalFile: case Song::Source::LocalFile:
case Song::Source::CDDA: case Song::Source::CDDA:
@ -581,7 +587,7 @@ void AlbumCoverChoiceController::SaveArtManualToSong(Song *song, const QUrl &art
case Song::Source::Tidal: case Song::Source::Tidal:
case Song::Source::Spotify: case Song::Source::Spotify:
case Song::Source::Qobuz: case Song::Source::Qobuz:
StreamingServicePtr service = app_->streaming_services()->ServiceBySource(song->source()); StreamingServicePtr service = streaming_services_->ServiceBySource(song->source());
if (!service) break; if (!service) break;
if (service->artists_collection_backend()) { if (service->artists_collection_backend()) {
service->artists_collection_backend()->UpdateManualAlbumArtAsync(song->effective_albumartist(), song->album(), art_manual); service->artists_collection_backend()->UpdateManualAlbumArtAsync(song->effective_albumartist(), song->album(), art_manual);
@ -595,8 +601,8 @@ void AlbumCoverChoiceController::SaveArtManualToSong(Song *song, const QUrl &art
break; break;
} }
if (*song == app_->current_albumcover_loader()->last_song()) { if (*song == current_albumcover_loader_->last_song()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song); current_albumcover_loader_->LoadAlbumCover(*song);
} }
} }
@ -611,11 +617,11 @@ void AlbumCoverChoiceController::ClearAlbumCoverForSong(Song *song) {
song->clear_art_manual(); song->clear_art_manual();
if (song->source() == Song::Source::Collection) { if (song->source() == Song::Source::Collection) {
app_->collection_backend()->ClearAlbumArtAsync(song->effective_albumartist(), song->album(), false); collection_backend_->ClearAlbumArtAsync(song->effective_albumartist(), song->album(), false);
} }
if (*song == app_->current_albumcover_loader()->last_song()) { if (*song == current_albumcover_loader_->last_song()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song); current_albumcover_loader_->LoadAlbumCover(*song);
} }
} }
@ -630,11 +636,11 @@ void AlbumCoverChoiceController::UnsetAlbumCoverForSong(Song *song) {
song->clear_art_automatic(); song->clear_art_automatic();
if (song->source() == Song::Source::Collection) { if (song->source() == Song::Source::Collection) {
app_->collection_backend()->UnsetAlbumArtAsync(song->effective_albumartist(), song->album()); collection_backend_->UnsetAlbumArtAsync(song->effective_albumartist(), song->album());
} }
if (*song == app_->current_albumcover_loader()->last_song()) { if (*song == current_albumcover_loader_->last_song()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song); current_albumcover_loader_->LoadAlbumCover(*song);
} }
} }
@ -718,7 +724,7 @@ void AlbumCoverChoiceController::SaveCoverEmbeddedToCollectionSongs(const Song &
void AlbumCoverChoiceController::SaveCoverEmbeddedToCollectionSongs(const QString &effective_albumartist, const QString &effective_album, const QString &cover_filename, const QByteArray &image_data, const QString &mime_type) { void AlbumCoverChoiceController::SaveCoverEmbeddedToCollectionSongs(const QString &effective_albumartist, const QString &effective_album, const QString &cover_filename, const QByteArray &image_data, const QString &mime_type) {
QFuture<SongList> future = QtConcurrent::run(&CollectionBackend::GetAlbumSongs, app_->collection_backend(), effective_albumartist, effective_album, CollectionFilterOptions()); QFuture<SongList> future = QtConcurrent::run(&CollectionBackend::GetAlbumSongs, collection_backend_, effective_albumartist, effective_album, CollectionFilterOptions());
QFutureWatcher<SongList> *watcher = new QFutureWatcher<SongList>(); QFutureWatcher<SongList> *watcher = new QFutureWatcher<SongList>();
QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, cover_filename, image_data, mime_type]() { QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, cover_filename, image_data, mime_type]() {
const SongList collection_songs = watcher->result(); const SongList collection_songs = watcher->result();
@ -736,7 +742,7 @@ void AlbumCoverChoiceController::SaveCoverEmbeddedToSong(const Song &song, const
QMutexLocker l(&mutex_cover_save_tasks_); QMutexLocker l(&mutex_cover_save_tasks_);
cover_save_tasks_.append(song); cover_save_tasks_.append(song);
const bool art_embedded = !image_data.isNull(); const bool art_embedded = !image_data.isNull();
TagReaderReplyPtr reply = app_->tag_reader_client()->SaveCoverAsync(song.url().toLocalFile(), SaveTagCoverData(cover_filename, image_data, mime_type)); TagReaderReplyPtr reply = tagreader_client_->SaveCoverAsync(song.url().toLocalFile(), SaveTagCoverData(cover_filename, image_data, mime_type));
QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, song, art_embedded]() { SaveEmbeddedCoverFinished(reply, song, art_embedded); }); QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, song, art_embedded]() { SaveEmbeddedCoverFinished(reply, song, art_embedded); });
} }

View File

@ -50,25 +50,33 @@ class QMenu;
class QDragEnterEvent; class QDragEnterEvent;
class QDropEvent; class QDropEvent;
class Application; class NetworkAccessManager;
class CollectionBackend;
class AlbumCoverLoader;
class CurrentAlbumCoverLoader;
class CoverProviders;
class AlbumCoverFetcher; class AlbumCoverFetcher;
class AlbumCoverSearcher; class AlbumCoverSearcher;
class CoverFromURLDialog; class CoverFromURLDialog;
struct CoverSearchStatistics; struct CoverSearchStatistics;
class StreamingServices;
// Controller for the common album cover related menu options. // Controller for the common album cover related menu options.
class AlbumCoverChoiceController : public QWidget { class AlbumCoverChoiceController : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
static const char *kLoadImageFileFilter;
static const char *kSaveImageFileFilter;
static const char *kAllFilesFilter;
explicit AlbumCoverChoiceController(QWidget *parent = nullptr); explicit AlbumCoverChoiceController(QWidget *parent = nullptr);
~AlbumCoverChoiceController() override; ~AlbumCoverChoiceController() override;
void Init(Application *app); void Init(const SharedPtr<NetworkAccessManager> network,
const SharedPtr<TagReaderClient> tagreader_client,
const SharedPtr<CollectionBackend> collection_backend,
const SharedPtr<AlbumCoverLoader> albumcover_loader,
const SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader,
const SharedPtr<CoverProviders> cover_providers,
const SharedPtr<StreamingServices> streaming_services);
void ReloadSettings(); void ReloadSettings();
CoverOptions::CoverType get_save_album_cover_type() const { return (save_embedded_cover_override_ ? CoverOptions::CoverType::Embedded : cover_options_.cover_type); } CoverOptions::CoverType get_save_album_cover_type() const { return (save_embedded_cover_override_ ? CoverOptions::CoverType::Embedded : cover_options_.cover_type); }
@ -172,7 +180,12 @@ class AlbumCoverChoiceController : public QWidget {
static bool IsKnownImageExtension(const QString &suffix); static bool IsKnownImageExtension(const QString &suffix);
static QSet<QString> *sImageExtensions; static QSet<QString> *sImageExtensions;
Application *app_; SharedPtr<CurrentAlbumCoverLoader> current_albumcover_loader_;
SharedPtr<NetworkAccessManager> network_;
SharedPtr<TagReaderClient> tagreader_client_;
SharedPtr<CollectionBackend> collection_backend_;
SharedPtr<StreamingServices> streaming_services_;
AlbumCoverSearcher *cover_searcher_; AlbumCoverSearcher *cover_searcher_;
AlbumCoverFetcher *cover_fetcher_; AlbumCoverFetcher *cover_fetcher_;

View File

@ -37,7 +37,9 @@
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
const char *AlbumCoverExport::kSettingsGroup = "AlbumCoverExport"; namespace {
constexpr char kSettingsGroup[] = "AlbumCoverExport";
}
AlbumCoverExport::AlbumCoverExport(QWidget *parent) : QDialog(parent), ui_(new Ui_AlbumCoverExport) { AlbumCoverExport::AlbumCoverExport(QWidget *parent) : QDialog(parent), ui_(new Ui_AlbumCoverExport) {

View File

@ -78,8 +78,6 @@ class AlbumCoverExport : public QDialog {
private: private:
Ui_AlbumCoverExport *ui_; Ui_AlbumCoverExport *ui_;
static const char *kSettingsGroup;
}; };
#endif // ALBUMCOVEREXPORT_H #endif // ALBUMCOVEREXPORT_H

View File

@ -33,8 +33,9 @@ namespace {
constexpr int kMaxConcurrentRequests = 3; constexpr int kMaxConcurrentRequests = 3;
} }
AlbumCoverExporter::AlbumCoverExporter(QObject *parent) AlbumCoverExporter::AlbumCoverExporter(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent)
: QObject(parent), : QObject(parent),
tagreader_client_(tagreader_client),
thread_pool_(new QThreadPool(this)), thread_pool_(new QThreadPool(this)),
exported_(0), exported_(0),
skipped_(0), skipped_(0),
@ -52,7 +53,7 @@ void AlbumCoverExporter::SetCoverTypes(const AlbumCoverLoaderOptions::Types &cov
void AlbumCoverExporter::AddExportRequest(const Song &song) { void AlbumCoverExporter::AddExportRequest(const Song &song) {
requests_.append(new CoverExportRunnable(dialog_result_, cover_types_, song)); requests_.append(new CoverExportRunnable(tagreader_client_, dialog_result_, cover_types_, song));
all_ = static_cast<int>(requests_.count()); all_ = static_cast<int>(requests_.count());
} }

View File

@ -27,18 +27,21 @@
#include <QQueue> #include <QQueue>
#include <QString> #include <QString>
#include "includes/shared_ptr.h"
#include "albumcoverloaderoptions.h" #include "albumcoverloaderoptions.h"
#include "albumcoverexport.h" #include "albumcoverexport.h"
class QThreadPool; class QThreadPool;
class Song; class Song;
class CoverExportRunnable; class CoverExportRunnable;
class TagReaderClient;
class AlbumCoverExporter : public QObject { class AlbumCoverExporter : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit AlbumCoverExporter(QObject *parent = nullptr); explicit AlbumCoverExporter(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent = nullptr);
void SetDialogResult(const AlbumCoverExport::DialogResult &dialog_result); void SetDialogResult(const AlbumCoverExport::DialogResult &dialog_result);
void SetCoverTypes(const AlbumCoverLoaderOptions::Types &cover_types); void SetCoverTypes(const AlbumCoverLoaderOptions::Types &cover_types);
@ -58,6 +61,8 @@ class AlbumCoverExporter : public QObject {
private: private:
void AddJobsToPool(); void AddJobsToPool();
const SharedPtr<TagReaderClient> tagreader_client_;
AlbumCoverLoaderOptions::Types cover_types_; AlbumCoverLoaderOptions::Types cover_types_;
AlbumCoverExport::DialogResult dialog_result_; AlbumCoverExport::DialogResult dialog_result_;

View File

@ -28,7 +28,7 @@
#include <QTimer> #include <QTimer>
#include <QString> #include <QString>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/networkaccessmanager.h" #include "core/networkaccessmanager.h"
#include "core/song.h" #include "core/song.h"
#include "albumcoverfetcher.h" #include "albumcoverfetcher.h"

View File

@ -36,7 +36,7 @@
#include <QUrl> #include <QUrl>
#include <QImage> #include <QImage>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "coversearchstatistics.h" #include "coversearchstatistics.h"
#include "albumcoverimageresult.h" #include "albumcoverimageresult.h"

View File

@ -36,8 +36,8 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkReply> #include <QNetworkReply>
#include "includes/shared_ptr.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/shared_ptr.h"
#include "core/networkaccessmanager.h" #include "core/networkaccessmanager.h"
#include "core/networktimeouts.h" #include "core/networktimeouts.h"
#include "utilities/imageutils.h" #include "utilities/imageutils.h"

View File

@ -35,7 +35,7 @@
#include <QUrl> #include <QUrl>
#include <QImage> #include <QImage>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "albumcoverfetcher.h" #include "albumcoverfetcher.h"
#include "coversearchstatistics.h" #include "coversearchstatistics.h"
#include "albumcoverimageresult.h" #include "albumcoverimageresult.h"

View File

@ -36,6 +36,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include "includes/shared_ptr.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/networkaccessmanager.h" #include "core/networkaccessmanager.h"
#include "core/song.h" #include "core/song.h"
@ -54,8 +55,9 @@ namespace {
constexpr int kMaxRedirects = 3; constexpr int kMaxRedirects = 3;
} }
AlbumCoverLoader::AlbumCoverLoader(QObject *parent) AlbumCoverLoader::AlbumCoverLoader(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent)
: QObject(parent), : QObject(parent),
tagreader_client_(tagreader_client),
network_(new NetworkAccessManager(this)), network_(new NetworkAccessManager(this)),
timer_process_tasks_(new QTimer(this)), timer_process_tasks_(new QTimer(this)),
stop_requested_(false), stop_requested_(false),
@ -318,7 +320,7 @@ AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadImage(TaskPtr task, cons
AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadEmbeddedImage(TaskPtr task) { AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadEmbeddedImage(TaskPtr task) {
if (task->art_embedded && task->song_url.isValid() && task->song_url.isLocalFile()) { if (task->art_embedded && task->song_url.isValid() && task->song_url.isLocalFile()) {
const TagReaderResult result = TagReaderClient::Instance()->LoadCoverDataBlocking(task->song_url.toLocalFile(), task->album_cover.image_data); const TagReaderResult result = tagreader_client_->LoadCoverDataBlocking(task->song_url.toLocalFile(), task->album_cover.image_data);
if (result.success() && !task->album_cover.image_data.isEmpty() && task->album_cover.image.loadFromData(task->album_cover.image_data)) { if (result.success() && !task->album_cover.image_data.isEmpty() && task->album_cover.image.loadFromData(task->album_cover.image_data)) {
return LoadImageResult(AlbumCoverLoaderResult::Type::Embedded, LoadImageResult::Status::Success); return LoadImageResult(AlbumCoverLoaderResult::Type::Embedded, LoadImageResult::Status::Success);
} }

View File

@ -32,7 +32,7 @@
#include <QString> #include <QString>
#include <QImage> #include <QImage>
#include "core/shared_ptr.h" #include "includes/shared_ptr.h"
#include "core/song.h" #include "core/song.h"
#include "albumcoverloaderoptions.h" #include "albumcoverloaderoptions.h"
#include "albumcoverloaderresult.h" #include "albumcoverloaderresult.h"
@ -42,12 +42,13 @@ class QThread;
class QTimer; class QTimer;
class QNetworkReply; class QNetworkReply;
class NetworkAccessManager; class NetworkAccessManager;
class TagReaderClient;
class AlbumCoverLoader : public QObject { class AlbumCoverLoader : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit AlbumCoverLoader(QObject *parent = nullptr); explicit AlbumCoverLoader(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent = nullptr);
void ExitAsync(); void ExitAsync();
void Stop() { stop_requested_ = true; } void Stop() { stop_requested_ = true; }
@ -125,7 +126,8 @@ class AlbumCoverLoader : public QObject {
void LoadRemoteImageFinished(QNetworkReply *reply, AlbumCoverLoader::TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QUrl &cover_url); void LoadRemoteImageFinished(QNetworkReply *reply, AlbumCoverLoader::TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QUrl &cover_url);
private: private:
SharedPtr<NetworkAccessManager> network_; const SharedPtr<TagReaderClient> tagreader_client_;
const SharedPtr<NetworkAccessManager> network_;
QTimer *timer_process_tasks_; QTimer *timer_process_tasks_;
bool stop_requested_; bool stop_requested_;
QMutex mutex_load_image_async_; QMutex mutex_load_image_async_;

View File

@ -22,7 +22,7 @@
#include <QSettings> #include <QSettings>
#include "core/settings.h" #include "core/settings.h"
#include "settings/coverssettingspage.h" #include "constants/coverssettings.h"
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
@ -37,9 +37,9 @@ AlbumCoverLoaderOptions::Types AlbumCoverLoaderOptions::LoadTypes() {
Types cover_types; Types cover_types;
Settings s; Settings s;
s.beginGroup(CoversSettingsPage::kSettingsGroup); s.beginGroup(CoversSettings::kSettingsGroup);
const QStringList all_cover_types = QStringList() << u"art_unset"_s << u"art_embedded"_s << u"art_manual"_s << u"art_automatic"_s; const QStringList all_cover_types = QStringList() << u"art_unset"_s << u"art_embedded"_s << u"art_manual"_s << u"art_automatic"_s;
const QStringList cover_types_strlist = s.value(CoversSettingsPage::kTypes, all_cover_types).toStringList(); const QStringList cover_types_strlist = s.value(CoversSettings::kTypes, all_cover_types).toStringList();
for (const QString &cover_type_str : cover_types_strlist) { for (const QString &cover_type_str : cover_types_strlist) {
if (cover_type_str == "art_unset"_L1) { if (cover_type_str == "art_unset"_L1) {
cover_types << AlbumCoverLoaderOptions::Type::Unset; cover_types << AlbumCoverLoaderOptions::Type::Unset;

Some files were not shown because too many files have changed in this diff Show More