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()
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
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)
if(UNIX AND NOT APPLE)
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)
endif()
endif()
pkg_check_modules(LIBCDIO IMPORTED_TARGET libcdio)
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"
DEPENDS "libgio" GIO_FOUND
DEPENDS "Unix or Windows" "NOT APPLE"
)
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
DEPENDS "libgio-unix" GIO_UNIX_FOUND
DEPENDS "Unix or Windows" "NOT APPLE"
)
optional_component(AUDIOCD ON "Devices: Audio CD support"
@ -406,9 +406,11 @@ set(SOURCES
src/core/logging.cpp
src/core/mainwindow.cpp
src/core/application.cpp
src/core/playerinterface.cpp
src/core/player.cpp
src/core/commandlineoptions.cpp
src/core/database.cpp
src/core/memorydatabase.cpp
src/core/sqlquery.cpp
src/core/sqlrow.cpp
src/core/metatypes.cpp
@ -433,14 +435,18 @@ set(SOURCES
src/core/taskmanager.cpp
src/core/thread.cpp
src/core/urlhandler.cpp
src/core/urlhandlers.cpp
src/core/iconloader.cpp
src/core/standarditemiconloader.cpp
src/core/scopedtransaction.cpp
src/core/translations.cpp
src/core/systemtrayicon.cpp
src/core/localredirectserver.cpp
src/core/mimedata.cpp
src/core/temporaryfile.cpp
src/core/enginemetadata.cpp
src/core/songmimedata.cpp
src/core/platforminterface.cpp
src/utilities/strutils.cpp
src/utilities/envutils.cpp
src/utilities/colorutils.cpp
@ -459,6 +465,7 @@ set(SOURCES
src/utilities/coverutils.cpp
src/utilities/screenutils.cpp
src/utilities/textencodingutils.cpp
src/utilities/coveroptions.cpp
src/tagreader/tagreaderclient.cpp
src/tagreader/tagreaderresult.cpp
@ -483,12 +490,45 @@ set(SOURCES
src/filterparser/filterparser.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/enginedevice.cpp
src/engine/devicefinders.cpp
src/engine/devicefinder.cpp
src/engine/enginemetadata.cpp
src/engine/gststartup.cpp
src/engine/gstengine.cpp
src/engine/gstenginepipeline.cpp
@ -509,11 +549,12 @@ set(SOURCES
src/context/contextview.cpp
src/context/contextalbum.cpp
src/collection/collection.cpp
src/collection/collectionlibrary.cpp
src/collection/collectionmodel.cpp
src/collection/collectionbackend.cpp
src/collection/collectionwatcher.cpp
src/collection/collectionview.cpp
src/collection/collectionitem.cpp
src/collection/collectionitemdelegate.cpp
src/collection/collectionviewcontainer.cpp
src/collection/collectiondirectorymodel.cpp
@ -539,17 +580,23 @@ set(SOURCES
src/playlist/playlistlistmodel.cpp
src/playlist/playlistlistsortfiltermodel.cpp
src/playlist/playlistlistview.cpp
src/playlist/playlistmanagerinterface.cpp
src/playlist/playlistmanager.cpp
src/playlist/playlistsaveoptionsdialog.cpp
src/playlist/playlistsequence.cpp
src/playlist/playlisttabbar.cpp
src/playlist/playlistundocommands.cpp
src/playlist/playlistview.cpp
src/playlist/playlistproxystyle.cpp
src/playlist/songmimedata.cpp
src/playlist/songloaderinserter.cpp
src/playlist/songplaylistitem.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/queueview.cpp
@ -633,6 +680,7 @@ set(SOURCES
src/settings/settingsdialog.cpp
src/settings/settingspage.cpp
src/settings/settingsitemdelegate.cpp
src/settings/behavioursettingspage.cpp
src/settings/collectionsettingspage.cpp
src/settings/collectionsettingsdirectorymodel.cpp
@ -667,8 +715,6 @@ set(SOURCES
src/widgets/fancytabbar.cpp
src/widgets/fancytabdata.cpp
src/widgets/favoritewidget.cpp
src/widgets/fileview.cpp
src/widgets/fileviewlist.cpp
src/widgets/forcescrollperpixel.cpp
src/widgets/freespacebar.cpp
src/widgets/groupediconview.cpp
@ -719,7 +765,7 @@ set(SOURCES
src/radios/radiomimedata.cpp
src/scrobbler/audioscrobbler.cpp
src/scrobbler/scrobblersettings.cpp
src/scrobbler/scrobblersettingsservice.cpp
src/scrobbler/scrobblerservice.cpp
src/scrobbler/scrobblercache.cpp
src/scrobbler/scrobblercacheitem.cpp
@ -749,6 +795,22 @@ set(SOURCES
src/transcoder/transcoderoptionsaac.cpp
src/transcoder/transcoderoptionsasf.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
@ -756,7 +818,9 @@ set(HEADERS
src/core/mainwindow.h
src/core/application.h
src/core/player.h
src/core/playerinterface.h
src/core/database.h
src/core/memorydatabase.h
src/core/deletefiles.h
src/core/filesystemwatcherinterface.h
src/core/mergedproxymodel.h
@ -769,11 +833,13 @@ set(HEADERS
src/core/songloader.h
src/core/taskmanager.h
src/core/thread.h
src/core/urlhandlers.h
src/core/urlhandler.h
src/core/standarditemiconloader.h
src/core/mimedata.h
src/core/stylesheetloader.h
src/core/localredirectserver.h
src/core/songmimedata.h
src/tagreader/tagreaderclient.h
src/tagreader/tagreaderreply.h
@ -802,7 +868,7 @@ set(HEADERS
src/context/contextview.h
src/context/contextalbum.h
src/collection/collection.h
src/collection/collectionlibrary.h
src/collection/collectionmodel.h
src/collection/collectionbackend.h
src/collection/collectionwatcher.h
@ -825,6 +891,7 @@ set(HEADERS
src/playlist/playlistlistmodel.h
src/playlist/playlistlistview.h
src/playlist/playlistlistsortfiltermodel.h
src/playlist/playlistmanagerinterface.h
src/playlist/playlistmanager.h
src/playlist/playlistsaveoptionsdialog.h
src/playlist/playlistsequence.h
@ -833,7 +900,6 @@ set(HEADERS
src/playlist/playlistproxystyle.h
src/playlist/playlistitemmimedata.h
src/playlist/songloaderinserter.h
src/playlist/songmimedata.h
src/playlist/dynamicplaylistcontrols.h
src/queue/queue.h
@ -910,6 +976,7 @@ set(HEADERS
src/settings/settingsdialog.h
src/settings/settingspage.h
src/settings/settingsitemdelegate.h
src/settings/behavioursettingspage.h
src/settings/collectionsettingspage.h
src/settings/collectionsettingsdirectorymodel.h
@ -944,8 +1011,6 @@ set(HEADERS
src/widgets/fancytabbar.h
src/widgets/fancytabdata.h
src/widgets/favoritewidget.h
src/widgets/fileview.h
src/widgets/fileviewlist.h
src/widgets/freespacebar.h
src/widgets/groupediconview.h
src/widgets/lineedit.h
@ -993,7 +1058,7 @@ set(HEADERS
src/radios/radioparadiseservice.h
src/scrobbler/audioscrobbler.h
src/scrobbler/scrobblersettings.h
src/scrobbler/scrobblersettingsservice.h
src/scrobbler/scrobblerservice.h
src/scrobbler/scrobblercache.h
src/scrobbler/scrobblingapi20.h
@ -1020,6 +1085,19 @@ set(HEADERS
src/transcoder/transcoderoptionsaac.h
src/transcoder/transcoderoptionsasf.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
@ -1081,7 +1159,6 @@ set(UI
src/dialogs/saveplaylistsdialog.ui
src/widgets/trackslider.ui
src/widgets/fileview.ui
src/widgets/loginstatewidget.ui
src/osd/osdpretty.ui
@ -1106,6 +1183,11 @@ set(UI
src/transcoder/transcoderoptionsspeex.ui
src/transcoder/transcoderoptionsasf.ui
src/transcoder/transcoderoptionsmp3.ui
src/fileview/fileview.ui
src/device/deviceproperties.ui
src/device/deviceviewcontainer.ui
)
if(APPLE)
@ -1114,54 +1196,80 @@ if(APPLE)
src/utilities/macosutils.mm
src/core/scoped_nsautorelease_pool.mm
src/core/mac_startup.mm
src/core/macsystemtrayicon.mm
src/systemtrayicon/macsystemtrayicon.mm
src/osd/osdmac.mm
src/widgets/searchfield_mac.mm
src/engine/macosdevicefinder.cpp
src/device/macosdevicelister.mm
HEADERS
src/core/macsystemtrayicon.h
src/systemtrayicon/macsystemtrayicon.h
src/osd/osdmac.h
src/device/macosdevicelister.h
)
else()
list(APPEND SOURCES src/core/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 SOURCES src/systemtrayicon/qtsystemtrayicon.cpp src/widgets/searchfield_qt.cpp src/widgets/searchfield_qt_private.cpp)
list(APPEND HEADERS src/systemtrayicon/qtsystemtrayicon.h src/widgets/searchfield_qt_private.h)
endif()
optional_source(WIN32
SOURCES
src/utilities/scopedwchararray.cpp
src/utilities/winutils.cpp
src/engine/directsounddevicefinder.cpp
src/engine/mmdevicefinder.cpp
src/core/scopedwchararray.cpp
src/core/windows7thumbbar.cpp
HEADERS
src/core/windows7thumbbar.h
)
if(HAVE_GLOBALSHORTCUTS)
optional_source(HAVE_GLOBALSHORTCUTS
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
UI src/globalshortcuts/globalshortcutgrabber.ui src/settings/globalshortcutssettingspage.ui
)
optional_source(HAVE_KDE_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsbackend-kde.cpp src/globalshortcuts/globalshortcutsbackend-gnome.cpp src/globalshortcuts/globalshortcutsbackend-mate.cpp
HEADERS src/globalshortcuts/globalshortcutsbackend-kde.h src/globalshortcuts/globalshortcutsbackend-gnome.h src/globalshortcuts/globalshortcutsbackend-mate.h
)
if(HAVE_KDE_GLOBALSHORTCUTS)
optional_source(HAVE_KDE_GLOBALSHORTCUTS
SOURCES src/globalshortcuts/globalshortcutsbackend-kde.cpp
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
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
)
optional_source(APPLE
SOURCES src/globalshortcuts/globalshortcutsbackend-macos.mm src/globalshortcuts/globalshortcutgrabber.mm
HEADERS src/globalshortcuts/globalshortcutsbackend-macos.h src/globalshortcuts/globalshortcutgrabber.h
)
optional_source(WIN32
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
)
endif()
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/moodbarproxystyle.cpp
src/moodbar/moodbarrenderer.cpp
src/moodbar/gstfastspectrumplugin.cpp
src/moodbar/gstfastspectrum.cpp
src/engine/gstfastspectrumplugin.cpp
src/engine/gstfastspectrum.cpp
src/settings/moodbarsettingspage.cpp
HEADERS
src/moodbar/moodbarcontroller.h
@ -1221,57 +1329,17 @@ optional_source(HAVE_MOODBAR
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)
optional_source(HAVE_GIO SOURCES src/device/giolister.cpp HEADERS src/device/giolister.h)
endif()
if(HAVE_UDISKS2)
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.UDisks2.Filesystem.xml PROPERTIES NO_NAMESPACE udisks2filesystem INCLUDE core/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.Drive.xml PROPERTIES NO_NAMESPACE udisks2drive INCLUDE core/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.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 includes/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 includes/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.UDisks2.Filesystem.xml udisks2filesystem)
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()
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)
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)
@ -1429,14 +1497,7 @@ if(HAVE_TRANSLATIONS)
endif()
endif()
target_include_directories(strawberry PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
)
target_include_directories(strawberry_lib PRIVATE
target_include_directories(strawberry_lib PUBLIC
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
@ -1444,14 +1505,14 @@ target_include_directories(strawberry_lib PRIVATE
)
if(SINGLEAPPLICATION_INCLUDE_DIRS)
target_include_directories(strawberry SYSTEM PRIVATE ${SINGLEAPPLICATION_INCLUDE_DIRS})
target_include_directories(strawberry_lib SYSTEM PUBLIC ${SINGLEAPPLICATION_INCLUDE_DIRS})
endif()
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()
target_link_libraries(strawberry_lib PRIVATE
target_link_libraries(strawberry_lib PUBLIC
${CMAKE_THREAD_LIBS_INIT}
PkgConfig::GLIB
PkgConfig::GOBJECT
@ -1487,10 +1548,11 @@ target_link_libraries(strawberry_lib PRIVATE
$<$<BOOL:${FREEBSD}>:execinfo>
$<$<BOOL:${WIN32}>:dsound dwmapi getopt-win::getopt>
$<$<BOOL:${MSVC}>:WindowsApp>
${SINGLEAPPLICATION_LIBRARIES}
)
if(APPLE)
target_link_libraries(strawberry_lib PRIVATE
target_link_libraries(strawberry_lib PUBLIC
"-framework Foundation"
"-framework AppKit"
"-framework Carbon"
@ -1502,19 +1564,7 @@ if(APPLE)
)
endif()
target_link_libraries(strawberry PRIVATE
${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
)
target_link_libraries(strawberry PUBLIC strawberry_lib)
if(NOT APPLE)
install(TARGETS strawberry RUNTIME DESTINATION bin)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -55,8 +55,8 @@
#include "groupbydialog.h"
#include "ui_collectionfilterwidget.h"
#include "widgets/searchfield.h"
#include "settings/collectionsettingspage.h"
#include "settings/appearancesettingspage.h"
#include "constants/collectionsettings.h"
#include "constants/appearancesettings.h"
using namespace Qt::Literals::StringLiterals;
@ -204,8 +204,8 @@ void CollectionFilterWidget::setFilter(CollectionFilter *filter) {
void CollectionFilterWidget::ReloadSettings() {
Settings s;
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
int iconsize = s.value(AppearanceSettingsPage::kIconSizeConfigureButtons, 20).toInt();
s.beginGroup(AppearanceSettings::kSettingsGroup);
int iconsize = s.value(AppearanceSettings::kIconSizeConfigureButtons, 20).toInt();
s.endGroup();
ui_->options->setIconSize(QSize(iconsize, iconsize));
ui_->search_field->setIconSize(iconsize);
@ -345,7 +345,7 @@ void CollectionFilterWidget::SaveGroupBy() {
qLog(Debug) << "Saving current grouping to" << name;
Settings s;
if (settings_group_.isEmpty() || settings_group_ == QLatin1String(CollectionSettingsPage::kSettingsGroup)) {
if (settings_group_.isEmpty() || settings_group_ == QLatin1String(CollectionSettings::kSettingsGroup)) {
s.beginGroup(SavedGroupingManager::kSavedGroupingsSettingsGroup);
}
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
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* 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
@ -22,8 +20,6 @@
#ifndef COLLECTIONITEM_H
#define COLLECTIONITEM_H
#include "config.h"
#include "core/simpletreeitem.h"
#include "core/song.h"
@ -37,17 +33,8 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
LoadingIndicator,
};
explicit CollectionItem(SimpleTreeModel<CollectionItem> *_model)
: SimpleTreeItem<CollectionItem>(_model),
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) {}
explicit CollectionItem(SimpleTreeModel<CollectionItem> *_model);
explicit CollectionItem(const Type _type, CollectionItem *_parent = nullptr);
Type type;
int container_level;

View File

@ -30,7 +30,6 @@
#include <QSettings>
#include <QtConcurrentRun>
#include "core/application.h"
#include "core/taskmanager.h"
#include "core/database.h"
#include "core/thread.h"
@ -39,22 +38,26 @@
#include "core/settings.h"
#include "tagreader/tagreaderclient.h"
#include "utilities/threadutils.h"
#include "collection.h"
#include "collectionlibrary.h"
#include "collectionwatcher.h"
#include "collectionbackend.h"
#include "collectionmodel.h"
#include "scrobbler/lastfmimport.h"
#include "settings/collectionsettingspage.h"
#include "constants/collectionsettings.h"
using std::make_shared;
const char *SCollection::kSongsTable = "songs";
const char *SCollection::kDirsTable = "directories";
const char *SCollection::kSubdirsTable = "subdirectories";
const char *CollectionLibrary::kSongsTable = "songs";
const char *CollectionLibrary::kDirsTable = "directories";
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),
app_(app),
task_manager_(task_manager),
tagreader_client_(tagreader_client),
backend_(nullptr),
model_(nullptr),
watcher_(nullptr),
@ -68,18 +71,18 @@ SCollection::SCollection(Application *app, QObject *parent)
original_thread_ = thread();
backend_ = make_shared<CollectionBackend>();
backend()->moveToThread(app->database()->thread());
qLog(Debug) << &*backend_ << "moved to thread" << app->database()->thread();
backend()->moveToThread(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();
}
SCollection::~SCollection() {
CollectionLibrary::~CollectionLibrary() {
if (watcher_) {
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_->setObjectName(watcher_->objectName());
@ -106,14 +109,11 @@ void SCollection::Init() {
watcher_thread_->start(QThread::IdlePriority);
watcher_->set_backend(backend_);
watcher_->set_task_manager(app_->task_manager());
QObject::connect(&*backend_, &CollectionBackend::Error, this, &SCollection::Error);
QObject::connect(&*backend_, &CollectionBackend::Error, this, &CollectionLibrary::Error);
QObject::connect(&*backend_, &CollectionBackend::DirectoryAdded, watcher_, &CollectionWatcher::AddDirectory);
QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
QObject::connect(&*backend_, &CollectionBackend::SongsRatingChanged, this, &SCollection::SongsRatingChanged);
QObject::connect(&*backend_, &CollectionBackend::SongsStatisticsChanged, this, &SCollection::SongsPlaycountChanged);
QObject::connect(&*backend_, &CollectionBackend::SongsRatingChanged, this, &CollectionLibrary::SongsRatingChanged);
QObject::connect(&*backend_, &CollectionBackend::SongsStatisticsChanged, this, &CollectionLibrary::SongsPlaycountChanged);
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, &*backend_, &CollectionBackend::AddOrUpdateSongs);
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::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
backend_->LoadDirectoriesAsync();
}
void SCollection::Exit() {
void CollectionLibrary::Exit() {
wait_for_exit_ << &*backend_ << watcher_;
QObject::disconnect(&*backend_, nullptr, watcher_, nullptr);
QObject::disconnect(watcher_, nullptr, &*backend_, nullptr);
QObject::connect(&*backend_, &CollectionBackend::ExitFinished, this, &SCollection::ExitReceived);
QObject::connect(watcher_, &CollectionWatcher::ExitFinished, this, &SCollection::ExitReceived);
QObject::connect(&*backend_, &CollectionBackend::ExitFinished, this, &CollectionLibrary::ExitReceived);
QObject::connect(watcher_, &CollectionWatcher::ExitFinished, this, &CollectionLibrary::ExitReceived);
backend_->ExitAsync();
watcher_->Abort();
watcher_->ExitAsync();
}
void SCollection::ExitReceived() {
void CollectionLibrary::ExitReceived() {
QObject *obj = sender();
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";
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();
model_->ReloadSettings();
Settings s;
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
save_playcounts_to_files_ = s.value("save_playcounts", false).toBool();
save_ratings_to_files_ = s.value("save_ratings", false).toBool();
s.beginGroup(CollectionSettings::kSettingsGroup);
save_playcounts_to_files_ = s.value(CollectionSettings::kSavePlayCounts, false).toBool();
save_ratings_to_files_ = s.value(CollectionSettings::kSaveRatings, false).toBool();
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"));
app_->task_manager()->SetTaskBlocksCollectionScans(task_id);
const int task_id = task_manager_->StartTask(tr("Saving playcounts and ratings"));
task_manager_->SetTaskBlocksCollectionScans(task_id);
const SongList songs = backend_->GetAllSongs();
const qint64 nb_songs = songs.size();
int i = 0;
for (const Song &song : songs) {
(void)TagReaderClient::Instance()->SaveSongPlaycountBlocking(song.url().toLocalFile(), song.playcount());
(void)TagReaderClient::Instance()->SaveSongRatingBlocking(song.url().toLocalFile(), song.rating());
app_->task_manager()->SetTaskProgress(task_id, ++i, nb_songs);
(void)tagreader_client_->SaveSongPlaycountBlocking(song.url().toLocalFile(), song.playcount());
(void)tagreader_client_->SaveSongRatingBlocking(song.url().toLocalFile(), song.rating());
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_) {
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_) {
app_->tag_reader_client()->SaveSongsRatingAsync(songs);
tagreader_client_->SaveSongsRatingAsync(songs);
}
}

View File

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

View File

@ -53,14 +53,13 @@
#include <QStandardPaths>
#include <QTimer>
#include "core/scoped_ptr.h"
#include "core/shared_ptr.h"
#include "core/application.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "core/database.h"
#include "core/iconloader.h"
#include "core/logging.h"
#include "core/sqlrow.h"
#include "core/settings.h"
#include "core/songmimedata.h"
#include "collectionfilteroptions.h"
#include "collectionquery.h"
#include "collectionbackend.h"
@ -69,12 +68,10 @@
#include "collectionmodel.h"
#include "collectionmodelupdate.h"
#include "collectionfilter.h"
#include "playlist/playlistmanager.h"
#include "playlist/songmimedata.h"
#include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h"
#include "covermanager/albumcoverloader.h"
#include "settings/collectionsettingspage.h"
#include "constants/collectionsettings.h"
using namespace std::chrono_literals;
using namespace Qt::Literals::StringLiterals;
@ -85,12 +82,10 @@ constexpr char kPixmapDiskCacheDir[] = "pixmapcache";
constexpr char kVariousArtists[] = QT_TR_NOOP("Various artists");
} // namespace
QNetworkDiskCache *CollectionModel::sIconCache = nullptr;
CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Application *app, QObject *parent)
CollectionModel::CollectionModel(const SharedPtr<CollectionBackend> backend, const SharedPtr<AlbumCoverLoader> albumcover_loader, QObject *parent)
: SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
backend_(backend),
app_(app),
albumcover_loader_(albumcover_loader),
dir_model_(new CollectionDirectoryModel(backend, this)),
filter_(new CollectionFilter(this)),
timer_reload_(new QTimer(this)),
@ -100,7 +95,8 @@ CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Applicati
total_song_count_(0),
total_artist_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())));
@ -108,8 +104,8 @@ CollectionModel::CollectionModel(SharedPtr<CollectionBackend> backend, Applicati
filter_->setSortRole(Role_SortText);
filter_->sort(0);
if (app_) {
QObject::connect(&*app_->album_cover_loader(), &AlbumCoverLoader::AlbumCoverLoaded, this, &CollectionModel::AlbumCoverLoaded);
if (albumcover_loader_) {
QObject::connect(&*albumcover_loader_, &AlbumCoverLoader::AlbumCoverLoaded, this, &CollectionModel::AlbumCoverLoaded);
}
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);
}
if (app_ && !sIconCache) {
sIconCache = new QNetworkDiskCache(this);
sIconCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/' + QLatin1String(kPixmapDiskCacheDir));
QObject::connect(app_, &Application::ClearPixmapDiskCache, this, &CollectionModel::ClearDiskCache);
if (!qgetenv("DISPLAY").isEmpty() && !icon_disk_cache_) {
icon_disk_cache_ = new QNetworkDiskCache(this);
icon_disk_cache_->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/' + QLatin1String(kPixmapDiskCacheDir) + u'-' + Song::TextForSource(backend_->source()));
}
QObject::connect(&*backend_, &CollectionBackend::SongsAdded, this, &CollectionModel::AddReAddOrUpdate);
@ -230,16 +225,16 @@ void CollectionModel::ScheduleReset() {
void CollectionModel::ReloadSettings() {
Settings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
const bool show_pretty_covers = settings.value("pretty_covers", true).toBool();
const bool show_dividers= settings.value("show_dividers", true).toBool();
const bool show_various_artists = settings.value("various_artists", true).toBool();
const bool sort_skips_articles = settings.value("sort_skips_articles", true).toBool();
settings.beginGroup(CollectionSettings::kSettingsGroup);
const bool show_pretty_covers = settings.value(CollectionSettings::kPrettyCovers, true).toBool();
const bool show_dividers= settings.value(CollectionSettings::kShowDividers, true).toBool();
const bool show_various_artists = settings.value(CollectionSettings::kVariousArtists, true).toBool();
const bool sort_skips_articles = settings.value(CollectionSettings::kSortSkipsArticles, true).toBool();
use_disk_cache_ = settings.value(CollectionSettingsPage::kSettingsDiskCacheEnable, false).toBool();
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettingsPage::kSettingsCacheSize, CollectionSettingsPage::kSettingsCacheSizeUnit, CollectionSettingsPage::kSettingsCacheSizeDefault) / 1024));
if (sIconCache) {
sIconCache->setMaximumCacheSize(MaximumCacheSize(&settings, CollectionSettingsPage::kSettingsDiskCacheSize, CollectionSettingsPage::kSettingsDiskCacheSizeUnit, CollectionSettingsPage::kSettingsDiskCacheSizeDefault));
use_disk_cache_ = settings.value(CollectionSettings::kSettingsDiskCacheEnable, false).toBool();
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettings::kSettingsCacheSize, CollectionSettings::kSettingsCacheSizeUnit, CollectionSettings::kSettingsCacheSizeDefault) / 1024));
if (icon_disk_cache_) {
icon_disk_cache_->setMaximumCacheSize(MaximumCacheSize(&settings, CollectionSettings::kSettingsDiskCacheSize, CollectionSettings::kSettingsDiskCacheSizeUnit, CollectionSettings::kSettingsDiskCacheSizeDefault));
}
settings.endGroup();
@ -258,7 +253,7 @@ void CollectionModel::ReloadSettings() {
}
if (!use_disk_cache_) {
ClearDiskCache();
ClearIconDiskCache();
}
}
@ -427,7 +422,7 @@ QMimeData *CollectionModel::mimeData(const QModelIndexList &indexes) const {
}
data->setUrls(urls);
data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs);
data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
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->sort_text = HasParentAlbumGroupBy(item->parent) ? SortTextForSong(song) : SortText(song.title());
@ -879,7 +874,7 @@ void CollectionModel::ClearItemPixmapCache(CollectionItem *item) {
// Remove from pixmap cache
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(item));
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)) {
pending_cache_keys_.remove(cache_key);
}
@ -910,8 +905,8 @@ QVariant CollectionModel::AlbumIcon(const QModelIndex &idx) {
}
// Try to load it from the disk cache
if (use_disk_cache_ && sIconCache) {
ScopedPtr<QIODevice> disk_cache_img(sIconCache->data(AlbumIconPixmapDiskCacheKey(cache_key)));
if (use_disk_cache_ && icon_disk_cache_) {
ScopedPtr<QIODevice> disk_cache_img(icon_disk_cache_->data(AlbumIconPixmapDiskCacheKey(cache_key)));
if (disk_cache_img) {
QImage cached_image;
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);
cover_loader_options.desired_scaled_size = QSize(kPrettyCoverSize, kPrettyCoverSize);
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_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 (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);
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) {
QNetworkCacheMetaData disk_cache_metadata;
disk_cache_metadata.setSaveToDisk(true);
disk_cache_metadata.setUrl(disk_cache_key);
// 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()));
QIODevice *device_iconcache = sIconCache->prepare(disk_cache_metadata);
QIODevice *device_iconcache = icon_disk_cache_->prepare(disk_cache_metadata);
if (device_iconcache) {
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 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 {
size *= 1024;
@ -1553,8 +1548,11 @@ void CollectionModel::TotalAlbumCountUpdatedSlot(const int count) {
}
void CollectionModel::ClearDiskCache() {
if (sIconCache) sIconCache->clear();
void CollectionModel::ClearIconDiskCache() {
if (icon_disk_cache_) icon_disk_cache_->clear();
QPixmapCache::clear();
}
void CollectionModel::RowsInserted(const QModelIndex &parent, const int first, const int last) {

View File

@ -44,10 +44,9 @@
#include <QNetworkDiskCache>
#include <QQueue>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "core/simpletreemodel.h"
#include "core/song.h"
#include "core/sqlrow.h"
#include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h"
#include "collectionmodelupdate.h"
@ -57,16 +56,16 @@
class QTimer;
class Settings;
class Application;
class CollectionBackend;
class CollectionDirectoryModel;
class CollectionFilter;
class AlbumCoverLoader;
class CollectionModel : public SimpleTreeModel<CollectionItem> {
Q_OBJECT
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;
static const int kPrettyCoverSize;
@ -156,7 +155,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
int total_artist_count() const { return total_artist_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; }
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 RemoveSongs(const SongList &songs);
void ClearIconDiskCache();
private:
void Clear();
void BeginReset();
@ -238,7 +239,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
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);
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);
void LoadSongsFromSqlAsync();
@ -267,15 +268,12 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
void TotalArtistCountUpdatedSlot(const int count);
void TotalAlbumCountUpdatedSlot(const int count);
static void ClearDiskCache();
void RowsInserted(const QModelIndex &parent, const int first, const int last);
void RowsRemoved(const QModelIndex &parent, const int first, const int last);
private:
static QNetworkDiskCache *sIconCache;
SharedPtr<CollectionBackend> backend_;
Application *app_;
const SharedPtr<CollectionBackend> backend_;
const SharedPtr<AlbumCoverLoader> albumcover_loader_;
CollectionDirectoryModel *dir_model_;
CollectionFilter *filter_;
QTimer *timer_reload_;
@ -310,6 +308,8 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
using ItemAndCacheKey = QPair<CollectionItem*, QString>;
QMap<quint64, ItemAndCacheKey> pending_art_;
QSet<QString> pending_cache_keys_;
QNetworkDiskCache *icon_disk_cache_;
};
Q_DECLARE_METATYPE(CollectionModel::Grouping)

View File

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

View File

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

View File

@ -50,20 +50,20 @@
#include <QKeyEvent>
#include <QContextMenuEvent>
#include "core/application.h"
#include "core/iconloader.h"
#include "core/mimedata.h"
#include "core/musicstorage.h"
#include "core/deletefiles.h"
#include "core/settings.h"
#include "utilities/filemanagerutils.h"
#include "collection.h"
#include "collectionlibrary.h"
#include "collectionbackend.h"
#include "collectiondirectorymodel.h"
#include "collectionmodel.h"
#include "collectionfilter.h"
#include "collectionfilterwidget.h"
#include "collectionitem.h"
#include "collectionitemdelegate.h"
#include "collectionmodel.h"
#include "collectionview.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
@ -73,15 +73,16 @@
#include "dialogs/deleteconfirmationdialog.h"
#include "organize/organizedialog.h"
#include "organize/organizeerrordialog.h"
#include "settings/collectionsettingspage.h"
#include "constants/collectionsettings.h"
using std::make_unique;
using namespace Qt::Literals::StringLiterals;
CollectionView::CollectionView(QWidget *parent)
: AutoExpandingTreeView(parent),
app_(nullptr),
model_(nullptr),
filter_(nullptr),
filter_widget_(nullptr),
total_song_count_(-1),
total_artist_count_(-1),
total_album_count_(-1),
@ -123,6 +124,35 @@ CollectionView::CollectionView(QWidget *parent)
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() {
const QModelIndex current = currentIndex();
@ -142,8 +172,8 @@ void CollectionView::SaveFocus() {
switch (item_type) {
case CollectionItem::Type::Song:{
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
SongList songs = app_->collection_model()->GetChildSongs(index);
QModelIndex index = filter_->mapToSource(current);
SongList songs = model_->GetChildSongs(index);
if (!songs.isEmpty()) {
last_selected_song_ = songs.last();
}
@ -210,8 +240,8 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
break;
case CollectionItem::Type::Song:
if (!last_selected_song_.url().isEmpty()) {
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
const SongList songs = app_->collection_model()->GetChildSongs(index);
QModelIndex index = filter_->mapToSource(current);
const SongList songs = model_->GetChildSongs(index);
if (std::any_of(songs.begin(), songs.end(), [this](const Song &song) { return song == last_selected_song_; })) {
setCurrentIndex(current);
return true;
@ -241,6 +271,7 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
}
}
}
return false;
}
@ -248,22 +279,14 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
void CollectionView::ReloadSettings() {
Settings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
SetAutoOpen(settings.value("auto_open", false).toBool());
delete_files_ = settings.value("delete_files", false).toBool();
settings.beginGroup(CollectionSettings::kSettingsGroup);
SetAutoOpen(settings.value(CollectionSettings::kAutoOpen, false).toBool());
delete_files_ = settings.value(CollectionSettings::kDeleteFiles, false).toBool();
settings.endGroup();
}
void CollectionView::SetApplication(Application *app) {
app_ = app;
ReloadSettings();
}
void CollectionView::SetFilter(CollectionFilterWidget *filter) { filter_ = filter; }
void CollectionView::SetFilterWidget(CollectionFilterWidget *filter_widget) { filter_widget_ = filter_widget; }
void CollectionView::TotalSongCountUpdated(const int count) {
@ -353,7 +376,7 @@ void CollectionView::mouseReleaseEvent(QMouseEvent *e) {
QTreeView::mouseReleaseEvent(e);
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_->addMenu(filter_->menu());
context_menu_->addMenu(filter_widget_->menu());
#ifndef Q_OS_WIN
action_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
action_copy_to_device_->setDisabled(device_manager_->connected_devices_model()->rowCount() == 0);
QObject::connect(device_manager_->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, action_copy_to_device_, &QAction::setDisabled);
#endif
}
@ -426,16 +449,16 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_index_ = indexAt(e->pos());
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_editable = 0;
for (const QModelIndex &idx : selected_indexes) {
++regular_elements;
if (app_->collection_model()->data(idx, CollectionModel::Role_Editable).toBool()) {
if (model_->data(idx, CollectionModel::Role_Editable).toBool()) {
++regular_editable;
}
}
@ -502,7 +525,7 @@ void CollectionView::SetShowInVarious(const bool on) {
if (on && albums.keys().count() == 1) {
const QStringList albums_list = albums.keys();
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;
for (const Song &s : all_of_album) {
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());
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;
}
QString search;
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
QModelIndex index = filter_->mapToSource(current);
switch (item_type) {
case CollectionItem::Type::Song:{
SongList songs = app_->collection_model()->GetChildSongs(index);
SongList songs = model_->GetChildSongs(index);
if (!songs.isEmpty()) {
last_selected_song_ = songs.last();
}
@ -601,8 +625,8 @@ void CollectionView::SearchForThis() {
}
case CollectionItem::Type::Container:{
CollectionItem *item = app_->collection_model()->IndexToItem(index);
const CollectionModel::GroupBy group_by = app_->collection_model()->GetGroupBy()[item->container_level];
CollectionItem *item = model_->IndexToItem(index);
const CollectionModel::GroupBy group_by = model_->GetGroupBy()[item->container_level];
while (!item->children.isEmpty()) {
item = item->children.constFirst();
}
@ -663,7 +687,7 @@ void CollectionView::SearchForThis() {
return;
}
filter_->ShowInCollection(search);
filter_widget_->ShowInCollection(search);
}
@ -688,18 +712,18 @@ void CollectionView::scrollTo(const QModelIndex &idx, ScrollHint hint) {
SongList CollectionView::GetSelectedSongs() const {
QModelIndexList selected_indexes = qobject_cast<QSortFilterProxyModel*>(model())->mapSelectionToSource(selectionModel()->selection()).indexes();
return app_->collection_model()->GetChildSongs(selected_indexes);
QModelIndexList selected_indexes = filter_->mapSelectionToSource(selectionModel()->selection()).indexes();
return model_->GetChildSongs(selected_indexes);
}
void CollectionView::Organize() {
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);
const SongList songs = GetSelectedSongs();
if (organize_dialog_->SetSongs(songs)) {
@ -714,7 +738,7 @@ void CollectionView::Organize() {
void CollectionView::EditTracks() {
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);
}
const SongList songs = GetSelectedSongs();
@ -729,7 +753,7 @@ void CollectionView::EditTagError(const QString &message) {
void CollectionView::RescanSongs() {
app_->collection()->Rescan(GetSelectedSongs());
collection_->Rescan(GetSelectedSongs());
}
@ -737,10 +761,10 @@ void CollectionView::CopyToDevice() {
#ifndef Q_OS_WIN
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_->SetSongs(GetSelectedSongs());
organize_dialog_->show();
@ -812,9 +836,9 @@ void CollectionView::Delete() {
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.
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);
delete_files->Start(songs);

View File

@ -30,10 +30,12 @@
#include <QPixmap>
#include <QSet>
#include "core/scoped_ptr.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "core/song.h"
#include "widgets/autoexpandingtreeview.h"
class QSortFilterProxyModel;
class QMenu;
class QAction;
class QContextMenuEvent;
@ -41,8 +43,20 @@ class QMouseEvent;
class QPaintEvent;
class QKeyEvent;
class Application;
class TaskManager;
class TagReaderClient;
class NetworkAccessManager;
class CollectionLibrary;
class CollectionBackend;
class CollectionModel;
class CollectionFilter;
class CollectionFilterWidget;
class DeviceManager;
class StreamingServices;
class AlbumCoverLoader;
class CurrentAlbumCoverLoader;
class CoverProviders;
class LyricsProviders;
class EditTagDialog;
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.
SongList GetSelectedSongs() const;
void SetApplication(Application *app);
void SetFilter(CollectionFilterWidget *filter);
void 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);
void SetFilterWidget(CollectionFilterWidget *filter_widget);
// QTreeView
void keyboardSearch(const QString &search) override;
@ -82,7 +106,7 @@ class CollectionView : public AutoExpandingTreeView {
void EditTagError(const QString &message);
Q_SIGNALS:
void ShowConfigDialog();
void ShowSettingsDialog();
void TotalSongCountUpdated_();
void TotalArtistCountUpdated_();
@ -120,8 +144,21 @@ class CollectionView : public AutoExpandingTreeView {
void SaveContainerPath(const QModelIndex &child);
private:
Application *app_;
CollectionFilterWidget *filter_;
SharedPtr<TaskManager> task_manager_;
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_artist_count_;

View File

@ -31,7 +31,7 @@
CollectionViewContainer::CollectionViewContainer(QWidget *parent) : QWidget(parent), ui_(new Ui_CollectionViewContainer) {
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::DownPressed, view(), &CollectionView::DownAndFocus);

View File

@ -50,13 +50,13 @@
#include "core/taskmanager.h"
#include "core/settings.h"
#include "utilities/imageutils.h"
#include "utilities/timeconstants.h"
#include "constants/timeconstants.h"
#include "tagreader/tagreaderclient.h"
#include "collectiondirectory.h"
#include "collectionbackend.h"
#include "collectionwatcher.h"
#include "playlistparsers/cueparser.h"
#include "settings/collectionsettingspage.h"
#include "constants/collectionsettings.h"
#include "engine/ebur128measures.h"
#ifdef HAVE_SONGFINGERPRINTING
# 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;
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),
source_(source),
backend_(nullptr),
task_manager_(nullptr),
task_manager_(task_manager),
tagreader_client_(tagreader_client),
backend_(backend),
fs_watcher_(FileSystemWatcherInterface::Create(this)),
original_thread_(nullptr),
scan_on_startup_(true),
@ -96,7 +101,7 @@ CollectionWatcher::CollectionWatcher(const Song::Source source, QObject *parent)
periodic_scan_timer_(new QTimer(this)),
rescan_paused_(false),
total_watches_(0),
cue_parser_(new CueParser(backend_, this)),
cue_parser_(new CueParser(tagreader_client, backend, this)),
last_scan_time_(0) {
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_;
Settings s;
s.beginGroup(CollectionSettingsPage::kSettingsGroup);
s.beginGroup(CollectionSettings::kSettingsGroup);
if (source_ == Song::Source::Collection) {
scan_on_startup_ = s.value("startup_scan", true).toBool();
monitor_ = s.value("monitor", true).toBool();
scan_on_startup_ = s.value(CollectionSettings::kStartupScan, true).toBool();
monitor_ = s.value(CollectionSettings::kMonitor, true).toBool();
}
else {
scan_on_startup_ = 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) {
song_tracking_ = s.value("song_tracking", false).toBool();
song_ebur128_loudness_analysis_ = s.value("song_ebur128_loudness_analysis", false).toBool();
mark_songs_unavailable_ = song_tracking_ ? true : s.value("mark_songs_unavailable", true).toBool();
song_tracking_ = s.value(CollectionSettings::kSongTracking, false).toBool();
song_ebur128_loudness_analysis_ = s.value(CollectionSettings::kSongENUR128LoudnessAnalysis, false).toBool();
mark_songs_unavailable_ = song_tracking_ ? true : s.value(CollectionSettings::kMarkSongsUnavailable, true).toBool();
}
else {
song_tracking_ = false;
song_ebur128_loudness_analysis_ = false;
mark_songs_unavailable_ = false;
}
expire_unavailable_songs_days_ = s.value("expire_unavailable_songs", 60).toInt();
overwrite_playcount_ = s.value("overwrite_playcount", false).toBool();
overwrite_rating_ = s.value("overwrite_rating", false).toBool();
expire_unavailable_songs_days_ = s.value(CollectionSettings::kExpireUnavailableSongs, 60).toInt();
overwrite_playcount_ = s.value(CollectionSettings::kOverwritePlaycount, false).toBool();
overwrite_rating_ = s.value(CollectionSettings::kOverwriteRating, false).toBool();
s.endGroup();
best_art_filters_.clear();
@ -575,7 +580,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
album_art[dir_part] << child;
t->AddToProgress(1);
}
else if (TagReaderClient::Instance()->IsMediaFileBlocking(child)) {
else if (tagreader_client_->IsMediaFileBlocking(child)) {
files_on_disk << child;
}
else {
@ -874,7 +879,7 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file,
}
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()) {
song_on_disk.set_source(source_);
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
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()) {
song.set_source(source_);
PerformEBUR128Analysis(song);

View File

@ -36,28 +36,32 @@
#include <QMutex>
#include "collectiondirectory.h"
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "core/song.h"
class QThread;
class QTimer;
class TaskManager;
class TagReaderClient;
class CollectionBackend;
class FileSystemWatcherInterface;
class TaskManager;
class CueParser;
class CollectionWatcher : public QObject {
Q_OBJECT
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();
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 IncrementalScanAsync();
@ -213,9 +217,12 @@ class CollectionWatcher : public QObject {
QString FindCueFilename(const QString &filename);
private:
Song::Source source_;
SharedPtr<CollectionBackend> backend_;
SharedPtr<TaskManager> task_manager_;
const Song::Source source_;
const SharedPtr<TaskManager> task_manager_;
const SharedPtr<TagReaderClient> tagreader_client_;
const SharedPtr<CollectionBackend> backend_;
QString device_name_;
FileSystemWatcherInterface *fs_watcher_;

View File

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

View File

@ -39,7 +39,7 @@
#include "core/logging.h"
#include "core/iconloader.h"
#include "core/settings.h"
#include "settings/collectionsettingspage.h"
#include "constants/collectionsettings.h"
#include "collectionmodel.h"
#include "savedgroupingmanager.h"
#include "ui_savedgroupingmanager.h"
@ -77,7 +77,7 @@ SavedGroupingManager::~SavedGroupingManager() {
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);
}

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
#define FILENAMECONSTANTS_H
#include "core/arraysize.h"
#include "includes/arraysize.h"
constexpr char kProblematicCharactersRegex[] = "[:?*\"<>|]";
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 <QPaintEvent>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "utilities/imageutils.h"
#include "covermanager/albumcoverchoicecontroller.h"

View File

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

View File

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

View File

@ -46,9 +46,9 @@ class QDragEnterEvent;
class QDropEvent;
class ResizableTextEdit;
class Application;
class CollectionView;
class AlbumCoverChoiceController;
class LyricsProviders;
class LyricsFetcher;
class ContextView : public QWidget {
@ -57,7 +57,7 @@ class ContextView : public QWidget {
public:
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_; }
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);
private:
Application *app_;
CollectionView *collectionview_;
AlbumCoverChoiceController *album_cover_choice_controller_;
LyricsFetcher *lyrics_fetcher_;

View File

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

View File

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

View File

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

View File

@ -36,16 +36,17 @@
#include <QStringList>
#include <QRecursiveMutex>
#include "includes/shared_ptr.h"
#include "sqlquery.h"
class QThread;
class Application;
class TaskManager;
class Database : public QObject {
Q_OBJECT
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;
static const int kSchemaVersion;
@ -102,7 +103,7 @@ class Database : public QObject {
void BackupFile(const QString &filename);
static bool OpenDatabase(const QString &filename, sqlite3 **connection);
Application *app_;
SharedPtr<TaskManager> task_manager_;
// Alias -> filename
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

View File

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

View File

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

View File

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

View File

@ -45,15 +45,6 @@
#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 <QCoreApplication>
#include <QWidget>
@ -61,7 +52,14 @@
#include <QEvent>
#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) {

View File

@ -84,52 +84,46 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "shared_ptr.h"
#include "commandlineoptions.h"
#include "mimedata.h"
#include "iconloader.h"
#include "taskmanager.h"
#include "song.h"
#include "stylehelper.h"
#include "stylesheetloader.h"
#include "constants/filefilterconstants.h"
#include "constants/timeconstants.h"
#include "constants/mainwindowsettings.h"
#include "includes/shared_ptr.h"
#include "core/commandlineoptions.h"
#include "core/mimedata.h"
#include "core/iconloader.h"
#include "core/taskmanager.h"
#include "core/song.h"
#include "core/stylehelper.h"
#include "core/stylesheetloader.h"
#include "application.h"
#include "database.h"
#include "player.h"
#include "filesystemmusicstorage.h"
#include "deletefiles.h"
#ifdef Q_OS_MACOS
# include "mac_startup.h"
# include "macsystemtrayicon.h"
# include "utilities/macosutils.h"
#else
# include "qtsystemtrayicon.h"
#endif
#include "networkaccessmanager.h"
#include "settings.h"
#include "core/database.h"
#include "core/filesystemmusicstorage.h"
#include "core/deletefiles.h"
#include "core/settings.h"
#include "core/player.h"
#include "utilities/envutils.h"
#include "utilities/filemanagerutils.h"
#include "utilities/timeconstants.h"
#include "utilities/screenutils.h"
#include "engine/enginebase.h"
#include "dialogs/errordialog.h"
#include "dialogs/about.h"
#include "dialogs/console.h"
#include "dialogs/trackselectiondialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/addstreamdialog.h"
#include "dialogs/deleteconfirmationdialog.h"
#include "dialogs/lastfmimportdialog.h"
#include "dialogs/snapdialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/trackselectiondialog.h"
#include "organize/organizedialog.h"
#include "widgets/fancytabwidget.h"
#include "widgets/playingwidget.h"
#include "widgets/volumeslider.h"
#include "widgets/fileview.h"
#include "widgets/multiloadingindicator.h"
#include "widgets/trackslider.h"
#include "fileview/fileview.h"
#include "osd/osdbase.h"
#include "context/contextview.h"
#include "collection/collection.h"
#include "collection/collectionlibrary.h"
#include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h"
#include "collection/collectionviewcontainer.h"
@ -160,30 +154,31 @@
#include "covermanager/coverproviders.h"
#include "covermanager/albumcoverimageresult.h"
#include "lyrics/lyricsproviders.h"
#include "device/devicemanager.h"
#include "device/devicestatefiltermodel.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
# include "device/deviceview.h"
# include "device/deviceviewcontainer.h"
#endif
#include "transcoder/transcodedialog.h"
#include "settings/settingsdialog.h"
#include "settings/behavioursettingspage.h"
#include "settings/backendsettingspage.h"
#include "settings/collectionsettingspage.h"
#include "settings/playlistsettingspage.h"
#include "constants/behavioursettings.h"
#include "constants/appearancesettings.h"
#include "constants/backendsettings.h"
#include "constants/collectionsettings.h"
#include "constants/playlistsettings.h"
#ifdef HAVE_SUBSONIC
# include "settings/subsonicsettingspage.h"
# include "constants/subsonicsettings.h"
#endif
#ifdef HAVE_TIDAL
# include "tidal/tidalservice.h"
# include "settings/tidalsettingspage.h"
# include "constants/tidalsettings.h"
#endif
#ifdef HAVE_SPOTIFY
# include "settings/spotifysettingspage.h"
# include "constants/spotifysettings.h"
#endif
#ifdef HAVE_QOBUZ
# include "settings/qobuzsettingspage.h"
# include "constants/qobuzsettings.h"
#endif
#include "streaming/streamingservices.h"
@ -205,6 +200,7 @@
#ifdef HAVE_MOODBAR
# include "moodbar/moodbarcontroller.h"
# include "moodbar/moodbarloader.h"
# include "moodbar/moodbarproxystyle.h"
#endif
@ -213,7 +209,15 @@
#include "organize/organizeerrordialog.h"
#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
#ifdef HAVE_QTSPARKLE
@ -225,9 +229,6 @@ using std::make_shared;
using namespace std::chrono_literals;
using namespace Qt::Literals::StringLiterals;
const char *MainWindow::kSettingsGroup = "MainWindow";
const char *MainWindow::kAllFilesFilterSpec = QT_TR_NOOP("All Files (*)");
namespace {
const int kTrackSliderUpdateTimeMs = 200;
const int kTrackPositionUpdateTimeMs = 1000;
@ -258,8 +259,9 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
app_(app),
tray_icon_(tray_icon),
osd_(osd),
console_([app]() {
Console *console = new Console(app);
console_([app, this]() {
Console *console = new Console(app->database());
QObject::connect(console, &Console::Error, this, &MainWindow::ShowErrorDialog);
return console;
}),
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)),
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
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 connections
@ -287,7 +289,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
}),
equalizer_(new Equalizer),
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());
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);
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
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
#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
#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
#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
radio_view_(new RadioViewContainer(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
taskbar_progress_(false),
#endif
doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour::Append),
doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour::Never),
doubleclick_playlist_addmode_(BehaviourSettingsPage::PlaylistAddBehaviour::Play),
menu_playmode_(BehaviourSettingsPage::PlayBehaviour::Never),
doubleclick_addmode_(BehaviourSettings::AddBehaviour::Append),
doubleclick_playmode_(BehaviourSettings::PlayBehaviour::Never),
doubleclick_playlist_addmode_(BehaviourSettings::PlaylistAddBehaviour::Play),
menu_playmode_(BehaviourSettings::PlayBehaviour::Never),
initialized_(false),
was_maximized_(true),
was_minimized_(false),
@ -358,19 +367,18 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
qLog(Debug) << "Starting";
QObject::connect(app, &Application::ErrorAdded, this, &MainWindow::ShowErrorDialog);
QObject::connect(app, &Application::SettingsDialogRequested, this, &MainWindow::OpenSettingsDialogAtPage);
// Initialize the UI
ui_->setupUi(this);
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());
context_view_->Init(app_, collection_view_->view(), album_cover_choice_controller_);
ui_->widget_playing->Init(app_, album_cover_choice_controller_);
context_view_->Init(collection_view_->view(), album_cover_choice_controller_, app_->lyrics_providers());
ui_->widget_playing->Init(album_cover_choice_controller_);
// Initialize the search widget
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
ui_->tabs->AddBottomWidget(ui_->widget_playing);
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);
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->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()->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
device_view_->view()->SetApplication(app_);
device_view_->view()->Init(app->task_manager(), app->tagreader_client(), app->device_manager(), app->collection_model()->directory_model());
#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());
@ -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_transcoder, &QAction::triggered, this, &MainWindow::ShowTranscodeDialog);
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_full_collection_scan, &QAction::triggered, &*app_->collection(), &SCollection::FullScan);
QObject::connect(ui_->action_stop_collection_scan, &QAction::triggered, &*app_->collection(), &SCollection::StopScan);
QObject::connect(ui_->action_update_collection, &QAction::triggered, &*app_->collection(), &CollectionLibrary::IncrementalScan);
QObject::connect(ui_->action_full_collection_scan, &QAction::triggered, &*app_->collection(), &CollectionLibrary::FullScan);
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);
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_->playlist_manager(), &PlaylistManager::CurrentSongChanged, osd_, &OSDBase::SongChanged);
QObject::connect(&*app_->player(), &Player::Paused, osd_, &OSDBase::Paused);
QObject::connect(&*app_->player(), &Player::Resumed, osd_, &OSDBase::Resumed);
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::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::CurrentSongChanged, this, &MainWindow::SongChanged);
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);
// 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::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(app_->collection_model(), &CollectionModel::TotalSongCountUpdated, collection_view_->view(), &CollectionView::TotalSongCountUpdated);
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::modelReset, collection_view_->view(), &CollectionView::RestoreFocus);
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &SCollection::PauseWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &SCollection::ResumeWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher);
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(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog);
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);
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);
collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettingsPage::kSettingsGroup));
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::OpenCollectionSettingsDialog);
collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettings::kSettingsGroup));
collection_view_->filter_widget()->Init(app_->collection()->model(), app_->collection()->model()->filter());
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);
#ifdef HAVE_SUBSONIC
QObject::connect(subsonic_view_, &StreamingSongsView::OpenSettingsDialog, this, &MainWindow::OpenServiceSettingsDialog);
QObject::connect(subsonic_view_->view(), &StreamingCollectionView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
#endif
#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_->albums_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);
if (TidalServicePtr tidalservice = app_->streaming_services()->Service<TidalService>()) {
QObject::connect(this, &MainWindow::AuthorizationUrlReceived, &*tidalservice, &TidalService::AuthorizationUrlReceived);
@ -702,16 +731,20 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#endif
#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_->albums_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);
#endif
#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_->albums_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);
#endif
@ -773,14 +806,15 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QObject::connect(ui_->playlist, &PlaylistContainer::UndoRedoActionsChanged, this, &MainWindow::PlaylistUndoRedoChanged);
#ifndef Q_OS_WIN
playlist_copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
QObject::connect(&*app_->device_manager(), &DeviceManager::DeviceError, this, &MainWindow::ShowErrorDialog);
#ifndef WIN32
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
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettings::LoveButtonVisibilityChanged, this, &MainWindow::LoveButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobblingEnabledChanged, this, &MainWindow::ScrobblingEnabledChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::ScrobbleButtonVisibilityChanged, this, &MainWindow::ScrobbleButtonVisibilityChanged);
QObject::connect(&*app_->scrobbler()->settings(), &ScrobblerSettingsService::LoveButtonVisibilityChanged, this, &MainWindow::LoveButtonVisibilityChanged);
#ifdef Q_OS_MACOS
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);
QObject::connect(ui_->multi_loading_indicator, &MultiLoadingIndicator::TaskCountChange, this, &MainWindow::TaskCountChanged);
ui_->track_slider->SetApplication(app);
ui_->track_slider->Init();
#ifdef HAVE_MOODBAR
// 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
// Playing widget
@ -875,7 +912,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
css_loader->SetStyleSheet(this, u":/style/strawberry.css"_s);
// 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());
@ -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::UpdateTotal, lastfm_import_dialog_, &LastFMImportDialog::UpdateTotal);
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
qLog(Debug) << "Loading settings";
settings_.beginGroup(kSettingsGroup);
settings_.beginGroup(MainWindowSettings::kSettingsGroup);
// Set last used geometry to position window on the correct monitor
// 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());
}
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));
}
@ -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
show();
#else
BehaviourSettingsPage::StartupBehaviour startupbehaviour = BehaviourSettingsPage::StartupBehaviour::Remember;
BehaviourSettings::StartupBehaviour startupbehaviour = BehaviourSettings::StartupBehaviour::Remember;
{
Settings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
startupbehaviour = static_cast<BehaviourSettingsPage::StartupBehaviour>(s.value("startupbehaviour", static_cast<int>(BehaviourSettingsPage::StartupBehaviour::Remember)).toInt());
s.beginGroup(BehaviourSettings::kSettingsGroup);
startupbehaviour = static_cast<BehaviourSettings::StartupBehaviour>(s.value(BehaviourSettings::kStartupBehaviour, static_cast<int>(BehaviourSettings::StartupBehaviour::Remember)).toInt());
s.endGroup();
}
switch (startupbehaviour) {
case BehaviourSettingsPage::StartupBehaviour::Show:
case BehaviourSettings::StartupBehaviour::Show:
show();
break;
case BehaviourSettingsPage::StartupBehaviour::ShowMaximized:
case BehaviourSettings::StartupBehaviour::ShowMaximized:
setWindowState(windowState() | Qt::WindowMaximized);
show();
break;
case BehaviourSettingsPage::StartupBehaviour::ShowMinimized:
case BehaviourSettings::StartupBehaviour::ShowMinimized:
setWindowState(windowState() | Qt::WindowMinimized);
show();
break;
case BehaviourSettingsPage::StartupBehaviour::Hide:
case BehaviourSettings::StartupBehaviour::Hide:
if (tray_icon_->IsSystemTrayAvailable() && tray_icon_->isVisible()) {
break;
}
[[fallthrough]];
case BehaviourSettingsPage::StartupBehaviour::Remember:
case BehaviourSettings::StartupBehaviour::Remember:
default:{
was_maximized_ = settings_.value("maximized", true).toBool();
was_maximized_ = settings_.value(MainWindowSettings::kMaximized, true).toBool();
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 (!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();
}
break;
@ -982,7 +1021,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
}
#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_->action_toggle_show_sidebar->setChecked(show_sidebar);
@ -1016,7 +1055,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#ifdef Q_OS_LINUX
if (!Utilities::GetEnv(u"SNAP"_s).isEmpty() && !Utilities::GetEnv(u"SNAP_NAME"_s).isEmpty()) {
Settings s;
s.beginGroup(kSettingsGroup);
s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool ignore_snap = s.value("ignore_snap", false).toBool();
s.endGroup();
if (!ignore_snap) {
@ -1030,12 +1069,12 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
#if defined(Q_OS_MACOS)
if (Utilities::ProcessTranslated()) {
Settings s;
s.beginGroup(kSettingsGroup);
s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool ignore_rosetta = s.value("ignore_rosetta", false).toBool();
s.endGroup();
if (!ignore_rosetta) {
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->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));
@ -1052,14 +1091,13 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
s.endGroup();
#endif
if (asked_permission) {
s.beginGroup(kSettingsGroup);
constexpr char do_not_show_sponsor_message_key[] = "do_not_show_sponsor_message";
const bool do_not_show_sponsor_message = s.value(do_not_show_sponsor_message_key, false).toBool();
s.beginGroup(MainWindowSettings::kSettingsGroup);
const bool do_not_show_sponsor_message = s.value(MainWindowSettings::kDoNotShowSponsorMessage, false).toBool();
s.endGroup();
if (!do_not_show_sponsor_message) {
MessageDialog *sponsor_message = new MessageDialog(this);
sponsor_message->set_settings_group(QLatin1String(kSettingsGroup));
sponsor_message->set_do_not_show_message_again(QLatin1String(do_not_show_sponsor_message_key));
sponsor_message->set_settings_group(QLatin1String(MainWindowSettings::kSettingsGroup));
sponsor_message->set_do_not_show_message_again(QLatin1String(MainWindowSettings::kDoNotShowSponsorMessage));
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));
}
@ -1083,8 +1121,8 @@ void MainWindow::ReloadSettings() {
constexpr bool keeprunning_available = true;
#else
const bool systemtray_available = tray_icon_->IsSystemTrayAvailable();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
const bool showtrayicon = s.value("showtrayicon", systemtray_available).toBool();
s.beginGroup(BehaviourSettings::kSettingsGroup);
const bool showtrayicon = s.value(BehaviourSettings::kShowTrayIcon, systemtray_available).toBool();
s.endGroup();
const bool keeprunning_available = systemtray_available && showtrayicon;
if (systemtray_available) {
@ -1095,22 +1133,22 @@ void MainWindow::ReloadSettings() {
}
#endif
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
keep_running_ = keeprunning_available && s.value("keeprunning", false).toBool();
playing_widget_ = s.value("playing_widget", true).toBool();
bool trayicon_progress = s.value("trayicon_progress", false).toBool();
s.beginGroup(BehaviourSettings::kSettingsGroup);
keep_running_ = keeprunning_available && s.value(BehaviourSettings::kKeepRunning, false).toBool();
playing_widget_ = s.value(BehaviourSettings::kPlayingWidget, true).toBool();
bool trayicon_progress = s.value(BehaviourSettings::kTrayIconProgress, false).toBool();
#ifdef HAVE_DBUS
const bool taskbar_progress = s.value("taskbar_progress", true).toBool();
const bool taskbar_progress = s.value(BehaviourSettings::kTaskbarProgress, true).toBool();
#endif
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_playmode_ = static_cast<BehaviourSettingsPage::PlayBehaviour>(s.value("doubleclick_playmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt());
doubleclick_playlist_addmode_ = static_cast<BehaviourSettingsPage::PlaylistAddBehaviour>(s.value("doubleclick_playlist_addmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt());
menu_playmode_ = static_cast<BehaviourSettingsPage::PlayBehaviour>(s.value("menu_playmode", static_cast<int>(BehaviourSettingsPage::PlayBehaviour::Never)).toInt());
doubleclick_addmode_ = static_cast<BehaviourSettings::AddBehaviour>(s.value(BehaviourSettings::kDoubleClickAddMode, static_cast<int>(BehaviourSettings::AddBehaviour::Append)).toInt());
doubleclick_playmode_ = static_cast<BehaviourSettings::PlayBehaviour>(s.value(BehaviourSettings::kDoubleClickPlayMode, static_cast<int>(BehaviourSettings::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<BehaviourSettings::PlayBehaviour>(s.value(BehaviourSettings::kMenuPlayMode, static_cast<int>(BehaviourSettings::PlayBehaviour::Never)).toInt());
s.endGroup();
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlayControlButtons, 32).toInt();
s.beginGroup(AppearanceSettings::kSettingsGroup);
int iconsize = s.value(AppearanceSettings::kIconSizePlayControlButtons, 32).toInt();
s.endGroup();
tray_icon_->SetTrayiconProgress(trayicon_progress);
@ -1128,7 +1166,7 @@ void MainWindow::ReloadSettings() {
ui_->forward_button->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();
s.endGroup();
if (volume_control != ui_->volume->isEnabled()) {
@ -1143,17 +1181,17 @@ void MainWindow::ReloadSettings() {
}
}
s.beginGroup(PlaylistSettingsPage::kSettingsGroup);
delete_files_ = s.value("delete_files", false).toBool();
s.beginGroup(PlaylistSettings::kSettingsGroup);
delete_files_ = s.value(PlaylistSettings::kDeleteFiles, false).toBool();
s.endGroup();
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
s.beginGroup(SubsonicSettingsPage::kSettingsGroup);
bool enable_subsonic = s.value("enabled", false).toBool();
s.beginGroup(SubsonicSettings::kSettingsGroup);
bool enable_subsonic = s.value(SubsonicSettings::kEnabled, false).toBool();
s.endGroup();
if (enable_subsonic) {
ui_->tabs->EnableTab(subsonic_view_);
@ -1164,8 +1202,8 @@ void MainWindow::ReloadSettings() {
#endif
#ifdef HAVE_TIDAL
s.beginGroup(TidalSettingsPage::kSettingsGroup);
bool enable_tidal = s.value("enabled", false).toBool();
s.beginGroup(TidalSettings::kSettingsGroup);
bool enable_tidal = s.value(TidalSettings::kEnabled, false).toBool();
s.endGroup();
if (enable_tidal) {
ui_->tabs->EnableTab(tidal_view_);
@ -1176,8 +1214,8 @@ void MainWindow::ReloadSettings() {
#endif
#ifdef HAVE_SPOTIFY
s.beginGroup(SpotifySettingsPage::kSettingsGroup);
bool enable_spotify = s.value("enabled", false).toBool();
s.beginGroup(SpotifySettings::kSettingsGroup);
bool enable_spotify = s.value(SpotifySettings::kEnabled, false).toBool();
s.endGroup();
if (enable_spotify) {
ui_->tabs->EnableTab(spotify_view_);
@ -1188,8 +1226,8 @@ void MainWindow::ReloadSettings() {
#endif
#ifdef HAVE_QOBUZ
s.beginGroup(QobuzSettingsPage::kSettingsGroup);
bool enable_qobuz = s.value("enabled", false).toBool();
s.beginGroup(QobuzSettings::kSettingsGroup);
bool enable_qobuz = s.value(QobuzSettings::kEnabled, false).toBool();
s.endGroup();
if (enable_qobuz) {
ui_->tabs->EnableTab(qobuz_view_);
@ -1208,7 +1246,6 @@ void MainWindow::ReloadAllSettings() {
ReloadSettings();
// Other settings
app_->ReloadSettings();
app_->collection()->ReloadSettings();
app_->player()->ReloadSettings();
collection_view_->ReloadSettings();
@ -1228,18 +1265,23 @@ void MainWindow::ReloadAllSettings() {
app_->lyrics_providers()->ReloadSettings();
#ifdef HAVE_MOODBAR
app_->moodbar_controller()->ReloadSettings();
app_->moodbar_loader()->ReloadSettings();
ui_->track_slider->moodbar_proxy_style()->ReloadSettings();
#endif
#ifdef HAVE_SUBSONIC
subsonic_view_->ReloadSettings();
#endif
#ifdef HAVE_TIDAL
tidal_view_->ReloadSettings();
tidal_view_->search_view()->ReloadSettings();
#endif
#ifdef HAVE_SPOTIFY
spotify_view_->ReloadSettings();
spotify_view_->search_view()->ReloadSettings();
#endif
#ifdef HAVE_QOBUZ
qobuz_view_->ReloadSettings();
qobuz_view_->search_view()->ReloadSettings();
#endif
}
@ -1255,12 +1297,12 @@ void MainWindow::SaveSettings() {
SaveGeometry();
app_->player()->SaveVolume();
app_->player()->SavePlaybackStatus();
ui_->tabs->SaveSettings(QLatin1String(kSettingsGroup));
ui_->tabs->SaveSettings(QLatin1String(MainWindowSettings::kSettingsGroup));
ui_->playlist->view()->SaveSettings();
app_->scrobbler()->WriteCache();
settings_.setValue("show_sidebar", ui_->action_toggle_show_sidebar->isChecked());
settings_.setValue("search_for_cover_auto", album_cover_choice_controller_->search_cover_auto_action()->isChecked());
settings_.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->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);
TabSwitched();
settings_.setValue("show_sidebar", checked);
settings_.setValue(MainWindowSettings::kShowSidebar, checked);
}
void MainWindow::ToggleSearchCoverAuto(const bool checked) {
settings_.setValue("search_for_cover_auto", checked);
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
}
void MainWindow::SaveGeometry() {
if (!initialized_) return;
settings_.setValue("maximized", isMaximized());
settings_.setValue("minimized", isMinimized());
settings_.setValue("hidden", isHidden());
settings_.setValue("geometry", saveGeometry());
settings_.setValue("splitter_state", ui_->splitter->saveState());
settings_.setValue(MainWindowSettings::kMaximized, isMaximized());
settings_.setValue(MainWindowSettings::kMinimized, isMinimized());
settings_.setValue(MainWindowSettings::kHidden, isHidden());
settings_.setValue(MainWindowSettings::kGeometry, saveGeometry());
settings_.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState());
}
@ -1546,12 +1588,12 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &idx) {
}
switch (doubleclick_playlist_addmode_) {
case BehaviourSettingsPage::PlaylistAddBehaviour::Play:
case BehaviourSettings::PlaylistAddBehaviour::Play:
app_->playlist_manager()->SetActiveToCurrent();
app_->player()->PlayAt(source_idx.row(), false, 0, EngineBase::TrackChangeType::Manual, Playlist::AutoScroll::Never, true, true);
break;
case BehaviourSettingsPage::PlaylistAddBehaviour::Enqueue:
case BehaviourSettings::PlaylistAddBehaviour::Enqueue:
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << source_idx);
if (app_->player()->GetState() != EngineBase::State::Playing) {
app_->playlist_manager()->SetActiveToCurrent();
@ -1724,42 +1766,42 @@ void MainWindow::UpdateTaskbarProgress(const bool visible, const double progress
}
#endif
void MainWindow::ApplyAddBehaviour(const BehaviourSettingsPage::AddBehaviour b, MimeData *mimedata) {
void MainWindow::ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata) {
switch (b) {
case BehaviourSettingsPage::AddBehaviour::Append:
case BehaviourSettings::AddBehaviour::Append:
mimedata->clear_first_ = false;
mimedata->enqueue_now_ = false;
break;
case BehaviourSettingsPage::AddBehaviour::Enqueue:
case BehaviourSettings::AddBehaviour::Enqueue:
mimedata->clear_first_ = false;
mimedata->enqueue_now_ = true;
break;
case BehaviourSettingsPage::AddBehaviour::Load:
case BehaviourSettings::AddBehaviour::Load:
mimedata->clear_first_ = true;
mimedata->enqueue_now_ = false;
break;
case BehaviourSettingsPage::AddBehaviour::OpenInNew:
case BehaviourSettings::AddBehaviour::OpenInNew:
mimedata->open_in_new_playlist_ = true;
break;
}
}
void MainWindow::ApplyPlayBehaviour(const BehaviourSettingsPage::PlayBehaviour b, MimeData *mimedata) const {
void MainWindow::ApplyPlayBehaviour(const BehaviourSettings::PlayBehaviour b, MimeData *mimedata) const {
switch (b) {
case BehaviourSettingsPage::PlayBehaviour::Always:
case BehaviourSettings::PlayBehaviour::Always:
mimedata->play_now_ = true;
break;
case BehaviourSettingsPage::PlayBehaviour::Never:
case BehaviourSettings::PlayBehaviour::Never:
mimedata->play_now_ = false;
break;
case BehaviourSettingsPage::PlayBehaviour::IfStopped:
case BehaviourSettings::PlayBehaviour::IfStopped:
mimedata->play_now_ = !(app_->player()->GetState() == EngineBase::State::Playing);
break;
}
@ -1814,7 +1856,7 @@ void MainWindow::AddToPlaylistFromAction(QAction *action) {
// Save the current playlist to reactivate it
const int current_id = app_->playlist_manager()->current_id();
// 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) {
// I'm sure the new playlist was created and is selected, so I can just insert items
app_->playlist_manager()->current()->InsertItems(items);
@ -2161,7 +2203,7 @@ void MainWindow::RenumberTracks() {
Song song = item->OriginalMetadata();
if (song.IsEditable()) {
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);
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();
if (!song.is_valid()) continue;
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);
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
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
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;
@ -2470,7 +2512,7 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
break;
case CommandlineOptions::UrlListAction::CreateNew:
mimedata->name_for_new_playlist_ = options.playlist_name();
ApplyAddBehaviour(BehaviourSettingsPage::AddBehaviour::OpenInNew, mimedata);
ApplyAddBehaviour(BehaviourSettings::AddBehaviour::OpenInNew, mimedata);
break;
}
@ -2563,10 +2605,34 @@ void MainWindow::AddFilesToTranscoder() {
}
void MainWindow::ShowCollectionConfig() {
void MainWindow::OpenCollectionSettingsDialog() {
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) {
if (count == 0) {
@ -2810,10 +2876,18 @@ void MainWindow::ShowEqualizer() {
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
settings_dialog->SetGlobalShortcutManager(globalshortcuts_manager_);
globalshortcuts_manager_,
#endif
osd_,
this);
// Settings
QObject::connect(settings_dialog, &SettingsDialog::ReloadSettings, this, &MainWindow::ReloadAllSettings);
@ -2838,7 +2912,7 @@ void MainWindow::OpenSettingsDialogAtPage(SettingsDialog::Page page) {
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::Error, this, &MainWindow::ShowErrorDialog);
return edit_tag_dialog;
@ -2938,7 +3012,7 @@ void MainWindow::AutoCompleteTags() {
// Create the tag fetching stuff if it hasn't been already
if (!tag_fetcher_) {
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);
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()) {
// Show a preview notification for the first song in the current playlist

View File

@ -46,19 +46,19 @@
#include <QSettings>
#include <QtEvents>
#include "scoped_ptr.h"
#include "shared_ptr.h"
#include "lazy.h"
#include "platforminterface.h"
#include "song.h"
#include "settings.h"
#include "includes/scoped_ptr.h"
#include "includes/shared_ptr.h"
#include "includes/lazy.h"
#include "core/platforminterface.h"
#include "core/song.h"
#include "core/settings.h"
#include "tagreader/tagreaderclient.h"
#include "engine/enginebase.h"
#include "osd/osdbase.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
#include "settings/settingsdialog.h"
#include "settings/behavioursettingspage.h"
#include "constants/behavioursettings.h"
#include "covermanager/albumcoverloaderresult.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);
~MainWindow() override;
static const char *kSettingsGroup;
static const char *kAllFilesFilterSpec;
void SetHiddenInTray(const bool hidden);
void CommandlineOptionsReceived(const CommandlineOptions &options);
@ -202,7 +199,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void TaskCountChanged(const int count);
void ShowCollectionConfig();
void OpenCollectionSettingsDialog();
void OpenServiceSettingsDialog(const Song::Source source);
void ReloadSettings();
void ReloadAllSettings();
void RefreshStyleSheet();
@ -237,7 +236,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void Exit();
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();
@ -274,8 +273,8 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void SaveSettings();
static void ApplyAddBehaviour(const BehaviourSettingsPage::AddBehaviour b, MimeData *mimedata);
void ApplyPlayBehaviour(const BehaviourSettingsPage::PlayBehaviour b, MimeData *mimedata) const;
static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata);
void ApplyPlayBehaviour(const BehaviourSettings::PlayBehaviour b, MimeData *mimedata) const;
void CheckFullRescanRevisions();
@ -385,10 +384,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
#ifdef HAVE_DBUS
bool taskbar_progress_;
#endif
BehaviourSettingsPage::AddBehaviour doubleclick_addmode_;
BehaviourSettingsPage::PlayBehaviour doubleclick_playmode_;
BehaviourSettingsPage::PlaylistAddBehaviour doubleclick_playlist_addmode_;
BehaviourSettingsPage::PlayBehaviour menu_playmode_;
BehaviourSettings::AddBehaviour doubleclick_addmode_;
BehaviourSettings::PlayBehaviour doubleclick_playmode_;
BehaviourSettings::PlaylistAddBehaviour doubleclick_playlist_addmode_;
BehaviourSettings::PlayBehaviour menu_playmode_;
bool initialized_;
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 <QStringList>
#include "core/scoped_ptr.h"
#include "includes/scoped_ptr.h"
class QMimeData;

View File

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

View File

@ -35,7 +35,7 @@
#include <QList>
#include <QImage>
#include "shared_ptr.h"
#include "includes/shared_ptr.h"
#include "song.h"
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
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* 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
@ -26,8 +25,8 @@
class PlatformInterface {
public:
PlatformInterface() = default;
virtual ~PlatformInterface() {}
explicit PlatformInterface();
virtual ~PlatformInterface();
// Called when the application should show itself.
virtual void Activate() = 0;

View File

@ -37,18 +37,21 @@
#include <QTimer>
#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/settings.h"
#include "utilities/timeconstants.h"
#include "scoped_ptr.h"
#include "shared_ptr.h"
#include "song.h"
#include "urlhandler.h"
#include "application.h"
#include "core/song.h"
#include "core/urlhandlers.h"
#include "core/urlhandler.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "engine/gstengine.h"
#include "engine/gststartup.h"
@ -60,18 +63,22 @@
#include "playlist/playlistsequence.h"
#include "equalizer/equalizer.h"
#include "analyzer/analyzercontainer.h"
#include "settings/backendsettingspage.h"
#include "settings/behavioursettingspage.h"
#include "settings/playlistsettingspage.h"
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),
app_(app),
task_manager_(task_manager),
url_handlers_(url_handlers),
playlist_manager_(playlist_manager),
engine_(nullptr),
gst_startup_(new GstStartup(this)),
analyzer_(nullptr),
@ -89,7 +96,7 @@ Player::Player(Application *app, QObject *parent)
last_pressed_previous_(QDateTime::currentDateTime()),
continue_on_error_(false),
greyout_(true),
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour::DontRestart),
menu_previousmode_(BehaviourSettings::PreviousBehaviour::DontRestart),
seek_step_sec_(10),
volume_increment_(5),
play_offset_nanosec_(0) {
@ -97,8 +104,8 @@ Player::Player(Application *app, QObject *parent)
setObjectName(QLatin1String(metaObject()->className()));
Settings s;
s.beginGroup(BackendSettingsPage::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value("engine", EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.beginGroup(BackendSettings::kSettingsGroup);
EngineBase::Type enginetype = EngineBase::TypeFromName(s.value(BackendSettings::kEngine, EngineBase::Name(EngineBase::Type::GStreamer)).toString().toLower());
s.endGroup();
CreateEngine(enginetype);
@ -107,6 +114,8 @@ Player::Player(Application *app, QObject *parent)
timer_save_volume_->setInterval(5s);
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) {
@ -118,7 +127,7 @@ EngineBase::Type Player::CreateEngine(EngineBase::Type enginetype) {
case EngineBase::Type::None:
case 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_);
engine_.reset(gst_engine.release());
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.
Settings s;
s.beginGroup(BackendSettingsPage::kSettingsGroup);
s.setValue("engine", EngineBase::Name(use_enginetype));
s.setValue("output", engine_->DefaultOutput());
s.setValue("device", QVariant());
s.beginGroup(BackendSettings::kSettingsGroup);
s.setValue(BackendSettings::kEngine, EngineBase::Name(use_enginetype));
s.setValue(BackendSettings::kOutput, engine_->DefaultOutput());
s.setValue(BackendSettings::kDevice, QVariant());
s.endGroup();
}
@ -156,7 +165,7 @@ void Player::Init() {
Settings s;
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());
s.endGroup();
CreateEngine(enginetype);
@ -179,10 +188,10 @@ void Player::Init() {
QObject::connect(&*engine_, &EngineBase::VolumeChanged, this, &Player::SetVolumeFromEngine);
// Equalizer
QObject::connect(&*equalizer_, &Equalizer::StereoBalancerEnabledChanged, &*app_->player()->engine(), &EngineBase::SetStereoBalancerEnabled);
QObject::connect(&*equalizer_, &Equalizer::StereoBalanceChanged, &*app_->player()->engine(), &EngineBase::SetStereoBalance);
QObject::connect(&*equalizer_, &Equalizer::EqualizerEnabledChanged, &*app_->player()->engine(), &EngineBase::SetEqualizerEnabled);
QObject::connect(&*equalizer_, &Equalizer::EqualizerParametersChanged, &*app_->player()->engine(), &EngineBase::SetEqualizerParameters);
QObject::connect(&*equalizer_, &Equalizer::StereoBalancerEnabledChanged, &*engine_, &EngineBase::SetStereoBalancerEnabled);
QObject::connect(&*equalizer_, &Equalizer::StereoBalanceChanged, &*engine_, &EngineBase::SetStereoBalance);
QObject::connect(&*equalizer_, &Equalizer::EqualizerEnabledChanged, &*engine_, &EngineBase::SetEqualizerEnabled);
QObject::connect(&*equalizer_, &Equalizer::EqualizerParametersChanged, &*engine_, &EngineBase::SetEqualizerParameters);
engine_->SetStereoBalancerEnabled(equalizer_->is_stereo_balancer_enabled());
engine_->SetStereoBalance(equalizer_->stereo_balance());
@ -199,26 +208,32 @@ void Player::ReloadSettings() {
Settings s;
s.beginGroup(PlaylistSettingsPage::kSettingsGroup);
s.beginGroup(PlaylistSettings::kSettingsGroup);
continue_on_error_ = s.value("continue_on_error", false).toBool();
greyout_ = s.value("greyout_songs_play", true).toBool();
s.endGroup();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
menu_previousmode_ = static_cast<BehaviourSettingsPage::PreviousBehaviour>(s.value("menu_previousmode", static_cast<int>(BehaviourSettingsPage::PreviousBehaviour::DontRestart)).toInt());
seek_step_sec_ = s.value("seek_step_sec", 10).toInt();
volume_increment_ = s.value("volume_increment", 5).toUInt();
s.beginGroup(BehaviourSettings::kSettingsGroup);
menu_previousmode_ = static_cast<BehaviourSettings::PreviousBehaviour>(s.value(BehaviourSettings::kMenuPreviousMode, static_cast<int>(BehaviourSettings::PreviousBehaviour::DontRestart)).toInt());
seek_step_sec_ = s.value(BehaviourSettings::kSeekStepSec, 10).toInt();
volume_increment_ = s.value(BehaviourSettings::kVolumeIncrement, 5).toUInt();
s.endGroup();
engine_->ReloadSettings();
}
void Player::UrlHandlerRegistered(UrlHandler *url_handler) const {
QObject::connect(url_handler, &UrlHandler::AsyncLoadComplete, this, &Player::HandleLoadResult);
}
void Player::LoadVolume() {
Settings s;
s.beginGroup(kSettingsGroup);
const uint volume = s.value("volume", 100).toInt();
const uint volume = s.value(kVolume, 100).toInt();
s.endGroup();
SetVolume(volume);
@ -229,7 +244,7 @@ void Player::SaveVolume() {
Settings s;
s.beginGroup(kSettingsGroup);
s.setValue("volume", volume_);
s.setValue(kVolume, volume_);
s.endGroup();
}
@ -239,14 +254,14 @@ void Player::SavePlaybackStatus() {
Settings s;
s.beginGroup(kSettingsGroup);
s.setValue("playback_state", static_cast<int>(app_->player()->GetState()));
if (app_->player()->GetState() == EngineBase::State::Playing || app_->player()->GetState() == EngineBase::State::Paused) {
s.setValue("playback_playlist", app_->playlist_manager()->active()->id());
s.setValue("playback_position", app_->player()->engine()->position_nanosec() / kNsecPerSec);
s.setValue(kPlaybackState, static_cast<int>(GetState()));
if (GetState() == EngineBase::State::Playing || GetState() == EngineBase::State::Paused) {
s.setValue(kPlaybackPlaylist, playlist_manager_->active()->id());
s.setValue(kPlaybackPosition, engine_->position_nanosec() / kNsecPerSec);
}
else {
s.setValue("playback_playlist", -1);
s.setValue("playback_position", 0);
s.setValue(kPlaybackPlaylist, -1);
s.setValue(kPlaybackPosition, 0);
}
s.endGroup();
@ -258,12 +273,12 @@ void Player::PlaylistsLoaded() {
Settings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
s.beginGroup(BehaviourSettings::kSettingsGroup);
const bool resume_playback = s.value("resumeplayback", false).toBool();
s.endGroup();
s.beginGroup(Player::kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value("playback_state", static_cast<int>(EngineBase::State::Empty)).toInt());
s.beginGroup(kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value(kPlaybackState, static_cast<int>(EngineBase::State::Empty)).toInt());
s.endGroup();
if (resume_playback && (playback_state == EngineBase::State::Playing || playback_state == EngineBase::State::Paused)) {
@ -283,14 +298,14 @@ void Player::ResumePlayback() {
Settings s;
s.beginGroup(kSettingsGroup);
const EngineBase::State playback_state = static_cast<EngineBase::State>(s.value("playback_state", static_cast<int>(EngineBase::State::Empty)).toInt());
const int playback_playlist = s.value("playback_playlist", -1).toInt();
const int playback_position = s.value("playback_position", 0).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(kPlaybackPlaylist, -1).toInt();
const int playback_position = s.value(kPlaybackPosition, 0).toInt();
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.
app_->playlist_manager()->SetActiveToCurrent();
playlist_manager_->SetActiveToCurrent();
if (playback_state == EngineBase::State::Playing) {
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.
s.beginGroup(kSettingsGroup);
s.setValue("playback_state", static_cast<int>(EngineBase::State::Empty));
s.setValue("playback_playlist", -1);
s.setValue("playback_position", 0);
s.setValue(kPlaybackState, static_cast<int>(EngineBase::State::Empty));
s.setValue(kPlaybackPlaylist, -1);
s.setValue(kPlaybackPosition, 0);
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
const int current_row = app_->playlist_manager()->active()->current_row();
const int current_row = playlist_manager_->active()->current_row();
if (current_row == -1) {
return;
}
PlaylistItemPtr current_item = app_->playlist_manager()->active()->current_item();
PlaylistItemPtr current_item = playlist_manager_->active()->current_item();
if (!current_item) {
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;
PlaylistItemPtr next_item;
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;
@ -408,10 +423,10 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
if (update) {
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) {
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();
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 (change & EngineBase::TrackChangeType::Auto) {
const PlaylistSequence::RepeatMode repeat_mode = active_playlist->RepeatMode();
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.
nb_errors_received_ = 0;
Stop();
@ -482,8 +497,8 @@ void Player::NextItem(const EngineBase::TrackChangeFlags change, const Playlist:
int i = active_playlist->next_row(ignore_repeat_track);
if (i == -1) {
app_->playlist_manager()->active()->set_current_row(i);
app_->playlist_manager()->active()->reset_last_played();
playlist_manager_->active()->set_current_row(i);
playlist_manager_->active()->reset_last_played();
Q_EMIT PlaylistFinished();
Stop();
return;
@ -503,9 +518,9 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
play_offset_nanosec_ = 0;
Playlist *playlist = nullptr;
const QList<Playlist*> playlists = app_->playlist_manager()->GetAllPlaylists();
const QList<Playlist*> playlists = playlist_manager_->GetAllPlaylists();
for (Playlist *p : playlists) {
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) {
if (playlist_name == playlist_manager_->GetPlaylistName(p->id())) {
playlist = p;
break;
}
@ -516,12 +531,12 @@ void Player::PlayPlaylistInternal(const EngineBase::TrackChangeFlags change, con
return;
}
app_->playlist_manager()->SetActivePlaylist(playlist->id());
app_->playlist_manager()->SetCurrentPlaylist(playlist->id());
playlist_manager_->SetActivePlaylist(playlist->id());
playlist_manager_->SetCurrentPlaylist(playlist->id());
if (playlist->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
int i = playlist_manager_->active()->current_row();
if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0;
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) {
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.
const int next_row = app_->playlist_manager()->active()->next_row();
const int next_row = playlist_manager_->active()->next_row();
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);
return true;
@ -550,7 +565,7 @@ bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
void Player::TrackEnded() {
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;
@ -584,10 +599,10 @@ void Player::PlayPause(const quint64 offset_nanosec, const Playlist::AutoScroll
case EngineBase::State::Idle:{
pause_time_ = QDateTime();
play_offset_nanosec_ = offset_nanosec;
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
if (app_->playlist_manager()->active()->rowCount() == 0) break;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
playlist_manager_->SetActivePlaylist(playlist_manager_->current_id());
if (playlist_manager_->active()->rowCount() == 0) break;
int i = playlist_manager_->active()->current_row();
if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0;
PlayAt(i, false, offset_nanosec, EngineBase::TrackChangeType::First, autoscroll, true);
break;
@ -600,12 +615,12 @@ void Player::UnPause() {
if (current_item_ && pause_time_.isValid()) {
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();
if (time >= 30) { // Stream URL might be expired.
qLog(Debug) << "Re-requesting stream URL for" << song.url();
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()));
return;
}
@ -636,8 +651,8 @@ void Player::RestartOrPrevious() {
void Player::Stop(const bool stop_after) {
engine_->Stop(stop_after);
app_->playlist_manager()->active()->set_current_row(-1);
app_->playlist_manager()->active()->reset_played_indexes();
playlist_manager_->active()->set_current_row(-1);
playlist_manager_->active()->reset_played_indexes();
current_item_.reset();
pause_time_ = QDateTime();
play_offset_nanosec_ = 0;
@ -645,13 +660,13 @@ void Player::Stop(const bool stop_after) {
}
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 {
// 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;
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
QDateTime now = QDateTime::currentDateTime();
if (last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(now) >= 2) {
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;
}
last_pressed_previous_ = now;
}
int i = app_->playlist_manager()->active()->previous_row(ignore_repeat_track);
app_->playlist_manager()->active()->set_current_row(i, Playlist::AutoScroll::Always, false);
int i = playlist_manager_->active()->previous_row(ignore_repeat_track);
playlist_manager_->active()->set_current_row(i, Playlist::AutoScroll::Always, false);
if (i == -1) {
Stop();
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_);
}
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;
}
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);
if (app_->playlist_manager()->active()->current_row() == -1) {
playlist_manager_->active()->set_current_row(index, autoscroll, false, force_inform);
if (playlist_manager_->active()->current_row() == -1) {
// Maybe index didn't exist in the playlist.
return;
}
current_item_ = app_->playlist_manager()->active()->current_item();
current_item_ = playlist_manager_->active()->current_item();
const QUrl url = current_item_->StreamUrl();
if (url_handlers_.contains(url.scheme())) {
if (url_handlers_->CanHandle(url)) {
// It's already loading
if (loading_async_.contains(url)) {
return;
@ -814,7 +829,7 @@ void Player::PlayAt(const int index, const bool pause, const quint64 offset_nano
pause_ = pause;
stream_change_type_ = change;
autoscroll_ = autoscroll;
UrlHandler *url_handler = url_handlers_.value(url.scheme());
UrlHandler *url_handler = url_handlers_->GetUrlHandler(url);
HandleLoadResult(url_handler->StartLoading(url));
}
else {
@ -844,12 +859,12 @@ void Player::SeekTo(const quint64 seconds) {
engine_->Seek(nanosec);
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);
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) {
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) {
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
PlaylistItemPtr item = playlist_manager_->active()->current_item();
if (item && engine_metadata.media_url == item->Url()) {
Song song = item->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;
}
}
}
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) {
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()) {
Song song = next_item->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 {
if (pos < 0 || pos >= app_->playlist_manager()->active()->rowCount())
if (pos < 0 || pos >= playlist_manager_->active()->rowCount())
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();
play_offset_nanosec_ = offset_nanosec;
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
if (app_->playlist_manager()->active()->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
playlist_manager_->SetActivePlaylist(playlist_manager_->current_id());
if (playlist_manager_->active()->rowCount() == 0) return;
int i = playlist_manager_->active()->current_row();
if (i == -1) i = playlist_manager_->active()->last_played_row();
if (i == -1) i = 0;
PlayAt(i, true, offset_nanosec, EngineBase::TrackChangeType::First, Playlist::AutoScroll::Always, true);
@ -959,11 +974,11 @@ void Player::TogglePrettyOSD() {
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;
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()) {
@ -989,10 +1004,10 @@ void Player::TrackAboutToEnd() {
QUrl url = next_item->StreamUrl();
// 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;
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);
switch (result.type_) {
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() {
Q_EMIT Authenticated();
}

View File

@ -31,110 +31,29 @@
#include <QString>
#include <QUrl>
#include "shared_ptr.h"
#include "urlhandler.h"
#include "includes/shared_ptr.h"
#include "core/urlhandler.h"
#include "core/enginemetadata.h"
#include "engine/enginebase.h"
#include "engine/enginemetadata.h"
#include "playlist/playlist.h"
#include "playlist/playlistitem.h"
#include "settings/behavioursettingspage.h"
#include "constants/behavioursettings.h"
#include "playerinterface.h"
class QTimer;
class Application;
class Song;
class TaskManager;
class UrlHandlers;
class PlaylistManager;
class AnalyzerContainer;
class Equalizer;
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 {
Q_OBJECT
public:
explicit Player(Application *app, QObject *parent = nullptr);
static const char *kSettingsGroup;
explicit Player(const SharedPtr<TaskManager> task_manager, const SharedPtr<UrlHandlers> url_handlers, const SharedPtr<PlaylistManager> playlist_manager, QObject *parent = nullptr);
EngineBase::Type CreateEngine(EngineBase::Type Type);
void Init();
@ -146,11 +65,6 @@ class Player : public PlayerInterface {
PlaylistItemPtr GetCurrentItem() const override { return current_item_; }
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;
void SetAnalyzer(AnalyzerContainer *analyzer) { analyzer_ = analyzer; }
@ -158,6 +72,7 @@ class Player : public PlayerInterface {
public Q_SLOTS:
void ReloadSettings() override;
void LoadVolume() override;
void SaveVolume() override;
void SavePlaybackStatus() override;
@ -197,6 +112,8 @@ class Player : public PlayerInterface {
void EngineChanged(const EngineBase::Type Type);
private Q_SLOTS:
void UrlHandlerRegistered(UrlHandler *url_handler) const;
void EngineStateChanged(const EngineBase::State);
void EngineMetadataReceived(const EngineMetadata &engine_metadata);
void TrackAboutToEnd();
@ -212,7 +129,6 @@ class Player : public PlayerInterface {
void ValidSongRequested(const QUrl&);
void InvalidSongRequested(const QUrl&);
void UrlHandlerDestroyed(QObject *object);
void HandleLoadResult(const UrlHandler::LoadResult &result);
private:
@ -224,7 +140,9 @@ class Player : public PlayerInterface {
void UnPause();
private:
Application *app_;
const SharedPtr<TaskManager> task_manager_;
const SharedPtr<UrlHandlers> url_handlers_;
const SharedPtr<PlaylistManager> playlist_manager_;
SharedPtr<EngineBase> engine_;
GstStartup *gst_startup_;
AnalyzerContainer *analyzer_;
@ -242,8 +160,6 @@ class Player : public PlayerInterface {
EngineBase::State last_state_;
int nb_errors_received_;
QMap<QString, UrlHandler*> url_handlers_;
QList<QUrl> loading_async_;
uint volume_;
uint volume_before_mute_;
@ -251,13 +167,12 @@ class Player : public PlayerInterface {
bool continue_on_error_;
bool greyout_;
BehaviourSettingsPage::PreviousBehaviour menu_previousmode_;
BehaviourSettings::PreviousBehaviour menu_previousmode_;
int seek_step_sec_;
uint volume_increment_;
QDateTime pause_time_;
quint64 play_offset_nanosec_;
};
#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 "core/iconloader.h"
#include "engine/enginemetadata.h"
#include "core/enginemetadata.h"
#include "utilities/strutils.h"
#include "utilities/timeutils.h"
#include "utilities/coverutils.h"
#include "utilities/timeconstants.h"
#include "constants/timeconstants.h"
#include "utilities/sqlhelper.h"
#include "song.h"
@ -1960,3 +1960,43 @@ QString Song::TitleRemoveMisc(const QString &title) {
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 TitleRemoveMisc(const QString &title);
static QString GetNameForNewPlaylist(const QList<Song> &songs);
static inline QString TagLibStringToQString(const TagLib::String &s) {
return QString::fromUtf8((s).toCString(true));
}

View File

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

View File

@ -38,11 +38,12 @@
#include <QStringList>
#include <QUrl>
#include "shared_ptr.h"
#include "song.h"
#include "includes/shared_ptr.h"
#include "core/song.h"
class QTimer;
class Player;
class UrlHandlers;
class TagReaderClient;
class CollectionBackendInterface;
class PlaylistParser;
class ParserBase;
@ -56,7 +57,11 @@ class SongLoader : public QObject {
Q_OBJECT
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;
enum class Result {
@ -137,8 +142,9 @@ class SongLoader : public QObject {
QUrl url_;
SongList songs_;
const SharedPtr<Player> player_;
SharedPtr<CollectionBackendInterface> collection_backend_;
const SharedPtr<UrlHandlers> url_handlers_;
const SharedPtr<CollectionBackendInterface> collection_backend_;
const SharedPtr<TagReaderClient> tagreader_client_;
QTimer *timeout_timer_;
PlaylistParser *playlist_parser_;
CueParser *cue_parser_;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* 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

View File

@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
* 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

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 <QtEvents>
#include "utilities/filenameconstants.h"
#include "constants/filenameconstants.h"
#include "constants/filefilterconstants.h"
#include "constants/coverssettings.h"
#include "utilities/strutils.h"
#include "utilities/mimeutils.h"
#include "utilities/coveroptions.h"
#include "utilities/coverutils.h"
#include "utilities/screenutils.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/song.h"
#include "core/iconloader.h"
#include "core/settings.h"
#include "tagreader/tagreaderclient.h"
#include "collection/collectionfilteroptions.h"
#include "collection/collectionbackend.h"
#include "settings/coverssettingspage.h"
#include "streaming/streamingservices.h"
#include "streaming/streamingservice.h"
#include "albumcoverchoicecontroller.h"
@ -80,15 +81,10 @@
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;
AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent)
: QWidget(parent),
app_(nullptr),
cover_searcher_(nullptr),
cover_fetcher_(nullptr),
save_file_dialog_(nullptr),
@ -130,12 +126,22 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent)
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_searcher_ = new AlbumCoverSearcher(QIcon(u":/pictures/cdcase.png"_s), app, this);
cover_fetcher_ = new AlbumCoverFetcher(cover_providers, network, this);
cover_searcher_ = new AlbumCoverSearcher(QIcon(u":/pictures/cdcase.png"_s), albumcover_loader, this);
cover_searcher_->Init(cover_fetcher_);
QObject::connect(cover_fetcher_, &AlbumCoverFetcher::AlbumCoverFetched, this, &AlbumCoverChoiceController::AlbumCoverFetched);
@ -145,13 +151,13 @@ void AlbumCoverChoiceController::Init(Application *app) {
void AlbumCoverChoiceController::ReloadSettings() {
Settings s;
s.beginGroup(CoversSettingsPage::kSettingsGroup);
cover_options_.cover_type = static_cast<CoverOptions::CoverType>(s.value(CoversSettingsPage::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_pattern = s.value(CoversSettingsPage::kSavePattern, u"%albumartist-%album"_s).toString();
cover_options_.cover_overwrite = s.value(CoversSettingsPage::kSaveOverwrite, false).toBool();
cover_options_.cover_lowercase = s.value(CoversSettingsPage::kSaveLowercase, false).toBool();
cover_options_.cover_replace_spaces = s.value(CoversSettingsPage::kSaveReplaceSpaces, false).toBool();
s.beginGroup(CoversSettings::kSettingsGroup);
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(CoversSettings::kSaveFilename, static_cast<int>(CoverOptions::CoverFilename::Pattern)).toInt());
cover_options_.cover_pattern = s.value(CoversSettings::kSavePattern, u"%albumartist-%album"_s).toString();
cover_options_.cover_overwrite = s.value(CoversSettings::kSaveOverwrite, false).toBool();
cover_options_.cover_lowercase = s.value(CoversSettings::kSaveLowercase, false).toBool();
cover_options_.cover_replace_spaces = s.value(CoversSettings::kSaveReplaceSpaces, false).toBool();
s.endGroup();
cover_types_ = AlbumCoverLoaderOptions::LoadTypes();
@ -179,7 +185,7 @@ AlbumCoverImageResult AlbumCoverChoiceController::LoadImageFromFile(Song *song)
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();
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();
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();
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);
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;
@ -316,7 +322,7 @@ void AlbumCoverChoiceController::LoadCoverFromURL(Song *song) {
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();
@ -446,7 +452,7 @@ void AlbumCoverChoiceController::ShowCover(const Song &song, const QImage &image
case AlbumCoverLoaderOptions::Type::Embedded:{
if (song.art_embedded() && !song.url().isEmpty() && song.url().isValid() && song.url().isLocalFile()) {
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()) {
QPixmap pixmap = QPixmap::fromImage(image_embedded_cover);
if (!pixmap.isNull()) {
@ -548,11 +554,11 @@ void AlbumCoverChoiceController::SaveArtEmbeddedToSong(Song *song, const bool ar
song->set_art_unset(false);
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()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song);
if (*song == current_albumcover_loader_->last_song()) {
current_albumcover_loader_->LoadAlbumCover(*song);
}
}
@ -567,7 +573,7 @@ void AlbumCoverChoiceController::SaveArtManualToSong(Song *song, const QUrl &art
// Update the backends.
switch (song->source()) {
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;
case Song::Source::LocalFile:
case Song::Source::CDDA:
@ -581,7 +587,7 @@ void AlbumCoverChoiceController::SaveArtManualToSong(Song *song, const QUrl &art
case Song::Source::Tidal:
case Song::Source::Spotify:
case Song::Source::Qobuz:
StreamingServicePtr service = app_->streaming_services()->ServiceBySource(song->source());
StreamingServicePtr service = streaming_services_->ServiceBySource(song->source());
if (!service) break;
if (service->artists_collection_backend()) {
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;
}
if (*song == app_->current_albumcover_loader()->last_song()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song);
if (*song == current_albumcover_loader_->last_song()) {
current_albumcover_loader_->LoadAlbumCover(*song);
}
}
@ -611,11 +617,11 @@ void AlbumCoverChoiceController::ClearAlbumCoverForSong(Song *song) {
song->clear_art_manual();
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()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song);
if (*song == current_albumcover_loader_->last_song()) {
current_albumcover_loader_->LoadAlbumCover(*song);
}
}
@ -630,11 +636,11 @@ void AlbumCoverChoiceController::UnsetAlbumCoverForSong(Song *song) {
song->clear_art_automatic();
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()) {
app_->current_albumcover_loader()->LoadAlbumCover(*song);
if (*song == current_albumcover_loader_->last_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) {
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>();
QObject::connect(watcher, &QFutureWatcher<SongList>::finished, this, [this, watcher, cover_filename, image_data, mime_type]() {
const SongList collection_songs = watcher->result();
@ -736,7 +742,7 @@ void AlbumCoverChoiceController::SaveCoverEmbeddedToSong(const Song &song, const
QMutexLocker l(&mutex_cover_save_tasks_);
cover_save_tasks_.append(song);
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); });
}

View File

@ -50,25 +50,33 @@ class QMenu;
class QDragEnterEvent;
class QDropEvent;
class Application;
class NetworkAccessManager;
class CollectionBackend;
class AlbumCoverLoader;
class CurrentAlbumCoverLoader;
class CoverProviders;
class AlbumCoverFetcher;
class AlbumCoverSearcher;
class CoverFromURLDialog;
struct CoverSearchStatistics;
class StreamingServices;
// Controller for the common album cover related menu options.
class AlbumCoverChoiceController : public QWidget {
Q_OBJECT
public:
static const char *kLoadImageFileFilter;
static const char *kSaveImageFileFilter;
static const char *kAllFilesFilter;
explicit AlbumCoverChoiceController(QWidget *parent = nullptr);
~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();
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 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_;
AlbumCoverFetcher *cover_fetcher_;

View File

@ -37,7 +37,9 @@
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) {

View File

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

View File

@ -33,8 +33,9 @@ namespace {
constexpr int kMaxConcurrentRequests = 3;
}
AlbumCoverExporter::AlbumCoverExporter(QObject *parent)
AlbumCoverExporter::AlbumCoverExporter(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent)
: QObject(parent),
tagreader_client_(tagreader_client),
thread_pool_(new QThreadPool(this)),
exported_(0),
skipped_(0),
@ -52,7 +53,7 @@ void AlbumCoverExporter::SetCoverTypes(const AlbumCoverLoaderOptions::Types &cov
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());
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include "includes/shared_ptr.h"
#include "core/logging.h"
#include "core/networkaccessmanager.h"
#include "core/song.h"
@ -54,8 +55,9 @@ namespace {
constexpr int kMaxRedirects = 3;
}
AlbumCoverLoader::AlbumCoverLoader(QObject *parent)
AlbumCoverLoader::AlbumCoverLoader(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent)
: QObject(parent),
tagreader_client_(tagreader_client),
network_(new NetworkAccessManager(this)),
timer_process_tasks_(new QTimer(this)),
stop_requested_(false),
@ -318,7 +320,7 @@ AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadImage(TaskPtr task, cons
AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadEmbeddedImage(TaskPtr task) {
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)) {
return LoadImageResult(AlbumCoverLoaderResult::Type::Embedded, LoadImageResult::Status::Success);
}

View File

@ -32,7 +32,7 @@
#include <QString>
#include <QImage>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "core/song.h"
#include "albumcoverloaderoptions.h"
#include "albumcoverloaderresult.h"
@ -42,12 +42,13 @@ class QThread;
class QTimer;
class QNetworkReply;
class NetworkAccessManager;
class TagReaderClient;
class AlbumCoverLoader : public QObject {
Q_OBJECT
public:
explicit AlbumCoverLoader(QObject *parent = nullptr);
explicit AlbumCoverLoader(const SharedPtr<TagReaderClient> tagreader_client, QObject *parent = nullptr);
void ExitAsync();
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);
private:
SharedPtr<NetworkAccessManager> network_;
const SharedPtr<TagReaderClient> tagreader_client_;
const SharedPtr<NetworkAccessManager> network_;
QTimer *timer_process_tasks_;
bool stop_requested_;
QMutex mutex_load_image_async_;

View File

@ -22,7 +22,7 @@
#include <QSettings>
#include "core/settings.h"
#include "settings/coverssettingspage.h"
#include "constants/coverssettings.h"
using namespace Qt::Literals::StringLiterals;
@ -37,9 +37,9 @@ AlbumCoverLoaderOptions::Types AlbumCoverLoaderOptions::LoadTypes() {
Types cover_types;
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 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) {
if (cover_type_str == "art_unset"_L1) {
cover_types << AlbumCoverLoaderOptions::Type::Unset;

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