Compare commits

..

454 Commits
0.9.3 ... 1.0.0

Author SHA1 Message Date
Jonas Kvinge
9d18f40c5f Release 1.0.0 2021-10-14 20:50:41 +02:00
Jonas Kvinge
47e771ee37 Update Changelog 2021-10-14 20:50:20 +02:00
Jonas Kvinge
20760dcf0e Fix FreeBSD CI 2021-10-13 19:30:46 +02:00
Strawbs Bot
2edf12e4cd Update translations 2021-10-13 01:16:38 +02:00
Jonas Kvinge
c946c1d1c4 Player: Don't preload next song while playing module music
Workaround bug in GStreamer

Fixes #787
Fixes #772
2021-10-12 22:59:23 +02:00
Jonas Kvinge
bb5fe5a6d1 Add Song::is_module_music() 2021-10-12 22:58:39 +02:00
Jonas Kvinge
814d2fa9fc MainWindow: Use Song::PrettyTitle() in debug message 2021-10-12 22:57:59 +02:00
Jonas Kvinge
10d89cb6a6 FileView: Add module music formats to file filter 2021-10-12 21:44:30 +02:00
Jonas Kvinge
a6569d09ac Read XM and IT module music 2021-10-12 21:43:46 +02:00
staticssleever668
b38ad81928 Remove use of C-style casts 2021-10-12 18:50:17 +02:00
Jonas Kvinge
637772f8f0 GstEnginePipeline: Convert S24_32LE for analyzer 2021-10-12 18:46:12 +02:00
Jonas Kvinge
0093eb6c2c GstEnginePipeline: Refactor code 2021-10-12 18:36:58 +02:00
Jonas Kvinge
f3b70d1cb2 GstEnginePipeline: Rename variable 2021-10-12 18:36:13 +02:00
Strawbs Bot
5152f57f6e Update translations 2021-10-11 01:02:01 +02:00
Jonas Kvinge
e6112a311d Add openmpt to nsi 2021-10-10 23:31:08 +02:00
Jonas Kvinge
e069d069d2 GstEngine: Remove unused variables 2021-10-10 21:12:31 +02:00
Jonas Kvinge
fd74bbc868 Remove unused GstElementDeleter 2021-10-10 16:33:07 +02:00
Strawbs Bot
3b37aea3da Update translations 2021-10-10 01:02:39 +02:00
Jonas Kvinge
2fd4bbbd49 Update Changelog 2021-10-09 22:25:27 +02:00
Jonas Kvinge
17226b524b SubsonicSettingsPage: Rename HTTP2 to HTTP/2 2021-10-09 16:38:13 +02:00
Jonas Kvinge
f38b33ee50 Scrobbler: Use seconds instead of minutes for submit delay 2021-10-09 14:05:05 +02:00
Jonas Kvinge
0da0e0a6db macdeployqt: Fix typo 2021-10-09 01:50:19 +02:00
Jonas Kvinge
de2b6d1dee macdeployqt: Replace QSet with QList 2021-10-09 01:50:19 +02:00
Jonas Kvinge
c9a501ddc8 Update README.md 2021-10-09 00:39:11 +02:00
Jonas Kvinge
c5babbbb22 Update README.md 2021-10-09 00:34:28 +02:00
Jonas Kvinge
1fd0d1a330 macdeployqt: Remove FinalCheck from header 2021-10-08 23:47:33 +02:00
Jonas Kvinge
84d9b30fec macdeployqt: Replace bearer with tls 2021-10-08 23:40:41 +02:00
Jonas Kvinge
d0f1522f0d GstEnginePipeline: Use gst_bus_remove_watch to remove bus watch 2021-10-08 23:39:29 +02:00
Jonas Kvinge
6b2e25122d nsi: Remove tls dir 2021-10-07 23:48:23 +02:00
Strawbs Bot
ce3026f91c Update translations 2021-10-06 01:02:49 +02:00
Jonas Kvinge
4b384b3b59 Update ccpp.yml 2021-10-04 11:51:16 +02:00
Strawbs Bot
da7605a234 Update translations 2021-10-03 01:08:24 +02:00
Jonas Kvinge
3abdec1b91 Add qopensslbackend.dll to nsi and CI 2021-10-01 23:52:59 +02:00
Strawbs Bot
ef34199887 Update translations 2021-10-01 01:02:14 +02:00
Jonas Kvinge
08eca88e2f Add require for qt6-network-tls to RPM spec 2021-09-30 22:54:08 +02:00
Strawbs Bot
4636cc74dc Update translations 2021-09-29 01:03:09 +02:00
Jonas Kvinge
4cc54a2f05 PlaylistTabBar: Hide star option with clicked outside of a tab 2021-09-28 22:19:31 +02:00
Jonas Kvinge
9bd728889b PlaylistTabBar: Add right click option to star playlist
Fixes #782
2021-09-28 22:14:25 +02:00
Adam Hill
d870115467 Set SmoothPixmapTransform for playing-widget and context album art display 2021-09-28 21:52:31 +02:00
Jonas Kvinge
2b445fb563 StyleSheetLoader: Cleanup code and add workaround for macOS dark theme
Fixes #778
2021-09-28 17:25:22 +02:00
Strawbs Bot
44a3bad278 Update translations 2021-09-28 01:12:34 +02:00
Jonas Kvinge
8d2615547d Add keyboard shortcut for focusing search fields
Fixes #779
2021-09-27 21:42:30 +02:00
Jonas Kvinge
3292db8b77 Add missing override 2021-09-27 21:41:45 +02:00
Jonas Kvinge
8c6ad52437 Organize: Update collection directory ID and song path immediately
Fixes #781
2021-09-27 19:30:31 +02:00
Jonas Kvinge
62e53b53f0 Replace assert with Q_ASSERT 2021-09-27 19:30:31 +02:00
Strawbs Bot
35db157617 Update translations 2021-09-27 01:22:03 +02:00
Jonas Kvinge
cfb137a94b DeviceDatabaseBackend: Fix GetAllDevices() 2021-09-26 21:38:06 +02:00
Jonas Kvinge
9b4a33a612 MPris2: Remove static 2021-09-26 20:14:11 +02:00
Jonas Kvinge
918b7c1e03 Move chrono include 2021-09-26 20:06:04 +02:00
Jonas Kvinge
8fb354705c Update Changelog 2021-09-26 19:03:01 +02:00
Jonas Kvinge
b3826064b7 Bump device schema version 2021-09-26 18:35:03 +02:00
Jonas Kvinge
acba68dab9 MergedProxyModel: Add FIXME 2021-09-26 18:34:44 +02:00
Jonas Kvinge
32ea709350 Subsonic: Turn off HTTP/2 setting by default 2021-09-26 16:50:41 +02:00
Jonas Kvinge
c15dfc7c5d Subsonic: Make HTTP/2 setting visible with Qt 5.15.0 and higher 2021-09-26 16:49:53 +02:00
Jonas Kvinge
92f6a0c6c8 Add mod and s3m to filetypes 2021-09-26 16:23:02 +02:00
Jonas Kvinge
0235be538b Moodbar: Check for nullptr 2021-09-26 14:42:24 +02:00
Jonas Kvinge
abf19e7a27 Moodbar: Use gst_element_link_many 2021-09-26 14:42:05 +02:00
Jonas Kvinge
1d3540dca6 Remove unused MoodbarPipeline::IsAvailable function 2021-09-26 14:41:36 +02:00
Jonas Kvinge
ddfc9d911b Extend unit tests 2021-09-26 01:15:17 +02:00
Strawbs Bot
b952256988 Update translations 2021-09-26 01:02:42 +02:00
Jonas Kvinge
71868936d1 Subsonic: Use common static function CreateUrl() 2021-09-25 01:18:40 +02:00
Strawbs Bot
23ef4fb132 Update translations 2021-09-22 01:04:55 +02:00
Jonas Kvinge
f74f2ca3ef Subsonic: Remove redundant null check 2021-09-21 01:36:38 +02:00
Jonas Kvinge
f4f005cdd6 Subsonic: Fix auth method for URL handler 2021-09-21 01:30:01 +02:00
Jonas Kvinge
3137652280 Use ParamList 2021-09-21 01:30:01 +02:00
Strawbs Bot
88c6f0340a Update translations 2021-09-21 01:01:57 +02:00
Strawbs Bot
86146c7292 Update translations 2021-09-20 01:20:57 +02:00
Tom Kranz
fad39b6d67 Store Tidal MPEG-DASH file in data uri 2021-09-19 19:56:22 +02:00
Jonas Kvinge
09a9330f3e Show error when reading or saving album covers 2021-09-19 19:31:34 +02:00
Jonas Kvinge
d2d7f32c45 Add new method for updating songs based on song ID
Show status updating database.

Fixes #750
2021-09-19 15:43:36 +02:00
Tom Kranz
120b18b399 Use XSPF image elements as manually set artwork 2021-09-19 13:40:42 +02:00
Jonas Kvinge
32b9e6b73d Make error dialog larger 2021-09-18 17:12:59 +02:00
Jonas Kvinge
8d6e5272b9 Update protobuf in nsi 2021-09-17 01:35:20 +02:00
Strawbs Bot
b6f90a9715 Update translations 2021-09-15 01:02:18 +02:00
Jonas Kvinge
e3d0b777fc Add algorithm include 2021-09-14 22:57:09 +02:00
Jonas Kvinge
79a8450462 SpotifyCoverProvider: Fix trying to set timer to minus value 2021-09-14 22:12:28 +02:00
EmmanuelMess
1a967597e8 Fix roundings with lround() 2021-09-14 21:29:29 +02:00
Jonas Kvinge
d5b0794b00 Remove unneeded this 2021-09-13 20:49:33 +02:00
Jonas Kvinge
d78ee3d62b Fix total playlist length for songs without length 2021-09-13 20:46:55 +02:00
Strawbs Bot
03037a9a6b Update translations 2021-09-13 01:12:16 +02:00
Emmanuel
0637b65846 Fix a few narrowing conversions (#761)
* Fix narrowing conversion in album cover loader

* Fix narrowing conversions in discogs cover provider

* Fix narrowing conversions in spotify cover provider

* Add explicit conversion in moodbarbuilder

* Fix narrowing conversions in osd dbus

* Make WordyTimeNanosec use unsigned quint64

* Fix narrowing conversions in song

* Fix narrowing conversions in qobuz stream url request

* Make ConnectionInfo.msgLen use unsigned quint64

* Make AnalizerBase.timeout to signed int

* Fix narrowing conversions in album cover fetcher

* Make fht type be unsigned int

* Correct for type in blockanalizer and use std::fill where possible

* Revert "Fix narrowing conversions in song"

This reverts commit 3de291394d.
2021-09-12 21:24:22 +02:00
Jonas Kvinge
e77e914f44 Escape ampersands in playlist tabs
Fixes #760
2021-09-12 21:19:58 +02:00
EmmanuelMess
68c44daef2 Fix SingleApplication static functions called from instance 2021-09-10 18:44:36 +02:00
Strawbs Bot
8ca0b54b18 Update translations 2021-09-10 01:21:33 +02:00
Jonas Kvinge
b77d01baca CI: Use new image for Windows build 2021-09-09 23:06:59 +02:00
Jonas Kvinge
cd509bbbdc Update nsi for OpenSSL 3 2021-09-09 22:05:53 +02:00
Jonas Kvinge
24a3ac9811 Use static QMetaObject::invokeMethod 2021-09-09 21:53:14 +02:00
Jonas Kvinge
d35d3aabc3 Show error dialog for failed SQL queries 2021-09-09 21:45:46 +02:00
Jonas Kvinge
9e624a6c0d Remove openSUSE 15.2 from CI 2021-09-07 01:16:02 +02:00
Strawbs Bot
9823cd25d2 Update translations 2021-09-07 01:01:55 +02:00
Jonas Kvinge
416a8c8f5d Context: Hide albums widget in constructor 2021-09-07 00:10:05 +02:00
Strawbs Bot
c55bdd423e Update translations 2021-09-06 01:02:08 +02:00
Jonas Kvinge
e45e202c5e Subsonic: Make network replies timeout after 30 seconds
Fixes #754
2021-09-05 21:47:00 +02:00
Jonas Kvinge
2280a30ba9 Subsonic: Minor code style fix 2021-09-05 21:46:41 +02:00
Jonas Kvinge
9ac7518156 Subsonic: Remove obsolete return 2021-09-05 21:46:16 +02:00
Jonas Kvinge
0e6dbaf71a Settings: Fix loading subsonic auth method setting 2021-09-05 21:35:21 +02:00
Strawbs Bot
fe018ff8f7 Update translations 2021-09-02 01:03:13 +02:00
Jonas Kvinge
9b01e09302 GlobalShortcutsManager: Minor code fixes 2021-09-02 00:52:44 +02:00
Jonas Kvinge
3127474fd7 GlobalShortcutsManager: Fix reading settings 2021-09-02 00:52:21 +02:00
Jonas Kvinge
b849edfcca GlobalShortcutsBackend: Fix type return 2021-09-01 22:37:08 +02:00
Jonas Kvinge
20a23c2868 Various cleanup to global shortcuts code 2021-09-01 21:37:11 +02:00
Jonas Kvinge
49d9ded684 Fix null pointer crash
Possible fix for #752
2021-09-01 16:37:34 +02:00
Jonas Kvinge
fc57b437c2 Fix radio items leak 2021-08-31 22:45:28 +02:00
Jonas Kvinge
1ba20561ed Fix global shortcuts memory leak 2021-08-31 21:21:59 +02:00
Strawbs Bot
2c8b26e091 Update translations 2021-08-31 01:02:17 +02:00
Jonas Kvinge
62d2a97f38 Add make deploy to macOS CI 2021-08-30 18:13:29 +02:00
Strawbs Bot
44aa292bb5 Update translations 2021-08-30 01:02:16 +02:00
Strawbs Bot
5273b52c31 Update translations 2021-08-26 01:04:00 +02:00
Jonas Kvinge
f143efb810 Revert "Use std::as_const"
This reverts commit 0b15e29324.
2021-08-25 03:47:50 +02:00
Jonas Kvinge
978fb06349 clazy:exclude=range-loop-reference 2021-08-25 03:45:12 +02:00
Jonas Kvinge
e274e8070d Update Changelog 2021-08-25 03:19:42 +02:00
Jonas Kvinge
46bd5b42fa Use 4arg connect 2021-08-25 03:08:30 +02:00
Jonas Kvinge
0b15e29324 Use std::as_const 2021-08-25 03:00:34 +02:00
Jonas Kvinge
75b6669371 Simplify return 2021-08-25 02:58:54 +02:00
Jonas Kvinge
c1c34017e4 Remove get() 2021-08-25 02:58:20 +02:00
Jonas Kvinge
ed08818b6f Use multi-arg 2021-08-25 02:57:57 +02:00
Jonas Kvinge
71adfc0a74 Use std::chrono_literals 2021-08-25 02:57:09 +02:00
Jonas Kvinge
49ccbddb17 Add Q_DISABLE_COPY 2021-08-25 02:56:30 +02:00
Jonas Kvinge
be3c7eef60 Use default 2021-08-25 02:55:54 +02:00
Jonas Kvinge
336c6cdd9d Use 4arg connect 2021-08-25 02:55:00 +02:00
Jonas Kvinge
709a706853 Delete mimedata when returning early 2021-08-25 02:38:58 +02:00
Jonas Kvinge
dad62faf88 Use fully-qualified parameter for signal 2021-08-25 02:36:18 +02:00
Jonas Kvinge
6e0a5fb5c6 Make const 2021-08-25 02:29:05 +02:00
Jonas Kvinge
4b9551d27f Simplify if statement 2021-08-25 02:28:41 +02:00
Strawbs Bot
2a67bc9926 Update translations 2021-08-25 01:16:47 +02:00
Jonas Kvinge
d02241d32c Fix compile with MSVC 2021-08-24 21:49:06 +02:00
Strawbs Bot
55e038d345 Update translations 2021-08-24 01:08:33 +02:00
Jonas Kvinge
ea2bfbda44 Formatting 2021-08-23 21:21:08 +02:00
Jonas Kvinge
ed7794f396 macdeployqt: Replace foreach with C++11 for loop 2021-08-23 01:44:35 +02:00
Strawbs Bot
086646f311 Update translations 2021-08-23 01:02:20 +02:00
Jonas Kvinge
ad169ca5a5 Remove unneeded namespace prefix 2021-08-22 23:29:44 +02:00
Jonas Kvinge
671e636aef Remove some uses of auto 2021-08-22 23:27:25 +02:00
Jonas Kvinge
72d381e9ed Replace qAsConst with C++17 std::as_const 2021-08-22 23:26:53 +02:00
Jonas Kvinge
c076933b52 Use FREEBSD variable 2021-08-22 23:22:15 +02:00
Jonas Kvinge
fdb5c813ad Use actions/checkout@v1.2.0 for FreeBSD too 2021-08-20 22:36:44 +02:00
Strawbs Bot
1bda6633b1 Update translations 2021-08-20 01:02:15 +02:00
Jonas Kvinge
249a5bf3b7 macOS deploy CI improvements 2021-08-19 22:59:38 +02:00
Jonas Kvinge
679b468618 macdeployqt: Fix QLibraryInfo::LibraryExecutablesPath with Qt 5 2021-08-19 20:27:21 +02:00
Jonas Kvinge
cf2a0af3d9 Fix macdeploycheck link directories 2021-08-19 20:24:11 +02:00
Jonas Kvinge
82be53224d Simplify libgpod link directories 2021-08-19 20:23:20 +02:00
Jonas Kvinge
9cc995cb52 Add macdeploycheck 2021-08-19 19:17:43 +02:00
Jonas Kvinge
42d414797a macdeployqt: Formatting 2021-08-19 19:17:43 +02:00
Jonas Kvinge
b071a4df70 macdeployqt: Simplify code in changeInstallName 2021-08-19 19:17:43 +02:00
Jonas Kvinge
46d927291c macdeployqt: Adapt upstream change 2021-08-19 19:17:43 +02:00
Jonas Kvinge
50ab01d9c9 macdeployqt: Remove unused option to specify plugins dir 2021-08-19 19:17:43 +02:00
Jonas Kvinge
dc8fe63acf Add script to copy gstreamer plugins on macOS 2021-08-19 19:17:43 +02:00
Jonas Kvinge
4fc5863888 Add taglib and tagparser link directories 2021-08-19 19:17:43 +02:00
Jonas Kvinge
e2d8149dcf Formatting 2021-08-19 19:17:43 +02:00
Jonas Kvinge
5f156d6bab Only read local files in SongLoader::EffectiveSongLoad 2021-08-19 19:17:43 +02:00
Strawbs Bot
816cdcf4bf Update translations 2021-08-16 01:01:43 +02:00
Jonas Kvinge
75c94ae092 Always look for qpa/qplatformnativeinterface.h 2021-08-14 20:00:27 +02:00
Jonas Kvinge
ecf2c50a26 Use QX11Application with Qt >= 6.2, use QX11Info with Qt 5 2021-08-14 17:06:40 +02:00
Jonas Kvinge
cecb9293f6 Add QRegularExpression::CaseInsensitiveOption 2021-08-13 23:33:41 +02:00
Jonas Kvinge
1ebfa0ad7e Song::kAlbumRemoveDisc is already QRegularExpression 2021-08-13 23:32:54 +02:00
Jonas Kvinge
35c7b57308 Allow fading when a ALSA PCM device is selected 2021-08-13 22:39:34 +02:00
Strawbs Bot
36bfeffbcc Update translations 2021-08-13 01:03:08 +02:00
Jonas Kvinge
e6858719c9 Reduce network access managers 2021-08-12 23:00:42 +02:00
Strawbs Bot
f2d52f83fe Update translations 2021-08-10 01:02:54 +02:00
Jonas Kvinge
f1d3cadb3b Add better logging for file open and write errors 2021-08-09 23:32:26 +02:00
Jonas Kvinge
7d61d8e646 Replace C-style cast with reinterpret_cast 2021-08-09 23:32:09 +02:00
Jonas Kvinge
6ede400f3a CMakeLists.txt formatting 2021-08-07 03:02:36 +02:00
Strawbs Bot
78de45fee2 Update translations 2021-08-07 01:01:40 +02:00
Jonas Kvinge
4559e33331 Update .github/workflows/ccpp.yml 2021-08-01 15:32:51 +02:00
Jonas Kvinge
43875dd3fe Remove Fedora 35 from CI
It's broken.
2021-08-01 08:41:12 +02:00
Jonas Kvinge
fa10384a92 Add FreeBSD to CI 2021-08-01 08:32:50 +02:00
Strawbs Bot
7e144da6b9 Update translations 2021-07-31 01:02:51 +02:00
Jonas Kvinge
54af17e7bf Remove linker hack 2021-07-30 23:01:44 +02:00
Jonas Kvinge
d4a9f5bb2e Replace Iconv_LIBRARY with Iconv_LIBRARIES 2021-07-30 23:01:07 +02:00
Jonas Kvinge
8040813da8 Remove QUIET from find_package for Backtrace and Iconv 2021-07-30 23:00:34 +02:00
Jonas Kvinge
871e40c5c0 Update Changelog 2021-07-30 21:17:53 +02:00
Jonas Kvinge
7ce922b084 Simplify CMake by using macros 2021-07-30 21:17:50 +02:00
Jonas Kvinge
0c9989695a Subsonic: Use 500 albums per request
Fixes #740
2021-07-30 21:17:44 +02:00
Jonas Kvinge
88d7cb3ed5 Add MD5 token authentication for Subsonic 2021-07-30 21:16:41 +02:00
Strawbs Bot
5b7fc80f26 Update translations 2021-07-29 01:15:44 +02:00
Jonas Kvinge
553e8baa8b Fix async song load
Fixes #739
2021-07-28 22:25:33 +02:00
Jonas Kvinge
62a5031ccf Delay pause on resume playback 2021-07-23 21:54:46 +02:00
Jonas Kvinge
4abac65316 Properly disconnect PlaylistManager::AllPlaylistsLoaded on resume 2021-07-23 21:54:21 +02:00
Jonas Kvinge
c6e42e1032 Reset play offset when switching song while paused
Fixes #735
2021-07-23 21:32:12 +02:00
Jonas Kvinge
a353631892 Code tidy 2021-07-23 21:30:08 +02:00
Jonas Kvinge
f144c982e3 Minor CMakeLists.txt cleanup 2021-07-21 17:45:22 +02:00
Jonas Kvinge
16625b1dc7 Move some files from ext to src 2021-07-21 17:29:16 +02:00
Jonas Kvinge
78ccce7d1a Change USE_BUNDLE default 2021-07-21 17:29:16 +02:00
Strawbs Bot
ffba5f7d31 Update translations 2021-07-19 01:01:56 +02:00
Jonas Kvinge
2cb8fa62df Remove duplicate TAGLIB_LIBRARY_DIRS 2021-07-18 16:38:20 +02:00
Jonas Kvinge
853b936cdd Revert "Add sudo"
This reverts commit 4f0fdbab62.
2021-07-18 15:34:07 +02:00
Jonas Kvinge
4f0fdbab62 Add sudo 2021-07-18 15:33:13 +02:00
Jonas Kvinge
190b23b702 Fix build with macports 2021-07-18 15:23:57 +02:00
Strawbs Bot
e0bb79b2c4 Update translations 2021-07-15 01:03:31 +02:00
Jonas Kvinge
2eab763d74 Check that QIODevice::open() is successful, and explicitly call close() 2021-07-14 20:52:57 +02:00
Jonas Kvinge
f64c1dd9e5 Update Changelog 2021-07-14 13:12:20 +02:00
Jonas Kvinge
fab1d94d34 Delay resume playback on startup to make sure Tidal login is refreshed 2021-07-14 13:02:54 +02:00
Jonas Kvinge
facf49b2b7 Spotify: Decrease login delay on startup 2021-07-14 13:02:12 +02:00
Jonas Kvinge
da86b86776 Rename variable 2021-07-14 12:53:28 +02:00
Jonas Kvinge
a79d9d4e77 Move beginInsertRows() 2021-07-14 12:26:48 +02:00
Jonas Kvinge
d9e378211a Fix sorting of radios 2021-07-14 11:22:24 +02:00
Strawbs Bot
99fbbf70de Update translations 2021-07-14 01:03:06 +02:00
Jonas Kvinge
50a616457d Add missing const 2021-07-13 23:25:44 +02:00
Jonas Kvinge
2ad30ebe88 Add missing Qt::CaseInsensitive 2021-07-13 23:25:36 +02:00
Jonas Kvinge
68dbc29f2c Use QString::compare with Qt::CaseInsensitive to reduce allocations 2021-07-13 23:18:12 +02:00
Strawbs Bot
a87863229f Update translations 2021-07-13 01:07:37 +02:00
Jonas Kvinge
f39ffcb997 Simplify if condition 2021-07-12 13:45:51 +02:00
Jonas Kvinge
f20bb388be Rename variable 2021-07-12 13:27:14 +02:00
Jonas Kvinge
fc45015b13 Remove unused variable 2021-07-12 13:26:41 +02:00
Jonas Kvinge
7a5f047f8e Add parent to RadioMimeData 2021-07-12 13:26:20 +02:00
Jonas Kvinge
4251bee3ca Change some uses of QMap to QHash 2021-07-12 08:28:52 +02:00
Jonas Kvinge
f02741284c Tagparser: Fix reading and writing year/originalyear 2021-07-12 07:54:01 +02:00
Jonas Kvinge
fec7419fcc Make const 2021-07-12 07:38:49 +02:00
Jonas Kvinge
e8694531f6 Iterate QMap values 2021-07-12 07:34:20 +02:00
Jonas Kvinge
461491f742 Use multiarg 2021-07-12 07:33:40 +02:00
Jonas Kvinge
af29acf462 tagparser: Save release and record date 2021-07-12 07:32:41 +02:00
Jonas Kvinge
b47f29e87c tagparser: Parse tracks when saving 2021-07-12 07:32:17 +02:00
Jonas Kvinge
fc8ec6d7fa Replace INSTANTIATE_TEST_CASE_P with INSTANTIATE_TEST_SUITE_P 2021-07-12 05:51:01 +02:00
Jonas Kvinge
1d4c736cfa Initialize container to nullptr 2021-07-12 05:51:01 +02:00
Strawbs Bot
600450082a Update translations 2021-07-12 01:18:19 +02:00
Jonas Kvinge
c6da0864f2 Use std::make_shared 2021-07-11 19:57:18 +02:00
Jonas Kvinge
10fc6b4562 Fix tagreader test compile error with tagparser 2021-07-11 19:56:21 +02:00
Jonas Kvinge
e48b7d83a3 Formatting 2021-07-11 09:49:38 +02:00
Jonas Kvinge
a6742d401c Formatting 2021-07-11 07:40:57 +02:00
Jonas Kvinge
87b9a8c4c8 Use RadioPlaylistItem 2021-07-11 07:39:27 +02:00
Jonas Kvinge
134dc55891 Rename RadioView::SetModel 2021-07-11 05:53:10 +02:00
Jonas Kvinge
295cf98e70 Add missing override 2021-07-11 05:30:35 +02:00
Jonas Kvinge
7333155b8c Edit tab order in scrobbler settings 2021-07-11 05:23:37 +02:00
Jonas Kvinge
3960c7d8e6 Add SomaFM and Radio Paradise to scrobbler sources 2021-07-11 05:18:56 +02:00
Jonas Kvinge
b053f99690 Add Song::is_radio() 2021-07-11 05:18:39 +02:00
Jonas Kvinge
5700c3f72e Change GStreamer text 2021-07-11 05:17:45 +02:00
Jonas Kvinge
cdb3729a88 Use Song::SourceFromURL in SongLoader::AddAsRawStream 2021-07-11 05:17:17 +02:00
Jonas Kvinge
c33f2a1d27 Unref bus in SongLoader::LoadRemote() 2021-07-11 04:58:37 +02:00
Jonas Kvinge
9b9a32b053 tagparser: Fix opening file readonly 2021-07-11 04:36:44 +02:00
Jonas Kvinge
95eec369b5 Fix includes 2021-07-11 02:31:15 +02:00
Jonas Kvinge
5d5860683b Add missing includes 2021-07-11 02:29:24 +02:00
Jonas Kvinge
434b31b932 Formatting 2021-07-11 02:27:26 +02:00
Jonas Kvinge
f69e42e520 Use Q_ASSERT 2021-07-11 02:27:03 +02:00
Jonas Kvinge
c5cadfe0c6 Fix memory leak in InternetSearchView::ResultsContextMenuEvent 2021-07-11 01:52:21 +02:00
Jonas Kvinge
f21be30004 Use static QPixmapCache everywhere 2021-07-11 01:43:52 +02:00
Jonas Kvinge
b8b21d53e1 Use static QPixmapCache 2021-07-11 01:32:51 +02:00
Jonas Kvinge
09ec39c87a Remove unused functions from RadioService 2021-07-11 01:20:42 +02:00
Jonas Kvinge
dbf18db3a3 No need to pass Application to RadioBackend 2021-07-11 01:12:50 +02:00
Jonas Kvinge
d8f0ae0980 Remove unused variable 2021-07-11 01:11:25 +02:00
Jonas Kvinge
09bbf1f4d7 Add radios 2021-07-11 01:08:06 +02:00
Strawbs Bot
d07aff9872 Update translations 2021-07-11 01:02:12 +02:00
Jonas Kvinge
4cb3f9d177 Close files in song loader 2021-07-11 00:45:30 +02:00
Jonas Kvinge
c1a815778b Formatting 2021-07-10 21:52:21 +02:00
Jonas Kvinge
5b003b09ac Make CollectionBackendInterface to PlaylistParser optional 2021-07-10 21:52:01 +02:00
Jonas Kvinge
22d0697c77 Use Song::is_stream 2021-07-10 21:50:23 +02:00
Jonas Kvinge
432b0f3e54 songloader: Make sure timeout timer is started from correct thread 2021-07-10 21:48:31 +02:00
Jonas Kvinge
2bee41e90e Fix compile of Utilities::Hmac with Qt 6.3 2021-07-07 23:54:21 +02:00
Jonas Kvinge
2a7312f2b4 Fix tagreader test 2021-07-07 23:46:21 +02:00
Strawbs Bot
f5091339ad Update translations 2021-07-06 01:02:54 +02:00
Jonas Kvinge
3d06d68196 Fix broken context albums 2021-07-04 17:34:42 +02:00
Strawbs Bot
1a643bfa8c Update translations 2021-07-03 01:20:53 +02:00
Jonas Kvinge
cd82b0a669 Fix spelling and typos 2021-07-02 18:45:53 +02:00
Jonas Kvinge
57312e29e2 Build with tagparser on openSUSE 2021-07-02 11:25:02 +02:00
Jonas Kvinge
592df4819b Update Changelog 2021-07-02 02:26:34 +02:00
Jonas Kvinge
f36997c7c5 Close MediaFileInfo 2021-07-02 01:39:21 +02:00
Jonas Kvinge
b1640d3626 Remove tagparser/abstractattachment.h include 2021-07-02 01:38:56 +02:00
Jonas Kvinge
825c62c62b Fix typo in TagReaderTagLib constructor and destructor 2021-07-02 01:26:48 +02:00
Jonas Kvinge
a4b0c6f37d Remove unused QVector include 2021-07-02 01:26:32 +02:00
Jonas Kvinge
239b88aa3b Add support for TagParser as an alternative to TagLib 2021-07-02 01:16:46 +02:00
Jonas Kvinge
88819611f4 Remove TagLib dependency from Song class 2021-07-02 01:16:46 +02:00
Jonas Kvinge
e6ff8368a9 Fix filefilter 2021-07-02 01:16:46 +02:00
Jonas Kvinge
8315f572ea Remove default parameter 2021-07-02 01:16:46 +02:00
Strawbs Bot
89e2070419 Update translations 2021-07-02 01:04:47 +02:00
Jonas Kvinge
b5f4df0912 Refactor subsonic, tidal and qobuz code 2021-07-01 02:02:02 +02:00
Jonas Kvinge
b9f3f80d50 Use std::make_unique / std::make_shared 2021-07-01 02:02:02 +02:00
Jonas Kvinge
0e8ae1a206 Change to std::shared_ptr 2021-07-01 02:02:02 +02:00
Jonas Kvinge
32729174bb Make CollectionModelTest constructor public 2021-07-01 02:02:02 +02:00
Jonas Kvinge
2cf6fe8da7 Fix ConcurrentRunTest 2021-07-01 02:02:02 +02:00
Strawbs Bot
929b031f09 Update translations 2021-07-01 01:03:41 +02:00
Jonas Kvinge
57a36491ee Remove mpris.cpp and mpris.h 2021-06-30 16:26:07 +02:00
Jonas Kvinge
f3a8dde5f0 Use std::make_shared 2021-06-30 16:15:55 +02:00
Jonas Kvinge
ffa2489998 Formatting 2021-06-30 16:15:55 +02:00
Strawbs Bot
6cd7bcdae6 Update translations 2021-06-28 01:07:32 +02:00
Jonas Kvinge
e4f684f411 Change const_iterator to iterator 2021-06-28 00:27:28 +02:00
Jonas Kvinge
fdc9a71f9e Use protected kApiUrl 2021-06-28 00:21:50 +02:00
Jonas Kvinge
bb134ee7ac Rename NeedLogin to set_need_login 2021-06-28 00:21:50 +02:00
Jonas Kvinge
18dc6f3c88 Remove unused variables in InternetCollectionViewContainer 2021-06-28 00:21:50 +02:00
Jonas Kvinge
9327fd3aa1 Formatting 2021-06-28 00:21:50 +02:00
Jonas Kvinge
aa859b9002 Remove dead code 2021-06-28 00:21:50 +02:00
Jonas Kvinge
20a15ecd35 Replace while with for loop 2021-06-28 00:21:50 +02:00
Jonas Kvinge
062c59b321 Rename FindSongs 2021-06-28 00:21:50 +02:00
Jonas Kvinge
9ace66edb7 Rename CollectionFilterWidget::SetCollectionModel to CollectionFilterWidget::Init 2021-06-28 00:21:50 +02:00
Jonas Kvinge
a97d784f26 Rename filter to search_field 2021-06-28 00:21:50 +02:00
Jonas Kvinge
a2e7173983 Rename filter to filter_widget 2021-06-28 00:21:50 +02:00
Jonas Kvinge
0869651dc3 Change parameter order of CollectionBackend::Init 2021-06-28 00:21:50 +02:00
Strawbs Bot
ca42286354 Update translations 2021-06-26 01:07:31 +02:00
Jonas Kvinge
174bfcc597 Use higher resolution images from last.fm API 2021-06-25 22:41:17 +02:00
Jonas Kvinge
244d25ce53 Fix compile with Qt 5 2021-06-25 18:46:45 +02:00
Jonas Kvinge
8287efd51f Remove silencing -Wclazy-qt6-deprecated-api-fixes to workaround clazy bugs 2021-06-25 18:19:37 +02:00
Jonas Kvinge
df31b5d59f Replace Q_ENUMS with Q_ENUM 2021-06-25 18:06:30 +02:00
Jonas Kvinge
53cc47a8b1 Use localhost directly as redirect for scrobbling API 2.0 2021-06-25 16:04:19 +02:00
Jonas Kvinge
55f8294a38 Remove escape characters from playlist filter string
Fixes #728
2021-06-25 12:01:05 +02:00
Strawbs Bot
67a5e3f37e Update translations 2021-06-23 01:16:19 +02:00
Jonas Kvinge
06cd2f3a57 Remove static from IsMacAccessibilityEnabled 2021-06-22 16:17:51 +02:00
Jonas Kvinge
d614a94203 Remove const from SingleApplication methods 2021-06-22 16:15:04 +02:00
Jonas Kvinge
f66aca2164 Remove static from OSDPretty::current_screen 2021-06-22 14:17:24 +02:00
Jonas Kvinge
b071ecb45e Formatting 2021-06-22 14:04:42 +02:00
Jonas Kvinge
83a70fecca Use QWidget::tr() 2021-06-22 14:04:42 +02:00
Jonas Kvinge
dbc7c224c1 Fix setting PID 2021-06-22 14:04:42 +02:00
Jonas Kvinge
7876f9a8a5 Remove unneeded QFont in parameter initialization list 2021-06-22 14:04:42 +02:00
Jonas Kvinge
215057ce6f Remove unneeded get() 2021-06-22 14:04:42 +02:00
Jonas Kvinge
6d8b0b3ab6 Simplify return 2021-06-22 14:04:42 +02:00
Jonas Kvinge
e5c85ddd32 Pass task ID to SetTaskBlocksCollectionScans 2021-06-22 14:04:42 +02:00
Jonas Kvinge
6bf3c34fe5 Fix parameter name mismatch 2021-06-22 14:04:42 +02:00
Jonas Kvinge
584f5e5935 Change bool/int condition 2021-06-22 14:04:42 +02:00
Jonas Kvinge
58a5367015 Make const 2021-06-22 14:04:42 +02:00
Jonas Kvinge
8c2b907ff5 Make static 2021-06-22 14:04:42 +02:00
Strawbs Bot
e3ab0c0192 Update translations 2021-06-22 01:05:22 +02:00
Jonas Kvinge
8572e3eabb Fix volume slider regression 2021-06-21 20:21:40 +02:00
Jonas Kvinge
d4f10c61ef Change lambda capture 2021-06-21 20:10:02 +02:00
Jonas Kvinge
59e11d6caa Remove reference 2021-06-21 20:00:31 +02:00
Jonas Kvinge
67778198de Formatting 2021-06-21 19:55:47 +02:00
Jonas Kvinge
ebfd8cd6f7 Initialize variables and formatting 2021-06-21 19:55:18 +02:00
Jonas Kvinge
83b54f2ae6 Formatting 2021-06-21 19:54:42 +02:00
Jonas Kvinge
7ea60ed178 Update copyrights 2021-06-21 19:54:12 +02:00
Jonas Kvinge
c61d1ce6b4 Use std::any_of 2021-06-21 19:52:37 +02:00
Jonas Kvinge
6a7959547e Change CoverProvider quality return type to float 2021-06-21 18:16:04 +02:00
Jonas Kvinge
af3834672e Remove unneeded break 2021-06-21 18:14:59 +02:00
Jonas Kvinge
f95ff6cb89 Formatting 2021-06-21 16:08:53 +02:00
Jonas Kvinge
e85c13b3fe Change switch to if statement 2021-06-21 15:44:13 +02:00
Jonas Kvinge
ed09627fdb Use std::make_unique 2021-06-21 15:40:44 +02:00
Jonas Kvinge
99ba2c2e8b Make playlist parameter to queue not optional 2021-06-21 15:40:44 +02:00
Jonas Kvinge
589bdf5dcd Use default 2021-06-21 15:40:44 +02:00
Jonas Kvinge
1abbd5ecbc Pass parent to queue 2021-06-21 15:40:44 +02:00
Strawbs Bot
7619e8427a Update translations 2021-06-21 01:17:51 +02:00
Jonas Kvinge
069e24d713 Fix compile 2021-06-21 00:29:38 +02:00
Jonas Kvinge
8e9b45f80f Add override 2021-06-20 23:57:19 +02:00
Jonas Kvinge
2d604a80c9 Change 0 to nullptr 2021-06-20 23:56:56 +02:00
Jonas Kvinge
1f59a1b952 unsigned long int 2021-06-20 23:56:33 +02:00
Jonas Kvinge
6e5ff01db5 Remove unused variables 2021-06-20 23:55:59 +02:00
Jonas Kvinge
50be44adf8 Add namespace comments 2021-06-20 23:55:02 +02:00
Jonas Kvinge
e1bf4347ab Fix uninitialized variables 2021-06-20 23:53:28 +02:00
Jonas Kvinge
3a3305c020 Uppercase literal suffix 2021-06-20 23:49:04 +02:00
Jonas Kvinge
a83f174e1a Remove unused settingsdialog variable 2021-06-20 19:47:38 +02:00
Jonas Kvinge
1295033fae Clang-Tidy and Clazy fixes 2021-06-20 19:04:08 +02:00
Jonas Kvinge
755abec636 Use QProcess::startCommand with Qt 6 2021-06-20 02:48:58 +02:00
Jonas Kvinge
310995fb87 Use static QFileInfo::exists 2021-06-20 02:26:30 +02:00
Jonas Kvinge
bee30e572f Fix incorrect use of return 2021-06-20 02:25:36 +02:00
Jonas Kvinge
cd37a40bab Only link to chromaprint when song fingerprinting or musicbrainz is enabled 2021-06-16 22:54:23 +02:00
Jonas Kvinge
8699790e78 Always call QFutureWatcher::setFuture after connects 2021-06-16 00:30:21 +02:00
Strawbs Bot
d2d3f58a14 Update translations 2021-06-15 01:03:14 +02:00
Jonas Kvinge
081df59ed7 Only call QSystemTrayIcon::isSystemTrayAvailable once
Workaround file descriptor leak

Fixes #724
2021-06-15 00:25:54 +02:00
Strawbs Bot
ec3bcdcb26 Update translations 2021-06-14 01:02:38 +02:00
Jonas Kvinge
8265cf8a6a Use selected plug devices when loading automatic/custom device 2021-06-14 00:18:14 +02:00
Jonas Kvinge
dfb53fb3dd Change regex for CARD and DEV in ALSA devices 2021-06-13 23:58:47 +02:00
Jonas Kvinge
705fc920e5 Fix 2021-06-13 23:44:15 +02:00
Jonas Kvinge
99764741e4 Fix detecting ALSA PCM devices 2021-06-13 23:42:08 +02:00
Jonas Kvinge
35b3bc4522 Handle some chromaprinter error cases 2021-06-13 23:18:22 +02:00
Jonas Kvinge
91eee99bab Increase pen size for OSD Pretty border
Workaround for QTBUG-93476

Fixes #708
2021-06-13 20:56:14 +02:00
Jonas Kvinge
e56defdc50 Formatting 2021-06-13 20:55:37 +02:00
Jonas Kvinge
fbe4d3ce9f Add Subsonic option to turn off HTTP2
Fixes #718
2021-06-13 19:55:45 +02:00
Strawbs Bot
8cf5707575 Update translations 2021-06-13 01:16:37 +02:00
Jonas Kvinge
f786a17014 Formatting 2021-06-12 20:53:23 +02:00
Jonas Kvinge
427b9c1ebc Formatting 2021-06-12 19:22:38 +02:00
Jonas Kvinge
141957e4b5 Remove godaddy certificate 2021-06-12 19:14:47 +02:00
Jonas Kvinge
b911f4f34e Formatting 2021-06-12 16:06:41 +02:00
Jonas Kvinge
0caf76dfae Remove unneeded includes 2021-06-12 16:06:41 +02:00
Jonas Kvinge
00f09168d7 Update Changelog 2021-06-12 16:06:41 +02:00
Strawbs Bot
d636359c10 Update translations 2021-06-11 01:16:45 +02:00
Jonas Kvinge
8fd32aba4f Improve resume playback on startup, re-request stream URL when unpausing
Fixes #270
2021-06-10 23:14:05 +02:00
Strawbs Bot
9db59d5deb Update translations 2021-06-09 01:03:30 +02:00
Jonas Kvinge
d98321c703 Use newer compiler with Qt 6 on openSUSE Leap 2021-06-08 23:43:14 +02:00
Jonas Kvinge
a6424c005f Add Gui to QT_COMPONENTS 2021-06-08 22:49:34 +02:00
Jonas Kvinge
80d127c277 Use fingerprints from database if available when fetching tags from MusicBrainz 2021-06-08 20:59:56 +02:00
Strawbs Bot
3ca0c828c6 Update translations 2021-06-07 01:01:41 +02:00
Strawbs Bot
d3a8e03b2c Add Portuguese (Brazil) 2021-06-06 01:47:57 +02:00
Strawbs Bot
6e75de0dcb Add Chinese 2021-06-06 01:43:49 +02:00
Strawbs Bot
a7bed9741a Add Catalan 2021-06-06 01:42:38 +02:00
Strawbs Bot
f70e52a7a5 Add Japanese 2021-06-06 01:38:22 +02:00
Strawbs Bot
95d7de0654 Add Dutch 2021-06-06 01:36:50 +02:00
Strawbs Bot
3398d25b35 Add Ukrainian 2021-06-06 01:35:40 +02:00
Strawbs Bot
80a4a43680 Add Finnish 2021-06-06 01:34:54 +02:00
Strawbs Bot
01ac61f38c Update translations 2021-06-06 01:23:21 +02:00
Jonas Kvinge
a98be36684 Set DEFAULT '' for all TEXT 2021-06-06 00:36:35 +02:00
Jonas Kvinge
7f085c4012 Check fingerprint for NULL 2021-06-06 00:26:11 +02:00
Jonas Kvinge
5f008d1560 Edit collection settings tab order 2021-06-05 22:34:32 +02:00
Jonas Kvinge
0782b579d5 Update README and Changelog 2021-06-05 22:30:15 +02:00
Jonas Kvinge
f8ed2afef1 Add song fingerprinting and tracking
Fixes #296
2021-06-05 21:56:40 +02:00
Jonas Kvinge
a883508eca Fix marking CUE songs available 2021-06-05 02:50:20 +02:00
Jonas Kvinge
a4a20ec220 Make sure schedule playlist save timer is started from correct thread 2021-06-05 00:07:15 +02:00
Jonas Kvinge
faed63712f Remove Fedora 32 and CentOS 8 from CI 2021-06-04 00:25:11 +02:00
Jonas Kvinge
0032ec5036 Don't log access tokens 2021-06-04 00:15:35 +02:00
Strawbs Bot
9df24a841f Update translations 2021-06-02 01:01:56 +02:00
Jonas Kvinge
d2aa4ff2bd Remove duplicate line 2021-06-01 23:16:04 +02:00
Jonas Kvinge
171db01401 Set default for original year 2021-05-31 19:07:55 +02:00
Strawbs Bot
0112f1e11d Update translations 2021-05-31 01:01:58 +02:00
Jonas Kvinge
8f7b09b58e Update Changelog 2021-05-30 20:57:54 +02:00
Strawbs Bot
479c2b6cec Update translations 2021-05-30 01:25:46 +02:00
Jonas Kvinge
e6f50e6637 Strip mimetype when downloading covers 2021-05-29 23:27:40 +02:00
Jonas Kvinge
548f8bf23e Fix macOS compile 2021-05-29 22:47:53 +02:00
Jonas Kvinge
b6bfe81f9a Use HAVE_X11_GLOBALSHORTCUTS 2021-05-29 22:20:46 +02:00
Jonas Kvinge
ab95bcc028 Change text PulseAudio 2021-05-29 21:52:52 +02:00
Jonas Kvinge
8b7b3df0a3 Fix Qt 5 compile 2021-05-29 20:54:01 +02:00
Jonas Kvinge
69a6416146 Change to uppercase 2021-05-29 20:37:58 +02:00
Jonas Kvinge
1ced4e277b Add global shortcuts support on MATE 2021-05-29 20:35:55 +02:00
Jonas Kvinge
0c5236ebcb Fix crash when no system tray is available 2021-05-29 18:52:47 +02:00
Jonas Kvinge
66c55c5007 Remove remaining HAVE_X11EXTRAS 2021-05-29 18:36:58 +02:00
Jonas Kvinge
7f5a0f1b0c Update RPM spec, man page and debian control 2021-05-29 00:57:25 +02:00
Jonas Kvinge
0c766938f1 Update appdata 2021-05-29 00:49:27 +02:00
Jonas Kvinge
1455e87aef Update appdata 2021-05-29 00:48:23 +02:00
Jonas Kvinge
7c50eef8ad Add Fedora 35 2021-05-28 23:39:30 +02:00
Jonas Kvinge
aa9f972ccf Update Changelog 2021-05-26 17:05:07 +02:00
Strawbs Bot
45df99bb56 Update translations 2021-05-26 13:00:30 +02:00
Strawbs Bot
a2968e57cf Update translations 2021-05-21 01:15:12 +02:00
Jonas Kvinge
67f831beba Refactor systemtrayicon code 2021-05-20 21:40:08 +02:00
Jonas Kvinge
264d47caf4 Change playlist tabbar favorite tooltip 2021-05-20 17:12:28 +02:00
Jonas Kvinge
ef72b3273e Move private Qt GUI include 2021-05-20 17:10:08 +02:00
jonas@jkvinge.net
7d3ade7ae1 Automatically detect Qt version 2021-05-17 00:56:04 +02:00
Strawbs Bot
59897fdf55 Update translations 2021-05-14 01:02:10 +02:00
jonas@jkvinge.net
0b4ea04c91 Update protobuf dll in nsi 2021-05-13 21:06:43 +02:00
jonas@jkvinge.net
094b3b4636 Update Dmg.cmake: Move depends for macdeployqt 2021-05-13 12:18:06 +02:00
jonas@jkvinge.net
0298beeb06 Fix macOS build 2021-05-13 02:17:58 +02:00
Strawbs Bot
971542d6bc Update translations 2021-05-13 01:26:18 +02:00
jonas@jkvinge.net
5bdbc9f40d Add CMake test for Qt sqlite support 2021-05-13 00:18:06 +02:00
jonas@jkvinge.net
efcd35d4a1 Remove use of X11Extras and WinExtras
Modules are deprecated in Qt 6
See: QTBUG-83251
2021-05-12 23:15:04 +02:00
Strawbs Bot
a01541d7ca Update translations 2021-05-12 01:05:21 +02:00
jonas@jkvinge.net
7d96d7c066 Update README 2021-05-11 22:06:09 +02:00
jonas@jkvinge.net
639ea35921 Use wchar version of GetDiskFreeSpaceEx on Windows Qt 5 2021-05-11 20:32:28 +02:00
jonas@jkvinge.net
dee1905e16 Make the default tabbbar background color lighter
Fixes #711
2021-05-11 20:10:31 +02:00
jonas@jkvinge.net
1803705749 Add option to turn off playlist alternating row colors 2021-05-11 19:40:47 +02:00
jonas@jkvinge.net
5d96ee5492 Add ALSA PCM devices and option to set channels
Fixes #262
2021-05-11 19:14:00 +02:00
jonas@jkvinge.net
4a0a1a32a4 Rename ENABLE_CHROMAPRINT to ENABLE_MUSICBRAINZ 2021-05-10 21:17:50 +02:00
Strawbs Bot
c2108a35d0 Update translations 2021-05-09 01:01:51 +02:00
jonas@jkvinge.net
31ecdbae18 Update protobuf dll in nsi 2021-05-08 01:56:42 +02:00
jonas@jkvinge.net
e9c733e78f Turn off -Werror for Windows build in CI 2021-05-08 01:56:42 +02:00
Strawbs Bot
2320c5e04c Update translations 2021-05-08 01:01:55 +02:00
Jonas Kvinge
fb7b3b0295 Revert "Shuffle all songs instead of from the current to the end"
This reverts commit 316a3d51ee.
2021-05-04 23:16:18 +02:00
Jonas Kvinge
316a3d51ee Shuffle all songs instead of from the current to the end
Fixes #707
2021-05-04 22:48:18 +02:00
Jonas Kvinge
240dcf2e5c Improve device icon loading 2021-05-04 21:55:19 +02:00
Strawbs Bot
5fbdbffa23 Update translations 2021-05-04 01:04:38 +02:00
Jonas Kvinge
932c8b65fb Add Ubuntu Hirsute to CI 2021-05-03 23:33:59 +02:00
Strawbs Bot
e18e174947 Update translations 2021-05-02 01:02:16 +02:00
Jonas Kvinge
f2c7df3a3b Build on macOS with libgpod 2021-05-01 22:12:13 +02:00
Jonas Kvinge
2b15dae806 Strip misc from song title when sending now playing 2021-05-01 18:19:08 +02:00
Jonas Kvinge
2625d72818 Don't strip off "Live" from song title 2021-05-01 18:03:26 +02:00
Strawbs Bot
a8b4d72421 Update translations 2021-04-27 01:06:58 +02:00
Jonas Kvinge
88562890ae Use int in MainWindow::UpdateTrackSliderPosition 2021-04-26 23:36:59 +02:00
Jonas Kvinge
86c267eb89 Seconds is already qint64 2021-04-26 23:13:37 +02:00
Jonas Kvinge
3d1928cca9 Fix typo 2021-04-26 23:12:59 +02:00
Jonas Kvinge
8c64d3b55c Do most item reloading in the background, schedule playlist saving 2021-04-26 22:57:08 +02:00
Jonas Kvinge
41b238e87b Formatting 2021-04-26 22:56:14 +02:00
Jonas Kvinge
da83025fb0 Update temporary metadata when reloading song
Possible fix for #697
2021-04-26 20:33:57 +02:00
Jonas Kvinge
84d3b190c8 Update Changelog 2021-04-25 21:20:43 +02:00
Strawbs Bot
272fc5a72d Update translations 2021-04-25 01:04:39 +02:00
Strawbs Bot
0f709059e3 Update translations 2021-04-24 01:02:19 +02:00
Jonas Kvinge
0f91bb28a9 Remove CircleCI 2021-04-23 21:09:11 +02:00
Jonas Kvinge
63d75a8e17 Build common DMG for all macOS versions 2021-04-23 21:08:23 +02:00
Strawbs Bot
be95d8409b Update translations 2021-04-23 01:02:57 +02:00
Jonas Kvinge
9d3000498b Add setting for setting reply gain fallback gain 2021-04-22 21:55:26 +02:00
Jonas Kvinge
48f8468e65 Change direction to out for signals 2021-04-22 21:54:45 +02:00
Jonas Kvinge
9b60559855 Use QFile::encodeName() in Song::InitFromFilePartial
Fixes #693
2021-04-21 20:11:37 +02:00
Jonas Kvinge
e97acf7938 Fix errors in D-Bus XMLs 2021-04-21 16:05:23 +02:00
Jonas Kvinge
1cfb92e75c Remove quotation mark from org.gnome.SettingsDaemon.MediaKeys.xml 2021-04-21 16:00:34 +02:00
Strawbs Bot
4c617ccd49 Update translations 2021-04-19 01:01:46 +02:00
Jonas Kvinge
494517e767 Turn back git revision 2021-04-18 22:07:43 +02:00
747 changed files with 67302 additions and 19669 deletions

View File

@@ -1,417 +0,0 @@
version: 2.1
commands:
cmake:
description: Configure build
steps:
- run:
name: Configure build
command: cmake ..
working_directory: build
build_source:
description: Create source tarball
steps:
- run:
name: Create source tarball
command: ../dist/scripts/maketarball.sh
working_directory: build
build_rpm:
description: Build RPM
steps:
- run:
name: Create RPM build sources directories
command: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- run:
name: Copy source tarball 1
command: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
working_directory: build
- run:
name: Copy source tarball 2
command: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
working_directory: build
- run:
name: Build RPM
command: rpmbuild -ba ../dist/unix/strawberry.spec
working_directory: build
build_deb:
description: Build Deb
steps:
- run:
name: make deb
command: dpkg-buildpackage -b -d -uc -us -nc -j2
install_opensuse_dependencies:
description: Install openSUSE dependencies
steps:
- run:
name: Update packages
command: zypper --non-interactive --gpg-auto-import-keys ref
- run:
name: Install openSUSE dependencies
command: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
taglib-devel
vlc-devel
libQt5Core-devel
libQt5Gui-devel
libQt5Widgets-devel
libQt5Concurrent-devel
libQt5Network-devel
libQt5Sql-devel
libQt5DBus-devel
libQt5Test-devel
libqt5-qtx11extras-devel
libqt5-qtbase-common-devel
libQt5Sql5-sqlite
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
install_fedora_dependencies:
description: Install Fedora dependencies
steps:
- run:
name: Update packages
command: yum update --assumeyes
- run:
name: Upgrade packages
command: yum upgrade --assumeyes
- run:
name: Install Fedora dependencies
command: >
dnf install --assumeyes
@development-tools
redhat-lsb-core
git
glibc
gcc-c++
rpmdevtools
make
cmake
pkgconfig
glib
man
tar
gettext
openssh
boost-devel
dbus-devel
protobuf-devel
protobuf-compiler
sqlite-devel
alsa-lib-devel
pulseaudio-libs-devel
libnotify-devel
gnutls-devel
qt5-qtbase-devel
qt5-qtx11extras-devel
qt5-qttools-devel
gstreamer1-devel
gstreamer1-plugins-base-devel
taglib-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
fftw-devel
desktop-file-utils
libappstream-glib
hicolor-icon-theme
install_debian_dependencies:
description: Install Debian dependencies
steps:
- run:
name: Install Debian dependencies
command: >
apt-get update && apt-get install -y
build-essential
dh-make
ssh
git
make
cmake
gcc
pkg-config
fakeroot
gettext
lsb-release
libglib2.0-dev
dpkg-dev
libdbus-1-dev
libboost-dev
libprotobuf-dev
protobuf-compiler
libsqlite3-dev
libgnutls28-dev
libasound2-dev
libpulse-dev
libtag1-dev
qtbase5-dev
qtbase5-dev-tools
qtbase5-private-dev
libqt5x11extras5-dev
qttools5-dev
libgstreamer1.0-dev
libgstreamer-plugins-base1.0-dev
gstreamer1.0-alsa
gstreamer1.0-pulseaudio
libchromaprint-dev
libfftw3-dev
libcdio-dev
libmtp-dev
libgpod-dev
install_ubuntu_dependencies:
description: Install Ubuntu dependencies
steps:
- run:
name: Setup environment
command: |
echo 'export DEBIAN_FRONTEND=noninteractive' >> $BASH_ENV
source $BASH_ENV
- run:
name: Install Ubuntu dependencies
command: >
apt-get update && apt-get install -y
build-essential
dh-make
ssh
git
make
cmake
pkg-config
gcc
fakeroot
wget
curl
gettext
lsb-release
dpkg-dev
libglib2.0-dev
libboost-dev
libdbus-1-dev
libprotobuf-dev
protobuf-compiler
libsqlite3-dev
libgnutls28-dev
libasound2-dev
libpulse-dev
libtag1-dev
qtbase5-dev
qtbase5-dev-tools
qtbase5-private-dev
libqt5x11extras5-dev
qttools5-dev
libgstreamer1.0-dev
libgstreamer-plugins-base1.0-dev
libgstreamer-plugins-good1.0-dev
gstreamer1.0-alsa
gstreamer1.0-pulseaudio
libchromaprint-dev
libfftw3-dev
libcdio-dev
libmtp-dev
libgpod-dev
jobs:
build_source:
docker:
- image: opensuse/leap:15.2
steps:
- install_opensuse_dependencies
- checkout
- cmake
- build_source
build_opensuse_lp151:
docker:
- image: opensuse/leap:15.1
environment:
RPM_BUILD_NCPUS: "2"
steps:
- install_opensuse_dependencies
- checkout
- cmake
- build_source
- build_rpm
build_opensuse_lp152:
docker:
- image: opensuse/leap:15.2
environment:
RPM_BUILD_NCPUS: "2"
steps:
- install_opensuse_dependencies
- checkout
- cmake
- build_source
- build_rpm
build_fedora_32:
docker:
- image: fedora:32
environment:
RPM_BUILD_NCPUS: "2"
steps:
- install_fedora_dependencies
- checkout
- cmake
- build_source
- build_rpm
build_fedora_33:
docker:
- image: fedora:33
environment:
RPM_BUILD_NCPUS: "2"
steps:
- install_fedora_dependencies
- checkout
- cmake
- build_source
- build_rpm
build_debian_buster:
docker:
- image: debian:buster
steps:
- install_debian_dependencies
- checkout
- cmake
- build_deb
build_debian_bullseye:
docker:
- image: debian:bullseye
steps:
- install_debian_dependencies
- checkout
- cmake
- build_deb
build_ubuntu_bionic:
docker:
- image: ubuntu:bionic
steps:
- install_ubuntu_dependencies
- checkout
- cmake
- build_deb
build_ubuntu_focal:
docker:
- image: ubuntu:focal
steps:
- install_ubuntu_dependencies
- checkout
- cmake
- build_deb
build_ubuntu_groovy:
docker:
- image: ubuntu:groovy
steps:
- install_ubuntu_dependencies
- checkout
- cmake
- build_deb
workflows:
version: 2
build_all:
jobs:
- build_source:
filters:
tags:
only: /.*/
- build_opensuse_lp151:
filters:
tags:
only: /.*/
- build_opensuse_lp152:
filters:
tags:
only: /.*/
- build_fedora_32:
filters:
tags:
only: /.*/
- build_fedora_33:
filters:
tags:
only: /.*/
- build_debian_buster:
filters:
tags:
only: /.*/
- build_debian_bullseye:
filters:
tags:
only: /.*/
- build_ubuntu_bionic:
filters:
tags:
only: /.*/
- build_ubuntu_focal:
filters:
tags:
only: /.*/
- build_ubuntu_groovy:
filters:
tags:
only: /.*/

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +0,0 @@
sudo: required
language: C++
os: osx
osx_image: xcode11.3
compiler: clang
before_install:
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d ;
chmod 600 ~/.ssh/id_rsa ;
fi
- git fetch --unshallow
- git pull
- brew update
- travis_wait 400 brew upgrade || echo "Failed"
- travis_wait 400 brew upgrade || echo "Failed"
- brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib
- brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
- brew install libcdio libmtp
- brew install create-dmg
- brew install --cask sparkle
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1) /usr/local/opt/sparkle
- export Qt6_DIR=/usr/local/opt/qt6/lib/cmake
- export Qt6LinguistTools_DIR=/usr/local/opt/qt6/lib/cmake/Qt6LinguistTools
- ls /usr/local/lib/gstreamer-1.0
before_script:
- mkdir build
- cd build
- cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_WITH_QT6=ON -DBUILD_WERROR=ON -DUSE_BUNDLE=ON
script:
- make -j8
- make install
- make dmg2
after_success:
- ls -lh strawberry*.dmg
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "travis" ]]; then
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
fi
fi
branches:
except:
- # Do not build tags that we create when we upload to GitHub Releases
- /^(?i:continuous)$/

View File

@@ -64,7 +64,6 @@ int main(int argc, char **argv)
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
qDebug() << " -libpath=<path> : Add the given path to the library search path";
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
qDebug() << " -plugins-dir=<path> : Set plugins directory";
qDebug() << "";
qDebug() << "macdeployqt takes an application bundle as input and makes it";
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
@@ -110,7 +109,6 @@ int main(int argc, char **argv)
extern bool appstoreCompliant;
extern bool deployFramework;
extern bool secureTimestamp;
QString plugin_dir;
for (int i = 2; i < argc; ++i) {
QByteArray argument = QByteArray(argv[i]);
@@ -198,7 +196,7 @@ int main(int argc, char **argv)
LogDebug() << "Argument found:" << argument;
appstoreCompliant = true;
// Undocumented option, may not work as intented
// Undocumented option, may not work as intended
} else if (argument == QByteArray("-deploy-framework")) {
LogDebug() << "Argument found:" << argument;
deployFramework = true;
@@ -210,13 +208,6 @@ int main(int argc, char **argv)
LogError() << "Missing filesystem type";
else
filesystem = argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-plugins-dir"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing filesystem type";
else
plugin_dir = argument.mid(index+1);
} else if (argument.startsWith("-")) {
LogError() << "Unknown argument" << argument << "\n";
return 1;
@@ -231,7 +222,7 @@ int main(int argc, char **argv)
if (deployFramework && deploymentInfo.isFramework)
fixupFramework(appBundlePath);
// Convenience: Look for .qml files in the current directoty if no -qmldir specified.
// Convenience: Look for .qml files in the current directory if no -qmldir specified.
if (qmlDirs.isEmpty()) {
QDir dir;
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
@@ -252,40 +243,37 @@ int main(int argc, char **argv)
deploymentInfo.deployedFrameworks.end()).values();
}
// Handle plugins
if (plugins) {
if (plugin_dir.isEmpty()) {
// Set the plugins search directory
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
#else
deploymentInfo.pluginPath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
#endif
}
else {
deploymentInfo.pluginPath = plugin_dir;
}
if (deploymentInfo.pluginPath.isEmpty()) {
LogError() << "Missing Qt plugins path\n";
return 1;
}
if (!QDir(deploymentInfo.pluginPath).exists()) {
LogError() << "Plugins path does not exist\n" << deploymentInfo.pluginPath;
return 1;
}
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
if (!deploymentInfo.pluginPath.isEmpty()) {
LogNormal();
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
createQtConf(appBundlePath);
}
// Sanity checks
if (deploymentInfo.pluginPath.isEmpty()) {
LogError() << "Missing Qt plugins path\n";
return 1;
}
if (!QDir(deploymentInfo.pluginPath).exists()) {
LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
return 1;
}
// Deploy plugins
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
if (!deploymentInfo.pluginPath.isEmpty()) {
LogNormal();
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
createQtConf(appBundlePath);
}
}
if (runStripEnabled)
stripAppBinary(appBundlePath);
if (!FinalCheck(appBundlePath)) {
return 1;
}
if (runCodesign)
codesign(codesignIdentiy, appBundlePath);

View File

@@ -35,9 +35,11 @@
#include <QStringList>
#include <QDebug>
#include <iostream>
#include <utility>
#include <QProcess>
#include <QDir>
#include <QSet>
#include <QList>
#include <QStack>
#include <QDirIterator>
#include <QLibraryInfo>
@@ -197,22 +199,19 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
}
outputLines.removeFirst(); // remove line containing the binary path
if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) {
const auto match = regexp.match(outputLines.first());
if (match.hasMatch()) {
QString installname = match.captured(1);
if (QFileInfo(binaryPath).fileName() == QFileInfo(installname).fileName()) {
info.installName = installname;
info.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
info.currentVersion = QVersionNumber::fromString(match.captured(3));
outputLines.removeFirst();
info.installName = installname;
info.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
info.currentVersion = QVersionNumber::fromString(match.captured(3));
outputLines.removeFirst();
} else {
info.installName = binaryPath;
}
else {
info.installName = binaryPath;
}
}
else {
} else {
LogError() << "Could not parse otool output line:" << outputLines.first();
outputLines.removeFirst();
}
@@ -234,7 +233,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
return info;
}
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
{
FrameworkInfo info;
QString trimmed = line.trimmed();
@@ -252,7 +251,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
if (trimmed.startsWith("@rpath/")) {
QString rpathRelativePath = trimmed.mid(QStringLiteral("@rpath/").length());
bool foundInsideBundle = false;
foreach (const QString &rpath, rpaths) {
for (const QString &rpath : std::as_const(rpaths)) {
QString path = QDir::cleanPath(rpath + "/" + rpathRelativePath);
// Skip paths already inside the bundle.
if (!appBundlePath.isEmpty()) {
@@ -293,7 +292,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
// Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/
QStringList parts = trimmed.split("/");
while (part < parts.count()) {
const QString currentPart = parts.at(part).simplified() ;
const QString currentPart = parts.at(part).simplified();
++part;
if (currentPart == "")
continue;
@@ -311,7 +310,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
} else if (trimmed.startsWith("/") == false) { // If the line does not contain a full path, the app is using a binary Qt package.
QStringList partsCopy = parts;
partsCopy.removeLast();
foreach (QString path, librarySearchPath) {
for (QString &path : librarySearchPath) {
if (!path.endsWith("/"))
path += '/';
QString nameInPath = path + parts.join(QLatin1Char('/'));
@@ -505,7 +504,7 @@ QString findEntitlementsFile(const QString& path)
return QString();
}
QList<FrameworkInfo> getQtFrameworks(const QList<DylibInfo> &dependencies, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
QList<FrameworkInfo> getQtFrameworks(const QList<DylibInfo> &dependencies, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
{
QList<FrameworkInfo> libraries;
for (const DylibInfo &dylibInfo : dependencies) {
@@ -545,9 +544,9 @@ QString resolveDyldPrefix(const QString &path, const QString &loaderPath, const
return path;
}
QSet<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString executablePath = QString())
QList<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString executablePath = QString())
{
QSet<QString> rpaths;
QList<QString> rpaths;
QProcess otool;
otool.start("otool", QStringList() << "-l" << path);
@@ -584,18 +583,20 @@ QSet<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString
return rpaths;
}
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
{
const OtoolInfo info = findDependencyInfo(path);
return getQtFrameworks(info.dependencies, appBundlePath, rpaths + getBinaryRPaths(path), useDebugLibs);
QList<QString> allRPaths = rpaths + getBinaryRPaths(path);
allRPaths.removeDuplicates();
return getQtFrameworks(info.dependencies, appBundlePath, allRPaths, useDebugLibs);
}
QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs)
QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs)
{
QList<FrameworkInfo> result;
QSet<QString> existing;
foreach (const QString &path, paths) {
foreach (const FrameworkInfo &info, getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) {
for (const QString &path : paths) {
for (const FrameworkInfo &info : getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) {
if (!existing.contains(info.frameworkPath)) { // avoid duplicates
existing.insert(info.frameworkPath);
result << info;
@@ -614,10 +615,10 @@ QStringList getBinaryDependencies(const QString executablePath,
const auto dependencies = findDependencyInfo(path).dependencies;
bool rpathsLoaded = false;
QSet<QString> rpaths;
QList<QString> rpaths;
// return bundle-local dependencies. (those starting with @executable_path)
foreach (const DylibInfo &info, dependencies) {
for (const DylibInfo &info : dependencies) {
QString trimmedLine = info.binaryPath;
if (trimmedLine.startsWith("@executable_path/")) {
QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length()));
@@ -626,14 +627,14 @@ QStringList getBinaryDependencies(const QString executablePath,
} else if (trimmedLine.startsWith("@rpath/")) {
if (!rpathsLoaded) {
rpaths = getBinaryRPaths(path, true, executablePath);
foreach (const QString &binaryPath, additionalBinariesContainingRpaths) {
QSet<QString> binaryRpaths = getBinaryRPaths(binaryPath, true);
rpaths += binaryRpaths;
for (const QString &binaryPath : additionalBinariesContainingRpaths) {
rpaths += getBinaryRPaths(binaryPath, true);
}
rpaths.removeDuplicates();
rpathsLoaded = true;
}
bool resolved = false;
foreach (const QString &rpath, rpaths) {
for (const QString &rpath : std::as_const(rpaths)) {
QString binary = QDir::cleanPath(rpath + "/" + trimmedLine.mid(QStringLiteral("@rpath/").length()));
LogDebug() << "Checking for" << binary;
if (QFile::exists(binary)) {
@@ -662,20 +663,20 @@ bool recursiveCopy(const QString &sourcePath, const QString &destinationPath)
LogNormal() << "copy:" << sourcePath << destinationPath;
QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot);
foreach (QString file, files) {
for (const QString &file : files) {
const QString fileSourcePath = sourcePath + "/" + file;
const QString fileDestinationPath = destinationPath + "/" + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath);
}
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
foreach (QString dir, subdirs) {
for (const QString &dir : subdirs) {
recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir);
}
return true;
}
void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &rpaths, const QString &sourcePath, const QString &destinationPath)
void recursiveCopyAndDeploy(const QString &appBundlePath, const QList<QString> &rpaths, const QString &sourcePath, const QString &destinationPath)
{
QDir().mkpath(destinationPath);
@@ -683,7 +684,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &r
const bool isDwarfPath = sourcePath.endsWith("DWARF");
QStringList files = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot);
foreach (QString file, files) {
for (const QString &file : files) {
const QString fileSourcePath = sourcePath + QLatin1Char('/') + file;
if (file.endsWith("_debug.dylib")) {
@@ -729,7 +730,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &r
}
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot);
foreach (QString dir, subdirs) {
for (const QString &dir : subdirs) {
recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + QLatin1Char('/') + dir, destinationPath + QLatin1Char('/') + dir);
}
}
@@ -752,7 +753,7 @@ QString copyDylib(const FrameworkInfo &framework, const QString path)
return QString();
}
// Retrun if the dylib has aleardy been deployed
// Return if the dylib has already been deployed
if (QFileInfo(dylibDestinationBinaryPath).exists() && !alwaysOwerwriteEnabled)
return dylibDestinationBinaryPath;
@@ -786,13 +787,13 @@ QString copyFramework(const FrameworkInfo &framework, const QString path)
// Now copy the framework. Some parts should be left out (headers/, .prl files).
// Some parts should be included (Resources/, symlink structure). We want this
// function to make as few assumtions about the framework as possible while at
// function to make as few assumptions about the framework as possible while at
// the same time producing a codesign-compatible framework.
// Copy framework binary
copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath);
// Copy Resouces/, Libraries/ and Helpers/
// Copy Resources/, Libraries/ and Helpers/
const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
@@ -850,7 +851,7 @@ void changeIdentification(const QString &id, const QString &binaryPath)
void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath)
{
const QString absBundlePath = QFileInfo(bundlePath).absoluteFilePath();
foreach (const QString &binary, binaryPaths) {
for (const QString &binary : binaryPaths) {
QString deployedInstallName;
if (useLoaderPath) {
deployedInstallName = QLatin1String("@loader_path/")
@@ -858,17 +859,11 @@ void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework
} else {
deployedInstallName = framework.deployedInstallName;
}
changeInstallName(framework.installName, deployedInstallName, binary);
// Workaround for the case when the library ID name is a symlink, while the dependencies
// specified using the canonical path to the library (QTBUG-56814)
QString canonicalInstallName = QFileInfo(framework.installName).canonicalFilePath();
if (!canonicalInstallName.isEmpty() && canonicalInstallName != framework.installName) {
changeInstallName(canonicalInstallName, deployedInstallName, binary);
if (!framework.sourceFilePath.isEmpty()) {
changeInstallName(framework.sourceFilePath, deployedInstallName, binary);
}
// Homebrew workaround, resolve symlink /usr/local/opt/library to /usr/local/Cellar/library
if (framework.installName.startsWith("/usr/local/opt/") && framework.installName.count('/') >= 5) {
canonicalInstallName = QFileInfo(framework.installName.section('/', 0, 4)).canonicalFilePath() + "/" + framework.installName.section('/', 5);
changeInstallName(canonicalInstallName, deployedInstallName, binary);
if (!framework.installName.isEmpty() && framework.installName != framework.sourceFilePath) {
changeInstallName(framework.installName, deployedInstallName, binary);
}
}
}
@@ -878,7 +873,7 @@ void addRPath(const QString &rpath, const QString &binaryPath)
runInstallNameTool(QStringList() << "-add_rpath" << rpath << binaryPath);
}
void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const QString &binaryPath, bool useLoaderPath)
void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QString &binaryPath, bool useLoaderPath)
{
const QString absFrameworksPath = QFileInfo(bundlePath).absoluteFilePath()
+ QLatin1String("/Contents/Frameworks");
@@ -886,7 +881,8 @@ void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const
const QString loaderPathToFrameworks = QLatin1String("@loader_path/") + relativeFrameworkPath;
bool rpathToFrameworksFound = false;
QStringList args;
foreach (const QString &rpath, getBinaryRPaths(binaryPath, false)) {
QList<QString> binaryRPaths = getBinaryRPaths(binaryPath, false);
for (const QString &rpath : std::as_const(binaryRPaths)) {
if (rpath == "@executable_path/../Frameworks" ||
rpath == loaderPathToFrameworks) {
rpathToFrameworksFound = true;
@@ -912,9 +908,9 @@ void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const
runInstallNameTool(QStringList() << args << binaryPath);
}
void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const QStringList &binaryPaths, bool useLoaderPath)
void deployRPaths(const QString &bundlePath, const QList<QString> &rpaths, const QStringList &binaryPaths, bool useLoaderPath)
{
foreach (const QString &binary, binaryPaths) {
for (const QString &binary : binaryPaths) {
deployRPaths(bundlePath, rpaths, binary, useLoaderPath);
}
}
@@ -980,7 +976,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
deploymentInfo.useLoaderPath = useLoaderPath;
deploymentInfo.isFramework = bundlePath.contains(".framework");
deploymentInfo.isDebug = false;
QSet<QString> rpathsUsed;
QList<QString> rpathsUsed;
while (frameworks.isEmpty() == false) {
const FrameworkInfo framework = frameworks.takeFirst();
@@ -1005,8 +1001,9 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
continue;
}
if (!framework.rpathUsed.isEmpty())
rpathsUsed << framework.rpathUsed;
if (!framework.rpathUsed.isEmpty() && !rpathsUsed.contains(framework.rpathUsed)) {
rpathsUsed.append(framework.rpathUsed);
}
// Copy the framework/dylib to the app bundle.
const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath)
@@ -1029,11 +1026,11 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
// Check for framework dependencies
QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs);
foreach (FrameworkInfo dependency, dependencies) {
for (const FrameworkInfo &dependency : dependencies) {
if (dependency.rpathUsed.isEmpty()) {
changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath);
} else {
rpathsUsed << dependency.rpathUsed;
rpathsUsed.append(dependency.rpathUsed);
}
// Deploy framework if necessary.
@@ -1045,6 +1042,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
deploymentInfo.deployedFrameworks = copiedFrameworks;
deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath);
deploymentInfo.rpathsUsed += rpathsUsed;
deploymentInfo.rpathsUsed.removeDuplicates();
return deploymentInfo;
}
@@ -1056,12 +1054,14 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
applicationBundle.libraryPaths = findAppLibraries(appBundlePath);
QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths
<< additionalExecutables;
QSet<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true);
QList<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
allLibraryPaths.insert(QLibraryInfo::path(QLibraryInfo::LibrariesPath));
allLibraryPaths.append(QLibraryInfo::path(QLibraryInfo::LibrariesPath));
#else
allLibraryPaths.insert(QLibraryInfo::location(QLibraryInfo::LibrariesPath));
allLibraryPaths.append(QLibraryInfo::location(QLibraryInfo::LibrariesPath));
#endif
allLibraryPaths.removeDuplicates();
QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs);
if (frameworks.isEmpty() && !alwaysOwerwriteEnabled) {
LogWarning();
@@ -1077,7 +1077,7 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
QString getLibInfix(const QStringList &deployedFrameworks)
{
QString libInfix;
foreach (const QString &framework, deployedFrameworks) {
for (const QString &framework : deployedFrameworks) {
if (framework.startsWith(QStringLiteral("QtCore")) && framework.endsWith(QStringLiteral(".framework"))) {
Q_ASSERT(framework.length() >= 16);
// 16 == "QtCore" + ".framework"
@@ -1132,7 +1132,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
// Network
if (deploymentInfo.containsModule("Network", libInfix))
addPlugins(QStringLiteral("bearer"));
addPlugins(QStringLiteral("tls"));
// All image formats (svg if QtSvg is used)
const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
@@ -1199,7 +1199,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
}
}
foreach (const QString &plugin, pluginList) {
for (const QString &plugin : pluginList) {
QString sourcePath = pluginSourcePath + "/" + plugin;
const QString destinationPath = pluginDestinationPath + "/" + plugin;
QDir dir;
@@ -1216,7 +1216,15 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
{
QString sourcePath = qgetenv("GIO_EXTRA_MODULES");
if (sourcePath.isEmpty()) {
sourcePath = "/usr/local/lib/gio/modules/libgiognutls.so";
if (QFileInfo::exists("/usr/local/lib/gio/modules/libgiognutls.so")) {
sourcePath = "/usr/local/lib/gio/modules/libgiognutls.so";
}
else if (QFileInfo::exists("/opt/local/lib/gio/modules/libgiognutls.so")) {
sourcePath = "/opt/local/lib/gio/modules/libgiognutls.so";
}
else {
qFatal("Missing GIO_EXTRA_MODULES");
}
}
else {
sourcePath = sourcePath + "/libgiognutls.so";
@@ -1234,7 +1242,15 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
{
QString sourcePath = qgetenv("GST_PLUGIN_SCANNER");
if (sourcePath.isEmpty()) {
sourcePath = "/usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner";
if (QFileInfo::exists("/usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner")) {
sourcePath = "/usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner";
}
else if (QFileInfo::exists("/opt/local/libexec/gstreamer-1.0/gst-plugin-scanner")) {
sourcePath = "/opt/local/libexec/gstreamer-1.0/gst-plugin-scanner";
}
else {
qFatal("Missing GST_PLUGIN_SCANNER.");
}
}
const QString destinationPath = appBundleInfo.path + "/" + "Contents/PlugIns/gst-plugin-scanner";
QDir dir;
@@ -1281,11 +1297,9 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
<< "libgstrtsp.dylib"
<< "libgstflac.dylib"
<< "libgstwavparse.dylib"
<< "libgstfaac.dylib"
<< "libgstfaad.dylib"
<< "libgstogg.dylib"
<< "libgstopus.dylib"
<< "libgstopusparse.dylib"
<< "libgstasf.dylib"
<< "libgstspeex.dylib"
<< "libgsttaglib.dylib"
@@ -1293,22 +1307,59 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
<< "libgstisomp4.dylib"
<< "libgstlibav.dylib"
<< "libgstaiff.dylib"
<< "libgstlame.dylib"
<< "libgstmusepack.dylib";
<< "libgstlame.dylib";
QString gstreamer_plugins_dir = qgetenv("GST_PLUGIN_PATH");
if (gstreamer_plugins_dir.isEmpty()) {
gstreamer_plugins_dir = "/usr/local/lib/gstreamer-1.0";
}
// macports does not have these.
QStringList gstreamer_plugins_optional = QStringList() << "libgstopusparse.dylib"
<< "libgstfaac.dylib"
<< "libgstmusepack.dylib";
QString gstreamer_plugins_dir = qgetenv("GST_PLUGIN_PATH");
if (gstreamer_plugins_dir.isEmpty()) {
if (QDir().exists("/usr/local/lib/gstreamer-1.0")) {
gstreamer_plugins_dir = "/usr/local/lib/gstreamer-1.0";
}
else if (QDir().exists("/opt/local/lib/gstreamer-1.0")) {
gstreamer_plugins_dir = "/opt/local/lib/gstreamer-1.0";
}
else {
qFatal("Missing GST_PLUGIN_PATH.");
}
}
for (const QString &plugin : gstreamer_plugins) {
const QString sourcePath = gstreamer_plugins_dir + "/" + plugin;
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + plugin;
QDir dir;
if (dir.mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) {
runStrip(destinationPath);
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
QFileInfo info(gstreamer_plugins_dir + "/" + plugin);
if (!info.exists()) {
info.setFile(gstreamer_plugins_dir + "/" + info.baseName() + QString(".so"));
if (!info.exists()) {
LogError() << "Missing gstreamer plugin" << info.baseName();
qFatal("Missing %s", info.baseName().toUtf8().constData());
}
}
const QString &sourcePath = info.filePath();
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + info.fileName();
if (QDir().mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) {
runStrip(destinationPath);
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
}
}
for (const QString &plugin : gstreamer_plugins_optional) {
QFileInfo info(gstreamer_plugins_dir + "/" + plugin);
if (!info.exists()) {
info.setFile(gstreamer_plugins_dir + "/" + info.baseName() + QString(".so"));
if (!info.exists()) {
LogWarning() << "Skip missing gstreamer plugin" << info.baseName();
continue;
}
}
const QString &sourcePath = info.filePath();
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + info.fileName();
if (QDir().mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) {
runStrip(destinationPath);
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
}
}
@@ -1355,7 +1406,7 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs);
}
void deployQmlImport(const QString &appBundlePath, const QSet<QString> &rpaths, const QString &importSourcePath, const QString &importName)
void deployQmlImport(const QString &appBundlePath, const QList<QString> &rpaths, const QString &importSourcePath, const QString &importName)
{
QString importDestinationPath = appBundlePath + "/Contents/Resources/qml/" + importName;
@@ -1384,11 +1435,11 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
LogNormal() << "Application QML file path(s) is" << qmlDirs;
LogNormal() << "QML module search path(s) is" << qmlImportPaths;
// Use qmlimportscanner from QLibraryInfo::BinariesPath
// Use qmlimportscanner from QLibraryInfo::LibraryExecutablesPath
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmlimportscanner");
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + "/qmlimportscanner");
#else
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlimportscanner");
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::location(QLibraryInfo::LibraryExecutablesPath) + "/qmlimportscanner");
#endif
// Fallback: Look relative to the macdeployqt binary
@@ -1405,7 +1456,7 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
// build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml"
// ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed)
QStringList argumentList;
foreach (const QString &qmlDir, qmlDirs) {
for (const QString &qmlDir : qmlDirs) {
argumentList.append("-rootPath");
argumentList.append(qmlDir);
}
@@ -1453,7 +1504,7 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
std::sort(array.begin(), array.end(), importLessThan);
// deploy each import
foreach (const QVariant &importValue, array) {
for (const QVariant &importValue : array) {
QVariantMap import = importValue.toMap();
QString name = import["name"].toString();
QString path = import["path"].toString();
@@ -1550,7 +1601,7 @@ QSet<QString> codesignBundle(const QString &identity,
QString appBundleAbsolutePath = QFileInfo(appBundlePath).absoluteFilePath();
QString rootBinariesPath = appBundleAbsolutePath + "/Contents/MacOS/";
QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files);
foreach (const QString &binary, foundRootBinaries) {
for (const QString &binary : foundRootBinaries) {
QString binaryPath = rootBinariesPath + binary;
pendingBinaries.push(binaryPath);
pendingBinariesSet.insert(binaryPath);
@@ -1559,14 +1610,14 @@ QSet<QString> codesignBundle(const QString &identity,
bool getAbsoltuePath = true;
QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/", getAbsoltuePath);
foreach (const QString &binary, foundPluginBinaries) {
for (const QString &binary : foundPluginBinaries) {
pendingBinaries.push(binary);
pendingBinariesSet.insert(binary);
}
// Add frameworks for processing.
QStringList frameworkPaths = findAppFrameworkPaths(appBundlePath);
foreach (const QString &frameworkPath, frameworkPaths) {
for (const QString &frameworkPath : frameworkPaths) {
// Prioritise first to sign any additional inner bundles found in the Helpers folder (e.g
// used by QtWebEngine).
@@ -1575,7 +1626,7 @@ QSet<QString> codesignBundle(const QString &identity,
helpersIterator.next();
QString helpersPath = helpersIterator.filePath();
QStringList innerBundleNames = QDir(helpersPath).entryList(QStringList() << "*.app", QDir::Dirs);
foreach (const QString &innerBundleName, innerBundleNames)
for (const QString &innerBundleName : innerBundleNames)
signedBinaries += codesignBundle(identity,
helpersPath + "/" + innerBundleName,
additionalBinariesContainingRpaths);
@@ -1588,7 +1639,7 @@ QSet<QString> codesignBundle(const QString &identity,
librariesIterator.next();
QString librariesPath = librariesIterator.filePath();
QStringList bundleFiles = findAppBundleFiles(librariesPath, getAbsoltuePath);
foreach (const QString &binary, bundleFiles) {
for (const QString &binary : bundleFiles) {
pendingBinaries.push(binary);
pendingBinariesSet.insert(binary);
}
@@ -1613,7 +1664,7 @@ QSet<QString> codesignBundle(const QString &identity,
pendingBinaries.push(binary);
pendingBinariesSet.insert(binary);
int dependenciesSkipped = 0;
foreach (const QString &dependency, dependencies) {
for (const QString &dependency : std::as_const(dependencies)) {
// Skip dependencies that are outside the current app bundle, because this might
// cause a codesign error if the current bundle is part of the dependency (e.g.
// a bundle is part of a framework helper, and depends on that framework).
@@ -1724,109 +1775,3 @@ void fixupFramework(const QString &frameworkName)
addRPath("@loader_path/../../Contents/Frameworks/", frameworkBinary);
}
bool FinalCheck(const QString &appBundlePath) {
bool success = true;
QDirIterator iter(appBundlePath, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
while (iter.hasNext()) {
iter.next();
QString filepath = iter.fileInfo().filePath();
if (filepath.endsWith(".plist") ||
filepath.endsWith(".icns") ||
filepath.endsWith(".prl") ||
filepath.endsWith(".conf") ||
filepath.endsWith(".h") ||
filepath.endsWith(".nib") ||
filepath.endsWith(".strings") ||
filepath.endsWith(".css") ||
filepath.endsWith("CodeResources") ||
filepath.endsWith("PkgInfo") ||
filepath.endsWith(".modulemap")) {
continue;
}
//qDebug() << "Final check on" << filepath;
QProcess otool;
otool.start("otool", QStringList() << "-L" << filepath);
otool.waitForFinished();
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
LogError() << otool.readAllStandardError();
success = false;
continue;
}
QString output = otool.readAllStandardOutput();
QStringList output_lines = output.split("\n", Qt::SkipEmptyParts);
if (output_lines.size() < 2) {
LogError() << "Could not parse otool output:" << output;
success = false;
continue;
}
QString first_line = output_lines.first();
if (first_line.endsWith(':')) first_line.chop(1);
if (first_line == filepath) {
output_lines.removeFirst();
}
else {
LogError() << "First line" << first_line << "does not match" << filepath;
success = false;
}
static const QRegularExpression regexp(QStringLiteral("^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$"));
for (const QString &output_line : output_lines) {
//qDebug() << "Final check on" << filepath << output_line;
const auto match = regexp.match(output_line);
if (match.hasMatch()) {
QString library = match.captured(1);
if (QFileInfo(library).fileName() == QFileInfo(filepath).fileName()) { // It's this.
continue;
}
else if (library.startsWith("@executable_path")) {
QString real_path = library;
real_path = real_path.replace("@executable_path", appBundlePath + "/Contents/MacOS");
if (!QFile(real_path).exists()) {
LogError() << real_path << "does not exist for" << filepath;
success = false;
}
}
else if (library.startsWith("@rpath")) {
QString real_path = library;
real_path = real_path.replace("@rpath", appBundlePath + "/Contents/Frameworks");
if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
LogError() << real_path << "does not exist for" << filepath;
success = false;
}
}
else if (library.startsWith("@loader_path")) {
QString loader_path = QFileInfo(filepath).path();
QString real_path = library;
real_path = real_path.replace("@loader_path", loader_path);
if (!QFile(real_path).exists()) {
LogError() << real_path << "does not exist for" << filepath;
success = false;
}
}
else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library
continue;
}
else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason.
continue;
}
else {
LogError() << "File" << filepath << "points to" << library;
success = false;
}
}
else {
LogError() << "Could not parse otool output line:" << output_line;
success = false;
}
}
}
return success;
}

View File

@@ -62,7 +62,7 @@ public:
bool isDebugLibrary() const
{
return binaryName.contains(QLatin1String("_debug"));
return binaryName.endsWith(QStringLiteral("_debug"));
}
};
@@ -101,7 +101,7 @@ public:
QString qtPath;
QString pluginPath;
QStringList deployedFrameworks;
QSet<QString> rpathsUsed;
QList<QString> rpathsUsed;
bool useLoaderPath;
bool isFramework;
bool isDebug;
@@ -112,10 +112,10 @@ public:
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
OtoolInfo findDependencyInfo(const QString &binaryPath);
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
QString findAppBinary(const QString &appBundlePath);
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
QString copyFramework(const FrameworkInfo &framework, const QString path);
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
@@ -136,6 +136,6 @@ QSet<QString> codesignBundle(const QString &identity,
void codesign(const QString &identity, const QString &appBundlePath);
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
void fixupFramework(const QString &appBundlePath);
bool FinalCheck(const QString &appBundlePath);
#endif

View File

@@ -10,11 +10,7 @@ endif()
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
if(BUILD_WITH_QT6)
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
else()
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
endif()
qt_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
target_include_directories(singleapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
@@ -28,11 +24,7 @@ target_link_libraries(singleapplication PRIVATE
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
if(BUILD_WITH_QT6)
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
else()
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
endif()
qt_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
target_include_directories(singlecoreapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}

View File

@@ -134,7 +134,7 @@ SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondar
}
}
if (inst->primary == false) {
if (!inst->primary) {
d->startPrimary();
if (!d->memory_->unlock()) {
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
@@ -234,9 +234,9 @@ QString SingleApplication::currentUser() {
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfuly, false otherwise.
* @return true if the message was sent successfully, false otherwise.
*/
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
bool SingleApplication::sendMessage(const QByteArray &message, const int timeout) {
Q_D(SingleApplication);
@@ -244,8 +244,9 @@ bool SingleApplication::sendMessage(QByteArray message, int timeout) {
if (isPrimary()) return false;
// Make sure the socket is connected
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect))
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect)) {
return false;
}
d->socket_->write(message);
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);

View File

@@ -42,10 +42,10 @@
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multipe instances of the same Application
* @brief The SingleApplication class handles multiple instances of the same Application
* @see QApplication
*/
class SingleApplication : public QApplication {
class SingleApplication : public QApplication { // clazy:exclude=ctor-missing-parent-argument
Q_OBJECT
typedef QApplication app_t;
@@ -136,7 +136,7 @@ class SingleApplication : public QApplication {
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage(QByteArray message, int timeout = 1000);
bool sendMessage(const QByteArray &message, const int timeout = 1000);
signals:
void instanceStarted();

View File

@@ -169,7 +169,7 @@ void SingleApplicationPrivate::genBlockServerName() {
}
void SingleApplicationPrivate::initializeMemoryBlock() {
void SingleApplicationPrivate::initializeMemoryBlock() const {
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
inst->primary = false;
@@ -237,8 +237,9 @@ bool SingleApplicationPrivate::connectToPrimary(const int timeout, const Connect
forever {
randomSleep();
if (socket_->state() != QLocalSocket::ConnectingState)
if (socket_->state() != QLocalSocket::ConnectingState) {
socket_->connectToServer(blockServerName_);
}
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
@@ -284,7 +285,7 @@ bool SingleApplicationPrivate::connectToPrimary(const int timeout, const Connect
}
quint16 SingleApplicationPrivate::blockChecksum() {
quint16 SingleApplicationPrivate::blockChecksum() const {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
@@ -296,7 +297,7 @@ quint16 SingleApplicationPrivate::blockChecksum() {
}
qint64 SingleApplicationPrivate::primaryPid() {
qint64 SingleApplicationPrivate::primaryPid() const {
memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -307,7 +308,7 @@ qint64 SingleApplicationPrivate::primaryPid() {
}
QString SingleApplicationPrivate::primaryUser() {
QString SingleApplicationPrivate::primaryUser() const {
memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -327,7 +328,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
const ConnectionInfo info = connectionMap_[nextConnSocket];
slotClientConnectionClosed(nextConnSocket, info.instanceId);
});
@@ -337,7 +338,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
});
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
const ConnectionInfo info = connectionMap_[nextConnSocket];
switch (info.stage) {
case StageHeader:
readInitMessageHeader(nextConnSocket);
@@ -461,7 +462,7 @@ void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSo
void SingleApplicationPrivate::randomSleep() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
#else
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));

View File

@@ -37,7 +37,7 @@
#include <QtGlobal>
#include <QObject>
#include <QString>
#include <QMap>
#include <QHash>
#include "singleapplication.h"
@@ -55,7 +55,7 @@ struct InstancesInfo {
struct ConnectionInfo {
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
qint64 msgLen;
quint64 msgLen;
quint32 instanceId;
quint8 stage;
};
@@ -80,18 +80,18 @@ class SingleApplicationPrivate : public QObject {
explicit SingleApplicationPrivate(SingleApplication *ptr);
~SingleApplicationPrivate() override;
QString getUsername();
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum();
qint64 primaryPid();
QString primaryUser();
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket);
void randomSleep();
static void randomSleep();
SingleApplication *q_ptr;
QSharedMemory *memory_;
@@ -100,7 +100,7 @@ class SingleApplicationPrivate : public QObject {
quint32 instanceNumber_;
QString blockServerName_;
SingleApplication::Options options_;
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
public slots:
void slotConnectionEstablished();

View File

@@ -134,7 +134,7 @@ SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allow
}
}
if (inst->primary == false) {
if (!inst->primary) {
d->startPrimary();
if (!d->memory_->unlock()) {
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
@@ -234,9 +234,9 @@ QString SingleCoreApplication::currentUser() {
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfuly, false otherwise.
* @return true if the message was sent successfully, false otherwise.
*/
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
bool SingleCoreApplication::sendMessage(const QByteArray &message, const int timeout) {
Q_D(SingleCoreApplication);
@@ -244,8 +244,9 @@ bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
if (isPrimary()) return false;
// Make sure the socket is connected
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect))
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect)) {
return false;
}
d->socket_->write(message);
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);

View File

@@ -42,7 +42,7 @@
class SingleCoreApplicationPrivate;
/**
* @brief The SingleCoreApplication class handles multipe instances of the same Application
* @brief The SingleCoreApplication class handles multiple instances of the same Application
* @see QCoreApplication
*/
class SingleCoreApplication : public QCoreApplication {
@@ -135,7 +135,7 @@ class SingleCoreApplication : public QCoreApplication {
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage(QByteArray message, int timeout = 1000);
bool sendMessage(const QByteArray &message, const int timeout = 1000);
signals:
void instanceStarted();

View File

@@ -169,7 +169,7 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
}
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
void SingleCoreApplicationPrivate::initializeMemoryBlock() const {
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
inst->primary = false;
@@ -237,8 +237,9 @@ bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const Con
forever {
randomSleep();
if (socket_->state() != QLocalSocket::ConnectingState)
if (socket_->state() != QLocalSocket::ConnectingState) {
socket_->connectToServer(blockServerName_);
}
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
@@ -284,7 +285,7 @@ bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const Con
}
quint16 SingleCoreApplicationPrivate::blockChecksum() {
quint16 SingleCoreApplicationPrivate::blockChecksum() const {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
@@ -296,7 +297,7 @@ quint16 SingleCoreApplicationPrivate::blockChecksum() {
}
qint64 SingleCoreApplicationPrivate::primaryPid() {
qint64 SingleCoreApplicationPrivate::primaryPid() const {
memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -307,7 +308,7 @@ qint64 SingleCoreApplicationPrivate::primaryPid() {
}
QString SingleCoreApplicationPrivate::primaryUser() {
QString SingleCoreApplicationPrivate::primaryUser() const {
memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -327,7 +328,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
const ConnectionInfo info = connectionMap_[nextConnSocket];
slotClientConnectionClosed(nextConnSocket, info.instanceId);
});
@@ -337,7 +338,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
});
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket];
const ConnectionInfo info = connectionMap_[nextConnSocket];
switch (info.stage) {
case StageHeader:
readInitMessageHeader(nextConnSocket);
@@ -461,7 +462,7 @@ void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *clos
void SingleCoreApplicationPrivate::randomSleep() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
#else
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));

View File

@@ -37,7 +37,7 @@
#include <QtGlobal>
#include <QObject>
#include <QString>
#include <QMap>
#include <QHash>
#include "singlecoreapplication.h"
@@ -80,18 +80,18 @@ class SingleCoreApplicationPrivate : public QObject {
explicit SingleCoreApplicationPrivate(SingleCoreApplication *ptr);
~SingleCoreApplicationPrivate() override;
QString getUsername();
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum();
qint64 primaryPid();
QString primaryUser();
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket);
void randomSleep();
static void randomSleep();
SingleCoreApplication *q_ptr;
QSharedMemory *memory_;
@@ -100,7 +100,7 @@ class SingleCoreApplicationPrivate : public QObject {
quint32 instanceNumber_;
QString blockServerName_;
SingleCoreApplication::Options options_;
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
public slots:
void slotConnectionEstablished();

View File

@@ -12,13 +12,13 @@ include(cmake/Summary.cmake)
include(cmake/OptionalSource.cmake)
include(cmake/ParseArguments.cmake)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(LINUX ON)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(FREEBSD ON)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
if(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(OPENBSD ON)
endif()
@@ -35,34 +35,38 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
list(APPEND COMPILE_OPTIONS
$<$<COMPILE_LANGUAGE:C>:-std=c99>
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
-Wall
-Wextra
-Wpedantic
-Wunused
-Wshadow
-Wundef
-Wuninitialized
-Wredundant-decls
-Wcast-align
-Winit-self
-Wmissing-include-dirs
-Wmissing-declarations
-Wstrict-overflow=2
-Wunused-parameter
-Wformat=2
-Wdisabled-optimization
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
)
if(MSVC)
list(APPEND COMPILE_OPTIONS /std:c++17 /MP)
else()
list(APPEND COMPILE_OPTIONS
$<$<COMPILE_LANGUAGE:C>:-std=c99>
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
-Wall
-Wextra
-Wpedantic
-Wunused
-Wshadow
-Wundef
-Wuninitialized
-Wredundant-decls
-Wcast-align
-Winit-self
-Wmissing-include-dirs
-Wmissing-declarations
-Wstrict-overflow=2
-Wunused-parameter
-Wformat=2
-Wdisabled-optimization
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
)
endif()
option(BUILD_WERROR "Build with -Werror" OFF)
if(BUILD_WERROR)
list(APPEND COMPILE_OPTIONS -Werror)
endif(BUILD_WERROR)
endif()
add_compile_options(${COMPILE_OPTIONS})
@@ -77,33 +81,33 @@ if(APPLE)
endif()
find_program(CCACHE_EXECUTABLE NAMES ccache)
if (CCACHE_EXECUTABLE)
if(CCACHE_EXECUTABLE)
message(STATUS "ccache found: will be used for compilation and linkage")
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_EXECUTABLE})
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
endif ()
endif()
find_package(PkgConfig REQUIRED)
find_package(Boost REQUIRED)
find_package(Threads)
find_package(Backtrace QUIET)
find_package(Backtrace)
if(Backtrace_FOUND)
set(HAVE_BACKTRACE ON)
endif()
find_package(Iconv QUIET)
find_package(Iconv)
find_package(GnuTLS REQUIRED)
find_package(Protobuf REQUIRED)
if (NOT Protobuf_PROTOC_EXECUTABLE)
if(NOT Protobuf_PROTOC_EXECUTABLE)
message(FATAL_ERROR "Missing protobuf compiler.")
endif()
if(LINUX)
find_package(ALSA REQUIRED)
pkg_check_modules(DBUS REQUIRED dbus-1)
else(LINUX)
else()
find_package(ALSA)
pkg_check_modules(DBUS dbus-1)
endif(LINUX)
if (UNIX AND NOT APPLE)
endif()
if(UNIX AND NOT APPLE)
find_package(X11)
pkg_check_modules(XCB xcb)
endif()
@@ -123,7 +127,7 @@ pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
pkg_check_modules(LIBVLC libvlc)
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9)
pkg_check_modules(LIBPULSE libpulse)
pkg_check_modules(CHROMAPRINT libchromaprint)
pkg_check_modules(CHROMAPRINT libchromaprint>=1.4)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
@@ -132,81 +136,99 @@ find_package(FFTW3)
find_package(GTest)
find_library(GMOCK_LIBRARY gmock)
if(NOT QT_DEFAULT_MAJOR_VERSION)
set(QT_DEFAULT_MAJOR_VERSION 5)
endif()
set(QT_MAJOR_VERSION ${QT_DEFAULT_MAJOR_VERSION} CACHE STRING "Qt version to use (5 or 6), defaults to ${QT_DEFAULT_MAJOR_VERSION}")
option(BUILD_WITH_QT5 "Use Qt 5" OFF)
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
option(QT_VERSION_MAJOR "Qt version to use (5 or 6)")
option(BUILD_WITH_QT5 "Build with Qt 5" OFF)
option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
if(WITH_QT6)
set(BUILD_WITH_QT6 ON)
endif()
if(BUILD_WITH_QT5)
set(QT_MAJOR_VERSION 5)
elseif(BUILD_WITH_QT6)
set(QT_MAJOR_VERSION 6)
else()
if(QT_MAJOR_VERSION EQUAL 5)
set(BUILD_WITH_QT5 ON)
elseif(QT_MAJOR_VERSION EQUAL 6)
set(BUILD_WITH_QT6 ON)
else()
set(BUILD_WITH_QT5 ON)
set(QT_MAJOR_VERSION 5)
endif()
if(QT_MAJOR_VERSION)
set(QT_VERSION_MAJOR ${QT_MAJOR_VERSION})
endif()
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
set(QT_OPTIONAL_COMPONENTS Test)
if(QT_MAJOR_VERSION EQUAL 5)
set(QT_MIN_VERSION 5.8)
if(QT_VERSION_MAJOR)
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
endif()
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
if(DBUS_FOUND AND NOT WIN32)
list(APPEND QT_COMPONENTS DBus)
endif()
if(X11_FOUND)
set(QT_OPTIONAL_COMPONENTS Test)
set(QT_MIN_VERSION 5.8)
if(BUILD_WITH_QT6 OR QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
elseif(BUILD_WITH_QT5 OR QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
else()
# Automatically detect Qt version.
find_package(QT NAMES Qt6 Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED)
if(QT_FOUND AND QT_VERSION_MAJOR EQUAL 6)
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
elseif(QT_FOUND AND QT_VERSION_MAJOR EQUAL 5)
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
else()
message(FATAL_ERROR "Missing Qt.")
endif()
endif()
if(QT_VERSION_MAJOR)
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
endif()
if(X11_FOUND AND BUILD_WITH_QT5)
list(APPEND QT_OPTIONAL_COMPONENTS X11Extras)
endif()
if(WIN32)
list(APPEND QT_OPTIONAL_COMPONENTS WinExtras)
endif()
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
set(QtCore_LIBRARIES Qt${QT_MAJOR_VERSION}::Core)
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent)
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui)
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets)
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network)
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql)
set(QT_LIBRARIES Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Concurrent Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Sql)
if(Qt${QT_MAJOR_VERSION}DBus_FOUND)
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION)
set(QtCore_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
set(QtConcurrent_LIBRARIES Qt${QT_VERSION_MAJOR}::Concurrent)
set(QtGui_LIBRARIES Qt${QT_VERSION_MAJOR}::Gui)
set(QtWidgets_LIBRARIES Qt${QT_VERSION_MAJOR}::Widgets)
set(QtNetwork_LIBRARIES Qt${QT_VERSION_MAJOR}::Network)
set(QtSql_LIBRARIES Qt${QT_VERSION_MAJOR}::Sql)
set(QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Sql)
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
set(QtDBus_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_VERSION_MAJOR}::qdbusxml2cpp LOCATION)
endif()
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND)
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
if(BUILD_WITH_QT5 AND Qt5X11Extras_FOUND)
set(HAVE_X11EXTRAS ON)
set(QtX11Extras_LIBRARIES Qt5::X11Extras)
list(APPEND QT_LIBRARIES Qt5::X11Extras)
endif()
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND)
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
set(HAVE_WINEXTRAS ON)
endif()
if(Qt${QT_MAJOR_VERSION}Test_FOUND)
set(QtTest_LIBRARIES Qt${QT_MAJOR_VERSION}::Test)
if(Qt${QT_VERSION_MAJOR}Test_FOUND)
set(QtTest_LIBRARIES Qt${QT_VERSION_MAJOR}::Test)
endif()
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert)
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS LinguistTools CONFIG)
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt${QT_VERSION_MAJOR}::lconvert)
endif()
if(BUILD_WITH_QT5 AND Qt5Core_VERSION VERSION_LESS 5.15.0)
macro(qt_add_resources)
qt5_add_resources(${ARGN})
endmacro()
macro(qt_wrap_cpp)
qt5_wrap_cpp(${ARGN})
endmacro()
macro(qt_wrap_ui)
qt5_wrap_ui(${ARGN})
endmacro()
macro(qt_add_dbus_adaptor)
qt5_add_dbus_adaptor(${ARGN})
endmacro()
macro(qt_add_dbus_interface)
qt5_add_dbus_interface(${ARGN})
endmacro()
endif()
if(X11_FOUND)
@@ -223,26 +245,45 @@ if(X11_FOUND)
message(WARNING, "Missing X11/XF86keysym.h")
endif()
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
if(QPA_QPLATFORMNATIVEINTERFACE_H)
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif()
endif(X11_FOUND)
option(USE_TAGLIB "Build with TagLib" OFF)
option(USE_TAGPARSER "Build with TagParser" OFF)
if(NOT USE_TAGLIB AND NOT USE_TAGPARSER)
set(USE_TAGLIB ON)
endif()
# TAGLIB
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
if(HAVE_TAGLIB_DSFFILE_H)
set(HAVE_TAGLIB_DSFFILE ON)
endif(HAVE_TAGLIB_DSFFILE_H)
if(HAVE_TAGLIB_DSDIFFFILE_H)
set(HAVE_TAGLIB_DSDIFFFILE ON)
endif(HAVE_TAGLIB_DSDIFFFILE_H)
if(USE_TAGLIB)
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
if(TAGLIB_FOUND)
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
if(HAVE_TAGLIB_DSFFILE_H)
set(HAVE_TAGLIB_DSFFILE ON)
endif(HAVE_TAGLIB_DSFFILE_H)
if(HAVE_TAGLIB_DSDIFFFILE_H)
set(HAVE_TAGLIB_DSDIFFFILE ON)
endif(HAVE_TAGLIB_DSDIFFFILE_H)
endif()
endif()
# TAGPARSER
if(USE_TAGPARSER)
pkg_check_modules(TAGPARSER REQUIRED tagparser)
endif()
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
message(FATAL_ERROR "You need either TagLib or TagParser!")
endif()
# SingleApplication
add_subdirectory(3rdparty/singleapplication)
@@ -252,11 +293,12 @@ set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
if(APPLE)
find_library(SPARKLE Sparkle PATHS "/usr/local/opt/sparkle")
add_subdirectory(3rdparty/macdeployqt)
add_subdirectory(3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
endif(APPLE)
add_subdirectory(3rdparty/macdeployqt)
add_subdirectory(ext/macdeploycheck)
endif()
if(NOT SPARKLE AND (APPLE OR WIN32))
if(BUILD_WITH_QT6)
@@ -269,23 +311,22 @@ if(NOT SPARKLE AND (APPLE OR WIN32))
endif()
endif()
if (WIN32)
if(WIN32 AND NOT MSVC)
# RC compiler
string(REPLACE "gcc" "windres" CMAKE_RC_COMPILER_INIT ${CMAKE_C_COMPILER})
enable_language(RC)
SET(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -o <OBJECT> <SOURCE> -I ${CMAKE_SOURCE_DIR}/dist/windows")
endif(WIN32)
endif()
# Optional bits
if(WIN32)
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
endif(WIN32)
endif()
optional_component(ALSA ON "ALSA integration"
DEPENDS "alsa" ALSA_FOUND
)
optional_component(LIBPULSE ON "Pulse audio integration"
optional_component(LIBPULSE ON "PulseAudio integration"
DEPENDS "libpulse" LIBPULSE_FOUND
)
@@ -306,11 +347,17 @@ optional_component(VLC ON "Engine: VLC backend"
DEPENDS "libvlc" LIBVLC_FOUND
)
optional_component(CHROMAPRINT ON "Chromaprint (Tag fetching from Musicbrainz)"
optional_component(SONGFINGERPRINTING ON "Song fingerprinting and tracking"
DEPENDS "chromaprint" CHROMAPRINT_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
)
if (X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
optional_component(MUSICBRAINZ ON "MusicBrainz integration"
DEPENDS "chromaprint" CHROMAPRINT_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
)
if(X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
endif()
@@ -318,12 +365,21 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
)
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
DEPENDS "X11Extras" Qt${QT_MAJOR_VERSION}X11Extras_FOUND
)
if(BUILD_WITH_QT6 AND (Qt6Core_VERSION VERSION_EQUAL 6.2.0 OR Qt6Core_VERSION VERSION_GREATER 6.2.0))
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts" DEPENDS "X11" X11_FOUND)
else()
if(HAVE_X11EXTRAS OR HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
set(HAVE_X11EXTRAS_OR_QPA_QPLATFORMNATIVEINTERFACE_H ON)
endif()
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
DEPENDS "X11" X11_FOUND
DEPENDS "Qt >= 6.2, X11Extras or qpa/qplatformnativeinterface.h header" HAVE_X11EXTRAS_OR_QPA_QPLATFORMNATIVEINTERFACE_H
)
endif()
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" LIBCDIO_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
)
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
@@ -372,24 +428,23 @@ optional_component(MOODBAR ON "Moodbar"
DEPENDS "gstreamer" HAVE_GSTREAMER
)
if(LINUX OR APPLE)
option(USE_BUNDLE "Bundle dependencies" OFF)
elseif(WIN32)
if(APPLE OR WIN32)
option(USE_BUNDLE "Bundle dependencies" ON)
else()
option(USE_BUNDLE "Bundle dependencies" OFF)
endif()
if (USE_BUNDLE AND NOT USE_BUNDLE_DIR)
if(LINUX)
set(USE_BUNDLE_DIR "../plugins")
endif(LINUX)
if(APPLE)
set(USE_BUNDLE_DIR "../PlugIns")
endif(APPLE)
endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
# Check that we have sqlite3 with FTS5
if(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
if(LINUX)
set(USE_BUNDLE_DIR "../plugins")
endif()
if(APPLE)
set(USE_BUNDLE_DIR "../PlugIns")
endif()
endif()
if(NOT CMAKE_CROSSCOMPILING)
# Check that we have Qt with sqlite driver
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
check_cxx_source_runs("
@@ -400,12 +455,29 @@ if(NOT CMAKE_CROSSCOMPILING)
db.setDatabaseName(\":memory:\");
if (!db.open()) { return 1; }
QSqlQuery q(db);
q.prepare(\"CREATE VIRTUAL TABLE test_fts USING fts5(test, tokenize = 'unicode61 remove_diacritics 0');\");
q.prepare(\"CREATE TABLE test (test TEXT);\");
if (!q.exec()) return 1;
}
"
SQLITE3_FTS5
QT_SQLITE_TEST
)
if(QT_SQLITE_TEST)
# Check that we have sqlite3 with FTS5
check_cxx_source_runs("
#include <QSqlDatabase>
#include <QSqlQuery>
int main() {
QSqlDatabase db = QSqlDatabase::addDatabase(\"QSQLITE\");
db.setDatabaseName(\":memory:\");
if (!db.open()) { return 1; }
QSqlQuery q(db);
q.prepare(\"CREATE VIRTUAL TABLE test_fts USING fts5(test, tokenize = 'unicode61 remove_diacritics 0');\");
if (!q.exec()) return 1;
}
"
SQLITE_FTS5_TEST
)
endif()
endif()
# Set up definitions
@@ -417,6 +489,13 @@ add_definitions(-DQT_USE_QSTRINGBUILDER)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DQT_NO_CAST_TO_ASCII)
if(WIN32)
add_definitions(-DUNICODE)
if(MSVC)
add_definitions(-DPROTOBUF_USE_DLLS)
endif()
endif()
# Subdirectories
add_subdirectory(src)
add_subdirectory(dist)
@@ -432,13 +511,9 @@ if(GTest_FOUND AND GMOCK_LIBRARY AND QtTest_LIBRARIES)
endif()
# Uninstall support
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
add_custom_target(uninstall
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
# Show a summary of what we have enabled
summary_show()
@@ -448,10 +523,16 @@ elseif(NOT HAVE_GSTREAMER)
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
endif()
if(NOT SQLITE3_FTS5 AND NOT CMAKE_CROSSCOMPILING)
message(WARNING "sqlite3 must be enabled with FTS5. See: https://www.sqlite.org/fts5.html")
if(NOT CMAKE_CROSSCOMPILING)
if(QT_SQLITE_TEST)
if(NOT SQLITE_FTS5_TEST)
message(WARNING "sqlite must be enabled with FTS5. See: https://www.sqlite.org/fts5.html")
endif()
else()
message(WARNING "The Qt sqlite driver test failed.")
endif()
endif()
if(NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
if(USE_TAGLIB AND TAGLIB_FOUND AND NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
message(WARNING "There is a critical bug in TagLib (1.11.1) that can result in corrupt Ogg files, see: https://github.com/taglib/taglib/issues/864, please consider updating TagLib to the newest version.")
endif()

View File

@@ -2,6 +2,60 @@ Strawberry Music Player
=======================
ChangeLog
1.0.0:
Bugfixes:
* Fix updating temporary metadata when reloading songs outside of the collection.
* Don't strip off "Live" from song title when sending scrobbles.
* Fix incorrect use of QFutureWatcher.
* Fix compile of Utilities::Hmac with Qt 6.2.
* Fix a memory leak when using right click context menu in internet search.
* Fix a gstreamer bus leak when adding streams and remote playlists.
* Fix "Source ID x was not found when attempting to remove it" error.
* Escape ampersands in playlist tabs.
* Fix analyzer with S24_32LE audio format.
* (macOS) Fix incorrect playlist alternating row colors with dark theme.
* (Windows) Fix adding songs with Japanese characters from the files tab.
Enhancements:
* Add replaygain fallback gain setting.
* Add option to turn off playlist alternating row colors.
* Make the default tabbbar background color lighter.
* Remove use of deprecated WinExtras Qt module.
* Add CMake test for Qt sqlite support.
* Automatically detect Qt version if BUILD_WITH_QT5 or BUILD_WITH_QT6 is not specified.
* Correct playlist tabbar favorite tooltip from "click" to "double-click".
* Remove scroll over icon to change track option since it does not work reliable.
* Improve resume playback on startup.
* Re-request stream URL for Tidal and QObuz when resuming playback after pausing for more than 30 seconds.
* Add Finnish, Ukrainian, Dutch, Japanese, Chinese, Catalan and Portuguese (Brazil).
* Add support for TagParser (https://github.com/Martchus/tagparser) as an alternative to TagLib.
* Add Subsonic option to turn off HTTP/2.
* Fix minor Clang-Tidy and Clazy warnings.
* Use higher resolution images from last.fm API.
* Add MD5 token authentication for Subsonic.
* Use 500 albums per request when receiving albums from Subsonic.
* Use QX11Application with Qt >= 6.2 for X11 global shortcuts.
* Allow fading when a ALSA PCM device is selected.
* Store Tidal MPEG-DASH file in data uri.
* Use XSPF image elements as manually set artwork.
* Make error dialog larger.
* Show error dialog for failed SQL queries.
* Show error dialog when failing to read or write album covers.
* Add module music formats (mod, s3m, xm, it) to detected filetypes.
* Disable gapless playback for module music formats to workaround gstreamer bug.
* Update directory ID and song path immediately when organizing collection songs.
* Add right click option to star a playlist in playlist tabs.
* Use seconds instead of minutes for scrobble submit delay.
* (macOS) Build with libgpod.
* (Windows) Fix compile with MSVC.
New features:
* Add ALSA PCM devices.
* Add song fingerprinting and tracking.
* Add support for native global shortcuts on MATE.
* Add radios view with channels from Radio Paradise and SomaFM.
0.9.3:
Bugfixes:
@@ -108,8 +162,8 @@ ChangeLog
* Add support for native global shortcuts on KDE.
* Add track progress in system tray icon as an option.
* Only strip problematic characters in suggested filename when saving a playlist to file.
* Change star/unstar playlist to doubleclick instead of singleclick.
* Don't edit playlist name on doubleclick in playlists view.
* Change star/unstar playlist to double-click instead of singleclick.
* Don't edit playlist name on double-click in playlists view.
* Make context view top label text selectable.
* Add setting to change Qt style.
* Clear ID3v3 tags that are empty, and clear ID3v1 tags when setting ID3v3 tags.
@@ -304,7 +358,7 @@ ChangeLog
0.6.10:
Bugfixes:
* Fixed Subsonic album covers not working for albums with non ASCII charcters.
* Fixed Subsonic album covers not working for albums with non ASCII characters.
* Fixed reading date and genre from individual tracks in CUE sheets.
* Fixed resume playback on startup for CUE songs.
* Fixed album cover manager not showing complete album titles in the list of album covers.
@@ -313,7 +367,7 @@ ChangeLog
* Fixed engine and device in context using too large icons when icons were loaded from the system theme.
* Fixed "Secure connection setup failed" problem on Windows when playing streams.
* Fixed margin for song title text in context.
* Fixed UNC paths with non ASCII charcters not working.
* Fixed UNC paths with non ASCII characters not working.
Enhancements:
* Allowing all characters except slash and backslash when organising music unless options to strip characters is checked.
@@ -328,9 +382,9 @@ ChangeLog
* Only showing song length in context when available.
* Sort album cover search results by score and pick the best 3 first before trying others to improve album cover search speed.
* Make scrobbler work for streams.
* Added search for lyrics as a seperate option in context.
* Added search for lyrics as a separate option in context.
* Made font and font sizes in context configurable.
* Splitting artist and song title to the relevant metadata when artist and song title is sent as title seperated by a dash in streams.
* Splitting artist and song title to the relevant metadata when artist and song title is sent as title separated by a dash in streams.
* Added label to show collection pixmap disk cache used in settings.
* Icreased default collection pixmap disk cache to 360.

View File

@@ -1,7 +1,8 @@
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/C/C++%20CI/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
[![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
[![Patreon](https://img.shields.io/badge/patreon-donate-green.svg)](https://patreon.com/jonaskvinge)
=======================
[![Sponsor](https://img.shields.io/badge/-Sponsor-green?logo=github)](https://github.com/sponsors/jonaski)
[![Patreon](https://img.shields.io/badge/patreon-donate-green.svg)](https://patreon.com/jonaskvinge)
[![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt toolkit.
@@ -44,14 +45,14 @@ Funding developers is a way to contribute to open source projects you appreciate
* Playlist management
* Smart and dynamic playlists
* Advanced audio output and device configuration for bit-perfect playback on Linux
* Edit tags on music files
* Edit tags on audio files
* Fetch tags from MusicBrainz
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
* Song lyrics from [AudD](https://audd.io/), [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/) and [lololyrics.com](https://www.lololyrics.com/)
* Support for multiple backends
* Audio analyzer
* Audio equalizer
* Transfer music to iPod, MTP or mass-storage USB player
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
* Subsonic, Tidal and Qobuz streaming support
@@ -64,29 +65,28 @@ It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
To build Strawberry from source you need the following installed on your system with the additional development packages/headers:
* [CMake and Make tools](https://cmake.org/)
* [CMake](https://cmake.org/)
* [GNU Make](https://www.gnu.org/software/make/)
* [GCC](https://gcc.gnu.org/) or [clang](https://clang.llvm.org/) compiler
* [Boost](https://www.boost.org/)
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
* [GLib](https://developer.gnome.org/glib/)
* [Protobuf](https://developers.google.com/protocol-buffers/)
* [Qt 5.8 or higher (or Qt 6) with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
* [Qt components X11Extras and D-Bus for Linux/BSD and WinExtras for Windows](https://www.qt.io/)
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
* [Chromaprint](https://acoustid.org/chromaprint)
* [ALSA (linux)](https://www.alsa-project.org/)
* [D-Bus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
* [ALSA (Linux required)](https://www.alsa-project.org/)
* [D-Bus (Linux required)](https://www.freedesktop.org/wiki/Software/dbus/)
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
* [GnuTLS](https://www.gnutls.org/)
* [TagLib](https://www.taglib.org/)
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
Optional dependencies:
* Song fingerprinting and MusicBrainz tagging: [Chromaprint](https://acoustid.org/chromaprint)
* Moodbar: [fftw3](http://www.fftw.org/)
* PulseAudio integration: [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
* Moodbar: [fftw3](http://www.fftw.org/)
Either GStreamer or VLC engine is required, but only GStreamer is fully implemented, and works best, it is therefore recommended to use GStreamer.
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav.

View File

@@ -1,11 +1,10 @@
#find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
#if(MACDEPLOYQT_EXECUTABLE)
# message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
#else()
# message(WARNING "Missing macdeployqt executable.")
#endif()
set(MACDEPLOYQT_EXECUTABLE "${CMAKE_BINARY_DIR}/3rdparty/macdeployqt/macdeployqt")
if(MACDEPLOYQT_EXECUTABLE)
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
else()
message(WARNING "Missing macdeployqt executable.")
endif()
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
if(CREATEDMG_EXECUTABLE)
@@ -14,24 +13,30 @@ else()
message(WARNING "Missing create-dmg executable.")
endif()
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macversion.sh OUTPUT_VARIABLE MACOS_VERSION_PACKAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT MACOS_VERSION_PACKAGE)
message(WARNING "Could not set macOS version.")
endif()
if(MACDEPLOYQT_EXECUTABLE AND CREATEDMG_EXECUTABLE AND MACOS_VERSION_PACKAGE)
add_custom_target(dmg
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
if(MACDEPLOYQT_EXECUTABLE)
add_custom_target(copy_gstreamer_plugins
#COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh strawberry.app
)
add_custom_target(dmg2
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
COMMAND ${CREATEDMG_EXECUTABLE} --skip-jenkins --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${MACOS_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
add_custom_target(deploy
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3
-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gio-modules/libgiognutls.so
#-executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS strawberry strawberry-tagreader copy_gstreamer_plugins macdeployqt
)
add_custom_target(deploycheck
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
DEPENDS macdeploycheck
)
if(CREATEDMG_EXECUTABLE)
add_custom_target(dmg
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS deploy deploycheck
)
endif()
endif()

View File

@@ -15,11 +15,7 @@ macro(optional_source TOGGLE)
list(APPEND OTHER_SOURCES ${OPTIONAL_SOURCE_HEADERS})
set(_uic_sources)
if(BUILD_WITH_QT6)
qt6_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
else()
qt5_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
endif()
qt_wrap_ui(_uic_sources ${OPTIONAL_SOURCE_UI})
list(APPEND OTHER_SOURCES ${_uic_sources})
list(APPEND OTHER_UIC_SOURCES ${_uic_sources})
endif(${TOGGLE})

View File

@@ -75,10 +75,6 @@ macro(add_po outfiles po_prefix)
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
endforeach(_lang)
file(APPEND ${_qrc} "</qresource></RCC>")
if(BUILD_WITH_QT6)
qt6_add_resources(${outfiles} ${_qrc})
else()
qt5_add_resources(${outfiles} ${_qrc})
endif()
qt_add_resources(${outfiles} ${_qrc})
endif()
endmacro(add_po)

View File

@@ -1,6 +1,6 @@
set(STRAWBERRY_VERSION_MAJOR 0)
set(STRAWBERRY_VERSION_MINOR 9)
set(STRAWBERRY_VERSION_PATCH 3)
set(STRAWBERRY_VERSION_MAJOR 1)
set(STRAWBERRY_VERSION_MINOR 0)
set(STRAWBERRY_VERSION_PATCH 0)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF)

View File

@@ -14,6 +14,8 @@
<file>schema/schema-11.sql</file>
<file>schema/schema-12.sql</file>
<file>schema/schema-13.sql</file>
<file>schema/schema-14.sql</file>
<file>schema/schema-15.sql</file>
<file>schema/device-schema.sql</file>
<file>style/strawberry.css</file>
<file>style/smartplaylistsearchterm.css</file>

View File

@@ -92,6 +92,9 @@
<file>icons/128x128/tidal.png</file>
<file>icons/128x128/qobuz.png</file>
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
<file>icons/128x128/radio.png</file>
<file>icons/128x128/somafm.png</file>
<file>icons/128x128/radioparadise.png</file>
<file>icons/64x64/albums.png</file>
<file>icons/64x64/alsa.png</file>
<file>icons/64x64/application-exit.png</file>
@@ -185,6 +188,9 @@
<file>icons/64x64/tidal.png</file>
<file>icons/64x64/qobuz.png</file>
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
<file>icons/64x64/radio.png</file>
<file>icons/64x64/somafm.png</file>
<file>icons/64x64/radioparadise.png</file>
<file>icons/48x48/albums.png</file>
<file>icons/48x48/alsa.png</file>
<file>icons/48x48/application-exit.png</file>
@@ -282,6 +288,9 @@
<file>icons/48x48/tidal.png</file>
<file>icons/48x48/qobuz.png</file>
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
<file>icons/48x48/radio.png</file>
<file>icons/48x48/somafm.png</file>
<file>icons/48x48/radioparadise.png</file>
<file>icons/32x32/albums.png</file>
<file>icons/32x32/alsa.png</file>
<file>icons/32x32/application-exit.png</file>
@@ -379,6 +388,9 @@
<file>icons/32x32/tidal.png</file>
<file>icons/32x32/qobuz.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
<file>icons/32x32/radio.png</file>
<file>icons/32x32/somafm.png</file>
<file>icons/32x32/radioparadise.png</file>
<file>icons/22x22/albums.png</file>
<file>icons/22x22/alsa.png</file>
<file>icons/22x22/application-exit.png</file>
@@ -476,5 +488,8 @@
<file>icons/22x22/tidal.png</file>
<file>icons/22x22/qobuz.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
<file>icons/22x22/radio.png</file>
<file>icons/22x22/somafm.png</file>
<file>icons/22x22/radioparadise.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,35 +1,35 @@
CREATE TABLE device_%deviceid_directories (
path TEXT NOT NULL,
path TEXT NOT NULL DEFAULT '',
subdirs INTEGER NOT NULL
);
CREATE TABLE device_%deviceid_subdirectories (
directory_id INTEGER NOT NULL,
path TEXT NOT NULL,
path TEXT NOT NULL DEFAULT '',
mtime INTEGER NOT NULL
);
CREATE TABLE device_%deviceid_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -40,29 +40,32 @@ CREATE TABLE device_%deviceid_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -77,4 +80,4 @@ CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
tokenize = "unicode61 remove_diacritics 1"
);
UPDATE devices SET schema_version=2 WHERE ROWID=%deviceid;
UPDATE devices SET schema_version=3 WHERE ROWID=%deviceid;

View File

@@ -0,0 +1,5 @@
ALTER TABLE %allsongstables ADD COLUMN fingerprint TEXT DEFAULT '';
ALTER TABLE %allsongstables ADD COLUMN lastseen INTEGER NOT NULL DEFAULT -1;
UPDATE schema_version SET version=14;

View File

@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS radio_channels (
source INTEGER NOT NULL DEFAULT 0,
name TEXT DEFAULT '',
url TEXT DEFAULT '',
thumbnail_url TEXT DEFAULT ''
);
UPDATE schema_version SET version=15;

View File

@@ -4,40 +4,40 @@ CREATE TABLE IF NOT EXISTS schema_version (
DELETE FROM schema_version;
INSERT INTO schema_version (version) VALUES (13);
INSERT INTO schema_version (version) VALUES (15);
CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL,
path TEXT NOT NULL DEFAULT '',
subdirs INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS subdirectories (
directory_id INTEGER NOT NULL,
path TEXT NOT NULL,
path TEXT NOT NULL DEFAULT '',
mtime INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -48,29 +48,32 @@ CREATE TABLE IF NOT EXISTS songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -78,25 +81,25 @@ CREATE TABLE IF NOT EXISTS songs (
CREATE TABLE IF NOT EXISTS subsonic_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -107,29 +110,32 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -137,25 +143,25 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -166,29 +172,32 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -196,25 +205,25 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -225,29 +234,32 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -255,25 +267,25 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
CREATE TABLE IF NOT EXISTS tidal_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -284,29 +296,32 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -314,25 +329,25 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -343,29 +358,32 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -373,25 +391,25 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -402,29 +420,32 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -432,25 +453,25 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
CREATE TABLE IF NOT EXISTS qobuz_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -461,29 +482,32 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
@@ -491,15 +515,15 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
CREATE TABLE IF NOT EXISTS playlists (
name TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
last_played INTEGER NOT NULL DEFAULT -1,
ui_order INTEGER NOT NULL DEFAULT 0,
special_type TEXT,
ui_path TEXT,
special_type TEXT DEFAULT '',
ui_path TEXT DEFAULT '',
is_favorite INTEGER NOT NULL DEFAULT 0,
dynamic_playlist_type INTEGER,
dynamic_playlist_backend TEXT,
dynamic_playlist_backend TEXT DEFAULT '',
dynamic_playlist_data BLOB
);
@@ -509,27 +533,27 @@ CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL,
type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER,
playlist_url TEXT,
playlist_url TEXT DEFAULT '',
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
track INTEGER,
disc INTEGER,
year INTEGER,
originalyear INTEGER,
genre TEXT,
genre TEXT DEFAULT '',
compilation INTEGER DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
beginning INTEGER,
length INTEGER,
@@ -540,44 +564,54 @@ CREATE TABLE IF NOT EXISTS playlist_items (
source INTEGER,
directory_id INTEGER,
url TEXT,
url TEXT DEFAULT '',
filetype INTEGER,
filesize INTEGER,
mtime INTEGER,
ctime INTEGER,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER DEFAULT 0,
skipcount INTEGER DEFAULT 0,
lastplayed INTEGER DEFAULT 0,
lastplayed INTEGER DEFAULT -1,
lastseen INTEGER DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER DEFAULT 0,
compilation_off INTEGER DEFAULT 0,
compilation_effective INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
effective_albumartist TEXT,
effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER,
cue_path TEXT,
cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1
);
CREATE TABLE IF NOT EXISTS devices (
unique_id TEXT NOT NULL,
friendly_name TEXT,
unique_id TEXT NOT NULL DEFAULT '',
friendly_name TEXT DEFAULT '',
size INTEGER,
icon TEXT,
icon TEXT DEFAULT '',
schema_version INTEGER NOT NULL DEFAULT 0,
transcode_mode NOT NULL DEFAULT 3,
transcode_format NOT NULL DEFAULT 5
);
CREATE TABLE IF NOT EXISTS radio_channels (
source INTEGER NOT NULL DEFAULT 0,
name TEXT DEFAULT '',
url TEXT DEFAULT '',
thumbnail_url TEXT DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_url ON songs (url);
CREATE INDEX IF NOT EXISTS idx_comp_artist ON songs (compilation_effective, artist);

13
debian/control vendored
View File

@@ -17,6 +17,7 @@ Build-Depends: debhelper (>= 11),
libpulse-dev,
libtag1-dev,
qtbase5-dev,
qtbase5-private-dev,
qtbase5-dev-tools,
qttools5-dev,
libqt5x11extras5-dev,
@@ -41,24 +42,26 @@ Depends: ${shlibs:Depends},
gstreamer1.0-pulseaudio
Homepage: http://www.strawberrymusicplayer.org/
Description: Audio player and music collection organizer
Strawberry is a music player aimed at music collectors, audio enthusiasts and audiophiles.
Strawberry is a music player aimed at music collectors and audiophiles.
.
Features:
- Play and organize music
- Supports WAV, FLAC, WavPack, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3 and ASF
- Audio CD playback
- Native desktop notifications
- Playlist management
- Playlists in multiple formats
- Advanced audio output and device configuration for bit-perfect playback on Linux
- Edit tags on music files
- Fetch tags from MusicBrainz
- Edit tags on audio files
- Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Support for multiple backends
- Audio analyzer
- Audio equalizer
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
- Streaming support for Subsonic
- Streaming support for Subsonic-compatible servers
- Unofficial streaming support for Tidal and Qobuz
.
It is a fork of Clementine. The name is inspired by the band Strawbs.

20
dist/CMakeLists.txt vendored
View File

@@ -7,24 +7,24 @@ if(DEB_CODENAME AND DEB_DATE)
endif(DEB_CODENAME AND DEB_DATE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
if (APPLE)
if(APPLE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
endif (APPLE)
endif(APPLE)
if (WIN32)
if(WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY)
endif (WIN32)
endif(WIN32)
if (UNIX AND NOT APPLE)
if(UNIX AND NOT APPLE)
install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/)
install(FILES ../data/icons/64x64/strawberry.png DESTINATION share/icons/hicolor/64x64/apps/)
install(FILES ../data/icons/128x128/strawberry.png DESTINATION share/icons/hicolor/128x128/apps/)
install(FILES unix/org.strawberrymusicplayer.strawberry.desktop DESTINATION share/applications)
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
install(FILES unix/strawberry.1 unix/strawberry-tagreader.1 DESTINATION share/man/man1)
endif (UNIX AND NOT APPLE)
endif(UNIX AND NOT APPLE)
if (APPLE)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
endif (APPLE)
if(APPLE)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
endif()

107
dist/macos/macgstcopy.sh vendored Executable file
View File

@@ -0,0 +1,107 @@
#!/bin/sh
# Script to copy gstreamer plugins before macdeployqt is run.
if [ "$1" = "" ]; then
echo "Usage: $0 <bundledir>"
exit 1
fi
bundledir=$1
if [ "$GIO_EXTRA_MODULES" = "" ]; then
echo "Error: Set the GIO_EXTRA_MODULES environment variable to the path containing libgiognutls.so."
exit 1
fi
if [ "$GST_PLUGIN_SCANNER" = "" ]; then
echo "Error: Set the GST_PLUGIN_SCANNER environment variable to the gst-plugin-scanner."
exit 1
fi
if [ "$GST_PLUGIN_PATH" = "" ]; then
echo "Error: Set the GST_PLUGIN_PATH environment variable to the path containing gstreamer plugins."
exit 1
fi
mkdir -p "${bundledir}/Contents/PlugIns/gio-modules" || exit 1
mkdir -p "${bundledir}/Contents/PlugIns/gstreamer" || exit 1
if ! [ -f "${GIO_EXTRA_MODULES}/libgiognutls.so" ]; then
echo "Error: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so."
exit 1
fi
cp -v -f "${GIO_EXTRA_MODULES}/libgiognutls.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
if ! [ -f "${GST_PLUGIN_SCANNER}" ]; then
echo "Error: Missing ${GST_PLUGIN_SCANNER}"
exit 1
fi
cp -v -f "${GST_PLUGIN_SCANNER}" "${bundledir}/Contents/PlugIns/" || exit 1
gst_plugins="
libgstapetag.dylib
libgstapp.dylib
libgstaudioconvert.dylib
libgstaudiofx.dylib
libgstaudiomixer.dylib
libgstaudioparsers.dylib
libgstaudiorate.dylib
libgstaudioresample.dylib
libgstaudiotestsrc.dylib
libgstaudiovisualizers.dylib
libgstauparse.dylib
libgstautoconvert.dylib
libgstautodetect.dylib
libgstcoreelements.dylib
libgstequalizer.dylib
libgstgio.dylib
libgsticydemux.dylib
libgstid3demux.dylib
libgstlevel.dylib
libgstosxaudio.dylib
libgstplayback.dylib
libgstrawparse.dylib
libgstreplaygain.dylib
libgstsoup.dylib
libgstspectrum.dylib
libgsttypefindfunctions.dylib
libgstvolume.dylib
libgstxingmux.dylib
libgsttcp.dylib
libgstudp.dylib
libgstpbtypes.dylib
libgstrtp.dylib
libgstrtsp.dylib
libgstflac.dylib
libgstwavparse.dylib
libgstfaad.dylib
libgstogg.dylib
libgstopus.dylib
libgstasf.dylib
libgstspeex.dylib
libgsttaglib.dylib
libgstvorbis.dylib
libgstisomp4.dylib
libgstlibav.dylib
libgstaiff.dylib
libgstlame.dylib
libgstopusparse.dylib
libgstfaac.dylib
libgstmusepack.dylib
"
gst_plugins=$(echo "$gst_plugins" | tr '\n' ' ' | sed -e 's/^ //g' | sed -e 's/ / /g')
for gst_plugin in $gst_plugins
do
if [ -f "${GST_PLUGIN_PATH}/${gst_plugin}" ]; then
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
else
echo "Warning: Missing gstreamer plugin ${GST_PLUGIN_PATH}/${gst_plugin}"
fi
done
if [ -f "/usr/local/lib/libbrotlicommon.1.dylib" ]; then
mkdir -p ${bundledir}/Contents/Frameworks
cp -v -f "/usr/local/lib/libbrotlicommon.1.dylib" "${bundledir}/Contents/Frameworks/"
fi

View File

@@ -15,34 +15,36 @@
<translation type="qt">strawberry</translation>
<description>
<p>
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles. The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.
Strawberry is a music player and music collection organizer. It is aimed at music collectors and audiophiles. With Strawberry you can play and manage your digital music collection, or stream your favorite radios. It also has unofficial streaming support for Tidal and Qobuz. Strawberry is free software released under GPL. The source code is available on GitHub. It's written in C++ using the Qt toolkit and GStreamer. Strawberry is compatible with both Qt version 5 and 6.
</p>
<p>Features:</p>
<ul>
<li>Play and organize music</li>
<li>Supports most popular audio formats and CD playback</li>
<li>Native desktop notifications</li>
<li>Playlists in multiple formats</li>
<li>Playlist management and playlists in multiple formats</li>
<li>Smart and dynamic playlists</li>
<li>Advanced audio output and device configuration for bit-perfect playback on Linux</li>
<li>Edit tags on music files</li>
<li>Fetch tags from MusicBrainz</li>
<li>Edit tags on audio files</li>
<li>Automatically retrieve tags from MusicBrainz</li>
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
<li>Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com</li>
<li>Support for multiple backends</li>
<li>Audio analyzer and equalizer</li>
<li>Transfer music to iPod, iPhone, MTP or mass-storage USB player</li>
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
<li>Scrobbler with support for Last.fm, Libre.fm and ListenBrainz</li>
<li>Streaming support for Subsonic</li>
<li>Streaming support for Subsonic-compatible servers</li>
<li>Unofficial streaming support for Tidal and Qobuz</li>
</ul>
</description>
<screenshots>
<screenshot type="default">
<caption>Song playing showing context</caption>
<image width="1600" height="874">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-001.png</image>
<image width="1432" height="834">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-003.png</image>
</screenshot>
<screenshot>
<caption>Collection overview</caption>
<image width="1600" height="874">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-002.png</image>
<image width="1432" height="834">https://www.strawberrymusicplayer.org/pictures/appdata-screenshot-004.png</image>
</screenshot>
</screenshots>
<update_contact>eclipseo@fedoraproject.org</update_contact>

View File

@@ -5,7 +5,7 @@ Strawberry \- music player and music collection organizer
.B strawberry
[\fI\,options\/\fR] [\fI\,URL(s)\/\fR]
.SH DESCRIPTION
Strawberry is a music player especially aimed at audiophiles.
Strawberry is a music player aimed at music collectors and audiophiles.
.TP
Features:
.br
@@ -17,13 +17,15 @@ Features:
.br
- Native desktop notifications
.br
- Playlist management
.br
- Playlists in multiple formats
.br
- Advanced output and device options with support for bit perfect playback on Linux
.br
- Edit tags on music files
- Edit tags on audio files
.br
- Fetch tags from MusicBrainz
- Automatically retrieve tags from MusicBrainz
.br
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
.br
@@ -33,11 +35,15 @@ Features:
.br
- Audio analyzer
.br
- Equalizer
- Audio Equalizer
.br
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
.br
- Streaming from Subsonic
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
.br
- Streaming support for Subsonic-compatible servers
.br
- Unofficial streaming support for Tidal and Qobuz
.TP
It is a fork of Clementine. The name is inspired by the band Strawbs.
.SH OPTIONS

View File

@@ -49,28 +49,31 @@ BuildRequires: pkgconfig(sqlite3) >= 3.9
BuildRequires: pkgconfig(taglib)
%endif
BuildRequires: pkgconfig(fftw3)
%if "@QT_MAJOR_VERSION@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Core)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Gui)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Widgets)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Concurrent)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Network)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Sql)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@X11Extras)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@DBus)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Test)
%if "@QT_VERSION_MAJOR@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Core)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Gui)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Widgets)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Concurrent)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Network)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Sql)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@DBus)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Test)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@X11Extras)
%else
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Core)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Gui)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Widgets)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Concurrent)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Network)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Sql)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@DBus)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Test)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Gui)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Widgets)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Sql)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@DBus)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Test)
%if "@QT_VERSION_MAJOR@" == "5"
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@X11Extras)
%endif
%endif
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@LinguistTools)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@LinguistTools)
%endif
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
@@ -89,10 +92,11 @@ BuildRequires: pkgconfig(libvlc)
%endif
%if 0%{?suse_version}
%if "@QT_MAJOR_VERSION@" == "6"
%if "@QT_VERSION_MAJOR@" == "6"
Requires: qt6-sql-sqlite
Requires: qt6-network-tls
%endif
%if "@QT_MAJOR_VERSION@" == "5"
%if "@QT_VERSION_MAJOR@" == "5"
Requires: libQt5Sql5-sqlite
%endif
%endif
@@ -107,18 +111,20 @@ Features:
MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
- Audio CD playback
- Native desktop notifications
- Playlist management
- Playlists in multiple formats
- Advanced audio output and device configuration for bit-perfect playback on Linux
- Edit tags on music files
- Fetch tags from MusicBrainz
- Edit tags on audio files
- Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Support for multiple backends
- Audio analyzer
- Audio equalizer
- Transfer music to iPod, MTP or mass-storage USB player
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
- Streaming support for Subsonic
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
- Streaming support for Subsonic-compatible servers
- Unofficial streaming support for Tidal and Qobuz
%if 0%{?suse_version}
%debug_package
@@ -131,7 +137,7 @@ Features:
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
%endif
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_MAJOR_VERSION=@QT_MAJOR_VERSION@
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7)
%make_build
%else

View File

@@ -195,14 +195,14 @@ Section "Strawberry" Strawberry
!ifdef arch_x86
File "libgcc_s_sjlj-1.dll"
File "libcrypto-1_1.dll"
File "libssl-1_1.dll"
File "libcrypto-3.dll"
File "libssl-3.dll"
!endif
!ifdef arch_x64
File "libgcc_s_seh-1.dll"
File "libcrypto-1_1-x64.dll"
File "libssl-1_1-x64.dll"
File "libcrypto-3-x64.dll"
File "libssl-3-x64.dll"
!endif
File "avcodec-58.dll"
@@ -229,6 +229,7 @@ Section "Strawberry" Strawberry
File "libgobject-2.0-0.dll"
File "libgstapp-1.0-0.dll"
File "libgstaudio-1.0-0.dll"
File "libgstbadaudio-1.0-0.dll"
File "libgstbase-1.0-0.dll"
File "libgstfft-1.0-0.dll"
File "libgstnet-1.0-0.dll"
@@ -250,12 +251,13 @@ Section "Strawberry" Strawberry
File "libmp3lame-0.dll"
File "libnettle-8.dll"
File "libogg-0.dll"
File "libopenmpt-0.dll"
File "libopus-0.dll"
File "liborc-0.4-0.dll"
File "libpcre-1.dll"
File "libpcre2-16-0.dll"
File "libpng16-16.dll"
File "libprotobuf-26.dll"
File "libprotobuf-29.dll"
File "libpsl-5.dll"
File "libsoup-2.4-1.dll"
File "libspeex-1.dll"
@@ -267,6 +269,7 @@ Section "Strawberry" Strawberry
File "libunistring-2.dll"
File "libvorbis-0.dll"
File "libvorbisenc-2.dll"
File "libvorbisfile-3.dll"
File "libwavpack-1.dll"
File "libwinpthread-1.dll"
File "libxml2-2.dll"
@@ -283,7 +286,6 @@ Section "Strawberry" Strawberry
File "Qt6Network.dll"
File "Qt6Sql.dll"
File "Qt6Widgets.dll"
File "Qt6WinExtras.dll"
File "libqtsparkle-qt6.dll"
!else
File "Qt5Concurrent.dll"
@@ -292,7 +294,6 @@ Section "Strawberry" Strawberry
File "Qt5Network.dll"
File "Qt5Sql.dll"
File "Qt5Widgets.dll"
File "Qt5WinExtras.dll"
File "libqtsparkle-qt5.dll"
!endif
@@ -342,26 +343,33 @@ Section "GIO modules" gio-modules
File "/oname=libgiognutls.dll" "gio-modules\libgiognutls.dll"
SectionEnd
Section "Qt Platforms" platforms
Section "Qt Platform plugins" platforms
SetOutPath "$INSTDIR\platforms"
File "/oname=qwindows.dll" "platforms\qwindows.dll"
SectionEnd
Section "Qt SQL Drivers" sqldrivers
SetOutPath "$INSTDIR\sqldrivers"
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll"
Section "Qt styles" styles
SetOutPath "$INSTDIR\styles"
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
SectionEnd
Section "Qt image format plugins" imageformats
Section "Qt imageformats" imageformats
SetOutPath "$INSTDIR\imageformats"
File "/oname=qgif.dll" "imageformats\qgif.dll"
File "/oname=qico.dll" "imageformats\qico.dll"
File "/oname=qjpeg.dll" "imageformats\qjpeg.dll"
SectionEnd
Section "Qt style plugins" styles
SetOutPath "$INSTDIR\styles"
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
!ifdef with_qt6
Section "Qt TLS plugins" tls
SetOutPath "$INSTDIR\tls"
File "/oname=qopensslbackend.dll" "tls\qopensslbackend.dll"
SectionEnd
!endif
Section "Qt SQL Drivers" sqldrivers
SetOutPath "$INSTDIR\sqldrivers"
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll"
SectionEnd
Section "Gstreamer plugins" gstreamer-plugins
@@ -413,6 +421,7 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstpbtypes.dll" "gstreamer-plugins\libgstpbtypes.dll"
File "/oname=libgstrtp.dll" "gstreamer-plugins\libgstrtp.dll"
File "/oname=libgstrtsp.dll" "gstreamer-plugins\libgstrtsp.dll"
File "/oname=libgstopenmpt.dll" "gstreamer-plugins\libgstopenmpt.dll"
SectionEnd
@@ -460,14 +469,14 @@ Section "Uninstall"
!ifdef arch_x86
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libcrypto-1_1.dll"
Delete "$INSTDIR\libssl-1_1.dll"
Delete "$INSTDIR\libcrypto-3.dll"
Delete "$INSTDIR\libssl-3.dll"
!endif
!ifdef arch_x64
Delete "$INSTDIR\libgcc_s_seh-1.dll"
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
Delete "$INSTDIR\libssl-1_1-x64.dll"
Delete "$INSTDIR\libcrypto-3-x64.dll"
Delete "$INSTDIR\libssl-3-x64.dll"
!endif
Delete "$INSTDIR\avcodec-58.dll"
@@ -494,6 +503,7 @@ Section "Uninstall"
Delete "$INSTDIR\libgobject-2.0-0.dll"
Delete "$INSTDIR\libgstapp-1.0-0.dll"
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
Delete "$INSTDIR\libgstbadaudio-1.0-0.dll"
Delete "$INSTDIR\libgstbase-1.0-0.dll"
Delete "$INSTDIR\libgstfft-1.0-0.dll"
Delete "$INSTDIR\libgstnet-1.0-0.dll"
@@ -515,12 +525,13 @@ Section "Uninstall"
Delete "$INSTDIR\libmp3lame-0.dll"
Delete "$INSTDIR\libnettle-8.dll"
Delete "$INSTDIR\libogg-0.dll"
Delete "$INSTDIR\libopenmpt-0.dll"
Delete "$INSTDIR\libopus-0.dll"
Delete "$INSTDIR\liborc-0.4-0.dll"
Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-26.dll"
Delete "$INSTDIR\libprotobuf-29.dll"
Delete "$INSTDIR\libpsl-5.dll"
Delete "$INSTDIR\libqtsparkle-qt5.dll"
Delete "$INSTDIR\libqtsparkle-qt6.dll"
@@ -534,6 +545,7 @@ Section "Uninstall"
Delete "$INSTDIR\libunistring-2.dll"
Delete "$INSTDIR\libvorbis-0.dll"
Delete "$INSTDIR\libvorbisenc-2.dll"
Delete "$INSTDIR\libvorbisfile-3.dll"
Delete "$INSTDIR\libwavpack-1.dll"
Delete "$INSTDIR\libwinpthread-1.dll"
Delete "$INSTDIR\libxml2-2.dll"
@@ -545,14 +557,12 @@ Section "Uninstall"
Delete "$INSTDIR\Qt5Network.dll"
Delete "$INSTDIR\Qt5Sql.dll"
Delete "$INSTDIR\Qt5Widgets.dll"
Delete "$INSTDIR\Qt5WinExtras.dll"
Delete "$INSTDIR\Qt6Concurrent.dll"
Delete "$INSTDIR\Qt6Core.dll"
Delete "$INSTDIR\Qt6Gui.dll"
Delete "$INSTDIR\Qt6Network.dll"
Delete "$INSTDIR\Qt6Sql.dll"
Delete "$INSTDIR\Qt6Widgets.dll"
Delete "$INSTDIR\Qt6WinExtras.dll"
Delete "$INSTDIR\swresample-3.dll"
Delete "$INSTDIR\swscale-5.dll"
Delete "$INSTDIR\zlib1.dll"
@@ -568,8 +578,11 @@ Section "Uninstall"
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
Delete "$INSTDIR\styles\qwindowsvistastyle.dll"
!ifdef with_qt6
Delete "$INSTDIR\tls\qopensslbackend.dll"
!endif
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
Delete "$INSTDIR\imageformats\qgif.dll"
Delete "$INSTDIR\imageformats\qico.dll"
@@ -621,12 +634,14 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstpbtypes.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstrtp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstrtsp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstopenmpt.dll"
Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders.
RMDir "$INSTDIR\platforms"
RMDir "$INSTDIR\styles"
RMDir "$INSTDIR\tls"
RMDir "$INSTDIR\sqldrivers"
RMDir "$INSTDIR\imageformats"
RMDir "$INSTDIR\gio-modules"

View File

@@ -311,8 +311,9 @@ static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint l
#else
gint32 value = GST_READ_UINT24_LE (_in);
#endif
if (value & 0x00800000)
if (value & 0x00800000) {
value |= 0xff000000;
}
out[op] = value / max_value;
op = (op + 1) % nfft;
@@ -374,8 +375,9 @@ static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_po
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
for (i = 0; i < nfft; i++)
for (i = 0; i < nfft; i++) {
spectrum->fft_input[i] = spectrum->input_ring_buffer[(input_pos + i) % nfft];
}
// Should be safe to execute the same plan multiple times in parallel.
fftw_execute(spectrum->plan);
@@ -435,8 +437,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
/* rounding error for frames_per_interval in ns,
* aggregated it in accumulated_error */
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
if (spectrum->frames_per_interval == 0)
if (spectrum->frames_per_interval == 0) {
spectrum->frames_per_interval = 1;
}
GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
@@ -448,8 +451,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
gst_fastspectrum_flush (spectrum);
}
if (spectrum->num_frames == 0)
if (spectrum->num_frames == 0) {
spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
}
input_pos = spectrum->input_pos;
input_data = spectrum->input_data;
@@ -462,10 +466,12 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
"message frames todo: %u, fft frames todo: %u, input frames %"
G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
block_size = msg_todo;
if (block_size > (size / bpf))
if (block_size > (size / bpf)) {
block_size = (size / bpf);
if (block_size > fft_todo)
}
if (block_size > fft_todo) {
block_size = fft_todo;
}
/* Move the current frames into our ringbuffers */
input_data(data, spectrum->input_ring_buffer, block_size, max_value, input_pos, nfft);
@@ -514,8 +520,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
memset(spectrum->spect_magnitude, 0, spectrum->bands * sizeof(double));
}
if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts))
if (GST_CLOCK_TIME_IS_VALID (spectrum->message_ts)) {
spectrum->message_ts += gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
}
spectrum->num_frames = 0;
spectrum->num_fft = 0;

View File

@@ -23,7 +23,7 @@
namespace {
static gboolean gst_moodbar_plugin_init(GstPlugin* plugin) {
static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) {
if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) {
return FALSE;

View File

@@ -8,38 +8,29 @@ set(SOURCES
)
set(HEADERS
core/logging.h
core/messagehandler.h
core/messagereply.h
core/workerpool.h
)
if(APPLE)
list(APPEND SOURCES core/scoped_nsautorelease_pool.mm)
endif(APPLE)
qt_wrap_cpp(MOC ${HEADERS})
if(BUILD_WITH_QT6)
qt6_wrap_cpp(MOC ${HEADERS})
else()
qt5_wrap_cpp(MOC ${HEADERS})
endif()
link_directories(
${GLIB_LIBRARY_DIRS}
)
link_directories(${GLIB_LIBRARY_DIRS})
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
target_include_directories(libstrawberry-common SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-common SYSTEM PRIVATE ${GLIB_INCLUDE_DIRS})
target_include_directories(libstrawberry-common PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
)
if(Backtrace_FOUND)
target_include_directories(libstrawberry-common SYSTEM PRIVATE ${Backtrace_INCLUDE_DIRS})
endif()
target_link_libraries(libstrawberry-common PRIVATE
${CMAKE_THREAD_LIBS_INIT}
${GLIB_LIBRARIES}
@@ -48,6 +39,5 @@ target_link_libraries(libstrawberry-common PRIVATE
)
if(Backtrace_FOUND)
target_include_directories(libstrawberry-common PRIVATE ${Backtrace_INCLUDE_DIRS})
target_link_libraries(libstrawberry-common PRIVATE ${Backtrace_LIBRARIES})
endif(Backtrace_FOUND)
endif()

View File

@@ -24,7 +24,9 @@
#include <cstring>
#include <iostream>
#include <memory>
#include <cxxabi.h>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <glib.h>
#ifdef HAVE_BACKTRACE
@@ -54,10 +56,10 @@ static QMap<QString, Level>* sClassLevels = nullptr;
static QIODevice *sNullDevice = nullptr;
//const char* kDefaultLogLevels = "*:3";
const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3";
const char *kDefaultLogLevels = "GstEnginePipeline:2,*:3";
static const char *kMessageHandlerMagic = "__logging_message__";
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
static const size_t kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
static QtMessageHandler sOriginalMessageHandler = nullptr;
template <class T>
@@ -99,7 +101,7 @@ class DebugBase : public QDebug {
// Debug message will be stored in a buffer.
class BufferedDebug : public DebugBase<BufferedDebug> {
public:
BufferedDebug() {}
BufferedDebug() = default;
explicit BufferedDebug(QtMsgType) : buf_(new QBuffer, later_deleter) {
buf_->open(QIODevice::WriteOnly);
@@ -118,7 +120,7 @@ class BufferedDebug : public DebugBase<BufferedDebug> {
// Debug message will be logged immediately.
class LoggedDebug : public DebugBase<LoggedDebug> {
public:
LoggedDebug() {}
LoggedDebug() = default;
explicit LoggedDebug(QtMsgType t) : DebugBase(t) { nospace() << kMessageHandlerMagic; }
};
@@ -178,7 +180,7 @@ void SetLevels(const QString &levels) {
if (!sClassLevels) return;
for (const QString& item : levels.split(',')) {
for (const QString &item : levels.split(',')) {
const QStringList class_level = item.split(':');
QString class_name;
@@ -224,7 +226,7 @@ static QString ParsePrettyFunction(const char *pretty_function) {
const int space = class_name.lastIndexOf(' ');
if (space != -1) {
class_name = class_name.mid(space+1);
class_name = class_name.mid(space + 1);
}
return class_name;
@@ -275,12 +277,12 @@ static T CreateLogger(Level level, const QString &class_name, int line, const ch
return ret.space();
}
#ifdef Q_OS_UNIX
QString CXXDemangle(const QString &mangled_function);
QString CXXDemangle(const QString &mangled_function) {
int status = 0;
char* demangled_function = abi::__cxa_demangle(mangled_function.toLatin1().constData(), nullptr, nullptr, &status);
char *demangled_function = abi::__cxa_demangle(mangled_function.toLatin1().constData(), nullptr, nullptr, &status);
if (status == 0) {
QString ret = QString::fromLatin1(demangled_function);
free(demangled_function);
@@ -289,9 +291,24 @@ QString CXXDemangle(const QString &mangled_function) {
return mangled_function; // Probably not a C++ function.
}
#endif // Q_OS_UNIX
#ifdef Q_OS_LINUX
QString LinuxDemangle(const QString &symbol);
QString LinuxDemangle(const QString &symbol) {
QRegularExpression regex("\\(([^+]+)");
QRegularExpressionMatch match = regex.match(symbol);
if (!match.hasMatch()) {
return symbol;
}
QString mangled_function = match.captured(1);
return CXXDemangle(mangled_function);
}
#endif // Q_OS_LINUX
#ifdef Q_OS_MACOS
QString DarwinDemangle(const QString &symbol);
QString DarwinDemangle(const QString &symbol) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
@@ -303,23 +320,9 @@ QString DarwinDemangle(const QString &symbol) {
return CXXDemangle(mangled_function);
}
QString LinuxDemangle(const QString &symbol);
QString LinuxDemangle(const QString &symbol) {
QRegularExpression regex("\\(([^+]+)");
QRegularExpressionMatch match = regex.match(symbol);
if (!match.hasMatch()) {
return symbol;
}
QString mangled_function = match.captured(1);
return CXXDemangle(mangled_function);
}
#endif // Q_OS_MACOS
QString DemangleSymbol(const QString &symbol);
QString DemangleSymbol(const QString &symbol) {
#ifdef Q_OS_MACOS
return DarwinDemangle(symbol);
@@ -332,9 +335,9 @@ QString DemangleSymbol(const QString &symbol) {
void DumpStackTrace() {
#ifdef HAVE_BACKTRACE
void* callstack[128];
void *callstack[128];
int callstack_size = backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack));
char** symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
char **symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
// Start from 1 to skip ourself.
for (int i = 1; i < callstack_size; ++i) {
std::cerr << DemangleSymbol(QString::fromLatin1(symbols[i])).toStdString() << std::endl;
@@ -350,9 +353,9 @@ void DumpStackTrace() {
// doesn't override any behavior that should be needed after return.
#define qCreateLogger(line, pretty_function, category, level) logging::CreateLogger<LoggedDebug>(logging::Level_##level, logging::ParsePrettyFunction(pretty_function), line, category)
QDebug CreateLoggerInfo(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Info); }
QDebug CreateLoggerFatal(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Fatal); }
QDebug CreateLoggerError(int line, const char *pretty_function, const char* category) { return qCreateLogger(line, pretty_function, category, Error); }
QDebug CreateLoggerInfo(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Info); }
QDebug CreateLoggerFatal(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Fatal); }
QDebug CreateLoggerError(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Error); }
#ifdef QT_NO_WARNING_OUTPUT
QNoDebug CreateLoggerWarning(int, const char*, const char*) { return QNoDebug(); }
@@ -371,7 +374,7 @@ QDebug CreateLoggerError(int line, const char *pretty_function, const char* cate
namespace {
template <typename T>
QString print_duration(T duration, const std::string& unit) {
QString print_duration(T duration, const std::string &unit) {
return QString("%1%2").arg(duration.count()).arg(unit.c_str());
}

View File

@@ -29,18 +29,31 @@
# define qLog(level) while (false) QNoDebug()
# define qLogCat(level, category) while (false) QNoDebug()
#else
# define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, nullptr)
# ifdef _MSC_VER
# define qLog(level) logging::CreateLogger##level(__LINE__, __FUNCSIG__, nullptr)
# else
# define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, nullptr)
# endif // _MSC_VER
// This macro specifies a separate category for message filtering.
// The default qLog will use the class name extracted from the function name for this purpose.
// The category is also printed in the message along with the class name.
# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, category)
# ifdef _MSC_VER
# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __FUNCSIG__, category)
# else
# define qLogCat(level, category) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__, category)
# endif // _MSC_VER
#endif // QT_NO_DEBUG_STREAM
namespace logging {
class NullDevice : public QIODevice {
Q_OBJECT
public:
NullDevice(QObject *parent = nullptr) : QIODevice(parent) {}
protected:
qint64 readData(char*, qint64) override { return -1; }
qint64 writeData(const char*, qint64 len) override { return len; }

View File

@@ -64,7 +64,7 @@ void _MessageHandlerBase::SetDevice(QIODevice *device) {
void _MessageHandlerBase::DeviceReadyRead() {
while (device_->bytesAvailable()) {
while (device_->bytesAvailable() > 0) {
if (!reading_protobuf_) {
// Read the length of the next message
QDataStream s(device_);

View File

@@ -130,7 +130,7 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
template <typename MT>
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
std::string data = message.SerializeAsString();
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
QMetaObject::invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
}
template<typename MT>

View File

@@ -164,8 +164,9 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4);
local_server_name_ = qApp->applicationName().toLower();
if (local_server_name_.isEmpty())
if (local_server_name_.isEmpty()) {
local_server_name_ = "workerpool";
}
}
@@ -218,7 +219,7 @@ void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name)
template <typename HandlerType>
void WorkerPool<HandlerType>::Start() {
metaObject()->invokeMethod(this, "DoStart");
QMetaObject::invokeMethod(this, "DoStart");
}
template <typename HandlerType>
@@ -232,9 +233,9 @@ void WorkerPool<HandlerType>::DoStart() {
executable_path_ = executable_name_;
QStringList search_path;
search_path << qApp->applicationDirPath();
search_path << QCoreApplication::applicationDirPath();
#if defined(Q_OS_MACOS) && defined(USE_BUNDLE)
search_path << qApp->applicationDirPath() + "/" + USE_BUNDLE_DIR;
search_path << QCoreApplication::applicationDirPath() + "/" + USE_BUNDLE_DIR;
#endif
for (const QString &path_prefix : search_path) {
@@ -381,7 +382,7 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
}
// Wake up the main thread
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
return reply;

View File

@@ -1,22 +1,36 @@
cmake_minimum_required(VERSION 3.0)
set(MESSAGES tagreadermessages.proto)
set(SOURCES tagreader.cpp)
set(SOURCES tagreaderbase.cpp)
if(USE_TAGLIB AND TAGLIB_FOUND)
list(APPEND SOURCES tagreadertaglib.cpp)
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
list(APPEND SOURCES tagreadertagparser.cpp)
endif()
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
link_directories(
${GLIB_LIBRARY_DIRS}
${PROTOBUF_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
)
if(USE_TAGLIB AND TAGLIB_FOUND)
link_directories(${TAGLIB_LIBRARY_DIRS})
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
link_directories(${TAGPARSER_LIBRARY_DIRS})
endif()
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-tagreader PRIVATE
@@ -30,8 +44,17 @@ target_include_directories(libstrawberry-tagreader PRIVATE
target_link_libraries(libstrawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
${PROTOBUF_LIBRARY}
${TAGLIB_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
libstrawberry-common
)
if(USE_TAGLIB AND TAGLIB_FOUND)
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
endif()

View File

@@ -0,0 +1,27 @@
/* This file is part of Strawberry.
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 "config.h"
#include <string>
#include "tagreaderbase.h"
const std::string TagReaderBase::kEmbeddedCover = "(embedded)";
TagReaderBase::TagReaderBase() = default;
TagReaderBase::~TagReaderBase() = default;

View File

@@ -0,0 +1,53 @@
/* This file is part of Strawberry.
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 TAGREADERBASE_H
#define TAGREADERBASE_H
#include "config.h"
#include <string>
#include <QByteArray>
#include <QString>
#include "tagreadermessages.pb.h"
/*
* This class holds all useful methods to read and write tags from/to files.
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
*/
class TagReaderBase {
public:
explicit TagReaderBase();
~TagReaderBase();
virtual bool IsMediaFile(const QString &filename) const = 0;
virtual void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
virtual bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
virtual QByteArray LoadEmbeddedArt(const QString &filename) const = 0;
virtual bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) = 0;
protected:
static const std::string kEmbeddedCover;
Q_DISABLE_COPY(TagReaderBase)
};
#endif // TAGREADERBASE_H

View File

@@ -23,6 +23,10 @@ message SongMetadata {
DSDIFF = 15;
PCM = 16;
APE = 17;
MOD = 18;
S3M = 19;
XM = 20;
IT = 21;
CDDA = 90;
STREAM = 91;
}
@@ -60,10 +64,11 @@ message SongMetadata {
optional int32 playcount = 27;
optional int32 skipcount = 28;
optional int32 lastplayed = 29;
optional int64 lastplayed = 29;
optional int64 lastseen = 30;
optional bool suspicious_tags = 30;
optional string art_automatic = 31;
optional bool suspicious_tags = 31;
optional string art_automatic = 32;
}

View File

@@ -18,11 +18,10 @@
#include "config.h"
#include "tagreader.h"
#include "tagreadertaglib.h"
#include <string>
#include <memory>
#include <list>
#include <map>
#include <sys/stat.h>
#include <taglib/taglib.h>
@@ -68,6 +67,10 @@
#include <taglib/opusfile.h>
#include <taglib/trueaudiofile.h>
#include <taglib/apefile.h>
#include <taglib/modfile.h>
#include <taglib/s3mfile.h>
#include <taglib/xmfile.h>
#include <taglib/itfile.h>
#ifdef HAVE_TAGLIB_DSFFILE
# include <taglib/dsffile.h>
#endif
@@ -78,13 +81,11 @@
#include <QtGlobal>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QVector>
#include <QByteArray>
#include <QDateTime>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QDateTime>
#include <QtDebug>
#include "core/logging.h"
@@ -93,12 +94,17 @@
class FileRefFactory {
public:
virtual ~FileRefFactory() {}
FileRefFactory() = default;
virtual ~FileRefFactory() = default;
virtual TagLib::FileRef *GetFileRef(const QString &filename) = 0;
private:
Q_DISABLE_COPY(FileRefFactory)
};
class TagLibFileRefFactory : public FileRefFactory {
public:
TagLibFileRefFactory() = default;
TagLib::FileRef *GetFileRef(const QString &filename) override {
#ifdef Q_OS_WIN32
return new TagLib::FileRef(filename.toStdWString().c_str());
@@ -106,6 +112,8 @@ class TagLibFileRefFactory : public FileRefFactory {
return new TagLib::FileRef(QFile::encodeName(filename).constData());
#endif
}
private:
Q_DISABLE_COPY(TagLibFileRefFactory)
};
namespace {
@@ -128,25 +136,22 @@ const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear";
} // namespace
TagReader::TagReader() :
factory_(new TagLibFileRefFactory),
kEmbeddedCover("(embedded)") {
}
TagReaderTagLib::TagReaderTagLib() : factory_(new TagLibFileRefFactory) {}
TagReader::~TagReader() {
TagReaderTagLib::~TagReaderTagLib() {
delete factory_;
}
bool TagReader::IsMediaFile(const QString &filename) const {
bool TagReaderTagLib::IsMediaFile(const QString &filename) const {
qLog(Debug) << "Checking for valid file" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
return !fileref->isNull() && fileref->tag();
return fileref && !fileref->isNull() && fileref->file() && fileref->tag();
}
spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
spb::tagreader::SongMetadata_FileType TagReaderTagLib::GuessFileType(TagLib::FileRef *fileref) const {
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_WAV;
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_FLAC;
@@ -162,6 +167,10 @@ spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_MPC;
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_TRUEAUDIO;
if (dynamic_cast<TagLib::APE::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_APE;
if (dynamic_cast<TagLib::Mod::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_MOD;
if (dynamic_cast<TagLib::S3M::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_S3M;
if (dynamic_cast<TagLib::XM::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_XM;
if (dynamic_cast<TagLib::IT::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_IT;
#ifdef HAVE_TAGLIB_DSFFILE
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_DSF;
#endif
@@ -173,7 +182,7 @@ spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *
}
void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
void TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
const QFileInfo info(filename);
@@ -189,6 +198,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
#else
song->set_ctime(info.created().toSecsSinceEpoch());
#endif
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (fileref->isNull()) {
@@ -399,7 +409,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
if (compilation.isEmpty()) {
// well, it wasn't set, but if the artist is VA assume it's a compilation
if (QStringFromStdString(song->artist()).toLower() == "various artists" || QStringFromStdString(song->albumartist()).toLower() == "various artists") {
if (QStringFromStdString(song->artist()).compare("various artists") == 0 || QStringFromStdString(song->albumartist()).compare("various artists") == 0) {
song->set_compilation(true);
}
}
@@ -414,6 +424,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
SetDefault(track);
SetDefault(disc);
SetDefault(year);
SetDefault(originalyear);
SetDefault(bitrate);
SetDefault(samplerate);
SetDefault(bitdepth);
@@ -422,20 +433,20 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
}
void TagReader::Decode(const TagLib::String &tag, std::string *output) {
void TagReaderTagLib::Decode(const TagLib::String &tag, std::string *output) {
QString tmp = TStringToQString(tag).trimmed();
output->assign(DataCommaSizeFromQString(tmp));
}
void TagReader::Decode(const QString &tag, std::string *output) {
void TagReaderTagLib::Decode(const QString &tag, std::string *output) {
output->assign(DataCommaSizeFromQString(tag));
}
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
void TagReaderTagLib::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
@@ -460,7 +471,7 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc,
}
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
void TagReaderTagLib::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
if (it != map.end()) {
@@ -504,7 +515,7 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc,
}
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const {
void TagReaderTagLib::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const {
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
@@ -522,7 +533,7 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
}
bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
bool TagReaderTagLib::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
if (filename.isEmpty()) return false;
@@ -603,7 +614,7 @@ bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMeta
return result;
}
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
void TagReaderTagLib::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
@@ -615,14 +626,14 @@ void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMeta
}
void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const {
void TagReaderTagLib::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const {
const QByteArray utf8(value.toUtf8());
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
}
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
void TagReaderTagLib::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
TagLib::ByteVector id_vector(id);
QVector<TagLib::ByteVector> frames_buffer;
@@ -653,7 +664,7 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
}
void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
void TagReaderTagLib::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
TagLib::ByteVector id_vector("USLT");
QVector<TagLib::ByteVector> frames_buffer;
@@ -685,7 +696,7 @@ void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Ta
}
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
if (filename.isEmpty()) return QByteArray();
@@ -786,7 +797,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
}
QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
QByteArray TagReaderTagLib::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
QByteArray ret;
@@ -804,7 +815,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
}
bool TagReader::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
bool TagReaderTagLib::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
if (filename.isEmpty()) return false;

View File

@@ -16,8 +16,8 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TAGREADER_H
#define TAGREADER_H
#ifndef TAGREADERTAGLIB_H
#define TAGREADERTAGLIB_H
#include "config.h"
@@ -33,29 +33,31 @@
#include <taglib/apefile.h>
#include <taglib/id3v2tag.h>
#include "tagreaderbase.h"
#include "tagreadermessages.pb.h"
class FileRefFactory;
/**
/*
* This class holds all useful methods to read and write tags from/to files.
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
*/
class TagReader {
class TagReaderTagLib : public TagReaderBase {
public:
explicit TagReader();
~TagReader();
explicit TagReaderTagLib();
~TagReaderTagLib();
bool IsMediaFile(const QString &filename) const;
bool IsMediaFile(const QString &filename) const override;
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
QByteArray LoadEmbeddedArt(const QString &filename) const override;
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) override;
private:
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const;
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const;
QByteArray LoadEmbeddedArt(const QString &filename) const;
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data);
static void Decode(const TagLib::String &tag, std::string *output);
static void Decode(const QString &tag, std::string *output);
@@ -69,10 +71,12 @@ class TagReader {
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
private:
FileRefFactory *factory_;
const std::string kEmbeddedCover;
Q_DISABLE_COPY(TagReaderTagLib)
};
#endif // TAGREADER_H
#endif // TAGREADERTAGLIB_H

View File

@@ -0,0 +1,424 @@
/* This file is part of Strawberry.
Copyright 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 "config.h"
#include "tagreadertagparser.h"
#include <string>
#include <memory>
#include <algorithm>
#include <sys/stat.h>
#include <tagparser/mediafileinfo.h>
#include <tagparser/diagnostics.h>
#include <tagparser/progressfeedback.h>
#include <tagparser/tag.h>
#include <tagparser/abstracttrack.h>
#include <QtGlobal>
#include <QFile>
#include <QFileInfo>
#include <QByteArray>
#include <QString>
#include <QUrl>
#include <QDateTime>
#include <QtDebug>
#include "core/logging.h"
#include "core/messagehandler.h"
#include "core/timeconstants.h"
TagReaderTagParser::TagReaderTagParser() = default;
TagReaderTagParser::~TagReaderTagParser() = default;
bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
qLog(Debug) << "Checking for valid file" << filename;
QFileInfo fileinfo(filename);
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return false;
try {
TagParser::MediaFileInfo taginfo;
TagParser::Diagnostics diag;
TagParser::AbortableProgressFeedback progress;
taginfo.setPath(QFile::encodeName(filename).toStdString());
taginfo.open(true);
taginfo.parseContainerFormat(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
taginfo.parseTracks(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
const auto tracks = taginfo.tracks();
for (const auto track : tracks) {
if (track->mediaType() == TagParser::MediaType::Audio) {
taginfo.close();
return true;
}
}
taginfo.close();
}
catch(...) {}
return false;
}
void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
qLog(Debug) << "Reading tags from" << filename;
const QFileInfo fileinfo(filename);
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return;
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
song->set_basefilename(DataCommaSizeFromQString(fileinfo.fileName()));
song->set_url(url.constData(), url.size());
song->set_filesize(fileinfo.size());
song->set_mtime(fileinfo.lastModified().toSecsSinceEpoch());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
song->set_ctime(fileinfo.birthTime().isValid() ? fileinfo.birthTime().toSecsSinceEpoch() : fileinfo.lastModified().toSecsSinceEpoch());
#else
song->set_ctime(fileinfo.created().toSecsSinceEpoch());
#endif
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
try {
TagParser::MediaFileInfo taginfo;
TagParser::Diagnostics diag;
TagParser::AbortableProgressFeedback progress;
#ifdef Q_OS_WIN32
taginfo.setPath(filename.toStdWString().toStdString());
#else
taginfo.setPath(QFile::encodeName(filename).toStdString());
#endif
taginfo.open(true);
taginfo.parseContainerFormat(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return;
}
taginfo.parseTracks(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return;
}
taginfo.parseTags(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return;
}
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
const auto tracks = taginfo.tracks();
for (const auto track : tracks) {
switch(track->format().general) {
case TagParser::GeneralMediaFormat::Flac:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_FLAC);
break;
case TagParser::GeneralMediaFormat::WavPack:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_WAVPACK);
break;
case TagParser::GeneralMediaFormat::MonkeysAudio:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_APE);
break;
case TagParser::GeneralMediaFormat::WindowsMediaAudio:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_ASF);
break;
case TagParser::GeneralMediaFormat::Vorbis:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGVORBIS);
break;
case TagParser::GeneralMediaFormat::Opus:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGOPUS);
break;
case TagParser::GeneralMediaFormat::Speex:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGSPEEX);
break;
case TagParser::GeneralMediaFormat::Mpeg1Audio:
switch(track->format().sub) {
case TagParser::SubFormats::Mpeg1Layer3:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_MPEG);
break;
case TagParser::SubFormats::None:
default:
break;
}
break;
case TagParser::GeneralMediaFormat::Mpc:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_MPC);
break;
case TagParser::GeneralMediaFormat::Pcm:
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_PCM);
break;
case TagParser::GeneralMediaFormat::Unknown:
default:
break;
}
song->set_length_nanosec(track->duration().totalMilliseconds() * kNsecPerMsec);
song->set_samplerate(track->samplingFrequency());
song->set_bitdepth(track->bitsPerSample());
song->set_bitrate(std::max(track->bitrate(), track->maxBitrate()));
}
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
taginfo.close();
return;
}
for (const auto tag : taginfo.tags()) {
song->set_albumartist(tag->value(TagParser::KnownField::AlbumArtist).toString(TagParser::TagTextEncoding::Utf8));
song->set_artist(tag->value(TagParser::KnownField::Artist).toString(TagParser::TagTextEncoding::Utf8));
song->set_album(tag->value(TagParser::KnownField::Album).toString(TagParser::TagTextEncoding::Utf8));
song->set_title(tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8));
song->set_genre(tag->value(TagParser::KnownField::Genre).toString(TagParser::TagTextEncoding::Utf8));
song->set_composer(tag->value(TagParser::KnownField::Composer).toString(TagParser::TagTextEncoding::Utf8));
song->set_performer(tag->value(TagParser::KnownField::Performers).toString(TagParser::TagTextEncoding::Utf8));
song->set_grouping(tag->value(TagParser::KnownField::Grouping).toString(TagParser::TagTextEncoding::Utf8));
song->set_comment(tag->value(TagParser::KnownField::Comment).toString(TagParser::TagTextEncoding::Utf8));
song->set_lyrics(tag->value(TagParser::KnownField::Lyrics).toString(TagParser::TagTextEncoding::Utf8));
song->set_year(tag->value(TagParser::KnownField::RecordDate).toInteger());
song->set_originalyear(tag->value(TagParser::KnownField::ReleaseDate).toInteger());
song->set_track(tag->value(TagParser::KnownField::TrackPosition).toInteger());
song->set_disc(tag->value(TagParser::KnownField::DiskPosition).toInteger());
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
song->set_art_automatic(kEmbeddedCover);
}
}
// Set integer fields to -1 if they're not valid
#define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); }
SetDefault(track);
SetDefault(disc);
SetDefault(year);
SetDefault(originalyear);
SetDefault(bitrate);
SetDefault(samplerate);
SetDefault(bitdepth);
SetDefault(lastplayed);
#undef SetDefault
song->set_valid(true);
taginfo.close();
}
catch(...) {}
}
bool TagReaderTagParser::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
if (filename.isEmpty()) return false;
qLog(Debug) << "Saving tags to" << filename;
try {
TagParser::MediaFileInfo taginfo;
TagParser::Diagnostics diag;
TagParser::AbortableProgressFeedback progress;
#ifdef Q_OS_WIN32
taginfo.setPath(filename.toStdWString().toStdString());
#else
taginfo.setPath(QFile::encodeName(filename).toStdString());
#endif
taginfo.open(false);
taginfo.parseContainerFormat(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
taginfo.parseTracks(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
taginfo.parseTags(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
if (taginfo.tags().size() <= 0) {
taginfo.createAppropriateTags();
}
for (const auto tag : taginfo.tags()) {
tag->setValue(TagParser::KnownField::AlbumArtist, TagParser::TagValue(song.albumartist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Artist, TagParser::TagValue(song.artist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Album, TagParser::TagValue(song.album(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Title, TagParser::TagValue(song.title(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Genre, TagParser::TagValue(song.genre(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Composer, TagParser::TagValue(song.composer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Performers, TagParser::TagValue(song.performer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Grouping, TagParser::TagValue(song.grouping(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Comment, TagParser::TagValue(song.comment(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Lyrics, TagParser::TagValue(song.lyrics(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::TrackPosition, TagParser::TagValue(song.track()));
tag->setValue(TagParser::KnownField::DiskPosition, TagParser::TagValue(song.disc()));
tag->setValue(TagParser::KnownField::RecordDate, TagParser::TagValue(song.year()));
tag->setValue(TagParser::KnownField::ReleaseDate, TagParser::TagValue(song.originalyear()));
}
taginfo.applyChanges(diag, progress);
taginfo.close();
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
return true;
}
catch(...) {}
return false;
}
QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
if (filename.isEmpty()) return QByteArray();
qLog(Debug) << "Loading art from" << filename;
try {
TagParser::MediaFileInfo taginfo;
TagParser::Diagnostics diag;
TagParser::AbortableProgressFeedback progress;
#ifdef Q_OS_WIN32
taginfo.setPath(filename.toStdWString().toStdString());
#else
taginfo.setPath(QFile::encodeName(filename).toStdString());
#endif
taginfo.open();
taginfo.parseContainerFormat(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return QByteArray();
}
taginfo.parseTags(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return QByteArray();
}
for (const auto tag : taginfo.tags()) {
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
QByteArray data(tag->value(TagParser::KnownField::Cover).dataPointer(), tag->value(TagParser::KnownField::Cover).dataSize());
taginfo.close();
return data;
}
}
taginfo.close();
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
}
catch(...) {}
return QByteArray();
}
bool TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
if (filename.isEmpty()) return false;
qLog(Debug) << "Saving art to" << filename;
try {
TagParser::MediaFileInfo taginfo;
TagParser::Diagnostics diag;
TagParser::AbortableProgressFeedback progress;
#ifdef Q_OS_WIN32
taginfo.setPath(filename.toStdWString().toStdString());
#else
taginfo.setPath(QFile::encodeName(filename).toStdString());
#endif
taginfo.open();
taginfo.parseContainerFormat(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
taginfo.parseTags(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
if (taginfo.tags().size() <= 0) {
taginfo.createAppropriateTags();
}
for (const auto tag : taginfo.tags()) {
tag->setValue(TagParser::KnownField::Cover, TagParser::TagValue(data.toStdString()));
}
taginfo.applyChanges(diag, progress);
taginfo.close();
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
return true;
}
catch(...) {}
return false;
}

View File

@@ -0,0 +1,51 @@
/* This file is part of Strawberry.
Copyright 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 TAGREADERTAGPARSER_H
#define TAGREADERTAGPARSER_H
#include "config.h"
#include <string>
#include <QByteArray>
#include <QString>
#include "tagreadermessages.pb.h"
#include "tagreaderbase.h"
/*
* This class holds all useful methods to read and write tags from/to files.
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
*/
class TagReaderTagParser : public TagReaderBase {
public:
explicit TagReaderTagParser();
~TagReaderTagParser();
bool IsMediaFile(const QString &filename) const override;
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
QByteArray LoadEmbeddedArt(const QString &filename) const override;
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) override;
Q_DISABLE_COPY(TagReaderTagParser)
};
#endif // TAGREADERTAGPARSER_H

View File

@@ -0,0 +1,15 @@
qt_wrap_cpp(MACDEPLOYCHECK_MOC ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common/core/logging.h)
link_directories(${GLIB_LIBRARY_DIRS})
add_executable(macdeploycheck macdeploycheck.cpp ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common/core/logging.cpp ${MACDEPLOYCHECK_MOC})
target_include_directories(macdeploycheck PUBLIC SYSTEM
${GLIB_INCLUDE_DIRS}
)
target_include_directories(macdeploycheck PUBLIC
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
${CMAKE_BINARY_DIR}/src
)
target_link_libraries(macdeploycheck PUBLIC
"-framework AppKit"
${GLIB_LIBRARIES}
${QtCore_LIBRARIES}
)

View File

@@ -0,0 +1,150 @@
/* This file is part of Strawberry.
Copyright 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 <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDirIterator>
#include <QProcess>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include "core/logging.h"
int main(int argc, char **argv);
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
logging::Init();
qLog(Info) << "Running macdeploycheck";
if (argc < 1) {
qLog(Error) << "Usage: macdeploycheck <bundledir>";
return 1;
}
QString bundle_path = QString::fromLocal8Bit(argv[1]);
bool success = true;
QDirIterator iter(bundle_path, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
while (iter.hasNext()) {
iter.next();
QString filepath = iter.fileInfo().filePath();
// Ignore these files.
if (filepath.endsWith(".plist") ||
filepath.endsWith(".icns") ||
filepath.endsWith(".prl") ||
filepath.endsWith(".conf") ||
filepath.endsWith(".h") ||
filepath.endsWith(".nib") ||
filepath.endsWith(".strings") ||
filepath.endsWith(".css") ||
filepath.endsWith("CodeResources") ||
filepath.endsWith("PkgInfo") ||
filepath.endsWith(".modulemap")) {
continue;
}
QProcess otool;
otool.start("otool", QStringList() << "-L" << filepath);
otool.waitForFinished();
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
qLog(Error) << "otool failed for" << filepath << ":" << otool.readAllStandardError();
success = false;
continue;
}
QString output = otool.readAllStandardOutput();
QStringList output_lines = output.split("\n", Qt::SkipEmptyParts);
if (output_lines.size() < 2) {
qLog(Error) << "Could not parse otool output:" << output;
success = false;
continue;
}
QString first_line = output_lines.first();
if (first_line.endsWith(':')) first_line.chop(1);
if (first_line == filepath) {
output_lines.removeFirst();
}
else {
qLog(Error) << "First line" << first_line << "does not match" << filepath;
success = false;
}
QRegularExpression regexp(QStringLiteral("^\\t(.+) \\(compatibility version (\\d+\\.\\d+\\.\\d+), current version (\\d+\\.\\d+\\.\\d+)(, weak)?\\)$"));
for (const QString &output_line : output_lines) {
//qDebug() << "Final check on" << filepath << output_line;
QRegularExpressionMatch match = regexp.match(output_line);
if (match.hasMatch()) {
QString library = match.captured(1);
if (QFileInfo(library).fileName() == QFileInfo(filepath).fileName()) { // It's this.
continue;
}
else if (library.startsWith("@executable_path")) {
QString real_path = library;
real_path = real_path.replace("@executable_path", bundle_path + "/Contents/MacOS");
if (!QFile(real_path).exists()) {
qLog(Error) << real_path << "does not exist for" << filepath;
success = false;
}
}
else if (library.startsWith("@rpath")) {
QString real_path = library;
real_path = real_path.replace("@rpath", bundle_path + "/Contents/Frameworks");
if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
qLog(Error) << real_path << "does not exist for" << filepath;
success = false;
}
}
else if (library.startsWith("@loader_path")) {
QString loader_path = QFileInfo(filepath).path();
QString real_path = library;
real_path = real_path.replace("@loader_path", loader_path);
if (!QFile(real_path).exists()) {
qLog(Error) << real_path << "does not exist for" << filepath;
success = false;
}
}
else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library
continue;
}
else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason.
continue;
}
else {
qLog(Error) << "File" << filepath << "points to" << library;
success = false;
}
}
else {
qLog(Error) << "Could not parse otool output line:" << output_line;
success = false;
}
}
}
return success ? 0 : 1;
}

View File

@@ -3,26 +3,25 @@ cmake_minimum_required(VERSION 3.0)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(SOURCES main.cpp tagreaderworker.cpp)
set(HEADERS tagreaderworker.h)
if(BUILD_WITH_QT6)
qt6_wrap_cpp(MOC ${HEADERS})
qt6_add_resources(QRC data/data.qrc)
else()
qt5_wrap_cpp(MOC ${HEADERS})
qt5_add_resources(QRC data/data.qrc)
qt_wrap_cpp(MOC ${HEADERS})
link_directories(${GLIB_LIBRARY_DIRS})
if(USE_TAGLIB AND TAGLIB_FOUND)
link_directories(${TAGLIB_LIBRARY_DIRS})
endif()
link_directories(
${GLIB_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
)
if(USE_TAGPARSER AND TAGPARSER_FOUND)
link_directories(${TAGPARSER_LIBRARY_DIRS})
endif()
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
target_include_directories(strawberry-tagreader PRIVATE
@@ -34,20 +33,29 @@ target_include_directories(strawberry-tagreader PRIVATE
target_link_libraries(strawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
${TAGLIB_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
libstrawberry-common
libstrawberry-tagreader
)
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
if(USE_TAGLIB AND TAGLIB_FOUND)
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
target_link_libraries(strawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
endif()
if(FREEBSD)
target_link_libraries(strawberry-tagreader PRIVATE execinfo)
endif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
endif()
if(APPLE)
target_link_libraries(strawberry-tagreader PRIVATE /System/Library/Frameworks/Foundation.framework)
endif(APPLE)
endif()
if(APPLE)
install(TARGETS strawberry-tagreader DESTINATION ${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns)

View File

@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/certs">
<file>godaddy-root.pem</file>
</qresource>
</RCC>

View File

@@ -1,24 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----

View File

@@ -18,18 +18,18 @@
#include "config.h"
#include <QtGlobal>
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
#include <sys/time.h>
#endif
#include <iostream>
#include <QtGlobal>
#include <QCoreApplication>
#include <QList>
#include <QLocalSocket>
#include <QSsl>
#include <QSslCertificate>
#include <QSslSocket>
#include <QString>
#include <QStringList>
#include <QtDebug>
#include <QLocalSocket>
#include "core/logging.h"
#include "tagreaderworker.h"
@@ -64,10 +64,6 @@ int main(int argc, char **argv) {
return 1;
}
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QSslSocket::addDefaultCaCertificates(QSslCertificate::fromPath(":/certs/godaddy-root.pem", QSsl::Pem));
#endif
TagReaderWorker worker(&socket);
return a.exec();

View File

@@ -59,5 +59,5 @@ void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
void TagReaderWorker::DeviceClosed() {
AbstractMessageHandler<spb::tagreader::Message>::DeviceClosed();
qApp->exit();
QCoreApplication::exit();
}

View File

@@ -24,12 +24,18 @@
#include <QObject>
#include "core/messagehandler.h"
#include "tagreader.h"
#if defined(USE_TAGLIB)
# include "tagreadertaglib.h"
#elif defined(USE_TAGPARSER)
# include "tagreadertagparser.h"
#endif
#include "tagreadermessages.pb.h"
class QIODevice;
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
Q_OBJECT
public:
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
@@ -38,7 +44,11 @@ class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
void DeviceClosed() override;
private:
TagReader tag_reader_;
#if defined(USE_TAGLIB)
TagReaderTagLib tag_reader_;
#elif defined(USE_TAGPARSER)
TagReaderTagParser tag_reader_;
#endif
};
#endif // TAGREADERWORKER_H

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
if(HAVE_TRANSLATIONS)
include(../cmake/Translations.cmake)
endif(HAVE_TRANSLATIONS)
endif()
set(SOURCES
core/mainwindow.cpp
@@ -11,6 +11,7 @@ set(SOURCES
core/player.cpp
core/commandlineoptions.cpp
core/database.cpp
core/sqlquery.cpp
core/metatypes.cpp
core/deletefiles.cpp
core/filesystemmusicstorage.cpp
@@ -36,11 +37,10 @@ set(SOURCES
core/utilities.cpp
core/imageutils.cpp
core/iconloader.cpp
core/qtsystemtrayicon.cpp
core/standarditemiconloader.cpp
core/systemtrayicon.cpp
core/scopedtransaction.cpp
core/translations.cpp
core/systemtrayicon.cpp
engine/enginetype.cpp
engine/enginebase.cpp
@@ -76,6 +76,7 @@ set(SOURCES
collection/sqlrow.cpp
collection/savedgroupingmanager.cpp
collection/groupbydialog.cpp
collection/collectiontask.cpp
playlist/playlist.cpp
playlist/playlistbackend.cpp
@@ -215,9 +216,6 @@ set(SOURCES
osd/osdbase.cpp
osd/osdpretty.cpp
musicbrainz/acoustidclient.cpp
musicbrainz/musicbrainzclient.cpp
internet/internetservices.cpp
internet/internetservice.cpp
internet/internetplaylistitem.cpp
@@ -232,6 +230,17 @@ set(SOURCES
internet/internetcollectionviewcontainer.cpp
internet/internetsearchview.cpp
radios/radioservices.cpp
radios/radiobackend.cpp
radios/radiomodel.cpp
radios/radioview.cpp
radios/radioviewcontainer.cpp
radios/radioservice.cpp
radios/radioplaylistitem.cpp
radios/radiochannel.cpp
radios/somafmservice.cpp
radios/radioparadiseservice.cpp
scrobbler/audioscrobbler.cpp
scrobbler/scrobblerservices.cpp
scrobbler/scrobblerservice.cpp
@@ -253,11 +262,13 @@ set(SOURCES
set(HEADERS
core/mainwindow.h
core/application.h
core/appearance.h
core/player.h
core/database.h
core/deletefiles.h
core/filesystemwatcherinterface.h
core/mergedproxymodel.h
core/multisortfilterproxy.h
core/networkaccessmanager.h
core/threadsafenetworkdiskcache.h
core/networktimeouts.h
@@ -265,10 +276,11 @@ set(HEADERS
core/songloader.h
core/tagreaderclient.h
core/taskmanager.h
core/thread.h
core/urlhandler.h
core/qtsystemtrayicon.h
core/standarditemiconloader.h
core/systemtrayicon.h
core/translations.h
core/potranslator.h
core/mimedata.h
core/stylesheetloader.h
@@ -310,6 +322,7 @@ set(HEADERS
playlist/playlistlistcontainer.h
playlist/playlistlistmodel.h
playlist/playlistlistview.h
playlist/playlistlistsortfiltermodel.h
playlist/playlistmanager.h
playlist/playlistsaveoptionsdialog.h
playlist/playlistsequence.h
@@ -330,10 +343,13 @@ set(HEADERS
playlistparsers/parserbase.h
playlistparsers/playlistparser.h
playlistparsers/plsparser.h
playlistparsers/wplparser.h
playlistparsers/xmlparser.h
playlistparsers/xspfparser.h
smartplaylists/playlistgenerator.h
smartplaylists/playlistgeneratorinserter.h
smartplaylists/playlistquerygenerator.h
smartplaylists/playlistgeneratormimedata.h
smartplaylists/smartplaylistquerywizardplugin.h
smartplaylists/smartplaylistsearchpreview.h
@@ -428,24 +444,33 @@ set(HEADERS
widgets/loginstatewidget.h
widgets/qsearchfield.h
widgets/ratingwidget.h
widgets/forcescrollperpixel.h
osd/osdbase.h
osd/osdpretty.h
musicbrainz/acoustidclient.h
musicbrainz/musicbrainzclient.h
internet/internetservices.h
internet/internetservice.h
internet/internetsongmimedata.h
internet/internetsearchview.h
internet/internetsearchmodel.h
internet/internetsearchsortmodel.h
internet/internetsearchitemdelegate.h
internet/internetsearchview.h
internet/localredirectserver.h
internet/internetsongsview.h
internet/internettabsview.h
internet/internetcollectionview.h
internet/internetcollectionviewcontainer.h
internet/internetsearchview.h
radios/radioservices.h
radios/radiobackend.h
radios/radiomodel.h
radios/radioview.h
radios/radioviewcontainer.h
radios/radioservice.h
radios/radiomimedata.h
radios/somafmservice.h
radios/radioparadiseservice.h
scrobbler/audioscrobbler.h
scrobbler/scrobblerservices.h
@@ -530,6 +555,8 @@ set(UI
internet/internetcollectionviewcontainer.ui
internet/internetsearchview.ui
radios/radioviewcontainer.ui
organize/organizedialog.ui
organize/organizeerrordialog.ui
@@ -542,7 +569,7 @@ option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
if(NOT APPLE)
set(NOT_APPLE ON)
optional_source(NOT_APPLE SOURCES widgets/qsearchfield_nonmac.cpp)
optional_source(NOT_APPLE SOURCES widgets/qsearchfield_nonmac.cpp core/qtsystemtrayicon.cpp HEADERS core/qtsystemtrayicon.h)
endif()
if(HAVE_GLOBALSHORTCUTS)
@@ -551,137 +578,62 @@ if(HAVE_GLOBALSHORTCUTS)
HEADERS globalshortcuts/globalshortcutsmanager.h globalshortcuts/globalshortcutsbackend.h globalshortcuts/globalshortcutgrabber.h settings/globalshortcutssettingspage.h
UI globalshortcuts/globalshortcutgrabber.ui settings/globalshortcutssettingspage.ui
)
if(HAVE_X11EXTRAS OR WIN32)
set(X11_OR_WIN ON)
endif()
optional_source(X11_OR_WIN
SOURCES globalshortcuts/globalshortcutsbackend-system.cpp globalshortcuts/globalshortcut.cpp
HEADERS globalshortcuts/globalshortcutsbackend-system.h globalshortcuts/globalshortcut.h
)
optional_source(HAVE_X11EXTRAS
SOURCES globalshortcuts/globalshortcut-x11.cpp
)
optional_source(HAVE_DBUS
SOURCES globalshortcuts/globalshortcutsbackend-gsd.cpp globalshortcuts/globalshortcutsbackend-kde.cpp
HEADERS globalshortcuts/globalshortcutsbackend-gsd.h globalshortcuts/globalshortcutsbackend-kde.h
SOURCES globalshortcuts/globalshortcutsbackend-kde.cpp globalshortcuts/globalshortcutsbackend-gnome.cpp globalshortcuts/globalshortcutsbackend-mate.cpp
HEADERS globalshortcuts/globalshortcutsbackend-kde.h globalshortcuts/globalshortcutsbackend-gnome.h globalshortcuts/globalshortcutsbackend-mate.h
)
optional_source(HAVE_X11_GLOBALSHORTCUTS
SOURCES globalshortcuts/globalshortcutsbackend-x11.cpp globalshortcuts/globalshortcut.cpp globalshortcuts/globalshortcut-x11.cpp
HEADERS globalshortcuts/globalshortcutsbackend-x11.h globalshortcuts/globalshortcut.h
)
optional_source(WIN32
SOURCES globalshortcuts/globalshortcut-win.cpp
SOURCES globalshortcuts/globalshortcutsbackend-win.cpp globalshortcuts/globalshortcut.cpp globalshortcuts/globalshortcut-win.cpp
HEADERS globalshortcuts/globalshortcutsbackend-win.h globalshortcuts/globalshortcut.h
)
endif(HAVE_GLOBALSHORTCUTS)
# ALSA
optional_source(HAVE_ALSA
SOURCES
engine/alsadevicefinder.cpp
)
optional_source(HAVE_ALSA SOURCES engine/alsadevicefinder.cpp engine/alsapcmdevicefinder.cpp)
# DBUS
optional_source(HAVE_DBUS
SOURCES
osd/osddbus.cpp
HEADERS
osd/osddbus.h
)
optional_source(HAVE_DBUS SOURCES osd/osddbus.cpp HEADERS osd/osddbus.h)
# GStreamer
optional_source(HAVE_GSTREAMER
SOURCES engine/gststartup.cpp engine/gstengine.cpp engine/gstenginepipeline.cpp engine/gstelementdeleter.cpp
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h
SOURCES engine/gststartup.cpp engine/gstengine.cpp engine/gstenginepipeline.cpp
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h
)
# VLC
optional_source(HAVE_VLC
SOURCES engine/vlcengine.cpp
HEADERS engine/vlcengine.h
)
optional_source(HAVE_VLC SOURCES engine/vlcengine.cpp HEADERS engine/vlcengine.h)
# DBUS and MPRIS - Unix specific
if(UNIX AND HAVE_DBUS)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus)
optional_source(HAVE_DBUS
SOURCES core/mpris.cpp core/mpris2.cpp
HEADERS core/mpris.h core/mpris2.h
)
optional_source(HAVE_UDISKS2
SOURCES device/udisks2lister.cpp
HEADERS device/udisks2lister.h
)
optional_source(HAVE_DBUS SOURCES core/mpris2.cpp HEADERS core/mpris2.h)
optional_source(HAVE_UDISKS2 SOURCES device/udisks2lister.cpp HEADERS device/udisks2lister.h)
if (BUILD_WITH_QT6)
# MPRIS 2.0 DBUS interfaces
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.Player.xml core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.xml core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.TrackList.xml core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
# MPRIS 2.0 DBUS interfaces
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Player.xml
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.xml
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.TrackList.xml
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
# MPRIS 2.1 DBUS interfaces
qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.Playlists.xml core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# MPRIS 2.1 DBUS interfaces
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# org.freedesktop.Notifications DBUS interface
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.Notifications.xml dbus/notification)
# org.freedesktop.Notifications DBUS interface
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.Notifications.xml
dbus/notification)
# org.gnome.SettingsDaemon interface
qt_add_dbus_interface(SOURCES dbus/org.gnome.SettingsDaemon.MediaKeys.xml dbus/gnomesettingsdaemon)
# org.gnome.SettingsDaemon interface
qt6_add_dbus_interface(SOURCES
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
# org.mate.SettingsDaemon interface
qt_add_dbus_interface(SOURCES dbus/org.mate.SettingsDaemon.MediaKeys.xml dbus/matesettingsdaemon)
# org.kde.KGlobalAccel interface
qt6_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.xml
dbus/kglobalaccel)
qt6_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.Component.xml
dbus/kglobalaccelcomponent)
else()
# MPRIS 2.0 DBUS interfaces
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Player.xml
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.xml
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.TrackList.xml
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
# MPRIS 2.1 DBUS interfaces
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# org.freedesktop.Notifications DBUS interface
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.Notifications.xml
dbus/notification)
# org.gnome.SettingsDaemon interface
qt5_add_dbus_interface(SOURCES
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
# org.kde.KGlobalAccel interface
qt5_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.xml
dbus/kglobalaccel)
qt5_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.Component.xml
dbus/kglobalaccelcomponent)
endif()
# org.kde.KGlobalAccel interface
qt_add_dbus_interface(SOURCES dbus/org.kde.KGlobalAccel.xml dbus/kglobalaccel)
qt_add_dbus_interface(SOURCES dbus/org.kde.KGlobalAccel.Component.xml dbus/kglobalaccelcomponent)
# org.freedesktop.Avahi.Server interface
add_custom_command(
@@ -712,50 +664,17 @@ if(UNIX AND HAVE_DBUS)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp)
if(HAVE_UDISKS2)
set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml
PROPERTIES NO_NAMESPACE dbus/objectmanager INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Filesystem.xml
PROPERTIES NO_NAMESPACE dbus/udisks2filesystem INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Block.xml
PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Drive.xml
PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml
PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h)
if(BUILD_WITH_QT6)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.DBus.ObjectManager.xml
dbus/objectmanager)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Filesystem.xml
dbus/udisks2filesystem)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Block.xml
dbus/udisks2block)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Drive.xml
dbus/udisks2drive)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Job.xml
dbus/udisks2job)
else()
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.DBus.ObjectManager.xml
dbus/objectmanager)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Filesystem.xml
dbus/udisks2filesystem)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Block.xml
dbus/udisks2block)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Drive.xml
dbus/udisks2drive)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Job.xml
dbus/udisks2job)
endif()
endif(HAVE_UDISKS2)
set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml PROPERTIES NO_NAMESPACE dbus/objectmanager INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Filesystem.xml PROPERTIES NO_NAMESPACE dbus/udisks2filesystem INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Block.xml PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Drive.xml PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h)
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.DBus.ObjectManager.xml dbus/objectmanager)
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Filesystem.xml dbus/udisks2filesystem)
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Block.xml dbus/udisks2block)
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Drive.xml dbus/udisks2drive)
qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Job.xml dbus/udisks2job)
endif(HAVE_UDISKS2)
endif(UNIX AND HAVE_DBUS)
@@ -797,29 +716,24 @@ optional_source(HAVE_LIBGPOD
)
# GIO device backend
optional_source(HAVE_GIO
SOURCES device/giolister.cpp
HEADERS device/giolister.h
)
optional_source(HAVE_GIO SOURCES device/giolister.cpp HEADERS device/giolister.h)
# mtp device
# MTP device
optional_source(HAVE_LIBMTP
SOURCES
device/mtpconnection.cpp
device/mtpdevice.cpp
device/mtploader.cpp
HEADERS
device/mtpconnection.h
device/mtpdevice.h
device/mtploader.h
)
# Pulse audio integration
optional_source(HAVE_LIBPULSE
SOURCES
engine/pulsedevicefinder.cpp
)
optional_source(HAVE_LIBPULSE SOURCES engine/pulsedevicefinder.cpp)
# MusicBrainz and transcoder require GStreamer
# Transcoder require GStreamer
optional_source(HAVE_GSTREAMER
SOURCES
transcoder/transcoder.cpp
@@ -838,6 +752,14 @@ HEADERS
transcoder/transcoder.h
transcoder/transcodedialog.h
transcoder/transcoderoptionsdialog.h
transcoder/transcoderoptionsinterface.h
transcoder/transcoderoptionsflac.h
transcoder/transcoderoptionswavpack.h
transcoder/transcoderoptionsvorbis.h
transcoder/transcoderoptionsopus.h
transcoder/transcoderoptionsspeex.h
transcoder/transcoderoptionsaac.h
transcoder/transcoderoptionsasf.h
transcoder/transcoderoptionsmp3.h
settings/transcodersettingspage.h
UI
@@ -855,30 +777,39 @@ UI
settings/transcodersettingspage.ui
)
# CDIO backend and device
if(HAVE_GSTREAMER)
optional_source(HAVE_CHROMAPRINT
SOURCES
musicbrainz/chromaprinter.cpp
musicbrainz/tagfetcher.cpp
HEADERS
musicbrainz/tagfetcher.h
)
optional_source(HAVE_AUDIOCD
SOURCES
device/cddadevice.cpp
device/cddalister.cpp
device/cddasongloader.cpp
HEADERS
device/cddadevice.h
device/cddalister.h
device/cddasongloader.h
)
# CHROMAPRINT
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
optional_source(CHROMAPRINT_FOUND SOURCES engine/chromaprinter.cpp)
endif()
# MusicBrainz
optional_source(HAVE_MUSICBRAINZ
SOURCES
musicbrainz/acoustidclient.cpp
musicbrainz/musicbrainzclient.cpp
musicbrainz/tagfetcher.cpp
HEADERS
musicbrainz/acoustidclient.h
musicbrainz/musicbrainzclient.h
musicbrainz/tagfetcher.h
)
# CDIO backend and device
optional_source(HAVE_AUDIOCD
SOURCES
device/cddadevice.cpp
device/cddalister.cpp
device/cddasongloader.cpp
HEADERS
device/cddadevice.h
device/cddalister.h
device/cddasongloader.h
)
# Platform specific - macOS
optional_source(APPLE
SOURCES
core/scoped_nsautorelease_pool.mm
core/mac_utilities.mm
core/mac_startup.mm
core/macsystemtrayicon.mm
@@ -888,22 +819,15 @@ optional_source(APPLE
engine/macosdevicefinder.cpp
globalshortcuts/globalshortcutsbackend-macos.mm
globalshortcuts/globalshortcutgrabber.mm
device/macosdevicelister.mm
HEADERS
core/macsystemtrayicon.h
core/macfslistener.h
osd/osdmac.h
globalshortcuts/globalshortcutsbackend-macos.h
device/macosdevicelister.h
)
if (APPLE)
optional_source(HAVE_LIBMTP
SOURCES
device/macosdevicelister.mm
HEADERS
device/macosdevicelister.h
)
endif()
# Platform specific - Windows
optional_source(WIN32
SOURCES
@@ -1004,33 +928,27 @@ optional_source(HAVE_MOODBAR
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
if(BUILD_WITH_QT6)
qt6_wrap_cpp(MOC ${HEADERS})
qt6_wrap_ui(UIC ${UI})
qt6_add_resources(QRC ${RESOURCES})
else()
qt5_wrap_cpp(MOC ${HEADERS})
qt5_wrap_ui(UIC ${UI})
qt5_add_resources(QRC ${RESOURCES})
endif()
qt_wrap_cpp(MOC ${HEADERS})
qt_wrap_ui(UIC ${UI})
qt_add_resources(QRC ${RESOURCES})
if(HAVE_TRANSLATIONS)
set(LINGUAS "All" CACHE STRING "A space-seperated list of translations to compile in to Strawberry, or \"None\".")
if (LINGUAS STREQUAL "All")
if(LINGUAS STREQUAL "All")
# build LANGUAGES from all existing .po files
file(GLOB pofiles translations/*.po)
foreach(pofile ${pofiles})
get_filename_component(lang ${pofile} NAME_WE)
list(APPEND LANGUAGES ${lang})
endforeach(pofile)
else (LINGUAS STREQUAL "All")
if (NOT LINGUAS OR LINGUAS STREQUAL "None")
set (LANGUAGES "")
else (NOT LINGUAS OR LINGUAS STREQUAL "None")
else(LINGUAS STREQUAL "All")
if(NOT LINGUAS OR LINGUAS STREQUAL "None")
set(LANGUAGES "")
else(NOT LINGUAS OR LINGUAS STREQUAL "None")
string(REGEX MATCHALL [a-zA-Z_@]+ LANGUAGES ${LINGUAS})
endif (NOT LINGUAS OR LINGUAS STREQUAL "None")
endif (LINGUAS STREQUAL "All")
endif(NOT LINGUAS OR LINGUAS STREQUAL "None")
endif(LINGUAS STREQUAL "All")
add_pot(POT
${CMAKE_CURRENT_SOURCE_DIR}/translations/header
@@ -1051,7 +969,6 @@ link_directories(
${GOBJECT_LIBRARY_DIRS}
${GNUTLS_LIBRARY_DIRS}
${SQLITE_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
${SINGLEAPPLICATION_LIBRARY_DIRS}
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
${QTSPARKLE_LIBRARY_DIRS}
@@ -1060,7 +977,7 @@ link_directories(
if(HAVE_ALSA)
link_directories(${ALSA_LIBRARY_DIRS})
endif(HAVE_ALSA)
endif()
if(HAVE_LIBPULSE)
link_directories(${LIBPULSE_LIBRARY_DIRS})
@@ -1075,40 +992,47 @@ if(HAVE_GSTREAMER)
${GSTREAMER_TAG_LIBRARY_DIRS}
${GSTREAMER_PBUTILS_LIBRARY_DIRS}
)
endif(HAVE_GSTREAMER)
endif()
if(HAVE_VLC)
link_directories(${LIBVLC_LIBRARY_DIRS})
endif()
if(HAVE_CHROMAPRINT)
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
link_directories(${CHROMAPRINT_LIBRARY_DIRS})
endif(HAVE_CHROMAPRINT)
endif()
if(X11_FOUND)
link_directories(${X11_LIBRARY_DIRS})
endif(X11_FOUND)
endif()
if(XCB_FOUND)
link_directories(${XCB_LIBRARY_DIRS})
endif(XCB_FOUND)
endif()
if(HAVE_GIO)
link_directories(${GIO_LIBRARY_DIRS})
endif(HAVE_GIO)
endif()
if(HAVE_AUDIOCD)
link_directories(${LIBCDIO_LIBRARY_DIRS})
endif(HAVE_AUDIOCD)
endif()
if(HAVE_LIBGPOD)
link_directories(${LIBGPOD_LIBRARY_DIRS})
link_directories(${GDK_PIXBUF_LIBRARY_DIRS})
endif(HAVE_LIBGPOD)
link_directories(${LIBGPOD_LIBRARY_DIRS} ${GDK_PIXBUF_LIBRARY_DIRS})
endif()
if(HAVE_LIBMTP)
link_directories(${LIBMTP_LIBRARY_DIRS})
endif(HAVE_LIBMTP)
endif()
if(USE_TAGLIB AND TAGLIB_FOUND)
link_directories(${TAGLIB_LIBRARY_DIRS})
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
link_directories(${TAGPARSER_LIBRARY_DIRS})
endif()
add_library(strawberry_lib STATIC
${SOURCES}
@@ -1126,9 +1050,12 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
${GOBJECT_INCLUDE_DIRS}
${GNUTLS_INCLUDE_DIRS}
${SQLITE_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
)
if(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
target_include_directories(strawberry_lib SYSTEM PUBLIC ${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
endif()
target_include_directories(strawberry_lib PUBLIC
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
@@ -1147,12 +1074,11 @@ target_link_libraries(strawberry_lib PUBLIC
${GOBJECT_LIBRARIES}
${GNUTLS_LIBRARIES}
${SQLITE_LIBRARIES}
${TAGLIB_LIBRARIES}
${QT_LIBRARIES}
${SINGLEAPPLICATION_LIBRARIES}
${SINGLECOREAPPLICATION_LIBRARIES}
${QTSPARKLE_LIBRARIES}
${Iconv_LIBRARY}
${Iconv_LIBRARIES}
libstrawberry-common
libstrawberry-tagreader
)
@@ -1160,7 +1086,7 @@ target_link_libraries(strawberry_lib PUBLIC
if(HAVE_ALSA)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${ALSA_LIBRARIES})
endif(HAVE_ALSA)
endif()
if(HAVE_LIBPULSE)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBPULSE_INCLUDE_DIRS})
@@ -1184,7 +1110,7 @@ if(HAVE_GSTREAMER)
${GSTREAMER_TAG_LIBRARIES}
${GSTREAMER_PBUTILS_LIBRARIES}
)
endif(HAVE_GSTREAMER)
endif()
if(HAVE_MOODBAR)
target_link_libraries(strawberry_lib PRIVATE gstmoodbar)
@@ -1195,41 +1121,44 @@ if(HAVE_VLC)
target_link_libraries(strawberry_lib PRIVATE ${LIBVLC_LIBRARIES})
endif()
if(HAVE_CHROMAPRINT)
if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${CHROMAPRINT_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${CHROMAPRINT_LIBRARIES})
endif(HAVE_CHROMAPRINT)
endif()
if(X11_FOUND)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${X11_INCLUDE_DIR})
target_link_libraries(strawberry_lib PRIVATE ${X11_LIBRARIES})
endif(X11_FOUND)
endif()
if(XCB_FOUND)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${XCB_INCLUDE_DIR})
target_link_libraries(strawberry_lib PRIVATE ${XCB_LIBRARIES})
endif(XCB_FOUND)
endif()
if(HAVE_GIO)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${GIO_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${GIO_LIBRARIES})
endif(HAVE_GIO)
endif()
if(HAVE_AUDIOCD)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBCDIO_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBCDIO_LIBRARIES})
endif(HAVE_AUDIOCD)
endif()
if(HAVE_LIBGPOD)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBGPOD_INCLUDE_DIRS} ${GDK_PIXBUF_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBGPOD_LIBRARIES} ${GDK_PIXBUF_LIBRARIES})
endif(HAVE_LIBGPOD)
endif()
if(HAVE_LIBMTP)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBMTP_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBMTP_LIBRARIES})
endif(HAVE_LIBMTP)
endif()
if(FREEBSD)
target_link_libraries(strawberry_lib PRIVATE iconv)
endif()
if(APPLE)
target_link_libraries(strawberry_lib PRIVATE
@@ -1245,24 +1174,17 @@ if(APPLE)
if(HAVE_SPARKLE)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${SPARKLE}/Headers)
target_link_libraries(strawberry_lib PRIVATE ${SPARKLE})
endif(HAVE_SPARKLE)
endif(APPLE)
if(WIN32)
target_link_libraries(strawberry_lib PRIVATE dsound)
endif(WIN32)
if(X11_FOUND)
# Hack: the Gold linker pays attention to the order that libraries are specified on the link line.
# -lX11 and -ldl are provided earlier in the link command but they're actually used by libraries that appear after them, so they end up getting ignored.
# This appends them to the very end of the link line, ensuring they're always used.
if(FREEBSD)
target_link_libraries(strawberry_lib PRIVATE ${X11_X11_LIB})
else()
target_link_libraries(strawberry_lib PRIVATE ${X11_X11_LIB} ${CMAKE_DL_LIBS})
endif()
endif()
if(WIN32)
target_link_libraries(strawberry_lib PRIVATE dsound dwmapi)
if(MSVC)
target_link_libraries(strawberry_lib PRIVATE sqlite3)
endif()
endif()
###############################################################################
set(EXECUTABLE_OUTPUT_PATH ..)
@@ -1270,13 +1192,13 @@ set(EXECUTABLE_OUTPUT_PATH ..)
# Show the console window in debug mode on Windows
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE)
set(STRAWBERRY-WIN32-FLAG WIN32)
endif(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE)
endif()
# Resource file for windows
if(WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../dist/windows/windres.rc.in ${CMAKE_CURRENT_BINARY_DIR}/windres.rc)
set(STRAWBERRY-WIN32-RESOURCES windres.rc)
endif(WIN32)
endif()
add_executable(strawberry
MACOSX_BUNDLE
@@ -1304,4 +1226,4 @@ endif()
if(APPLE)
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist")
endif (APPLE)
endif()

View File

@@ -56,7 +56,7 @@
template class Analyzer::Base<QWidget>;
#endif
Analyzer::Base::Base(QWidget *parent, uint scopeSize)
Analyzer::Base::Base(QWidget *parent, const uint scopeSize)
: QWidget(parent),
timeout_(40),
fht_(new FHT(scopeSize)),
@@ -69,10 +69,10 @@ void Analyzer::Base::hideEvent(QHideEvent*) { timer_.stop(); }
void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) {
void Analyzer::Base::transform(Scope &scope) {
QVector<float> aux(fht_->size());
if (static_cast<long unsigned int>(aux.size()) >= scope.size()) {
if (static_cast<unsigned long int>(aux.size()) >= scope.size()) {
std::copy(scope.begin(), scope.end(), aux.begin());
}
else {
@@ -93,12 +93,12 @@ void Analyzer::Base::paintEvent(QPaintEvent *e) {
switch (engine_->state()) {
case Engine::Playing: {
const Engine::Scope& thescope = engine_->scope(timeout_);
const Engine::Scope &thescope = engine_->scope(timeout_);
int i = 0;
// convert to mono here - our built in analyzers need mono, but the engines provide interleaved pcm
for (uint x = 0; static_cast<int>(x) < fht_->size(); ++x) {
lastscope_[x] = double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
lastscope_[x] = static_cast<float>(thescope[i] + thescope[i + 1]) / (2 * (1U << 15U));
i += 2;
}
@@ -126,10 +126,12 @@ void Analyzer::Base::paintEvent(QPaintEvent *e) {
int Analyzer::Base::resizeExponent(int exp) {
if (exp < 3)
if (exp < 3) {
exp = 3;
else if (exp > 9)
}
else if (exp > 9) {
exp = 9;
}
if (exp != fht_->sizeExp()) {
delete fht_;
@@ -139,43 +141,53 @@ int Analyzer::Base::resizeExponent(int exp) {
}
int Analyzer::Base::resizeForBands(int bands) {
int Analyzer::Base::resizeForBands(const int bands) {
int exp = 0;
if (bands <= 8)
if (bands <= 8) {
exp = 4;
else if (bands <= 16)
}
else if (bands <= 16) {
exp = 5;
else if (bands <= 32)
}
else if (bands <= 32) {
exp = 6;
else if (bands <= 64)
}
else if (bands <= 64) {
exp = 7;
else if (bands <= 128)
}
else if (bands <= 128) {
exp = 8;
else
}
else {
exp = 9;
}
resizeExponent(exp);
return fht_->size() / 2;
}
void Analyzer::Base::demo(QPainter& p) {
void Analyzer::Base::demo(QPainter &p) {
static int t = 201; // FIXME make static to namespace perhaps
if (t > 999) t = 1; // 0 = wasted calculations
if (t > 999) {
t = 1; // 0 = wasted calculations
}
if (t < 201) {
Scope s(32);
const double dt = double(t) / 200;
for (uint i = 0; i < s.size(); ++i)
const double dt = static_cast<double>(t) / 200;
for (uint i = 0; i < s.size(); ++i) {
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
}
analyze(p, s, new_frame_);
}
else
else {
analyze(p, Scope(32, 0), new_frame_);
}
++t;
@@ -185,7 +197,7 @@ void Analyzer::Base::polishEvent() {
init();
}
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
void Analyzer::interpolate(const Scope &inVec, Scope &outVec) {
double pos = 0.0;
const double step = static_cast<double>(inVec.size()) / outVec.size();
@@ -196,18 +208,22 @@ void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
uint64_t indexLeft = offset + 0;
if (indexLeft >= inVec.size()) indexLeft = inVec.size() - 1;
if (indexLeft >= inVec.size()) {
indexLeft = inVec.size() - 1;
}
uint64_t indexRight = offset + 1;
if (indexRight >= inVec.size()) indexRight = inVec.size() - 1;
if (indexRight >= inVec.size()) {
indexRight = inVec.size() - 1;
}
outVec[i] = inVec[indexLeft] * (1.0 - error) + inVec[indexRight] * error;
}
}
void Analyzer::initSin(Scope& v, const uint size) {
void Analyzer::initSin(Scope &v, const uint size) {
double step = (M_PI * 2) / size;
double radian = 0;
@@ -222,7 +238,9 @@ void Analyzer::initSin(Scope& v, const uint size) {
void Analyzer::Base::timerEvent(QTimerEvent *e) {
QWidget::timerEvent(e);
if (e->timerId() != timer_.timerId()) return;
if (e->timerId() != timer_.timerId()) {
return;
}
new_frame_ = true;
update();

View File

@@ -57,11 +57,11 @@ class Base : public QWidget {
public:
~Base() override { delete fht_; }
uint timeout() const { return timeout_; }
int timeout() const { return timeout_; }
void set_engine(EngineBase *engine) { engine_ = engine; }
void changeTimeout(uint newTimeout) {
void changeTimeout(int newTimeout) {
timeout_ = newTimeout;
if (timer_.isActive()) {
timer_.stop();
@@ -72,7 +72,7 @@ class Base : public QWidget {
virtual void framerateChanged() {}
protected:
explicit Base(QWidget*, uint scopeSize = 7);
explicit Base(QWidget*, const uint scopeSize = 7);
void hideEvent(QHideEvent*) override;
void showEvent(QShowEvent*) override;
@@ -82,15 +82,15 @@ class Base : public QWidget {
void polishEvent();
int resizeExponent(int);
int resizeForBands(int);
int resizeForBands(const int);
virtual void init() {}
virtual void transform(Scope&);
virtual void analyze(QPainter& p, const Scope&, bool new_frame) = 0;
virtual void demo(QPainter& p);
virtual void analyze(QPainter &p, const Scope&, const bool new_frame) = 0;
virtual void demo(QPainter &p);
protected:
QBasicTimer timer_;
uint timeout_;
int timeout_;
FHT *fht_;
EngineBase *engine_;
Scope lastscope_;

View File

@@ -19,6 +19,8 @@
#include "config.h"
#include <chrono>
#include <QObject>
#include <QWidget>
#include <QVariant>
@@ -44,6 +46,8 @@
#include "engine/enginebase.h"
#include "engine/enginetype.h"
using namespace std::chrono_literals;
const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
const char *AnalyzerContainer::kSettingsFramerate = "framerate";
@@ -90,7 +94,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
context_menu_->addSeparator();
double_click_timer_->setSingleShot(true);
double_click_timer_->setInterval(250);
double_click_timer_->setInterval(250ms);
QObject::connect(double_click_timer_, &QTimer::timeout, this, &AnalyzerContainer::ShowPopupMenu);
Load();
@@ -99,7 +103,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
if (engine_->type() != Engine::EngineType::GStreamer) return;
if (engine_->type() != Engine::EngineType::GStreamer) {
return;
}
if (e->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@@ -120,8 +126,10 @@ void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
}
void AnalyzerContainer::SetEngine(EngineBase *engine) {
if (current_analyzer_) current_analyzer_->set_engine(engine);
engine_ = engine;
}
void AnalyzerContainer::DisableAnalyzer() {
@@ -222,12 +230,12 @@ void AnalyzerContainer::Save() {
}
void AnalyzerContainer::AddFramerate(const QString& name, const int framerate) {
void AnalyzerContainer::AddFramerate(const QString &name, const int framerate) {
QAction *action = context_menu_framerate_->addAction(name);
group_framerate_->addAction(action);
framerate_list_ << framerate;
action->setCheckable(true);
QObject::connect(action, &QAction::triggered, [this, framerate]() { ChangeFramerate(framerate); } );
QObject::connect(action, &QAction::triggered, this, [this, framerate]() { ChangeFramerate(framerate); } );
}

View File

@@ -76,7 +76,7 @@ class AnalyzerContainer : public QWidget {
void SaveFramerate(const int framerate);
template <typename T>
void AddAnalyzerType();
void AddFramerate(const QString& name, const int framerate);
void AddFramerate(const QString &name, const int framerate);
private:
int current_framerate_; // fps
@@ -94,7 +94,7 @@ class AnalyzerContainer : public QWidget {
QPoint last_click_pos_;
bool ignore_next_click_;
Analyzer::Base* current_analyzer_;
Analyzer::Base *current_analyzer_;
EngineBase *engine_;
};

View File

@@ -24,6 +24,7 @@
#include "blockanalyzer.h"
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <QWidget>
@@ -37,8 +38,8 @@
const int BlockAnalyzer::kHeight = 2;
const int BlockAnalyzer::kWidth = 4;
const int BlockAnalyzer::kMinRows = 3; // arbituary
const int BlockAnalyzer::kMinColumns = 32; // arbituary
const int BlockAnalyzer::kMinRows = 3; // arbitrary
const int BlockAnalyzer::kMinColumns = 32; // arbitrary
const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const int BlockAnalyzer::kFadeSize = 90;
@@ -56,14 +57,13 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
fade_bars_(kFadeSize),
fade_pos_(1 << 8, 50),
fade_intensity_(1 << 8, 32),
step_(0)
{
step_(0) {
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
// mxcl says null pixmaps cause crashes, so let's play it safe
for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(1, 1));
}
@@ -79,7 +79,7 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
// all is explained in analyze()..
// +1 to counter -1 in maxSizes, trust me we need this!
columns_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
rows_ = static_cast<int>(static_cast<double>(height() + 1) / (kHeight + 1));
// this is the y-offset for drawing from the top of the widget
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
@@ -89,15 +89,15 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
if (rows_ != oldRows) {
barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
for (uint i = 0; i < kFadeSize; ++i)
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(kWidth, rows_ * (kHeight + 1)));
yscale_.resize(rows_ + 1);
const int PRE = 1, PRO = 1; // PRE and PRO allow us to restrict the range somewhat
for (int z = 0; z < rows_; ++z)
for (int z = 0; z < rows_; ++z) {
yscale_[z] = 1 - (log10(PRE + z) / log10(PRE + rows_ + PRO));
}
yscale_[rows_] = 0;
@@ -117,7 +117,7 @@ void BlockAnalyzer::determineStep() {
// the fall time of 30 is too slow on framerates above 50fps
const double fallTime = static_cast<double>(timeout() < 20 ? 20 * rows_ : 30 * rows_);
step_ = double(rows_ * timeout()) / fallTime;
step_ = static_cast<double>(rows_ * timeout()) / fallTime;
}
@@ -169,10 +169,12 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
if (static_cast<double>(y) > store_[x])
if (static_cast<double>(y) > store_[x]) {
y = static_cast<int>(store_[x] += step_);
else
}
else {
store_[x] = y;
}
// If y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
// if the fadeout is quite faded now, then display the new one
@@ -193,14 +195,15 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
}
for (int x = 0; x < store_.size(); ++x)
for (int x = 0; x < store_.size(); ++x) {
canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
}
p.drawPixmap(0, 0, canvas_);
}
static inline void adjustToLimits(int &b, int &f, int &amount) {
static inline void adjustToLimits(const int b, int &f, int &amount) {
// with a range of 0-255 and maximum adjustment of amount, maximise the difference between f and b
@@ -248,6 +251,8 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
private:
const QColor &c;
Q_DISABLE_COPY(OutputOnExit)
};
OutputOnExit allocateOnTheStack(fg);
@@ -278,20 +283,24 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
// check the saturation for the two colours is sufficient that hue alone can
// provide sufficient contrast
if (ds > amount / 2 && (bs > 125 && fs > 125))
if (ds > amount / 2 && (bs > 125 && fs > 125)) {
return fg;
else if (dv > amount / 2 && (bv > 125 && fv > 125))
}
else if (dv > amount / 2 && (bv > 125 && fv > 125)) {
return fg;
}
}
if (fs < 50 && ds < 40) {
// low saturation on a low saturation is sad
const int tmp = 50 - fs;
fs = 50;
if (static_cast<int>(amount) > tmp)
if (static_cast<int>(amount) > tmp) {
amount -= tmp;
else
}
else {
amount = 0;
}
}
// test that there is available value to honor our contrast requirement
@@ -308,19 +317,24 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
return QColor::fromHsv(fh, fs, fv);
}
if (fv > bv && bv > static_cast<int>(amount))
if (fv > bv && bv > static_cast<int>(amount)) {
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
}
if (fv < bv && fv > static_cast<int>(amount))
if (fv < bv && fv > static_cast<int>(amount)) {
return QColor::fromHsv(fh, fs, fv - amount);
}
if (fv > bv && (255 - fv > static_cast<int>(amount)))
if (fv > bv && (255 - fv > static_cast<int>(amount))) {
return QColor::fromHsv(fh, fs, fv + amount);
}
if (fv < bv && (255 - bv > static_cast<int>(amount)))
if (fv < bv && (255 - bv > static_cast<int>(amount))) {
return QColor::fromHsv(fh, fs, bv + amount);
}
return Qt::blue;
}
void BlockAnalyzer::paletteChange(const QPalette&) {
@@ -338,9 +352,10 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
bar()->fill(bg);
QPainter p(bar());
for (int y = 0; y < rows_; ++y)
for (int y = 0; y < rows_; ++y) {
// graduate the fg color
p.fillRect(0, y * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y), b + static_cast<int>(db * y)));
}
{
const QColor bg2 = palette().color(QPalette::Window).darker(112);
@@ -357,7 +372,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
const int r2 = bg2.red(), g2 = bg2.green(), b2 = bg2.blue();
// Precalculate all fade-bar pixmaps
for (uint y = 0; y < kFadeSize; ++y) {
for (int y = 0; y < kFadeSize; ++y) {
fade_bars_[y].fill(palette().color(QPalette::Window));
QPainter f(&fade_bars_[y]);
for (int z = 0; z < rows_; ++z) {
@@ -386,8 +401,10 @@ void BlockAnalyzer::drawBackground() {
if (!p.paintEngine()) return;
for (int x = 0; x < columns_; ++x)
for (int y = 0; y < rows_; ++y)
for (int x = 0; x < columns_; ++x) {
for (int y = 0; y < rows_; ++y) {
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
}
}
}

View File

@@ -43,17 +43,15 @@ const int BoomAnalyzer::kColumnWidth = 4;
const int BoomAnalyzer::kMaxBandCount = 256;
const int BoomAnalyzer::kMinBandCount = 32;
const char* BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
const char *BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
BoomAnalyzer::BoomAnalyzer(QWidget *parent)
: Analyzer::Base(parent, 9),
bands_(0),
scope_(kMinBandCount),
fg_(palette().color(QPalette::Highlight)),
K_barHeight_(1.271) // 1.471
,
F_peakSpeed_(1.103) // 1.122
,
K_barHeight_(1.271), // 1.471
F_peakSpeed_(1.103), // 1.122
F_(1.0),
bar_height_(kMaxBandCount, 0),
peak_height_(kMaxBandCount, 0),
@@ -65,15 +63,15 @@ BoomAnalyzer::BoomAnalyzer(QWidget* parent)
}
void BoomAnalyzer::changeK_barHeight(int newValue) {
void BoomAnalyzer::changeK_barHeight(const int newValue) {
K_barHeight_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
void BoomAnalyzer::changeF_peakSpeed(const int newValue) {
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
}
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
void BoomAnalyzer::resizeEvent(QResizeEvent *e) {
QWidget::resizeEvent(e);
@@ -83,7 +81,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
scope_.resize(bands_);
F_ = double(HEIGHT) / (log10(256) * double(1.1) /*<- max. amplitude*/);
F_ = static_cast<double>(HEIGHT) / (log10(256) * static_cast<double>(1.1) /*<- max. amplitude*/);
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size());
@@ -101,7 +99,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
}
void BoomAnalyzer::transform(Scope& s) {
void BoomAnalyzer::transform(Scope &s) {
fht_->spectrum(s.data());
fht_->scale(s.data(), 1.0 / 50);
@@ -110,7 +108,7 @@ void BoomAnalyzer::transform(Scope& s) {
}
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
void BoomAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame) {
if (!new_frame || engine_->state() == Engine::Paused) {
p.drawPixmap(0, 0, canvas_);
@@ -160,8 +158,9 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
y = height() - static_cast<int>(bar_height_[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
canvas_painter.setPen(fg_);
if (bar_height_[i] > 0)
if (bar_height_[i] > 0) {
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
}
y = height() - static_cast<int>(peak_height_[i]);
canvas_painter.setPen(palette().color(QPalette::Midlight));

View File

@@ -42,10 +42,10 @@ class BoomAnalyzer : public Analyzer::Base {
public:
Q_INVOKABLE explicit BoomAnalyzer(QWidget*);
static const char* kName;
static const char *kName;
void transform(Analyzer::Scope &s) override;
void analyze(QPainter &p, const Analyzer::Scope&, bool new_frame) override;
void analyze(QPainter &p, const Analyzer::Scope&, const bool new_frame) override;
public slots:
void changeK_barHeight(int);

View File

@@ -26,47 +26,52 @@
#include <cmath>
#include <QVector>
#include <QtMath>
FHT::FHT(uint n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? static_cast<int>(-1) : static_cast<int>(n)) {
FHT::FHT(int n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : n) {
if (n > 3) {
buf_vector_.resize(num_);
tab_vector_.resize(num_ * 2);
makeCasTable();
}
}
FHT::~FHT() {}
FHT::~FHT() = default;
int FHT::sizeExp() const { return exp2_; }
int FHT::size() const { return num_; }
float* FHT::buf_() { return buf_vector_.data(); }
float* FHT::tab_() { return tab_vector_.data(); }
int* FHT::log_() { return log_vector_.data(); }
float *FHT::buf_() { return buf_vector_.data(); }
float *FHT::tab_() { return tab_vector_.data(); }
int *FHT::log_() { return log_vector_.data(); }
void FHT::makeCasTable(void) {
float* costab = tab_();
float* sintab = tab_() + num_ / 2 + 1;
float *costab = tab_();
float *sintab = tab_() + num_ / 2 + 1;
for (int ul = 0; ul < num_; ul++) {
float d = M_PI * ul / (num_ / 2);
float d = M_PI * ul / (num_ / 2); // NOLINT(bugprone-integer-division)
*costab = *sintab = cos(d);
costab += 2;
sintab += 2;
if (sintab > tab_() + num_ * 2) sintab = tab_() + 1;
}
}
void FHT::scale(float* p, float d) {
void FHT::scale(float *p, float d) const {
for (int i = 0; i < (num_ / 2); i++) *p++ *= d;
}
void FHT::ewma(float* d, float* s, float w) {
void FHT::ewma(float *d, float *s, float w) const {
for (int i = 0; i < (num_ / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
}
void FHT::logSpectrum(float* out, float* p) {
void FHT::logSpectrum(float *out, float *p) {
int n = num_ / 2, i = 0, j = 0, k = 0, *r = nullptr;
if (log_vector_.size() < n) {
@@ -93,47 +98,60 @@ void FHT::logSpectrum(float* out, float* p) {
}
void FHT::semiLogSpectrum(float* p) {
void FHT::semiLogSpectrum(float *p) {
power2(p);
for (int i = 0; i < (num_ / 2); i++, p++) {
float e = 10.0 * log10(sqrt(*p / 2));
*p = e < 0 ? 0 : e;
}
}
void FHT::spectrum(float* p) {
void FHT::spectrum(float *p) {
power2(p);
for (int i = 0; i < (num_ / 2); i++, p++)
for (int i = 0; i < (num_ / 2); i++, p++) {
*p = static_cast<float>(sqrt(*p / 2));
}
}
void FHT::power(float* p) {
void FHT::power(float *p) {
power2(p);
for (int i = 0; i < (num_ / 2); i++) *p++ /= 2;
}
void FHT::power2(float* p) {
void FHT::power2(float *p) {
_transform(p, num_, 0);
*p = static_cast<float>(2 * pow(*p, 2));
p++;
float* q = p + num_ - 2;
float *q = p + num_ - 2;
for (int i = 1; i < (num_ / 2); i++) {
*p = static_cast<float>(pow(*p, 2) + pow(*q, 2));
p++;
q--;
}
}
void FHT::transform(float* p) {
if (num_ == 8)
void FHT::transform(float *p) {
if (num_ == 8) {
transform8(p);
else
}
else {
_transform(p, num_, 0);
}
}
void FHT::transform8(float* p) {
void FHT::transform8(float *p) {
float a = 0.0, b = 0.0, c = 0.0, d = 0.0, e = 0.0, f = 0.0, g = 0.0, h = 0.0, b_f2 = 0.0, d_h2 = 0.0;
float a_c_eg = 0.0, a_ce_g = 0.0, ac_e_g = 0.0, aceg = 0.0, b_df_h = 0.0, bdfh = 0.0;
@@ -162,7 +180,7 @@ void FHT::transform8(float* p) {
}
void FHT::_transform(float* p, int n, int k) {
void FHT::_transform(float *p, int n, int k) {
if (n == 8) {
transform8(p + k);

View File

@@ -41,9 +41,9 @@ class FHT {
QVector<float> tab_vector_;
QVector<int> log_vector_;
float* buf_();
float* tab_();
int* log_();
float *buf_();
float *tab_();
int *log_();
/**
* Create a table of "cas" (cosine and sine) values.
@@ -63,12 +63,12 @@ class FHT {
* should be at least 3. Values of more than 3 need a trigonometry table.
* @see makeCasTable()
*/
explicit FHT(int);
explicit FHT(uint);
~FHT();
int sizeExp() const;
int size() const;
void scale(float*, float);
void scale(float*, float) const;
/**
* Exponentially Weighted Moving Average (EWMA) filter.
@@ -76,7 +76,7 @@ class FHT {
* @param s is fresh input.
* @param w is the weighting factor.
*/
void ewma(float* d, float* s, float w);
void ewma(float *d, float *s, float w) const;
/**
* Logarithmic audio spectrum. Maps semi-logarithmic spectrum
@@ -85,7 +85,7 @@ class FHT {
* @param p is the input array.
* @param out is the spectrum.
*/
void logSpectrum(float* out, float* p);
void logSpectrum(float *out, float *p);
/**
* Semi-logarithmic audio spectrum.
@@ -117,7 +117,7 @@ class FHT {
/**
* Discrete Hartley transform of data sets with 8 values.
*/
void transform8(float*);
static void transform8(float*);
void transform(float*);
};

View File

@@ -50,13 +50,13 @@ const int Rainbow::RainbowAnalyzer::kRainbowHeight[] = {21, 16};
const int Rainbow::RainbowAnalyzer::kRainbowOverlap[] = {13, 15};
const int Rainbow::RainbowAnalyzer::kSleepingHeight[] = {24, 33};
const char* Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
const char* Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02f;
const char *Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
const char *Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02F;
Rainbow::RainbowAnalyzer::RainbowType Rainbow::RainbowAnalyzer::rainbowtype;
Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* parent)
Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
: Analyzer::Base(parent, 9),
timer_id_(startTimer(kFrameIntervalMs)),
frame_(0),
@@ -64,8 +64,7 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
available_rainbow_width_(0),
px_per_frame_(0),
x_offset_(0),
background_brush_(QColor(0x0f, 0x43, 0x73))
{
background_brush_(QColor(0x0f, 0x43, 0x73)) {
rainbowtype = rbtype;
cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
@@ -73,7 +72,7 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
memset(history_, 0, sizeof(history_));
for (int i = 0; i < kRainbowBands; ++i) {
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255), kRainbowHeight[rainbowtype] / kRainbowBands, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255), kRainbowHeight[rainbowtype] / kRainbowBands, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin); // NOLINT(bugprone-integer-division)
// pow constants computed so that
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
@@ -82,9 +81,9 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
}
void Rainbow::RainbowAnalyzer::transform(Scope& s) { fht_->spectrum(s.data()); }
void Rainbow::RainbowAnalyzer::transform(Scope &s) { fht_->spectrum(s.data()); }
void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent* e) {
void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent *e) {
if (e->timerId() == timer_id_) {
frame_ = (frame_ + 1) % kFrameCount[rainbowtype];
@@ -95,7 +94,7 @@ void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent* e) {
}
void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent *e) {
Q_UNUSED(e);
@@ -109,7 +108,7 @@ void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
}
void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_frame) {
void Rainbow::RainbowAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_frame) {
// Discard the second half of the transform
const int scope_size = static_cast<int>(s.size() / 2);
@@ -117,7 +116,7 @@ void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bo
if ((new_frame && is_playing_) || (buffer_[0].isNull() && buffer_[1].isNull())) {
// Transform the music into rainbows!
for (int band = 0; band < kRainbowBands; ++band) {
float* band_start = history_ + band * kHistorySize;
float *band_start = history_ + band * kHistorySize;
// Move the history of each band across by 1 frame.
memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float));
@@ -139,8 +138,8 @@ void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bo
// Create polylines for the rainbows.
QPointF polyline[kRainbowBands * kHistorySize];
QPointF* dest = polyline;
float* source = history_;
QPointF *dest = polyline;
float *source = history_;
const float top_of = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight[rainbowtype]) / 2;
for (int band = 0; band < kRainbowBands; ++band) {
@@ -204,8 +203,8 @@ void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bo
}
Rainbow::NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
Rainbow::NyanCatAnalyzer::NyanCatAnalyzer(QWidget *parent)
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Nyancat, parent) {}
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget *parent)
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Dash, parent) {}

View File

@@ -47,10 +47,10 @@ class RainbowAnalyzer : public Analyzer::Base {
public:
enum RainbowType {
Nyancat = 0,
Dash = 1
Dash = 1
};
RainbowAnalyzer(const RainbowType &rbtype, QWidget *parent);
RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
protected:
void transform(Analyzer::Scope&) override;
@@ -93,7 +93,7 @@ class RainbowAnalyzer : public Analyzer::Base {
private:
// "constants" that get initialized in the constructor
float band_scale_[kRainbowBands];
float band_scale_[kRainbowBands]{};
QPen colors_[kRainbowBands];
// Rainbow Nyancat & Dash
@@ -104,9 +104,9 @@ class RainbowAnalyzer : public Analyzer::Base {
int frame_;
// The y positions of each point on the rainbow.
float history_[kHistorySize * kRainbowBands];
float history_[kHistorySize * kRainbowBands]{};
// A cache of the last frame's rainbow,
// A cache of the last frame's rainbow,
// so it can be used in the next frame.
QPixmap buffer_[2];
int current_buffer_;
@@ -118,7 +118,7 @@ class RainbowAnalyzer : public Analyzer::Base {
// X spacing between each point in the polyline.
int px_per_frame_;
// Amount the buffer_ is shifted to the left (off the edge of the widget)
// Amount the buffer_ is shifted to the left (off the edge of the widget)
// to make the rainbow extend from 0 to available_rainbow_width_.
int x_offset_;
@@ -129,18 +129,18 @@ class NyanCatAnalyzer : public RainbowAnalyzer {
Q_OBJECT
public:
Q_INVOKABLE explicit NyanCatAnalyzer(QWidget* parent);
static const char* kName;
Q_INVOKABLE explicit NyanCatAnalyzer(QWidget *parent);
static const char *kName;
};
class RainbowDashAnalyzer : public RainbowAnalyzer {
Q_OBJECT
public:
Q_INVOKABLE explicit RainbowDashAnalyzer(QWidget* parent);
Q_INVOKABLE explicit RainbowDashAnalyzer(QWidget *parent);
static const char* kName;
static const char *kName;
};
}

View File

@@ -43,9 +43,9 @@
#include "scrobbler/lastfmimport.h"
const char *SCollection::kSongsTable = "songs";
const char *SCollection::kFtsTable = "songs_fts";
const char *SCollection::kDirsTable = "directories";
const char *SCollection::kSubdirsTable = "subdirectories";
const char *SCollection::kFtsTable = "songs_fts";
SCollection::SCollection(Application *app, QObject *parent)
: QObject(parent),
@@ -62,7 +62,7 @@ SCollection::SCollection(Application *app, QObject *parent)
backend()->moveToThread(app->database()->thread());
qLog(Debug) << backend_ << "moved to thread" << app->database()->thread();
backend_->Init(app->database(), Song::Source_Collection, kSongsTable, kDirsTable, kSubdirsTable, kFtsTable);
backend_->Init(app->database(), app->task_manager(), Song::Source_Collection, kSongsTable, kFtsTable, kDirsTable, kSubdirsTable);
model_ = new CollectionModel(backend_, app_, this);
@@ -78,7 +78,7 @@ SCollection::~SCollection() {
}
if (watcher_thread_) {
watcher_thread_->exit();
watcher_thread_->wait(5000 /* five seconds */);
watcher_thread_->wait(5000);
}
backend_->deleteLater();
@@ -97,6 +97,7 @@ void SCollection::Init() {
watcher_->set_backend(backend_);
watcher_->set_task_manager(app_->task_manager());
QObject::connect(backend_, &CollectionBackend::Error, this, &SCollection::Error);
QObject::connect(backend_, &CollectionBackend::DirectoryDiscovered, watcher_, &CollectionWatcher::AddDirectory);
QObject::connect(backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, backend_, &CollectionBackend::AddOrUpdateSongs);
@@ -107,6 +108,7 @@ void SCollection::Init() {
QObject::connect(watcher_, &CollectionWatcher::SubdirsDiscovered, backend_, &CollectionBackend::AddOrUpdateSubdirs);
QObject::connect(watcher_, &CollectionWatcher::SubdirsMTimeUpdated, backend_, &CollectionBackend::AddOrUpdateSubdirs);
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);
@@ -132,7 +134,7 @@ void SCollection::Exit() {
void SCollection::ExitReceived() {
QObject *obj = qobject_cast<QObject*>(sender());
QObject *obj = sender();
QObject::disconnect(obj, nullptr, this, nullptr);
qLog(Debug) << obj << "successfully exited.";
wait_for_exit_.removeAll(obj);
@@ -148,8 +150,8 @@ void SCollection::AbortScan() { watcher_->Stop(); }
void SCollection::Rescan(const SongList &songs) {
qLog(Debug) << "Rescan" << songs.size() << "songs";
if (!songs.isEmpty()) watcher_->RescanTracksAsync(songs);
qLog(Debug) << "Rescan" << songs.size() << "songs";
if (!songs.isEmpty()) watcher_->RescanTracksAsync(songs);
}

View File

@@ -47,9 +47,9 @@ class SCollection : public QObject {
~SCollection() override;
static const char *kSongsTable;
static const char *kFtsTable;
static const char *kDirsTable;
static const char *kSubdirsTable;
static const char *kFtsTable;
void Init();
void Exit();
@@ -79,6 +79,7 @@ class SCollection : public QObject {
void ExitReceived();
signals:
void Error(QString);
void ExitFinished();
private:

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,8 @@
#include "config.h"
#include <optional>
#include <QtGlobal>
#include <QObject>
#include <QFileInfo>
@@ -35,10 +37,12 @@
#include <QSqlQuery>
#include "core/song.h"
#include "core/sqlquery.h"
#include "collectionquery.h"
#include "directory.h"
class QThread;
class TaskManager;
class Database;
class SmartPlaylistSearch;
@@ -50,14 +54,14 @@ class CollectionBackendInterface : public QObject {
struct Album {
Album() {}
Album(const QString &_album_artist, const QString &_album, const QUrl &_art_automatic, const QUrl &_art_manual, const QList<QUrl> &_urls, const Song::FileType _filetype, const QString &_cue_path) :
album_artist(_album_artist),
album(_album),
art_automatic(_art_automatic),
art_manual(_art_manual),
urls(_urls),
filetype(_filetype),
cue_path(_cue_path) {}
Album(const QString &_album_artist, const QString &_album, const QUrl &_art_automatic, const QUrl &_art_manual, const QList<QUrl> &_urls, const Song::FileType _filetype, const QString &_cue_path)
: album_artist(_album_artist),
album(_album),
art_automatic(_art_automatic),
art_manual(_art_manual),
urls(_urls),
filetype(_filetype),
cue_path(_cue_path) {}
QString album_artist;
QString album;
@@ -83,6 +87,7 @@ class CollectionBackendInterface : public QObject {
virtual void UpdateTotalAlbumCountAsync() = 0;
virtual SongList FindSongsInDirectory(const int id) = 0;
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
virtual SubdirectoryList SubdirsInDirectory(const int id) = 0;
virtual DirectoryList GetAllDirectories() = 0;
virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0;
@@ -106,8 +111,10 @@ class CollectionBackendInterface : public QObject {
virtual Song GetSongById(const int id) = 0;
virtual SongList GetSongsByFingerprint(const QString &fingerprint) = 0;
// Returns all sections of a song with the given filename. If there's just one section the resulting list will have it's size equal to 1.
virtual SongList GetSongsByUrl(const QUrl &url) = 0;
virtual SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) = 0;
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
@@ -123,11 +130,13 @@ class CollectionBackend : public CollectionBackendInterface {
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
void Init(Database *db, const Song::Source source, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table);
void Init(Database *db, TaskManager *task_manager, const Song::Source source, const QString &songs_table, const QString &fts_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
void Close();
void ExitAsync();
void ReportErrors(const CollectionQuery &query);
Database *db() const override { return db_; }
QString songs_table() const override { return songs_table_; }
@@ -143,6 +152,7 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateTotalAlbumCountAsync() override;
SongList FindSongsInDirectory(const int id) override;
SongList SongsWithMissingFingerprint(const int id) override;
SubdirectoryList SubdirsInDirectory(const int id) override;
DirectoryList GetAllDirectories() override;
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
@@ -160,23 +170,24 @@ class CollectionBackend : public CollectionBackendInterface {
AlbumList GetCompilationAlbums(const QueryOptions &opt = QueryOptions()) override;
AlbumList GetAlbumsByArtist(const QString &artist, const QueryOptions &opt = QueryOptions()) override;
void UpdateManualAlbumArtAsync(const QString &album_artist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) override;
void UpdateManualAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url, const bool clear_art_automatic = false) override;
void UpdateAutomaticAlbumArtAsync(const QString &effective_albumartist, const QString &album, const QUrl &cover_url) override;
Album GetAlbumArt(const QString &album_artist, const QString &album) override;
Album GetAlbumArt(const QString &effective_albumartist, const QString &album) override;
Song GetSongById(const int id) override;
SongList GetSongsById(const QList<int> &ids);
SongList GetSongsById(const QStringList &ids);
SongList GetSongsByForeignId(const QStringList &ids, const QString &table, const QString &column);
SongList GetSongsByUrl(const QUrl &url) override;
SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) override;
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
void AddDirectory(const QString &path) override;
void RemoveDirectory(const Directory &dir) override;
SongList ExecCollectionQuery(CollectionQuery *query);
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
void IncrementPlayCountAsync(const int id);
void IncrementSkipCountAsync(const int id, const float progress);
@@ -187,12 +198,15 @@ class CollectionBackend : public CollectionBackendInterface {
Song GetSongBySongId(const QString &song_id);
SongList GetSongsBySongId(const QStringList &song_ids);
SongList GetAllSongs();
SongList FindSongs(const SmartPlaylistSearch &search);
SongList GetSongsByFingerprint(const QString &fingerprint) override;
SongList SmartPlaylistsGetAllSongs();
SongList SmartPlaylistsFindSongs(const SmartPlaylistSearch &search);
Song::Source Source() const;
void AddOrUpdateSongsAsync(const SongList &songs);
void UpdateSongsBySongIDAsync(const SongMap &new_songs);
void UpdateSongRatingAsync(const int id, const double rating);
void UpdateSongsRatingAsync(const QList<int> &ids, const double rating);
@@ -204,6 +218,7 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateTotalArtistCount();
void UpdateTotalAlbumCount();
void AddOrUpdateSongs(const SongList &songs);
void UpdateSongsBySongID(const SongMap &new_songs);
void UpdateMTimesOnly(const SongList &songs);
void DeleteSongs(const SongList &songs);
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
@@ -215,7 +230,7 @@ class CollectionBackend : public CollectionBackendInterface {
void IncrementPlayCount(const int id);
void IncrementSkipCount(const int id, const float progress);
void ResetStatistics(const int id);
void SongPathChanged(const Song &song, const QFileInfo &new_file);
void SongPathChanged(const Song &song, const QFileInfo &new_file, const std::optional<int> new_collection_directory_id);
SongList GetSongsBy(const QString &artist, const QString &album, const QString &title);
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed);
@@ -224,6 +239,9 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateSongRating(const int id, const double rating);
void UpdateSongsRating(const QList<int> &id_list, const double rating);
void UpdateLastSeen(const int directory_id, const int expire_unavailable_songs_days);
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
signals:
void DirectoryDiscovered(Directory, SubdirectoryList);
void DirectoryDeleted(Directory);
@@ -241,6 +259,8 @@ class CollectionBackend : public CollectionBackendInterface {
void ExitFinished();
void Error(QString);
private:
struct CompilationInfo {
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
@@ -252,7 +272,7 @@ class CollectionBackend : public CollectionBackendInterface {
int has_not_compilation_detected;
};
void UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
bool UpdateCompilations(const QSqlDatabase &db, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const QueryOptions &opt = QueryOptions());
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt = QueryOptions());
SubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
@@ -265,6 +285,7 @@ class CollectionBackend : public CollectionBackendInterface {
private:
Database *db_;
TaskManager *task_manager_;
Song::Source source_;
QString songs_table_;
QString dirs_table_;

View File

@@ -51,7 +51,7 @@ void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
QStandardItem *item = new QStandardItem(dir.path);
item->setData(dir.id, kIdRole);
item->setIcon(dir_icon_);
storage_ << std::make_shared<FilesystemMusicStorage>(dir.path);
storage_ << std::make_shared<FilesystemMusicStorage>(dir.path, dir.id);
appendRow(item);
}
@@ -94,13 +94,10 @@ QVariant CollectionDirectoryModel::data(const QModelIndex &idx, int role) const
case MusicStorage::Role_Storage:
case MusicStorage::Role_StorageForceConnect:
return QVariant::fromValue(storage_[idx.row()]);
case MusicStorage::Role_FreeSpace:
return Utilities::FileSystemFreeSpace(data(idx, Qt::DisplayRole).toString());
return Utilities::FileSystemFreeSpace(data(idx, Qt::DisplayRole).toString());
case MusicStorage::Role_Capacity:
return Utilities::FileSystemCapacity(data(idx, Qt::DisplayRole).toString());
return Utilities::FileSystemCapacity(data(idx, Qt::DisplayRole).toString());
default:
return QStandardItemModel::data(idx, role);
}

View File

@@ -60,7 +60,7 @@ class CollectionDirectoryModel : public QStandardItemModel {
static const int kIdRole = Qt::UserRole + 1;
QIcon dir_icon_;
CollectionBackend* backend_;
CollectionBackend *backend_;
QList<std::shared_ptr<MusicStorage>> storage_;
};

View File

@@ -20,6 +20,8 @@
#include "config.h"
#include <memory>
#include <QApplication>
#include <QWidget>
#include <QObject>
@@ -65,7 +67,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegularExpression("\\bfts"), "");
ui_->filter->setToolTip(
ui_->search_field->setToolTip(
QString("<html><head/><body><p>") +
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
QString(" ") +
@@ -85,7 +87,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
QString("</p></body></html>")
);
QObject::connect(ui_->filter, &QSearchField::returnPressed, this, &CollectionFilterWidget::ReturnPressed);
QObject::connect(ui_->search_field, &QSearchField::returnPressed, this, &CollectionFilterWidget::ReturnPressed);
QObject::connect(filter_delay_, &QTimer::timeout, this, &CollectionFilterWidget::FilterDelayTimeout);
filter_delay_->setInterval(kFilterDelay);
@@ -133,7 +135,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
collection_menu_->addSeparator();
ui_->options->setMenu(collection_menu_);
QObject::connect(ui_->filter, &QSearchField::textChanged, this, &CollectionFilterWidget::FilterTextChanged);
QObject::connect(ui_->search_field, &QSearchField::textChanged, this, &CollectionFilterWidget::FilterTextChanged);
ReloadSettings();
@@ -141,6 +143,51 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
CollectionFilterWidget::~CollectionFilterWidget() { delete ui_; }
void CollectionFilterWidget::Init(CollectionModel *model) {
if (model_) {
QObject::disconnect(model_, nullptr, this, nullptr);
QObject::disconnect(model_, nullptr, group_by_dialog_.get(), nullptr);
QObject::disconnect(group_by_dialog_.get(), nullptr, model_, nullptr);
QList<QAction*> filter_ages = filter_ages_.keys();
for (QAction *action : filter_ages) {
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
}
}
model_ = model;
// Connect signals
QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_.get(), &GroupByDialog::CollectionGroupingChanged);
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
QObject::connect(group_by_dialog_.get(), &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
QList<QAction*> filter_ages = filter_ages_.keys();
for (QAction *action : filter_ages) {
int age = filter_ages_[action];
QObject::connect(action, &QAction::triggered, this, [this, age]() { model_->SetFilterAge(age); } );
}
// Load settings
if (!settings_group_.isEmpty()) {
QSettings s;
s.beginGroup(settings_group_);
int version = 0;
if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt();
if (version == 1) {
model_->SetGroupBy(CollectionModel::Grouping(
CollectionModel::GroupBy(s.value(group_by(1), static_cast<int>(CollectionModel::GroupBy_AlbumArtist)).toInt()),
CollectionModel::GroupBy(s.value(group_by(2), static_cast<int>(CollectionModel::GroupBy_AlbumDisc)).toInt()),
CollectionModel::GroupBy(s.value(group_by(3), static_cast<int>(CollectionModel::GroupBy_None)).toInt())));
}
else {
model_->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_AlbumDisc, CollectionModel::GroupBy_None));
}
s.endGroup();
}
}
void CollectionFilterWidget::ReloadSettings() {
QSettings s;
@@ -148,7 +195,7 @@ void CollectionFilterWidget::ReloadSettings() {
int iconsize = s.value(AppearanceSettingsPage::kIconSizeConfigureButtons, 20).toInt();
s.endGroup();
ui_->options->setIconSize(QSize(iconsize, iconsize));
ui_->filter->setIconSize(iconsize);
ui_->search_field->setIconSize(iconsize);
}
@@ -255,7 +302,7 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(QObject *parent) {
}
QAction *CollectionFilterWidget::CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping &grouping) {
QAction *CollectionFilterWidget::CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping grouping) {
QAction *ret = new QAction(text, parent);
ret->setCheckable(true);
@@ -281,7 +328,7 @@ void CollectionFilterWidget::SaveGroupBy() {
void CollectionFilterWidget::ShowGroupingManager() {
if (!groupings_manager_) {
groupings_manager_.reset(new SavedGroupingManager);
groupings_manager_ = std::make_unique<SavedGroupingManager>();
}
groupings_manager_->SetFilter(this);
groupings_manager_->UpdateModel();
@@ -289,56 +336,22 @@ void CollectionFilterWidget::ShowGroupingManager() {
}
bool CollectionFilterWidget::SearchFieldHasFocus() const {
void CollectionFilterWidget::FocusOnFilter(QKeyEvent *event) {
ui_->filter->setFocus();
QApplication::sendEvent(ui_->filter, event);
return ui_->search_field->hasFocus();
}
void CollectionFilterWidget::SetCollectionModel(CollectionModel *model) {
void CollectionFilterWidget::FocusSearchField() {
if (model_) {
QObject::disconnect(model_, nullptr, this, nullptr);
QObject::disconnect(model_, nullptr, group_by_dialog_.get(), nullptr);
QObject::disconnect(group_by_dialog_.get(), nullptr, model_, nullptr);
QList<QAction*> filter_ages = filter_ages_.keys();
for (QAction *action : filter_ages) {
QObject::disconnect(action, &QAction::triggered, model_, nullptr);
}
}
ui_->search_field->setFocus();
model_ = model;
}
// Connect signals
QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_.get(), &GroupByDialog::CollectionGroupingChanged);
QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged);
QObject::connect(group_by_dialog_.get(), &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy);
void CollectionFilterWidget::FocusOnFilter(QKeyEvent *event) {
QList<QAction*> filter_ages = filter_ages_.keys();
for (QAction *action : filter_ages) {
int age = filter_ages_[action];
QObject::connect(action, &QAction::triggered, [this, age]() { model_->SetFilterAge(age); } );
}
// Load settings
if (!settings_group_.isEmpty()) {
QSettings s;
s.beginGroup(settings_group_);
int version = 0;
if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt();
if (version == 1) {
model_->SetGroupBy(CollectionModel::Grouping(
CollectionModel::GroupBy(s.value(group_by(1), int(CollectionModel::GroupBy_AlbumArtist)).toInt()),
CollectionModel::GroupBy(s.value(group_by(2), int(CollectionModel::GroupBy_AlbumDisc)).toInt()),
CollectionModel::GroupBy(s.value(group_by(3), int(CollectionModel::GroupBy_None)).toInt())));
}
else {
model_->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_AlbumDisc, CollectionModel::GroupBy_None));
}
s.endGroup();
}
ui_->search_field->setFocus();
QApplication::sendEvent(ui_->search_field, event);
}
@@ -354,16 +367,16 @@ void CollectionFilterWidget::GroupByClicked(QAction *action) {
}
void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping &g) {
void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g) {
if (!settings_group_.isEmpty()) {
// Save the settings
QSettings s;
s.beginGroup(settings_group_);
s.setValue(group_by_version(), 1);
s.setValue(group_by(1), int(g[0]));
s.setValue(group_by(2), int(g[1]));
s.setValue(group_by(3), int(g[2]));
s.setValue(group_by(1), static_cast<int>(g[0]));
s.setValue(group_by(2), static_cast<int>(g[1]));
s.setValue(group_by(3), static_cast<int>(g[2]));
s.endGroup();
}
@@ -372,7 +385,7 @@ void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping &g)
}
void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Grouping &g) {
void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Grouping g) {
for (QAction *action : group_by_group_->actions()) {
if (action->property("group_by").isNull()) continue;
@@ -391,20 +404,20 @@ void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Groupin
}
void CollectionFilterWidget::SetFilterHint(const QString &hint) {
ui_->filter->setPlaceholderText(hint);
ui_->search_field->setPlaceholderText(hint);
}
void CollectionFilterWidget::SetQueryMode(QueryOptions::QueryMode query_mode) {
ui_->filter->clear();
ui_->filter->setEnabled(query_mode == QueryOptions::QueryMode_All);
ui_->search_field->clear();
ui_->search_field->setEnabled(query_mode == QueryOptions::QueryMode_All);
model_->SetFilterQueryMode(query_mode);
}
void CollectionFilterWidget::ShowInCollection(const QString &search) {
ui_->filter->setText(search);
ui_->search_field->setText(search);
}
void CollectionFilterWidget::SetAgeFilterEnabled(bool enabled) {
@@ -433,7 +446,7 @@ void CollectionFilterWidget::keyReleaseEvent(QKeyEvent *e) {
break;
case Qt::Key_Escape:
ui_->filter->clear();
ui_->search_field->clear();
e->accept();
break;
}
@@ -461,9 +474,9 @@ void CollectionFilterWidget::FilterTextChanged(const QString &text) {
void CollectionFilterWidget::FilterDelayTimeout() {
emit Filter(ui_->filter->text());
emit Filter(ui_->search_field->text());
if (filter_applies_to_model_) {
model_->SetFilterText(ui_->filter->text());
model_->SetFilterText(ui_->search_field->text());
}
}

View File

@@ -27,7 +27,7 @@
#include <QWidget>
#include <QObject>
#include <QMap>
#include <QHash>
#include <QString>
#include "collectionquery.h"
@@ -41,7 +41,6 @@ class QKeyEvent;
class GroupByDialog;
class SavedGroupingManager;
class SettingsDialog;
class Ui_CollectionFilterWidget;
class CollectionFilterWidget : public QWidget {
@@ -59,6 +58,8 @@ class CollectionFilterWidget : public QWidget {
AlwaysDelayed,
};
void Init(CollectionModel *model);
static QActionGroup *CreateGroupByActions(QObject *parent);
void UpdateGroupByActions();
@@ -74,7 +75,6 @@ class CollectionFilterWidget : public QWidget {
void SetSettingsGroup(const QString &group) { settings_group_ = group; }
void SetSettingsPrefix(const QString &prefix) { settings_prefix_ = prefix; }
void SetCollectionModel(CollectionModel *model);
QString group_by();
QString group_by_version();
@@ -82,6 +82,9 @@ class CollectionFilterWidget : public QWidget {
void ReloadSettings();
bool SearchFieldHasFocus() const;
void FocusSearchField();
public slots:
void SetQueryMode(QueryOptions::QueryMode query_mode);
void FocusOnFilter(QKeyEvent *e);
@@ -96,7 +99,7 @@ class CollectionFilterWidget : public QWidget {
void keyReleaseEvent(QKeyEvent *e) override;
private slots:
void GroupingChanged(const CollectionModel::Grouping &g);
void GroupingChanged(const CollectionModel::Grouping g);
void GroupByClicked(QAction *action);
void SaveGroupBy();
void ShowGroupingManager();
@@ -105,8 +108,8 @@ class CollectionFilterWidget : public QWidget {
void FilterDelayTimeout();
private:
static QAction *CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping &grouping);
void CheckCurrentGrouping(const CollectionModel::Grouping &g);
static QAction *CreateGroupByAction(const QString &text, QObject *parent, const CollectionModel::Grouping grouping);
void CheckCurrentGrouping(const CollectionModel::Grouping g);
private:
Ui_CollectionFilterWidget *ui_;
@@ -114,13 +117,12 @@ class CollectionFilterWidget : public QWidget {
std::unique_ptr<GroupByDialog> group_by_dialog_;
std::unique_ptr<SavedGroupingManager> groupings_manager_;
SettingsDialog *settings_dialog_;
QMenu *filter_age_menu_;
QMenu *group_by_menu_;
QMenu *collection_menu_;
QActionGroup *group_by_group_;
QMap<QAction*, int> filter_ages_;
QHash<QAction*, int> filter_ages_;
QTimer *filter_delay_;

View File

@@ -30,7 +30,7 @@
<number>0</number>
</property>
<item>
<widget class="QSearchField" name="filter" native="true">
<widget class="QSearchField" name="search_field" native="true">
<property name="placeholderText" stdset="0">
<string>Enter search terms here</string>
</property>

View File

@@ -50,6 +50,9 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
int container_level;
Song metadata;
CollectionItem *compilation_artist_node_;
private:
Q_DISABLE_COPY(CollectionItem)
};
#endif // COLLECTIONITEM_H

View File

@@ -125,10 +125,9 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
if (!event || !view) return false;
QHelpEvent *he = static_cast<QHelpEvent*>(event);
QString text = displayText(idx.data(), QLocale::system());
if (text.isEmpty() || !he) return false;
if (text.isEmpty() || !event) return false;
switch (event->type()) {
case QEvent::ToolTip: {
@@ -138,12 +137,12 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
bool is_elided = displayed_text.width() < real_text.width();
if (is_elided) {
QToolTip::showText(he->globalPos(), text, view);
QToolTip::showText(event->globalPos(), text, view);
}
else if (idx.data(Qt::ToolTipRole).isValid()) {
// If the item has a tooltip text, display it
QString tooltip_text = idx.data(Qt::ToolTipRole).toString();
QToolTip::showText(he->globalPos(), tooltip_text, view);
QToolTip::showText(event->globalPos(), tooltip_text, view);
}
else {
// in case that another text was previously displayed
@@ -156,7 +155,7 @@ bool CollectionItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *vie
return true;
case QEvent::WhatsThis:
QWhatsThis::showText(he->globalPos(), text, view);
QWhatsThis::showText(event->globalPos(), text, view);
return true;
default:

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