Compare commits

..

519 Commits
0.9.2 ... 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
Jonas Kvinge
655c872ac9 Release 0.9.3 2021-04-18 18:44:43 +02:00
Strawbs Bot
cb0f2b48fd Update translations 2021-04-18 01:03:10 +02:00
Strawbs Bot
20a84bdefc Update translations 2021-04-17 01:02:28 +02:00
Jonas Kvinge
83ad7d4935 Set exit before quit is called 2021-04-16 18:34:38 +02:00
Jonas Kvinge
ae2ca175d3 Fix remembering hidden state
Prevent SetHiddenInTray() or Exit() from being called again after
qApp->quit() in MainWindow::closeEvent()
2021-04-16 18:27:24 +02:00
Jonas Kvinge
c855108b94 Update Changelog 2021-04-15 21:06:19 +02:00
Strawbs Bot
5e80b694c2 Update translations 2021-04-15 01:02:00 +02:00
Jonas Kvinge
99640aa2ad Add Windows Qt 6 build to CI 2021-04-14 21:11:01 +02:00
Jonas Kvinge
4c0f7c3dd4 Add commandline option to resize window 2021-04-14 19:50:38 +02:00
Jonas Kvinge
2a8490ef31 Use modern user interface and locked list also for uninstalling 2021-04-14 18:07:23 +02:00
Jonas Kvinge
43dc694963 Include gst-discoverer-1.0.exe in windows setup 2021-04-14 16:56:30 +02:00
Strawbs Bot
e03ee00554 Update translations 2021-04-14 01:02:25 +02:00
Jonas Kvinge
01a07ec0e8 Enable stream discoverer on all systems
Fixes #689
2021-04-14 00:16:37 +02:00
Jonas Kvinge
74b7738a9d Create GLib main event loop on non-glib systems 2021-04-14 00:16:07 +02:00
Jonas Kvinge
10aaaebc38 Enable stream discoverer on all Unix except macOS 2021-04-13 22:11:44 +02:00
Jonas Kvinge
a8d072150d Remove avresample-4.dll from nsi 2021-04-13 19:11:48 +02:00
Jonas Kvinge
7877bb7956 Add setting for OSD Pretty fading 2021-04-13 17:27:10 +02:00
Strawbs Bot
bd31e3df89 Update translations 2021-04-13 01:01:55 +02:00
Jonas Kvinge
531e97b499 Only use QPlatformNativeInterface on X11 2021-04-12 19:40:34 +02:00
Jonas Kvinge
43876e967a Enable Qt6WinExtras.dll in nsi 2021-04-12 15:41:28 +02:00
Jonas Kvinge
efaf917939 Add pragma unused 2021-04-11 19:16:07 +02:00
Jonas Kvinge
7d3ceb7d8c Fix Tidal authentication on macOS 2021-04-11 18:33:35 +02:00
Jonas Kvinge
201ac47943 Add new screenshot 2021-04-11 09:11:24 +02:00
Jonas Kvinge
5a70285c10 Update Changelog 2021-04-11 03:24:21 +02:00
Jonas Kvinge
e13c27d32c Allow editing of playlist metadata for streams 2021-04-11 02:01:53 +02:00
Strawbs Bot
9c9b673eeb Update translations 2021-04-11 01:13:36 +02:00
Jonas Kvinge
7323fe0000 Fix queued item painting 2021-04-11 01:03:02 +02:00
Jonas Kvinge
460d045cbe Fix rescan when collection directory is removed and added 2021-04-11 01:01:57 +02:00
Jonas Kvinge
4983c1cfc9 FIx tidal search field not showing on macOS 2021-04-10 09:56:59 +02:00
Jonas Kvinge
c7bc9d471a Allow keep running option on macOS too 2021-04-10 08:22:08 +02:00
Jonas Kvinge
a36b91ffe8 Cast to quint32 2021-04-10 08:21:58 +02:00
Jonas Kvinge
88da0db0f7 Update copyrights 2021-04-10 07:32:38 +02:00
Jonas Kvinge
816c2b0ca8 Use qint32 for unique worker number 2021-04-10 07:20:36 +02:00
Jonas Kvinge
d6ff68339b Use qint64 for unique worker number 2021-04-10 07:02:08 +02:00
Jonas Kvinge
fdf483c98b Update README.md 2021-04-10 06:36:18 +02:00
Jonas Kvinge
eb020c1cb8 Register tidal URL scheme on macOS 2021-04-10 06:04:31 +02:00
Jonas Kvinge
6fa331ad06 Make macdeployqt work with Qt 5 too 2021-04-10 03:28:38 +02:00
Jonas Kvinge
5a58ac2845 Make CollectionQuery subclass QSqlQuery, don't copy QSqlQuery 2021-04-10 03:21:05 +02:00
Strawbs Bot
0c4edc4d17 Update translations 2021-04-09 01:02:09 +02:00
Jonas Kvinge
b5cea4c27e Make enabling FTS3 non-fatal 2021-04-08 22:33:43 +02:00
Jonas Kvinge
b7de5d6df3 Only enable FTS3 when schema needs upgrading 2021-04-08 22:13:21 +02:00
Strawbs Bot
d7310ba307 Update translations 2021-04-07 01:03:08 +02:00
Jonas Kvinge
cabb94b856 Move taglib include dir to system include dirs 2021-04-05 22:25:52 +02:00
Jonas Kvinge
82b649f67d Move taglib include dir to system include dirs 2021-04-05 22:22:25 +02:00
Strawbs Bot
8753782a0c Update translations 2021-04-04 01:02:57 +02:00
Strawbs Bot
566a139edc Update translations 2021-04-02 01:03:04 +02:00
Jonas Kvinge
39dda0f16b Check that rating position is to the right or left of the rectangle.
Adapted from Clementine.

Author: Jim Broadus
2021-04-02 00:47:53 +02:00
Jonas Kvinge
bddb371e31 Fix open in file browser when inode/directory mimetype is set to thunar
Fixes #677
2021-04-01 18:31:19 +02:00
Jonas Kvinge
128223a28a Add setting for configuring the color for the currently playing song
Fixes #676
2021-04-01 02:18:15 +02:00
Jonas Kvinge
45b9d0553f Update FUNDING.yml 2021-04-01 00:27:54 +02:00
Jonas Kvinge
a87f0e0475 Also strip variables with uppercase letters in OpenInFileManager() 2021-03-31 23:14:16 +02:00
Jonas Kvinge
beacea0482 Improve macdeployqt patch 2021-03-31 01:00:51 +02:00
Jonas Kvinge
85b59e79d3 Update ccpp.yml 2021-03-30 20:41:13 +02:00
Jonas Kvinge
9a947c5544 Use LockedList nsis plugin 2021-03-30 20:35:48 +02:00
Strawbs Bot
e4cebf4cbe Update translations 2021-03-30 01:03:27 +02:00
Jonas Kvinge
f63e05b7d4 Use QString::SkipEmptyParts for older Qt versions 2021-03-29 22:45:01 +02:00
Jonas Kvinge
84b7fa02bb Strip all variables from command in OpenInFileManager
Fixes #674
2021-03-29 22:37:55 +02:00
Strawbs Bot
36e597a045 Update translations 2021-03-29 01:28:10 +02:00
Strawbs Bot
f760b3f271 Update translations 2021-03-27 01:12:06 +01:00
Jonas Kvinge
d4c8fa6a24 Use const QJsonValueRef 2021-03-26 23:55:55 +01:00
Jonas Kvinge
c6604734c9 Remove using std::placeholders 2021-03-26 23:33:56 +01:00
Jonas Kvinge
91ab8e22b7 Use QJsonValueRef 2021-03-26 22:10:43 +01:00
Jonas Kvinge
14fb647647 Fix uninitialized variables 2021-03-26 21:30:13 +01:00
Jonas Kvinge
8a39a43ad5 Make sure QCache::insert is successful
Otherwise object is deleted by QCache::insert
2021-03-26 21:07:47 +01:00
Jonas Kvinge
a3d23a6f57 Turn back git revision 2021-03-26 20:35:18 +01:00
757 changed files with 69658 additions and 21190 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: /.*/

1
.github/FUNDING.yml vendored
View File

@@ -1,2 +1,3 @@
github: jonaski github: jonaski
patreon: jonaskvinge patreon: jonaskvinge
custom: https://paypal.me/jonaskvinge

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

View File

@@ -35,9 +35,11 @@
#include <QStringList> #include <QStringList>
#include <QDebug> #include <QDebug>
#include <iostream> #include <iostream>
#include <utility>
#include <QProcess> #include <QProcess>
#include <QDir> #include <QDir>
#include <QSet> #include <QSet>
#include <QList>
#include <QStack> #include <QStack>
#include <QDirIterator> #include <QDirIterator>
#include <QLibraryInfo> #include <QLibraryInfo>
@@ -200,13 +202,19 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) { if (binaryPath.contains(".framework/") || binaryPath.endsWith(".dylib")) {
const auto match = regexp.match(outputLines.first()); const auto match = regexp.match(outputLines.first());
if (match.hasMatch()) { if (match.hasMatch()) {
info.installName = match.captured(1); QString installname = match.captured(1);
info.compatibilityVersion = QVersionNumber::fromString(match.captured(2)); if (QFileInfo(binaryPath).fileName() == QFileInfo(installname).fileName()) {
info.currentVersion = QVersionNumber::fromString(match.captured(3)); info.installName = installname;
info.compatibilityVersion = QVersionNumber::fromString(match.captured(2));
info.currentVersion = QVersionNumber::fromString(match.captured(3));
outputLines.removeFirst();
} else {
info.installName = binaryPath;
}
} else { } else {
LogError() << "Could not parse otool output line:" << outputLines.first(); LogError() << "Could not parse otool output line:" << outputLines.first();
outputLines.removeFirst();
} }
//outputLines.removeFirst();
} }
for (const QString &outputLine : outputLines) { for (const QString &outputLine : outputLines) {
@@ -225,7 +233,7 @@ OtoolInfo findDependencyInfo(const QString &binaryPath)
return info; 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; FrameworkInfo info;
QString trimmed = line.trimmed(); QString trimmed = line.trimmed();
@@ -243,7 +251,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
if (trimmed.startsWith("@rpath/")) { if (trimmed.startsWith("@rpath/")) {
QString rpathRelativePath = trimmed.mid(QStringLiteral("@rpath/").length()); QString rpathRelativePath = trimmed.mid(QStringLiteral("@rpath/").length());
bool foundInsideBundle = false; bool foundInsideBundle = false;
foreach (const QString &rpath, rpaths) { for (const QString &rpath : std::as_const(rpaths)) {
QString path = QDir::cleanPath(rpath + "/" + rpathRelativePath); QString path = QDir::cleanPath(rpath + "/" + rpathRelativePath);
// Skip paths already inside the bundle. // Skip paths already inside the bundle.
if (!appBundlePath.isEmpty()) { if (!appBundlePath.isEmpty()) {
@@ -284,7 +292,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundl
// Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/ // Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/
QStringList parts = trimmed.split("/"); QStringList parts = trimmed.split("/");
while (part < parts.count()) { while (part < parts.count()) {
const QString currentPart = parts.at(part).simplified() ; const QString currentPart = parts.at(part).simplified();
++part; ++part;
if (currentPart == "") if (currentPart == "")
continue; continue;
@@ -302,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. } 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; QStringList partsCopy = parts;
partsCopy.removeLast(); partsCopy.removeLast();
foreach (QString path, librarySearchPath) { for (QString &path : librarySearchPath) {
if (!path.endsWith("/")) if (!path.endsWith("/"))
path += '/'; path += '/';
QString nameInPath = path + parts.join(QLatin1Char('/')); QString nameInPath = path + parts.join(QLatin1Char('/'));
@@ -496,7 +504,7 @@ QString findEntitlementsFile(const QString& path)
return QString(); 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; QList<FrameworkInfo> libraries;
for (const DylibInfo &dylibInfo : dependencies) { for (const DylibInfo &dylibInfo : dependencies) {
@@ -536,9 +544,9 @@ QString resolveDyldPrefix(const QString &path, const QString &loaderPath, const
return path; 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; QProcess otool;
otool.start("otool", QStringList() << "-l" << path); otool.start("otool", QStringList() << "-l" << path);
@@ -575,18 +583,20 @@ QSet<QString> getBinaryRPaths(const QString &path, bool resolve = true, QString
return rpaths; 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); 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; QList<FrameworkInfo> result;
QSet<QString> existing; QSet<QString> existing;
foreach (const QString &path, paths) { for (const QString &path : paths) {
foreach (const FrameworkInfo &info, getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) { for (const FrameworkInfo &info : getQtFrameworks(path, appBundlePath, rpaths, useDebugLibs)) {
if (!existing.contains(info.frameworkPath)) { // avoid duplicates if (!existing.contains(info.frameworkPath)) { // avoid duplicates
existing.insert(info.frameworkPath); existing.insert(info.frameworkPath);
result << info; result << info;
@@ -605,10 +615,10 @@ QStringList getBinaryDependencies(const QString executablePath,
const auto dependencies = findDependencyInfo(path).dependencies; const auto dependencies = findDependencyInfo(path).dependencies;
bool rpathsLoaded = false; bool rpathsLoaded = false;
QSet<QString> rpaths; QList<QString> rpaths;
// return bundle-local dependencies. (those starting with @executable_path) // return bundle-local dependencies. (those starting with @executable_path)
foreach (const DylibInfo &info, dependencies) { for (const DylibInfo &info : dependencies) {
QString trimmedLine = info.binaryPath; QString trimmedLine = info.binaryPath;
if (trimmedLine.startsWith("@executable_path/")) { if (trimmedLine.startsWith("@executable_path/")) {
QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length())); QString binary = QDir::cleanPath(executablePath + trimmedLine.mid(QStringLiteral("@executable_path/").length()));
@@ -617,14 +627,14 @@ QStringList getBinaryDependencies(const QString executablePath,
} else if (trimmedLine.startsWith("@rpath/")) { } else if (trimmedLine.startsWith("@rpath/")) {
if (!rpathsLoaded) { if (!rpathsLoaded) {
rpaths = getBinaryRPaths(path, true, executablePath); rpaths = getBinaryRPaths(path, true, executablePath);
foreach (const QString &binaryPath, additionalBinariesContainingRpaths) { for (const QString &binaryPath : additionalBinariesContainingRpaths) {
QSet<QString> binaryRpaths = getBinaryRPaths(binaryPath, true); rpaths += getBinaryRPaths(binaryPath, true);
rpaths += binaryRpaths;
} }
rpaths.removeDuplicates();
rpathsLoaded = true; rpathsLoaded = true;
} }
bool resolved = false; 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())); QString binary = QDir::cleanPath(rpath + "/" + trimmedLine.mid(QStringLiteral("@rpath/").length()));
LogDebug() << "Checking for" << binary; LogDebug() << "Checking for" << binary;
if (QFile::exists(binary)) { if (QFile::exists(binary)) {
@@ -653,20 +663,20 @@ bool recursiveCopy(const QString &sourcePath, const QString &destinationPath)
LogNormal() << "copy:" << sourcePath << destinationPath; LogNormal() << "copy:" << sourcePath << destinationPath;
QStringList files = QDir(sourcePath).entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot); 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 fileSourcePath = sourcePath + "/" + file;
const QString fileDestinationPath = destinationPath + "/" + file; const QString fileDestinationPath = destinationPath + "/" + file;
copyFilePrintStatus(fileSourcePath, fileDestinationPath); copyFilePrintStatus(fileSourcePath, fileDestinationPath);
} }
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); QStringList subdirs = QDir(sourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot);
foreach (QString dir, subdirs) { for (const QString &dir : subdirs) {
recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir); recursiveCopy(sourcePath + "/" + dir, destinationPath + "/" + dir);
} }
return true; 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); QDir().mkpath(destinationPath);
@@ -674,7 +684,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &r
const bool isDwarfPath = sourcePath.endsWith("DWARF"); const bool isDwarfPath = sourcePath.endsWith("DWARF");
QStringList files = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Files | QDir::NoDotAndDotDot); 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; const QString fileSourcePath = sourcePath + QLatin1Char('/') + file;
if (file.endsWith("_debug.dylib")) { if (file.endsWith("_debug.dylib")) {
@@ -720,7 +730,7 @@ void recursiveCopyAndDeploy(const QString &appBundlePath, const QSet<QString> &r
} }
QStringList subdirs = QDir(sourcePath).entryList(QStringList() << QStringLiteral("*"), QDir::Dirs | QDir::NoDotAndDotDot); 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); recursiveCopyAndDeploy(appBundlePath, rpaths, sourcePath + QLatin1Char('/') + dir, destinationPath + QLatin1Char('/') + dir);
} }
} }
@@ -743,7 +753,7 @@ QString copyDylib(const FrameworkInfo &framework, const QString path)
return QString(); return QString();
} }
// Retrun if the dylib has aleardy been deployed // Return if the dylib has already been deployed
if (QFileInfo(dylibDestinationBinaryPath).exists() && !alwaysOwerwriteEnabled) if (QFileInfo(dylibDestinationBinaryPath).exists() && !alwaysOwerwriteEnabled)
return dylibDestinationBinaryPath; return dylibDestinationBinaryPath;
@@ -777,13 +787,13 @@ QString copyFramework(const FrameworkInfo &framework, const QString path)
// Now copy the framework. Some parts should be left out (headers/, .prl files). // Now copy the framework. Some parts should be left out (headers/, .prl files).
// Some parts should be included (Resources/, symlink structure). We want this // 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. // the same time producing a codesign-compatible framework.
// Copy framework binary // Copy framework binary
copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath); copyFilePrintStatus(framework.sourceFilePath, frameworkDestinationBinaryPath);
// Copy Resouces/, Libraries/ and Helpers/ // Copy Resources/, Libraries/ and Helpers/
const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources"; const QString resourcesDestianationPath = frameworkDestinationDirectory + "/Versions/" + framework.version + "/Resources";
recursiveCopy(resourcesSourcePath, resourcesDestianationPath); recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
@@ -841,7 +851,7 @@ void changeIdentification(const QString &id, const QString &binaryPath)
void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath) void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework, const QStringList &binaryPaths, bool useLoaderPath)
{ {
const QString absBundlePath = QFileInfo(bundlePath).absoluteFilePath(); const QString absBundlePath = QFileInfo(bundlePath).absoluteFilePath();
foreach (const QString &binary, binaryPaths) { for (const QString &binary : binaryPaths) {
QString deployedInstallName; QString deployedInstallName;
if (useLoaderPath) { if (useLoaderPath) {
deployedInstallName = QLatin1String("@loader_path/") deployedInstallName = QLatin1String("@loader_path/")
@@ -849,17 +859,11 @@ void changeInstallName(const QString &bundlePath, const FrameworkInfo &framework
} else { } else {
deployedInstallName = framework.deployedInstallName; deployedInstallName = framework.deployedInstallName;
} }
changeInstallName(framework.installName, deployedInstallName, binary); if (!framework.sourceFilePath.isEmpty()) {
// Workaround for the case when the library ID name is a symlink, while the dependencies changeInstallName(framework.sourceFilePath, deployedInstallName, binary);
// 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);
} }
// Homebrew workaround, resolve symlink /usr/local/opt/library to /usr/local/Cellar/library if (!framework.installName.isEmpty() && framework.installName != framework.sourceFilePath) {
if (framework.installName.startsWith("/usr/local/opt/") && framework.installName.count('/') >= 5) { changeInstallName(framework.installName, deployedInstallName, binary);
canonicalInstallName = QFileInfo(framework.installName.section('/', 0, 4)).canonicalFilePath() + "/" + framework.installName.section('/', 5);
changeInstallName(canonicalInstallName, deployedInstallName, binary);
} }
} }
} }
@@ -869,7 +873,7 @@ void addRPath(const QString &rpath, const QString &binaryPath)
runInstallNameTool(QStringList() << "-add_rpath" << rpath << 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() const QString absFrameworksPath = QFileInfo(bundlePath).absoluteFilePath()
+ QLatin1String("/Contents/Frameworks"); + QLatin1String("/Contents/Frameworks");
@@ -877,7 +881,8 @@ void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const
const QString loaderPathToFrameworks = QLatin1String("@loader_path/") + relativeFrameworkPath; const QString loaderPathToFrameworks = QLatin1String("@loader_path/") + relativeFrameworkPath;
bool rpathToFrameworksFound = false; bool rpathToFrameworksFound = false;
QStringList args; 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" || if (rpath == "@executable_path/../Frameworks" ||
rpath == loaderPathToFrameworks) { rpath == loaderPathToFrameworks) {
rpathToFrameworksFound = true; rpathToFrameworksFound = true;
@@ -903,9 +908,9 @@ void deployRPaths(const QString &bundlePath, const QSet<QString> &rpaths, const
runInstallNameTool(QStringList() << args << binaryPath); 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); deployRPaths(bundlePath, rpaths, binary, useLoaderPath);
} }
} }
@@ -971,7 +976,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
deploymentInfo.useLoaderPath = useLoaderPath; deploymentInfo.useLoaderPath = useLoaderPath;
deploymentInfo.isFramework = bundlePath.contains(".framework"); deploymentInfo.isFramework = bundlePath.contains(".framework");
deploymentInfo.isDebug = false; deploymentInfo.isDebug = false;
QSet<QString> rpathsUsed; QList<QString> rpathsUsed;
while (frameworks.isEmpty() == false) { while (frameworks.isEmpty() == false) {
const FrameworkInfo framework = frameworks.takeFirst(); const FrameworkInfo framework = frameworks.takeFirst();
@@ -983,16 +988,22 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
if (framework.isDebugLibrary()) if (framework.isDebugLibrary())
deploymentInfo.isDebug = true; deploymentInfo.isDebug = true;
if (deploymentInfo.qtPath.isNull()) if (deploymentInfo.qtPath.isNull()) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
deploymentInfo.qtPath = QLibraryInfo::path(QLibraryInfo::PrefixPath); deploymentInfo.qtPath = QLibraryInfo::path(QLibraryInfo::PrefixPath);
#else
deploymentInfo.qtPath = QLibraryInfo::location(QLibraryInfo::PrefixPath);
#endif
}
if (framework.frameworkDirectory.startsWith(bundlePath)) { if (framework.frameworkDirectory.startsWith(bundlePath)) {
LogError() << framework.frameworkName << "already deployed, skipping."; LogError() << framework.frameworkName << "already deployed, skipping.";
continue; continue;
} }
if (!framework.rpathUsed.isEmpty()) if (!framework.rpathUsed.isEmpty() && !rpathsUsed.contains(framework.rpathUsed)) {
rpathsUsed << framework.rpathUsed; rpathsUsed.append(framework.rpathUsed);
}
// Copy the framework/dylib to the app bundle. // Copy the framework/dylib to the app bundle.
const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath) const QString deployedBinaryPath = framework.isDylib ? copyDylib(framework, bundlePath)
@@ -1015,11 +1026,11 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
// Check for framework dependencies // Check for framework dependencies
QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs); QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, bundlePath, rpathsUsed, useDebugLibs);
foreach (FrameworkInfo dependency, dependencies) { for (const FrameworkInfo &dependency : dependencies) {
if (dependency.rpathUsed.isEmpty()) { if (dependency.rpathUsed.isEmpty()) {
changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath); changeInstallName(bundlePath, dependency, QStringList() << deployedBinaryPath, useLoaderPath);
} else { } else {
rpathsUsed << dependency.rpathUsed; rpathsUsed.append(dependency.rpathUsed);
} }
// Deploy framework if necessary. // Deploy framework if necessary.
@@ -1031,6 +1042,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
deploymentInfo.deployedFrameworks = copiedFrameworks; deploymentInfo.deployedFrameworks = copiedFrameworks;
deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath); deployRPaths(bundlePath, rpathsUsed, binaryPaths, useLoaderPath);
deploymentInfo.rpathsUsed += rpathsUsed; deploymentInfo.rpathsUsed += rpathsUsed;
deploymentInfo.rpathsUsed.removeDuplicates();
return deploymentInfo; return deploymentInfo;
} }
@@ -1042,8 +1054,14 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
applicationBundle.libraryPaths = findAppLibraries(appBundlePath); applicationBundle.libraryPaths = findAppLibraries(appBundlePath);
QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths
<< additionalExecutables; << additionalExecutables;
QSet<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true); QList<QString> allLibraryPaths = getBinaryRPaths(applicationBundle.binaryPath, true);
allLibraryPaths.insert(QLibraryInfo::path(QLibraryInfo::LibrariesPath)); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
allLibraryPaths.append(QLibraryInfo::path(QLibraryInfo::LibrariesPath));
#else
allLibraryPaths.append(QLibraryInfo::location(QLibraryInfo::LibrariesPath));
#endif
allLibraryPaths.removeDuplicates();
QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs); QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, appBundlePath, allLibraryPaths, useDebugLibs);
if (frameworks.isEmpty() && !alwaysOwerwriteEnabled) { if (frameworks.isEmpty() && !alwaysOwerwriteEnabled) {
LogWarning(); LogWarning();
@@ -1059,7 +1077,7 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
QString getLibInfix(const QStringList &deployedFrameworks) QString getLibInfix(const QStringList &deployedFrameworks)
{ {
QString libInfix; QString libInfix;
foreach (const QString &framework, deployedFrameworks) { for (const QString &framework : deployedFrameworks) {
if (framework.startsWith(QStringLiteral("QtCore")) && framework.endsWith(QStringLiteral(".framework"))) { if (framework.startsWith(QStringLiteral("QtCore")) && framework.endsWith(QStringLiteral(".framework"))) {
Q_ASSERT(framework.length() >= 16); Q_ASSERT(framework.length() >= 16);
// 16 == "QtCore" + ".framework" // 16 == "QtCore" + ".framework"
@@ -1114,7 +1132,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
// Network // Network
if (deploymentInfo.containsModule("Network", libInfix)) if (deploymentInfo.containsModule("Network", libInfix))
addPlugins(QStringLiteral("bearer")); addPlugins(QStringLiteral("tls"));
// All image formats (svg if QtSvg is used) // All image formats (svg if QtSvg is used)
const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix); const bool usesSvg = deploymentInfo.containsModule("Svg", libInfix);
@@ -1181,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; QString sourcePath = pluginSourcePath + "/" + plugin;
const QString destinationPath = pluginDestinationPath + "/" + plugin; const QString destinationPath = pluginDestinationPath + "/" + plugin;
QDir dir; QDir dir;
@@ -1198,7 +1216,15 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
{ {
QString sourcePath = qgetenv("GIO_EXTRA_MODULES"); QString sourcePath = qgetenv("GIO_EXTRA_MODULES");
if (sourcePath.isEmpty()) { 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 { else {
sourcePath = sourcePath + "/libgiognutls.so"; sourcePath = sourcePath + "/libgiognutls.so";
@@ -1216,7 +1242,15 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
{ {
QString sourcePath = qgetenv("GST_PLUGIN_SCANNER"); QString sourcePath = qgetenv("GST_PLUGIN_SCANNER");
if (sourcePath.isEmpty()) { 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"; const QString destinationPath = appBundleInfo.path + "/" + "Contents/PlugIns/gst-plugin-scanner";
QDir dir; QDir dir;
@@ -1263,11 +1297,9 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
<< "libgstrtsp.dylib" << "libgstrtsp.dylib"
<< "libgstflac.dylib" << "libgstflac.dylib"
<< "libgstwavparse.dylib" << "libgstwavparse.dylib"
<< "libgstfaac.dylib"
<< "libgstfaad.dylib" << "libgstfaad.dylib"
<< "libgstogg.dylib" << "libgstogg.dylib"
<< "libgstopus.dylib" << "libgstopus.dylib"
<< "libgstopusparse.dylib"
<< "libgstasf.dylib" << "libgstasf.dylib"
<< "libgstspeex.dylib" << "libgstspeex.dylib"
<< "libgsttaglib.dylib" << "libgsttaglib.dylib"
@@ -1275,22 +1307,59 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
<< "libgstisomp4.dylib" << "libgstisomp4.dylib"
<< "libgstlibav.dylib" << "libgstlibav.dylib"
<< "libgstaiff.dylib" << "libgstaiff.dylib"
<< "libgstlame.dylib" << "libgstlame.dylib";
<< "libgstmusepack.dylib";
QString gstreamer_plugins_dir = qgetenv("GST_PLUGIN_PATH"); // macports does not have these.
if (gstreamer_plugins_dir.isEmpty()) { QStringList gstreamer_plugins_optional = QStringList() << "libgstopusparse.dylib"
gstreamer_plugins_dir = "/usr/local/lib/gstreamer-1.0"; << "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) { for (const QString &plugin : gstreamer_plugins) {
const QString sourcePath = gstreamer_plugins_dir + "/" + plugin; QFileInfo info(gstreamer_plugins_dir + "/" + plugin);
const QString destinationPath = appBundleInfo.path + "/Contents/PlugIns/gstreamer/" + plugin; if (!info.exists()) {
QDir dir; info.setFile(gstreamer_plugins_dir + "/" + info.baseName() + QString(".so"));
if (dir.mkpath(QFileInfo(destinationPath).path()) && copyFilePrintStatus(sourcePath, destinationPath)) { if (!info.exists()) {
runStrip(destinationPath); LogError() << "Missing gstreamer plugin" << info.baseName();
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs); qFatal("Missing %s", info.baseName().toUtf8().constData());
deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath); }
}
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);
} }
} }
@@ -1337,7 +1406,7 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs); 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; QString importDestinationPath = appBundlePath + "/Contents/Resources/qml/" + importName;
@@ -1366,8 +1435,12 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
LogNormal() << "Application QML file path(s) is" << qmlDirs; LogNormal() << "Application QML file path(s) is" << qmlDirs;
LogNormal() << "QML module search path(s) is" << qmlImportPaths; LogNormal() << "QML module search path(s) is" << qmlImportPaths;
// Use qmlimportscanner from QLibraryInfo::BinariesPath // Use qmlimportscanner from QLibraryInfo::LibraryExecutablesPath
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmlimportscanner"); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + "/qmlimportscanner");
#else
QString qmlImportScannerPath = QDir::cleanPath(QLibraryInfo::location(QLibraryInfo::LibraryExecutablesPath) + "/qmlimportscanner");
#endif
// Fallback: Look relative to the macdeployqt binary // Fallback: Look relative to the macdeployqt binary
if (!QFile(qmlImportScannerPath).exists()) if (!QFile(qmlImportScannerPath).exists())
@@ -1383,13 +1456,17 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
// build argument list for qmlimportsanner: "-rootPath foo/ -rootPath bar/ -importPath path/to/qt/qml" // 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) // ("rootPath" points to a directory containing app qml, "importPath" is where the Qt imports are installed)
QStringList argumentList; QStringList argumentList;
foreach (const QString &qmlDir, qmlDirs) { for (const QString &qmlDir : qmlDirs) {
argumentList.append("-rootPath"); argumentList.append("-rootPath");
argumentList.append(qmlDir); argumentList.append(qmlDir);
} }
for (const QString &importPath : qmlImportPaths) for (const QString &importPath : qmlImportPaths)
argumentList << "-importPath" << importPath; argumentList << "-importPath" << importPath;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QString qmlImportsPath = QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath); QString qmlImportsPath = QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath);
#else
QString qmlImportsPath = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
#endif
argumentList.append( "-importPath"); argumentList.append( "-importPath");
argumentList.append(qmlImportsPath); argumentList.append(qmlImportsPath);
@@ -1427,7 +1504,7 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
std::sort(array.begin(), array.end(), importLessThan); std::sort(array.begin(), array.end(), importLessThan);
// deploy each import // deploy each import
foreach (const QVariant &importValue, array) { for (const QVariant &importValue : array) {
QVariantMap import = importValue.toMap(); QVariantMap import = importValue.toMap();
QString name = import["name"].toString(); QString name = import["name"].toString();
QString path = import["path"].toString(); QString path = import["path"].toString();
@@ -1524,7 +1601,7 @@ QSet<QString> codesignBundle(const QString &identity,
QString appBundleAbsolutePath = QFileInfo(appBundlePath).absoluteFilePath(); QString appBundleAbsolutePath = QFileInfo(appBundlePath).absoluteFilePath();
QString rootBinariesPath = appBundleAbsolutePath + "/Contents/MacOS/"; QString rootBinariesPath = appBundleAbsolutePath + "/Contents/MacOS/";
QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files); QStringList foundRootBinaries = QDir(rootBinariesPath).entryList(QStringList() << "*", QDir::Files);
foreach (const QString &binary, foundRootBinaries) { for (const QString &binary : foundRootBinaries) {
QString binaryPath = rootBinariesPath + binary; QString binaryPath = rootBinariesPath + binary;
pendingBinaries.push(binaryPath); pendingBinaries.push(binaryPath);
pendingBinariesSet.insert(binaryPath); pendingBinariesSet.insert(binaryPath);
@@ -1533,14 +1610,14 @@ QSet<QString> codesignBundle(const QString &identity,
bool getAbsoltuePath = true; bool getAbsoltuePath = true;
QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/", getAbsoltuePath); QStringList foundPluginBinaries = findAppBundleFiles(appBundlePath + "/Contents/PlugIns/", getAbsoltuePath);
foreach (const QString &binary, foundPluginBinaries) { for (const QString &binary : foundPluginBinaries) {
pendingBinaries.push(binary); pendingBinaries.push(binary);
pendingBinariesSet.insert(binary); pendingBinariesSet.insert(binary);
} }
// Add frameworks for processing. // Add frameworks for processing.
QStringList frameworkPaths = findAppFrameworkPaths(appBundlePath); 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 // Prioritise first to sign any additional inner bundles found in the Helpers folder (e.g
// used by QtWebEngine). // used by QtWebEngine).
@@ -1549,7 +1626,7 @@ QSet<QString> codesignBundle(const QString &identity,
helpersIterator.next(); helpersIterator.next();
QString helpersPath = helpersIterator.filePath(); QString helpersPath = helpersIterator.filePath();
QStringList innerBundleNames = QDir(helpersPath).entryList(QStringList() << "*.app", QDir::Dirs); QStringList innerBundleNames = QDir(helpersPath).entryList(QStringList() << "*.app", QDir::Dirs);
foreach (const QString &innerBundleName, innerBundleNames) for (const QString &innerBundleName : innerBundleNames)
signedBinaries += codesignBundle(identity, signedBinaries += codesignBundle(identity,
helpersPath + "/" + innerBundleName, helpersPath + "/" + innerBundleName,
additionalBinariesContainingRpaths); additionalBinariesContainingRpaths);
@@ -1562,7 +1639,7 @@ QSet<QString> codesignBundle(const QString &identity,
librariesIterator.next(); librariesIterator.next();
QString librariesPath = librariesIterator.filePath(); QString librariesPath = librariesIterator.filePath();
QStringList bundleFiles = findAppBundleFiles(librariesPath, getAbsoltuePath); QStringList bundleFiles = findAppBundleFiles(librariesPath, getAbsoltuePath);
foreach (const QString &binary, bundleFiles) { for (const QString &binary : bundleFiles) {
pendingBinaries.push(binary); pendingBinaries.push(binary);
pendingBinariesSet.insert(binary); pendingBinariesSet.insert(binary);
} }
@@ -1587,7 +1664,7 @@ QSet<QString> codesignBundle(const QString &identity,
pendingBinaries.push(binary); pendingBinaries.push(binary);
pendingBinariesSet.insert(binary); pendingBinariesSet.insert(binary);
int dependenciesSkipped = 0; 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 // 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. // 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). // a bundle is part of a framework helper, and depends on that framework).
@@ -1698,109 +1775,3 @@ void fixupFramework(const QString &frameworkName)
addRPath("@loader_path/../../Contents/Frameworks/", frameworkBinary); 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 bool isDebugLibrary() const
{ {
return binaryName.contains(QLatin1String("_debug")); return binaryName.endsWith(QStringLiteral("_debug"));
} }
}; };
@@ -101,7 +101,7 @@ public:
QString qtPath; QString qtPath;
QString pluginPath; QString pluginPath;
QStringList deployedFrameworks; QStringList deployedFrameworks;
QSet<QString> rpathsUsed; QList<QString> rpathsUsed;
bool useLoaderPath; bool useLoaderPath;
bool isFramework; bool isFramework;
bool isDebug; bool isDebug;
@@ -112,10 +112,10 @@ public:
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info); inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
OtoolInfo findDependencyInfo(const QString &binaryPath); 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); QString findAppBinary(const QString &appBundlePath);
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);
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QSet<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); QString copyFramework(const FrameworkInfo &framework, const QString path);
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs); 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); 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 codesign(const QString &identity, const QString &appBundlePath);
void createDiskImage(const QString &appBundlePath, const QString &filesystemType); void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
void fixupFramework(const QString &appBundlePath); void fixupFramework(const QString &appBundlePath);
bool FinalCheck(const QString &appBundlePath);
#endif #endif

View File

@@ -10,11 +10,7 @@ endif()
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp) set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h) set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
if(BUILD_WITH_QT6) qt_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
else()
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
endif()
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC}) add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
target_include_directories(singleapplication PRIVATE target_include_directories(singleapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
@@ -28,11 +24,7 @@ target_link_libraries(singleapplication PRIVATE
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp) set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h) set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
if(BUILD_WITH_QT6) qt_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
else()
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
endif()
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC}) add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
target_include_directories(singlecoreapplication PRIVATE target_include_directories(singlecoreapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR} ${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(); d->startPrimary();
if (!d->memory_->unlock()) { if (!d->memory_->unlock()) {
qDebug() << "SingleApplication: Unable to unlock memory after primary start."; qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
@@ -234,9 +234,9 @@ QString SingleApplication::currentUser() {
* Sends message to the Primary Instance. * Sends message to the Primary Instance.
* @param message The message to send. * @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions. * @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); Q_D(SingleApplication);
@@ -244,8 +244,9 @@ bool SingleApplication::sendMessage(QByteArray message, int timeout) {
if (isPrimary()) return false; if (isPrimary()) return false;
// Make sure the socket is connected // Make sure the socket is connected
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect)) if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect)) {
return false; return false;
}
d->socket_->write(message); d->socket_->write(message);
const bool dataWritten = d->socket_->waitForBytesWritten(timeout); const bool dataWritten = d->socket_->waitForBytesWritten(timeout);

View File

@@ -42,10 +42,10 @@
class SingleApplicationPrivate; 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 * @see QApplication
*/ */
class SingleApplication : public QApplication { class SingleApplication : public QApplication { // clazy:exclude=ctor-missing-parent-argument
Q_OBJECT Q_OBJECT
typedef QApplication app_t; typedef QApplication app_t;
@@ -136,7 +136,7 @@ class SingleApplication : public QApplication {
* @note sendMessage() will return false if invoked from the primary * @note sendMessage() will return false if invoked from the primary
* instance. * instance.
*/ */
bool sendMessage(QByteArray message, int timeout = 1000); bool sendMessage(const QByteArray &message, const int timeout = 1000);
signals: signals:
void instanceStarted(); 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()); InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
inst->primary = false; inst->primary = false;
@@ -237,8 +237,9 @@ bool SingleApplicationPrivate::connectToPrimary(const int timeout, const Connect
forever { forever {
randomSleep(); randomSleep();
if (socket_->state() != QLocalSocket::ConnectingState) if (socket_->state() != QLocalSocket::ConnectingState) {
socket_->connectToServer(blockServerName_); socket_->connectToServer(blockServerName_);
}
if (socket_->state() == QLocalSocket::ConnectingState) { if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed())); 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) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum))); 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(); memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data()); InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -307,7 +308,7 @@ qint64 SingleApplicationPrivate::primaryPid() {
} }
QString SingleApplicationPrivate::primaryUser() { QString SingleApplicationPrivate::primaryUser() const {
memory_->lock(); memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data()); InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -327,7 +328,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
connectionMap_.insert(nextConnSocket, ConnectionInfo()); connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() { QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket]; const ConnectionInfo info = connectionMap_[nextConnSocket];
slotClientConnectionClosed(nextConnSocket, info.instanceId); slotClientConnectionClosed(nextConnSocket, info.instanceId);
}); });
@@ -337,7 +338,7 @@ void SingleApplicationPrivate::slotConnectionEstablished() {
}); });
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() { QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket]; const ConnectionInfo info = connectionMap_[nextConnSocket];
switch (info.stage) { switch (info.stage) {
case StageHeader: case StageHeader:
readInitMessageHeader(nextConnSocket); readInitMessageHeader(nextConnSocket);
@@ -461,7 +462,7 @@ void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSo
void SingleApplicationPrivate::randomSleep() { void SingleApplicationPrivate::randomSleep() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u)); QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
#else #else
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max()); qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10)); QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));

View File

@@ -37,7 +37,7 @@
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QMap> #include <QHash>
#include "singleapplication.h" #include "singleapplication.h"
@@ -55,7 +55,7 @@ struct InstancesInfo {
struct ConnectionInfo { struct ConnectionInfo {
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {} explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
qint64 msgLen; quint64 msgLen;
quint32 instanceId; quint32 instanceId;
quint8 stage; quint8 stage;
}; };
@@ -80,18 +80,18 @@ class SingleApplicationPrivate : public QObject {
explicit SingleApplicationPrivate(SingleApplication *ptr); explicit SingleApplicationPrivate(SingleApplication *ptr);
~SingleApplicationPrivate() override; ~SingleApplicationPrivate() override;
QString getUsername(); static QString getUsername();
void genBlockServerName(); void genBlockServerName();
void initializeMemoryBlock(); void initializeMemoryBlock() const;
void startPrimary(); void startPrimary();
void startSecondary(); void startSecondary();
bool connectToPrimary(const int timeout, const ConnectionType connectionType); bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum(); quint16 blockChecksum() const;
qint64 primaryPid(); qint64 primaryPid() const;
QString primaryUser(); QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket); void readInitMessageHeader(QLocalSocket *socket);
void readInitMessageBody(QLocalSocket *socket); void readInitMessageBody(QLocalSocket *socket);
void randomSleep(); static void randomSleep();
SingleApplication *q_ptr; SingleApplication *q_ptr;
QSharedMemory *memory_; QSharedMemory *memory_;
@@ -100,7 +100,7 @@ class SingleApplicationPrivate : public QObject {
quint32 instanceNumber_; quint32 instanceNumber_;
QString blockServerName_; QString blockServerName_;
SingleApplication::Options options_; SingleApplication::Options options_;
QMap<QLocalSocket*, ConnectionInfo> connectionMap_; QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
public slots: public slots:
void slotConnectionEstablished(); 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(); d->startPrimary();
if (!d->memory_->unlock()) { if (!d->memory_->unlock()) {
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start."; qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
@@ -234,9 +234,9 @@ QString SingleCoreApplication::currentUser() {
* Sends message to the Primary Instance. * Sends message to the Primary Instance.
* @param message The message to send. * @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions. * @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); Q_D(SingleCoreApplication);
@@ -244,8 +244,9 @@ bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
if (isPrimary()) return false; if (isPrimary()) return false;
// Make sure the socket is connected // Make sure the socket is connected
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect)) if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect)) {
return false; return false;
}
d->socket_->write(message); d->socket_->write(message);
const bool dataWritten = d->socket_->waitForBytesWritten(timeout); const bool dataWritten = d->socket_->waitForBytesWritten(timeout);

View File

@@ -42,7 +42,7 @@
class SingleCoreApplicationPrivate; 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 * @see QCoreApplication
*/ */
class SingleCoreApplication : public QCoreApplication { class SingleCoreApplication : public QCoreApplication {
@@ -135,7 +135,7 @@ class SingleCoreApplication : public QCoreApplication {
* @note sendMessage() will return false if invoked from the primary * @note sendMessage() will return false if invoked from the primary
* instance. * instance.
*/ */
bool sendMessage(QByteArray message, int timeout = 1000); bool sendMessage(const QByteArray &message, const int timeout = 1000);
signals: signals:
void instanceStarted(); 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()); InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
inst->primary = false; inst->primary = false;
@@ -237,8 +237,9 @@ bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const Con
forever { forever {
randomSleep(); randomSleep();
if (socket_->state() != QLocalSocket::ConnectingState) if (socket_->state() != QLocalSocket::ConnectingState) {
socket_->connectToServer(blockServerName_); socket_->connectToServer(blockServerName_);
}
if (socket_->state() == QLocalSocket::ConnectingState) { if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed())); 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) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum))); 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(); memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data()); InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -307,7 +308,7 @@ qint64 SingleCoreApplicationPrivate::primaryPid() {
} }
QString SingleCoreApplicationPrivate::primaryUser() { QString SingleCoreApplicationPrivate::primaryUser() const {
memory_->lock(); memory_->lock();
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data()); InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
@@ -327,7 +328,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
connectionMap_.insert(nextConnSocket, ConnectionInfo()); connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() { QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket]; const ConnectionInfo info = connectionMap_[nextConnSocket];
slotClientConnectionClosed(nextConnSocket, info.instanceId); slotClientConnectionClosed(nextConnSocket, info.instanceId);
}); });
@@ -337,7 +338,7 @@ void SingleCoreApplicationPrivate::slotConnectionEstablished() {
}); });
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() { QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
auto &info = connectionMap_[nextConnSocket]; const ConnectionInfo info = connectionMap_[nextConnSocket];
switch (info.stage) { switch (info.stage) {
case StageHeader: case StageHeader:
readInitMessageHeader(nextConnSocket); readInitMessageHeader(nextConnSocket);
@@ -461,7 +462,7 @@ void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *clos
void SingleCoreApplicationPrivate::randomSleep() { void SingleCoreApplicationPrivate::randomSleep() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u)); QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
#else #else
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max()); qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10)); QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));

View File

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

View File

@@ -12,13 +12,13 @@ include(cmake/Summary.cmake)
include(cmake/OptionalSource.cmake) include(cmake/OptionalSource.cmake)
include(cmake/ParseArguments.cmake) include(cmake/ParseArguments.cmake)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(LINUX ON) set(LINUX ON)
endif() endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(FREEBSD ON) set(FREEBSD ON)
endif() endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") if(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(OPENBSD ON) set(OPENBSD ON)
endif() endif()
@@ -35,34 +35,38 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
list(APPEND COMPILE_OPTIONS if(MSVC)
$<$<COMPILE_LANGUAGE:C>:-std=c99> list(APPEND COMPILE_OPTIONS /std:c++17 /MP)
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17> else()
-Wall list(APPEND COMPILE_OPTIONS
-Wextra $<$<COMPILE_LANGUAGE:C>:-std=c99>
-Wpedantic $<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
-Wunused -Wall
-Wshadow -Wextra
-Wundef -Wpedantic
-Wuninitialized -Wunused
-Wredundant-decls -Wshadow
-Wcast-align -Wundef
-Winit-self -Wuninitialized
-Wmissing-include-dirs -Wredundant-decls
-Wmissing-declarations -Wcast-align
-Wstrict-overflow=2 -Winit-self
-Wunused-parameter -Wmissing-include-dirs
-Wformat=2 -Wmissing-declarations
-Wdisabled-optimization -Wstrict-overflow=2
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual> -Wunused-parameter
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast> -Wformat=2
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive> -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) option(BUILD_WERROR "Build with -Werror" OFF)
if(BUILD_WERROR) if(BUILD_WERROR)
list(APPEND COMPILE_OPTIONS -Werror) list(APPEND COMPILE_OPTIONS -Werror)
endif(BUILD_WERROR) endif()
add_compile_options(${COMPILE_OPTIONS}) add_compile_options(${COMPILE_OPTIONS})
@@ -77,33 +81,33 @@ if(APPLE)
endif() endif()
find_program(CCACHE_EXECUTABLE NAMES ccache) find_program(CCACHE_EXECUTABLE NAMES ccache)
if (CCACHE_EXECUTABLE) if(CCACHE_EXECUTABLE)
message(STATUS "ccache found: will be used for compilation and linkage") 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_COMPILE ${CCACHE_EXECUTABLE})
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE}) SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
endif () endif()
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
find_package(Boost REQUIRED) find_package(Boost REQUIRED)
find_package(Threads) find_package(Threads)
find_package(Backtrace QUIET) find_package(Backtrace)
if(Backtrace_FOUND) if(Backtrace_FOUND)
set(HAVE_BACKTRACE ON) set(HAVE_BACKTRACE ON)
endif() endif()
find_package(Iconv QUIET) find_package(Iconv)
find_package(GnuTLS REQUIRED) find_package(GnuTLS REQUIRED)
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
if (NOT Protobuf_PROTOC_EXECUTABLE) if(NOT Protobuf_PROTOC_EXECUTABLE)
message(FATAL_ERROR "Missing protobuf compiler.") message(FATAL_ERROR "Missing protobuf compiler.")
endif() endif()
if(LINUX) if(LINUX)
find_package(ALSA REQUIRED) find_package(ALSA REQUIRED)
pkg_check_modules(DBUS REQUIRED dbus-1) pkg_check_modules(DBUS REQUIRED dbus-1)
else(LINUX) else()
find_package(ALSA) find_package(ALSA)
pkg_check_modules(DBUS dbus-1) pkg_check_modules(DBUS dbus-1)
endif(LINUX) endif()
if (UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
find_package(X11) find_package(X11)
pkg_check_modules(XCB xcb) pkg_check_modules(XCB xcb)
endif() endif()
@@ -123,7 +127,7 @@ pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
pkg_check_modules(LIBVLC libvlc) pkg_check_modules(LIBVLC libvlc)
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9) pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9)
pkg_check_modules(LIBPULSE libpulse) 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(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0) pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0) pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
@@ -132,81 +136,99 @@ find_package(FFTW3)
find_package(GTest) find_package(GTest)
find_library(GMOCK_LIBRARY gmock) find_library(GMOCK_LIBRARY gmock)
if(NOT QT_DEFAULT_MAJOR_VERSION) option(QT_VERSION_MAJOR "Qt version to use (5 or 6)")
set(QT_DEFAULT_MAJOR_VERSION 5) option(BUILD_WITH_QT5 "Build with Qt 5" OFF)
endif() option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
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)
if(WITH_QT6) if(WITH_QT6)
set(BUILD_WITH_QT6 ON) set(BUILD_WITH_QT6 ON)
endif() endif()
if(QT_MAJOR_VERSION)
if(BUILD_WITH_QT5) set(QT_VERSION_MAJOR ${QT_MAJOR_VERSION})
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()
endif() endif()
set(QT_COMPONENTS Core Concurrent Widgets Network Sql) if(QT_VERSION_MAJOR)
set(QT_OPTIONAL_COMPONENTS Test) set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
if(QT_MAJOR_VERSION EQUAL 5)
set(QT_MIN_VERSION 5.8)
endif() endif()
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
if(DBUS_FOUND AND NOT WIN32) if(DBUS_FOUND AND NOT WIN32)
list(APPEND QT_COMPONENTS DBus) list(APPEND QT_COMPONENTS DBus)
endif() 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) list(APPEND QT_OPTIONAL_COMPONENTS X11Extras)
endif() 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(QtCore_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent) set(QtConcurrent_LIBRARIES Qt${QT_VERSION_MAJOR}::Concurrent)
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui) set(QtGui_LIBRARIES Qt${QT_VERSION_MAJOR}::Gui)
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets) set(QtWidgets_LIBRARIES Qt${QT_VERSION_MAJOR}::Widgets)
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network) set(QtNetwork_LIBRARIES Qt${QT_VERSION_MAJOR}::Network)
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql) set(QtSql_LIBRARIES Qt${QT_VERSION_MAJOR}::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) 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_MAJOR_VERSION}DBus_FOUND) if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus) set(QtDBus_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus) list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION) get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_VERSION_MAJOR}::qdbusxml2cpp LOCATION)
endif() endif()
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND) if(BUILD_WITH_QT5 AND Qt5X11Extras_FOUND)
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
set(HAVE_X11EXTRAS ON) set(HAVE_X11EXTRAS ON)
set(QtX11Extras_LIBRARIES Qt5::X11Extras)
list(APPEND QT_LIBRARIES Qt5::X11Extras)
endif() endif()
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND) if(Qt${QT_VERSION_MAJOR}Test_FOUND)
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras) set(QtTest_LIBRARIES Qt${QT_VERSION_MAJOR}::Test)
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)
endif() endif()
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG) find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS LinguistTools CONFIG)
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND) if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert) 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() endif()
if(X11_FOUND) if(X11_FOUND)
@@ -222,27 +244,46 @@ if(X11_FOUND)
else() else()
message(WARNING, "Missing X11/XF86keysym.h") message(WARNING, "Missing X11/XF86keysym.h")
endif() endif()
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)
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif()
endif(X11_FOUND) endif(X11_FOUND)
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) option(USE_TAGLIB "Build with TagLib" OFF)
if(QPA_QPLATFORMNATIVEINTERFACE_H) option(USE_TAGPARSER "Build with TagParser" OFF)
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) if(NOT USE_TAGLIB AND NOT USE_TAGPARSER)
message(STATUS "Have qpa/qplatformnativeinterface.h header.") set(USE_TAGLIB ON)
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif() endif()
# TAGLIB # TAGLIB
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1) if(USE_TAGLIB)
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h) pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h) if(TAGLIB_FOUND)
if(HAVE_TAGLIB_DSFFILE_H) find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
set(HAVE_TAGLIB_DSFFILE ON) find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
endif(HAVE_TAGLIB_DSFFILE_H) if(HAVE_TAGLIB_DSFFILE_H)
if(HAVE_TAGLIB_DSDIFFFILE_H) set(HAVE_TAGLIB_DSFFILE ON)
set(HAVE_TAGLIB_DSDIFFFILE ON) endif(HAVE_TAGLIB_DSFFILE_H)
endif(HAVE_TAGLIB_DSDIFFFILE_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 # SingleApplication
add_subdirectory(3rdparty/singleapplication) add_subdirectory(3rdparty/singleapplication)
@@ -252,11 +293,12 @@ set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
if(APPLE) if(APPLE)
find_library(SPARKLE Sparkle PATHS "/usr/local/opt/sparkle") find_library(SPARKLE Sparkle PATHS "/usr/local/opt/sparkle")
add_subdirectory(3rdparty/macdeployqt)
add_subdirectory(3rdparty/SPMediaKeyTap) add_subdirectory(3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap) set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap) set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
endif(APPLE) add_subdirectory(3rdparty/macdeployqt)
add_subdirectory(ext/macdeploycheck)
endif()
if(NOT SPARKLE AND (APPLE OR WIN32)) if(NOT SPARKLE AND (APPLE OR WIN32))
if(BUILD_WITH_QT6) if(BUILD_WITH_QT6)
@@ -269,23 +311,22 @@ if(NOT SPARKLE AND (APPLE OR WIN32))
endif() endif()
endif() endif()
if (WIN32) if(WIN32 AND NOT MSVC)
# RC compiler # RC compiler
string(REPLACE "gcc" "windres" CMAKE_RC_COMPILER_INIT ${CMAKE_C_COMPILER}) string(REPLACE "gcc" "windres" CMAKE_RC_COMPILER_INIT ${CMAKE_C_COMPILER})
enable_language(RC) enable_language(RC)
SET(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -o <OBJECT> <SOURCE> -I ${CMAKE_SOURCE_DIR}/dist/windows") 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) if(WIN32)
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF) option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
endif(WIN32) endif()
optional_component(ALSA ON "ALSA integration" optional_component(ALSA ON "ALSA integration"
DEPENDS "alsa" ALSA_FOUND DEPENDS "alsa" ALSA_FOUND
) )
optional_component(LIBPULSE ON "Pulse audio integration" optional_component(LIBPULSE ON "PulseAudio integration"
DEPENDS "libpulse" LIBPULSE_FOUND DEPENDS "libpulse" LIBPULSE_FOUND
) )
@@ -306,11 +347,17 @@ optional_component(VLC ON "Engine: VLC backend"
DEPENDS "libvlc" LIBVLC_FOUND 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 "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) set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
endif() endif()
@@ -318,12 +365,21 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
) )
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts" if(BUILD_WITH_QT6 AND (Qt6Core_VERSION VERSION_EQUAL 6.2.0 OR Qt6Core_VERSION VERSION_GREATER 6.2.0))
DEPENDS "X11Extras" Qt${QT_MAJOR_VERSION}X11Extras_FOUND 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" optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" LIBCDIO_FOUND DEPENDS "libcdio" LIBCDIO_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
) )
optional_component(UDISKS2 ON "Devices: UDisks2 backend" optional_component(UDISKS2 ON "Devices: UDisks2 backend"
@@ -372,24 +428,23 @@ optional_component(MOODBAR ON "Moodbar"
DEPENDS "gstreamer" HAVE_GSTREAMER DEPENDS "gstreamer" HAVE_GSTREAMER
) )
if(LINUX OR APPLE) if(APPLE OR WIN32)
option(USE_BUNDLE "Bundle dependencies" OFF)
elseif(WIN32)
option(USE_BUNDLE "Bundle dependencies" ON) option(USE_BUNDLE "Bundle dependencies" ON)
else()
option(USE_BUNDLE "Bundle dependencies" OFF)
endif() endif()
if (USE_BUNDLE AND NOT USE_BUNDLE_DIR) if(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
if(LINUX) if(LINUX)
set(USE_BUNDLE_DIR "../plugins") set(USE_BUNDLE_DIR "../plugins")
endif(LINUX) endif()
if(APPLE) if(APPLE)
set(USE_BUNDLE_DIR "../PlugIns") set(USE_BUNDLE_DIR "../PlugIns")
endif(APPLE) endif()
endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR) endif()
# Check that we have sqlite3 with FTS5
if(NOT CMAKE_CROSSCOMPILING) if(NOT CMAKE_CROSSCOMPILING)
# Check that we have Qt with sqlite driver
set(CMAKE_REQUIRED_FLAGS "-std=c++17") set(CMAKE_REQUIRED_FLAGS "-std=c++17")
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
check_cxx_source_runs(" check_cxx_source_runs("
@@ -400,12 +455,29 @@ if(NOT CMAKE_CROSSCOMPILING)
db.setDatabaseName(\":memory:\"); db.setDatabaseName(\":memory:\");
if (!db.open()) { return 1; } if (!db.open()) { return 1; }
QSqlQuery q(db); 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; 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() endif()
# Set up definitions # Set up definitions
@@ -417,6 +489,13 @@ add_definitions(-DQT_USE_QSTRINGBUILDER)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING) add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DQT_NO_CAST_TO_ASCII) add_definitions(-DQT_NO_CAST_TO_ASCII)
if(WIN32)
add_definitions(-DUNICODE)
if(MSVC)
add_definitions(-DPROTOBUF_USE_DLLS)
endif()
endif()
# Subdirectories # Subdirectories
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(dist) add_subdirectory(dist)
@@ -432,13 +511,9 @@ if(GTest_FOUND AND GMOCK_LIBRARY AND QtTest_LIBRARIES)
endif() endif()
# Uninstall support # Uninstall support
configure_file( configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
# Show a summary of what we have enabled # Show a summary of what we have enabled
summary_show() 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.") message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
endif() endif()
if(NOT SQLITE3_FTS5 AND NOT CMAKE_CROSSCOMPILING) if(NOT CMAKE_CROSSCOMPILING)
message(WARNING "sqlite3 must be enabled with FTS5. See: https://www.sqlite.org/fts5.html") 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() 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.") 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() endif()

View File

@@ -2,6 +2,83 @@ Strawberry Music Player
======================= =======================
ChangeLog 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:
* Fix "Show in file browser" to work with thunar.
* Check that the clicked rating position is to the right or left of the rectangle.
* Fix rescan when collection directory is removed and readded.
* Create GLib main event loop on non-glib systems to fix stream discoverer.
* (macOS) Fix intermittent abort on startup.
* (macOS) Fix Tidal and Qobuz search field not showing.
* (macOS) Add tidal URL scheme to Info.plist.
* (macOS) Fix Tidal OAuth authentication.
Enhancements:
* Allow editing playlist metadata for radio streams.
* Make CollectionQuery subclass QSqlQuery, avoid copying QSqlQuery.
* Only enable FTS3 when schema needs upgrading, since FTS5 is used for search.
* Add setting for configuring the color for the currently playing song.
* Add setting to turn on OSD Pretty fading.
* Add commandline option to resize window.
* (Windows) Show dialog with programs that needs to close in nsis installer.
* (macOS) Make macdeployqt work with Qt 5 too.
* (macOS) Show keep running option in behaviour settings.
0.9.2: 0.9.2:
Bugfixes: Bugfixes:
@@ -85,8 +162,8 @@ ChangeLog
* Add support for native global shortcuts on KDE. * Add support for native global shortcuts on KDE.
* Add track progress in system tray icon as an option. * Add track progress in system tray icon as an option.
* Only strip problematic characters in suggested filename when saving a playlist to file. * Only strip problematic characters in suggested filename when saving a playlist to file.
* Change star/unstar playlist to doubleclick instead of singleclick. * Change star/unstar playlist to double-click instead of singleclick.
* Don't edit playlist name on doubleclick in playlists view. * Don't edit playlist name on double-click in playlists view.
* Make context view top label text selectable. * Make context view top label text selectable.
* Add setting to change Qt style. * Add setting to change Qt style.
* Clear ID3v3 tags that are empty, and clear ID3v1 tags when setting ID3v3 tags. * Clear ID3v3 tags that are empty, and clear ID3v1 tags when setting ID3v3 tags.
@@ -281,7 +358,7 @@ ChangeLog
0.6.10: 0.6.10:
Bugfixes: 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 reading date and genre from individual tracks in CUE sheets.
* Fixed resume playback on startup for CUE songs. * Fixed resume playback on startup for CUE songs.
* Fixed album cover manager not showing complete album titles in the list of album covers. * Fixed album cover manager not showing complete album titles in the list of album covers.
@@ -290,7 +367,7 @@ ChangeLog
* Fixed engine and device in context using too large icons when icons were loaded from the system theme. * 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 "Secure connection setup failed" problem on Windows when playing streams.
* Fixed margin for song title text in context. * 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: Enhancements:
* Allowing all characters except slash and backslash when organising music unless options to strip characters is checked. * Allowing all characters except slash and backslash when organising music unless options to strip characters is checked.
@@ -305,9 +382,9 @@ ChangeLog
* Only showing song length in context when available. * 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. * 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. * 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. * 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. * Added label to show collection pixmap disk cache used in settings.
* Icreased default collection pixmap disk cache to 360. * Icreased default collection pixmap disk cache to 360.

View File

@@ -1,11 +1,12 @@
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/C/C++%20CI/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions) :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. 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.
![Browse](https://www.strawberrymusicplayer.org/pictures/screenshot-002-large.png) ![Browse](https://raw.githubusercontent.com/strawberrymusicplayer/strawberry/master/data/screenshot/screenshot.png)
Resources: Resources:
@@ -15,7 +16,8 @@ Resources:
* Buildbot: https://buildbot.strawberrymusicplayer.org/ * Buildbot: https://buildbot.strawberrymusicplayer.org/
* Latest builds: https://builds.strawberrymusicplayer.org/ * Latest builds: https://builds.strawberrymusicplayer.org/
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry * openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry
* PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry * Ubuntu PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
* Translations: https://translate.zanata.org/iteration/view/strawberry/master * Translations: https://translate.zanata.org/iteration/view/strawberry/master
### :bangbang: Opening an issue: ### :bangbang: Opening an issue:
@@ -43,14 +45,14 @@ Funding developers is a way to contribute to open source projects you appreciate
* Playlist management * Playlist management
* Smart and dynamic playlists * Smart and dynamic playlists
* Advanced audio output and device configuration for bit-perfect playback on Linux * 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 * 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/) * 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/) * 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 * Support for multiple backends
* Audio analyzer * Audio analyzer
* Audio equalizer * 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/) * 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 * Subsonic, Tidal and Qobuz streaming support
@@ -63,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: 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 * [GCC](https://gcc.gnu.org/) or [clang](https://clang.llvm.org/) compiler
* [Boost](https://www.boost.org/) * [Boost](https://www.boost.org/)
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
* [GLib](https://developer.gnome.org/glib/) * [GLib](https://developer.gnome.org/glib/)
* [Protobuf](https://developers.google.com/protocol-buffers/) * [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 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) * [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
* [Chromaprint](https://acoustid.org/chromaprint) * [ALSA (Linux required)](https://www.alsa-project.org/)
* [ALSA (linux)](https://www.alsa-project.org/) * [D-Bus (Linux required)](https://www.freedesktop.org/wiki/Software/dbus/)
* [D-Bus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org) * [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
* [GnuTLS](https://www.gnutls.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: 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/) * Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
* MTP devices: [libmtp](http://libmtp.sourceforge.net/) * MTP devices: [libmtp](http://libmtp.sourceforge.net/)
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/) * 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. 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. 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) #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") 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) find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
if(CREATEDMG_EXECUTABLE) if(CREATEDMG_EXECUTABLE)
@@ -14,24 +13,30 @@ else()
message(WARNING "Missing create-dmg executable.") message(WARNING "Missing create-dmg executable.")
endif() endif()
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macversion.sh OUTPUT_VARIABLE MACOS_VERSION_PACKAGE OUTPUT_STRIP_TRAILING_WHITESPACE) if(MACDEPLOYQT_EXECUTABLE)
if(NOT MACOS_VERSION_PACKAGE) add_custom_target(copy_gstreamer_plugins
message(WARNING "Could not set macOS version.") #COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh strawberry.app
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}
) )
add_custom_target(dmg2 add_custom_target(deploy
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/ COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
COMMAND cp -r /usr/local/opt/sparkle/Sparkle.framework ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Frameworks/ COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
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 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} 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() endif()

View File

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

View File

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

View File

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

View File

@@ -14,6 +14,8 @@
<file>schema/schema-11.sql</file> <file>schema/schema-11.sql</file>
<file>schema/schema-12.sql</file> <file>schema/schema-12.sql</file>
<file>schema/schema-13.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>schema/device-schema.sql</file>
<file>style/strawberry.css</file> <file>style/strawberry.css</file>
<file>style/smartplaylistsearchterm.css</file> <file>style/smartplaylistsearchterm.css</file>

View File

@@ -92,6 +92,9 @@
<file>icons/128x128/tidal.png</file> <file>icons/128x128/tidal.png</file>
<file>icons/128x128/qobuz.png</file> <file>icons/128x128/qobuz.png</file>
<file>icons/128x128/multimedia-player-ipod-standard-black.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/albums.png</file>
<file>icons/64x64/alsa.png</file> <file>icons/64x64/alsa.png</file>
<file>icons/64x64/application-exit.png</file> <file>icons/64x64/application-exit.png</file>
@@ -185,6 +188,9 @@
<file>icons/64x64/tidal.png</file> <file>icons/64x64/tidal.png</file>
<file>icons/64x64/qobuz.png</file> <file>icons/64x64/qobuz.png</file>
<file>icons/64x64/multimedia-player-ipod-standard-black.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/albums.png</file>
<file>icons/48x48/alsa.png</file> <file>icons/48x48/alsa.png</file>
<file>icons/48x48/application-exit.png</file> <file>icons/48x48/application-exit.png</file>
@@ -282,6 +288,9 @@
<file>icons/48x48/tidal.png</file> <file>icons/48x48/tidal.png</file>
<file>icons/48x48/qobuz.png</file> <file>icons/48x48/qobuz.png</file>
<file>icons/48x48/multimedia-player-ipod-standard-black.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/albums.png</file>
<file>icons/32x32/alsa.png</file> <file>icons/32x32/alsa.png</file>
<file>icons/32x32/application-exit.png</file> <file>icons/32x32/application-exit.png</file>
@@ -379,6 +388,9 @@
<file>icons/32x32/tidal.png</file> <file>icons/32x32/tidal.png</file>
<file>icons/32x32/qobuz.png</file> <file>icons/32x32/qobuz.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-black.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/albums.png</file>
<file>icons/22x22/alsa.png</file> <file>icons/22x22/alsa.png</file>
<file>icons/22x22/application-exit.png</file> <file>icons/22x22/application-exit.png</file>
@@ -476,5 +488,8 @@
<file>icons/22x22/tidal.png</file> <file>icons/22x22/tidal.png</file>
<file>icons/22x22/qobuz.png</file> <file>icons/22x22/qobuz.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-black.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> </qresource>
</RCC> </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 ( CREATE TABLE device_%deviceid_directories (
path TEXT NOT NULL, path TEXT NOT NULL DEFAULT '',
subdirs INTEGER NOT NULL subdirs INTEGER NOT NULL
); );
CREATE TABLE device_%deviceid_subdirectories ( CREATE TABLE device_%deviceid_subdirectories (
directory_id INTEGER NOT NULL, directory_id INTEGER NOT NULL,
path TEXT NOT NULL, path TEXT NOT NULL DEFAULT '',
mtime INTEGER NOT NULL mtime INTEGER NOT NULL
); );
CREATE TABLE device_%deviceid_songs ( CREATE TABLE device_%deviceid_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0, originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -77,4 +80,4 @@ CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
tokenize = "unicode61 remove_diacritics 1" 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; DELETE FROM schema_version;
INSERT INTO schema_version (version) VALUES (13); INSERT INTO schema_version (version) VALUES (15);
CREATE TABLE IF NOT EXISTS directories ( CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL, path TEXT NOT NULL DEFAULT '',
subdirs INTEGER NOT NULL subdirs INTEGER NOT NULL
); );
CREATE TABLE IF NOT EXISTS subdirectories ( CREATE TABLE IF NOT EXISTS subdirectories (
directory_id INTEGER NOT NULL, directory_id INTEGER NOT NULL,
path TEXT NOT NULL, path TEXT NOT NULL DEFAULT '',
mtime INTEGER NOT NULL mtime INTEGER NOT NULL
); );
CREATE TABLE IF NOT EXISTS songs ( CREATE TABLE IF NOT EXISTS songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -78,25 +81,25 @@ CREATE TABLE IF NOT EXISTS songs (
CREATE TABLE IF NOT EXISTS subsonic_songs ( CREATE TABLE IF NOT EXISTS subsonic_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -137,25 +143,25 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
CREATE TABLE IF NOT EXISTS tidal_artists_songs ( CREATE TABLE IF NOT EXISTS tidal_artists_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -196,25 +205,25 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
CREATE TABLE IF NOT EXISTS tidal_albums_songs ( CREATE TABLE IF NOT EXISTS tidal_albums_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -255,25 +267,25 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
CREATE TABLE IF NOT EXISTS tidal_songs ( CREATE TABLE IF NOT EXISTS tidal_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -314,25 +329,25 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
CREATE TABLE IF NOT EXISTS qobuz_artists_songs ( CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -373,25 +391,25 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
CREATE TABLE IF NOT EXISTS qobuz_albums_songs ( CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -432,25 +453,25 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
CREATE TABLE IF NOT EXISTS qobuz_songs ( CREATE TABLE IF NOT EXISTS qobuz_songs (
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER NOT NULL DEFAULT -1, track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1, disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1, year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1, originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER NOT NULL DEFAULT 0, compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER NOT NULL DEFAULT 0, beginning INTEGER NOT NULL DEFAULT 0,
length 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, source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1, directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL, url TEXT NOT NULL DEFAULT '',
filetype INTEGER NOT NULL DEFAULT 0, filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1, filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1, mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1, ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER NOT NULL DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0, skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1, lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0, compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0, compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0, compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0, compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER NOT NULL DEFAULT 0, effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
@@ -491,15 +515,15 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
CREATE TABLE IF NOT EXISTS playlists ( CREATE TABLE IF NOT EXISTS playlists (
name TEXT NOT NULL, name TEXT NOT NULL DEFAULT '',
last_played INTEGER NOT NULL DEFAULT -1, last_played INTEGER NOT NULL DEFAULT -1,
ui_order INTEGER NOT NULL DEFAULT 0, ui_order INTEGER NOT NULL DEFAULT 0,
special_type TEXT, special_type TEXT DEFAULT '',
ui_path TEXT, ui_path TEXT DEFAULT '',
is_favorite INTEGER NOT NULL DEFAULT 0, is_favorite INTEGER NOT NULL DEFAULT 0,
dynamic_playlist_type INTEGER, dynamic_playlist_type INTEGER,
dynamic_playlist_backend TEXT, dynamic_playlist_backend TEXT DEFAULT '',
dynamic_playlist_data BLOB dynamic_playlist_data BLOB
); );
@@ -509,27 +533,27 @@ CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL, playlist INTEGER NOT NULL,
type INTEGER NOT NULL DEFAULT 0, type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER, collection_id INTEGER,
playlist_url TEXT, playlist_url TEXT DEFAULT '',
title TEXT, title TEXT DEFAULT '',
album TEXT, album TEXT DEFAULT '',
artist TEXT, artist TEXT DEFAULT '',
albumartist TEXT, albumartist TEXT DEFAULT '',
track INTEGER, track INTEGER,
disc INTEGER, disc INTEGER,
year INTEGER, year INTEGER,
originalyear INTEGER, originalyear INTEGER,
genre TEXT, genre TEXT DEFAULT '',
compilation INTEGER DEFAULT 0, compilation INTEGER DEFAULT 0,
composer TEXT, composer TEXT DEFAULT '',
performer TEXT, performer TEXT DEFAULT '',
grouping TEXT, grouping TEXT DEFAULT '',
comment TEXT, comment TEXT DEFAULT '',
lyrics TEXT, lyrics TEXT DEFAULT '',
artist_id TEXT, artist_id TEXT DEFAULT '',
album_id TEXT, album_id TEXT DEFAULT '',
song_id TEXT, song_id TEXT DEFAULT '',
beginning INTEGER, beginning INTEGER,
length INTEGER, length INTEGER,
@@ -540,44 +564,54 @@ CREATE TABLE IF NOT EXISTS playlist_items (
source INTEGER, source INTEGER,
directory_id INTEGER, directory_id INTEGER,
url TEXT, url TEXT DEFAULT '',
filetype INTEGER, filetype INTEGER,
filesize INTEGER, filesize INTEGER,
mtime INTEGER, mtime INTEGER,
ctime INTEGER, ctime INTEGER,
unavailable INTEGER DEFAULT 0, unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
playcount INTEGER DEFAULT 0, playcount INTEGER DEFAULT 0,
skipcount 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_detected INTEGER DEFAULT 0,
compilation_on INTEGER DEFAULT 0, compilation_on INTEGER DEFAULT 0,
compilation_off INTEGER DEFAULT 0, compilation_off INTEGER DEFAULT 0,
compilation_effective INTEGER DEFAULT 0, compilation_effective INTEGER DEFAULT 0,
art_automatic TEXT, art_automatic TEXT DEFAULT '',
art_manual TEXT, art_manual TEXT DEFAULT '',
effective_albumartist TEXT, effective_albumartist TEXT DEFAULT '',
effective_originalyear INTEGER, effective_originalyear INTEGER,
cue_path TEXT, cue_path TEXT DEFAULT '',
rating INTEGER DEFAULT -1 rating INTEGER DEFAULT -1
); );
CREATE TABLE IF NOT EXISTS devices ( CREATE TABLE IF NOT EXISTS devices (
unique_id TEXT NOT NULL, unique_id TEXT NOT NULL DEFAULT '',
friendly_name TEXT, friendly_name TEXT DEFAULT '',
size INTEGER, size INTEGER,
icon TEXT, icon TEXT DEFAULT '',
schema_version INTEGER NOT NULL DEFAULT 0, schema_version INTEGER NOT NULL DEFAULT 0,
transcode_mode NOT NULL DEFAULT 3, transcode_mode NOT NULL DEFAULT 3,
transcode_format NOT NULL DEFAULT 5 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_url ON songs (url);
CREATE INDEX IF NOT EXISTS idx_comp_artist ON songs (compilation_effective, artist); CREATE INDEX IF NOT EXISTS idx_comp_artist ON songs (compilation_effective, artist);

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 KiB

13
debian/control vendored
View File

@@ -17,6 +17,7 @@ Build-Depends: debhelper (>= 11),
libpulse-dev, libpulse-dev,
libtag1-dev, libtag1-dev,
qtbase5-dev, qtbase5-dev,
qtbase5-private-dev,
qtbase5-dev-tools, qtbase5-dev-tools,
qttools5-dev, qttools5-dev,
libqt5x11extras5-dev, libqt5x11extras5-dev,
@@ -41,24 +42,26 @@ Depends: ${shlibs:Depends},
gstreamer1.0-pulseaudio gstreamer1.0-pulseaudio
Homepage: http://www.strawberrymusicplayer.org/ Homepage: http://www.strawberrymusicplayer.org/
Description: Audio player and music collection organizer 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: Features:
- Play and organize music - Play and organize music
- Supports WAV, FLAC, WavPack, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3 and ASF - Supports WAV, FLAC, WavPack, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3 and ASF
- Audio CD playback - Audio CD playback
- Native desktop notifications - Native desktop notifications
- Playlist management
- Playlists in multiple formats - Playlists in multiple formats
- Advanced audio output and device configuration for bit-perfect playback on Linux - 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 - Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify - 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 - Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Support for multiple backends - Support for multiple backends
- Audio analyzer - Audio analyzer
- Audio equalizer - 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 - 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. 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) endif(DEB_CODENAME AND DEB_DATE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY) 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) 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) 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/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/64x64/strawberry.png DESTINATION share/icons/hicolor/64x64/apps/)
install(FILES ../data/icons/128x128/strawberry.png DESTINATION share/icons/hicolor/128x128/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.desktop DESTINATION share/applications)
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo) install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
install(FILES unix/strawberry.1 unix/strawberry-tagreader.1 DESTINATION share/man/man1) 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) 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}/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") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
endif (APPLE) endif()

View File

@@ -38,6 +38,19 @@
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string> <string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
<key>SUPublicEDKey</key> <key>SUPublicEDKey</key>
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string> <string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>org.strawberrymusicplayer.strawberry</string>
<key>CFBundleURLSchemes</key>
<array>
<string>tidal</string>
</array>
</dict>
</array>
<key>CFBundleDocumentTypes</key> <key>CFBundleDocumentTypes</key>
<array> <array>
<dict> <dict>

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> <translation type="qt">strawberry</translation>
<description> <description>
<p> <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>
<p>Features:</p> <p>Features:</p>
<ul> <ul>
<li>Play and organize music</li> <li>Play and organize music</li>
<li>Supports most popular audio formats and CD playback</li> <li>Supports most popular audio formats and CD playback</li>
<li>Native desktop notifications</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>Advanced audio output and device configuration for bit-perfect playback on Linux</li>
<li>Edit tags on music files</li> <li>Edit tags on audio files</li>
<li>Fetch tags from MusicBrainz</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>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>Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com</li>
<li>Support for multiple backends</li> <li>Support for multiple backends</li>
<li>Audio analyzer and equalizer</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>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> </ul>
</description> </description>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<caption>Song playing showing context</caption> <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>
<screenshot> <screenshot>
<caption>Collection overview</caption> <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> </screenshot>
</screenshots> </screenshots>
<update_contact>eclipseo@fedoraproject.org</update_contact> <update_contact>eclipseo@fedoraproject.org</update_contact>

View File

@@ -5,7 +5,7 @@ Strawberry \- music player and music collection organizer
.B strawberry .B strawberry
[\fI\,options\/\fR] [\fI\,URL(s)\/\fR] [\fI\,options\/\fR] [\fI\,URL(s)\/\fR]
.SH DESCRIPTION .SH DESCRIPTION
Strawberry is a music player especially aimed at audiophiles. Strawberry is a music player aimed at music collectors and audiophiles.
.TP .TP
Features: Features:
.br .br
@@ -17,13 +17,15 @@ Features:
.br .br
- Native desktop notifications - Native desktop notifications
.br .br
- Playlist management
.br
- Playlists in multiple formats - Playlists in multiple formats
.br .br
- Advanced output and device options with support for bit perfect playback on Linux - Advanced output and device options with support for bit perfect playback on Linux
.br .br
- Edit tags on music files - Edit tags on audio files
.br .br
- Fetch tags from MusicBrainz - Automatically retrieve tags from MusicBrainz
.br .br
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify - Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
.br .br
@@ -33,11 +35,15 @@ Features:
.br .br
- Audio analyzer - Audio analyzer
.br .br
- Equalizer - Audio Equalizer
.br .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 .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 .TP
It is a fork of Clementine. The name is inspired by the band Strawbs. It is a fork of Clementine. The name is inspired by the band Strawbs.
.SH OPTIONS .SH OPTIONS

View File

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

View File

@@ -66,9 +66,10 @@
!define CAPABILITIES_HIDE_ICONS "Command to hide icons" !define CAPABILITIES_HIDE_ICONS "Command to hide icons"
!define CAPABILITIES_SHOW_ICONS "Command to show icons" !define CAPABILITIES_SHOW_ICONS "Command to show icons"
Unicode True
SetCompressor /SOLID lzma SetCompressor /SOLID lzma
!addplugindir nsisplugins
!include "MUI2.nsh" !include "MUI2.nsh"
!include "FileAssociation.nsh" !include "FileAssociation.nsh"
!include "Capabilities.nsh" !include "Capabilities.nsh"
@@ -79,14 +80,21 @@ SetCompressor /SOLID lzma
!define MUI_COMPONENTSPAGE_SMALLDESC !define MUI_COMPONENTSPAGE_SMALLDESC
ReserveFile "${NSISDIR}/Plugins/x86-unicode/LockedList.dll"
ReserveFile "${NSISDIR}/Plugins/LockedList64.dll"
!define LockedListPATH $InstallDir
; Installer pages ; Installer pages
!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE COPYING
Page Custom LockedListPageShow
!insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH !insertmacro MUI_PAGE_FINISH
; Uninstaller pages ; Uninstaller pages
!insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_CONFIRM
UninstPage custom un.LockedListPageShow
!insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH !insertmacro MUI_UNPAGE_FINISH
@@ -133,7 +141,16 @@ InstallDirRegKey ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallStri
ShowInstDetails show ShowInstDetails show
ShowUnInstDetails show ShowUnInstDetails show
RequestExecutionLevel admin RequestExecutionLevel admin
;RequestExecutionLevel user
Function LockedListPageShow
LockedList::AddModule /NOUNLOAD \strawberry.exe
LockedList::Dialog /heading "Checking for running programs:" /noprograms "No programs need to close." /searching "Searching for running programs..."
FunctionEnd
Function un.LockedListPageShow
LockedList::AddModule /NOUNLOAD \strawberry.exe
LockedList::Dialog /heading "Checking for running programs:" /noprograms "No programs need to close." /searching "Searching for running programs..."
FunctionEnd
; Check for previous installation, and call the uninstaller if any ; Check for previous installation, and call the uninstaller if any
Function CheckPreviousInstall Function CheckPreviousInstall
@@ -169,30 +186,28 @@ SectionEnd
Section "Strawberry" Strawberry Section "Strawberry" Strawberry
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
File "strawberry.exe" File "strawberry.exe"
File "strawberry-tagreader.exe" File "strawberry-tagreader.exe"
File "strawberry.ico" File "strawberry.ico"
File "sqlite3.exe" File "sqlite3.exe"
File "gst-launch-1.0.exe" File "gst-launch-1.0.exe"
File "gst-discoverer-1.0.exe"
!ifdef arch_x86 !ifdef arch_x86
File "libgcc_s_sjlj-1.dll" File "libgcc_s_sjlj-1.dll"
File "libcrypto-1_1.dll" File "libcrypto-3.dll"
File "libssl-1_1.dll" File "libssl-3.dll"
!endif !endif
!ifdef arch_x64 !ifdef arch_x64
File "libgcc_s_seh-1.dll" File "libgcc_s_seh-1.dll"
File "libcrypto-1_1-x64.dll" File "libcrypto-3-x64.dll"
File "libssl-1_1-x64.dll" File "libssl-3-x64.dll"
!endif !endif
File "avcodec-58.dll" File "avcodec-58.dll"
File "avfilter-7.dll" File "avfilter-7.dll"
File "avformat-58.dll" File "avformat-58.dll"
File "avresample-4.dll"
File "avutil-56.dll" File "avutil-56.dll"
File "libbrotlicommon.dll" File "libbrotlicommon.dll"
File "libbrotlidec.dll" File "libbrotlidec.dll"
@@ -214,6 +229,7 @@ Section "Strawberry" Strawberry
File "libgobject-2.0-0.dll" File "libgobject-2.0-0.dll"
File "libgstapp-1.0-0.dll" File "libgstapp-1.0-0.dll"
File "libgstaudio-1.0-0.dll" File "libgstaudio-1.0-0.dll"
File "libgstbadaudio-1.0-0.dll"
File "libgstbase-1.0-0.dll" File "libgstbase-1.0-0.dll"
File "libgstfft-1.0-0.dll" File "libgstfft-1.0-0.dll"
File "libgstnet-1.0-0.dll" File "libgstnet-1.0-0.dll"
@@ -235,12 +251,13 @@ Section "Strawberry" Strawberry
File "libmp3lame-0.dll" File "libmp3lame-0.dll"
File "libnettle-8.dll" File "libnettle-8.dll"
File "libogg-0.dll" File "libogg-0.dll"
File "libopenmpt-0.dll"
File "libopus-0.dll" File "libopus-0.dll"
File "liborc-0.4-0.dll" File "liborc-0.4-0.dll"
File "libpcre-1.dll" File "libpcre-1.dll"
File "libpcre2-16-0.dll" File "libpcre2-16-0.dll"
File "libpng16-16.dll" File "libpng16-16.dll"
File "libprotobuf-26.dll" File "libprotobuf-29.dll"
File "libpsl-5.dll" File "libpsl-5.dll"
File "libsoup-2.4-1.dll" File "libsoup-2.4-1.dll"
File "libspeex-1.dll" File "libspeex-1.dll"
@@ -252,6 +269,7 @@ Section "Strawberry" Strawberry
File "libunistring-2.dll" File "libunistring-2.dll"
File "libvorbis-0.dll" File "libvorbis-0.dll"
File "libvorbisenc-2.dll" File "libvorbisenc-2.dll"
File "libvorbisfile-3.dll"
File "libwavpack-1.dll" File "libwavpack-1.dll"
File "libwinpthread-1.dll" File "libwinpthread-1.dll"
File "libxml2-2.dll" File "libxml2-2.dll"
@@ -268,7 +286,6 @@ Section "Strawberry" Strawberry
File "Qt6Network.dll" File "Qt6Network.dll"
File "Qt6Sql.dll" File "Qt6Sql.dll"
File "Qt6Widgets.dll" File "Qt6Widgets.dll"
;File "Qt6WinExtras.dll"
File "libqtsparkle-qt6.dll" File "libqtsparkle-qt6.dll"
!else !else
File "Qt5Concurrent.dll" File "Qt5Concurrent.dll"
@@ -277,7 +294,6 @@ Section "Strawberry" Strawberry
File "Qt5Network.dll" File "Qt5Network.dll"
File "Qt5Sql.dll" File "Qt5Sql.dll"
File "Qt5Widgets.dll" File "Qt5Widgets.dll"
File "Qt5WinExtras.dll"
File "libqtsparkle-qt5.dll" File "libqtsparkle-qt5.dll"
!endif !endif
@@ -290,8 +306,6 @@ Section "Strawberry" Strawberry
File "libtermcap.dll" File "libtermcap.dll"
!endif !endif
File "killproc.exe"
; Register Strawberry with Default Programs ; Register Strawberry with Default Programs
Var /GLOBAL AppIcon Var /GLOBAL AppIcon
Var /GLOBAL AppExe Var /GLOBAL AppExe
@@ -329,26 +343,33 @@ Section "GIO modules" gio-modules
File "/oname=libgiognutls.dll" "gio-modules\libgiognutls.dll" File "/oname=libgiognutls.dll" "gio-modules\libgiognutls.dll"
SectionEnd SectionEnd
Section "Qt Platforms" platforms Section "Qt Platform plugins" platforms
SetOutPath "$INSTDIR\platforms" SetOutPath "$INSTDIR\platforms"
File "/oname=qwindows.dll" "platforms\qwindows.dll" File "/oname=qwindows.dll" "platforms\qwindows.dll"
SectionEnd SectionEnd
Section "Qt SQL Drivers" sqldrivers Section "Qt styles" styles
SetOutPath "$INSTDIR\sqldrivers" SetOutPath "$INSTDIR\styles"
File "/oname=qsqlite.dll" "sqldrivers\qsqlite.dll" File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
SectionEnd SectionEnd
Section "Qt image format plugins" imageformats Section "Qt imageformats" imageformats
SetOutPath "$INSTDIR\imageformats" SetOutPath "$INSTDIR\imageformats"
File "/oname=qgif.dll" "imageformats\qgif.dll" File "/oname=qgif.dll" "imageformats\qgif.dll"
File "/oname=qico.dll" "imageformats\qico.dll" File "/oname=qico.dll" "imageformats\qico.dll"
File "/oname=qjpeg.dll" "imageformats\qjpeg.dll" File "/oname=qjpeg.dll" "imageformats\qjpeg.dll"
SectionEnd SectionEnd
Section "Qt style plugins" styles !ifdef with_qt6
SetOutPath "$INSTDIR\styles" Section "Qt TLS plugins" tls
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll" 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 SectionEnd
Section "Gstreamer plugins" gstreamer-plugins Section "Gstreamer plugins" gstreamer-plugins
@@ -400,6 +421,7 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstpbtypes.dll" "gstreamer-plugins\libgstpbtypes.dll" File "/oname=libgstpbtypes.dll" "gstreamer-plugins\libgstpbtypes.dll"
File "/oname=libgstrtp.dll" "gstreamer-plugins\libgstrtp.dll" File "/oname=libgstrtp.dll" "gstreamer-plugins\libgstrtp.dll"
File "/oname=libgstrtsp.dll" "gstreamer-plugins\libgstrtsp.dll" File "/oname=libgstrtsp.dll" "gstreamer-plugins\libgstrtsp.dll"
File "/oname=libgstopenmpt.dll" "gstreamer-plugins\libgstopenmpt.dll"
SectionEnd SectionEnd
@@ -436,8 +458,6 @@ SectionEnd
Section "Uninstall" Section "Uninstall"
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
; Delete all the files ; Delete all the files
Delete "$INSTDIR\strawberry.ico" Delete "$INSTDIR\strawberry.ico"
@@ -445,23 +465,23 @@ Section "Uninstall"
Delete "$INSTDIR\strawberry-tagreader.exe" Delete "$INSTDIR\strawberry-tagreader.exe"
Delete "$INSTDIR\sqlite3.exe" Delete "$INSTDIR\sqlite3.exe"
Delete "$INSTDIR\gst-launch-1.0.exe" Delete "$INSTDIR\gst-launch-1.0.exe"
Delete "$INSTDIR\gst-discoverer-1.0.exe"
!ifdef arch_x86 !ifdef arch_x86
Delete "$INSTDIR\libgcc_s_sjlj-1.dll" Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libcrypto-1_1.dll" Delete "$INSTDIR\libcrypto-3.dll"
Delete "$INSTDIR\libssl-1_1.dll" Delete "$INSTDIR\libssl-3.dll"
!endif !endif
!ifdef arch_x64 !ifdef arch_x64
Delete "$INSTDIR\libgcc_s_seh-1.dll" Delete "$INSTDIR\libgcc_s_seh-1.dll"
Delete "$INSTDIR\libcrypto-1_1-x64.dll" Delete "$INSTDIR\libcrypto-3-x64.dll"
Delete "$INSTDIR\libssl-1_1-x64.dll" Delete "$INSTDIR\libssl-3-x64.dll"
!endif !endif
Delete "$INSTDIR\avcodec-58.dll" Delete "$INSTDIR\avcodec-58.dll"
Delete "$INSTDIR\avfilter-7.dll" Delete "$INSTDIR\avfilter-7.dll"
Delete "$INSTDIR\avformat-58.dll" Delete "$INSTDIR\avformat-58.dll"
Delete "$INSTDIR\avresample-4.dll"
Delete "$INSTDIR\avutil-56.dll" Delete "$INSTDIR\avutil-56.dll"
Delete "$INSTDIR\libbrotlicommon.dll" Delete "$INSTDIR\libbrotlicommon.dll"
Delete "$INSTDIR\libbrotlidec.dll" Delete "$INSTDIR\libbrotlidec.dll"
@@ -483,6 +503,7 @@ Section "Uninstall"
Delete "$INSTDIR\libgobject-2.0-0.dll" Delete "$INSTDIR\libgobject-2.0-0.dll"
Delete "$INSTDIR\libgstapp-1.0-0.dll" Delete "$INSTDIR\libgstapp-1.0-0.dll"
Delete "$INSTDIR\libgstaudio-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\libgstbase-1.0-0.dll"
Delete "$INSTDIR\libgstfft-1.0-0.dll" Delete "$INSTDIR\libgstfft-1.0-0.dll"
Delete "$INSTDIR\libgstnet-1.0-0.dll" Delete "$INSTDIR\libgstnet-1.0-0.dll"
@@ -504,12 +525,13 @@ Section "Uninstall"
Delete "$INSTDIR\libmp3lame-0.dll" Delete "$INSTDIR\libmp3lame-0.dll"
Delete "$INSTDIR\libnettle-8.dll" Delete "$INSTDIR\libnettle-8.dll"
Delete "$INSTDIR\libogg-0.dll" Delete "$INSTDIR\libogg-0.dll"
Delete "$INSTDIR\libopenmpt-0.dll"
Delete "$INSTDIR\libopus-0.dll" Delete "$INSTDIR\libopus-0.dll"
Delete "$INSTDIR\liborc-0.4-0.dll" Delete "$INSTDIR\liborc-0.4-0.dll"
Delete "$INSTDIR\libpcre-1.dll" Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll" Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll" Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-26.dll" Delete "$INSTDIR\libprotobuf-29.dll"
Delete "$INSTDIR\libpsl-5.dll" Delete "$INSTDIR\libpsl-5.dll"
Delete "$INSTDIR\libqtsparkle-qt5.dll" Delete "$INSTDIR\libqtsparkle-qt5.dll"
Delete "$INSTDIR\libqtsparkle-qt6.dll" Delete "$INSTDIR\libqtsparkle-qt6.dll"
@@ -523,6 +545,7 @@ Section "Uninstall"
Delete "$INSTDIR\libunistring-2.dll" Delete "$INSTDIR\libunistring-2.dll"
Delete "$INSTDIR\libvorbis-0.dll" Delete "$INSTDIR\libvorbis-0.dll"
Delete "$INSTDIR\libvorbisenc-2.dll" Delete "$INSTDIR\libvorbisenc-2.dll"
Delete "$INSTDIR\libvorbisfile-3.dll"
Delete "$INSTDIR\libwavpack-1.dll" Delete "$INSTDIR\libwavpack-1.dll"
Delete "$INSTDIR\libwinpthread-1.dll" Delete "$INSTDIR\libwinpthread-1.dll"
Delete "$INSTDIR\libxml2-2.dll" Delete "$INSTDIR\libxml2-2.dll"
@@ -534,14 +557,12 @@ Section "Uninstall"
Delete "$INSTDIR\Qt5Network.dll" Delete "$INSTDIR\Qt5Network.dll"
Delete "$INSTDIR\Qt5Sql.dll" Delete "$INSTDIR\Qt5Sql.dll"
Delete "$INSTDIR\Qt5Widgets.dll" Delete "$INSTDIR\Qt5Widgets.dll"
Delete "$INSTDIR\Qt5WinExtras.dll"
Delete "$INSTDIR\Qt6Concurrent.dll" Delete "$INSTDIR\Qt6Concurrent.dll"
Delete "$INSTDIR\Qt6Core.dll" Delete "$INSTDIR\Qt6Core.dll"
Delete "$INSTDIR\Qt6Gui.dll" Delete "$INSTDIR\Qt6Gui.dll"
Delete "$INSTDIR\Qt6Network.dll" Delete "$INSTDIR\Qt6Network.dll"
Delete "$INSTDIR\Qt6Sql.dll" Delete "$INSTDIR\Qt6Sql.dll"
Delete "$INSTDIR\Qt6Widgets.dll" Delete "$INSTDIR\Qt6Widgets.dll"
Delete "$INSTDIR\Qt6WinExtras.dll"
Delete "$INSTDIR\swresample-3.dll" Delete "$INSTDIR\swresample-3.dll"
Delete "$INSTDIR\swscale-5.dll" Delete "$INSTDIR\swscale-5.dll"
Delete "$INSTDIR\zlib1.dll" Delete "$INSTDIR\zlib1.dll"
@@ -557,8 +578,11 @@ Section "Uninstall"
Delete "$INSTDIR\gio-modules\libgiognutls.dll" Delete "$INSTDIR\gio-modules\libgiognutls.dll"
Delete "$INSTDIR\platforms\qwindows.dll" Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
Delete "$INSTDIR\styles\qwindowsvistastyle.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\qgif.dll"
Delete "$INSTDIR\imageformats\qico.dll" Delete "$INSTDIR\imageformats\qico.dll"
@@ -610,13 +634,14 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstpbtypes.dll" Delete "$INSTDIR\gstreamer-plugins\libgstpbtypes.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstrtp.dll" Delete "$INSTDIR\gstreamer-plugins\libgstrtp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstrtsp.dll" Delete "$INSTDIR\gstreamer-plugins\libgstrtsp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstopenmpt.dll"
Delete "$INSTDIR\killproc.exe"
Delete "$INSTDIR\Uninstall.exe" Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders. ; Remove the installation folders.
RMDir "$INSTDIR\platforms" RMDir "$INSTDIR\platforms"
RMDir "$INSTDIR\styles" RMDir "$INSTDIR\styles"
RMDir "$INSTDIR\tls"
RMDir "$INSTDIR\sqldrivers" RMDir "$INSTDIR\sqldrivers"
RMDir "$INSTDIR\imageformats" RMDir "$INSTDIR\imageformats"
RMDir "$INSTDIR\gio-modules" RMDir "$INSTDIR\gio-modules"

View File

@@ -22,6 +22,7 @@
#include <QtGlobal> #include <QtGlobal>
#include <cstring> #include <cstring>
#include <cmath>
#include <glib.h> #include <glib.h>
@@ -74,7 +75,7 @@ static void gst_fastspectrum_class_init (GstFastSpectrumClass * klass) {
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass); GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass); GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (klass);
GstCaps *caps; GstCaps *caps = nullptr;
gobject_class->set_property = gst_fastspectrum_set_property; gobject_class->set_property = gst_fastspectrum_set_property;
gobject_class->get_property = gst_fastspectrum_get_property; gobject_class->get_property = gst_fastspectrum_get_property;
@@ -264,7 +265,7 @@ static void input_data_mixed_float(const guint8* _in, double* out, guint len, do
Q_UNUSED(max_value); Q_UNUSED(max_value);
guint j, ip = 0; guint j = 0, ip = 0;
const gfloat *in = reinterpret_cast<const gfloat*>(_in); const gfloat *in = reinterpret_cast<const gfloat*>(_in);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
@@ -278,7 +279,7 @@ static void input_data_mixed_double (const guint8 * _in, double* out, guint len,
Q_UNUSED(max_value); Q_UNUSED(max_value);
guint j, ip = 0; guint j = 0, ip = 0;
const gdouble *in = reinterpret_cast<const gdouble*>(_in); const gdouble *in = reinterpret_cast<const gdouble*>(_in);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
@@ -290,7 +291,7 @@ static void input_data_mixed_double (const guint8 * _in, double* out, guint len,
static void input_data_mixed_int32_max (const guint8 * _in, double* out, guint len, double max_value, guint op, guint nfft) { static void input_data_mixed_int32_max (const guint8 * _in, double* out, guint len, double max_value, guint op, guint nfft) {
guint j, ip = 0; guint j = 0, ip = 0;
const gint32 *in = reinterpret_cast<const gint32*>(_in); const gint32 *in = reinterpret_cast<const gint32*>(_in);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
@@ -302,7 +303,7 @@ static void input_data_mixed_int32_max (const guint8 * _in, double* out, guint l
static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint len, double max_value, guint op, guint nfft) { static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint len, double max_value, guint op, guint nfft) {
guint j; guint j = 0;
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN #if G_BYTE_ORDER == G_BIG_ENDIAN
@@ -310,8 +311,9 @@ static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint l
#else #else
gint32 value = GST_READ_UINT24_LE (_in); gint32 value = GST_READ_UINT24_LE (_in);
#endif #endif
if (value & 0x00800000) if (value & 0x00800000) {
value |= 0xff000000; value |= 0xff000000;
}
out[op] = value / max_value; out[op] = value / max_value;
op = (op + 1) % nfft; op = (op + 1) % nfft;
@@ -322,7 +324,7 @@ static void input_data_mixed_int24_max (const guint8 * _in, double* out, guint l
static void input_data_mixed_int16_max (const guint8 * _in, double * out, guint len, double max_value, guint op, guint nfft) { static void input_data_mixed_int16_max (const guint8 * _in, double * out, guint len, double max_value, guint op, guint nfft) {
guint j, ip = 0; guint j = 0, ip = 0;
const gint16 *in = reinterpret_cast<const gint16*>(_in); const gint16 *in = reinterpret_cast<const gint16*>(_in);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
@@ -369,17 +371,18 @@ static gboolean gst_fastspectrum_setup (GstAudioFilter * base, const GstAudioInf
static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_pos) { static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_pos) {
guint i; guint i = 0;
guint bands = spectrum->bands; guint bands = spectrum->bands;
guint nfft = 2 * bands - 2; 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]; spectrum->fft_input[i] = spectrum->input_ring_buffer[(input_pos + i) % nfft];
}
// Should be safe to execute the same plan multiple times in parallel. // Should be safe to execute the same plan multiple times in parallel.
fftw_execute(spectrum->plan); fftw_execute(spectrum->plan);
gdouble val; gdouble val = 0.0;
/* Calculate magnitude in db */ /* Calculate magnitude in db */
for (i = 0; i < bands; i++) { for (i = 0; i < bands; i++) {
val = spectrum->fft_output[i][0] * spectrum->fft_output[i][0]; val = spectrum->fft_output[i][0] * spectrum->fft_output[i][0];
@@ -399,13 +402,13 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
double max_value = (1UL << ((bps << 3) - 1)) - 1; double max_value = (1UL << ((bps << 3) - 1)) - 1;
guint bands = spectrum->bands; guint bands = spectrum->bands;
guint nfft = 2 * bands - 2; guint nfft = 2 * bands - 2;
guint input_pos; guint input_pos = 0;
GstMapInfo map; GstMapInfo map;
const guint8 *data; const guint8 *data = nullptr;
gsize size; gsize size = 0;
guint fft_todo, msg_todo, block_size; guint fft_todo = 0, msg_todo = 0, block_size = 0;
gboolean have_full_interval; gboolean have_full_interval = false;
GstFastSpectrumInputData input_data; GstFastSpectrumInputData input_data = nullptr;
g_mutex_lock (&spectrum->lock); g_mutex_lock (&spectrum->lock);
gst_buffer_map (buffer, &map, GST_MAP_READ); gst_buffer_map (buffer, &map, GST_MAP_READ);
@@ -434,8 +437,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
/* rounding error for frames_per_interval in ns, /* rounding error for frames_per_interval in ns,
* aggregated it in accumulated_error */ * aggregated it in accumulated_error */
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND; 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; spectrum->frames_per_interval = 1;
}
GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %" GST_INFO_OBJECT (spectrum, "interval %" GST_TIME_FORMAT ", fpi %"
G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT, G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT,
@@ -447,8 +451,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
gst_fastspectrum_flush (spectrum); gst_fastspectrum_flush (spectrum);
} }
if (spectrum->num_frames == 0) if (spectrum->num_frames == 0) {
spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer); spectrum->message_ts = GST_BUFFER_TIMESTAMP (buffer);
}
input_pos = spectrum->input_pos; input_pos = spectrum->input_pos;
input_data = spectrum->input_data; input_data = spectrum->input_data;
@@ -461,10 +466,12 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
"message frames todo: %u, fft frames todo: %u, input frames %" "message frames todo: %u, fft frames todo: %u, input frames %"
G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf)); G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
block_size = msg_todo; block_size = msg_todo;
if (block_size > (size / bpf)) if (block_size > (size / bpf)) {
block_size = (size / bpf); block_size = (size / bpf);
if (block_size > fft_todo) }
if (block_size > fft_todo) {
block_size = fft_todo; block_size = fft_todo;
}
/* Move the current frames into our ringbuffers */ /* Move the current frames into our ringbuffers */
input_data(data, spectrum->input_ring_buffer, block_size, max_value, input_pos, nfft); input_data(data, spectrum->input_ring_buffer, block_size, max_value, input_pos, nfft);
@@ -513,8 +520,9 @@ static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, Gst
memset(spectrum->spect_magnitude, 0, spectrum->bands * sizeof(double)); 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->message_ts += gst_util_uint64_scale (spectrum->num_frames, GST_SECOND, rate);
}
spectrum->num_frames = 0; spectrum->num_frames = 0;
spectrum->num_fft = 0; spectrum->num_fft = 0;

View File

@@ -23,7 +23,7 @@
namespace { 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)) { if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) {
return FALSE; return FALSE;

View File

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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,7 +24,9 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <cxxabi.h> #ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <glib.h> #include <glib.h>
#ifdef HAVE_BACKTRACE #ifdef HAVE_BACKTRACE
@@ -53,10 +56,10 @@ static QMap<QString, Level>* sClassLevels = nullptr;
static QIODevice *sNullDevice = nullptr; static QIODevice *sNullDevice = nullptr;
//const char* kDefaultLogLevels = "*:3"; //const char* kDefaultLogLevels = "*:3";
const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3"; const char *kDefaultLogLevels = "GstEnginePipeline:2,*:3";
static const char *kMessageHandlerMagic = "__logging_message__"; static const char *kMessageHandlerMagic = "__logging_message__";
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic); static const size_t kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
static QtMessageHandler sOriginalMessageHandler = nullptr; static QtMessageHandler sOriginalMessageHandler = nullptr;
template <class T> template <class T>
@@ -98,7 +101,7 @@ class DebugBase : public QDebug {
// Debug message will be stored in a buffer. // Debug message will be stored in a buffer.
class BufferedDebug : public DebugBase<BufferedDebug> { class BufferedDebug : public DebugBase<BufferedDebug> {
public: public:
BufferedDebug() {} BufferedDebug() = default;
explicit BufferedDebug(QtMsgType) : buf_(new QBuffer, later_deleter) { explicit BufferedDebug(QtMsgType) : buf_(new QBuffer, later_deleter) {
buf_->open(QIODevice::WriteOnly); buf_->open(QIODevice::WriteOnly);
@@ -117,7 +120,7 @@ class BufferedDebug : public DebugBase<BufferedDebug> {
// Debug message will be logged immediately. // Debug message will be logged immediately.
class LoggedDebug : public DebugBase<LoggedDebug> { class LoggedDebug : public DebugBase<LoggedDebug> {
public: public:
LoggedDebug() {} LoggedDebug() = default;
explicit LoggedDebug(QtMsgType t) : DebugBase(t) { nospace() << kMessageHandlerMagic; } explicit LoggedDebug(QtMsgType t) : DebugBase(t) { nospace() << kMessageHandlerMagic; }
}; };
@@ -177,7 +180,7 @@ void SetLevels(const QString &levels) {
if (!sClassLevels) return; if (!sClassLevels) return;
for (const QString& item : levels.split(',')) { for (const QString &item : levels.split(',')) {
const QStringList class_level = item.split(':'); const QStringList class_level = item.split(':');
QString class_name; QString class_name;
@@ -223,7 +226,7 @@ static QString ParsePrettyFunction(const char *pretty_function) {
const int space = class_name.lastIndexOf(' '); const int space = class_name.lastIndexOf(' ');
if (space != -1) { if (space != -1) {
class_name = class_name.mid(space+1); class_name = class_name.mid(space + 1);
} }
return class_name; return class_name;
@@ -274,12 +277,12 @@ static T CreateLogger(Level level, const QString &class_name, int line, const ch
return ret.space(); return ret.space();
} }
#ifdef Q_OS_UNIX
QString CXXDemangle(const QString &mangled_function); QString CXXDemangle(const QString &mangled_function);
QString CXXDemangle(const QString &mangled_function) { QString CXXDemangle(const QString &mangled_function) {
int status; 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) { if (status == 0) {
QString ret = QString::fromLatin1(demangled_function); QString ret = QString::fromLatin1(demangled_function);
free(demangled_function); free(demangled_function);
@@ -288,9 +291,24 @@ QString CXXDemangle(const QString &mangled_function) {
return mangled_function; // Probably not a C++ 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);
QString DarwinDemangle(const QString &symbol) { QString DarwinDemangle(const QString &symbol) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
@@ -302,23 +320,9 @@ QString DarwinDemangle(const QString &symbol) {
return CXXDemangle(mangled_function); return CXXDemangle(mangled_function);
} }
#endif // Q_OS_MACOS
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);
}
QString DemangleSymbol(const QString &symbol); QString DemangleSymbol(const QString &symbol);
QString DemangleSymbol(const QString &symbol) { QString DemangleSymbol(const QString &symbol) {
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
return DarwinDemangle(symbol); return DarwinDemangle(symbol);
@@ -331,9 +335,9 @@ QString DemangleSymbol(const QString &symbol) {
void DumpStackTrace() { void DumpStackTrace() {
#ifdef HAVE_BACKTRACE #ifdef HAVE_BACKTRACE
void* callstack[128]; void *callstack[128];
int callstack_size = backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack)); 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. // Start from 1 to skip ourself.
for (int i = 1; i < callstack_size; ++i) { for (int i = 1; i < callstack_size; ++i) {
std::cerr << DemangleSymbol(QString::fromLatin1(symbols[i])).toStdString() << std::endl; std::cerr << DemangleSymbol(QString::fromLatin1(symbols[i])).toStdString() << std::endl;
@@ -349,9 +353,9 @@ void DumpStackTrace() {
// doesn't override any behavior that should be needed after return. // 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) #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 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 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 CreateLoggerError(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Error); }
#ifdef QT_NO_WARNING_OUTPUT #ifdef QT_NO_WARNING_OUTPUT
QNoDebug CreateLoggerWarning(int, const char*, const char*) { return QNoDebug(); } QNoDebug CreateLoggerWarning(int, const char*, const char*) { return QNoDebug(); }
@@ -370,7 +374,7 @@ QDebug CreateLoggerError(int line, const char *pretty_function, const char* cate
namespace { namespace {
template <typename T> 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()); return QString("%1%2").arg(duration.count()).arg(unit.c_str());
} }

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -28,18 +29,31 @@
# define qLog(level) while (false) QNoDebug() # define qLog(level) while (false) QNoDebug()
# define qLogCat(level, category) while (false) QNoDebug() # define qLogCat(level, category) while (false) QNoDebug()
#else #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. // 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 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. // 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 #endif // QT_NO_DEBUG_STREAM
namespace logging { namespace logging {
class NullDevice : public QIODevice { class NullDevice : public QIODevice {
Q_OBJECT
public:
NullDevice(QObject *parent = nullptr) : QIODevice(parent) {}
protected: protected:
qint64 readData(char*, qint64) override { return -1; } qint64 readData(char*, qint64) override { return -1; }
qint64 writeData(const char*, qint64 len) override { return len; } qint64 writeData(const char*, qint64 len) override { return len; }

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -63,7 +64,7 @@ void _MessageHandlerBase::SetDevice(QIODevice *device) {
void _MessageHandlerBase::DeviceReadyRead() { void _MessageHandlerBase::DeviceReadyRead() {
while (device_->bytesAvailable()) { while (device_->bytesAvailable() > 0) {
if (!reading_protobuf_) { if (!reading_protobuf_) {
// Read the length of the next message // Read the length of the next message
QDataStream s(device_); QDataStream s(device_);

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -129,7 +130,7 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
template <typename MT> template <typename MT>
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) { void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
std::string data = message.SerializeAsString(); 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> template<typename MT>

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -163,8 +164,9 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4); worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4);
local_server_name_ = qApp->applicationName().toLower(); local_server_name_ = qApp->applicationName().toLower();
if (local_server_name_.isEmpty()) if (local_server_name_.isEmpty()) {
local_server_name_ = "workerpool"; local_server_name_ = "workerpool";
}
} }
@@ -217,7 +219,7 @@ void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name)
template <typename HandlerType> template <typename HandlerType>
void WorkerPool<HandlerType>::Start() { void WorkerPool<HandlerType>::Start() {
metaObject()->invokeMethod(this, "DoStart"); QMetaObject::invokeMethod(this, "DoStart");
} }
template <typename HandlerType> template <typename HandlerType>
@@ -231,9 +233,9 @@ void WorkerPool<HandlerType>::DoStart() {
executable_path_ = executable_name_; executable_path_ = executable_name_;
QStringList search_path; QStringList search_path;
search_path << qApp->applicationDirPath(); search_path << QCoreApplication::applicationDirPath();
#if defined(Q_OS_MACOS) && defined(USE_BUNDLE) #if defined(Q_OS_MACOS) && defined(USE_BUNDLE)
search_path << qApp->applicationDirPath() + "/" + USE_BUNDLE_DIR; search_path << QCoreApplication::applicationDirPath() + "/" + USE_BUNDLE_DIR;
#endif #endif
for (const QString &path_prefix : search_path) { for (const QString &path_prefix : search_path) {
@@ -278,9 +280,9 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
// Create a server, find an unused name and start listening // Create a server, find an unused name and start listening
forever { forever {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
const int unique_number = QRandomGenerator::global()->bounded((int)(quint64(this) & 0xFFFFFFFF)); const quint32 unique_number = QRandomGenerator::global()->bounded(static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
#else #else
const int unique_number = qrand() ^ ((int)(quint64(this) & 0xFFFFFFFF)); const quint32 unique_number = qrand() ^ (static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
#endif #endif
const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number); const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number);
@@ -380,7 +382,7 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
} }
// Wake up the main thread // Wake up the main thread
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
return reply; return reply;

View File

@@ -1,16 +1,31 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
set(MESSAGES tagreadermessages.proto) 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}) protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
link_directories( link_directories(
${GLIB_LIBRARY_DIRS} ${GLIB_LIBRARY_DIRS}
${PROTOBUF_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}) add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
@@ -19,7 +34,6 @@ target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
) )
target_include_directories(libstrawberry-tagreader PRIVATE target_include_directories(libstrawberry-tagreader PRIVATE
${TAGLIB_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
@@ -30,8 +44,17 @@ target_include_directories(libstrawberry-tagreader PRIVATE
target_link_libraries(libstrawberry-tagreader PRIVATE target_link_libraries(libstrawberry-tagreader PRIVATE
${GLIB_LIBRARIES} ${GLIB_LIBRARIES}
${PROTOBUF_LIBRARY} ${PROTOBUF_LIBRARY}
${TAGLIB_LIBRARIES}
${QtCore_LIBRARIES} ${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES} ${QtNetwork_LIBRARIES}
libstrawberry-common 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; DSDIFF = 15;
PCM = 16; PCM = 16;
APE = 17; APE = 17;
MOD = 18;
S3M = 19;
XM = 20;
IT = 21;
CDDA = 90; CDDA = 90;
STREAM = 91; STREAM = 91;
} }
@@ -60,10 +64,11 @@ message SongMetadata {
optional int32 playcount = 27; optional int32 playcount = 27;
optional int32 skipcount = 28; optional int32 skipcount = 28;
optional int32 lastplayed = 29; optional int64 lastplayed = 29;
optional int64 lastseen = 30;
optional bool suspicious_tags = 30; optional bool suspicious_tags = 31;
optional string art_automatic = 31; optional string art_automatic = 32;
} }

View File

@@ -1,6 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com> Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018, Jonas Kvinge <jonas@jkvinge.net> Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,11 +18,10 @@
#include "config.h" #include "config.h"
#include "tagreader.h" #include "tagreadertaglib.h"
#include <string>
#include <memory> #include <memory>
#include <list>
#include <map>
#include <sys/stat.h> #include <sys/stat.h>
#include <taglib/taglib.h> #include <taglib/taglib.h>
@@ -68,6 +67,10 @@
#include <taglib/opusfile.h> #include <taglib/opusfile.h>
#include <taglib/trueaudiofile.h> #include <taglib/trueaudiofile.h>
#include <taglib/apefile.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 #ifdef HAVE_TAGLIB_DSFFILE
# include <taglib/dsffile.h> # include <taglib/dsffile.h>
#endif #endif
@@ -78,13 +81,11 @@
#include <QtGlobal> #include <QtGlobal>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QList>
#include <QVector> #include <QVector>
#include <QByteArray> #include <QByteArray>
#include <QDateTime>
#include <QVariant>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QDateTime>
#include <QtDebug> #include <QtDebug>
#include "core/logging.h" #include "core/logging.h"
@@ -93,12 +94,17 @@
class FileRefFactory { class FileRefFactory {
public: public:
virtual ~FileRefFactory() {} FileRefFactory() = default;
virtual ~FileRefFactory() = default;
virtual TagLib::FileRef *GetFileRef(const QString &filename) = 0; virtual TagLib::FileRef *GetFileRef(const QString &filename) = 0;
private:
Q_DISABLE_COPY(FileRefFactory)
}; };
class TagLibFileRefFactory : public FileRefFactory { class TagLibFileRefFactory : public FileRefFactory {
public: public:
TagLibFileRefFactory() = default;
TagLib::FileRef *GetFileRef(const QString &filename) override { TagLib::FileRef *GetFileRef(const QString &filename) override {
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
return new TagLib::FileRef(filename.toStdWString().c_str()); return new TagLib::FileRef(filename.toStdWString().c_str());
@@ -106,6 +112,8 @@ class TagLibFileRefFactory : public FileRefFactory {
return new TagLib::FileRef(QFile::encodeName(filename).constData()); return new TagLib::FileRef(QFile::encodeName(filename).constData());
#endif #endif
} }
private:
Q_DISABLE_COPY(TagLibFileRefFactory)
}; };
namespace { namespace {
@@ -128,25 +136,22 @@ const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear";
} // namespace } // namespace
TagReader::TagReader() : TagReaderTagLib::TagReaderTagLib() : factory_(new TagLibFileRefFactory) {}
factory_(new TagLibFileRefFactory),
kEmbeddedCover("(embedded)") {
}
TagReader::~TagReader() { TagReaderTagLib::~TagReaderTagLib() {
delete factory_; delete factory_;
} }
bool TagReader::IsMediaFile(const QString &filename) const { bool TagReaderTagLib::IsMediaFile(const QString &filename) const {
qLog(Debug) << "Checking for valid file" << filename; qLog(Debug) << "Checking for valid file" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(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::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; 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::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::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::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 #ifdef HAVE_TAGLIB_DSFFILE
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_DSF; if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_DSF;
#endif #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 QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
const QFileInfo info(filename); const QFileInfo info(filename);
@@ -189,6 +198,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
#else #else
song->set_ctime(info.created().toSecsSinceEpoch()); song->set_ctime(info.created().toSecsSinceEpoch());
#endif #endif
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename)); std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (fileref->isNull()) { if (fileref->isNull()) {
@@ -399,7 +409,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
if (compilation.isEmpty()) { if (compilation.isEmpty()) {
// well, it wasn't set, but if the artist is VA assume it's a compilation // 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); song->set_compilation(true);
} }
} }
@@ -414,6 +424,7 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
SetDefault(track); SetDefault(track);
SetDefault(disc); SetDefault(disc);
SetDefault(year); SetDefault(year);
SetDefault(originalyear);
SetDefault(bitrate); SetDefault(bitrate);
SetDefault(samplerate); SetDefault(samplerate);
SetDefault(bitdepth); 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(); QString tmp = TStringToQString(tag).trimmed();
output->assign(DataCommaSizeFromQString(tmp)); 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)); 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["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer()); 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"); TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
if (it != map.end()) { 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("COMPOSER", StdStringToTaglibString(song.composer()), true);
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), 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; if (filename.isEmpty()) return false;
@@ -603,7 +614,7 @@ bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMeta
return result; 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->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); 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()); const QByteArray utf8(value.toUtf8());
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag); 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); TagLib::ByteVector id_vector(id);
QVector<TagLib::ByteVector> frames_buffer; 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"); TagLib::ByteVector id_vector("USLT");
QVector<TagLib::ByteVector> frames_buffer; 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(); 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; 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; if (filename.isEmpty()) return false;

View File

@@ -1,6 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com> Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018, Jonas Kvinge <jonas@jkvinge.net> Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,8 +16,8 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef TAGREADER_H #ifndef TAGREADERTAGLIB_H
#define TAGREADER_H #define TAGREADERTAGLIB_H
#include "config.h" #include "config.h"
@@ -33,29 +33,31 @@
#include <taglib/apefile.h> #include <taglib/apefile.h>
#include <taglib/id3v2tag.h> #include <taglib/id3v2tag.h>
#include "tagreaderbase.h"
#include "tagreadermessages.pb.h" #include "tagreadermessages.pb.h"
class FileRefFactory; class FileRefFactory;
/** /*
* This class holds all useful methods to read and write tags from/to files. * 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) * 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: public:
explicit TagReader(); explicit TagReaderTagLib();
~TagReader(); ~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; 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 TagLib::String &tag, std::string *output);
static void Decode(const QString &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 SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
void SetUnsyncLyricsFrame(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: private:
FileRefFactory *factory_; 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,19 +3,19 @@ cmake_minimum_required(VERSION 3.0)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(SOURCES main.cpp tagreaderworker.cpp) set(SOURCES main.cpp tagreaderworker.cpp)
set(HEADERS tagreaderworker.h)
if(BUILD_WITH_QT6) qt_wrap_cpp(MOC ${HEADERS})
qt6_wrap_cpp(MOC ${HEADERS})
qt6_add_resources(QRC data/data.qrc) link_directories(${GLIB_LIBRARY_DIRS})
else()
qt5_wrap_cpp(MOC ${HEADERS}) if(USE_TAGLIB AND TAGLIB_FOUND)
qt5_add_resources(QRC data/data.qrc) link_directories(${TAGLIB_LIBRARY_DIRS})
endif() endif()
link_directories( if(USE_TAGPARSER AND TAGPARSER_FOUND)
${GLIB_LIBRARY_DIRS} link_directories(${TAGPARSER_LIBRARY_DIRS})
${TAGLIB_LIBRARY_DIRS} endif()
)
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC}) add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
@@ -25,7 +25,6 @@ target_include_directories(strawberry-tagreader SYSTEM PRIVATE
) )
target_include_directories(strawberry-tagreader PRIVATE target_include_directories(strawberry-tagreader PRIVATE
${TAGLIB_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common ${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader ${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader
${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader ${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader
@@ -34,20 +33,29 @@ target_include_directories(strawberry-tagreader PRIVATE
target_link_libraries(strawberry-tagreader PRIVATE target_link_libraries(strawberry-tagreader PRIVATE
${GLIB_LIBRARIES} ${GLIB_LIBRARIES}
${TAGLIB_LIBRARIES}
${QtCore_LIBRARIES} ${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES} ${QtNetwork_LIBRARIES}
libstrawberry-common libstrawberry-common
libstrawberry-tagreader 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) target_link_libraries(strawberry-tagreader PRIVATE execinfo)
endif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") endif()
if(APPLE) if(APPLE)
target_link_libraries(strawberry-tagreader PRIVATE /System/Library/Frameworks/Foundation.framework) target_link_libraries(strawberry-tagreader PRIVATE /System/Library/Frameworks/Foundation.framework)
endif(APPLE) endif()
if(APPLE) if(APPLE)
install(TARGETS strawberry-tagreader DESTINATION ${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns) 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

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,18 +18,18 @@
#include "config.h" #include "config.h"
#include <QtGlobal>
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
#include <sys/time.h> #include <sys/time.h>
#endif
#include <iostream> #include <iostream>
#include <QtGlobal>
#include <QCoreApplication> #include <QCoreApplication>
#include <QList> #include <QList>
#include <QLocalSocket> #include <QString>
#include <QSsl>
#include <QSslCertificate>
#include <QSslSocket>
#include <QStringList> #include <QStringList>
#include <QtDebug> #include <QLocalSocket>
#include "core/logging.h" #include "core/logging.h"
#include "tagreaderworker.h" #include "tagreaderworker.h"
@@ -63,10 +64,6 @@ int main(int argc, char **argv) {
return 1; 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); TagReaderWorker worker(&socket);
return a.exec(); return a.exec();

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -58,5 +59,5 @@ void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
void TagReaderWorker::DeviceClosed() { void TagReaderWorker::DeviceClosed() {
AbstractMessageHandler<spb::tagreader::Message>::DeviceClosed(); AbstractMessageHandler<spb::tagreader::Message>::DeviceClosed();
qApp->exit(); QCoreApplication::exit();
} }

View File

@@ -1,5 +1,6 @@
/* This file is part of Strawberry. /* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com> Copyright 2011, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
Strawberry is free software: you can redistribute it and/or modify Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,12 +24,18 @@
#include <QObject> #include <QObject>
#include "core/messagehandler.h" #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" #include "tagreadermessages.pb.h"
class QIODevice; class QIODevice;
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> { class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
Q_OBJECT
public: public:
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr); explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
@@ -37,7 +44,11 @@ class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
void DeviceClosed() override; void DeviceClosed() override;
private: private:
TagReader tag_reader_; #if defined(USE_TAGLIB)
TagReaderTagLib tag_reader_;
#elif defined(USE_TAGPARSER)
TagReaderTagParser tag_reader_;
#endif
}; };
#endif // TAGREADERWORKER_H #endif // TAGREADERWORKER_H

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
if(HAVE_TRANSLATIONS) if(HAVE_TRANSLATIONS)
include(../cmake/Translations.cmake) include(../cmake/Translations.cmake)
endif(HAVE_TRANSLATIONS) endif()
set(SOURCES set(SOURCES
core/mainwindow.cpp core/mainwindow.cpp
@@ -11,6 +11,7 @@ set(SOURCES
core/player.cpp core/player.cpp
core/commandlineoptions.cpp core/commandlineoptions.cpp
core/database.cpp core/database.cpp
core/sqlquery.cpp
core/metatypes.cpp core/metatypes.cpp
core/deletefiles.cpp core/deletefiles.cpp
core/filesystemmusicstorage.cpp core/filesystemmusicstorage.cpp
@@ -36,11 +37,10 @@ set(SOURCES
core/utilities.cpp core/utilities.cpp
core/imageutils.cpp core/imageutils.cpp
core/iconloader.cpp core/iconloader.cpp
core/qtsystemtrayicon.cpp
core/standarditemiconloader.cpp core/standarditemiconloader.cpp
core/systemtrayicon.cpp
core/scopedtransaction.cpp core/scopedtransaction.cpp
core/translations.cpp core/translations.cpp
core/systemtrayicon.cpp
engine/enginetype.cpp engine/enginetype.cpp
engine/enginebase.cpp engine/enginebase.cpp
@@ -76,6 +76,7 @@ set(SOURCES
collection/sqlrow.cpp collection/sqlrow.cpp
collection/savedgroupingmanager.cpp collection/savedgroupingmanager.cpp
collection/groupbydialog.cpp collection/groupbydialog.cpp
collection/collectiontask.cpp
playlist/playlist.cpp playlist/playlist.cpp
playlist/playlistbackend.cpp playlist/playlistbackend.cpp
@@ -215,9 +216,6 @@ set(SOURCES
osd/osdbase.cpp osd/osdbase.cpp
osd/osdpretty.cpp osd/osdpretty.cpp
musicbrainz/acoustidclient.cpp
musicbrainz/musicbrainzclient.cpp
internet/internetservices.cpp internet/internetservices.cpp
internet/internetservice.cpp internet/internetservice.cpp
internet/internetplaylistitem.cpp internet/internetplaylistitem.cpp
@@ -232,6 +230,17 @@ set(SOURCES
internet/internetcollectionviewcontainer.cpp internet/internetcollectionviewcontainer.cpp
internet/internetsearchview.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/audioscrobbler.cpp
scrobbler/scrobblerservices.cpp scrobbler/scrobblerservices.cpp
scrobbler/scrobblerservice.cpp scrobbler/scrobblerservice.cpp
@@ -253,11 +262,13 @@ set(SOURCES
set(HEADERS set(HEADERS
core/mainwindow.h core/mainwindow.h
core/application.h core/application.h
core/appearance.h
core/player.h core/player.h
core/database.h core/database.h
core/deletefiles.h core/deletefiles.h
core/filesystemwatcherinterface.h core/filesystemwatcherinterface.h
core/mergedproxymodel.h core/mergedproxymodel.h
core/multisortfilterproxy.h
core/networkaccessmanager.h core/networkaccessmanager.h
core/threadsafenetworkdiskcache.h core/threadsafenetworkdiskcache.h
core/networktimeouts.h core/networktimeouts.h
@@ -265,10 +276,11 @@ set(HEADERS
core/songloader.h core/songloader.h
core/tagreaderclient.h core/tagreaderclient.h
core/taskmanager.h core/taskmanager.h
core/thread.h
core/urlhandler.h core/urlhandler.h
core/qtsystemtrayicon.h
core/standarditemiconloader.h core/standarditemiconloader.h
core/systemtrayicon.h core/translations.h
core/potranslator.h
core/mimedata.h core/mimedata.h
core/stylesheetloader.h core/stylesheetloader.h
@@ -310,6 +322,7 @@ set(HEADERS
playlist/playlistlistcontainer.h playlist/playlistlistcontainer.h
playlist/playlistlistmodel.h playlist/playlistlistmodel.h
playlist/playlistlistview.h playlist/playlistlistview.h
playlist/playlistlistsortfiltermodel.h
playlist/playlistmanager.h playlist/playlistmanager.h
playlist/playlistsaveoptionsdialog.h playlist/playlistsaveoptionsdialog.h
playlist/playlistsequence.h playlist/playlistsequence.h
@@ -330,10 +343,13 @@ set(HEADERS
playlistparsers/parserbase.h playlistparsers/parserbase.h
playlistparsers/playlistparser.h playlistparsers/playlistparser.h
playlistparsers/plsparser.h playlistparsers/plsparser.h
playlistparsers/wplparser.h
playlistparsers/xmlparser.h
playlistparsers/xspfparser.h playlistparsers/xspfparser.h
smartplaylists/playlistgenerator.h smartplaylists/playlistgenerator.h
smartplaylists/playlistgeneratorinserter.h smartplaylists/playlistgeneratorinserter.h
smartplaylists/playlistquerygenerator.h
smartplaylists/playlistgeneratormimedata.h smartplaylists/playlistgeneratormimedata.h
smartplaylists/smartplaylistquerywizardplugin.h smartplaylists/smartplaylistquerywizardplugin.h
smartplaylists/smartplaylistsearchpreview.h smartplaylists/smartplaylistsearchpreview.h
@@ -428,24 +444,33 @@ set(HEADERS
widgets/loginstatewidget.h widgets/loginstatewidget.h
widgets/qsearchfield.h widgets/qsearchfield.h
widgets/ratingwidget.h widgets/ratingwidget.h
widgets/forcescrollperpixel.h
osd/osdbase.h osd/osdbase.h
osd/osdpretty.h osd/osdpretty.h
musicbrainz/acoustidclient.h
musicbrainz/musicbrainzclient.h
internet/internetservices.h internet/internetservices.h
internet/internetservice.h internet/internetservice.h
internet/internetsongmimedata.h internet/internetsongmimedata.h
internet/internetsearchview.h
internet/internetsearchmodel.h internet/internetsearchmodel.h
internet/internetsearchsortmodel.h
internet/internetsearchitemdelegate.h
internet/internetsearchview.h
internet/localredirectserver.h internet/localredirectserver.h
internet/internetsongsview.h internet/internetsongsview.h
internet/internettabsview.h internet/internettabsview.h
internet/internetcollectionview.h internet/internetcollectionview.h
internet/internetcollectionviewcontainer.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/audioscrobbler.h
scrobbler/scrobblerservices.h scrobbler/scrobblerservices.h
@@ -530,6 +555,8 @@ set(UI
internet/internetcollectionviewcontainer.ui internet/internetcollectionviewcontainer.ui
internet/internetsearchview.ui internet/internetsearchview.ui
radios/radioviewcontainer.ui
organize/organizedialog.ui organize/organizedialog.ui
organize/organizeerrordialog.ui organize/organizeerrordialog.ui
@@ -542,7 +569,7 @@ option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
if(NOT APPLE) if(NOT APPLE)
set(NOT_APPLE ON) 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() endif()
if(HAVE_GLOBALSHORTCUTS) if(HAVE_GLOBALSHORTCUTS)
@@ -551,137 +578,62 @@ if(HAVE_GLOBALSHORTCUTS)
HEADERS globalshortcuts/globalshortcutsmanager.h globalshortcuts/globalshortcutsbackend.h globalshortcuts/globalshortcutgrabber.h settings/globalshortcutssettingspage.h HEADERS globalshortcuts/globalshortcutsmanager.h globalshortcuts/globalshortcutsbackend.h globalshortcuts/globalshortcutgrabber.h settings/globalshortcutssettingspage.h
UI globalshortcuts/globalshortcutgrabber.ui settings/globalshortcutssettingspage.ui 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 optional_source(HAVE_DBUS
SOURCES globalshortcuts/globalshortcutsbackend-gsd.cpp globalshortcuts/globalshortcutsbackend-kde.cpp SOURCES globalshortcuts/globalshortcutsbackend-kde.cpp globalshortcuts/globalshortcutsbackend-gnome.cpp globalshortcuts/globalshortcutsbackend-mate.cpp
HEADERS globalshortcuts/globalshortcutsbackend-gsd.h globalshortcuts/globalshortcutsbackend-kde.h 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 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) endif(HAVE_GLOBALSHORTCUTS)
# ALSA # ALSA
optional_source(HAVE_ALSA optional_source(HAVE_ALSA SOURCES engine/alsadevicefinder.cpp engine/alsapcmdevicefinder.cpp)
SOURCES
engine/alsadevicefinder.cpp
)
# DBUS # DBUS
optional_source(HAVE_DBUS optional_source(HAVE_DBUS SOURCES osd/osddbus.cpp HEADERS osd/osddbus.h)
SOURCES
osd/osddbus.cpp
HEADERS
osd/osddbus.h
)
# GStreamer # GStreamer
optional_source(HAVE_GSTREAMER optional_source(HAVE_GSTREAMER
SOURCES engine/gststartup.cpp engine/gstengine.cpp engine/gstenginepipeline.cpp engine/gstelementdeleter.cpp SOURCES engine/gststartup.cpp engine/gstengine.cpp engine/gstenginepipeline.cpp
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h
) )
# VLC # VLC
optional_source(HAVE_VLC optional_source(HAVE_VLC SOURCES engine/vlcengine.cpp HEADERS engine/vlcengine.h)
SOURCES engine/vlcengine.cpp
HEADERS engine/vlcengine.h
)
# DBUS and MPRIS - Unix specific # DBUS and MPRIS - Unix specific
if(UNIX AND HAVE_DBUS) if(UNIX AND HAVE_DBUS)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus)
optional_source(HAVE_DBUS optional_source(HAVE_DBUS SOURCES core/mpris2.cpp HEADERS core/mpris2.h)
SOURCES core/mpris.cpp core/mpris2.cpp optional_source(HAVE_UDISKS2 SOURCES device/udisks2lister.cpp HEADERS device/udisks2lister.h)
HEADERS core/mpris.h 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 # MPRIS 2.1 DBUS interfaces
qt6_add_dbus_adaptor(SOURCES qt_add_dbus_adaptor(SOURCES dbus/org.mpris.MediaPlayer2.Playlists.xml core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
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 # org.freedesktop.Notifications DBUS interface
qt6_add_dbus_adaptor(SOURCES qt_add_dbus_interface(SOURCES dbus/org.freedesktop.Notifications.xml dbus/notification)
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# org.freedesktop.Notifications DBUS interface # org.gnome.SettingsDaemon interface
qt6_add_dbus_interface(SOURCES qt_add_dbus_interface(SOURCES dbus/org.gnome.SettingsDaemon.MediaKeys.xml dbus/gnomesettingsdaemon)
dbus/org.freedesktop.Notifications.xml
dbus/notification)
# org.gnome.SettingsDaemon interface # org.mate.SettingsDaemon interface
qt6_add_dbus_interface(SOURCES qt_add_dbus_interface(SOURCES dbus/org.mate.SettingsDaemon.MediaKeys.xml dbus/matesettingsdaemon)
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
# org.kde.KGlobalAccel interface # org.kde.KGlobalAccel interface
qt6_add_dbus_interface(SOURCES qt_add_dbus_interface(SOURCES dbus/org.kde.KGlobalAccel.xml dbus/kglobalaccel)
dbus/org.kde.KGlobalAccel.xml qt_add_dbus_interface(SOURCES dbus/org.kde.KGlobalAccel.Component.xml dbus/kglobalaccelcomponent)
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.freedesktop.Avahi.Server interface # org.freedesktop.Avahi.Server interface
add_custom_command( add_custom_command(
@@ -712,50 +664,17 @@ if(UNIX AND HAVE_DBUS)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp) list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp)
if(HAVE_UDISKS2) if(HAVE_UDISKS2)
set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml PROPERTIES NO_NAMESPACE dbus/objectmanager INCLUDE dbus/metatypes.h)
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.Filesystem.xml set_source_files_properties(dbus/org.freedesktop.UDisks2.Block.xml PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h)
PROPERTIES NO_NAMESPACE dbus/udisks2filesystem 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.Block.xml set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h)
PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h) qt_add_dbus_interface(SOURCES dbus/org.freedesktop.DBus.ObjectManager.xml dbus/objectmanager)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Drive.xml qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Filesystem.xml dbus/udisks2filesystem)
PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h) qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Block.xml dbus/udisks2block)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Drive.xml dbus/udisks2drive)
PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h) qt_add_dbus_interface(SOURCES dbus/org.freedesktop.UDisks2.Job.xml dbus/udisks2job)
if(BUILD_WITH_QT6) endif(HAVE_UDISKS2)
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)
endif(UNIX AND HAVE_DBUS) endif(UNIX AND HAVE_DBUS)
@@ -797,29 +716,24 @@ optional_source(HAVE_LIBGPOD
) )
# GIO device backend # GIO device backend
optional_source(HAVE_GIO optional_source(HAVE_GIO SOURCES device/giolister.cpp HEADERS device/giolister.h)
SOURCES device/giolister.cpp
HEADERS device/giolister.h
)
# mtp device # MTP device
optional_source(HAVE_LIBMTP optional_source(HAVE_LIBMTP
SOURCES SOURCES
device/mtpconnection.cpp device/mtpconnection.cpp
device/mtpdevice.cpp device/mtpdevice.cpp
device/mtploader.cpp device/mtploader.cpp
HEADERS HEADERS
device/mtpconnection.h
device/mtpdevice.h device/mtpdevice.h
device/mtploader.h device/mtploader.h
) )
# Pulse audio integration # Pulse audio integration
optional_source(HAVE_LIBPULSE optional_source(HAVE_LIBPULSE SOURCES engine/pulsedevicefinder.cpp)
SOURCES
engine/pulsedevicefinder.cpp
)
# MusicBrainz and transcoder require GStreamer # Transcoder require GStreamer
optional_source(HAVE_GSTREAMER optional_source(HAVE_GSTREAMER
SOURCES SOURCES
transcoder/transcoder.cpp transcoder/transcoder.cpp
@@ -838,6 +752,14 @@ HEADERS
transcoder/transcoder.h transcoder/transcoder.h
transcoder/transcodedialog.h transcoder/transcodedialog.h
transcoder/transcoderoptionsdialog.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 transcoder/transcoderoptionsmp3.h
settings/transcodersettingspage.h settings/transcodersettingspage.h
UI UI
@@ -855,30 +777,39 @@ UI
settings/transcodersettingspage.ui settings/transcodersettingspage.ui
) )
# CDIO backend and device # CHROMAPRINT
if(HAVE_GSTREAMER) if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
optional_source(HAVE_CHROMAPRINT optional_source(CHROMAPRINT_FOUND SOURCES engine/chromaprinter.cpp)
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
)
endif() 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 # Platform specific - macOS
optional_source(APPLE optional_source(APPLE
SOURCES SOURCES
core/scoped_nsautorelease_pool.mm
core/mac_utilities.mm core/mac_utilities.mm
core/mac_startup.mm core/mac_startup.mm
core/macsystemtrayicon.mm core/macsystemtrayicon.mm
@@ -888,22 +819,15 @@ optional_source(APPLE
engine/macosdevicefinder.cpp engine/macosdevicefinder.cpp
globalshortcuts/globalshortcutsbackend-macos.mm globalshortcuts/globalshortcutsbackend-macos.mm
globalshortcuts/globalshortcutgrabber.mm globalshortcuts/globalshortcutgrabber.mm
device/macosdevicelister.mm
HEADERS HEADERS
core/macsystemtrayicon.h core/macsystemtrayicon.h
core/macfslistener.h core/macfslistener.h
osd/osdmac.h osd/osdmac.h
globalshortcuts/globalshortcutsbackend-macos.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 # Platform specific - Windows
optional_source(WIN32 optional_source(WIN32
SOURCES 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}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
if(BUILD_WITH_QT6) qt_wrap_cpp(MOC ${HEADERS})
qt6_wrap_cpp(MOC ${HEADERS}) qt_wrap_ui(UIC ${UI})
qt6_wrap_ui(UIC ${UI}) qt_add_resources(QRC ${RESOURCES})
qt6_add_resources(QRC ${RESOURCES})
else()
qt5_wrap_cpp(MOC ${HEADERS})
qt5_wrap_ui(UIC ${UI})
qt5_add_resources(QRC ${RESOURCES})
endif()
if(HAVE_TRANSLATIONS) if(HAVE_TRANSLATIONS)
set(LINGUAS "All" CACHE STRING "A space-seperated list of translations to compile in to Strawberry, or \"None\".") 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 # build LANGUAGES from all existing .po files
file(GLOB pofiles translations/*.po) file(GLOB pofiles translations/*.po)
foreach(pofile ${pofiles}) foreach(pofile ${pofiles})
get_filename_component(lang ${pofile} NAME_WE) get_filename_component(lang ${pofile} NAME_WE)
list(APPEND LANGUAGES ${lang}) list(APPEND LANGUAGES ${lang})
endforeach(pofile) endforeach(pofile)
else (LINGUAS STREQUAL "All") else(LINGUAS STREQUAL "All")
if (NOT LINGUAS OR LINGUAS STREQUAL "None") if(NOT LINGUAS OR LINGUAS STREQUAL "None")
set (LANGUAGES "") set(LANGUAGES "")
else (NOT LINGUAS OR LINGUAS STREQUAL "None") else(NOT LINGUAS OR LINGUAS STREQUAL "None")
string(REGEX MATCHALL [a-zA-Z_@]+ LANGUAGES ${LINGUAS}) string(REGEX MATCHALL [a-zA-Z_@]+ LANGUAGES ${LINGUAS})
endif (NOT LINGUAS OR LINGUAS STREQUAL "None") endif(NOT LINGUAS OR LINGUAS STREQUAL "None")
endif (LINGUAS STREQUAL "All") endif(LINGUAS STREQUAL "All")
add_pot(POT add_pot(POT
${CMAKE_CURRENT_SOURCE_DIR}/translations/header ${CMAKE_CURRENT_SOURCE_DIR}/translations/header
@@ -1051,7 +969,6 @@ link_directories(
${GOBJECT_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS}
${GNUTLS_LIBRARY_DIRS} ${GNUTLS_LIBRARY_DIRS}
${SQLITE_LIBRARY_DIRS} ${SQLITE_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
${SINGLEAPPLICATION_LIBRARY_DIRS} ${SINGLEAPPLICATION_LIBRARY_DIRS}
${SINGLECOREAPPLICATION_LIBRARY_DIRS} ${SINGLECOREAPPLICATION_LIBRARY_DIRS}
${QTSPARKLE_LIBRARY_DIRS} ${QTSPARKLE_LIBRARY_DIRS}
@@ -1060,7 +977,7 @@ link_directories(
if(HAVE_ALSA) if(HAVE_ALSA)
link_directories(${ALSA_LIBRARY_DIRS}) link_directories(${ALSA_LIBRARY_DIRS})
endif(HAVE_ALSA) endif()
if(HAVE_LIBPULSE) if(HAVE_LIBPULSE)
link_directories(${LIBPULSE_LIBRARY_DIRS}) link_directories(${LIBPULSE_LIBRARY_DIRS})
@@ -1075,40 +992,47 @@ if(HAVE_GSTREAMER)
${GSTREAMER_TAG_LIBRARY_DIRS} ${GSTREAMER_TAG_LIBRARY_DIRS}
${GSTREAMER_PBUTILS_LIBRARY_DIRS} ${GSTREAMER_PBUTILS_LIBRARY_DIRS}
) )
endif(HAVE_GSTREAMER) endif()
if(HAVE_VLC) if(HAVE_VLC)
link_directories(${LIBVLC_LIBRARY_DIRS}) link_directories(${LIBVLC_LIBRARY_DIRS})
endif() endif()
if(HAVE_CHROMAPRINT) if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
link_directories(${CHROMAPRINT_LIBRARY_DIRS}) link_directories(${CHROMAPRINT_LIBRARY_DIRS})
endif(HAVE_CHROMAPRINT) endif()
if(X11_FOUND) if(X11_FOUND)
link_directories(${X11_LIBRARY_DIRS}) link_directories(${X11_LIBRARY_DIRS})
endif(X11_FOUND) endif()
if(XCB_FOUND) if(XCB_FOUND)
link_directories(${XCB_LIBRARY_DIRS}) link_directories(${XCB_LIBRARY_DIRS})
endif(XCB_FOUND) endif()
if(HAVE_GIO) if(HAVE_GIO)
link_directories(${GIO_LIBRARY_DIRS}) link_directories(${GIO_LIBRARY_DIRS})
endif(HAVE_GIO) endif()
if(HAVE_AUDIOCD) if(HAVE_AUDIOCD)
link_directories(${LIBCDIO_LIBRARY_DIRS}) link_directories(${LIBCDIO_LIBRARY_DIRS})
endif(HAVE_AUDIOCD) endif()
if(HAVE_LIBGPOD) if(HAVE_LIBGPOD)
link_directories(${LIBGPOD_LIBRARY_DIRS}) link_directories(${LIBGPOD_LIBRARY_DIRS} ${GDK_PIXBUF_LIBRARY_DIRS})
link_directories(${GDK_PIXBUF_LIBRARY_DIRS}) endif()
endif(HAVE_LIBGPOD)
if(HAVE_LIBMTP) if(HAVE_LIBMTP)
link_directories(${LIBMTP_LIBRARY_DIRS}) 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 add_library(strawberry_lib STATIC
${SOURCES} ${SOURCES}
@@ -1128,6 +1052,10 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
${SQLITE_INCLUDE_DIRS} ${SQLITE_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 target_include_directories(strawberry_lib PUBLIC
${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}
@@ -1138,7 +1066,6 @@ target_include_directories(strawberry_lib PUBLIC
${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader ${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader
${SINGLEAPPLICATION_INCLUDE_DIRS} ${SINGLEAPPLICATION_INCLUDE_DIRS}
${SINGLECOREAPPLICATION_INCLUDE_DIRS} ${SINGLECOREAPPLICATION_INCLUDE_DIRS}
${TAGLIB_INCLUDE_DIRS}
) )
target_link_libraries(strawberry_lib PUBLIC target_link_libraries(strawberry_lib PUBLIC
@@ -1147,12 +1074,11 @@ target_link_libraries(strawberry_lib PUBLIC
${GOBJECT_LIBRARIES} ${GOBJECT_LIBRARIES}
${GNUTLS_LIBRARIES} ${GNUTLS_LIBRARIES}
${SQLITE_LIBRARIES} ${SQLITE_LIBRARIES}
${TAGLIB_LIBRARIES}
${QT_LIBRARIES} ${QT_LIBRARIES}
${SINGLEAPPLICATION_LIBRARIES} ${SINGLEAPPLICATION_LIBRARIES}
${SINGLECOREAPPLICATION_LIBRARIES} ${SINGLECOREAPPLICATION_LIBRARIES}
${QTSPARKLE_LIBRARIES} ${QTSPARKLE_LIBRARIES}
${Iconv_LIBRARY} ${Iconv_LIBRARIES}
libstrawberry-common libstrawberry-common
libstrawberry-tagreader libstrawberry-tagreader
) )
@@ -1160,7 +1086,7 @@ target_link_libraries(strawberry_lib PUBLIC
if(HAVE_ALSA) if(HAVE_ALSA)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${ALSA_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${ALSA_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${ALSA_LIBRARIES})
endif(HAVE_ALSA) endif()
if(HAVE_LIBPULSE) if(HAVE_LIBPULSE)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBPULSE_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBPULSE_INCLUDE_DIRS})
@@ -1184,7 +1110,7 @@ if(HAVE_GSTREAMER)
${GSTREAMER_TAG_LIBRARIES} ${GSTREAMER_TAG_LIBRARIES}
${GSTREAMER_PBUTILS_LIBRARIES} ${GSTREAMER_PBUTILS_LIBRARIES}
) )
endif(HAVE_GSTREAMER) endif()
if(HAVE_MOODBAR) if(HAVE_MOODBAR)
target_link_libraries(strawberry_lib PRIVATE gstmoodbar) target_link_libraries(strawberry_lib PRIVATE gstmoodbar)
@@ -1195,41 +1121,44 @@ if(HAVE_VLC)
target_link_libraries(strawberry_lib PRIVATE ${LIBVLC_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${LIBVLC_LIBRARIES})
endif() endif()
if(HAVE_CHROMAPRINT) if(HAVE_SONGFINGERPRINTING OR HAVE_MUSICBRAINZ)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${CHROMAPRINT_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${CHROMAPRINT_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${CHROMAPRINT_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${CHROMAPRINT_LIBRARIES})
endif(HAVE_CHROMAPRINT) endif()
if(X11_FOUND) if(X11_FOUND)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${X11_INCLUDE_DIR}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${X11_INCLUDE_DIR})
target_link_libraries(strawberry_lib PRIVATE ${X11_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${X11_LIBRARIES})
endif(X11_FOUND) endif()
if(XCB_FOUND) if(XCB_FOUND)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${XCB_INCLUDE_DIR}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${XCB_INCLUDE_DIR})
target_link_libraries(strawberry_lib PRIVATE ${XCB_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${XCB_LIBRARIES})
endif(XCB_FOUND) endif()
if(HAVE_GIO) if(HAVE_GIO)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${GIO_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${GIO_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${GIO_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${GIO_LIBRARIES})
endif(HAVE_GIO) endif()
if(HAVE_AUDIOCD) if(HAVE_AUDIOCD)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBCDIO_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBCDIO_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBCDIO_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${LIBCDIO_LIBRARIES})
endif(HAVE_AUDIOCD) endif()
if(HAVE_LIBGPOD) if(HAVE_LIBGPOD)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBGPOD_INCLUDE_DIRS} ${GDK_PIXBUF_INCLUDE_DIRS}) 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}) target_link_libraries(strawberry_lib PRIVATE ${LIBGPOD_LIBRARIES} ${GDK_PIXBUF_LIBRARIES})
endif(HAVE_LIBGPOD) endif()
if(HAVE_LIBMTP) if(HAVE_LIBMTP)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBMTP_INCLUDE_DIRS}) target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBMTP_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBMTP_LIBRARIES}) target_link_libraries(strawberry_lib PRIVATE ${LIBMTP_LIBRARIES})
endif(HAVE_LIBMTP) endif()
if(FREEBSD)
target_link_libraries(strawberry_lib PRIVATE iconv)
endif()
if(APPLE) if(APPLE)
target_link_libraries(strawberry_lib PRIVATE target_link_libraries(strawberry_lib PRIVATE
@@ -1245,24 +1174,17 @@ if(APPLE)
if(HAVE_SPARKLE) if(HAVE_SPARKLE)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${SPARKLE}/Headers) target_include_directories(strawberry_lib SYSTEM PRIVATE ${SPARKLE}/Headers)
target_link_libraries(strawberry_lib PRIVATE ${SPARKLE}) 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()
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 ..) set(EXECUTABLE_OUTPUT_PATH ..)
@@ -1270,13 +1192,13 @@ set(EXECUTABLE_OUTPUT_PATH ..)
# Show the console window in debug mode on Windows # Show the console window in debug mode on Windows
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE)
set(STRAWBERRY-WIN32-FLAG WIN32) set(STRAWBERRY-WIN32-FLAG WIN32)
endif(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT ENABLE_WIN32_CONSOLE) endif()
# Resource file for windows # Resource file for windows
if(WIN32) if(WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../dist/windows/windres.rc.in ${CMAKE_CURRENT_BINARY_DIR}/windres.rc) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../dist/windows/windres.rc.in ${CMAKE_CURRENT_BINARY_DIR}/windres.rc)
set(STRAWBERRY-WIN32-RESOURCES windres.rc) set(STRAWBERRY-WIN32-RESOURCES windres.rc)
endif(WIN32) endif()
add_executable(strawberry add_executable(strawberry
MACOSX_BUNDLE MACOSX_BUNDLE
@@ -1304,4 +1226,4 @@ endif()
if(APPLE) if(APPLE)
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist") 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>; template class Analyzer::Base<QWidget>;
#endif #endif
Analyzer::Base::Base(QWidget *parent, uint scopeSize) Analyzer::Base::Base(QWidget *parent, const uint scopeSize)
: QWidget(parent), : QWidget(parent),
timeout_(40), timeout_(40),
fht_(new FHT(scopeSize)), 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::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) { void Analyzer::Base::transform(Scope &scope) {
QVector<float> aux(fht_->size()); 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()); std::copy(scope.begin(), scope.end(), aux.begin());
} }
else { else {
@@ -93,12 +93,12 @@ void Analyzer::Base::paintEvent(QPaintEvent *e) {
switch (engine_->state()) { switch (engine_->state()) {
case Engine::Playing: { case Engine::Playing: {
const Engine::Scope& thescope = engine_->scope(timeout_); const Engine::Scope &thescope = engine_->scope(timeout_);
int i = 0; int i = 0;
// convert to mono here - our built in analyzers need mono, but the engines provide interleaved pcm // 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) { 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; i += 2;
} }
@@ -126,10 +126,12 @@ void Analyzer::Base::paintEvent(QPaintEvent *e) {
int Analyzer::Base::resizeExponent(int exp) { int Analyzer::Base::resizeExponent(int exp) {
if (exp < 3) if (exp < 3) {
exp = 3; exp = 3;
else if (exp > 9) }
else if (exp > 9) {
exp = 9; exp = 9;
}
if (exp != fht_->sizeExp()) { if (exp != fht_->sizeExp()) {
delete fht_; 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; int exp = 0;
if (bands <= 8) if (bands <= 8) {
exp = 4; exp = 4;
else if (bands <= 16) }
else if (bands <= 16) {
exp = 5; exp = 5;
else if (bands <= 32) }
else if (bands <= 32) {
exp = 6; exp = 6;
else if (bands <= 64) }
else if (bands <= 64) {
exp = 7; exp = 7;
else if (bands <= 128) }
else if (bands <= 128) {
exp = 8; exp = 8;
else }
else {
exp = 9; exp = 9;
}
resizeExponent(exp); resizeExponent(exp);
return fht_->size() / 2; 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 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) { if (t < 201) {
Scope s(32); Scope s(32);
const double dt = double(t) / 200; const double dt = static_cast<double>(t) / 200;
for (uint i = 0; i < s.size(); ++i) for (uint i = 0; i < s.size(); ++i) {
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0); s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
}
analyze(p, s, new_frame_); analyze(p, s, new_frame_);
} }
else else {
analyze(p, Scope(32, 0), new_frame_); analyze(p, Scope(32, 0), new_frame_);
}
++t; ++t;
@@ -185,7 +197,7 @@ void Analyzer::Base::polishEvent() {
init(); init();
} }
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) { void Analyzer::interpolate(const Scope &inVec, Scope &outVec) {
double pos = 0.0; double pos = 0.0;
const double step = static_cast<double>(inVec.size()) / outVec.size(); 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; 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; 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; 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 step = (M_PI * 2) / size;
double radian = 0; double radian = 0;
@@ -222,7 +238,9 @@ void Analyzer::initSin(Scope& v, const uint size) {
void Analyzer::Base::timerEvent(QTimerEvent *e) { void Analyzer::Base::timerEvent(QTimerEvent *e) {
QWidget::timerEvent(e); QWidget::timerEvent(e);
if (e->timerId() != timer_.timerId()) return; if (e->timerId() != timer_.timerId()) {
return;
}
new_frame_ = true; new_frame_ = true;
update(); update();

View File

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

View File

@@ -19,6 +19,8 @@
#include "config.h" #include "config.h"
#include <chrono>
#include <QObject> #include <QObject>
#include <QWidget> #include <QWidget>
#include <QVariant> #include <QVariant>
@@ -44,6 +46,8 @@
#include "engine/enginebase.h" #include "engine/enginebase.h"
#include "engine/enginetype.h" #include "engine/enginetype.h"
using namespace std::chrono_literals;
const char *AnalyzerContainer::kSettingsGroup = "Analyzer"; const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
const char *AnalyzerContainer::kSettingsFramerate = "framerate"; const char *AnalyzerContainer::kSettingsFramerate = "framerate";
@@ -90,7 +94,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
context_menu_->addSeparator(); context_menu_->addSeparator();
double_click_timer_->setSingleShot(true); 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); QObject::connect(double_click_timer_, &QTimer::timeout, this, &AnalyzerContainer::ShowPopupMenu);
Load(); Load();
@@ -99,7 +103,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) { 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 (e->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@@ -120,8 +126,10 @@ void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
} }
void AnalyzerContainer::SetEngine(EngineBase *engine) { void AnalyzerContainer::SetEngine(EngineBase *engine) {
if (current_analyzer_) current_analyzer_->set_engine(engine); if (current_analyzer_) current_analyzer_->set_engine(engine);
engine_ = engine; engine_ = engine;
} }
void AnalyzerContainer::DisableAnalyzer() { 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); QAction *action = context_menu_framerate_->addAction(name);
group_framerate_->addAction(action); group_framerate_->addAction(action);
framerate_list_ << framerate; framerate_list_ << framerate;
action->setCheckable(true); 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); void SaveFramerate(const int framerate);
template <typename T> template <typename T>
void AddAnalyzerType(); void AddAnalyzerType();
void AddFramerate(const QString& name, const int framerate); void AddFramerate(const QString &name, const int framerate);
private: private:
int current_framerate_; // fps int current_framerate_; // fps
@@ -94,7 +94,7 @@ class AnalyzerContainer : public QWidget {
QPoint last_click_pos_; QPoint last_click_pos_;
bool ignore_next_click_; bool ignore_next_click_;
Analyzer::Base* current_analyzer_; Analyzer::Base *current_analyzer_;
EngineBase *engine_; EngineBase *engine_;
}; };

View File

@@ -24,6 +24,7 @@
#include "blockanalyzer.h" #include "blockanalyzer.h"
#include <cstdlib> #include <cstdlib>
#include <algorithm>
#include <cmath> #include <cmath>
#include <QWidget> #include <QWidget>
@@ -37,8 +38,8 @@
const int BlockAnalyzer::kHeight = 2; const int BlockAnalyzer::kHeight = 2;
const int BlockAnalyzer::kWidth = 4; const int BlockAnalyzer::kWidth = 4;
const int BlockAnalyzer::kMinRows = 3; // arbituary const int BlockAnalyzer::kMinRows = 3; // arbitrary
const int BlockAnalyzer::kMinColumns = 32; // arbituary const int BlockAnalyzer::kMinColumns = 32; // arbitrary
const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const int BlockAnalyzer::kFadeSize = 90; const int BlockAnalyzer::kFadeSize = 90;
@@ -56,14 +57,13 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
fade_bars_(kFadeSize), fade_bars_(kFadeSize),
fade_pos_(1 << 8, 50), fade_pos_(1 << 8, 50),
fade_intensity_(1 << 8, 32), 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 setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1); setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
// mxcl says null pixmaps cause crashes, so let's play it safe // 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().. // all is explained in analyze()..
// +1 to counter -1 in maxSizes, trust me we need this! // +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); 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 // this is the y-offset for drawing from the top of the widget
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2; y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
@@ -89,15 +89,15 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
if (rows_ != oldRows) { if (rows_ != oldRows) {
barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1)); barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
for (uint i = 0; i < kFadeSize; ++i) std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(kWidth, rows_ * (kHeight + 1)));
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
yscale_.resize(rows_ + 1); yscale_.resize(rows_ + 1);
const int PRE = 1, PRO = 1; // PRE and PRO allow us to restrict the range somewhat 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_[z] = 1 - (log10(PRE + z) / log10(PRE + rows_ + PRO));
}
yscale_[rows_] = 0; yscale_[rows_] = 0;
@@ -117,7 +117,7 @@ void BlockAnalyzer::determineStep() {
// the fall time of 30 is too slow on framerates above 50fps // the fall time of 30 is too slow on framerates above 50fps
const double fallTime = static_cast<double>(timeout() < 20 ? 20 * rows_ : 30 * rows_); const double fallTime = static_cast<double>(timeout() < 20 ? 20 * rows_ : 30 * rows_);
step_ = double(rows_ * timeout()) / fallTime; step_ = static_cast<double>(rows_ * timeout()) / fallTime;
} }
@@ -164,15 +164,17 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
// Paint the background // Paint the background
canvas_painter.drawPixmap(0, 0, background_); canvas_painter.drawPixmap(0, 0, background_);
for (int y, x = 0; x < static_cast<int>(scope_.size()); ++x) { for (int x = 0, y = 0; x < static_cast<int>(scope_.size()); ++x) {
// determine y // determine y
for (y = 0; scope_[x] < yscale_[y]; ++y) continue; 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) // 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_); y = static_cast<int>(store_[x] += step_);
else }
else {
store_[x] = y; store_[x] = y;
}
// If y is lower than fade_pos_, then the bar has exceeded the height of the fadeout // 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 // 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()); 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_); canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
}
p.drawPixmap(0, 0, canvas_); 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 // with a range of 0-255 and maximum adjustment of amount, maximise the difference between f and b
@@ -242,18 +245,20 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
public: public:
explicit OutputOnExit(const QColor &color) : c(color) {} explicit OutputOnExit(const QColor &color) : c(color) {}
~OutputOnExit() { ~OutputOnExit() {
int h, s, v; int h = 0, s = 0, v = 0;
c.getHsv(&h, &s, &v); c.getHsv(&h, &s, &v);
} }
private: private:
const QColor &c; const QColor &c;
Q_DISABLE_COPY(OutputOnExit)
}; };
OutputOnExit allocateOnTheStack(fg); OutputOnExit allocateOnTheStack(fg);
int bh, bs, bv; int bh = 0, bs = 0, bv = 0;
int fh, fs, fv; int fh = 0, fs = 0, fv = 0;
bg.getHsv(&bh, &bs, &bv); bg.getHsv(&bh, &bs, &bv);
fg.getHsv(&fh, &fs, &fv); fg.getHsv(&fh, &fs, &fv);
@@ -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 // check the saturation for the two colours is sufficient that hue alone can
// provide sufficient contrast // provide sufficient contrast
if (ds > amount / 2 && (bs > 125 && fs > 125)) if (ds > amount / 2 && (bs > 125 && fs > 125)) {
return fg; return fg;
else if (dv > amount / 2 && (bv > 125 && fv > 125)) }
else if (dv > amount / 2 && (bv > 125 && fv > 125)) {
return fg; return fg;
}
} }
if (fs < 50 && ds < 40) { if (fs < 50 && ds < 40) {
// low saturation on a low saturation is sad // low saturation on a low saturation is sad
const int tmp = 50 - fs; const int tmp = 50 - fs;
fs = 50; fs = 50;
if (static_cast<int>(amount) > tmp) if (static_cast<int>(amount) > tmp) {
amount -= tmp; amount -= tmp;
else }
else {
amount = 0; amount = 0;
}
} }
// test that there is available value to honor our contrast requirement // 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); 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)); 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); 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); 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 QColor::fromHsv(fh, fs, bv + amount);
}
return Qt::blue; return Qt::blue;
} }
void BlockAnalyzer::paletteChange(const QPalette&) { void BlockAnalyzer::paletteChange(const QPalette&) {
@@ -338,16 +352,17 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
bar()->fill(bg); bar()->fill(bg);
QPainter p(bar()); QPainter p(bar());
for (int y = 0; y < rows_; ++y) for (int y = 0; y < rows_; ++y) {
// graduate the fg color // 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))); 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); const QColor bg2 = palette().color(QPalette::Window).darker(112);
// make a complimentary fadebar colour // make a complimentary fadebar colour
// TODO dark is not always correct, dumbo! // TODO dark is not always correct, dumbo!
int h, s, v; int h = 0, s = 0, v = 0;
palette().color(QPalette::Window).darker(150).getHsv(&h, &s, &v); palette().color(QPalette::Window).darker(150).getHsv(&h, &s, &v);
const QColor fg2(QColor::fromHsv(h + 120, s, v)); const QColor fg2(QColor::fromHsv(h + 120, s, v));
@@ -357,7 +372,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
const int r2 = bg2.red(), g2 = bg2.green(), b2 = bg2.blue(); const int r2 = bg2.red(), g2 = bg2.green(), b2 = bg2.blue();
// Precalculate all fade-bar pixmaps // 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)); fade_bars_[y].fill(palette().color(QPalette::Window));
QPainter f(&fade_bars_[y]); QPainter f(&fade_bars_[y]);
for (int z = 0; z < rows_; ++z) { for (int z = 0; z < rows_; ++z) {
@@ -386,8 +401,10 @@ void BlockAnalyzer::drawBackground() {
if (!p.paintEngine()) return; if (!p.paintEngine()) return;
for (int x = 0; x < columns_; ++x) for (int x = 0; x < columns_; ++x) {
for (int y = 0; y < rows_; ++y) for (int y = 0; y < rows_; ++y) {
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark); 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::kMaxBandCount = 256;
const int BoomAnalyzer::kMinBandCount = 32; 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), : Analyzer::Base(parent, 9),
bands_(0), bands_(0),
scope_(kMinBandCount), scope_(kMinBandCount),
fg_(palette().color(QPalette::Highlight)), fg_(palette().color(QPalette::Highlight)),
K_barHeight_(1.271) // 1.471 K_barHeight_(1.271), // 1.471
, F_peakSpeed_(1.103), // 1.122
F_peakSpeed_(1.103) // 1.122
,
F_(1.0), F_(1.0),
bar_height_(kMaxBandCount, 0), bar_height_(kMaxBandCount, 0),
peak_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; 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; F_peakSpeed_ = static_cast<double>(newValue) / 1000;
} }
void BoomAnalyzer::resizeEvent(QResizeEvent* e) { void BoomAnalyzer::resizeEvent(QResizeEvent *e) {
QWidget::resizeEvent(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); bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
scope_.resize(bands_); 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); barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size()); 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_->spectrum(s.data());
fht_->scale(s.data(), 1.0 / 50); fht_->scale(s.data(), 1.0 / 50);
@@ -110,13 +108,13 @@ 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) { if (!new_frame || engine_->state() == Engine::Paused) {
p.drawPixmap(0, 0, canvas_); p.drawPixmap(0, 0, canvas_);
return; return;
} }
double h; double h = 0.0;
const uint MAX_HEIGHT = height() - 1; const uint MAX_HEIGHT = height() - 1;
QPainter canvas_painter(&canvas_); QPainter canvas_painter(&canvas_);
@@ -124,7 +122,7 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
Analyzer::interpolate(scope, scope_); Analyzer::interpolate(scope, scope_);
for (int i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) { for (int i = 0, x = 0, y = 0; i < bands_; ++i, x += kColumnWidth + 1) {
h = log10(scope_[i] * 256.0) * F_; h = log10(scope_[i] * 256.0) * F_;
if (h > MAX_HEIGHT) h = MAX_HEIGHT; if (h > MAX_HEIGHT) h = MAX_HEIGHT;
@@ -160,8 +158,9 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
y = height() - static_cast<int>(bar_height_[i]); y = height() - static_cast<int>(bar_height_[i]);
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1); canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
canvas_painter.setPen(fg_); canvas_painter.setPen(fg_);
if (bar_height_[i] > 0) if (bar_height_[i] > 0) {
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1); canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
}
y = height() - static_cast<int>(peak_height_[i]); y = height() - static_cast<int>(peak_height_[i]);
canvas_painter.setPen(palette().color(QPalette::Midlight)); canvas_painter.setPen(palette().color(QPalette::Midlight));

View File

@@ -42,10 +42,10 @@ class BoomAnalyzer : public Analyzer::Base {
public: public:
Q_INVOKABLE explicit BoomAnalyzer(QWidget*); Q_INVOKABLE explicit BoomAnalyzer(QWidget*);
static const char* kName; static const char *kName;
void transform(Analyzer::Scope &s) override; 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: public slots:
void changeK_barHeight(int); void changeK_barHeight(int);

View File

@@ -26,49 +26,54 @@
#include <cmath> #include <cmath>
#include <QVector> #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) { if (n > 3) {
buf_vector_.resize(num_); buf_vector_.resize(num_);
tab_vector_.resize(num_ * 2); tab_vector_.resize(num_ * 2);
makeCasTable(); makeCasTable();
} }
} }
FHT::~FHT() {} FHT::~FHT() = default;
int FHT::sizeExp() const { return exp2_; } int FHT::sizeExp() const { return exp2_; }
int FHT::size() const { return num_; } int FHT::size() const { return num_; }
float* FHT::buf_() { return buf_vector_.data(); } float *FHT::buf_() { return buf_vector_.data(); }
float* FHT::tab_() { return tab_vector_.data(); } float *FHT::tab_() { return tab_vector_.data(); }
int* FHT::log_() { return log_vector_.data(); } int *FHT::log_() { return log_vector_.data(); }
void FHT::makeCasTable(void) { 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++) { 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 = *sintab = cos(d);
costab += 2; costab += 2;
sintab += 2; sintab += 2;
if (sintab > tab_() + num_ * 2) sintab = tab_() + 1; 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; 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); 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, j, k, *r; int n = num_ / 2, i = 0, j = 0, k = 0, *r = nullptr;
if (log_vector_.size() < n) { if (log_vector_.size() < n) {
log_vector_.resize(n); log_vector_.resize(n);
float f = n / log10(static_cast<double>(n)); float f = n / log10(static_cast<double>(n));
@@ -93,50 +98,63 @@ void FHT::logSpectrum(float* out, float* p) {
} }
void FHT::semiLogSpectrum(float* p) { void FHT::semiLogSpectrum(float *p) {
power2(p); power2(p);
for (int i = 0; i < (num_ / 2); i++, p++) { for (int i = 0; i < (num_ / 2); i++, p++) {
float e = 10.0 * log10(sqrt(*p / 2)); float e = 10.0 * log10(sqrt(*p / 2));
*p = e < 0 ? 0 : e; *p = e < 0 ? 0 : e;
} }
} }
void FHT::spectrum(float* p) { void FHT::spectrum(float *p) {
power2(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)); *p = static_cast<float>(sqrt(*p / 2));
}
} }
void FHT::power(float* p) { void FHT::power(float *p) {
power2(p); power2(p);
for (int i = 0; i < (num_ / 2); i++) *p++ /= 2; for (int i = 0; i < (num_ / 2); i++) *p++ /= 2;
} }
void FHT::power2(float* p) { void FHT::power2(float *p) {
_transform(p, num_, 0); _transform(p, num_, 0);
*p = static_cast<float>(2 * pow(*p, 2)); *p = static_cast<float>(2 * pow(*p, 2));
p++; p++;
float* q = p + num_ - 2; float *q = p + num_ - 2;
for (int i = 1; i < (num_ / 2); i++) { for (int i = 1; i < (num_ / 2); i++) {
*p = static_cast<float>(pow(*p, 2) + pow(*q, 2)); *p = static_cast<float>(pow(*p, 2) + pow(*q, 2));
p++; p++;
q--; q--;
} }
} }
void FHT::transform(float* p) { void FHT::transform(float *p) {
if (num_ == 8)
if (num_ == 8) {
transform8(p); transform8(p);
else }
else {
_transform(p, num_, 0); _transform(p, num_, 0);
}
} }
void FHT::transform8(float* p) { void FHT::transform8(float *p) {
float a, b, c, d, e, f, g, h, b_f2, d_h2; 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, a_ce_g, ac_e_g, aceg, b_df_h, bdfh; 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;
a = *p++, b = *p++, c = *p++, d = *p++; a = *p++, b = *p++, c = *p++, d = *p++;
e = *p++, f = *p++, g = *p++, h = *p; e = *p++, f = *p++, g = *p++, h = *p;
@@ -162,15 +180,15 @@ 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) { if (n == 8) {
transform8(p + k); transform8(p + k);
return; return;
} }
int i, j, ndiv2 = n / 2; int i = 0, j = 0, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp; float a = 0.0, *t1 = nullptr, *t2 = nullptr, *t3 = nullptr, *t4 = nullptr, *ptab = nullptr, *pp = nullptr;
for (i = 0, t1 = buf_(), t2 = buf_() + ndiv2, pp = &p[k]; i < ndiv2; i++) for (i = 0, t1 = buf_(), t2 = buf_() + ndiv2, pp = &p[k]; i < ndiv2; i++)
*t1++ = *pp++, *t2++ = *pp++; *t1++ = *pp++, *t2++ = *pp++;

View File

@@ -41,9 +41,9 @@ class FHT {
QVector<float> tab_vector_; QVector<float> tab_vector_;
QVector<int> log_vector_; QVector<int> log_vector_;
float* buf_(); float *buf_();
float* tab_(); float *tab_();
int* log_(); int *log_();
/** /**
* Create a table of "cas" (cosine and sine) values. * 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. * should be at least 3. Values of more than 3 need a trigonometry table.
* @see makeCasTable() * @see makeCasTable()
*/ */
explicit FHT(int); explicit FHT(uint);
~FHT(); ~FHT();
int sizeExp() const; int sizeExp() const;
int size() const; int size() const;
void scale(float*, float); void scale(float*, float) const;
/** /**
* Exponentially Weighted Moving Average (EWMA) filter. * Exponentially Weighted Moving Average (EWMA) filter.
@@ -76,7 +76,7 @@ class FHT {
* @param s is fresh input. * @param s is fresh input.
* @param w is the weighting factor. * @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 * Logarithmic audio spectrum. Maps semi-logarithmic spectrum
@@ -85,7 +85,7 @@ class FHT {
* @param p is the input array. * @param p is the input array.
* @param out is the spectrum. * @param out is the spectrum.
*/ */
void logSpectrum(float* out, float* p); void logSpectrum(float *out, float *p);
/** /**
* Semi-logarithmic audio spectrum. * Semi-logarithmic audio spectrum.
@@ -117,7 +117,7 @@ class FHT {
/** /**
* Discrete Hartley transform of data sets with 8 values. * Discrete Hartley transform of data sets with 8 values.
*/ */
void transform8(float*); static void transform8(float*);
void transform(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::kRainbowOverlap[] = {13, 15};
const int Rainbow::RainbowAnalyzer::kSleepingHeight[] = {24, 33}; const int Rainbow::RainbowAnalyzer::kSleepingHeight[] = {24, 33};
const char* Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat"; const char *Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
const char* Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash"; const char *Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02f; const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02F;
Rainbow::RainbowAnalyzer::RainbowType Rainbow::RainbowAnalyzer::rainbowtype; 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), : Analyzer::Base(parent, 9),
timer_id_(startTimer(kFrameIntervalMs)), timer_id_(startTimer(kFrameIntervalMs)),
frame_(0), frame_(0),
@@ -64,8 +64,7 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
available_rainbow_width_(0), available_rainbow_width_(0),
px_per_frame_(0), px_per_frame_(0),
x_offset_(0), x_offset_(0),
background_brush_(QColor(0x0f, 0x43, 0x73)) background_brush_(QColor(0x0f, 0x43, 0x73)) {
{
rainbowtype = rbtype; rainbowtype = rbtype;
cat_dash_[0] = QPixmap(":/pictures/nyancat.png"); cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
@@ -73,7 +72,7 @@ Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* pa
memset(history_, 0, sizeof(history_)); memset(history_, 0, sizeof(history_));
for (int i = 0; i < kRainbowBands; ++i) { 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 // pow constants computed so that
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32 // | 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_) { if (e->timerId() == timer_id_) {
frame_ = (frame_ + 1) % kFrameCount[rainbowtype]; 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); 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 // Discard the second half of the transform
const int scope_size = static_cast<int>(s.size() / 2); 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())) { if ((new_frame && is_playing_) || (buffer_[0].isNull() && buffer_[1].isNull())) {
// Transform the music into rainbows! // Transform the music into rainbows!
for (int band = 0; band < kRainbowBands; ++band) { 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. // Move the history of each band across by 1 frame.
memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float)); 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. // Create polylines for the rainbows.
QPointF polyline[kRainbowBands * kHistorySize]; QPointF polyline[kRainbowBands * kHistorySize];
QPointF* dest = polyline; QPointF *dest = polyline;
float* source = history_; float *source = history_;
const float top_of = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight[rainbowtype]) / 2; const float top_of = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight[rainbowtype]) / 2;
for (int band = 0; band < kRainbowBands; ++band) { 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) {} : RainbowAnalyzer(Rainbow::RainbowAnalyzer::Nyancat, parent) {}
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent) Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget *parent)
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Dash, parent) {} : RainbowAnalyzer(Rainbow::RainbowAnalyzer::Dash, parent) {}

View File

@@ -47,10 +47,10 @@ class RainbowAnalyzer : public Analyzer::Base {
public: public:
enum RainbowType { enum RainbowType {
Nyancat = 0, Nyancat = 0,
Dash = 1 Dash = 1
}; };
RainbowAnalyzer(const RainbowType &rbtype, QWidget *parent); RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
protected: protected:
void transform(Analyzer::Scope&) override; void transform(Analyzer::Scope&) override;
@@ -93,7 +93,7 @@ class RainbowAnalyzer : public Analyzer::Base {
private: private:
// "constants" that get initialized in the constructor // "constants" that get initialized in the constructor
float band_scale_[kRainbowBands]; float band_scale_[kRainbowBands]{};
QPen colors_[kRainbowBands]; QPen colors_[kRainbowBands];
// Rainbow Nyancat & Dash // Rainbow Nyancat & Dash
@@ -104,9 +104,9 @@ class RainbowAnalyzer : public Analyzer::Base {
int frame_; int frame_;
// The y positions of each point on the rainbow. // 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. // so it can be used in the next frame.
QPixmap buffer_[2]; QPixmap buffer_[2];
int current_buffer_; int current_buffer_;
@@ -118,7 +118,7 @@ class RainbowAnalyzer : public Analyzer::Base {
// X spacing between each point in the polyline. // X spacing between each point in the polyline.
int px_per_frame_; 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_. // to make the rainbow extend from 0 to available_rainbow_width_.
int x_offset_; int x_offset_;
@@ -129,18 +129,18 @@ class NyanCatAnalyzer : public RainbowAnalyzer {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE explicit NyanCatAnalyzer(QWidget* parent); Q_INVOKABLE explicit NyanCatAnalyzer(QWidget *parent);
static const char* kName; static const char *kName;
}; };
class RainbowDashAnalyzer : public RainbowAnalyzer { class RainbowDashAnalyzer : public RainbowAnalyzer {
Q_OBJECT Q_OBJECT
public: 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" #include "scrobbler/lastfmimport.h"
const char *SCollection::kSongsTable = "songs"; const char *SCollection::kSongsTable = "songs";
const char *SCollection::kFtsTable = "songs_fts";
const char *SCollection::kDirsTable = "directories"; const char *SCollection::kDirsTable = "directories";
const char *SCollection::kSubdirsTable = "subdirectories"; const char *SCollection::kSubdirsTable = "subdirectories";
const char *SCollection::kFtsTable = "songs_fts";
SCollection::SCollection(Application *app, QObject *parent) SCollection::SCollection(Application *app, QObject *parent)
: QObject(parent), : QObject(parent),
@@ -62,7 +62,7 @@ SCollection::SCollection(Application *app, QObject *parent)
backend()->moveToThread(app->database()->thread()); backend()->moveToThread(app->database()->thread());
qLog(Debug) << backend_ << "moved to thread" << 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); model_ = new CollectionModel(backend_, app_, this);
@@ -78,7 +78,7 @@ SCollection::~SCollection() {
} }
if (watcher_thread_) { if (watcher_thread_) {
watcher_thread_->exit(); watcher_thread_->exit();
watcher_thread_->wait(5000 /* five seconds */); watcher_thread_->wait(5000);
} }
backend_->deleteLater(); backend_->deleteLater();
@@ -97,6 +97,7 @@ void SCollection::Init() {
watcher_->set_backend(backend_); watcher_->set_backend(backend_);
watcher_->set_task_manager(app_->task_manager()); 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::DirectoryDiscovered, watcher_, &CollectionWatcher::AddDirectory);
QObject::connect(backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory); QObject::connect(backend_, &CollectionBackend::DirectoryDeleted, watcher_, &CollectionWatcher::RemoveDirectory);
QObject::connect(watcher_, &CollectionWatcher::NewOrUpdatedSongs, backend_, &CollectionBackend::AddOrUpdateSongs); 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::SubdirsDiscovered, backend_, &CollectionBackend::AddOrUpdateSubdirs);
QObject::connect(watcher_, &CollectionWatcher::SubdirsMTimeUpdated, backend_, &CollectionBackend::AddOrUpdateSubdirs); QObject::connect(watcher_, &CollectionWatcher::SubdirsMTimeUpdated, backend_, &CollectionBackend::AddOrUpdateSubdirs);
QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, backend_, &CollectionBackend::CompilationsNeedUpdating); QObject::connect(watcher_, &CollectionWatcher::CompilationsNeedUpdating, backend_, &CollectionBackend::CompilationsNeedUpdating);
QObject::connect(watcher_, &CollectionWatcher::UpdateLastSeen, backend_, &CollectionBackend::UpdateLastSeen);
QObject::connect(app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, backend_, &CollectionBackend::UpdateLastPlayed); QObject::connect(app_->lastfm_import(), &LastFMImport::UpdateLastPlayed, backend_, &CollectionBackend::UpdateLastPlayed);
QObject::connect(app_->lastfm_import(), &LastFMImport::UpdatePlayCount, backend_, &CollectionBackend::UpdatePlayCount); QObject::connect(app_->lastfm_import(), &LastFMImport::UpdatePlayCount, backend_, &CollectionBackend::UpdatePlayCount);
@@ -132,7 +134,7 @@ void SCollection::Exit() {
void SCollection::ExitReceived() { void SCollection::ExitReceived() {
QObject *obj = qobject_cast<QObject*>(sender()); QObject *obj = sender();
QObject::disconnect(obj, nullptr, this, nullptr); QObject::disconnect(obj, nullptr, this, nullptr);
qLog(Debug) << obj << "successfully exited."; qLog(Debug) << obj << "successfully exited.";
wait_for_exit_.removeAll(obj); wait_for_exit_.removeAll(obj);
@@ -148,8 +150,8 @@ void SCollection::AbortScan() { watcher_->Stop(); }
void SCollection::Rescan(const SongList &songs) { void SCollection::Rescan(const SongList &songs) {
qLog(Debug) << "Rescan" << songs.size() << "songs"; qLog(Debug) << "Rescan" << songs.size() << "songs";
if (!songs.isEmpty()) watcher_->RescanTracksAsync(songs); if (!songs.isEmpty()) watcher_->RescanTracksAsync(songs);
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,8 @@
#include "config.h" #include "config.h"
#include <optional>
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QFileInfo> #include <QFileInfo>
@@ -35,10 +37,12 @@
#include <QSqlQuery> #include <QSqlQuery>
#include "core/song.h" #include "core/song.h"
#include "core/sqlquery.h"
#include "collectionquery.h" #include "collectionquery.h"
#include "directory.h" #include "directory.h"
class QThread; class QThread;
class TaskManager;
class Database; class Database;
class SmartPlaylistSearch; class SmartPlaylistSearch;
@@ -50,14 +54,14 @@ class CollectionBackendInterface : public QObject {
struct Album { struct Album {
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(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_artist(_album_artist),
album(_album), album(_album),
art_automatic(_art_automatic), art_automatic(_art_automatic),
art_manual(_art_manual), art_manual(_art_manual),
urls(_urls), urls(_urls),
filetype(_filetype), filetype(_filetype),
cue_path(_cue_path) {} cue_path(_cue_path) {}
QString album_artist; QString album_artist;
QString album; QString album;
@@ -71,6 +75,9 @@ class CollectionBackendInterface : public QObject {
typedef QList<Album> AlbumList; typedef QList<Album> AlbumList;
virtual QString songs_table() const = 0; virtual QString songs_table() const = 0;
virtual QString fts_table() const = 0;
virtual Database *db() const = 0;
// Get a list of directories in the collection. Emits DirectoriesDiscovered. // Get a list of directories in the collection. Emits DirectoriesDiscovered.
virtual void LoadDirectoriesAsync() = 0; virtual void LoadDirectoriesAsync() = 0;
@@ -80,6 +87,7 @@ class CollectionBackendInterface : public QObject {
virtual void UpdateTotalAlbumCountAsync() = 0; virtual void UpdateTotalAlbumCountAsync() = 0;
virtual SongList FindSongsInDirectory(const int id) = 0; virtual SongList FindSongsInDirectory(const int id) = 0;
virtual SongList SongsWithMissingFingerprint(const int id) = 0;
virtual SubdirectoryList SubdirsInDirectory(const int id) = 0; virtual SubdirectoryList SubdirsInDirectory(const int id) = 0;
virtual DirectoryList GetAllDirectories() = 0; virtual DirectoryList GetAllDirectories() = 0;
virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0; virtual void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) = 0;
@@ -103,16 +111,16 @@ class CollectionBackendInterface : public QObject {
virtual Song GetSongById(const int id) = 0; 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. // 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. // 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. // Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0; virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
virtual void AddDirectory(const QString &path) = 0; virtual void AddDirectory(const QString &path) = 0;
virtual void RemoveDirectory(const Directory &dir) = 0; virtual void RemoveDirectory(const Directory &dir) = 0;
virtual bool ExecQuery(CollectionQuery *q) = 0;
}; };
class CollectionBackend : public CollectionBackendInterface { class CollectionBackend : public CollectionBackendInterface {
@@ -122,14 +130,17 @@ class CollectionBackend : public CollectionBackendInterface {
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr); 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 Close();
void ExitAsync(); void ExitAsync();
Database *db() const { return db_; } void ReportErrors(const CollectionQuery &query);
Database *db() const override { return db_; }
QString songs_table() const override { return songs_table_; } QString songs_table() const override { return songs_table_; }
QString fts_table() const override { return fts_table_; }
QString dirs_table() const { return dirs_table_; } QString dirs_table() const { return dirs_table_; }
QString subdirs_table() const { return subdirs_table_; } QString subdirs_table() const { return subdirs_table_; }
@@ -141,6 +152,7 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateTotalAlbumCountAsync() override; void UpdateTotalAlbumCountAsync() override;
SongList FindSongsInDirectory(const int id) override; SongList FindSongsInDirectory(const int id) override;
SongList SongsWithMissingFingerprint(const int id) override;
SubdirectoryList SubdirsInDirectory(const int id) override; SubdirectoryList SubdirsInDirectory(const int id) override;
DirectoryList GetAllDirectories() override; DirectoryList GetAllDirectories() override;
void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override; void ChangeDirPath(const int id, const QString &old_path, const QString &new_path) override;
@@ -158,24 +170,24 @@ class CollectionBackend : public CollectionBackendInterface {
AlbumList GetCompilationAlbums(const QueryOptions &opt = QueryOptions()) override; AlbumList GetCompilationAlbums(const QueryOptions &opt = QueryOptions()) override;
AlbumList GetAlbumsByArtist(const QString &artist, 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; 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; Song GetSongById(const int id) override;
SongList GetSongsById(const QList<int> &ids); SongList GetSongsById(const QList<int> &ids);
SongList GetSongsById(const QStringList &ids); SongList GetSongsById(const QStringList &ids);
SongList GetSongsByForeignId(const QStringList &ids, const QString &table, const QString &column); 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; Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
void AddDirectory(const QString &path) override; void AddDirectory(const QString &path) override;
void RemoveDirectory(const Directory &dir) override; void RemoveDirectory(const Directory &dir) override;
bool ExecQuery(CollectionQuery *q) override; bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
SongList ExecCollectionQuery(CollectionQuery *query); bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
void IncrementPlayCountAsync(const int id); void IncrementPlayCountAsync(const int id);
void IncrementSkipCountAsync(const int id, const float progress); void IncrementSkipCountAsync(const int id, const float progress);
@@ -186,12 +198,15 @@ class CollectionBackend : public CollectionBackendInterface {
Song GetSongBySongId(const QString &song_id); Song GetSongBySongId(const QString &song_id);
SongList GetSongsBySongId(const QStringList &song_ids); SongList GetSongsBySongId(const QStringList &song_ids);
SongList GetAllSongs(); SongList GetSongsByFingerprint(const QString &fingerprint) override;
SongList FindSongs(const SmartPlaylistSearch &search);
SongList SmartPlaylistsGetAllSongs();
SongList SmartPlaylistsFindSongs(const SmartPlaylistSearch &search);
Song::Source Source() const; Song::Source Source() const;
void AddOrUpdateSongsAsync(const SongList &songs); void AddOrUpdateSongsAsync(const SongList &songs);
void UpdateSongsBySongIDAsync(const SongMap &new_songs);
void UpdateSongRatingAsync(const int id, const double rating); void UpdateSongRatingAsync(const int id, const double rating);
void UpdateSongsRatingAsync(const QList<int> &ids, const double rating); void UpdateSongsRatingAsync(const QList<int> &ids, const double rating);
@@ -203,6 +218,7 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateTotalArtistCount(); void UpdateTotalArtistCount();
void UpdateTotalAlbumCount(); void UpdateTotalAlbumCount();
void AddOrUpdateSongs(const SongList &songs); void AddOrUpdateSongs(const SongList &songs);
void UpdateSongsBySongID(const SongMap &new_songs);
void UpdateMTimesOnly(const SongList &songs); void UpdateMTimesOnly(const SongList &songs);
void DeleteSongs(const SongList &songs); void DeleteSongs(const SongList &songs);
void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true); void MarkSongsUnavailable(const SongList &songs, const bool unavailable = true);
@@ -214,7 +230,7 @@ class CollectionBackend : public CollectionBackendInterface {
void IncrementPlayCount(const int id); void IncrementPlayCount(const int id);
void IncrementSkipCount(const int id, const float progress); void IncrementSkipCount(const int id, const float progress);
void ResetStatistics(const int id); 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); 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); void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed);
@@ -223,6 +239,9 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateSongRating(const int id, const double rating); void UpdateSongRating(const int id, const double rating);
void UpdateSongsRating(const QList<int> &id_list, 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: signals:
void DirectoryDiscovered(Directory, SubdirectoryList); void DirectoryDiscovered(Directory, SubdirectoryList);
void DirectoryDeleted(Directory); void DirectoryDeleted(Directory);
@@ -240,6 +259,8 @@ class CollectionBackend : public CollectionBackendInterface {
void ExitFinished(); void ExitFinished();
void Error(QString);
private: private:
struct CompilationInfo { struct CompilationInfo {
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {} CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
@@ -251,7 +272,7 @@ class CollectionBackend : public CollectionBackendInterface {
int has_not_compilation_detected; 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 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()); AlbumList GetAlbums(const QString &artist, const bool compilation_required, const QueryOptions &opt = QueryOptions());
SubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db); SubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
@@ -264,6 +285,7 @@ class CollectionBackend : public CollectionBackendInterface {
private: private:
Database *db_; Database *db_;
TaskManager *task_manager_;
Song::Source source_; Song::Source source_;
QString songs_table_; QString songs_table_;
QString dirs_table_; QString dirs_table_;

View File

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

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