Compare commits

...

728 Commits

Author SHA1 Message Date
Jonas Kvinge
df0ec6b709 Release 1.2.2 2024-11-23 17:25:42 +01:00
Jonas Kvinge
376af26f0e Playlist: Move new QMimeData 2024-11-23 15:19:31 +01:00
Jonas Kvinge
9bff55e1ee PlaylistView: Ignore invalid QHeaderView::sectionResized
Workaround a possible Qt bug: moving a song in the playlist triggers `QHeaderView::sectionResized` with the state reset for each column, this causes an an invalid state for the last column since SetHeaderState is called before the last column state is restored, which again is used when restoring the playlist columns after switching to different playlist. To workaround this, only call SetHeaderState when the new column size is not zero.
2024-11-23 11:07:15 +01:00
Jonas Kvinge
8f7e29f503 PlaylistView: Use constants 2024-11-23 10:56:29 +01:00
Jonas Kvinge
c3aa885a0f SmartPlaylistSearchPreview: Remove early SetItemDelegates
It's called too early before MoodbarLoader is set, Init() already calls SetItemDelegates.

Fixes #1609
2024-11-22 17:04:42 +01:00
Jonas Kvinge
77ea5729c3 Turn on git revision 2024-11-21 18:34:29 +01:00
Jonas Kvinge
fac323a4a5 Release 1.2.1 2024-11-21 16:05:21 +01:00
Jonas Kvinge
a26066d70f Disconnect tagreader reply metaobject connection in lambda
Otherwise the tagreader reply is deleted to early causing crashes.
2024-11-21 15:59:07 +01:00
Strawberry Bot
d500d38e63 Update translations 2024-11-20 19:10:59 +01:00
Jonas Kvinge
295d4d9d05 Update strawberry_en_US.ts 2024-11-20 19:09:45 +01:00
Jonas Kvinge
01aaa0ba0b Disable SPMediaKeyTap
Workaround issue #1606
2024-11-20 18:12:20 +01:00
Jonas Kvinge
7b23118475 BehaviourSettingsPage: Disable song progress on taskbar for macOS 2024-11-19 06:52:23 +01:00
Jonas Kvinge
0c7806ab0a Turn on git revision 2024-11-17 07:34:18 +01:00
Jonas Kvinge
93c10fd77d Release 1.2.1-rc1 2024-11-16 06:08:33 +01:00
Jonas Kvinge
14e8cc511a Update .gitignore 2024-11-16 05:56:28 +01:00
Jonas Kvinge
7674bd1926 Update Changelog 2024-11-16 04:57:55 +01:00
Jonas Kvinge
be351f3118 Update Changelog 2024-11-16 04:56:02 +01:00
Jonas Kvinge
47aa0b05b6 Update Changelog 2024-11-16 04:53:26 +01:00
Jonas Kvinge
6d32cc9c4e nsi: Add back giognutls 2024-11-14 22:24:48 +01:00
Jonas Kvinge
fd9f4575eb CI: Copy all GIO modules for MSVC 2024-11-14 22:23:31 +01:00
Jonas Kvinge
c62fd2b58a GstEnginePipeline: Add more logging for fader 2024-11-14 22:05:06 +01:00
Jonas Kvinge
712db598f7 GstEnginePipeline: Fix setting volume after fader timeout 2024-11-14 22:04:53 +01:00
Jonas Kvinge
0cccc30c98 CI: Add Fedora 42 2024-11-14 18:57:13 +01:00
Jonas Kvinge
19e6ebe9c4 CI: Only copy gioopenssl for MSVC 2024-11-14 18:57:13 +01:00
Jonas Kvinge
24ed5413b2 nsi: Remove giognutls from MSVC 2024-11-14 18:57:13 +01:00
Jonas Kvinge
a239c127bd CI: Enable Spotify for MSVC 2024-11-14 18:57:13 +01:00
Jonas Kvinge
61daec6597 nsi: Add back Spotify 2024-11-14 18:57:13 +01:00
Jonas Kvinge
544db75eb4 main: Move GstStartup::Initialize after QApplication 2024-11-14 18:56:23 +01:00
Strawberry Bot
6a60eff5cd Update translations 2024-11-12 23:46:46 +01:00
Jonas Kvinge
63aa04241c Update Changelog 2024-11-12 23:45:39 +01:00
Jonas Kvinge
7a7550388d Rename KDE global shortcuts to KGlobalAccel 2024-11-12 23:03:57 +01:00
Jonas Kvinge
f2845b6632 Remove deprecated gnome/mate SettingsDaemon global shortcuts 2024-11-12 22:38:21 +01:00
Jonas Kvinge
deaeab3cbb CMake: Fix build without Chromaprint 2024-11-12 20:15:10 +01:00
Jonas Kvinge
16c9a0f974 GstEnginePipeline: Set final fader volume on timeout 2024-11-11 16:17:57 +01:00
Jonas Kvinge
18000b1b2c GstEnginePipeline: Increase fader timeout 2024-11-11 16:17:35 +01:00
Jonas Kvinge
f1b56028b7 GstEnginePipeline: Use fully-qualified names for QTimeLine 2024-11-11 16:17:11 +01:00
Jonas Kvinge
3d2315f754 GstEnginePipeline: Add mutex locker for Spotify access token 2024-11-11 16:16:29 +01:00
Jonas Kvinge
07c898581c CommandlineOptions: Pass absolute paths for urls 2024-11-11 16:15:06 +01:00
Jonas Kvinge
6612eeb9e3 GstEnginePipeline: Simplify next uri reset code 2024-11-10 15:38:00 +01:00
Jonas Kvinge
04064ebf68 mutex_protected: Return bool for operator== 2024-11-10 15:37:35 +01:00
Jonas Kvinge
3de3c52c01 Update Changelog 2024-11-10 02:18:58 +01:00
Jonas Kvinge
93929c73ee GstEnginePipeline: Add fader timeout 2024-11-10 02:07:22 +01:00
Jonas Kvinge
d68bede374 GstEnginePipeline: Fix fader fudge timer naming 2024-11-10 01:53:38 +01:00
Jonas Kvinge
b659b27f95 GstEnginePipeline: Replace QBasicTimer with QTimer 2024-11-10 01:50:53 +01:00
Jonas Kvinge
70d0772e04 GstEnginePipeline: Add separate set state async function 2024-11-10 01:36:48 +01:00
Jonas Kvinge
0a361bfb3b BackendSettingsPage: Add HAVE_ALSA 2024-11-10 01:23:39 +01:00
Jonas Kvinge
f9f47458d5 Remove engine type 2024-11-10 01:21:43 +01:00
Jonas Kvinge
218dd439b6 Player: Use shared pointer for engine 2024-11-10 01:21:25 +01:00
Jonas Kvinge
7f3293609b Player: Always use GStreamer 2024-11-10 01:11:43 +01:00
Jonas Kvinge
356b7d8e64 MainWindow: Remove engine changed 2024-11-10 01:11:01 +01:00
Jonas Kvinge
d26c291a2a BackendSettingsPage: Remove engine setting 2024-11-10 01:10:04 +01:00
Jonas Kvinge
82d34eea7b FilesystemDevice: Ignore compile warning C4250 2024-11-10 00:30:01 +01:00
Jonas Kvinge
4b0b0aa989 Player: Simplify creating engine 2024-11-10 00:16:04 +01:00
Jonas Kvinge
975d0dff25 Move GstStartup 2024-11-09 23:39:31 +01:00
Jonas Kvinge
b75410abc6 Song: Remove spaces and replace semicolon with slash in MBID's
Fixes #1581
2024-11-09 20:23:34 +01:00
Jonas Kvinge
c0f5b53aaf GstEnginePipeline: Simplify checking for NULL state 2024-11-09 19:36:41 +01:00
Jonas Kvinge
ba285925ca GstEnginePipeline: Check that state is actually NULL before finishing pipeline
Possible fix for #1582
2024-11-09 19:30:28 +01:00
Jonas Kvinge
a0dd2c66e4 GstEnginePipeline: Always set state to NULL 2024-11-09 19:26:39 +01:00
Jonas Kvinge
64a9d557a4 GstEnginePipeline: Add missing declarations 2024-11-09 19:26:09 +01:00
Jonas Kvinge
3a5e5d4aaa GstEngine: Improve pipeline finish handling 2024-11-09 19:24:07 +01:00
Jonas Kvinge
f59c6c356e GstEnginePipeline: Get audio-sink 2024-11-09 19:22:56 +01:00
Jonas Kvinge
65b6e6d540 GstEnginePipeline: Save fader state 2024-11-09 19:21:56 +01:00
Jonas Kvinge
0d2e933ed1 MoodbarProxyStyle: Stop timeline if already running 2024-11-09 19:18:38 +01:00
Jonas Kvinge
b16fbb3040 PlaylistView: Check timeline not running instead of running 2024-11-09 19:18:16 +01:00
Jonas Kvinge
638fc2881c ContextAlbum: Use fully-qualified QTimeLine::Direction and QTimeLine::State 2024-11-09 18:08:58 +01:00
Jonas Kvinge
2656cfd3ad ContextAlbum: Stop timeline if not running 2024-11-09 18:08:24 +01:00
Jonas Kvinge
80c5829792 CMake: Make QtSparkle optional
Fixes #1595
2024-11-08 21:39:35 +01:00
Jonas Kvinge
957a850adc FilterParser: Fix "OR" and "AND"
Fixes #1599
2024-11-08 21:39:35 +01:00
Jonas Kvinge
2a5e425b71 FavoriteWidget: Add parameter names 2024-11-08 21:39:35 +01:00
Jonas Kvinge
904ffb1417 TagReaderTagLib: Handle multiple values for ID3v2 tags 2024-11-08 21:39:35 +01:00
Jonas Kvinge
777fec9a92 Playlist: Remove deleteLater() as it uses QSharedPointer 2024-11-08 21:36:43 +01:00
Jonas Kvinge
a2017c003e CollectionModel: Always create QNetworkDiskCache
Fixes #1593
2024-11-03 16:29:41 +01:00
Jonas Kvinge
95842225fb FancyTabBar: Remove newlines 2024-11-03 04:44:28 +01:00
Jonas Kvinge
7cfb175a45 FancyTabWidget: Move functions 2024-11-03 04:42:44 +01:00
Jonas Kvinge
b7165e0124 FancyTabWidget: Use tab pointer directly 2024-11-03 04:41:26 +01:00
Jonas Kvinge
7132b06d6a Move nsi file to binary directory 2024-11-02 15:02:07 +01:00
Jonas Kvinge
4f79b8692c FancyTabWidget: Only set text and tooltip for inserted tab 2024-11-02 15:01:48 +01:00
Jonas Kvinge
a8e307bb6a Playlist: Only move album for current row first
The logic was flawed as current_virtual_index_ can be set even when current_row isn't causing crash.

Fixes #1588
2024-11-02 01:52:01 +01:00
Jonas Kvinge
a30aca4759 FancyTabWidget: Set tab text after setting data
Fixes crash when enabling a tab.
2024-11-02 01:29:44 +01:00
Jonas Kvinge
4c0220d10a FancyTabBar: Check that tab data is valid 2024-11-02 01:29:44 +01:00
Strawberry Bot
5fede2551e Update translations 2024-11-02 00:41:10 +01:00
Jonas Kvinge
e83b521ee0 Add const 2024-11-01 23:23:46 +01:00
Jonas Kvinge
8da2b9cd94 Refactoring 2024-11-01 23:04:42 +01:00
Jonas Kvinge
dfcf715291 nsi: Disable Spotify 2024-10-26 17:30:18 +02:00
Erriez
24c89b8ca3 nsi: Update ICU dependencies after update to 76.1 2024-10-26 17:19:31 +02:00
Jonas Kvinge
b106e94494 CMake: Add missing STATIC for strawberry_lib
Fixes #1580
2024-10-23 18:16:13 +02:00
Jonas Kvinge
28222b1832 Translations: Remove QObject 2024-10-21 00:30:12 +02:00
Jonas Kvinge
c818dabe92 Remove Pot translator 2024-10-21 00:29:21 +02:00
Jonas Kvinge
60f4a57425 tests: Port to QStringLiteral operator 2024-10-20 23:19:38 +02:00
Jonas Kvinge
a9ea686577 Fix unit tests 2024-10-20 22:54:33 +02:00
Jonas Kvinge
756f7cf6af CMake: Fix finding qplatformnativeinterface.h 2024-10-20 22:52:41 +02:00
Jonas Kvinge
ef9ef63f02 Port to QStringLiteral operator 2024-10-20 06:38:55 +02:00
Jonas Kvinge
722035913e BehaviourSettingsPage: Remove duplicate English language 2024-10-20 01:12:01 +02:00
Jonas Kvinge
50aa2dcc2b CMake: Prefix TS_FILES path with CMAKE_SOURCE_DIR 2024-10-20 01:11:39 +02:00
Jonas Kvinge
b6cbebcc8a CMake: Only run lupdate on strawberry_en_US.ts 2024-10-20 00:50:41 +02:00
Strawberry Bot
ba127c57d8 Update translations 2024-10-20 00:32:49 +02:00
Jonas Kvinge
ef261455a2 Update Changelog 2024-10-20 00:11:07 +02:00
Jonas Kvinge
1b1ab2e833 Port to Qt translations 2024-10-20 00:06:42 +02:00
Jonas Kvinge
fbf7fa51e5 CMake: Remove unused QT_DBUSXML2CPP_EXECUTABLE 2024-10-19 22:19:24 +02:00
Jonas Kvinge
b0d2da04ac EditTagDialog: Move hint texts to class 2024-10-19 21:53:22 +02:00
Strawberry Bot
0ab16c9ebf Update translations 2024-10-18 21:55:21 +02:00
Jonas Kvinge
d930ee205f CI: Turn off spotify for Windows MSVC 2024-10-18 21:54:28 +02:00
Jonas Kvinge
0e330b81db Use Qt::Literals::StringLiterals 2024-10-18 20:17:23 +02:00
Jonas Kvinge
6931538ebf Update README.md 2024-10-18 19:58:50 +02:00
Jonas Kvinge
b166396ef3 Update Changelog 2024-10-18 19:58:40 +02:00
Jonas Kvinge
026c2677f9 PlaylistManager: Use album artist for new playlist name 2024-10-18 19:58:34 +02:00
Jonas Kvinge
34e2e01992 CollectionWatcher: Monitoring always on for devices 2024-10-18 19:58:04 +02:00
Jonas Kvinge
7b2d8ac1a2 CMake: Simplify linking 2024-10-07 20:51:38 +02:00
Jonas Kvinge
256cc7d15c SongLoader: Try resolve symbolic links to match collection directory 2024-10-06 15:42:03 +02:00
Jonas Kvinge
1d9b8f464a CMake: Only include Qobuz cover provider if Qobuz is enabled 2024-10-06 14:42:48 +02:00
Jonas Kvinge
a8d1bf7e73 CollectionModel: Use song sort text if any group by is set to album
Fixes #1573
2024-10-06 14:39:14 +02:00
Jonas Kvinge
c58207dd2f CMake: Specify languages 2024-10-06 01:10:28 +02:00
Jonas Kvinge
1720ddc808 CMake: Remove unneeded windres and RC compiler hack 2024-10-06 01:10:03 +02:00
Jonas Kvinge
056d8817b2 main: Use QLocale::TagSeparator::Underscore 2024-10-05 15:43:29 +02:00
Jonas Kvinge
e12f27a945 Translations: Add debug logging 2024-10-05 15:43:03 +02:00
Jonas Kvinge
39fd89cb90 main: Use Qt stringliterals 2024-10-05 15:41:57 +02:00
Jonas Kvinge
756215544a GlobalShortcutsManager: Fix incorrect ifdef 2024-10-04 22:18:02 +02:00
Jonas Kvinge
0768298b95 Refactor CMake files 2024-10-04 22:05:20 +02:00
Jonas Kvinge
525ebbb9b7 CollectionModel: Fix updating song when disc is changed 2024-10-04 16:56:59 +02:00
Jonas Kvinge
c47ec3e70a DynamicPlaylistControls: Use QPalette::AlternateBase 2024-10-04 16:29:21 +02:00
Jonas Kvinge
20394271c7 DynamicPlaylistControls: Replace u'%' with u"%"_s
Fixes #1572
2024-10-04 16:29:04 +02:00
Strawberry Bot
731f670a2b Update translations 2024-10-01 21:14:11 +02:00
Jonas Kvinge
d35c8e5b93 CI: Ignore translations commits 2024-10-01 21:10:50 +02:00
Strawberry Bot
b1161d3542 Update translations 2024-10-01 20:49:57 +02:00
Jonas Kvinge
47a01fc659 CMake: Remove Spotify GStreamer dependency 2024-10-01 20:46:53 +02:00
Jonas Kvinge
d72694ce06 CollectionModel: Only use song sort text if album is the parent group by 2024-09-30 17:14:43 +02:00
Jonas Kvinge
85af736bfd CI: Add back Windows x86 builds 2024-09-30 00:36:01 +02:00
Jonas Kvinge
b50da3eba4 GstEnginePipeline: Add missing end of stream
A bug was introduced when I added the mutex locker for the URLs, it did nothing when it was supposed to emit end of stream.

Fixes #1568
2024-09-29 23:40:09 +02:00
Jonas Kvinge
4479daeaf1 GstEngine: Finish pipeline before resetting in end of stream 2024-09-29 23:35:14 +02:00
Jonas Kvinge
a123de06c6 GstEngine: Add mutex lock for checking stream url 2024-09-29 23:33:47 +02:00
Jonas Kvinge
62f2aee00c README: Remove tagparser and add Qt D-Bus 2024-09-29 11:56:00 +02:00
Jonas Kvinge
b59cc4d038 TagReaderTagLib: Set source and init from file 2024-09-29 03:25:06 +02:00
Jonas Kvinge
3468737e14 Song: Add init from file setter 2024-09-29 03:24:44 +02:00
Jonas Kvinge
5292e53b4a CMake: Remove GStreamer optional component 2024-09-28 16:43:13 +02:00
Jonas Kvinge
bae3fcfaba Update Changelog 2024-09-28 16:38:36 +02:00
Jonas Kvinge
e4a57aa768 Remove VLC 2024-09-28 16:38:23 +02:00
Jonas Kvinge
25451d361c CI: Remove unused CMake options 2024-09-28 15:38:14 +02:00
Jonas Kvinge
daaacf4663 Remove external tagreader 2024-09-28 15:29:10 +02:00
Jonas Kvinge
3cb0f60900 Add missing names for parameter variables 2024-09-28 12:32:12 +02:00
Jonas Kvinge
f2e28d18bc Player: Initialize variable 2024-09-28 12:27:51 +02:00
Jonas Kvinge
6cb54b9f9f MimeData: Remove useless initialization 2024-09-28 12:27:33 +02:00
Jonas Kvinge
ce3db115a3 MainWindow: Remove unused variable 2024-09-28 12:27:02 +02:00
Jonas Kvinge
dae4943593 Replace Spotify username/password with access token 2024-09-28 00:09:23 +02:00
Jonas Kvinge
ca8aa40034 SystemTrayIcon: Add missing visible method 2024-09-27 20:42:24 +02:00
Jonas Kvinge
a239374c4b MainWindow: Simplify hiding to tray logic 2024-09-27 19:44:02 +02:00
Jonas Kvinge
1c833a28dc BehaviourSettingsPage: Remember keep running option 2024-09-27 18:35:22 +02:00
Jonas Kvinge
c30de78bf1 BehaviourSettingsPage: Add const 2024-09-27 18:21:42 +02:00
Jonas Kvinge
19b3adeb96 MainWindow: Keep running require system tray icon
Fixes bug where Strawberry remains running when system tray icon is disabled.
2024-09-27 18:13:38 +02:00
Jonas Kvinge
4b55150515 PlaylistListView: Remove newlines from tr text
Fixes #1564
2024-09-26 02:33:50 +02:00
Strawberry Bot
5bcfd5dd25 Update translations 2024-09-26 01:21:58 +02:00
Jonas Kvinge
722f93b090 CMake: Use CMAKE_COMPILE_WARNING_AS_ERROR 2024-09-26 01:08:47 +02:00
Jonas Kvinge
1ef50333a6 UWPDeviceFinder: Silence codecvt deprecation warning 2024-09-26 01:06:15 +02:00
Jonas Kvinge
fa6ac4df41 Include fixes 2024-09-25 23:52:56 +02:00
Jonas Kvinge
517a8baea2 Fix missing includes 2024-09-24 21:35:31 +02:00
Jonas Kvinge
5473a6f8ac CollectionTask: Replace QtClassHelperMacros with QtGlobal 2024-09-24 20:51:12 +02:00
Jonas Kvinge
4d4dd6b249 Replace QVector with QList 2024-09-24 20:47:43 +02:00
Jonas Kvinge
2e6408a4f7 Remove unneeded includes 2024-09-24 20:41:11 +02:00
Jonas Kvinge
3c984ac8cf AnalyzerContainer: Add missing newlines 2024-09-24 20:40:45 +02:00
Jonas Kvinge
e52ea2a255 Add parameter variable names 2024-09-24 20:40:35 +02:00
Jonas Kvinge
cf22b183a5 Move gstfastspectrum to src 2024-09-24 16:29:19 +02:00
Jonas Kvinge
11d4151a82 CollectionWatcher: Mark variable unused
Fixes #1560
2024-09-24 16:28:39 +02:00
Jonas Kvinge
c5f9e43d10 CollectionWatcher: Remove unused ctor 2024-09-24 16:27:47 +02:00
Jonas Kvinge
3329bc7d76 CollectionWatcher: Add missing newline 2024-09-24 16:27:32 +02:00
Jonas Kvinge
4c8ada2b80 CollectionWatcher: Add missing const 2024-09-24 16:27:09 +02:00
Jonas Kvinge
6b446eb693 CollectionWatcher: Fix typos 2024-09-24 16:26:08 +02:00
Jonas Kvinge
1663fa3ef1 ebur128analysis: Fix typo 2024-09-24 16:25:23 +02:00
Jonas Kvinge
7da0df2178 CMake: Exclude -Wno-missing-declarations on MSVC 2024-09-23 23:38:36 +02:00
Jonas Kvinge
254c442a69 kdsingleapplication: Add -Wno-missing-declarations 2024-09-23 23:33:22 +02:00
Jonas Kvinge
0568c47a37 Build with -Werror by default 2024-09-23 23:26:58 +02:00
Jonas Kvinge
c9c6c2ad12 libstrawberry-tagreader: Add -Wno-missing-declarations 2024-09-23 23:25:14 +02:00
Jonas Kvinge
f8ce2f1705 Remove MacFSListener
It's no longer needed. This code dates back to before Qt had a FSEvents-based filesystem listener.
2024-09-23 16:05:02 +02:00
Jonas Kvinge
f9c35edd7c Update debian/copyright 2024-09-23 16:01:34 +02:00
Jonas Kvinge
4cd490d871 gstfastspectrum: Use G_GUINT64_FORMAT 2024-09-23 16:00:27 +02:00
Jonas Kvinge
936521981b README: Update required TagLib version 2024-09-22 22:24:59 +02:00
Jonas Kvinge
fa96cdfaea MoodbarPipeline: Add newline 2024-09-22 21:40:50 +02:00
Jonas Kvinge
2c13942ed5 MoodbarPipeline: Remove unused include 2024-09-22 21:39:00 +02:00
Jonas Kvinge
af8a7d5093 MoodbarPipeline: Rename variables 2024-09-22 20:43:44 +02:00
Jonas Kvinge
04eb97ef93 MoodbarPipeline: Remove unused NewBufferCallback 2024-09-22 20:39:25 +02:00
Jonas Kvinge
0b49dcaa2d Remove 3rdparty/SPMediaKeyTap 2024-09-22 13:15:35 +02:00
Jonas Kvinge
c3008b4179 Silence Clang Wunused-const-variable 2024-09-22 13:15:19 +02:00
Jonas Kvinge
28a29d219e moodbar: Formatting 2024-09-22 12:41:22 +02:00
Strawberry Bot
0919d5d3de Update translations 2024-09-22 00:54:47 +02:00
Christian Kr
167e0d73d1 Add icons only sidebar mode 2024-09-22 00:12:17 +02:00
Jonas Kvinge
d87560b73e Update Changelog 2024-09-21 23:17:13 +02:00
Jonas Kvinge
e6607fef6e Bump version to 1.2.0 2024-09-21 23:16:37 +02:00
Jonas Kvinge
f815b2b699 Turn on git revision 2024-09-21 23:12:15 +02:00
Jonas Kvinge
247c0ae46a Release 1.1.3 2024-09-21 23:12:05 +02:00
Jonas Kvinge
f62d22f94a CI: Upload PPA on 1.1 branch 2024-09-21 23:11:53 +02:00
Jonas Kvinge
78a9cb35a3 filterparser: Optimize filter term
Fixes #1536
2024-09-21 00:57:36 +02:00
Jonas Kvinge
e72b001d02 FilterParser: Add check for empty column 2024-09-20 23:51:25 +02:00
Jonas Kvinge
5bdbb9f13f appdata: Update description 2024-09-19 16:06:36 +02:00
Jonas Kvinge
0e7ab1cab8 GeniusLyricsProvider: Convert client ID and secret from base64
Fixes #1554
2024-09-18 22:51:28 +02:00
Jonas Kvinge
f11de5bfc2 CI: Fix source upload path 2024-09-17 23:15:01 +02:00
Jonas Kvinge
6d68f7f008 GstEngine: Don't set state to play if already playing 2024-09-17 22:23:17 +02:00
Jonas Kvinge
2bed827eb6 gstfastspectrum: Use %lu in GST_LOG_OBJECT 2024-09-17 22:19:01 +02:00
Jonas Kvinge
98cfef3306 gstfastspectrum: Change guint to guint64 2024-09-17 22:13:11 +02:00
Jonas Kvinge
f44839137c GstEngine: Use beginning nanosec on play
Fixes #1549
2024-09-16 00:34:39 +02:00
Jonas Kvinge
0decbdc2fb nsi: Move libgetopt.dll to minw section 2024-09-13 20:55:27 +02:00
Jonas Kvinge
23b5ceb6c9 Remove 3rdparty\getopt 2024-09-13 20:41:23 +02:00
Jonas Kvinge
4b36beec40 Remove macdeploycheck 2024-09-13 19:56:50 +02:00
Alexander Bikadorov
b60ca9f6fa Settings: Add "Restart or previous" global shortcut 2024-09-13 19:54:47 +02:00
Jonas Kvinge
718c24ee47 spec: Remove Qt 5 specific requires 2024-09-13 00:41:59 +02:00
Jonas Kvinge
86dd2886d2 Settings: Remove compatibility code 2024-09-13 00:41:35 +02:00
Jonas Kvinge
23fb4651be Remove remaining X11Extras stuff 2024-09-13 00:30:37 +02:00
Jonas Kvinge
877c9ca41a CurrentAlbumCoverLoader: Should be not empty 2024-09-12 23:57:40 +02:00
Jonas Kvinge
99fb775e30 CurrentAlbumCoverLoader: Port away from QTemporaryFile 2024-09-12 23:09:38 +02:00
Jonas Kvinge
1bb764a2d2 CurrentAlbumCoverLoader: Use standard temp location
Addresses #1540
2024-09-12 23:00:36 +02:00
Jonas Kvinge
a3eab902ff SpotifySettingsPage: Fix gst reg lookup leak 2024-09-12 22:55:52 +02:00
Jonas Kvinge
e9684cd1a1 Move gstfastspectrum to 3rdparty 2024-09-12 22:51:01 +02:00
Jonas Kvinge
9ae0afaaf7 Require TagLib 1.12 or newer 2024-09-12 22:15:13 +02:00
Jonas Kvinge
48bb5a3e9f PlaylistListModel: Use const_iterator 2024-09-12 22:13:21 +02:00
Jonas Kvinge
f642513587 StreamingSearchView: Use const_iterator 2024-09-12 22:13:21 +02:00
Jonas Kvinge
255623bbfd Replace QStringLiteral with u""_s operator 2024-09-12 22:13:21 +02:00
Jonas Kvinge
4270b12cd1 Replace QLatin1String with operator _L1 2024-09-12 22:13:21 +02:00
Jonas Kvinge
e3e6a22172 Drop Qt 5 support
Qt 6 has been available for almost 4 years. Qt 5 is no longer officially supported by Qt for opensource, it's time to drop Qt 5.
2024-09-12 22:13:21 +02:00
Strawberry Bot
eb30c654c5 Update translations 2024-09-12 22:12:34 +02:00
Jonas Kvinge
3b925c24ad Turn on git revision 2024-09-12 21:57:12 +02:00
Jonas Kvinge
beefdabc64 Release 1.1.2 2024-09-12 19:49:52 +02:00
Jonas Kvinge
91e06cadf3 Update Changelog 2024-09-12 19:49:23 +02:00
Jonas Kvinge
4ea5eb8292 GstEnginePipeline: Set volume internal in notify volume callback
Fixes #1541
2024-09-10 17:04:24 +02:00
Jonas Kvinge
af06f8e70a Playlist: Remove flawed sorting logic for filename
Possible fix for #1538
2024-09-08 13:12:13 +02:00
Jonas Kvinge
3238e295ba Update Changelog 2024-09-07 16:45:33 +02:00
Jonas Kvinge
26fbd5c144 Update 3rdparty/README.md 2024-09-07 16:42:43 +02:00
Jonas Kvinge
e64a2b98c4 Turn on git revision 2024-09-07 01:17:57 +02:00
Jonas Kvinge
42417e5554 Release 1.1.2-rc1 2024-09-06 23:08:22 +02:00
Jonas Kvinge
82e613206e CollectionQuery: Add const 2024-09-06 22:58:21 +02:00
Jonas Kvinge
f1038152e9 Update Changelog 2024-09-06 22:31:12 +02:00
Jonas Kvinge
2597a8faed CMake: Add option to turn off debug output 2024-09-03 23:41:49 +02:00
Jonas Kvinge
8008ec895a logging: Respect QT_NO_INFO_OUTPUT 2024-09-03 23:10:55 +02:00
Jonas Kvinge
d85d25b931 TagReaderTagLib: Use Strawberry for editor 2024-09-03 23:10:16 +02:00
Jonas Kvinge
de62552ad1 AlbumCoverManager: Queue album cover loading using timer
Helps reduce memory growth.
2024-09-03 22:02:05 +02:00
Jonas Kvinge
155485173b AlbumCoverLoader: Process tasks using timer
Helps reduce memory growth.
2024-09-03 22:01:13 +02:00
Strawberry Bot
82079fcf70 Update translations 2024-09-03 01:56:28 +02:00
Jonas Kvinge
3b2eea5292 nsi: Add utf8_validity.dll 2024-09-02 23:56:26 +02:00
Jonas Kvinge
1f2ad9c177 CI: Manually sign libutf8_validity.dylib 2024-09-02 23:51:47 +02:00
Jonas Kvinge
cc6e5a6c69 TagReaderTagLib: Handle multiple mbids for MP4
Fixes #1531
2024-09-02 23:48:25 +02:00
Jonas Kvinge
c77c7a247a ListenBrainzScrobbler: Split work mbids 2024-09-02 23:46:13 +02:00
Jonas Kvinge
552440f50e Add mutexes 2024-09-02 22:27:45 +02:00
Jonas Kvinge
2a9ccd7480 Set object names 2024-09-02 22:26:36 +02:00
Jonas Kvinge
f265c055a5 metatypes: Add LyricsSearchResults 2024-09-02 22:25:12 +02:00
Jonas Kvinge
837e5388ea CMake: Use target_link_directories 2024-08-27 19:39:53 +02:00
Jonas Kvinge
9883dc6925 TemporaryFile: Use int 2024-08-25 18:09:09 +02:00
Jonas Kvinge
e3ad00e930 CI: Adjust manual codesign 2024-08-25 18:05:20 +02:00
Jonas Kvinge
6428ae8b3a GPodDevice: Use own temporary file class
QTemporaryFile keeps files open.

Fixes #1527
2024-08-25 17:59:56 +02:00
Jonas Kvinge
07c182d5b8 Add temporary file class 2024-08-25 17:58:27 +02:00
Jonas Kvinge
64aa15842c Organize: Correct debug message 2024-08-25 17:30:09 +02:00
Jonas Kvinge
19c8da06e6 PlaylistTest: Replace forever with Q_FOREVER 2024-08-25 16:58:56 +02:00
Jonas Kvinge
7dd959f4a1 CMake: Skip pot on MSVC 2024-08-25 15:40:18 +02:00
Jonas Kvinge
148ae530d8 Playlist: Add missing reference for Columns 2024-08-25 06:24:55 +02:00
Jonas Kvinge
e75698ee9a CMake: Add QT_NO_KEYWORDS 2024-08-25 06:24:26 +02:00
Jonas Kvinge
80bea31b98 Replace forever with Q_FOREVER 2024-08-25 06:24:02 +02:00
Jonas Kvinge
4ea57d1181 searchfield_mac: Replace emit with Q_EMIT 2024-08-25 06:23:20 +02:00
Jonas Kvinge
854847ca8a nsi: Update ffmpeg DLL's 2024-08-25 05:59:32 +02:00
Jonas Kvinge
bd39e7cb0d smartplaylists: Move classes to own files 2024-08-25 05:49:41 +02:00
Jonas Kvinge
20ef621a20 Rename SearchField 2024-08-25 05:48:37 +02:00
Jonas Kvinge
145c276c97 PlayingWidget: Remove unused variables 2024-08-25 05:46:53 +02:00
Jonas Kvinge
1c5d0dceb1 Replace emit with Q_EMIT 2024-08-25 05:46:17 +02:00
Jonas Kvinge
359f320b06 MacFSListener: Formatting 2024-08-25 05:45:33 +02:00
Jonas Kvinge
108d522dcf OrganizeFormat: Move to own classes 2024-08-25 03:09:11 +02:00
Jonas Kvinge
96e746c508 AlbumCoverLoaderOptions: Fix incorrect enum number 2024-08-25 02:08:12 +02:00
Jonas Kvinge
b2c862e7d5 CollectionModel: Use chrono literals 2024-08-25 02:07:31 +02:00
Jonas Kvinge
9334fe9f24 Remove const from qHash 2024-08-25 01:37:46 +02:00
Jonas Kvinge
b964385024 AlbumCoverLoader: Remove const 2024-08-25 01:16:29 +02:00
Jonas Kvinge
3f3059c98b Replace QLatin1String with QStringLiteral 2024-08-25 01:08:25 +02:00
Jonas Kvinge
8da616491d Replace emit with Q_EMIT 2024-08-25 01:06:30 +02:00
Jonas Kvinge
cb0db8750f CollectionModelUpdate: Remove reference from enum 2024-08-24 23:20:20 +02:00
Jonas Kvinge
08224443e3 MainWindow: Don't use the playlists backend on right click
Fixes #1478
2024-08-24 22:43:16 +02:00
Jonas Kvinge
5c2989196f MainWindow: Fix comments 2024-08-24 22:40:20 +02:00
Jonas Kvinge
4c4c84e104 PlaylistManager: Add methods for accessing playlists 2024-08-24 22:39:55 +02:00
Jonas Kvinge
232399ea28 DynamicPlaylistControls: Make background follow system colors
Fixes #1483
2024-08-24 22:07:46 +02:00
Jonas Kvinge
9d22e4ec07 SongLoader: Use Song::kRejectedExtensions
Fixes #1525
2024-08-24 21:12:19 +02:00
Jonas Kvinge
ee5bc16e47 CollectionWatcher: Use Song::kRejectedExtensions 2024-08-24 21:10:52 +02:00
Jonas Kvinge
74f0f885b9 Song: Add rejected extensions 2024-08-24 21:10:27 +02:00
Jonas Kvinge
b914d9aaba Update translations.pot 2024-08-24 21:09:22 +02:00
Jonas Kvinge
136f150d67 Translations: Remove deprecated sort option 2024-08-24 21:08:00 +02:00
Jonas Kvinge
5b50cbb61b Update .gitignore 2024-08-24 21:03:53 +02:00
Jonas Kvinge
cb3a9bf195 Update translations.pot 2024-08-24 20:44:46 +02:00
Strawberry Bot
cb847951e6 Update translations 2024-08-24 20:43:21 +02:00
Jonas Kvinge
11228f0634 Player: Add missing override 2024-08-24 20:38:08 +02:00
Jonas Kvinge
a0889d60f1 Update translations.pot 2024-08-24 20:21:45 +02:00
Jonas Kvinge
0055ebe8a7 CMake: Move translations.pot to src dir 2024-08-24 20:21:37 +02:00
Jonas Kvinge
58c7a9b9cc Translations: Add --no-location to xgettext 2024-08-24 20:21:20 +02:00
Strawberry Bot
6afc081ff0 Update translations 2024-08-24 20:15:38 +02:00
Jonas Kvinge
2c0ad2fc88 Move lyrics providers to own thread 2024-08-24 20:07:36 +02:00
Jonas Kvinge
77e934beab SpotifyService: Use LoginError 2024-08-24 19:29:00 +02:00
Jonas Kvinge
368022ec43 concurrentrun_test: Remove QtConcurrent include 2024-08-24 19:28:44 +02:00
Jonas Kvinge
69f8ca95bc Add missing reference 2024-08-24 19:28:15 +02:00
Jonas Kvinge
dde8661e93 Use QDateTime::currentSecsSinceEpoch() 2024-08-24 17:28:29 +02:00
Jonas Kvinge
2604e1a0ff Use multi-arg 2024-08-24 17:27:47 +02:00
Jonas Kvinge
e8471bcc66 MusixmatchCoverProvider: Use static QRegularExpression 2024-08-24 17:27:05 +02:00
Jonas Kvinge
d230dd7365 Use fully-qualified namespaces in slot parameters 2024-08-24 17:25:56 +02:00
Jonas Kvinge
74dce24e91 Mpris2: Remove QtDBus include 2024-08-24 17:24:56 +02:00
Jonas Kvinge
bc667a6474 Use static QRegularExpression 2024-08-24 17:23:10 +02:00
Jonas Kvinge
a2cae06582 Remove QtConcurrent include 2024-08-24 17:01:53 +02:00
Jonas Kvinge
5212587055 TagReaderGME: Mark variable unused 2024-08-24 17:00:00 +02:00
Jonas Kvinge
efd42bc68f MusicBrainzClient: Remove unneeded values from qDeleteAll 2024-08-23 20:40:36 +02:00
Jonas Kvinge
ebaa2e7918 BlockAnalyzer: Replace value with at 2024-08-23 20:32:56 +02:00
Jonas Kvinge
7ebcc73a49 More const detach fixes 2024-08-23 20:30:59 +02:00
Jonas Kvinge
be09011bb7 CollectionWatcher: Use mutex for stop and abort 2024-08-23 20:22:18 +02:00
Jonas Kvinge
2778a55e8e SpotifySettingsPage: Update Wiki page URL 2024-08-23 19:16:17 +02:00
Jonas Kvinge
9b5fe3bfd6 GstEnginePipeline: Rename PlaybinProbe to PadProbe 2024-08-23 00:17:33 +02:00
Jonas Kvinge
91eef0d695 GstEnginePipeline: Sort variables 2024-08-23 00:08:14 +02:00
Jonas Kvinge
88704efad8 Add lyricfind.com lyrics provider 2024-08-18 20:35:09 +02:00
Jonas Kvinge
f4e4483392 HtmlLyricsProvider: Remove extra QRegularExpression 2024-08-18 19:58:57 +02:00
Jonas Kvinge
63102bce23 Update Changelog 2024-08-17 23:13:30 +02:00
Jonas Kvinge
8890a3dd0f Delay play until playlists have finished loading
Fixes #1465
2024-08-17 22:38:48 +02:00
Jonas Kvinge
3d9dec2c27 nsi: Add libbrotlienc.dll 2024-08-17 15:39:35 +02:00
Jonas Kvinge
c96ad61c80 CMake: Add QT_NO_SHOW_OLD_QT_WRAP_CPP_WARNING 2024-08-17 14:31:51 +02:00
Jonas Kvinge
acd74d5b54 README: Remove buildbot URL 2024-08-12 23:16:24 +02:00
Strawberry Bot
4808188964 Update translations 2024-08-12 22:36:10 +02:00
Jonas Kvinge
1bb045b3b0 Mpris2: Remove .desktop file extension in DesktopEntry
According to the specifications it should be the desktop entry without .desktop file extension: https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html#Property:DesktopEntry

Fixes #1516
2024-08-12 21:49:25 +02:00
Jonas Kvinge
bdca60c0ad Add missing const 2024-08-12 18:12:26 +02:00
Jonas Kvinge
8d9c135498 DeviceManager: Remove no longer relevant comment 2024-08-12 01:09:59 +02:00
Jonas Kvinge
0f76482916 GioLister: Remove undef signals 2024-08-12 01:09:33 +02:00
Jonas Kvinge
38eb86bdee CMake: Add QT_NO_SIGNALS_SLOTS_KEYWORDS 2024-08-12 01:09:01 +02:00
Jonas Kvinge
cbce9892d5 Replace slots with Q_SLOTS 2024-08-12 01:06:15 +02:00
Jonas Kvinge
f624b7a331 Add cpp files for classes with only header files 2024-08-12 00:48:16 +02:00
Jonas Kvinge
1ebcd61a75 PlaylistListView: Fix incorrect header guard 2024-08-11 23:30:03 +02:00
Jonas Kvinge
358da72ffe Replace signals with Q_SIGNALS 2024-08-11 23:23:12 +02:00
Jonas Kvinge
9666feca37 GstEngine: Rename variable 2024-08-11 18:40:07 +02:00
Jonas Kvinge
03eb52eac8 GstEngine: Ensure no fading is done with exclusive mode 2024-08-11 17:37:23 +02:00
Jonas Kvinge
6562cc710c GstEngine: Disconnect old pipelines
Fixes #1518
2024-08-11 15:53:41 +02:00
Jonas Kvinge
222001bc13 GstEnginePipeline: Fix buffering 2024-08-11 14:52:00 +02:00
Jonas Kvinge
e93f14248a Update appdata xml file 2024-08-11 00:44:27 +02:00
Jonas Kvinge
7119f1bc81 Add filename and url to text search columns 2024-08-11 00:12:41 +02:00
Jonas Kvinge
548fa3f6ee Wait for set state to finish before deleting pipeline
Setting state to GST_STATE_NULL sometimes blocks, to fix this use the threadpool to set the state to NULL and wait with deleting the pipeline until the state is changed.
This fixes blocking the main thread when switching Spotify songs.
2024-08-10 18:22:56 +02:00
Jonas Kvinge
8ddd309d5d FilterTree: Use Song::PrettyTitle for title
Uses the filename instead if the title tag is emtpy.
2024-08-10 12:43:38 +02:00
Jonas Kvinge
8c8acbb546 GstEnginePipeline: Rename variables 2024-08-09 19:29:12 +02:00
Jonas Kvinge
fe30f27af3 GstEngine: Simplify use of State 2024-08-09 19:26:15 +02:00
Jonas Kvinge
d5d2eaba8a CollectionWatcher: Make const 2024-08-08 17:13:14 +02:00
Jonas Kvinge
e8f64bfe8f CollectionWatcher: Formatting 2024-08-08 17:12:51 +02:00
Jonas Kvinge
79b03993b0 Update .gitignore 2024-08-08 17:12:23 +02:00
Jonas Kvinge
e3b8f9cb8c Windows7ThumbBar: Fix namespace 2024-08-07 01:15:26 +02:00
Jonas Kvinge
819463a865 Use anonymous namespace for constants 2024-08-07 00:52:58 +02:00
Jonas Kvinge
c69777ca39 ContextView: Only update top text when changed 2024-08-06 23:24:58 +02:00
Jonas Kvinge
40f3e828aa ResizableTextEdit: Store current text 2024-08-06 23:23:27 +02:00
Jonas Kvinge
dc8520ebec Move translations.pot to binary directory 2024-08-06 23:05:26 +02:00
Jonas Kvinge
7ffbedf0dd AnalyzerContainer: Fix setting default analyzer 2024-08-06 22:59:26 +02:00
Jonas Kvinge
826fad1ad4 SpotifyRequest: Limit returned albums to queried artists 2024-08-05 18:24:47 +02:00
Jonas Kvinge
1c4d3aebad Rename enums 2024-08-05 17:53:07 +02:00
Jonas Kvinge
ff6e93fc15 CollectionWatcher: Only start transaction with scan on startup
Fixes #1469
2024-08-03 00:44:47 +02:00
Jonas Kvinge
17e88bb97d Add const 2024-08-02 23:35:52 +02:00
Jonas Kvinge
d3dd26c596 GstEnginePipeline: Set Spotify bitrate to 320 2024-08-01 23:22:19 +02:00
Jonas Kvinge
c2a7509e0e Update Changelog 2024-08-01 21:56:44 +02:00
Jonas Kvinge
079040b721 CI: Build on macos-13 for x86 and macos-14 for arm 2024-07-31 20:16:37 +02:00
Strawberry Bot
b80d239820 Update translations 2024-07-31 17:26:35 +02:00
Jonas Kvinge
c3ff43dac2 CI: Build macOS public with spotify 2024-07-30 19:59:09 +02:00
Jonas Kvinge
4ac51afd8f macgstcopy: Add spotify plugin 2024-07-30 19:59:09 +02:00
luzpaz
26eab51698 README: Tweak Repology badge
Use 3 column format + Add header
2024-07-30 16:56:24 +02:00
Jonas Kvinge
388dcf2a1f Fix dtor compile warnings with Clang 2024-07-30 16:34:20 +02:00
Jonas Kvinge
9bfd14d067 FancyTabBar: Add missing override 2024-07-30 16:33:50 +02:00
Jonas Kvinge
061e0562d1 CollectionView: Fix search for this
Fixes #1510
2024-07-29 20:23:42 +02:00
Jonas Kvinge
2ba20b013d Move fancy tabbar classes into separate files 2024-07-29 18:19:41 +02:00
Jonas Kvinge
dc36f87a6e FancyTabWidget: Remove use of QTabBar::tabText
Fixes #1476
Fixes #1389
2024-07-29 17:15:33 +02:00
Jonas Kvinge
e82ecb48b8 PlaylistTabBar: Don't use QTabBar::tabText
Fixes #1499
2024-07-29 16:17:46 +02:00
Jonas Kvinge
c56a134179 Update translations.pot 2024-07-29 13:02:37 +02:00
Jonas Kvinge
11028456ad LocalRedirectServer: Remove unused declaration 2024-07-29 01:53:16 +02:00
Jonas Kvinge
81fcb02974 OpenTidalCoverProvider: Only login when required
Fixes #1500
2024-07-29 00:48:16 +02:00
Jonas Kvinge
5ef4976c53 SpotifyRequest: Remove unused variable 2024-07-28 14:55:42 +02:00
Jonas Kvinge
160356072f TagReaderTagLib: Add missing override 2024-07-28 02:35:35 +02:00
Strawberry Bot
c10df5b634 Update French translations 2024-07-28 01:26:26 +02:00
Jonas Kvinge
70fdd5b0b3 Use anonymous namespace instead of static for constants 2024-07-28 01:24:56 +02:00
Jonas Kvinge
6c1ec51fab README: Add translations URL 2024-07-25 02:30:38 +02:00
Strawberry Bot
66b1c22174 Update translations 2024-07-25 02:27:38 +02:00
Strawberry Bot
97138fd6b9 Update Crowdin configuration file 2024-07-25 01:34:54 +02:00
Jonas Kvinge
03c69be421 Rename translation files 2024-07-25 01:29:40 +02:00
Jonas Kvinge
3a9a952cb0 Add translations.pot 2024-07-24 22:39:05 +02:00
Eri the Switch
3bd0331aa3 Use accumulator for seeking via scrolling
This results in much smoother experience on my touchpad.
The value of 120 was chosen as the most common for mice,
according to Qt 6 documentation.
2024-07-24 20:02:33 +02:00
Eri the Switch
3ecf224d91 Use accumulator for volume scrolling events
This results in much smoother experience on my touchpad.
2024-07-24 20:02:33 +02:00
Jonas Kvinge
37743606a2 CollectionModel: Remove unused ExpandAll function 2024-07-24 19:58:53 +02:00
Jonas Kvinge
58587f4a9a Turn on git revision 2024-07-23 01:14:57 +02:00
Jonas Kvinge
464fde1851 Release 1.1.1 2024-07-22 23:44:17 +02:00
Jonas Kvinge
2639498642 Update Changelog 2024-07-22 23:37:46 +02:00
Jonas Kvinge
49f074737c Remove placeholder text 2024-07-22 20:48:46 +02:00
Jonas Kvinge
3d53a8b434 AppearanceSettingsPage: Remove translatable 2024-07-22 19:00:24 +02:00
Jonas Kvinge
e260433c7a settings: Remove translatable 2024-07-22 18:58:18 +02:00
Jonas Kvinge
88ef8bff0b Update .gitignore 2024-07-22 18:29:43 +02:00
Jonas Kvinge
fbdac36f6f PlaylistView: Adjust initial header layout 2024-07-20 15:19:50 +02:00
Jonas Kvinge
da3876bd83 StretchHeaderView: Properly implement reset 2024-07-20 15:19:28 +02:00
Jonas Kvinge
d303e700ae CollectionFilter: Override mimedata function 2024-07-20 01:55:53 +02:00
Jonas Kvinge
92a1173b9e Remove unused FilterParserRatingComparatorDecorator 2024-07-19 18:18:02 +02:00
Jonas Kvinge
9f7ebb1ac7 CI: Remove Ubuntu mantic and add oracular for PPA 2024-07-19 18:14:10 +02:00
Jonas Kvinge
1a8690e1f2 StretchHeaderView: Make sure section size never is zero
Fixes #1085
2024-07-19 17:51:49 +02:00
Jonas Kvinge
6543e4c5da Use common filter parser for collection and playlist 2024-07-19 17:29:05 +02:00
Jonas Kvinge
dd904fe3c2 Remove unused CollectionQueryOptions class 2024-07-18 02:06:48 +02:00
ajtribick
c14cc6bf0b CMake: Use result of find_program instead of calling xgettext directly 2024-07-18 00:20:25 +02:00
Jonas Kvinge
95c265ffd3 CollectionFilter: Match individual words 2024-07-17 01:41:25 +02:00
Jonas Kvinge
31c1ae68df EditTagDialog: Fix build without MusicBrainz
Fixes #1492
2024-07-17 00:00:17 +02:00
Jonas Kvinge
f2eb0c3b6b CollectionModel: Add ItemNeverHasChildren 2024-07-15 14:28:29 +02:00
Jonas Kvinge
32be33847c CollectionFilter: Move early return 2024-07-15 14:16:56 +02:00
Jonas Kvinge
3100b0c044 CollectionFilter: Use recursive filtering
Fixes #1486
Fixes #1487
2024-07-15 13:44:50 +02:00
Jonas Kvinge
f4ec3ab379 CollectionModel: Don't append artist if song is compilation 2024-07-14 20:21:08 +02:00
Jonas Kvinge
cdd7faa9bb CI: Add Fedora 41 and Ubuntu Oracular 2024-07-14 17:47:43 +02:00
Jonas Kvinge
e7b35aeaf7 CMake: Use find_package CONFIG for Boost 2024-07-14 17:47:43 +02:00
Jonas Kvinge
696256eb5b README: Update macOS and Windows download info 2024-07-14 17:46:11 +02:00
Mikel Pérez
8ad560ce0e simplify CreateElementForMimeType + good practices
suggestions from gstreamer dev slomo on gst's matrix:
- whole static_pad_templates loop can be avoided with
  gst_element_factory_can_src_any_caps
- ffmpeg elements have been av* prefixed for a while now
- should be looking for Muxer instead of Codec/Muxer,
  Encoder/Audio instead of Codec/Encoder/Audio,
  and there are constants for that
2024-07-14 17:24:42 +02:00
Jonas Kvinge
1c71506f62 Turn off git revision 2024-07-14 17:20:58 +02:00
Jonas Kvinge
8bea6ec5b0 Release 1.1.0 2024-07-14 14:55:14 +02:00
Jonas Kvinge
e8144487ee Update Changelog 2024-07-14 14:48:29 +02:00
Jonas Kvinge
41d9d15dda MainWindow: Only show sponsor dialog if update dialog is answered 2024-07-13 18:24:47 +02:00
Jonas Kvinge
124b97c024 Turn on git revision 2024-07-10 21:58:29 +02:00
Jonas Kvinge
98e0b45403 Release 1.1.0-rc4 2024-07-10 20:07:01 +02:00
Jonas Kvinge
1f2b8d8bf6 Rename playlist filter classes 2024-07-10 18:27:17 +02:00
Jonas Kvinge
8327751b91 CollectionFilter: Optimize use of QRegularExpression
Possible fix for #1482
2024-07-09 22:06:42 +02:00
Jonas Kvinge
6417f89596 CollectionFilter: Add std::as_const 2024-07-09 18:06:46 +02:00
Jonas Kvinge
2e53656f44 Turn on git revision 2024-07-09 17:52:21 +02:00
Jonas Kvinge
822cf0ad07 Release 1.1.0-rc3 2024-07-09 16:23:10 +02:00
Jonas Kvinge
67f04a81b3 Playlist: Add data changed when setting current row 2024-07-09 16:21:09 +02:00
Jonas Kvinge
9232ad0125 TagReaderTagLib: Use QString for converting TagLib::String
Converting directly to std::string does not seem to work correctly.
2024-07-09 15:56:07 +02:00
Jonas Kvinge
0de87b3e1e TagReaderTagLib: Use UTF-8 when converting to CString
Fixes #1481
2024-07-09 15:06:07 +02:00
Jonas Kvinge
74b8cd6156 StretchHeaderView: Formatting 2024-07-09 11:35:01 +02:00
Jonas Kvinge
ac959387fe StretchHeaderView: Fix infinite loop
Fixes #1480
2024-07-09 11:14:31 +02:00
Jonas Kvinge
ffd8ce9281 Turn on git revision 2024-07-09 10:44:11 +02:00
Jonas Kvinge
d8052b295f Release 1.1.0-rc2 2024-07-09 04:39:48 +02:00
Jonas Kvinge
625929133c Rename analyzers and add turbine analyzer 2024-07-09 04:39:48 +02:00
Jonas Kvinge
79c28e7e1d sqlite_test: Remove fts5 2024-07-09 03:24:13 +02:00
Jonas Kvinge
01f4a79f07 schema: Bump schema version to 20 2024-07-09 03:19:29 +02:00
Jonas Kvinge
47c5a2215e device-schema: Remove fts5 table 2024-07-09 03:18:43 +02:00
Jonas Kvinge
bb6e38630f Turn on git revision 2024-07-09 03:18:04 +02:00
Jonas Kvinge
85acf69d4f Release 1.1.0-rc1 2024-07-09 02:49:49 +02:00
Jonas Kvinge
69ba56ebef CMake: Fix rc versioning 2024-07-09 02:37:51 +02:00
Jonas Kvinge
527a61f909 OpenTidalCoverProvider: Handle authentication required 2024-07-09 02:37:21 +02:00
Jonas Kvinge
a15ebcde24 Song: Update supported fields for ASF 2024-07-05 21:27:49 +02:00
Jonas Kvinge
e80629cc40 tagreader_test: Add more fields for ASF test 2024-07-05 21:27:24 +02:00
Jonas Kvinge
d956ae9726 TagReaderTagLib: Add consts for all fields, add more support for ASF 2024-07-05 21:26:28 +02:00
Jonas Kvinge
0435d6d7b9 TagReader: Cleanup string functions 2024-07-05 16:21:50 +02:00
Jonas Kvinge
59752e6fe9 CI: Minor adjustments 2024-07-05 00:05:18 +02:00
Jonas Kvinge
d3ee0bfdf4 CI: Use rsync for Windows builds 2024-07-05 00:01:52 +02:00
Jonas Kvinge
463df825f0 nsi: Remove libbrotlienc.dll 2024-07-04 23:21:54 +02:00
Jonas Kvinge
480559ef0b Update Changelog 2024-07-03 20:33:28 +02:00
Jonas Kvinge
2a4fd346f9 Rename QueryType to Type 2024-07-02 18:34:27 +02:00
Jonas Kvinge
6200fed224 Add missing const 2024-07-02 18:33:15 +02:00
Jonas Kvinge
e1b4585dc7 CollectionModel: Minor change in debug log 2024-07-02 17:53:14 +02:00
Jonas Kvinge
c5eb72fa9f SpotifyRequest: Use artist from albums reply 2024-07-02 17:50:55 +02:00
Jonas Kvinge
d2e19ef4c3 SpotifyRequest: Add include_groups for albums query 2024-07-02 17:50:11 +02:00
Jonas Kvinge
4509c43b81 SpotifyRequest: Check for download covers 2024-07-02 17:49:56 +02:00
Jonas Kvinge
e2ffe716e7 SpotifyRequest: Remove unused variable 2024-07-02 17:49:40 +02:00
Jonas Kvinge
0d9ededea9 tagreadermessages: Switch back to proto2 2024-07-01 17:39:42 +02:00
Jonas Kvinge
2edc4369d2 CollectionModel: Only prepend artist on album group by 2024-07-01 17:11:14 +02:00
Jonas Kvinge
32baa95500 Add better error handling for Tag reader 2024-07-01 02:06:42 +02:00
Jonas Kvinge
ad9f3ce078 playlistparsers: Rename collection_search to collection_lookup 2024-07-01 02:06:42 +02:00
Jonas Kvinge
c1f66b1885 CueParser: Remove unused variable 2024-06-29 15:24:54 +02:00
Jonas Kvinge
e0be15cf01 CMakeLists: Add QCoreApplication to SQLite test
Fixes #1471
2024-06-27 18:15:53 +02:00
Jonas Kvinge
93660bfc81 Formatting 2024-06-27 18:00:44 +02:00
Jonas Kvinge
6446942e73 Add error handling to playlist parsers 2024-06-24 20:20:49 +02:00
Jonas Kvinge
0038cf8c4e CollectionWatcher: Make sure periodic scan is stopped 2024-06-24 19:43:09 +02:00
Jonas Kvinge
7f177aef08 CollectionModel: Always separate albums by different artists
Fixes #1276
2024-06-24 19:21:24 +02:00
Jonas Kvinge
a7a42ea5ec AnalyzerContainer: Use constexpr 2024-06-22 00:48:21 +02:00
Jonas Kvinge
14cddfd42f AnalyzerContainer: Add parameter 2024-06-22 00:48:03 +02:00
Jonas Kvinge
ae0ce65674 AnalyzerContainer: Remove unused declaration 2024-06-22 00:47:52 +02:00
Jonas Kvinge
9c9926d5a7 PlaylistHeader: Cast column to int
Fixes #1468
2024-06-22 00:43:20 +02:00
Jonas Kvinge
95a3c41baa CI: Disable Spotify for MinGW and macOS 2024-06-20 23:39:42 +02:00
Jonas Kvinge
f2518baef9 nsi: Add spotify plugin for MSVC 2024-06-20 23:39:23 +02:00
Jonas Kvinge
4be9265546 PlaylistView: Use Playlist::ColumnCount 2024-06-20 23:00:33 +02:00
Jonas Kvinge
9f9c46e370 Update individual playlist columns, use enum class 2024-06-20 22:52:27 +02:00
Jonas Kvinge
5816d0bb12 About: Update contributors 2024-06-20 16:48:45 +02:00
Jonas Kvinge
70c2b99771 ContextAlbum: Delete timeline to delete previous cover
QTimeLine was holding the previous covers shared pointer in the signal/slot connection, which caused it to never be free'd even though it's removed from the previous_covers_ list.
To fix this, make sure the QTimeLine is deleted.

This fixes a huge memory leak.

Addresses issue #1464
2024-06-20 16:05:07 +02:00
Jonas Kvinge
6177d4a2c4 ContextAlbum: Use const reference for image parameter 2024-06-20 15:59:34 +02:00
Jonas Kvinge
05f012e590 ContextAlbum: Formatting 2024-06-20 15:58:24 +02:00
Jonas Kvinge
cc0506490f ContextAlbum: Use constexpr for kFadeTimeLineMs 2024-06-20 15:57:53 +02:00
Jonas Kvinge
06114c9835 ContextAlbum: Add explicit for PreviousCover 2024-06-20 15:57:12 +02:00
Jonas Kvinge
2518e4d47d ContextAlbum: Remove unused function declaration 2024-06-20 15:56:49 +02:00
Jonas Kvinge
ceea805196 main: Remove QCoreApplication::setQuitLockEnabled(false);
This was a workaround for QTBUG-124386.
2024-06-19 21:33:01 +02:00
Jonas Kvinge
ae7e515945 Update Changelog 2024-06-19 00:39:13 +02:00
Jonas Kvinge
b275f91a58 PlaylistView: Set new default column sizes 2024-06-18 19:52:51 +02:00
Jonas Kvinge
b8ef96028c StretchHeaderView: Refactor code and improve header view
Save what sections are visible, and always save sizes.
Do not set section size to zero when hiding sections.
When resizing columns in stretch mode, only resize the right column to fit the left column.

Fixes #1085
2024-06-18 19:52:34 +02:00
Jonas Kvinge
6ba1fdb744 CI: Remove openSUSE 15.5 2024-06-16 18:18:43 +02:00
Jonas Kvinge
dcef38427b CI: Remove protobuf workaround for openSUSE 2024-06-15 01:48:20 +02:00
Jonas Kvinge
20d7ae7144 CI: Fix setting ENABLE_WIN32_CONSOLE for MSVC 2024-06-15 01:20:57 +02:00
Jonas Kvinge
d576777d94 CueParser: Always set track 2024-06-14 21:19:18 +02:00
Jonas Kvinge
1f7344ca1b CueParser: Move artist / album variables
Fixes #1463
2024-06-14 21:19:04 +02:00
Jonas Kvinge
87c69f7456 CueParser: Formatting 2024-06-14 21:17:59 +02:00
Jonas Kvinge
a684b35203 ParserBase: Always read file, CUE depends on it 2024-06-14 21:03:52 +02:00
Jonas Kvinge
37855fe836 CollectionBackend: Remove QUrl::FullyDecoded from QUrl::toString() 2024-06-14 18:46:48 +02:00
Jonas Kvinge
f596695f61 CollectionModel: Don't process model updates when loading 2024-06-14 18:40:52 +02:00
Jonas Kvinge
076d065f7c nsi: Replace libxml2-2.dll with libxml2.dll 2024-06-14 00:20:33 +02:00
Jonas Kvinge
70a7a7bbdd CI: Cleanup PATH for MSVC build 2024-06-13 23:01:33 +02:00
Jonas Kvinge
5f540a4c08 Add Spotify support 2024-06-13 17:09:06 +02:00
Jonas Kvinge
f33b30fe79 OrganizeFormat: Replace QLatin1String with QStringLiteral 2024-06-13 00:40:08 +02:00
Jonas Kvinge
2f546f214d Replace QLatin1String with QStringLiteral 2024-06-12 23:51:09 +02:00
Jonas Kvinge
7ba4fda346 SnapDialog: Replace QLatin1String with QStringLiteral 2024-06-12 23:23:30 +02:00
Jonas Kvinge
299415a889 Rename "Internet" to "Streaming" 2024-06-12 22:23:05 +02:00
Jonas Kvinge
718af984ab Move LocalRedirectServer to core 2024-06-12 21:21:11 +02:00
Jonas Kvinge
5d51657f32 Drop FTS tables 2024-06-12 21:17:01 +02:00
Jonas Kvinge
a2958ba808 ListenBrainzScrobbler: Replace QLatin1String with QStringLiteral 2024-06-12 21:00:25 +02:00
Jonas Kvinge
79c2130152 ScrobblingAPI20: Replace QLatin1String with QStringLiteral 2024-06-12 20:59:09 +02:00
Jonas Kvinge
98d3cc2637 AnalyzerBase: Add static_cast 2024-06-12 20:58:51 +02:00
Jonas Kvinge
8339aa0934 CI: Remove Fedora 41 2024-06-12 20:32:34 +02:00
Jonas Kvinge
5451c110b1 Replace QStringLiteral with QLatin1String 2024-06-12 20:30:36 +02:00
Jonas Kvinge
20595a11bc SmartPlaylistSearchTermWidget: Add const 2024-06-12 18:56:21 +02:00
Jonas Kvinge
c92a1b516c GstEngine: Fix swapped media_url / stream_url 2024-06-12 18:52:53 +02:00
Jonas Kvinge
a8f1a881ff GioLister: Remove useless else 2024-06-12 18:52:33 +02:00
Jonas Kvinge
ec21a55271 CollectionModelTest: Remove unused test 2024-06-12 18:14:37 +02:00
Jonas Kvinge
89990624ec CollectionBackendTest: Use std::make_shared 2024-06-12 18:14:24 +02:00
Jonas Kvinge
6caf7f356b SubsonicRequest: Add const 2024-06-12 18:12:20 +02:00
Jonas Kvinge
241a6c5818 EditTagDialog: Initialize cover_menu_ 2024-06-12 18:12:08 +02:00
Jonas Kvinge
57fb52e8f0 Add LL 2024-06-12 18:11:43 +02:00
Jonas Kvinge
7b00385155 Udisks2Lister: Add static_cast 2024-06-12 18:11:10 +02:00
Jonas Kvinge
2b4aa1d6b2 AlbumCoverChoiceController: Add missing close 2024-06-12 18:09:59 +02:00
Jonas Kvinge
4ba5113842 Remove const 2024-06-12 18:09:23 +02:00
Jonas Kvinge
a36bf2df65 Replace QStringLiteral with QLatin1String 2024-06-12 18:08:54 +02:00
Jonas Kvinge
f5002cae36 Make static 2024-06-12 18:07:58 +02:00
Jonas Kvinge
cb8022c55d WaveRubber: use static_cast 2024-06-12 18:06:37 +02:00
Jonas Kvinge
2a65e00988 WaveRubber: Remove trailing whitespaces and fix formatting 2024-06-12 17:45:01 +02:00
Jonas Kvinge
05358cdfe4 Add default to switch 2024-06-12 17:41:17 +02:00
Jonas Kvinge
7b43a94055 CollectionBackend: Use static QMetaObject::invokeMethod 2024-06-12 17:40:08 +02:00
Jonas Kvinge
36e19e82e7 FHT: Remove void 2024-06-12 17:39:46 +02:00
Jonas Kvinge
c52a802b83 AnalyzerBase: Remove static_cast 2024-06-12 17:39:37 +02:00
Jonas Kvinge
b233600b8c Remove useless else 2024-06-12 17:38:58 +02:00
Jonas Kvinge
93df859aa4 About: Replace QLatin1String with QStringLiteral 2024-06-12 17:31:17 +02:00
Jonas Kvinge
f1f79fb961 Move default constructor to header 2024-06-12 17:30:40 +02:00
Jonas Kvinge
92fa75b6c2 gstmoodbarplugin: Remove namespace 2024-06-12 17:28:23 +02:00
Jonas Kvinge
1d5f3a0486 CI: Remove MSVC abseil workaround 2024-06-12 17:09:21 +02:00
Jonas Kvinge
b89c200076 Replace QStringLiteral with QLatin1String 2024-06-12 02:13:27 +02:00
Jonas Kvinge
597a8cd6c8 Remove QStringBuilder include 2024-06-12 00:44:48 +02:00
Jonas Kvinge
e477449cd4 Rewrite collection model and search
Fixes #392
2024-06-11 23:18:38 +02:00
Jonas Kvinge
ea1e4541c0 CI: Use MSVC protoc from debug to workaround a crash 2024-06-09 23:03:57 +02:00
Guzpido
50572ac40f WaveRubber: a simple waveform analyzer
This kinda elastic analyzer uses no transformations and is colored by a gradient based on the user QT "Highlight" palette.

WaveRubber: a kinda elastic waveform analyzer

Update src/analyzer/waverubber.cpp

better pointer declaration

Co-authored-by: Jonas Kvinge <jonas@jkvinge.net>
2024-06-09 18:01:28 +02:00
Guzpido
fd81036909 GstEnginePipeline: Divide samples and format by channels for buffer duration 2024-06-09 15:56:29 +02:00
Jonas Kvinge
0e27886e28 CI: Add Boost_INCLUDE_DIR for MSVC build 2024-06-09 14:12:38 +02:00
whatwareweb
45bad3be04 Fix integer underflow bug 2024-06-08 11:49:35 +02:00
Jonas Kvinge
30b268dc3a Remove msvc toolset 2024-06-08 00:48:43 +02:00
jj
ef99f0ef36 Add volume increment setting 2024-06-06 21:53:16 +02:00
Jonas Kvinge
e357ba0125 GstEngine: Check individual classes when parsing outputs 2024-06-04 19:43:54 +02:00
Jonas Kvinge
36b75a5928 CI: Manually codesign libraries 2024-06-03 17:13:28 +02:00
Jonas Kvinge
64d3ea2804 Udisks2Lister: Fix mountpoint 2024-06-03 17:12:23 +02:00
Jonas Kvinge
0a93affeef SongLoader: Use QObject::connect 2024-06-03 00:00:20 +02:00
Jonas Kvinge
402d13a322 CI: Bump macOS to 12 2024-06-02 12:49:05 +02:00
Jonas Kvinge
adf0efc859 CI: Remove Fedora 38 2024-06-02 12:48:23 +02:00
Jonas Kvinge
d1c65fd273 Bump default LSMinimumSystemVersion to 12.0 2024-06-02 12:30:48 +02:00
Jonas Kvinge
8a27c6a52f GstEnginePipeline: Use playbin3 with GStreamer 1.24 and higher
playbin3 is buggy with GStreamer 1.22, for some reason the bug is only reproducible on Gnome.

https://forum.strawberrymusicplayer.org/topic/1506/buffering-forever/23
2024-06-02 12:09:38 +02:00
Jonas Kvinge
d7cc52bc99 EngineBase: Use fully qualified namespace in StateChanged
Makes sure the metatype matches with Qt 5:
QObject::connect: Cannot queue arguments of type 'State'
(Make sure 'State' is registered using qRegisterMetaType().)

Fixes #1446
2024-05-30 20:09:32 +02:00
Jonas Kvinge
f0f5300891 ParserBase: Make the path absolute and try canonical path
Somehow I got this mixed up in commit 2953f9e :(

Fixes #1448
2024-05-29 00:18:39 +02:00
Jonas Kvinge
6e90e72b4a CollectionModel: Add content to fake header for pixmap cache
QNetworkCacheMetaData requires this now.
2024-05-26 02:49:26 +02:00
Jonas Kvinge
c655963483 Use checkStateChanged(Qt::CheckState) with Qt >= 6.7 2024-05-24 02:13:48 +02:00
Jonas Kvinge
9f2e4ac312 CueParser: Detect and handle different text encodings
Fixes #1429
2024-05-19 01:49:37 +02:00
Jonas Kvinge
9e25366f85 Add function for detecting text encoding 2024-05-19 01:47:44 +02:00
Jonas Kvinge
c102d8731a Require ICU 2024-05-19 01:45:19 +02:00
Jonas Kvinge
0983ba1339 MoodbarLoader: Add header name for disk cache 2024-05-13 00:44:37 +02:00
dependabot[bot]
0a99eca7cd Bump apple-actions/import-codesign-certs from 2 to 3
Bumps [apple-actions/import-codesign-certs](https://github.com/apple-actions/import-codesign-certs) from 2 to 3.
- [Release notes](https://github.com/apple-actions/import-codesign-certs/releases)
- [Commits](https://github.com/apple-actions/import-codesign-certs/compare/v2...v3)

---
updated-dependencies:
- dependency-name: apple-actions/import-codesign-certs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 00:27:33 +02:00
Jonas Kvinge
116bbec73e Replace qPrintable with qUtf8Printable
Fixes #1440
2024-05-13 00:05:58 +02:00
Jonas Kvinge
bf19540f8d Set LSMinimumSystemVersion from MACOSX_DEPLOYMENT_TARGET
Fixes #1436
2024-05-12 23:10:43 +02:00
Jonas Kvinge
dff3ae7410 CI: Remove --skip-broken from dnf for Fedora 2024-05-12 21:41:38 +02:00
Jonas Kvinge
76614bcde0 Only apply collection directories changes on save 2024-05-12 21:40:51 +02:00
Jonas Kvinge
2953f9eefc ParserBase: Use original paths 2024-05-12 21:38:59 +02:00
Jonas Kvinge
4a24605361 FileViewList: Use original paths instead of canonical paths 2024-05-12 21:38:15 +02:00
Jonas Kvinge
decabe8d47 CommandlineOptions: Use original paths instead of canonical paths 2024-05-12 21:37:54 +02:00
Jonas Kvinge
51adcf0f1e MainWindow: Use original paths instead of canonical paths 2024-05-12 21:37:32 +02:00
Jonas Kvinge
2a6a07fef6 CollectionBackendTest: Remove use of QFileInfo::canonicalFilePath 2024-05-12 21:36:52 +02:00
Jonas Kvinge
315cf63118 CI: Remove Windows 32 bit 2024-05-11 23:23:56 +02:00
Wedone
e28d362aad Update fr.po 2024-05-04 16:22:51 +02:00
Jonas Kvinge
e0d9b8f715 ResizableTextEdit: Remove tab 2024-05-04 16:21:59 +02:00
Jonas Kvinge
eff6b75c43 nsi: Remove /SOLID from SetCompressor for debug 2024-04-24 02:25:05 +02:00
Robert Gingras
e8be0adf37 TagReaderTagLib: Remove redundant ID3v2 validity check
TagLib will have created a valid ID3v2 tag on this file by this point in the code, due to the way it handles the tag() method for WAV::File. Thus the null pointer check is redundant and the hasID3v2() call is at best redundant and at worst will cause tags to not save when they otherwise should have
2024-04-24 01:23:51 +02:00
Robert Gingras
9f4a82bb62 TagReaderTagLib: Remove file_mpeg argument from the SetEmbeddedArt ID3v2 overload 2024-04-24 01:23:51 +02:00
Robert Gingras
8d7e14f21d Song: Added WAV to list of supported filetypes 2024-04-24 01:23:51 +02:00
Robert Gingras
d03d3622aa TagReaderTagLib: Have RIFF WAV files save ID3v2 tags, when applicable 2024-04-24 01:23:51 +02:00
Robert Gingras
821c32992d TagReaderTagLib: Refactor ID3v2 saving to a dedicated function 2024-04-24 01:23:51 +02:00
Robert Gingras
b52cf9f3cd TagReaderTagLib: Reposition ParseID3v2Tag 2024-04-24 01:23:51 +02:00
Robert Gingras
ab8e687f96 TagReaderTagLib: Add id3v2 parsing for RIFF WAV files 2024-04-24 01:23:51 +02:00
Robert Gingras
90703703aa TagReaderTagLib: Make id3v2 parsing reusable 2024-04-24 01:23:51 +02:00
Jonas Kvinge
176984afe0 RadioPlaylistItem: Set correct col position for InitFromQuery
Fixes #1430
2024-04-23 22:34:00 +02:00
Jonas Kvinge
542efa17ff Fix position for song and internet playlist items
Fixes #1430
2024-04-23 20:18:28 +02:00
Jonas Kvinge
6a2c2dbba1 Song: Fix loading length 2024-04-23 20:16:41 +02:00
Jonas Kvinge
426de61525 Add const and std::as_const 2024-04-23 17:15:42 +02:00
Jonas Kvinge
24c8d06d41 SongPlaylistItem: Use static_cast 2024-04-23 17:00:10 +02:00
Jonas Kvinge
227f5e5176 Replace QStringLiteral with QLatin1String 2024-04-23 16:57:49 +02:00
Jonas Kvinge
92e39a3e21 GlobalShortcut: Use optional 2024-04-23 16:54:54 +02:00
Jonas Kvinge
fb2300e2fa EBUR128State: Add missing const reference 2024-04-23 16:54:22 +02:00
Jonas Kvinge
78becae5e9 AlbumCoverLoader: Use fully qualified namespace in slot 2024-04-23 16:53:40 +02:00
Jonas Kvinge
d10eb4370e Player: Use chrono_literals 2024-04-23 16:53:12 +02:00
Jonas Kvinge
9c92ef941f CollectionModel: Remove redundant const_cast 2024-04-23 16:52:17 +02:00
Jonas Kvinge
7aefe3d71b Change QList<QString> to QStringList 2024-04-23 16:51:42 +02:00
Jonas Kvinge
398db964b8 Use QDateTime::currentSecsSinceEpoch 2024-04-23 16:48:51 +02:00
Jonas Kvinge
579349b104 ResizableTextEdit: Add Q_OBJECT macro 2024-04-23 16:44:44 +02:00
Jonas Kvinge
5f9a83871d Playlist: Cast to int 2024-04-23 02:45:27 +02:00
Jonas Kvinge
da0c5e67c5 Playlist: Use optional::has_value 2024-04-23 02:45:11 +02:00
Jonas Kvinge
a0cfde18c3 PlaylistSequence: Use constexpr 2024-04-23 02:44:48 +02:00
Jonas Kvinge
0ad4889936 PlaylistSequence: Remove unused member variable 2024-04-23 02:44:36 +02:00
Jonas Kvinge
36e809d530 InternetPlaylistItem: Cast to int 2024-04-23 02:18:24 +02:00
Jonas Kvinge
78096658e2 Chromaprinter: Remove useless cast 2024-04-23 02:18:08 +02:00
Jonas Kvinge
3edd218e3a Remove redundant casts 2024-04-23 01:58:08 +02:00
Jonas Kvinge
569bf6335b CollectionView: Add action_search_for_this_ to initialization list 2024-04-23 01:57:16 +02:00
Jonas Kvinge
7b8919d706 CommandlineOptions: Add const 2024-04-23 01:56:55 +02:00
Jonas Kvinge
147fd87d8c MainWindow: Only pass progress to UpdateTaskbarProgress 2024-04-23 01:56:30 +02:00
Jonas Kvinge
ac0926d40b Song: Add ColumnIndex helper function 2024-04-23 01:55:57 +02:00
Jonas Kvinge
105d472f5d README: Fix badge 2024-04-21 22:35:15 +02:00
Jonas Kvinge
c1a49da385 tests: Use QStringLiteral 2024-04-21 19:37:39 +02:00
Jonas Kvinge
c3f596e64e CI: Add devel-tools-building repo for Leap 2024-04-21 17:03:06 +02:00
Jonas Kvinge
adfda84c41 nsi: Bump icu from 74 to 75 2024-04-21 16:44:07 +02:00
Jonas Kvinge
345cc118a3 CI: Use different mirror for Mageia 2024-04-21 16:10:26 +02:00
Jonas Kvinge
df070ac0cf Optimize Song::InitFromQuery
Use `QSqlQuery::value(int)` or `QSqlRecord::value(int)` instead of `QSqlQuery::value(QString)`.
Make `SqlRow` use `QSqlRecord` directly instead iterating over all columns.
2024-04-21 15:42:29 +02:00
Jonas Kvinge
7b88be2635 CollectionModel: Only set grouping if it's selected 2024-04-21 15:42:29 +02:00
Kientz Arnaud
c30a39d29a Fix infinitive in french translations 2024-04-16 00:24:59 +02:00
Jonas Kvinge
36db41a1f0 Add sidebar background 2024-04-13 23:47:48 +02:00
Jonas Kvinge
8b249dc06a QSearchField: Remove NSSearchField workaround
This was a workaround for QTBUG-124160.
2024-04-13 05:39:19 +02:00
Jonas Kvinge
0c6872b352 Disable automatic conversions from 8-bit strings 2024-04-13 05:05:33 +02:00
Jonas Kvinge
58944993b8 Use QStringLiteral 2024-04-09 23:20:26 +02:00
Olivier HUMBERT
3cfffa5fbb Adds French to the menu item 2024-04-07 19:26:23 +02:00
Jonas Kvinge
4873b8b413 CI: Move OpenMandriva if false 2024-04-06 21:24:19 +02:00
Jonas Kvinge
0b85f5192c CI: Enable Mageia 2024-04-06 21:19:03 +02:00
Jonas Kvinge
3e9a1776a1 CI: Run upload and attach independent of failure 2024-04-06 21:10:10 +02:00
Jonas Kvinge
e1fbe9ae54 Resolve song from collection using track with Cue in XSPF
Fixes #1181
2024-04-04 22:22:02 +02:00
Jonas Kvinge
f48d1a8017 OrganizeDialog: Don't save settings unless button is pressed
Fixes #1404
2024-04-04 21:43:57 +02:00
rimasx
0debc90695 Create estonian translation
I hope this is OK. Strawberry compiled without any problems and Estonian was automatically selected. I mostly used the Clementine translation, which I tweaked a bit. Many thanks to the Clementine translators, especially Priit Jõerüüt.
2024-04-04 21:29:48 +02:00
Jonas Kvinge
8d42ea7cfd README: Remove link to zanata 2024-04-04 21:18:57 +02:00
Jonas Kvinge
1dae80a633 Add scrobbler option for stripping "remastered" etc
Fixes #1387
2024-04-04 21:17:07 +02:00
Jonas Kvinge
d398c86b0c Make showing song progress on taskbar optional 2024-04-04 16:49:53 +02:00
Jonas Kvinge
70809e0647 MainWindow: Add error dialog when file deletion fails
Fixes #1384
2024-04-03 21:37:20 +02:00
Jonas Kvinge
4c1a5168f0 CollectionModel: Reset model before deletion 2024-04-03 21:17:20 +02:00
Jonas Kvinge
f9acfbc224 SimpleTreeModel: Handle null root 2024-04-03 21:17:20 +02:00
Jonas Kvinge
5f78e1a983 MergedProxyModel: Fix beginRemoveRows first
Fixes #1314
2024-04-03 21:17:06 +02:00
Jonas Kvinge
7bc5579fb7 Song: Check that filetype is supported for writing tags
Fixes #1413
2024-04-03 20:45:52 +02:00
Jonas Kvinge
57750efcb2 CI: Remove openSUSE Leap 15.5
Protobuf is broken
2024-04-03 20:09:07 +02:00
Jonas Kvinge
a33ee1cda9 CI: Use GCC 13 for Leap 2024-04-03 20:08:05 +02:00
Jonas Kvinge
cd20a0679a CI: Replace qwindowsvistastyle with qmodernwindowsstyle 2024-04-02 17:37:58 +02:00
Jonas Kvinge
20e546e02b nsi: Replace qwindowsvistastyle with qmodernwindowsstyle 2024-04-02 17:37:38 +02:00
Jonas Kvinge
f5547f093e Player: Use timer for saving volume
Fixes #1272
2024-04-02 17:16:29 +02:00
Jonas Kvinge
c00d95242d Utilities: Handle missing XDG_DATA_DIRS variable 2024-04-02 16:39:48 +02:00
Jonas Kvinge
05c4d23df6 Utilities: Remove --new-window parameter from dolphin
Fixes #1412
2024-04-02 00:48:29 +02:00
Jonas Kvinge
06fa17f33f CI: Run apt upgrade for debian/ubuntu 2024-04-01 00:38:21 +02:00
Jonas Kvinge
194285289c Update Changelog 2024-03-31 22:56:46 +02:00
Jonas Kvinge
a61fa61330 CI: Disable OpenMandriva 2024-03-31 01:49:30 +01:00
Jonas Kvinge
68c922ee12 SmartPlaylistWizard: Set classic style if using fusion on Windows
Workaround a Qt bug.

Fixes #1399
2024-03-29 02:54:25 +01:00
Jonas Kvinge
d1042b276b GstEnginePipeline: Set volume_set_ to false in ElementRemovedCallback 2024-03-24 19:44:47 +01:00
Jonas Kvinge
9bbffe150f GstEnginePipeline: Add back volume sync for auto
We need to remove the volume sync when the element is deleted on "deep-element-removed", then re-add it on the next "deep-element-added" that isn't a fakesink.

Fixes #1123
2024-03-24 19:36:32 +01:00
Jonas Kvinge
b95be526d3 HtmlLyricsProvider: Use QNetworkRequest::UserAgentHeader 2024-03-24 07:04:07 +01:00
Jonas Kvinge
165f9d769b MusixmatchCoverProvider: Fix parsing 2024-03-24 06:59:01 +01:00
Jonas Kvinge
a0ea75b74e NetworkAccessManager: Use QNetworkRequest::setHeader 2024-03-24 06:58:33 +01:00
Jonas Kvinge
4075f92eec OpenTidalCoverProvider: Adjust settings 2024-03-24 05:27:43 +01:00
Jonas Kvinge
035aff5454 Add Open Tidal cover provider 2024-03-24 05:23:35 +01:00
Jonas Kvinge
52dc7ad259 CI: Add Fedora 41 2024-03-23 14:18:17 +01:00
Jonas Kvinge
c3c83f608c CI: Add Leap 15.6 and remove 15.4 2024-03-23 14:05:11 +01:00
Strawbs Bot
ffba351a16 Update translations 2024-03-23 01:43:24 +01:00
Jonas Kvinge
a12623e142 Update Changelog 2024-03-23 01:04:29 +01:00
Jonas Kvinge
1a691a103e Fix Qt 5 and mpris2 build errors 2024-03-22 20:26:13 +01:00
Jonas Kvinge
5e725e0bbe Fix playlist shuffle
- Shuffle all indexes
- Use persistent indexes to store play history
- Update virtual items to keep original shuffle order when the playlist is reordered
- Make sure to always set virtual index on manual shuffle
- Ignore repeat and shuffle when dynamic playlist is activated

Fixes #707
Fixes #1381
Fixes #1366
Fixes #1353
2024-03-22 20:00:12 +01:00
Jonas Kvinge
93c2fa4c73 MusixmatchLyricsProvider: Parse metadata 2024-03-17 23:41:05 +01:00
Jonas Kvinge
f412fb21d6 SettingsPage: Double spinboxes are double, not int 2024-03-17 21:56:39 +01:00
Jonas Kvinge
bd4b6c1f01 main: Add QCoreApplication::setQuitLockEnabled(false);
Fixes #1401
2024-03-17 21:46:15 +01:00
Jonas Kvinge
d1839d87e7 MusixmatchLyricsProvider: Fix parsing lyrics 2024-03-16 22:46:45 +01:00
Jonas Kvinge
1fc163eb5f Playlist: Comments formatting 2024-03-13 21:59:17 +01:00
Reverier-Xu
cd2b3cb73e mpris2: Fix mpris:trackid type with Plasma 6 2024-03-13 21:31:25 +01:00
Reverier-Xu
88b5cf2461 mpris2: Fix mpris:trackid type with Plasma 6 2024-03-13 21:31:25 +01:00
Jonas Kvinge
2ccb0af75e Song: Only include mpris when built with DBUS 2024-03-13 18:15:51 +01:00
Célestin Matte
27ee6e7643 EditTagDialog: Add button to fetch lyrics
Co-Authored-By: Jonas Kvinge <jonas@jkvinge.net>
2024-03-13 17:54:19 +01:00
Jonas Kvinge
a3207a5703 macgstcopy: Update gstreamer plugins list 2024-03-12 18:25:06 +01:00
Jonas Kvinge
72ff64a7f8 nsi: Remove unused gstreamer plugins 2024-03-12 16:29:48 +01:00
Jonas Kvinge
9c06d1d0ae nsi: Add gstreamer dsd plugin 2024-03-12 00:52:49 +01:00
Jonas Kvinge
f11afd4414 GstEnginePipeline: Add default to switch 2024-03-12 00:40:11 +01:00
Sami Boukortt
2aa70b6ab8 Add an option not to skip “The” when sorting artist names 2024-03-11 23:34:42 +01:00
Jonas Kvinge
4626a6f609 GstEnginePipeline: Use playbin3 with gstreamer >= 1.22 2024-03-08 18:52:22 +01:00
Jonas Kvinge
9152f8559f Song: Split remastered and explicit regex 2024-03-03 01:50:05 +01:00
Jonas Kvinge
7f4c61b15a Improve album and title disc, remastered, etc matching
Don't partial remove things like "(Mono / Remastered)".

Fixes #1387
2024-03-02 19:48:19 +01:00
Jonas Kvinge
b365131363 Playlist: Remove veto listeners
We have never used this, it's basically dead code.
2024-02-28 23:00:24 +01:00
Jonas Kvinge
a6ea4dd0d7 Remove unused includes 2024-02-28 21:37:14 +01:00
Jonas Kvinge
9c6649f077 Add letras lyrics provider 2024-02-28 21:33:30 +01:00
Jonas Kvinge
04ba202e12 HtmlLyricsProvider: Use browser-like user-agent 2024-02-25 04:32:09 +01:00
Jonas Kvinge
352a6c5691 Remove lyricsmode.com provider
They have a "Verifying you are human" thing now.
2024-02-25 04:23:04 +01:00
Jonas Kvinge
72bccad82d Add accessible name for QToolButton css
Make sure it does not apply to other buttons.

Fixes #1255
2024-02-25 02:50:40 +01:00
Jonas Kvinge
6d52a2b409 QSearchField: Replace QToolButton with QPushButton 2024-02-25 02:46:51 +01:00
Jonas Kvinge
9b1035a5f2 Translations: Fix generating pot with NMake
Using GETTEXT_XGETTEXT_EXECUTABLE causes issues with NMake, possibly because of spaces in path.
2024-02-24 19:45:58 +01:00
Jonas Kvinge
ce7c3e8039 CI: Use gh instead of hub 2024-02-24 01:18:02 +01:00
Jonas Kvinge
9baec288c3 CI: Set FFTW3_DIR 2024-02-23 22:57:00 +01:00
Jonas Kvinge
82894b94f4 CI: Set vsversion to 2022 2024-02-23 21:59:06 +01:00
Jonas Kvinge
fb00d68aa7 Update Changelog 2024-02-20 22:47:55 +01:00
Jonas Kvinge
12288a2622 BackendSettingsPage: Fix enabling/disabling exclusive mode 2024-02-20 01:22:40 +01:00
Jonas Kvinge
f84ce3f1d1 Add exclusive mode option for WASAPI 2024-02-20 01:08:00 +01:00
Jonas Kvinge
306b3f72d8 SettingsPage: Pass on scroll event to page
If the settings widget does not have focus, pass the event to the page for scrolling down instead of changing the setting.

Fixes #1380
2024-02-19 23:07:27 +01:00
buckmelanoma
593a04d047 InternetSearchView: Use DescriptionForSource instead of TextForSource
DescriptionForSource provides uppercase names in the filter menu instead of the lowercase names provided by TextForSource
2024-02-18 20:14:01 +01:00
buckmelanoma
667548f3ed InternetSongsView: Use DescriptionForSource instead of TextForSource
DescriptionForSource provides uppercase names in the filter menu instead of the lowercase names provided by TextForSource
2024-02-18 20:14:01 +01:00
Jonas Kvinge
8f89bf6402 Replace tabs with spaces 2024-02-18 14:24:20 +01:00
Jonas Kvinge
ff28e7c86e Add ASIO device finder 2024-02-17 00:40:55 +01:00
Jonas Kvinge
67cc69179b nsi: Update gstreamer plugins 2024-02-16 21:51:45 +01:00
Jonas Kvinge
06fac2b7a3 CI: Copy all gstreamer plugins for mingw and msvc 2024-02-16 21:51:45 +01:00
Jonas Kvinge
a354f6bdc5 GstEnginePipeline: Set device-clsid 2024-02-16 21:38:33 +01:00
Jonas Kvinge
cb44c71733 DirectSoundDeviceFinder: Add waveformsink 2024-02-16 21:38:08 +01:00
Jonas Kvinge
6b1c14f875 GstEngine: Make sure asiosink is detected 2024-02-16 00:29:17 +01:00
Jonas Kvinge
7770aba877 GstEngine: Add pipewiresink 2024-02-14 18:46:23 +01:00
Jonas Kvinge
05381096aa RadioParadiseService: Use API to receive streams 2024-02-12 16:57:51 +01:00
Jonas Kvinge
6bdd9ad4dd GstEnginePipeline: Only hard-code playbin3 with gst 1.22 2024-02-11 23:52:22 +01:00
Jonas Kvinge
19836e8898 CI: Fix variable typo 2024-02-11 12:24:29 +01:00
Jonas Kvinge
5e4b193260 CI: Fix greping variable 2024-02-11 12:17:04 +01:00
Jonas Kvinge
923d0f2b7a CI: Use common SSH upload job 2024-02-11 12:12:54 +01:00
Jonas Kvinge
56f1a93c4e CI: Fix tag name variable typo 2024-02-11 01:19:39 +01:00
Jonas Kvinge
0168182af5 CddaDevice: Add missing override 2024-02-11 00:14:19 +01:00
Jonas Kvinge
679f0e1cd8 CI: Trigger on release 2024-02-11 00:10:36 +01:00
Adam Hill
dd6b9bb38d MainWindow: Add function to display progress on taskbar 2024-02-09 21:48:12 +01:00
Jonas Kvinge
5bd8f35dc0 Update Changelog 2024-02-07 01:46:12 +01:00
Jonas Kvinge
53fc939e35 ScrobblingAPI20: Ignore permission related error
Last.fm returns permission denied error when servers are overloaded, ignore this error instead.

Fixes #442
2024-02-07 01:34:46 +01:00
Strawbs Bot
42d6c79710 Update translations 2024-02-01 21:43:21 +01:00
Jonas Kvinge
882869255b CI: Remove Ubuntu Lunar 2024-01-30 18:42:32 +01:00
Jonas Kvinge
19903751c1 CI: Codesign png.framework for macOS private 2024-01-27 17:39:26 +01:00
Jonas Kvinge
836074fb60 CI: Manually codesign png framework 2024-01-26 18:58:10 +01:00
Jonas Kvinge
5865a70c2e CI: Add --skip-broken to dnf install for Fedora 2024-01-26 00:29:25 +01:00
Jacob Henner
fa057ee9d3 CollectionView: Add "search for this"
Add selection search in the collection view, to allow users to quickly
search for similarly-named songs, artists, albums, etc.
2024-01-25 19:16:14 +01:00
Jonas Kvinge
2d88fcb249 CI: Add Ubuntu Noble 2024-01-24 23:05:46 +01:00
Jonas Kvinge
920bb04b00 CMakeLists: Find TagLib using CMake 2024-01-24 21:30:10 +01:00
Jonas Kvinge
a915e62e2c GPodDevice: Fix error message 2024-01-24 19:52:24 +01:00
Jonas Kvinge
226a6c50e0 Add better error messages for device and organize
Fixes #1364
2024-01-24 19:27:30 +01:00
Jonas Kvinge
269f13de76 MtpLoader: Allow empty artist 2024-01-24 19:21:02 +01:00
Jonas Kvinge
3ee796a663 macgstcopy: Add adaptivedemux2 and mpeg mux/demux 2024-01-22 19:26:21 +01:00
Jonas Kvinge
d24af2f4db nsi: Add adaptivedemux2 and mpeg demux/mux plugins 2024-01-22 16:53:43 +01:00
Enrique Garcia
f86c3cfd91 SubsonicSettingsPage: Add recommended to MD5 authentication 2024-01-21 04:25:31 +01:00
William Andrea
11061bdd07 CONTRIBUTING: Clarify "Commit messages" section.
Specifically:
- Clarify what "class" is referring to
- Use an actual example, which requires moving out two bits of explanation
2024-01-19 23:43:44 +01:00
Jonas Kvinge
f901f802bb CollectionView: Implement add to playlist with enter
Fixes #1360
2024-01-19 23:04:03 +01:00
Jonas Kvinge
a98c209101 AutoExpandingTreeView: Remove doubleClicked on enter 2024-01-19 23:03:37 +01:00
Jonas Kvinge
6a459682ca Playlist: Check for valid index
Fixes #1359
2024-01-19 22:37:14 +01:00
Jonas Kvinge
1a7194b3a9 Turn on git revision 2024-01-11 22:32:36 +01:00
Jonas Kvinge
b6c9ef4a15 Release 1.0.23 2024-01-11 20:44:55 +01:00
Jonas Kvinge
20e550bc7d Update Changelog 2024-01-11 17:40:09 +01:00
Jonas Kvinge
a4b7766947 DeviceManager: Add nullptr check for connected device
Possible fix for #1313
2024-01-11 17:03:00 +01:00
Jonas Kvinge
8d1a0071b6 Playlist: Use effective original year for sorting
Fixes #1349
2024-01-11 16:40:12 +01:00
Jonas Kvinge
a3c00e607b README: Update sponsoring info 2024-01-09 18:08:40 +01:00
Jonas Kvinge
de7ca8b736 CI: Enable OpenMandriva 2024-01-04 03:05:44 +01:00
Jonas Kvinge
04e593dc62 CollectionWatcher: Add unavailable song restored logging 2024-01-03 00:45:30 +01:00
Jonas Kvinge
2294c38aa9 CollectionBackend: Rename SqlQuery variable 2024-01-03 00:44:54 +01:00
Jonas Kvinge
6f41d39a9c CI: Remove Mageia from upload release 2024-01-02 23:47:15 +01:00
Jonas Kvinge
7c4e33b676 GstEngine: Treat all stream errors as non-fatal
Fixes #1347
2024-01-02 19:54:19 +01:00
Jonas Kvinge
4cc66bccad CI: Disable Mageia and OpenMandriva
OpenMandriva Cooker has package conflict issues. Mageia 8 is end of life and there is no docker image for Mageia 9 yet.
2024-01-02 19:49:10 +01:00
Jonas Kvinge
55b1d34f48 CI: Use unique artifact names 2024-01-01 06:56:53 +01:00
dependabot[bot]
4da0e8d8ad Bump actions/download-artifact from 3 to 4
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 04:48:34 +01:00
dependabot[bot]
b95bfba676 Bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 04:48:19 +01:00
Jonas Kvinge
1ff2bfd390 Organize: Only update song path for collection songs
Possible fix for #1341
2023-12-28 23:30:07 +01:00
Jonas Kvinge
a35fa5b158 Require KDSingleApplication 1.1.0 2023-12-26 23:30:25 +01:00
Strawbs Bot
22169bda0d Update translations 2023-12-13 23:59:26 +01:00
Jonas Kvinge
faf5f6c69d CI: Remove Fedora 37 and add 40 2023-12-09 23:51:07 +01:00
Jonas Kvinge
1ae01d4078 Turn on git revision 2023-12-09 23:50:14 +01:00
1095 changed files with 247175 additions and 169006 deletions

File diff suppressed because it is too large Load Diff

132
.gitignore vendored
View File

@@ -1,120 +1,16 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
# Build
build/
bin/
# CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile*
Testing
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.so.*
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Dump files
*.core
*.stackdump
# Qt
*build-*
moc_*.cpp
moc_*.h
qrc_*.cpp
ui_*.h
*.moc
*.qm
# Temporary files
*~
*.autosave
*.orig
*.rej
.*.kate-swp
.swp.*
.*.swp
*.flc
# Directory files
.directory
.DS_Store
Thumbs.db
# MinGW generated files
*.Debug
*.Release
# Package files
*.spec
*.nsi
*.plist
# Stuff in dist
maketarball.sh
changelog
# Translations
translations.pot
zanata.xml
.zanata-cache/
# QtCreator
CMakeLists.txt.user*
*.pro.user
*.pro.user.*
*creator.user*
target_wrapper.*
compile_commands.json
*.kdev4
*.vscode
*.code-workspace
*.sublime-workspace
# MSVC
CMakeSettings.json
/build
/bin
/CMakeLists.txt.user
/.kdev4
/strawberry.kdev4
/.vscode
/.code-workspace
/.sublime-workspace
/.idea
/.vs
/out
# CLion
/.idea
/CMakeSettings.json
/dist/scripts/maketarball.sh
/dist/unix/strawberry.spec
/debian/changelog
/dist/macos/Info.plist

16
3rdparty/README.md vendored
View File

@@ -2,20 +2,10 @@
============================================
KDSingleApplication
-----------------
This is a small static library used by Strawberry to prevent it from starting twice per user session.
-------------------
A small library used by Strawberry to prevent it from starting twice per user session.
If the user tries to start strawberry twice, the main window will maximize instead of starting another instance.
It is also used to pass command-line options through to the first instance.
This 3rdparty copy is used only if KDSingleApplication 1.1 or higher is not found on the system.
URL: https://github.com/KDAB/KDSingleApplication/
SPMediaKeyTap
-------------
Used on macOS to exclusively enable strawberry to grab global media shortcuts.
Can safely be deleted on other platforms.
getopt
------
getopt included only when compiling on Windows.

View File

@@ -1,11 +0,0 @@
set(SPMEDIAKEY-SOURCES
SPMediaKeyTap.m
)
set(SPMEDIAKEY-HEADERS
SPMediaKeyTap.h
)
ADD_LIBRARY(SPMediaKeyTap STATIC
${SPMEDIAKEY-SOURCES}
)

View File

@@ -1,8 +0,0 @@
Copyright (c) 2011, Joachim Bengtsson
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,12 +0,0 @@
SPMediaKeyTap
=============
`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple is not exposing any APIs allowing third-parties to join in on this collaboration.
For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon.
In `Example/SPMediaKeyTapExampleAppDelegate.m` is an example of both how you use `SPMediaKeyTap`, and how you handle the semi-private `NSEvent` subtypes involved in media keys, including on how to fall back to non-event tap handling of these events.
`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active.
`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4.

View File

@@ -1,53 +0,0 @@
/*
Copyright (c) 2011, Joachim Bengtsson
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Neither the name of the organization nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Cocoa/Cocoa.h>
#import <IOKit/hidsystem/ev_keymap.h>
// http://overooped.com/post/2593597587/mediakeys
#define SPSystemDefinedEventMediaKeys 8
@interface SPMediaKeyTap : NSObject
- (id)initWithDelegate:(id)delegate;
+ (BOOL)usesGlobalMediaKeyTap;
- (BOOL)startWatchingMediaKeys;
- (void)stopWatchingMediaKeys;
- (void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
@end
@interface NSObject (SPMediaKeyTapDelegate)
- (void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
@end
#ifdef __cplusplus
extern "C" {
#endif
extern NSString *kIgnoreMediaKeysDefaultsKey;
#ifdef __cplusplus
}
#endif

View File

@@ -1,361 +0,0 @@
/*
Copyright (c) 2011, Joachim Bengtsson
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Neither the name of the organization nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Copyright (c) 2010 Spotify AB
#import "SPMediaKeyTap.h"
// Define to enable app list debug output
// #define DEBUG_SPMEDIAKEY_APPLIST 1
NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
@interface SPMediaKeyTap () {
CFMachPortRef _eventPort;
CFRunLoopSourceRef _eventPortSource;
CFRunLoopRef _tapThreadRL;
NSThread *_tapThread;
BOOL _shouldInterceptMediaKeyEvents;
id _delegate;
// The app that is frontmost in this list owns media keys
NSMutableArray<NSRunningApplication *> *_mediaKeyAppList;
}
- (BOOL)shouldInterceptMediaKeyEvents;
- (void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
- (void)startWatchingAppSwitching;
- (void)stopWatchingAppSwitching;
- (void)eventTapThread;
@end
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
// Inspired by http://gist.github.com/546311
@implementation SPMediaKeyTap
#pragma mark -
#pragma mark Setup and teardown
- (id)initWithDelegate:(id)delegate
{
self = [super init];
if (self) {
_delegate = delegate;
[self startWatchingAppSwitching];
_mediaKeyAppList = [NSMutableArray new];
}
return self;
}
- (void)dealloc
{
[self stopWatchingMediaKeys];
[self stopWatchingAppSwitching];
[super dealloc];
}
- (void)startWatchingAppSwitching
{
// Listen to "app switched" event, so that we don't intercept media keys if we
// weren't the last "media key listening" app to be active
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(frontmostAppChanged:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(appTerminated:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
}
- (void)stopWatchingAppSwitching
{
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
}
- (BOOL)startWatchingMediaKeys
{
// Prevent having multiple mediaKeys threads
[self stopWatchingMediaKeys];
[self setShouldInterceptMediaKeyEvents:YES];
// Add an event tap to intercept the system defined media key events
_eventPort = CGEventTapCreate(kCGSessionEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(NX_SYSDEFINED),
tapEventCallback,
(__bridge void * __nullable)(self));
// Can be NULL if the app has no accessibility access permission
if (_eventPort == NULL)
return NO;
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
assert(_eventPortSource != NULL);
if (_eventPortSource == NULL)
return NO;
// Let's do this in a separate thread so that a slow app doesn't lag the event tap
_tapThread = [[NSThread alloc] initWithTarget:self
selector:@selector(eventTapThread)
object:nil];
[_tapThread start];
return YES;
}
- (void)stopWatchingMediaKeys
{
// Shut down tap thread
if(_tapThreadRL){
CFRunLoopStop(_tapThreadRL);
_tapThreadRL = nil;
}
// Remove tap port
if(_eventPort){
CFMachPortInvalidate(_eventPort);
CFRelease(_eventPort);
_eventPort = nil;
}
// Remove tap source
if(_eventPortSource){
CFRelease(_eventPortSource);
_eventPortSource = nil;
}
}
#pragma mark -
#pragma mark Accessors
+ (BOOL)usesGlobalMediaKeyTap
{
#ifdef _DEBUG
// breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot
return NO;
#else
// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
return
![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey]
&& floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
#endif
}
+ (NSArray*)mediaKeyUserBundleIdentifiers
{
return [NSArray arrayWithObjects:
[[NSBundle mainBundle] bundleIdentifier], // your app
@"com.spotify.client",
@"com.apple.iTunes",
@"com.apple.QuickTimePlayerX",
@"com.apple.quicktimeplayer",
@"com.apple.iWork.Keynote",
@"com.apple.iPhoto",
@"org.videolan.vlc",
@"com.apple.Aperture",
@"com.plexsquared.Plex",
@"com.soundcloud.desktop",
@"org.niltsh.MPlayerX",
@"com.ilabs.PandorasHelper",
@"com.mahasoftware.pandabar",
@"com.bitcartel.pandorajam",
@"org.clementine-player.clementine",
@"fm.last.Last.fm",
@"fm.last.Scrobbler",
@"com.beatport.BeatportPro",
@"com.Timenut.SongKey",
@"com.macromedia.fireworks", // the tap messes up their mouse input
@"at.justp.Theremin",
@"ru.ya.themblsha.YandexMusic",
@"com.jriver.MediaCenter18",
@"com.jriver.MediaCenter19",
@"com.jriver.MediaCenter20",
@"co.rackit.mate",
@"com.ttitt.b-music",
@"com.beardedspice.BeardedSpice",
@"com.plug.Plug",
@"com.plug.Plug2",
@"com.netease.163music",
@"org.quodlibet.quodlibet",
nil
];
}
- (BOOL)shouldInterceptMediaKeyEvents
{
BOOL shouldIntercept = NO;
@synchronized(self) {
shouldIntercept = _shouldInterceptMediaKeyEvents;
}
return shouldIntercept;
}
- (void)pauseTapOnTapThread:(NSNumber *)yeahno
{
CGEventTapEnable(self->_eventPort, [yeahno boolValue]);
}
- (void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting
{
BOOL oldSetting;
@synchronized(self) {
oldSetting = _shouldInterceptMediaKeyEvents;
_shouldInterceptMediaKeyEvents = newSetting;
}
if(_tapThreadRL && oldSetting != newSetting) {
[self performSelector:@selector(pauseTapOnTapThread:)
onThread:_tapThread
withObject:@(newSetting)
waitUntilDone:NO];
}
}
#pragma mark -
#pragma mark Event tap callbacks
// Note: method called on background thread
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
#pragma unused(proxy)
SPMediaKeyTap *self = (__bridge SPMediaKeyTap *)refcon;
if(type == kCGEventTapDisabledByTimeout) {
NSLog(@"Media key event tap was disabled by timeout");
CGEventTapEnable(self->_eventPort, TRUE);
return event;
} else if(type == kCGEventTapDisabledByUserInput) {
// Was disabled manually by -[pauseTapOnTapThread]
return event;
}
NSEvent *nsEvent = nil;
@try {
nsEvent = [NSEvent eventWithCGEvent:event];
}
@catch (NSException * e) {
NSLog(@"Strange CGEventType: %d: %@", type, e);
assert(0);
return event;
}
if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys)
return event;
int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16);
if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND && keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_NEXT)
return event;
if (![self shouldInterceptMediaKeyEvents])
return event;
[self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
return NULL;
}
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
@autoreleasepool {
CGEventRef ret = tapEventCallback2(proxy, type, event, refcon);
return ret;
}
}
- (void)handleAndReleaseMediaKeyEvent:(NSEvent *)event
{
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
}
- (void)eventTapThread
{
_tapThreadRL = CFRunLoopGetCurrent();
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
CFRunLoopRun();
}
#pragma mark -
#pragma mark Task switching callbacks
- (void)mediaKeyAppListChanged
{
#ifdef DEBUG_SPMEDIAKEY_APPLIST
[self debugPrintAppList];
#endif
if([_mediaKeyAppList count] == 0)
return;
NSRunningApplication *thisApp = [NSRunningApplication currentApplication];
NSRunningApplication *otherApp = [_mediaKeyAppList firstObject];
BOOL isCurrent = [thisApp isEqual:otherApp];
[self setShouldInterceptMediaKeyEvents:isCurrent];
}
- (void)frontmostAppChanged:(NSNotification *)notification
{
NSRunningApplication *app = [notification.userInfo objectForKey:NSWorkspaceApplicationKey];
if (app.bundleIdentifier == nil)
return;
if (![[SPMediaKeyTap mediaKeyUserBundleIdentifiers] containsObject:app.bundleIdentifier])
return;
[_mediaKeyAppList removeObject:app];
[_mediaKeyAppList insertObject:app atIndex:0];
[self mediaKeyAppListChanged];
}
- (void)appTerminated:(NSNotification *)notification
{
NSRunningApplication *app = [notification.userInfo objectForKey:NSWorkspaceApplicationKey];
[_mediaKeyAppList removeObject:app];
[self mediaKeyAppListChanged];
}
#ifdef DEBUG_SPMEDIAKEY_APPLIST
- (void)debugPrintAppList
{
NSMutableString *list = [NSMutableString stringWithCapacity:255];
for (NSRunningApplication *app in _mediaKeyAppList) {
[list appendFormat:@" - %@\n", app.bundleIdentifier];
}
NSLog(@"List: \n%@", list);
}
#endif
@end

View File

@@ -1,3 +0,0 @@
add_library(getopt STATIC getopt.cpp)
target_compile_definitions(getopt PRIVATE -DSTATIC_GETOPT -D_UNICODE)
target_include_directories(getopt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -1,753 +0,0 @@
/* Getopt for Microsoft C
This code is a modification of the Free Software Foundation, Inc.
Getopt library for parsing command line argument the purpose was
to provide a Microsoft Visual C friendly derivative. This code
provides functionality for both Unicode and Multibyte builds.
Date: 02/03/2011 - Ludvik Jerabek - Initial Release
Version: 1.1
Comment: Supports getopt, getopt_long, and getopt_long_only
and POSIXLY_CORRECT environment flag
License: LGPL
Revisions:
02/03/2011 - Ludvik Jerabek - Initial Release
02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
09/24/2022 - Ludvik Jerabek - Updated to match most recent getopt release
09/25/2022 - Ludvik Jerabek - Fixed memory allocation (malloc call) issue for wchar_t*
**DISCLAIMER**
THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#define _CRT_SECURE_NO_WARNINGS
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "getopt.h"
#ifdef __cplusplus
# define _GETOPT_THROW throw()
#else
# define _GETOPT_THROW
#endif
int optind = 1;
int opterr = 1;
int optopt = '?';
enum ENUM_ORDERING {
REQUIRE_ORDER,
PERMUTE,
RETURN_IN_ORDER
};
//
//
// Ansi structures and functions follow
//
//
static struct _getopt_data_a {
int optind;
int opterr;
int optopt;
char *optarg;
int __initialized;
char *__nextchar;
enum ENUM_ORDERING __ordering;
int __first_nonopt;
int __last_nonopt;
} getopt_data_a;
char *optarg_a;
static void exchange_a(char **argv, struct _getopt_data_a *d) {
int bottom = d->__first_nonopt;
int middle = d->__last_nonopt;
int top = d->optind;
char *tem;
while (top > middle && middle > bottom) {
if (top - middle > middle - bottom) {
int len = middle - bottom;
int i;
for (i = 0; i < len; i++) {
tem = argv[bottom + i];
argv[bottom + i] = argv[top - (middle - bottom) + i];
argv[top - (middle - bottom) + i] = tem;
}
top -= len;
}
else {
int len = top - middle;
int i;
for (i = 0; i < len; i++) {
tem = argv[bottom + i];
argv[bottom + i] = argv[middle + i];
argv[middle + i] = tem;
}
bottom += len;
}
}
d->__first_nonopt += (d->optind - d->__last_nonopt);
d->__last_nonopt = d->optind;
}
static int process_long_option_a(int argc, char **argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int print_errors, const char *prefix);
static int process_long_option_a(int argc, char **argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int print_errors, const char *prefix) {
assert(longopts != NULL);
char *nameend;
size_t namelen;
const struct option_a *p;
const struct option_a *pfound = NULL;
int n_options;
int option_index = 0;
for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
;
namelen = nameend - d->__nextchar;
for (p = longopts, n_options = 0; p->name; p++, n_options++)
if (!strncmp(p->name, d->__nextchar, namelen) && namelen == strlen(p->name)) {
pfound = p;
option_index = n_options;
break;
}
if (pfound == NULL) {
unsigned char *ambig_set = NULL;
int ambig_fallback = 0;
int indfound = -1;
for (p = longopts, option_index = 0; p->name; p++, option_index++)
if (!strncmp(p->name, d->__nextchar, namelen)) {
if (pfound == NULL) {
pfound = p;
indfound = option_index;
}
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) {
if (!ambig_fallback) {
if (!print_errors)
ambig_fallback = 1;
else if (!ambig_set) {
if ((ambig_set = reinterpret_cast<unsigned char *>(malloc(n_options * sizeof(char)))) == NULL)
ambig_fallback = 1;
if (ambig_set) {
memset(ambig_set, 0, n_options * sizeof(char));
ambig_set[indfound] = 1;
}
}
if (ambig_set)
ambig_set[option_index] = 1;
}
}
}
if (ambig_set || ambig_fallback) {
if (print_errors) {
if (ambig_fallback)
fprintf(stderr, "%s: option '%s%s' is ambiguous\n", argv[0], prefix, d->__nextchar);
else {
_lock_file(stderr);
fprintf(stderr, "%s: option '%s%s' is ambiguous; possibilities:", argv[0], prefix, d->__nextchar);
for (option_index = 0; option_index < n_options; option_index++)
if (ambig_set[option_index])
fprintf(stderr, " '%s%s'", prefix, longopts[option_index].name);
fprintf(stderr, "\n");
_unlock_file(stderr);
}
}
free(ambig_set);
d->__nextchar += strlen(d->__nextchar);
d->optind++;
d->optopt = 0;
return '?';
}
option_index = indfound;
}
if (pfound == NULL) {
if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL) {
if (print_errors)
fprintf(stderr, "%s: unrecognized option '%s%s'\n", argv[0], prefix, d->__nextchar);
d->__nextchar = NULL;
d->optind++;
d->optopt = 0;
return '?';
}
return -1;
}
d->optind++;
d->__nextchar = NULL;
if (*nameend) {
if (pfound->has_arg)
d->optarg = nameend + 1;
else {
if (print_errors)
fprintf(stderr, "%s: option '%s%s' doesn't allow an argument\n", argv[0], prefix, pfound->name);
d->optopt = pfound->val;
return '?';
}
}
else if (pfound->has_arg == 1) {
if (d->optind < argc)
d->optarg = argv[d->optind++];
else {
if (print_errors)
fprintf(stderr, "%s: option '%s%s' requires an argument\n", argv[0], prefix, pfound->name);
d->optopt = pfound->val;
return optstring[0] == ':' ? ':' : '?';
}
}
if (longind != NULL)
*longind = option_index;
if (pfound->flag) {
*(pfound->flag) = pfound->val;
return 0;
}
return pfound->val;
}
static const char *_getopt_initialize_a(const char *optstring, struct _getopt_data_a *d, int posixly_correct) {
if (d->optind == 0)
d->optind = 1;
d->__first_nonopt = d->__last_nonopt = d->optind;
d->__nextchar = NULL;
if (optstring[0] == '-') {
d->__ordering = RETURN_IN_ORDER;
++optstring;
}
else if (optstring[0] == '+') {
d->__ordering = REQUIRE_ORDER;
++optstring;
}
else if (posixly_correct | !!getenv("POSIXLY_CORRECT"))
d->__ordering = REQUIRE_ORDER;
else
d->__ordering = PERMUTE;
d->__initialized = 1;
return optstring;
}
int _getopt_internal_r_a(int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct);
int _getopt_internal_r_a(int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct) {
int print_errors = d->opterr;
if (argc < 1)
return -1;
d->optarg = NULL;
if (d->optind == 0 || !d->__initialized)
optstring = _getopt_initialize_a(optstring, d, posixly_correct);
else if (optstring[0] == '-' || optstring[0] == '+')
optstring++;
if (optstring[0] == ':')
print_errors = 0;
if (d->__nextchar == NULL || *d->__nextchar == '\0') {
if (d->__last_nonopt > d->optind)
d->__last_nonopt = d->optind;
if (d->__first_nonopt > d->optind)
d->__first_nonopt = d->optind;
if (d->__ordering == PERMUTE) {
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
exchange_a(const_cast<char **>(argv), d);
else if (d->__last_nonopt != d->optind)
d->__first_nonopt = d->optind;
while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
d->optind++;
d->__last_nonopt = d->optind;
}
if (d->optind != argc && !strcmp(argv[d->optind], "--")) {
d->optind++;
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
exchange_a(const_cast<char **>(argv), d);
else if (d->__first_nonopt == d->__last_nonopt)
d->__first_nonopt = d->optind;
d->__last_nonopt = argc;
d->optind = argc;
}
if (d->optind == argc) {
if (d->__first_nonopt != d->__last_nonopt)
d->optind = d->__first_nonopt;
return -1;
}
if (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') {
if (d->__ordering == REQUIRE_ORDER)
return -1;
d->optarg = argv[d->optind++];
return 1;
}
if (longopts) {
if (argv[d->optind][1] == '-') {
d->__nextchar = argv[d->optind] + 2;
return process_long_option_a(argc, const_cast<char **>(argv), optstring, longopts, longind, long_only, d, print_errors, "--");
}
if (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1]))) {
int code;
d->__nextchar = argv[d->optind] + 1;
code = process_long_option_a(argc, const_cast<char **>(argv), optstring, longopts,
longind, long_only, d,
print_errors, "-");
if (code != -1)
return code;
}
}
d->__nextchar = argv[d->optind] + 1;
}
{
char c = *d->__nextchar++;
const char *temp = strchr(optstring, c);
if (*d->__nextchar == '\0')
++d->optind;
if (temp == NULL || c == ':' || c == ';') {
if (print_errors)
fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c);
d->optopt = c;
return '?';
}
if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL) {
if (*d->__nextchar != '\0')
d->optarg = d->__nextchar;
else if (d->optind == argc) {
if (print_errors)
fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], c);
d->optopt = c;
if (optstring[0] == ':')
c = ':';
else
c = '?';
return c;
}
else
d->optarg = argv[d->optind];
d->__nextchar = d->optarg;
d->optarg = NULL;
return process_long_option_a(argc, const_cast<char **>(argv), optstring, longopts, longind, 0, d, print_errors, "-W ");
}
if (temp[1] == ':') {
if (temp[2] == ':') {
if (*d->__nextchar != '\0') {
d->optarg = d->__nextchar;
d->optind++;
}
else
d->optarg = NULL;
d->__nextchar = NULL;
}
else {
if (*d->__nextchar != '\0') {
d->optarg = d->__nextchar;
d->optind++;
}
else if (d->optind == argc) {
if (print_errors)
fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], c);
d->optopt = c;
if (optstring[0] == ':')
c = ':';
else
c = '?';
}
else
d->optarg = argv[d->optind++];
d->__nextchar = NULL;
}
}
return c;
}
}
int _getopt_internal_a(int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct);
int _getopt_internal_a(int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct) {
int result;
getopt_data_a.optind = optind;
getopt_data_a.opterr = opterr;
result = _getopt_internal_r_a(argc, argv, optstring, longopts, longind, long_only, &getopt_data_a, posixly_correct);
optind = getopt_data_a.optind;
optarg_a = getopt_data_a.optarg;
optopt = getopt_data_a.optopt;
return result;
}
int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW {
return _getopt_internal_a(argc, argv, optstring, static_cast<const struct option_a *>(0), static_cast<int *>(0), 0, 0);
}
int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW {
return _getopt_internal_a(argc, argv, options, long_options, opt_index, 0, 0);
}
int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW {
return _getopt_internal_a(argc, argv, options, long_options, opt_index, 1, 0);
}
int _getopt_long_r_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d);
int _getopt_long_r_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d) {
return _getopt_internal_r_a(argc, argv, options, long_options, opt_index, 0, d, 0);
}
int _getopt_long_only_r_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d);
int _getopt_long_only_r_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d) {
return _getopt_internal_r_a(argc, argv, options, long_options, opt_index, 1, d, 0);
}
//
//
// Unicode Structures and Functions
//
//
static struct _getopt_data_w {
int optind;
int opterr;
int optopt;
wchar_t *optarg;
int __initialized;
wchar_t *__nextchar;
enum ENUM_ORDERING __ordering;
int __first_nonopt;
int __last_nonopt;
} getopt_data_w;
wchar_t *optarg_w;
static void exchange_w(wchar_t **argv, struct _getopt_data_w *d) {
int bottom = d->__first_nonopt;
int middle = d->__last_nonopt;
int top = d->optind;
wchar_t *tem;
while (top > middle && middle > bottom) {
if (top - middle > middle - bottom) {
int len = middle - bottom;
int i;
for (i = 0; i < len; i++) {
tem = argv[bottom + i];
argv[bottom + i] = argv[top - (middle - bottom) + i];
argv[top - (middle - bottom) + i] = tem;
}
top -= len;
}
else {
int len = top - middle;
int i;
for (i = 0; i < len; i++) {
tem = argv[bottom + i];
argv[bottom + i] = argv[middle + i];
argv[middle + i] = tem;
}
bottom += len;
}
}
d->__first_nonopt += (d->optind - d->__last_nonopt);
d->__last_nonopt = d->optind;
}
static int process_long_option_w(int argc, wchar_t **argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int print_errors, const wchar_t *prefix) {
assert(longopts != NULL);
wchar_t *nameend;
size_t namelen;
const struct option_w *p;
const struct option_w *pfound = NULL;
int n_options;
int option_index = 0;
for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++)
;
namelen = nameend - d->__nextchar;
for (p = longopts, n_options = 0; p->name; p++, n_options++)
if (!wcsncmp(p->name, d->__nextchar, namelen) && namelen == wcslen(p->name)) {
pfound = p;
option_index = n_options;
break;
}
if (pfound == NULL) {
wchar_t *ambig_set = NULL;
int ambig_fallback = 0;
int indfound = -1;
for (p = longopts, option_index = 0; p->name; p++, option_index++)
if (!wcsncmp(p->name, d->__nextchar, namelen)) {
if (pfound == NULL) {
pfound = p;
indfound = option_index;
}
else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) {
if (!ambig_fallback) {
if (!print_errors)
ambig_fallback = 1;
else if (!ambig_set) {
if ((ambig_set = reinterpret_cast<wchar_t *>(malloc(n_options * sizeof(wchar_t)))) == NULL)
ambig_fallback = 1;
if (ambig_set) {
memset(ambig_set, 0, n_options * sizeof(wchar_t));
ambig_set[indfound] = 1;
}
}
if (ambig_set)
ambig_set[option_index] = 1;
}
}
}
if (ambig_set || ambig_fallback) {
if (print_errors) {
if (ambig_fallback)
fwprintf(stderr, L"%s: option '%s%s' is ambiguous\n", argv[0], prefix, d->__nextchar);
else {
_lock_file(stderr);
fwprintf(stderr, L"%s: option '%s%s' is ambiguous; possibilities:", argv[0], prefix, d->__nextchar);
for (option_index = 0; option_index < n_options; option_index++)
if (ambig_set[option_index])
fwprintf(stderr, L" '%s%s'", prefix, longopts[option_index].name);
fwprintf(stderr, L"\n");
_unlock_file(stderr);
}
}
free(ambig_set);
d->__nextchar += wcslen(d->__nextchar);
d->optind++;
d->optopt = 0;
return L'?';
}
option_index = indfound;
}
if (pfound == NULL) {
if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL) {
if (print_errors)
fwprintf(stderr, L"%s: unrecognized option '%s%s'\n", argv[0], prefix, d->__nextchar);
d->__nextchar = NULL;
d->optind++;
d->optopt = 0;
return L'?';
}
return -1;
}
d->optind++;
d->__nextchar = NULL;
if (*nameend) {
if (pfound->has_arg)
d->optarg = nameend + 1;
else {
if (print_errors)
fwprintf(stderr, L"%s: option '%s%s' doesn't allow an argument\n", argv[0], prefix, pfound->name);
d->optopt = pfound->val;
return L'?';
}
}
else if (pfound->has_arg == 1) {
if (d->optind < argc)
d->optarg = argv[d->optind++];
else {
if (print_errors)
fwprintf(stderr, L"%s: option '%s%s' requires an argument\n", argv[0], prefix, pfound->name);
d->optopt = pfound->val;
return optstring[0] == L':' ? L':' : L'?';
}
}
if (longind != NULL)
*longind = option_index;
if (pfound->flag) {
*(pfound->flag) = pfound->val;
return 0;
}
return pfound->val;
}
static const wchar_t *_getopt_initialize_w(const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct) {
if (d->optind == 0)
d->optind = 1;
d->__first_nonopt = d->__last_nonopt = d->optind;
d->__nextchar = NULL;
if (optstring[0] == L'-') {
d->__ordering = RETURN_IN_ORDER;
++optstring;
}
else if (optstring[0] == L'+') {
d->__ordering = REQUIRE_ORDER;
++optstring;
}
else if (posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT"))
d->__ordering = REQUIRE_ORDER;
else
d->__ordering = PERMUTE;
d->__initialized = 1;
return optstring;
}
int _getopt_internal_r_w(int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct);
int _getopt_internal_r_w(int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct) {
int print_errors = d->opterr;
if (argc < 1)
return -1;
d->optarg = NULL;
if (d->optind == 0 || !d->__initialized)
optstring = _getopt_initialize_w(optstring, d, posixly_correct);
else if (optstring[0] == L'-' || optstring[0] == L'+')
optstring++;
if (optstring[0] == L':')
print_errors = 0;
#define NONOPTION_P (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0')
if (d->__nextchar == NULL || *d->__nextchar == L'\0') {
if (d->__last_nonopt > d->optind)
d->__last_nonopt = d->optind;
if (d->__first_nonopt > d->optind)
d->__first_nonopt = d->optind;
if (d->__ordering == PERMUTE) {
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
exchange_w(const_cast<wchar_t **>(argv), d);
else if (d->__last_nonopt != d->optind)
d->__first_nonopt = d->optind;
while (d->optind < argc && NONOPTION_P)
d->optind++;
d->__last_nonopt = d->optind;
}
if (d->optind != argc && !wcscmp(argv[d->optind], L"--")) {
d->optind++;
if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
exchange_w(const_cast<wchar_t **>(argv), d);
else if (d->__first_nonopt == d->__last_nonopt)
d->__first_nonopt = d->optind;
d->__last_nonopt = argc;
d->optind = argc;
}
if (d->optind == argc) {
if (d->__first_nonopt != d->__last_nonopt)
d->optind = d->__first_nonopt;
return -1;
}
if (NONOPTION_P) {
if (d->__ordering == REQUIRE_ORDER)
return -1;
d->optarg = argv[d->optind++];
return 1;
}
if (longopts) {
if (argv[d->optind][1] == L'-') {
d->__nextchar = argv[d->optind] + 2;
return process_long_option_w(argc, const_cast<wchar_t **>(argv), optstring, longopts, longind, long_only, d, print_errors, L"--");
}
if (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1]))) {
int code;
d->__nextchar = argv[d->optind] + 1;
code = process_long_option_w(argc, const_cast<wchar_t **>(argv), optstring, longopts, longind, long_only, d, print_errors, L"-");
if (code != -1)
return code;
}
}
d->__nextchar = argv[d->optind] + 1;
}
{
wchar_t c = *d->__nextchar++;
const wchar_t *temp = wcschr(optstring, c);
if (*d->__nextchar == L'\0')
++d->optind;
if (temp == NULL || c == L':' || c == L';') {
if (print_errors)
fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c);
d->optopt = c;
return L'?';
}
if (temp[0] == L'W' && temp[1] == L';' && longopts != NULL) {
if (*d->__nextchar != L'\0')
d->optarg = d->__nextchar;
else if (d->optind == argc) {
if (print_errors)
fwprintf(stderr, L"%s: option requires an argument -- '%c'\n", argv[0], c);
d->optopt = c;
if (optstring[0] == L':')
c = L':';
else
c = L'?';
return c;
}
else
d->optarg = argv[d->optind];
d->__nextchar = d->optarg;
d->optarg = NULL;
return process_long_option_w(argc, const_cast<wchar_t **>(argv), optstring, longopts, longind,
0, d, print_errors, L"-W ");
}
if (temp[1] == L':') {
if (temp[2] == L':') {
if (*d->__nextchar != L'\0') {
d->optarg = d->__nextchar;
d->optind++;
}
else
d->optarg = NULL;
d->__nextchar = NULL;
}
else {
if (*d->__nextchar != L'\0') {
d->optarg = d->__nextchar;
d->optind++;
}
else if (d->optind == argc) {
if (print_errors)
fwprintf(stderr, L"%s: option requires an argument -- '%c'\n", argv[0], c);
d->optopt = c;
if (optstring[0] == L':')
c = L':';
else
c = L'?';
}
else
d->optarg = argv[d->optind++];
d->__nextchar = NULL;
}
}
return c;
}
}
int _getopt_internal_w(int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct);
int _getopt_internal_w(int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct) {
int result;
getopt_data_w.optind = optind;
getopt_data_w.opterr = opterr;
result = _getopt_internal_r_w(argc, argv, optstring, longopts, longind, long_only, &getopt_data_w, posixly_correct);
optind = getopt_data_w.optind;
optarg_w = getopt_data_w.optarg;
optopt = getopt_data_w.optopt;
return result;
}
int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW {
return _getopt_internal_w(argc, argv, optstring, static_cast<const struct option_w *>(0), static_cast<int *>(0), 0, 0);
}
int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW {
return _getopt_internal_w(argc, argv, options, long_options, opt_index, 0, 0);
}
int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW {
return _getopt_internal_w(argc, argv, options, long_options, opt_index, 1, 0);
}
int _getopt_long_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d);
int _getopt_long_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) {
return _getopt_internal_r_w(argc, argv, options, long_options, opt_index, 0, d, 0);
}
int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d);
int _getopt_long_only_r_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) {
return _getopt_internal_r_w(argc, argv, options, long_options, opt_index, 1, d, 0);
}

View File

@@ -1,135 +0,0 @@
/* Getopt for Microsoft C
This code is a modification of the Free Software Foundation, Inc.
Getopt library for parsing command line argument the purpose was
to provide a Microsoft Visual C friendly derivative. This code
provides functionality for both Unicode and Multibyte builds.
Date: 02/03/2011 - Ludvik Jerabek - Initial Release
Version: 1.1
Comment: Supports getopt, getopt_long, and getopt_long_only
and POSIXLY_CORRECT environment flag
License: LGPL
Revisions:
02/03/2011 - Ludvik Jerabek - Initial Release
02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
09/24/2022 - Ludvik Jerabek - Updated to match most recent getopt release
09/25/2022 - Ludvik Jerabek - Fixed memory allocation (malloc call) issue for wchar_t*
**DISCLAIMER**
THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#ifndef __GETOPT_H_
#define __GETOPT_H_
#ifdef _GETOPT_API
# undef _GETOPT_API
#endif
#if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
# error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually"
#elif defined(STATIC_GETOPT)
# define _GETOPT_API
#elif defined(EXPORTS_GETOPT)
# pragma message("Exporting getopt library")
# define _GETOPT_API __declspec(dllexport)
#else
# pragma message("Importing getopt library")
# define _GETOPT_API __declspec(dllimport)
#endif
// Change behavior for C\C++
#ifdef __cplusplus
# define _BEGIN_EXTERN_C extern "C" {
# define _END_EXTERN_C }
# define _GETOPT_THROW throw()
#else
# define _BEGIN_EXTERN_C
# define _END_EXTERN_C
# define _GETOPT_THROW
#endif
// Standard GNU options
#define null_argument 0 /*Argument Null*/
#define no_argument 0 /*Argument Switch Only*/
#define required_argument 1 /*Argument Required*/
#define optional_argument 2 /*Argument Optional*/
// Shorter Options
#define ARG_NULL 0 /*Argument Null*/
#define ARG_NONE 0 /*Argument Switch Only*/
#define ARG_REQ 1 /*Argument Required*/
#define ARG_OPT 2 /*Argument Optional*/
#include <string.h>
#include <wchar.h>
_BEGIN_EXTERN_C
extern _GETOPT_API int optind;
extern _GETOPT_API int opterr;
extern _GETOPT_API int optopt;
// Ansi
struct option_a {
const char *name;
int has_arg;
int *flag;
int val;
};
extern _GETOPT_API char *optarg_a;
extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW;
extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
// Unicode
struct option_w {
const wchar_t *name;
int has_arg;
int *flag;
int val;
};
extern _GETOPT_API wchar_t *optarg_w;
extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW;
extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
_END_EXTERN_C
#undef _BEGIN_EXTERN_C
#undef _END_EXTERN_C
#undef _GETOPT_THROW
#undef _GETOPT_API
#ifdef _UNICODE
# define getopt getopt_w
# define getopt_long getopt_long_w
# define getopt_long_only getopt_long_only_w
# define option option_w
# define optarg optarg_w
#else
# define getopt getopt_a
# define getopt_long getopt_long_a
# define getopt_long_only getopt_long_only_a
# define option option_a
# define optarg optarg_a
#endif
#endif // __GETOPT_H_

View File

@@ -1,8 +1,11 @@
cmake_minimum_required(VERSION 3.7)
cmake_minimum_required(VERSION 3.13)
set(SOURCES KDSingleApplication/src/kdsingleapplication.cpp KDSingleApplication/src/kdsingleapplication_localsocket.cpp)
set(HEADERS KDSingleApplication/src/kdsingleapplication.h KDSingleApplication/src/kdsingleapplication_localsocket_p.h)
qt_wrap_cpp(MOC ${HEADERS})
add_library(kdsingleapplication STATIC ${SOURCES} ${MOC})
if(NOT MSVC)
target_compile_options(kdsingleapplication PRIVATE -Wno-missing-declarations)
endif()
target_compile_definitions(kdsingleapplication PRIVATE -DKDSINGLEAPPLICATION_STATIC_BUILD)
target_include_directories(kdsingleapplication PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(kdsingleapplication PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network)

File diff suppressed because it is too large Load Diff

View File

@@ -48,24 +48,27 @@ small as possible.
### Commit messages
The first line should start with "Class:", which referer to the class
that is changed. Don't use a trailing period after the first line.
If this change affects more than one class, omit the class and write a
The first line should start with the name of the class that is changed
followed by a colon then a short explanation of the commit.
Don't use a trailing period after the first line.
If this change affects more than one class, omit the class name and write a
more general message.
You only need to include a main description (body) for larger changes
where the one line is not enough to describe everything.
The main description starts after two newlines, it is normal prose and
should use normal punctuation and capital letters where appropriate.
It should explain exactly what's changed, why it's changed,
and what bugs were fixed.
An example of the expected format for git commit messages is as follows:
```
class: Short explanation of the commit
StretchHeaderView: Set default section size
Longer explanation explaining exactly what's changed, why it's changed,
and what bugs were fixed.
As of Qt 6.6.1, style changes are resetting the column sizes. To prevent this, we set a default section size.
Fixes #1234
Fixes #1328
```

166
Changelog
View File

@@ -2,6 +2,172 @@ Strawberry Music Player
=======================
ChangeLog
Version 1.2.2 (2024.11.23):
Bugfixes:
* Fixed crash when creating a new smart playlist (#1609).
* Fixed last playlist column being added when dragging a song and switching playlists.
Version 1.2.1 (2024.11.21):
This release features major restructuring of the codebase, moving source files,
rewriting CMake build files, dropping Qt 5 support, external tagreader,
and dropping some unmaintained parts such as VLC.
Bugfixes:
* Fixed playback of CUE continuing to play from the same file after the song has finished playing (#1568).
* Fixed updating collection song sort text when disc is changed.
* Fixed current playing file left open when the next track errored (#1582).
* Fixed filter search not finding song containing uppercase "A" (#1599).
* Fixed crash when removing album from playlist when using shuffle albums (#1588).
* Fixed IDv3 MBID's tags with multiple entries being ignored.
* Fixed crash when enabling Tidal, Spotify, Qobuz or Subsonic services.
* Fixed passing filenames to strawberry on command line not resolving to absolute paths.
* (macOS) Fixed program not starting for users with long usernames.
* (macoS) Fixed crash when pressing caps lock (#1606).
* (macOS) Remove "song progress on taskbar" option in behaviour settings.
Enhancements:
* Resolve symbolic links when dragging files to the playlist to match collection song.
* Replaced Spotify username/password with access token.
* Require Qt 6.4 or higher and drop support for Qt 5.
* Require TagLib 1.12 or higher.
* Use Qt stringliterals.
* Move gstfastspectrum to src.
* Use standard user temp location for current album cover.
* Removed old MacFSListener.
* Removed external tagreader and protobuf dependency.
* Removed VLC support.
* Ported to Qt translation (.ts) files and removed gettext dependency.
* Removed deprecated Gnome/Mate SettingsDaemon global shortcuts.
Version 1.1.3 (2024.09.21):
Bugfixes:
* Fixed gstreamer registry lookup leak in Spotify settings.
* Fixed all songs in a CUE sheet starting playback at the zero position (#1549).
* Fixed playback going to pause and back to play on song change.
* Fixed Genius Lyrics login not working (#1554).
* Fixed slow collection filter search.
Version 1.1.2 (2024.09.12):
Bugfixes:
* Fixed Tidal Open API cover provider to only login when needed instead of on startup.
* Fixed KDE added keyboard accelerator characters (ampersands) appearing in sidebar (#1400, #1389, #1476).
* Fixed KDE added keyboard accelerator characters (ampersands) appearing when editing playlist name (#1499).
* Fixed collection "Search for this" adding prefix without value (#1510).
* Fixed play (-p) command line option not working on startup (#1465).
* Fixed scan transaction being started when "Update the collection when Strawberry starts" option is unchecked (#1469)
* Fixed Spotify bitrate being limited 128kbit/s.
* Fixed Spotify returning too many artists and albums.
* Fixed manually switching Spotify songs blocking UI.
* Fixed analyzer not being set.
* Fixed context top text being updated causing selected text to be unselected.
* Fixed filter search to use filename for songs with empty title.
* Fixed missing developer in Appstream appdata file.
* Fixed MPRIS2 DesktopEntry to return desktop file entry without ".desktop" (#1516)
* Fixed WavPack .wvc accepted as valid audio files (#1525).
* Fixed dynamic playlist controls not following system colors (#1483).
* Fixed freeze on playlist right click (#1478).
* Fixed copying songs to a iPod device keeping too many files open (#1527).
* Fixed MBIDs from MP4 being parsed incorrectly causing ListenBrainz errors (#1531).
* Fixed playlist sorting after filename (#1538).
Enhancements:
* Improved volume adjustment and track seeking using touchpad (#1498).
* Use own thread for lyrics parsing.
* Added url and filename columns to collection and playlist filter search.
* (macOS) Added Spotify.
Version 1.1.1 (2024.07.22):
Bugfixes:
* Fixed compilation songs being split into different albums when using album grouping.
* Fixed adding playlist columns not working when stretch mode is disabled (#1085).
* Fixed resetting playlist columns.
* Fixed adding songs to playlist adding all songs instead of filtered songs.
* Fixed collection filter matching entire text instead of individual words.
Enhancements:
* Use same code for collection and playlist filter search.
Version 1.1.0 (2024.07.14):
Bugfixes:
* Fixed crash when pressing CTRL + C (#1359).
* Pass on scroll events to page in settings to avoid changing settings when scrolling with mouse (#1380).
* Fixed misredered playlist search field with Wayland using scaling (#1255).
* Fixed Azlyrics lyrics provider because of website changes.
* Fixed Musixmatch lyrics provider because of website changes.
* Fixed application exiting when closing file dialog (#1401).
* Fixed playlist shuffle randomness (#707).
* Fixed playlist shuffle order always the same when restarting playback (#1381).
* Fixed dynamic random mix not always ignoring shuffle and repeat mode (#1366).
* Fixed manual shuffle while playing not setting current song to new index (##1353).
* Fixed volume sync with PA when output is set to auto (#1123).
* Fixed mpris:trackid type with KDE 6 (#1397).
* Fixed open in file manager feature not handling missing XDG_DATA_DIRS variable.
* Fixed collection pixmap disk cache and moodbar cache with newer Qt versions.
* Fixed reading common metadata CUE's with multiple files (#1463).
* Fixed playlist header stretch mode to only resize the right column of the column being resized.
* Fixed adding columns to playlist header not working when not using stretch mode (#1085).
* Fixed severe memory leak (!) in context album cover fading (#1464).
* Separate albums by different artist in album groupings (#1276).
* Removed -new-window parameter from dolphin command for open in file manager (#1412).
* Only use playbin3 with GStreamer 1.24 and higher, not with GStreamer 1.22 or lower.
* (macOS/Windows) Fixed dash and hls streaming, plugins were missing.
* (Windows) Fixed incorrect colors in smart playlist wizard with Fusion in dark mode (#1399).
* (Windows) Fixed update window blocking sponsor window on startup.
Enhancements:
* Improve error messages when connecting and copying to devices.
* Allow enter to be used with multiselection to add songs to playlist (#1360)
* Add song progress to taskbar using D-Bus.
* Use API to receive Radio Paradise channels.
* Added button for fetching lyrics to tag editor (#1391).
* Added option not to skip "A", "An" and "The” when sorting artist names in collection (#1393).
* Improved album and title disc, remastered, etc matching and stripping (#1387).
* Save volume to settings when adjusting (#1272).
* Resolve song from collection using track with Cue in XSPF (#1181).
* Added background image to sidebar.
* Read metadata from RIFF WAV files (#1424).
* Use original path instead of canonical path when adding directories to the collection.
* Only apply added/removed collection directories when settings are saved.
* Detect and handle different text encodings when reading CUE files (#1429).
* The collection has been rewritten and improved (model/filter/search) (#392).
* Improve error messages from tag reader.
* (Unix) Add experimental GStreamer pipewire support.
* (Windows) Add experimental exclusive mode for WASAPI.
* (Windows MSVC) Added experimental ASIO support.
* (Windows MSVC) Add back WASAPI2.
New features:
* Letras lyrics provider.
* Open Tidal API (openapi.tidal.com) cover provider.
* Turbine analyzer.
* WaveRubber analyzer.
* Spotify streaming support.
Removed features:
* Removed now broken lyricsmode.com lyrics provider because of website changes.
Version 1.0.23 (2024.01.11):
Bugfixes:
* Fixed possible duplication of song entries after organizing (#1341).
* Fixed possible crash when connecting devices (#1313).
* Fixed playlist sorting of original year (#1349).
* (macOS) Fixed crash when adding collection directory (QTBUG-120469) (#1350).
Enhancements:
* Treat all stream errors as non-fatal (#1347).
* Require KDSingleApplication 1.1.0.
* Fix logging of restored unavailable songs.
Version 1.0.22 (2023.12.09):
Bugfixes:

View File

@@ -1,4 +1,4 @@
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/build/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/Build/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
=======================
[![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)
@@ -14,12 +14,11 @@ Resources:
* Wiki: https://wiki.strawberrymusicplayer.org/
* Forum: https://forum.strawberrymusicplayer.org/
* Github: https://github.com/strawberrymusicplayer/strawberry
* Buildbot: https://buildbot.strawberrymusicplayer.org/
* Latest builds: https://builds.strawberrymusicplayer.org/
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/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://crowdin.com/project/strawberrymusicplayer/
### :bangbang: Opening an issue
@@ -29,13 +28,13 @@ Resources:
* We do not take feature requests from users on GitHub. Any issues related to feature requests will be closed. This does not necessarily mean that we won't add new features, but we don't have time to take feature requests or answer questions about new features from users. It is still possible to suggest or discuss new features on the forum (https://forum.strawberrymusicplayer.org/).
* We do not maintain the Flatpak package. Do not report issues related to Flatpak unless the issue can be reproduced with a native package, use Flatpak support instead https://flatpak.org/about/
### :moneybag: Sponsoring
### :moneybag: Sponsoring
The program is free software, released under GPL. If you like this program and can make use of it, consider sponsoring or donating to help fund the project.
There are currently 3 options for sponsoring:
There are currently 4 options for sponsoring:
1. [GitHub Sponsors](https://github.com/sponsors/jonaski)
2. [Patreon](https://www.patreon.com/jonaskvinge)
1. [Patreon](https://www.patreon.com/jonaskvinge)
2. [GitHub](https://github.com/sponsors/jonaski)
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
4. [PayPal](https://paypal.me/jonaskvinge)
@@ -54,18 +53,18 @@ Funding developers is a way to contribute to open source projects you appreciate
* Edit tags on audio files
* Fetch tags from MusicBrainz
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
* Song lyrics from [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/), [lololyrics.com](https://www.lololyrics.com/), [songlyrics.com](https://www.songlyrics.com/), [azlyrics.com](https://www.azlyrics.com/), [elyrics.net](https://www.elyrics.net/) and [lyricsmode.com](https://www.lyricsmode.com/)
* Song lyrics from [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/), [lololyrics.com](https://www.lololyrics.com/), [songlyrics.com](https://www.songlyrics.com/), [azlyrics.com](https://www.azlyrics.com/) and [elyrics.net](https://www.elyrics.net/)
* Support for multiple backends
* Audio analyzer
* Audio equalizer
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
* Subsonic, Tidal and Qobuz streaming support
* Subsonic, Tidal, Spotify and Qobuz streaming support
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
**macOS releases are currently limited to sponsors. This is because macOS releases require a developer account, Apple hardware and maintaining all libraries strawberry depends on. If you are sponsoring strawberry, e-mail support@strawberrymusicplayer.org for access to downloads.**
**Access to macOS and Windows releases are currently restricted to sponsors, a 5 USD monthly sponsorship is required. You can sponsor strawberry through <a href="https://www.patreon.com/jonaskvinge">Patreon</a> for direct access to new releases. If you are sponsoring through GitHub, Ko-fi or PayPal, please e-mail support AT strawberrymusicplayer.org for access to downloads.**
### :heavy_exclamation_mark: Requirements
@@ -76,14 +75,13 @@ To build Strawberry from source you need the following installed on your system
* [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) or [pkgconf](https://github.com/pkgconf/pkgconf)
* [Boost](https://www.boost.org/)
* [GLib](https://developer.gnome.org/glib/)
* [Qt 6 or Qt 5.12 or higher with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
* [Protobuf](https://developers.google.com/protocol-buffers/)
* [Qt 6.4.0 or higher with components Core, Concurrent, Gui, Widgets, Network, Sql and D-Bus](https://www.qt.io/)
* [SQLite 3.9 or newer](https://www.sqlite.org)
* [ALSA (Required on Linux)](https://www.alsa-project.org/)
* [D-Bus (Required on Linux)](https://www.freedesktop.org/wiki/Software/dbus/)
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
* [GStreamer](https://gstreamer.freedesktop.org/)
* [TagLib 1.12 or higher](https://www.taglib.org/)
* [ICU](https://unicode-org.github.io/icu/)
* [KDSingleApplication](https://github.com/KDAB/KDSingleApplication)
Optional dependencies:
@@ -97,7 +95,7 @@ Optional dependencies:
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav to support all audio formats.
### :wrench: Compiling from source
### :wrench: Compiling from source
### Get the code:
@@ -108,16 +106,12 @@ You should also install the gstreamer plugins base and good, and optionally bad,
cd strawberry
mkdir build
cd build
cmake .. -DBUILD_WITH_QT6=ON
cmake ..
make -j $(nproc)
sudo make install
Strawberry is backwards compatible with Qt 5, to compile with Qt 5 use:
cmake .. -DBUILD_WITH_QT5=ON
To compile on Windows with Visual Studio 2019 or 2022, see https://github.com/strawberrymusicplayer/strawberry-msvc
### :penguin: Packaging status
### :penguin: Packaging status
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg?exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg?columns=3&header=Strawberry&exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)

View File

@@ -1,10 +1,17 @@
find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/bin /usr/local/bin /opt/local/bin /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin REQUIRED)
find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/bin /usr/local/bin /opt/local/bin /usr/local/opt/qt6/bin REQUIRED)
if(MACDEPLOYQT_EXECUTABLE)
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
else()
message(WARNING "Missing macdeployqt executable.")
endif()
find_program(MACDEPLOYCHECK_EXECUTABLE NAMES macdeploycheck PATHS /usr/bin /usr/local/bin /opt/local/bin /usr/local/opt/qt6/bin REQUIRED)
if(MACDEPLOYCHECK_EXECUTABLE)
message(STATUS "Found macdeploycheck: ${MACDEPLOYCHECK_EXECUTABLE}")
else()
message(WARNING "Missing macdeploycheck executable.")
endif()
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
if(CREATEDMG_EXECUTABLE)
message(STATUS "Found create-dmg: ${CREATEDMG_EXECUTABLE}")
@@ -27,14 +34,15 @@ if(MACDEPLOYQT_EXECUTABLE)
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh ${CMAKE_BINARY_DIR}/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/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS strawberry strawberry-tagreader
)
add_custom_target(deploycheck
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
DEPENDS macdeploycheck
DEPENDS strawberry
)
if(MACDEPLOYCHECK_EXECUTABLE)
add_custom_target(deploycheck
COMMAND ${MACDEPLOYCHECK_EXECUTABLE} strawberry.app
)
endif()
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 ${CREATEDMG_CODESIGN} ${CREATEDMG_SKIP_JENKINS_ARG} strawberry-${STRAWBERRY_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app

View File

@@ -1,69 +0,0 @@
# - Try to find the libcppunit libraries
# Once done this will define
#
# CppUnit_FOUND - system has libcppunit
# CPPUNIT_INCLUDE_DIR - the libcppunit include directory
# CPPUNIT_LIBRARIES - libcppunit library
#include (MacroEnsureVersion)
if(NOT CPPUNIT_MIN_VERSION)
SET(CPPUNIT_MIN_VERSION 1.12.0)
endif(NOT CPPUNIT_MIN_VERSION)
FIND_PROGRAM(CPPUNIT_CONFIG_EXECUTABLE cppunit-config )
IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
# in cache already
SET(CppUnit_FOUND TRUE)
ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
SET(CPPUNIT_INCLUDE_DIR)
SET(CPPUNIT_LIBRARIES)
IF(CPPUNIT_CONFIG_EXECUTABLE)
EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --cflags RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_CFLAGS)
EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --libs RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_LIBRARIES)
EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --version RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_INSTALLED_VERSION)
STRING(REGEX REPLACE "-I(.+)" "\\1" CPPUNIT_CFLAGS "${CPPUNIT_CFLAGS}")
ELSE(CPPUNIT_CONFIG_EXECUTABLE)
# in case win32 needs to find it the old way?
FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include )
FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib )
# how can we find cppunit version?
MESSAGE (STATUS "Ensure you cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION})
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)
SET(CPPUNIT_INCLUDE_DIR ${CPPUNIT_CFLAGS} "${CPPUNIT_CFLAGS}/cppunit")
ENDIF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
SET(CppUnit_FOUND TRUE)
if(NOT CppUnit_FIND_QUIETLY)
MESSAGE (STATUS "Found cppunit: ${CPPUNIT_LIBRARIES}")
endif(NOT CppUnit_FIND_QUIETLY)
IF(CPPUNIT_CONFIG_EXECUTABLE)
EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --version RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_INSTALLED_VERSION)
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)
#macro_ensure_version( ${CPPUNIT_MIN_VERSION} ${CPPUNIT_INSTALLED_VERSION} CPPUNIT_INSTALLED_VERSION_OK )
#IF(NOT CPPUNIT_INSTALLED_VERSION_OK)
# MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or major is required")
# SET(CppUnit_FOUND FALSE)
#ENDIF(NOT CPPUNIT_INSTALLED_VERSION_OK)
ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
SET(CppUnit_FOUND FALSE CACHE BOOL "Not found cppunit library")
ENDIF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
MARK_AS_ADVANCED(CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARIES)

View File

@@ -1,132 +0,0 @@
#
# Try to find FFTW3 library
# (see www.fftw.org)
# Once run this will define:
#
# FFTW3_FOUND
# FFTW3_INCLUDE_DIR
# FFTW3_LIBRARIES
# FFTW3_LINK_DIRECTORIES
#
# You may set one of these options before including this file:
# FFTW3_USE_SSE2
#
# TODO: _F_ versions.
#
# Jan Woetzel 05/2004
# www.mip.informatik.uni-kiel.de
# --------------------------------
FIND_PATH(FFTW3_INCLUDE_DIR fftw3.h
${FFTW3_DIR}/include
${FFTW3_HOME}/include
${FFTW3_DIR}
${FFTW3_HOME}
$ENV{FFTW3_DIR}/include
$ENV{FFTW3_HOME}/include
$ENV{FFTW3_DIR}
$ENV{FFTW3_HOME}
/usr/include
/usr/local/include
$ENV{SOURCE_DIR}/fftw3
$ENV{SOURCE_DIR}/fftw3/include
$ENV{SOURCE_DIR}/fftw
$ENV{SOURCE_DIR}/fftw/include
)
#MESSAGE("DBG FFTW3_INCLUDE_DIR=${FFTW3_INCLUDE_DIR}")
SET(FFTW3_POSSIBLE_LIBRARY_PATH
${FFTW3_DIR}/lib
${FFTW3_HOME}/lib
${FFTW3_DIR}
${FFTW3_HOME}
$ENV{FFTW3_DIR}/lib
$ENV{FFTW3_HOME}/lib
$ENV{FFTW3_DIR}
$ENV{FFTW3_HOME}
/usr/lib
/usr/local/lib
$ENV{SOURCE_DIR}/fftw3
$ENV{SOURCE_DIR}/fftw3/lib
$ENV{SOURCE_DIR}/fftw
$ENV{SOURCE_DIR}/fftw/lib
)
# The lib prefix is contained in filename of W32, unfortunately. In the "general" lib:
FIND_LIBRARY(FFTW3_FFTW_LIBRARY
NAMES fftw3 libfftw libfftw3 libfftw3-3
PATHS
${FFTW3_POSSIBLE_LIBRARY_PATH}
)
#MESSAGE("DBG FFTW3_FFTW_LIBRARY=${FFTW3_FFTW_LIBRARY}")
FIND_LIBRARY(FFTW3_FFTWF_LIBRARY
NAMES fftwf3 fftw3f fftwf libfftwf libfftwf3 libfftw3f-3
PATHS
${FFTW3_POSSIBLE_LIBRARY_PATH}
)
#MESSAGE("DBG FFTW3_FFTWF_LIBRARY=${FFTW3_FFTWF_LIBRARY}")
FIND_LIBRARY(FFTW3_FFTWL_LIBRARY
NAMES fftwl3 fftw3l fftwl libfftwl libfftwl3 libfftw3l-3
PATHS
${FFTW3_POSSIBLE_LIBRARY_PATH}
)
#MESSAGE("DBG FFTW3_FFTWF_LIBRARY=${FFTW3_FFTWL_LIBRARY}")
FIND_LIBRARY(FFTW3_FFTW_SSE2_LIBRARY
NAMES fftw_sse2 fftw3_sse2 libfftw_sse2 libfftw3_sse2
PATHS
${FFTW3_POSSIBLE_LIBRARY_PATH}
)
#MESSAGE("DBG FFTW3_FFTW_SSE2_LIBRARY=${FFTW3_FFTW_SSE2_LIBRARY}")
FIND_LIBRARY(FFTW3_FFTWF_SSE_LIBRARY
NAMES fftwf_sse fftwf3_sse libfftwf_sse libfftwf3_sse
PATHS
${FFTW3_POSSIBLE_LIBRARY_PATH}
)
#MESSAGE("DBG FFTW3_FFTWF_SSE_LIBRARY=${FFTW3_FFTWF_SSE_LIBRARY}")
# --------------------------------
# select one of the above
# default:
IF (FFTW3_FFTW_LIBRARY)
SET(FFTW3_LIBRARIES ${FFTW3_FFTW_LIBRARY})
ENDIF (FFTW3_FFTW_LIBRARY)
# specialized:
IF (FFTW3_USE_SSE2 AND FFTW3_FFTW_SSE2_LIBRARY)
SET(FFTW3_LIBRARIES ${FFTW3_FFTW_SSE2_LIBRARY})
ENDIF (FFTW3_USE_SSE2 AND FFTW3_FFTW_SSE2_LIBRARY)
# --------------------------------
IF(FFTW3_LIBRARIES)
IF (FFTW3_INCLUDE_DIR)
# OK, found all we need
SET(FFTW3_FOUND TRUE)
GET_FILENAME_COMPONENT(FFTW3_LINK_DIRECTORIES ${FFTW3_LIBRARIES} PATH)
ELSE (FFTW3_INCLUDE_DIR)
MESSAGE("FFTW3 include dir not found. Set FFTW3_DIR to find it.")
ENDIF(FFTW3_INCLUDE_DIR)
ELSE(FFTW3_LIBRARIES)
MESSAGE("FFTW3 lib not found. Set FFTW3_DIR to find it.")
ENDIF(FFTW3_LIBRARIES)
MARK_AS_ADVANCED(
FFTW3_INCLUDE_DIR
FFTW3_LIBRARIES
FFTW3_FFTW_LIBRARY
FFTW3_FFTW_SSE2_LIBRARY
FFTW3_FFTWF_LIBRARY
FFTW3_FFTWF_SSE_LIBRARY
FFTW3_FFTWL_LIBRARY
FFTW3_LINK_DIRECTORIES
)

View File

@@ -1,80 +0,0 @@
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
message(FATAL_ERROR "Could not find xgettext executable")
endif(NOT GETTEXT_XGETTEXT_EXECUTABLE)
set (XGETTEXT_OPTIONS
--qt
--keyword=tr:1,2c
--keyword=tr --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
--keyword=trUtf8 --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
--keyword=translate:2,3c
--keyword=translate:2 --flag=translate:2:pass-c-format --flag=translate:2:pass-qt-format
--keyword=QT_TR_NOOP --flag=QT_TR_NOOP:1:pass-c-format --flag=QT_TR_NOOP:1:pass-qt-format
--keyword=QT_TRANSLATE_NOOP:2 --flag=QT_TRANSLATE_NOOP:2:pass-c-format --flag=QT_TRANSLATE_NOOP:2:pass-qt-format
--keyword=_ --flag=_:1:pass-c-format --flag=_:1:pass-qt-format
--keyword=N_ --flag=N_:1:pass-c-format --flag=N_:1:pass-qt-format
--from-code=utf-8
)
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations)
macro(add_pot outfiles header pot)
# Make relative filenames for all source files
set(add_pot_sources)
foreach(_filename ${ARGN})
get_filename_component(_absolute_filename ${_filename} ABSOLUTE)
file(RELATIVE_PATH _relative_filename ${CMAKE_CURRENT_SOURCE_DIR} ${_absolute_filename})
list(APPEND add_pot_sources ${_relative_filename})
endforeach(_filename)
# Generate the .pot
add_custom_command(
OUTPUT ${pot}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTIONS} -s -C --omit-header --output=${CMAKE_CURRENT_BINARY_DIR}/pot.temp ${add_pot_sources}
COMMAND cat ${header} ${CMAKE_CURRENT_BINARY_DIR}/pot.temp > ${pot}
DEPENDS ${add_pot_sources} ${header}
)
list(APPEND ${outfiles} ${pot})
endmacro(add_pot)
# Syntax is:
# add_po(sources_var po_prefix LANGUAGES language1 language2 ... DIRECTORY dir)
macro(add_po outfiles po_prefix)
parse_arguments(ADD_PO
"LANGUAGES;DIRECTORY"
""
${ARGN}
)
foreach (_lang ${ADD_PO_LANGUAGES})
set(_po_filename "${_lang}.po")
set(_po_filepath "${CMAKE_CURRENT_SOURCE_DIR}/${ADD_PO_DIRECTORY}/${_po_filename}")
set(_qm_filename "strawberry_${_lang}.qm")
set(_qm_filepath "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/${_qm_filename}")
# Convert the .po files to .qm files
add_custom_command(
OUTPUT ${_qm_filepath}
COMMAND ${QT_LCONVERT_EXECUTABLE} ARGS ${_po_filepath} -o ${_qm_filepath} -of qm -target-language ${_lang}
DEPENDS ${_po_filepath} ${_po_filepath}
)
list(APPEND ${outfiles} ${_qm_filepath})
list(APPEND INSTALL_TRANSLATIONS_FILES ${_qm_filepath})
endforeach (_lang)
# Generate a qrc file for the translations
if(NOT INSTALL_TRANSLATIONS)
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
foreach(_lang ${ADD_PO_LANGUAGES})
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
endforeach(_lang)
file(APPEND ${_qrc} "</qresource></RCC>")
qt_add_resources(${outfiles} ${_qrc})
endif()
endmacro(add_po)

View File

@@ -1,27 +1,15 @@
set(STRAWBERRY_VERSION_MAJOR 1)
set(STRAWBERRY_VERSION_MINOR 0)
set(STRAWBERRY_VERSION_PATCH 22)
set(STRAWBERRY_VERSION_MINOR 2)
set(STRAWBERRY_VERSION_PATCH 2)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF)
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_R "1")
set(STRAWBERRY_VERSION_PAC_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_PAC_R "1")
if(STRAWBERRY_VERSION_PRERELEASE)
set(STRAWBERRY_VERSION_DISPLAY "${STRAWBERRY_VERSION_DISPLAY} ${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_RPM_R "0.${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_PACKAGE "${STRAWBERRY_VERSION_PACKAGE}${STRAWBERRY_VERSION_PRERELEASE}")
endif(STRAWBERRY_VERSION_PRERELEASE)
if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
if(FORCE_GIT_REVISION)
set(GIT_REVISION ${FORCE_GIT_REVISION})
elseif(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
find_program(GIT_EXECUTABLE git)
if(NOT GIT_EXECUTABLE OR GIT_EXECUTABLE-NOTFOUND)
@@ -53,10 +41,6 @@ if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
endif()
if(FORCE_GIT_REVISION)
set(GIT_REVISION ${FORCE_GIT_REVISION})
endif()
if(GIT_REVISION)
string(REGEX REPLACE "^(.+)-([0-9]+)-(g[a-f0-9]+)$" "\\1;\\2;\\3" GIT_PARTS ${GIT_REVISION})
@@ -78,15 +62,23 @@ if(GIT_REVISION)
set(STRAWBERRY_VERSION_DISPLAY "${GIT_REVISION}")
set(STRAWBERRY_VERSION_PACKAGE "${GIT_TAGNAME}.${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
string(REPLACE "-" "~" STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
set(STRAWBERRY_VERSION_RPM_R "2.${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_PAC_V "${GIT_TAGNAME}.r${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_PAC_R "1")
else()
if(STRAWBERRY_VERSION_PRERELEASE)
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}-${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}~${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}${STRAWBERRY_VERSION_PRERELEASE}")
else()
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
endif()
set(STRAWBERRY_VERSION_RPM_R "1")
endif()
message(STATUS "Strawberry Version:")
message(STATUS "Display: ${STRAWBERRY_VERSION_DISPLAY}")
message(STATUS "Package: ${STRAWBERRY_VERSION_PACKAGE}")
message(STATUS "RPM: ${STRAWBERRY_VERSION_RPM_V}-${STRAWBERRY_VERSION_RPM_R}")
message(STATUS "PAC: ${STRAWBERRY_VERSION_PAC_V}-${STRAWBERRY_VERSION_PAC_R}")

3
crowdin.yml Normal file
View File

@@ -0,0 +1,3 @@
files:
- source: /src/translations/strawberry_en_US.ts
translation: /src/translations/strawberry_%locale_with_underscore%.ts

View File

@@ -10,6 +10,8 @@
<file>schema/schema-16.sql</file>
<file>schema/schema-17.sql</file>
<file>schema/schema-18.sql</file>
<file>schema/schema-19.sql</file>
<file>schema/schema-20.sql</file>
<file>schema/device-schema.sql</file>
<file>style/strawberry.css</file>
<file>style/smartplaylistsearchterm.css</file>
@@ -42,5 +44,7 @@
<file>pictures/star-off.png</file>
<file>mood/sample.mood</file>
<file>text/ghosts.txt</file>
<file>pictures/sidebar-background.png</file>
<file>style/dynamicplaylistcontrols.css</file>
</qresource>
</RCC>

View File

@@ -91,6 +91,7 @@
<file>icons/128x128/love.png</file>
<file>icons/128x128/subsonic.png</file>
<file>icons/128x128/tidal.png</file>
<file>icons/128x128/spotify.png</file>
<file>icons/128x128/qobuz.png</file>
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
<file>icons/128x128/radio.png</file>
@@ -189,6 +190,7 @@
<file>icons/64x64/love.png</file>
<file>icons/64x64/subsonic.png</file>
<file>icons/64x64/tidal.png</file>
<file>icons/64x64/spotify.png</file>
<file>icons/64x64/qobuz.png</file>
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
<file>icons/64x64/radio.png</file>
@@ -290,8 +292,9 @@
<file>icons/48x48/moodbar.png</file>
<file>icons/48x48/love.png</file>
<file>icons/48x48/subsonic.png</file>
<file>icons/48x48/tidal.png</file>
<file>icons/48x48/qobuz.png</file>
<file>icons/48x48/tidal.png</file>
<file>icons/48x48/spotify.png</file>
<file>icons/48x48/qobuz.png</file>
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
<file>icons/48x48/radio.png</file>
<file>icons/48x48/somafm.png</file>
@@ -393,6 +396,7 @@
<file>icons/32x32/love.png</file>
<file>icons/32x32/subsonic.png</file>
<file>icons/32x32/tidal.png</file>
<file>icons/32x32/spotify.png</file>
<file>icons/32x32/qobuz.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
<file>icons/32x32/radio.png</file>
@@ -495,6 +499,7 @@
<file>icons/22x22/love.png</file>
<file>icons/22x22/subsonic.png</file>
<file>icons/22x22/tidal.png</file>
<file>icons/22x22/spotify.png</file>
<file>icons/22x22/qobuz.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
<file>icons/22x22/radio.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
data/icons/full/spotify.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -94,9 +94,4 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;

19
data/schema/schema-19.sql Normal file
View File

@@ -0,0 +1,19 @@
DROP TABLE IF EXISTS %allsongstables_fts;
DROP TABLE IF EXISTS songs_fts;
DROP TABLE IF EXISTS subsonic_songs_fts;
DROP TABLE IF EXISTS tidal_artists_songs_fts;
DROP TABLE IF EXISTS tidal_albums_songs_fts;
DROP TABLE IF EXISTS tidal_songs_fts;
DROP TABLE IF EXISTS qobuz_artists_songs_fts;
DROP TABLE IF EXISTS qobuz_albums_songs_fts;
DROP TABLE IF EXISTS qobuz_songs_fts;
UPDATE schema_version SET version=19;

244
data/schema/schema-20.sql Normal file
View File

@@ -0,0 +1,244 @@
CREATE TABLE IF NOT EXISTS spotify_artists_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT -1,
samplerate INTEGER NOT NULL DEFAULT -1,
bitdepth INTEGER NOT NULL DEFAULT -1,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1,
acoustid_id TEXT,
acoustid_fingerprint TEXT,
musicbrainz_album_artist_id TEXT,
musicbrainz_artist_id TEXT,
musicbrainz_original_artist_id TEXT,
musicbrainz_album_id TEXT,
musicbrainz_original_album_id TEXT,
musicbrainz_recording_id TEXT,
musicbrainz_track_id TEXT,
musicbrainz_disc_id TEXT,
musicbrainz_release_group_id TEXT,
musicbrainz_work_id TEXT,
ebur128_integrated_loudness_lufs REAL,
ebur128_loudness_range_lu REAL
);
CREATE TABLE IF NOT EXISTS spotify_albums_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT -1,
samplerate INTEGER NOT NULL DEFAULT -1,
bitdepth INTEGER NOT NULL DEFAULT -1,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1,
acoustid_id TEXT,
acoustid_fingerprint TEXT,
musicbrainz_album_artist_id TEXT,
musicbrainz_artist_id TEXT,
musicbrainz_original_artist_id TEXT,
musicbrainz_album_id TEXT,
musicbrainz_original_album_id TEXT,
musicbrainz_recording_id TEXT,
musicbrainz_track_id TEXT,
musicbrainz_disc_id TEXT,
musicbrainz_release_group_id TEXT,
musicbrainz_work_id TEXT,
ebur128_integrated_loudness_lufs REAL,
ebur128_loudness_range_lu REAL
);
CREATE TABLE IF NOT EXISTS spotify_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT -1,
samplerate INTEGER NOT NULL DEFAULT -1,
bitdepth INTEGER NOT NULL DEFAULT -1,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1,
acoustid_id TEXT,
acoustid_fingerprint TEXT,
musicbrainz_album_artist_id TEXT,
musicbrainz_artist_id TEXT,
musicbrainz_original_artist_id TEXT,
musicbrainz_album_id TEXT,
musicbrainz_original_album_id TEXT,
musicbrainz_recording_id TEXT,
musicbrainz_track_id TEXT,
musicbrainz_disc_id TEXT,
musicbrainz_release_group_id TEXT,
musicbrainz_work_id TEXT,
ebur128_integrated_loudness_lufs REAL,
ebur128_loudness_range_lu REAL
);
UPDATE schema_version SET version=20;

View File

@@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
DELETE FROM schema_version;
INSERT INTO schema_version (version) VALUES (18);
INSERT INTO schema_version (version) VALUES (20);
CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL,
@@ -422,6 +422,249 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
);
CREATE TABLE IF NOT EXISTS spotify_artists_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT -1,
samplerate INTEGER NOT NULL DEFAULT -1,
bitdepth INTEGER NOT NULL DEFAULT -1,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1,
acoustid_id TEXT,
acoustid_fingerprint TEXT,
musicbrainz_album_artist_id TEXT,
musicbrainz_artist_id TEXT,
musicbrainz_original_artist_id TEXT,
musicbrainz_album_id TEXT,
musicbrainz_original_album_id TEXT,
musicbrainz_recording_id TEXT,
musicbrainz_track_id TEXT,
musicbrainz_disc_id TEXT,
musicbrainz_release_group_id TEXT,
musicbrainz_work_id TEXT,
ebur128_integrated_loudness_lufs REAL,
ebur128_loudness_range_lu REAL
);
CREATE TABLE IF NOT EXISTS spotify_albums_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT -1,
samplerate INTEGER NOT NULL DEFAULT -1,
bitdepth INTEGER NOT NULL DEFAULT -1,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1,
acoustid_id TEXT,
acoustid_fingerprint TEXT,
musicbrainz_album_artist_id TEXT,
musicbrainz_artist_id TEXT,
musicbrainz_original_artist_id TEXT,
musicbrainz_album_id TEXT,
musicbrainz_original_album_id TEXT,
musicbrainz_recording_id TEXT,
musicbrainz_track_id TEXT,
musicbrainz_disc_id TEXT,
musicbrainz_release_group_id TEXT,
musicbrainz_work_id TEXT,
ebur128_integrated_loudness_lufs REAL,
ebur128_loudness_range_lu REAL
);
CREATE TABLE IF NOT EXISTS spotify_songs (
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT -1,
samplerate INTEGER NOT NULL DEFAULT -1,
bitdepth INTEGER NOT NULL DEFAULT -1,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT -1,
mtime INTEGER NOT NULL DEFAULT -1,
ctime INTEGER NOT NULL DEFAULT -1,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
lastseen INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1,
acoustid_id TEXT,
acoustid_fingerprint TEXT,
musicbrainz_album_artist_id TEXT,
musicbrainz_artist_id TEXT,
musicbrainz_original_artist_id TEXT,
musicbrainz_album_id TEXT,
musicbrainz_original_album_id TEXT,
musicbrainz_recording_id TEXT,
musicbrainz_track_id TEXT,
musicbrainz_disc_id TEXT,
musicbrainz_release_group_id TEXT,
musicbrainz_work_id TEXT,
ebur128_integrated_loudness_lufs REAL,
ebur128_loudness_range_lu REAL
);
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
title TEXT,
@@ -796,138 +1039,3 @@ CREATE INDEX IF NOT EXISTS idx_album ON songs (album);
CREATE INDEX IF NOT EXISTS idx_title ON songs (title);
CREATE VIEW IF NOT EXISTS duplicated_songs as select artist dup_artist, album dup_album, title dup_title from songs as inner_songs where artist != '' and album != '' and title != '' and unavailable = 0 group by artist, album , title having count(*) > 1;
CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS subsonic_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_artists_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_albums_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_songs_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE IF NOT EXISTS %allsongstables_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);

View File

@@ -0,0 +1,13 @@
#container {
background: %background;
border-radius: 10px;
border: 1px solid rgba(200, 200, 200, 75%);
}
#label1 {
font-weight: bold;
}
#label2 {
font-size: 7.5pt;
}

View File

@@ -35,32 +35,32 @@
background-color: %palette-base;
}
QToolButton {
QToolButton[accessibleName="MenuPopupToolButton"] {
border: 2px solid transparent;
border-radius: 3px;
padding: 1px;
}
QToolButton:hover {
QToolButton:hover[accessibleName="MenuPopupToolButton"] {
border: 2px solid %palette-highlight;
background-color: %palette-highlight-lighter;
}
QToolButton:pressed {
QToolButton:pressed[accessibleName="MenuPopupToolButton"] {
border: 2px solid %palette-highlight-darker;
background-color: %palette-highlight-lighter;
}
QToolButton[popupMode="MenuButtonPopup"], QToolButton:hover[popupMode="MenuButtonPopup"], QToolButton:pressed[popupMode="MenuButtonPopup"] {
QToolButton[popupMode="MenuButtonPopup"][accessibleName="MenuPopupToolButton"], QToolButton:hover[popupMode="MenuButtonPopup"][accessibleName="MenuPopupToolButton"], QToolButton:pressed[popupMode="MenuButtonPopup"][accessibleName="MenuPopupToolButton"] {
padding-right: 16px;
}
/* For backwards compatibility with Qt 5 as it does not support property name */
QToolButton[popupMode="1"], QToolButton:hover[popupMode="1"], QToolButton:pressed[popupMode="1"] {
QToolButton[popupMode="1"][accessibleName="MenuPopupToolButton"], QToolButton:hover[popupMode="1"][accessibleName="MenuPopupToolButton"], QToolButton:pressed[popupMode="1"][accessibleName="MenuPopupToolButton"] {
padding-right: 16px;
}
QToolButton::menu-button {
QToolButton::menu-button[accessibleName="MenuPopupToolButton"] {
width: 16px;
border: none;
}

12
debian/CMakeLists.txt vendored
View File

@@ -4,19 +4,7 @@ if(LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -cs" OUTPUT_VARIABLE DEB_CODENAME OUTPUT_STRIP_TRAILING_WHITESPACE)
if(DEB_CODENAME AND DEB_DATE)
if(QT_VERSION_MAJOR EQUAL 5)
set(DEBIAN_BUILD_DEPENDS_QT_PACKAGES qtbase5-dev,qtbase5-dev-tools,qttools5-dev,qttools5-dev-tools,libqt5x11extras5-dev)
set(DEBIAN_DEPENDS_QT_PACKAGES libqt5sql5-sqlite)
endif()
if(QT_VERSION_MAJOR EQUAL 6)
set(DEBIAN_BUILD_DEPENDS_QT_PACKAGES qt6-base-dev,qt6-base-dev-tools,qt6-tools-dev,qt6-tools-dev-tools,qt6-l10n-tools)
set(DEBIAN_DEPENDS_QT_PACKAGES libqt6sql6-sqlite,qt6-qpa-plugins)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/control.in ${CMAKE_CURRENT_SOURCE_DIR}/control @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/changelog.in ${CMAKE_CURRENT_SOURCE_DIR}/changelog)
endif()
endif()

View File

@@ -8,17 +8,18 @@ Build-Depends: debhelper (>= 11),
cmake,
gcc,
g++,
protobuf-compiler,
libglib2.0-dev,
libdbus-1-dev,
libprotobuf-dev,
libboost-dev,
libsqlite3-dev,
libasound2-dev,
libpulse-dev,
libtag1-dev,
libicu-dev,
@DEBIAN_BUILD_DEPENDS_QT_PACKAGES@,
qt6-base-dev,
qt6-base-dev-tools,
qt6-tools-dev,
qt6-tools-dev-tools,
qt6-l10n-tools,
libgstreamer1.0-dev,
libgstreamer-plugins-base1.0-dev,
libcdio-dev,
@@ -33,7 +34,8 @@ Package: strawberry
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
@DEBIAN_DEPENDS_QT_PACKAGES@,
libqt6sql6-sqlite,
qt6-qpa-plugins,
gstreamer1.0-plugins-base,
gstreamer1.0-plugins-good,
gstreamer1.0-alsa,
@@ -53,7 +55,7 @@ Description: music player and music collection organizer
- Edit tags on audio files
- Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net
- Audio analyzer
- Audio equalizer
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic

61
debian/copyright vendored
View File

@@ -5,14 +5,12 @@ Source: https://github.com/strawberrymusicplayer/strawberry
Files: *
Copyright: 2010-2015, David Sansome <me@davidsansome.com>
2012-2014, 2017-2023 Jonas Kvinge <jonas@jkvinge.net>
2012-2014, 2017-2024 Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/utilities/timeconstants.h
ext/libstrawberry-common/core/logging.cpp
ext/libstrawberry-common/core/logging.h
ext/libstrawberry-common/core/messagehandler.cpp
ext/libstrawberry-common/core/messagehandler.h
src/core/logging.cpp
src/core/logging.h
Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
2018-2022, Jonas Kvinge <jonas@jkvinge.net>
License: Apache-2.0
@@ -31,18 +29,20 @@ Files: src/core/main.h
src/engine/alsapcmdevicefinder.h
src/engine/mmdevicefinder.cpp
src/engine/mmdevicefinder.h
src/engine/uwpdevicefinder.cpp
src/engine/uwpdevicefinder.h
src/engine/devicefinder.cpp
src/engine/devicefinder.h
src/engine/enginedevice.cpp
src/engine/enginedevice.h
src/engine/enginemetadata.cpp
src/engine/enginemetadata.h
src/internet/internetservice.cpp
src/internet/internetservice.h
src/internet/internettabsview.cpp
src/internet/internettabsview.h
src/internet/internetsongsview.cpp
src/internet/internetsongsview.h
src/streaming/streamingservice.cpp
src/streaming/streamingservice.h
src/streaming/streamingtabsview.cpp
src/streaming/streamingtabsview.h
src/streaming/streamingsongsview.cpp
src/streaming/streamingsongsview.h
src/settings/backendsettingspage.cpp
src/settings/backendsettingspage.h
src/settings/coverssettingspage.cpp
@@ -55,6 +55,8 @@ Files: src/core/main.h
src/settings/subsonicsettingspage.h
src/settings/tidalsettingspage.cpp
src/settings/tidalsettingspage.h
src/settings/spotifysettingspage.cpp
src/settings/spotifysettingspage.h
src/covermanager/jsoncoverprovider.cpp
src/covermanager/jsoncoverprovider.h
src/covermanager/lastfmcoverprovider.cpp
@@ -65,16 +67,16 @@ Files: src/core/main.h
src/covermanager/deezercoverprovider.h
src/covermanager/tidalcoverprovider.cpp
src/covermanager/tidalcoverprovider.h
src/covermanager/opentidalcoverprovider.cpp
src/covermanager/opentidalcoverprovider.h
src/covermanager/qobuzcoverprovider.cpp
src/covermanager/qobuzcoverprovider.h
src/covermanager/spotifycoverprovider.cpp
src/covermanager/spotifycoverprovider.h
src/covermanager/musixmatchcoverprovider.cpp
src/covermanager/musixmatchcoverprovider.h
src/globalshortcuts/globalshortcutsbackend-kde.cpp
src/globalshortcuts/globalshortcutsbackend-kde.h
src/globalshortcuts/globalshortcutsbackend-mate.cpp
src/globalshortcuts/globalshortcutsbackend-mate.h
src/globalshortcuts/globalshortcutsbackend-kglobalaccel.cpp
src/globalshortcuts/globalshortcutsbackend-kglobalaccel.h
src/globalshortcuts/globalshortcutsbackend-x11.cpp
src/globalshortcuts/globalshortcutsbackend-x11.h
src/globalshortcuts/globalshortcutsbackend-win.cpp
@@ -91,14 +93,14 @@ Files: src/core/main.h
src/tidal/*
src/qobuz/*
src/radios/*
src/spotify/*
src/transcoder/transcoderoptionswavpack.cpp
src/transcoder/transcoderoptionswavpack.h
ext/libstrawberry-tagreader/tagreadertagparser.cpp
ext/libstrawberry-tagreader/tagreadertagparser.h
ext/macdeploycheck/*
src/widgets/resizabletextedit.cpp
src/widgets/resizabletextedit.h
Copyright: 2012-2014, 2017-2023, Jonas Kvinge <jonas@jkvinge.net>
src/widgets/fancytabdata.cpp
src/widgets/fancytabdata.h
Copyright: 2012-2014, 2017-2024, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/engine/enginebase.cpp
@@ -119,6 +121,8 @@ License: GPL-2+
Files: src/widgets/fancytabwidget.cpp
src/widgets/fancytabwidget.h
src/widgets/fancytabbar.cpp
src/widgets/fancytabbar.h
Copyright: 2018, Vikram Ambrose <ambroseworks@gmail.com>
2018, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
@@ -208,8 +212,8 @@ Files: src/device/udisks2lister.cpp
Copyright: 2016, Valeriy Malov <jazzvoid@gmail.com>
License: GPL-3+
Files: src/internet/localredirectserver.cpp
src/internet/localredirectserver.h
Files: src/core/localredirectserver.cpp
src/core/localredirectserver.h
Copyright: 2012, 2014, John Maguire <john.maguire@gmail.com>
2014, Krzysztof Sobiecki <sobkas@gmail.com>
2018-2021, Jonas Kvinge <jonas@jkvinge.net>
@@ -248,25 +252,24 @@ Files: src/core/stylehelper.cpp
Copyright: 2016 The Qt Company Ltd.
License: GPL-3+
Files: ext/gstmoodbar/gstfastspectrum.cpp
ext/gstmoodbar/gstfastspectrum.h
Files: 3rdparty/gstfastspectrum/gstfastspectrum.cpp
3rdparty/gstfastspectrum/gstfastspectrum.h
Copyright: 1999 Erik Walthinsen <omega@cse.ogi.edu>
2006,2011 Stefan Kost <ensonic@users.sf.net>
2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
2018-2024 Jonas Kvinge <jonas@jkvinge.net>
License: GPL-2+
Files: src/widgets/qsearchfield_nonmac.cpp
Files: src/widgets/qsearchfield_qt.cpp
src/widgets/qsearchfield_mac.mm
src/widgets/qsearchfield.h
src/widgets/qocoa_mac.h
src/widgets/searchfield_qt_private.cpp
src/widgets/searchfield_qt_private.h
Copyright: 2011, Mike McQuaid <mike@mikemcquaid.com>
2018-2024, Jonas Kvinge <jonas@jkvinge.net>
License: Expat
Files: 3rdparty/SPMediaKeyTap/*
Copyright: 2010, Spotify AB
2011, Joachim Bengtsson
License: BSD-3-clause
Files: 3rdparty/kdsingleapplication/*
Copyright: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
License: MIT

10
dist/CMakeLists.txt vendored
View File

@@ -4,11 +4,17 @@ if(RPM_DISTRO AND RPM_DATE)
endif(RPM_DISTRO AND RPM_DATE)
if(APPLE)
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
set(LSMinimumSystemVersion $ENV{MACOSX_DEPLOYMENT_TARGET})
else()
set(LSMinimumSystemVersion 12.0)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
endif(APPLE)
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/windres.rc.in ${CMAKE_BINARY_DIR}/windres.rc)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_BINARY_DIR}/strawberry.nsi @ONLY)
endif(WIN32)
if(UNIX AND NOT APPLE)
@@ -17,7 +23,7 @@ if(UNIX AND NOT APPLE)
install(FILES ../data/icons/128x128/strawberry.png DESTINATION share/icons/hicolor/128x128/apps/)
install(FILES unix/org.strawberrymusicplayer.strawberry.desktop DESTINATION share/applications)
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
install(FILES unix/strawberry.1 unix/strawberry-tagreader.1 DESTINATION share/man/man1)
install(FILES unix/strawberry.1 DESTINATION share/man/man1)
endif(UNIX AND NOT APPLE)
if(APPLE)

View File

@@ -33,7 +33,7 @@
<key>LSApplicationCategoryType</key>
<string>public.app-category.music</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<string>@LSMinimumSystemVersion@</string>
<key>SUFeedURL</key>
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
<key>SUPublicEDKey</key>

View File

@@ -62,6 +62,7 @@ cp -v -f "${GST_PLUGIN_SCANNER}" "${bundledir}/Contents/PlugIns/" || exit 1
install_name_tool -add_rpath "@loader_path/../Frameworks" "${bundledir}/Contents/PlugIns/$(basename ${GST_PLUGIN_SCANNER})" || exit 1
gst_plugins="
libgstadaptivedemux2
libgstaes
libgstaiff
libgstapetag
@@ -70,16 +71,14 @@ libgstasf
libgstasfmux
libgstaudioconvert
libgstaudiofx
libgstaudiomixer
libgstaudioparsers
libgstaudiorate
libgstaudioresample
libgstaudiotestsrc
libgstautodetect
libgstbs2b
libgstcdio
libgstcoreelements
libgstdash
libgstdsd
libgstequalizer
libgstfaac
libgstfaad
@@ -92,6 +91,10 @@ libgstid3demux
libgstid3tag
libgstisomp4
libgstlame
libgstmpegpsdemux
libgstmpegpsmux
libgstmpegtsdemux
libgstmpegtsmux
libgstlibav
libgstmpg123
libgstmusepack
@@ -108,6 +111,7 @@ libgstrtsp
libgstsoup
libgstspectrum
libgstspeex
libgstspotify
libgsttaglib
libgsttcp
libgsttwolame

View File

@@ -6,17 +6,19 @@
<project_license>GPL-3.0+</project_license>
<provides>
<binary>strawberry</binary>
<binary>strawberry-tagreader</binary>
</provides>
<name>Strawberry Music Player</name>
<summary>A music player and collection organizer</summary>
<url type="homepage">https://www.strawberrymusicplayer.org/</url>
<url type="bugtracker">https://github.com/strawberrymusicplayer/strawberry/</url>
<developer id="net.jkvinge.jonas">
<name>Jonas Kvinge</name>
</developer>
<translation type="qt">strawberry</translation>
<content_rating type="oars-1.1" />
<description>
<p>
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.
Strawberry is a music player and music collection organizer. It is aimed at music collectors and audiophiles. Strawberry is free software released under GPL. It's written in C++ using the Qt framework and GStreamer.
</p>
<p>Features:</p>
<ul>
@@ -29,13 +31,12 @@
<li>Edit tags on audio files</li>
<li>Automatically retrieve tags from MusicBrainz</li>
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
<li>Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com</li>
<li>Support for multiple backends</li>
<li>Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net</li>
<li>Audio analyzer and equalizer</li>
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
<li>Scrobbler with support for Last.fm, Libre.fm and ListenBrainz</li>
<li>Streaming support for Subsonic-compatible servers</li>
<li>Unofficial streaming support for Tidal and Qobuz</li>
<li>Unofficial streaming support for Tidal, Spotify and Qobuz</li>
</ul>
</description>
<screenshots>
@@ -50,6 +51,13 @@
</screenshots>
<update_contact>eclipseo@fedoraproject.org</update_contact>
<releases>
<release version="1.2.2" date="2024-11-23"/>
<release version="1.2.1" date="2024-11-21"/>
<release version="1.1.3" date="2024-09-21"/>
<release version="1.1.2" date="2024-09-12"/>
<release version="1.1.1" date="2024-07-22"/>
<release version="1.1.0" date="2024-07-14"/>
<release version="1.0.23" date="2024-01-11"/>
<release version="1.0.22" date="2023-12-09"/>
<release version="1.0.21" date="2023-10-21"/>
<release version="1.0.20" date="2023-09-24"/>

View File

@@ -3,8 +3,10 @@ Version=1.0
Type=Application
Name=Strawberry
GenericName=Strawberry Music Player
GenericName[fr]=Lecteur de musique Strawberry
GenericName[ru]=Музыкальный проигрыватель Strawberry
Comment=Plays music
Comment[fr]=Joue de la musique
Comment[ru]=Прослушивание музыки
Exec=strawberry %U
TryExec=strawberry

View File

@@ -1,10 +0,0 @@
.TH STRAWBERRY-TAGREADER "1"
.SH NAME
strawberry-tagreader \- internal tag reader for strawberry
.SH SYNOPSIS
.B strawberry-tagreader
.SH DESCRIPTION
This program is used internally by Strawberry to parse tags in music files without exposing the whole application to crashes caused by malformed files. It is not meant to be run on its own.
.SH "AUTHORS"
.PP
Strawberry main developer is Jonas Kvinge <jonas@jkvinge.net>.

View File

@@ -21,7 +21,6 @@ BuildRequires: gcc-c++
BuildRequires: hicolor-icon-theme
BuildRequires: make
BuildRequires: git
BuildRequires: gettext
BuildRequires: desktop-file-utils
%if 0%{?suse_version}
BuildRequires: update-desktop-files
@@ -38,9 +37,7 @@ BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(gio-2.0)
BuildRequires: pkgconfig(gio-unix-2.0)
BuildRequires: pkgconfig(gthread-2.0)
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(alsa)
BuildRequires: pkgconfig(protobuf)
BuildRequires: pkgconfig(sqlite3) >= 3.9
BuildRequires: pkgconfig(taglib)
BuildRequires: pkgconfig(fftw3)
@@ -55,9 +52,6 @@ BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Gui)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Widgets)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Test)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@LinguistTools)
%if "@QT_VERSION_MAJOR@" == "5"
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@X11Extras)
%endif
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
BuildRequires: pkgconfig(gstreamer-audio-1.0)
@@ -69,18 +63,10 @@ BuildRequires: pkgconfig(libcdio)
BuildRequires: pkgconfig(libebur128)
BuildRequires: pkgconfig(libgpod-1.0)
BuildRequires: pkgconfig(libmtp)
%if 0%{?suse_version} || 0%{?fedora_version}
BuildRequires: pkgconfig(libvlc)
%endif
%if 0%{?suse_version}
%if "@QT_VERSION_MAJOR@" == "6"
Requires: qt6-sql-sqlite
Requires: qt6-network-tls
%endif
%if "@QT_VERSION_MAJOR@" == "5"
Requires: libQt5Sql5-sqlite
%endif
%endif
%description
@@ -99,7 +85,7 @@ Features:
- Edit tags on audio files
- Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com and elyrics.net
- Support for multiple backends
- Audio analyzer
- Audio equalizer
@@ -151,11 +137,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicpl
%doc README.md Changelog
%license COPYING
%{_bindir}/strawberry
%{_bindir}/strawberry-tagreader
%{_datadir}/applications/*.desktop
%{_datadir}/icons/hicolor/*/apps/strawberry.*
%{_mandir}/man1/%{name}.1.*
%{_mandir}/man1/%{name}-tagreader.1.*
%if 0%{?suse_version}
%{_datadir}/metainfo/*.appdata.xml
%else

View File

@@ -109,7 +109,11 @@
Unicode True
!ifdef debug
SetCompressor lzma
!else
SetCompressor /SOLID lzma
!endif
!include "MUI2.nsh"
!include "FileAssociation.nsh"
@@ -232,7 +236,6 @@ Section "Strawberry" Strawberry
; Common executables
File "strawberry.exe"
File "strawberry-tagreader.exe"
File "strawberry.ico"
File "sqlite3.exe"
File "gst-launch-1.0.exe"
@@ -269,6 +272,7 @@ Section "Strawberry" Strawberry
File "libffi-8.dll"
File "libfreetype-6.dll"
File "libgcrypt-20.dll"
File "libgetopt.dll"
File "libgio-2.0-0.dll"
File "libglib-2.0-0.dll"
File "libgme.dll"
@@ -282,8 +286,10 @@ Section "Strawberry" Strawberry
File "libgstaudio-1.0-0.dll"
File "libgstbadaudio-1.0-0.dll"
File "libgstbase-1.0-0.dll"
File "libgstcodecparsers-1.0-0.dll"
File "libgstfft-1.0-0.dll"
File "libgstisoff-1.0-0.dll"
File "libgstmpegts-1.0-0.dll"
File "libgstnet-1.0-0.dll"
File "libgstpbutils-1.0-0.dll"
File "libgstreamer-1.0-0.dll"
@@ -328,52 +334,9 @@ Section "Strawberry" Strawberry
File "libvorbisfile-3.dll"
File "libwavpack-1.dll"
File "libwinpthread-1.dll"
File "libxml2-2.dll"
File "libzstd.dll"
File "zlib1.dll"
File "libabsl_base.dll"
File "libabsl_city.dll"
File "libabsl_cord.dll"
File "libabsl_cord_internal.dll"
File "libabsl_cordz_handle.dll"
File "libabsl_cordz_info.dll"
File "libabsl_crc32c.dll"
File "libabsl_crc_cord_state.dll"
File "libabsl_crc_internal.dll"
File "libabsl_die_if_null.dll"
File "libabsl_examine_stack.dll"
File "libabsl_hash.dll"
File "libabsl_int128.dll"
File "libabsl_kernel_timeout_internal.dll"
File "libabsl_log_globals.dll"
File "libabsl_log_internal_check_op.dll"
File "libabsl_log_internal_conditions.dll"
File "libabsl_log_internal_format.dll"
File "libabsl_log_internal_globals.dll"
File "libabsl_log_internal_log_sink_set.dll"
File "libabsl_log_internal_message.dll"
File "libabsl_log_internal_nullguard.dll"
File "libabsl_log_internal_proto.dll"
File "libabsl_log_sink.dll"
File "libabsl_low_level_hash.dll"
File "libabsl_malloc_internal.dll"
File "libabsl_raw_hash_set.dll"
File "libabsl_raw_logging_internal.dll"
File "libabsl_spinlock_wait.dll"
File "libabsl_stacktrace.dll"
File "libabsl_status.dll"
File "libabsl_statusor.dll"
File "libabsl_strerror.dll"
File "libabsl_str_format_internal.dll"
File "libabsl_strings.dll"
File "libabsl_strings_internal.dll"
File "libabsl_symbolize.dll"
File "libabsl_synchronization.dll"
File "libabsl_throw_delegate.dll"
File "libabsl_time.dll"
File "libabsl_time_zone.dll"
!ifdef debug
File "gdb.exe"
File "libexpat-1.dll"
@@ -383,7 +346,6 @@ Section "Strawberry" Strawberry
File "libpcre2-16d.dll"
File "libreadline8.dll"
File "libtermcap.dll"
File "libabsl_graphcycles_internal.dll"
!else
File "libpcre2-8.dll"
File "libpcre2-16.dll"
@@ -412,18 +374,22 @@ Section "Strawberry" Strawberry
File "faad-2.dll"
File "fdk-aac.dll"
File "ffi-7.dll"
File "getopt.dll"
File "gio-2.0-0.dll"
File "glib-2.0-0.dll"
File "gme.dll"
File "gmodule-2.0-0.dll"
File "gnutls.dll"
File "gobject-2.0-0.dll"
File "gstadaptivedemux-1.0-0.dll"
File "gstapp-1.0-0.dll"
File "gstaudio-1.0-0.dll"
File "gstbadaudio-1.0-0.dll"
File "gstbase-1.0-0.dll"
File "gstcodecparsers-1.0-0.dll"
File "gstfft-1.0-0.dll"
File "gstisoff-1.0-0.dll"
File "gstmpegts-1.0-0.dll"
File "gstnet-1.0-0.dll"
File "gstpbutils-1.0-0.dll"
File "gstreamer-1.0-0.dll"
@@ -458,14 +424,12 @@ Section "Strawberry" Strawberry
File "vorbis.dll"
File "vorbisfile.dll"
File "wavpackdll.dll"
File "abseil_dll.dll"
!ifdef release
File "freetype.dll"
File "libiconv.dll"
File "libpng16.dll"
File "libspeex.dll"
File "libxml2.dll"
File "pcre2-8.dll"
File "pcre2-16.dll"
File "twolame.dll"
@@ -476,7 +440,6 @@ Section "Strawberry" Strawberry
File "libiconvd.dll"
File "libpng16d.dll"
File "libspeexd.dll"
File "libxml2d.dll"
File "pcre2-8d.dll"
File "pcre2-16d.dll"
File "twolamed.dll"
@@ -493,16 +456,12 @@ Section "Strawberry" Strawberry
; Common files
File "icudt74.dll"
File "icudt76.dll"
File "libfftw3-3.dll"
!ifdef debug
File "libprotobufd.dll"
!else
File "libprotobuf.dll"
!endif
!ifdef msvc && debug
File "icuin74d.dll"
File "icuuc74d.dll"
File "icuin76d.dll"
File "icuuc76d.dll"
File "libxml2d.dll"
File "Qt6Concurrentd.dll"
File "Qt6Cored.dll"
File "Qt6Guid.dll"
@@ -510,8 +469,9 @@ Section "Strawberry" Strawberry
File "Qt6Sqld.dll"
File "Qt6Widgetsd.dll"
!else
File "icuin74.dll"
File "icuuc74.dll"
File "icuin76.dll"
File "icuuc76.dll"
File "libxml2.dll"
File "Qt6Concurrent.dll"
File "Qt6Core.dll"
File "Qt6Gui.dll"
@@ -520,13 +480,13 @@ Section "Strawberry" Strawberry
File "Qt6Widgets.dll"
!endif
File "avcodec-60.dll"
File "avfilter-9.dll"
File "avformat-60.dll"
File "avutil-58.dll"
File "postproc-57.dll"
File "swresample-4.dll"
File "swscale-7.dll"
File "avcodec-61.dll"
File "avfilter-10.dll"
File "avformat-61.dll"
File "avutil-59.dll"
File "postproc-58.dll"
File "swresample-5.dll"
File "swscale-8.dll"
; Register Strawberry with Default Programs
Var /GLOBAL AppIcon
@@ -584,9 +544,9 @@ SectionEnd
Section "Qt styles" styles
SetOutPath "$INSTDIR\styles"
!ifdef msvc && debug
File "/oname=qwindowsvistastyled.dll" "styles\qwindowsvistastyled.dll"
File "/oname=qmodernwindowsstyled.dll" "styles\qmodernwindowsstyled.dll"
!else
File "/oname=qwindowsvistastyle.dll" "styles\qwindowsvistastyle.dll"
File "/oname=qmodernwindowsstyle.dll" "styles\qmodernwindowsstyle.dll"
!endif
SectionEnd
@@ -627,6 +587,7 @@ Section "Gstreamer plugins" gstreamer-plugins
SetOutPath "$INSTDIR\gstreamer-plugins"
!ifdef mingw
File "/oname=libgstadaptivedemux2.dll" "gstreamer-plugins\libgstadaptivedemux2.dll"
File "/oname=libgstaes.dll" "gstreamer-plugins\libgstaes.dll"
File "/oname=libgstaiff.dll" "gstreamer-plugins\libgstaiff.dll"
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
@@ -635,16 +596,14 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstasfmux.dll" "gstreamer-plugins\libgstasfmux.dll"
File "/oname=libgstaudioconvert.dll" "gstreamer-plugins\libgstaudioconvert.dll"
File "/oname=libgstaudiofx.dll" "gstreamer-plugins\libgstaudiofx.dll"
File "/oname=libgstaudiomixer.dll" "gstreamer-plugins\libgstaudiomixer.dll"
File "/oname=libgstaudioparsers.dll" "gstreamer-plugins\libgstaudioparsers.dll"
File "/oname=libgstaudiorate.dll" "gstreamer-plugins\libgstaudiorate.dll"
File "/oname=libgstaudioresample.dll" "gstreamer-plugins\libgstaudioresample.dll"
File "/oname=libgstaudiotestsrc.dll" "gstreamer-plugins\libgstaudiotestsrc.dll"
File "/oname=libgstautodetect.dll" "gstreamer-plugins\libgstautodetect.dll"
File "/oname=libgstbs2b.dll" "gstreamer-plugins\libgstbs2b.dll"
File "/oname=libgstcoreelements.dll" "gstreamer-plugins\libgstcoreelements.dll"
File "/oname=libgstdash.dll" "gstreamer-plugins\libgstdash.dll"
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
File "/oname=libgstdsd.dll" "gstreamer-plugins\libgstdsd.dll"
File "/oname=libgstequalizer.dll" "gstreamer-plugins\libgstequalizer.dll"
File "/oname=libgstfaac.dll" "gstreamer-plugins\libgstfaac.dll"
File "/oname=libgstfaad.dll" "gstreamer-plugins\libgstfaad.dll"
@@ -659,6 +618,10 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstisomp4.dll" "gstreamer-plugins\libgstisomp4.dll"
File "/oname=libgstlame.dll" "gstreamer-plugins\libgstlame.dll"
File "/oname=libgstlibav.dll" "gstreamer-plugins\libgstlibav.dll"
File "/oname=libgstmpegpsdemux.dll" "gstreamer-plugins\libgstmpegpsdemux.dll"
File "/oname=libgstmpegpsmux.dll" "gstreamer-plugins\libgstmpegpsmux.dll"
File "/oname=libgstmpegtsdemux.dll" "gstreamer-plugins\libgstmpegtsdemux.dll"
File "/oname=libgstmpegtsmux.dll" "gstreamer-plugins\libgstmpegtsmux.dll"
File "/oname=libgstmpg123.dll" "gstreamer-plugins\libgstmpg123.dll"
File "/oname=libgstmusepack.dll" "gstreamer-plugins\libgstmusepack.dll"
File "/oname=libgstogg.dll" "gstreamer-plugins\libgstogg.dll"
@@ -681,6 +644,7 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstvolume.dll" "gstreamer-plugins\libgstvolume.dll"
File "/oname=libgstvorbis.dll" "gstreamer-plugins\libgstvorbis.dll"
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
File "/oname=libgstwaveform.dll" "gstreamer-plugins\libgstwaveform.dll"
File "/oname=libgstwavenc.dll" "gstreamer-plugins\libgstwavenc.dll"
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
File "/oname=libgstwavparse.dll" "gstreamer-plugins\libgstwavparse.dll"
@@ -688,24 +652,24 @@ Section "Gstreamer plugins" gstreamer-plugins
!endif ; MinGW
!ifdef msvc
File "/oname=gstadaptivedemux2.dll" "gstreamer-plugins\gstadaptivedemux2.dll"
File "/oname=gstaes.dll" "gstreamer-plugins\gstaes.dll"
File "/oname=gstaiff.dll" "gstreamer-plugins\gstaiff.dll"
File "/oname=gstapetag.dll" "gstreamer-plugins\gstapetag.dll"
File "/oname=gstapp.dll" "gstreamer-plugins\gstapp.dll"
File "/oname=gstasf.dll" "gstreamer-plugins\gstasf.dll"
File "/oname=gstasfmux.dll" "gstreamer-plugins\gstasfmux.dll"
File "/oname=gstasio.dll" "gstreamer-plugins\gstasio.dll"
File "/oname=gstaudioconvert.dll" "gstreamer-plugins\gstaudioconvert.dll"
File "/oname=gstaudiofx.dll" "gstreamer-plugins\gstaudiofx.dll"
File "/oname=gstaudiomixer.dll" "gstreamer-plugins\gstaudiomixer.dll"
File "/oname=gstaudioparsers.dll" "gstreamer-plugins\gstaudioparsers.dll"
File "/oname=gstaudiorate.dll" "gstreamer-plugins\gstaudiorate.dll"
File "/oname=gstaudioresample.dll" "gstreamer-plugins\gstaudioresample.dll"
File "/oname=gstaudiotestsrc.dll" "gstreamer-plugins\gstaudiotestsrc.dll"
File "/oname=gstautodetect.dll" "gstreamer-plugins\gstautodetect.dll"
File "/oname=gstbs2b.dll" "gstreamer-plugins\gstbs2b.dll"
File "/oname=gstcoreelements.dll" "gstreamer-plugins\gstcoreelements.dll"
File "/oname=gstdash.dll" "gstreamer-plugins\gstdash.dll"
File "/oname=gstdirectsound.dll" "gstreamer-plugins\gstdirectsound.dll"
File "/oname=gstdsd.dll" "gstreamer-plugins\gstdsd.dll"
File "/oname=gstequalizer.dll" "gstreamer-plugins\gstequalizer.dll"
File "/oname=gstfaac.dll" "gstreamer-plugins\gstfaac.dll"
File "/oname=gstfaad.dll" "gstreamer-plugins\gstfaad.dll"
@@ -720,6 +684,10 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=gstisomp4.dll" "gstreamer-plugins\gstisomp4.dll"
File "/oname=gstlame.dll" "gstreamer-plugins\gstlame.dll"
File "/oname=gstlibav.dll" "gstreamer-plugins\gstlibav.dll"
File "/oname=gstmpegpsdemux.dll" "gstreamer-plugins\gstmpegpsdemux.dll"
File "/oname=gstmpegpsmux.dll" "gstreamer-plugins\gstmpegpsmux.dll"
File "/oname=gstmpegtsdemux.dll" "gstreamer-plugins\gstmpegtsdemux.dll"
File "/oname=gstmpegtsmux.dll" "gstreamer-plugins\gstmpegtsmux.dll"
File "/oname=gstmpg123.dll" "gstreamer-plugins\gstmpg123.dll"
File "/oname=gstmusepack.dll" "gstreamer-plugins\gstmusepack.dll"
File "/oname=gstogg.dll" "gstreamer-plugins\gstogg.dll"
@@ -742,12 +710,15 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=gstvolume.dll" "gstreamer-plugins\gstvolume.dll"
File "/oname=gstvorbis.dll" "gstreamer-plugins\gstvorbis.dll"
File "/oname=gstwasapi.dll" "gstreamer-plugins\gstwasapi.dll"
; Disable wasapi2 until issue (https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2870) is fixed.
;File "/oname=gstwasapi2.dll" "gstreamer-plugins\gstwasapi2.dll"
File "/oname=gstwasapi2.dll" "gstreamer-plugins\gstwasapi2.dll"
File "/oname=gstwaveform.dll" "gstreamer-plugins\gstwaveform.dll"
File "/oname=gstwavenc.dll" "gstreamer-plugins\gstwavenc.dll"
File "/oname=gstwavpack.dll" "gstreamer-plugins\gstwavpack.dll"
File "/oname=gstwavparse.dll" "gstreamer-plugins\gstwavparse.dll"
File "/oname=gstxingmux.dll" "gstreamer-plugins\gstxingmux.dll"
!ifdef arch_x64
File "/oname=gstspotify.dll" "gstreamer-plugins\gstspotify.dll"
!endif
!endif ; MSVC
SectionEnd
@@ -788,7 +759,6 @@ Section "Uninstall"
; Delete all the files
Delete "$INSTDIR\strawberry.exe"
Delete "$INSTDIR\strawberry-tagreader.exe"
Delete "$INSTDIR\strawberry.ico"
Delete "$INSTDIR\sqlite3.exe"
Delete "$INSTDIR\gst-launch-1.0.exe"
@@ -826,6 +796,7 @@ Section "Uninstall"
Delete "$INSTDIR\libffi-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libgcrypt-20.dll"
Delete "$INSTDIR\libgetopt.dll"
Delete "$INSTDIR\libgio-2.0-0.dll"
Delete "$INSTDIR\libglib-2.0-0.dll"
Delete "$INSTDIR\libgme.dll"
@@ -839,8 +810,10 @@ Section "Uninstall"
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
Delete "$INSTDIR\libgstbadaudio-1.0-0.dll"
Delete "$INSTDIR\libgstbase-1.0-0.dll"
Delete "$INSTDIR\libgstcodecparsers-1.0-0.dll"
Delete "$INSTDIR\libgstfft-1.0-0.dll"
Delete "$INSTDIR\libgstisoff-1.0-0.dll"
Delete "$INSTDIR\libgstmpegts-1.0-0.dll"
Delete "$INSTDIR\libgstnet-1.0-0.dll"
Delete "$INSTDIR\libgstpbutils-1.0-0.dll"
Delete "$INSTDIR\libgstreamer-1.0-0.dll"
@@ -885,52 +858,9 @@ Section "Uninstall"
Delete "$INSTDIR\libvorbisfile-3.dll"
Delete "$INSTDIR\libwavpack-1.dll"
Delete "$INSTDIR\libwinpthread-1.dll"
Delete "$INSTDIR\libxml2-2.dll"
Delete "$INSTDIR\libzstd.dll"
Delete "$INSTDIR\zlib1.dll"
Delete "$INSTDIR\libabsl_base.dll"
Delete "$INSTDIR\libabsl_city.dll"
Delete "$INSTDIR\libabsl_cord.dll"
Delete "$INSTDIR\libabsl_cord_internal.dll"
Delete "$INSTDIR\libabsl_cordz_handle.dll"
Delete "$INSTDIR\libabsl_cordz_info.dll"
Delete "$INSTDIR\libabsl_crc32c.dll"
Delete "$INSTDIR\libabsl_crc_cord_state.dll"
Delete "$INSTDIR\libabsl_crc_internal.dll"
Delete "$INSTDIR\libabsl_die_if_null.dll"
Delete "$INSTDIR\libabsl_examine_stack.dll"
Delete "$INSTDIR\libabsl_hash.dll"
Delete "$INSTDIR\libabsl_int128.dll"
Delete "$INSTDIR\libabsl_kernel_timeout_internal.dll"
Delete "$INSTDIR\libabsl_log_globals.dll"
Delete "$INSTDIR\libabsl_log_internal_check_op.dll"
Delete "$INSTDIR\libabsl_log_internal_conditions.dll"
Delete "$INSTDIR\libabsl_log_internal_format.dll"
Delete "$INSTDIR\libabsl_log_internal_globals.dll"
Delete "$INSTDIR\libabsl_log_internal_log_sink_set.dll"
Delete "$INSTDIR\libabsl_log_internal_message.dll"
Delete "$INSTDIR\libabsl_log_internal_nullguard.dll"
Delete "$INSTDIR\libabsl_log_internal_proto.dll"
Delete "$INSTDIR\libabsl_log_sink.dll"
Delete "$INSTDIR\libabsl_low_level_hash.dll"
Delete "$INSTDIR\libabsl_malloc_internal.dll"
Delete "$INSTDIR\libabsl_raw_hash_set.dll"
Delete "$INSTDIR\libabsl_raw_logging_internal.dll"
Delete "$INSTDIR\libabsl_spinlock_wait.dll"
Delete "$INSTDIR\libabsl_stacktrace.dll"
Delete "$INSTDIR\libabsl_status.dll"
Delete "$INSTDIR\libabsl_statusor.dll"
Delete "$INSTDIR\libabsl_strerror.dll"
Delete "$INSTDIR\libabsl_str_format_internal.dll"
Delete "$INSTDIR\libabsl_strings.dll"
Delete "$INSTDIR\libabsl_strings_internal.dll"
Delete "$INSTDIR\libabsl_symbolize.dll"
Delete "$INSTDIR\libabsl_synchronization.dll"
Delete "$INSTDIR\libabsl_throw_delegate.dll"
Delete "$INSTDIR\libabsl_time.dll"
Delete "$INSTDIR\libabsl_time_zone.dll"
!ifdef debug
Delete "$INSTDIR\gdb.exe"
Delete "$INSTDIR\libexpat-1.dll"
@@ -940,7 +870,6 @@ Section "Uninstall"
Delete "$INSTDIR\libpcre2-16d.dll"
Delete "$INSTDIR\libreadline8.dll"
Delete "$INSTDIR\libtermcap.dll"
Delete "$INSTDIR\libabsl_graphcycles_internal.dll"
!else
Delete "$INSTDIR\libpcre2-8.dll"
Delete "$INSTDIR\libpcre2-16.dll"
@@ -969,18 +898,22 @@ Section "Uninstall"
Delete "$INSTDIR\faad-2.dll"
Delete "$INSTDIR\fdk-aac.dll"
Delete "$INSTDIR\ffi-7.dll"
Delete "$INSTDIR\getopt.dll"
Delete "$INSTDIR\gio-2.0-0.dll"
Delete "$INSTDIR\glib-2.0-0.dll"
Delete "$INSTDIR\gme.dll"
Delete "$INSTDIR\gmodule-2.0-0.dll"
Delete "$INSTDIR\gnutls.dll"
Delete "$INSTDIR\gobject-2.0-0.dll"
Delete "$INSTDIR\gstadaptivedemux-1.0-0.dll"
Delete "$INSTDIR\gstapp-1.0-0.dll"
Delete "$INSTDIR\gstaudio-1.0-0.dll"
Delete "$INSTDIR\gstbadaudio-1.0-0.dll"
Delete "$INSTDIR\gstbase-1.0-0.dll"
Delete "$INSTDIR\gstcodecparsers-1.0-0.dll"
Delete "$INSTDIR\gstfft-1.0-0.dll"
Delete "$INSTDIR\gstisoff-1.0-0.dll"
Delete "$INSTDIR\gstmpegts-1.0-0.dll"
Delete "$INSTDIR\gstnet-1.0-0.dll"
Delete "$INSTDIR\gstpbutils-1.0-0.dll"
Delete "$INSTDIR\gstreamer-1.0-0.dll"
@@ -1015,14 +948,12 @@ Section "Uninstall"
Delete "$INSTDIR\vorbis.dll"
Delete "$INSTDIR\vorbisfile.dll"
Delete "$INSTDIR\wavpackdll.dll"
Delete "$INSTDIR\abseil_dll.dll"
!ifdef release
Delete "$INSTDIR\freetype.dll"
Delete "$INSTDIR\libiconv.dll"
Delete "$INSTDIR\libpng16.dll"
Delete "$INSTDIR\libspeex.dll"
Delete "$INSTDIR\libxml2.dll"
Delete "$INSTDIR\pcre2-8.dll"
Delete "$INSTDIR\pcre2-16.dll"
Delete "$INSTDIR\twolame.dll"
@@ -1033,7 +964,6 @@ Section "Uninstall"
Delete "$INSTDIR\libiconvd.dll"
Delete "$INSTDIR\libpng16d.dll"
Delete "$INSTDIR\libspeexd.dll"
Delete "$INSTDIR\libxml2d.dll"
Delete "$INSTDIR\pcre2-8d.dll"
Delete "$INSTDIR\pcre2-16d.dll"
Delete "$INSTDIR\twolamed.dll"
@@ -1049,16 +979,12 @@ Section "Uninstall"
; Common files
Delete "$INSTDIR\icudt74.dll"
Delete "$INSTDIR\icudt76.dll"
Delete "$INSTDIR\libfftw3-3.dll"
!ifdef debug
Delete "$INSTDIR\libprotobufd.dll"
!else
Delete "$INSTDIR\libprotobuf.dll"
!endif
!ifdef msvc && debug
Delete "$INSTDIR\icuin74d.dll"
Delete "$INSTDIR\icuuc74d.dll"
Delete "$INSTDIR\icuin76d.dll"
Delete "$INSTDIR\icuuc76d.dll"
Delete "$INSTDIR\libxml2d.dll"
Delete "$INSTDIR\Qt6Concurrentd.dll"
Delete "$INSTDIR\Qt6Cored.dll"
Delete "$INSTDIR\Qt6Guid.dll"
@@ -1066,8 +992,9 @@ Section "Uninstall"
Delete "$INSTDIR\Qt6Sqld.dll"
Delete "$INSTDIR\Qt6Widgetsd.dll"
!else
Delete "$INSTDIR\icuin74.dll"
Delete "$INSTDIR\icuuc74.dll"
Delete "$INSTDIR\icuin76.dll"
Delete "$INSTDIR\icuuc76.dll"
Delete "$INSTDIR\libxml2.dll"
Delete "$INSTDIR\Qt6Concurrent.dll"
Delete "$INSTDIR\Qt6Core.dll"
Delete "$INSTDIR\Qt6Gui.dll"
@@ -1076,13 +1003,13 @@ Section "Uninstall"
Delete "$INSTDIR\Qt6Widgets.dll"
!endif
Delete "$INSTDIR\avcodec-60.dll"
Delete "$INSTDIR\avfilter-9.dll"
Delete "$INSTDIR\avformat-60.dll"
Delete "$INSTDIR\avutil-58.dll"
Delete "$INSTDIR\postproc-57.dll"
Delete "$INSTDIR\swresample-4.dll"
Delete "$INSTDIR\swscale-7.dll"
Delete "$INSTDIR\avcodec-61.dll"
Delete "$INSTDIR\avfilter-10.dll"
Delete "$INSTDIR\avformat-61.dll"
Delete "$INSTDIR\avutil-59.dll"
Delete "$INSTDIR\postproc-58.dll"
Delete "$INSTDIR\swresample-5.dll"
Delete "$INSTDIR\swscale-8.dll"
!ifdef mingw
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
@@ -1095,7 +1022,7 @@ Section "Uninstall"
!ifdef msvc && debug
Delete "$INSTDIR\platforms\qwindowsd.dll"
Delete "$INSTDIR\styles\qwindowsvistastyled.dll"
Delete "$INSTDIR\styles\qmodernwindowsstyled.dll"
Delete "$INSTDIR\tls\qschannelbackendd.dll"
Delete "$INSTDIR\tls\qopensslbackendd.dll"
Delete "$INSTDIR\sqldrivers\qsqlited.dll"
@@ -1104,7 +1031,7 @@ Section "Uninstall"
Delete "$INSTDIR\imageformats\qjpegd.dll"
!else
Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\styles\qwindowsvistastyle.dll"
Delete "$INSTDIR\styles\qmodernwindowsstyle.dll"
Delete "$INSTDIR\tls\qschannelbackend.dll"
Delete "$INSTDIR\tls\qopensslbackend.dll"
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
@@ -1116,6 +1043,7 @@ Section "Uninstall"
; MinGW GStreamer plugins
!ifdef mingw
Delete "$INSTDIR\gstreamer-plugins\libgstadaptivedemux2.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaes.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaiff.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstapetag.dll"
@@ -1124,16 +1052,14 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstasfmux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioconvert.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiofx.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiomixer.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioparsers.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiorate.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudioresample.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiotestsrc.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstbs2b.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdash.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdsd.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstequalizer.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstfaac.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstfaad.dll"
@@ -1148,6 +1074,10 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstmpegpsdemux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstmpegpsmux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstmpegtsdemux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstmpegtsmux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstmpg123.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstmusepack.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstogg.dll"
@@ -1170,6 +1100,7 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwaveform.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavenc.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavparse.dll"
@@ -1179,24 +1110,24 @@ Section "Uninstall"
; MSVC GStreamer plugins
!ifdef msvc
Delete "$INSTDIR\gstreamer-plugins\gstadaptivedemux2.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaes.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaiff.dll"
Delete "$INSTDIR\gstreamer-plugins\gstapetag.dll"
Delete "$INSTDIR\gstreamer-plugins\gstapp.dll"
Delete "$INSTDIR\gstreamer-plugins\gstasf.dll"
Delete "$INSTDIR\gstreamer-plugins\gstasfmux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstasio.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudioconvert.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudiofx.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudiomixer.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudioparsers.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudiorate.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudioresample.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaudiotestsrc.dll"
Delete "$INSTDIR\gstreamer-plugins\gstautodetect.dll"
Delete "$INSTDIR\gstreamer-plugins\gstbs2b.dll"
Delete "$INSTDIR\gstreamer-plugins\gstcoreelements.dll"
Delete "$INSTDIR\gstreamer-plugins\gstdash.dll"
Delete "$INSTDIR\gstreamer-plugins\gstdirectsound.dll"
Delete "$INSTDIR\gstreamer-plugins\gstdsd.dll"
Delete "$INSTDIR\gstreamer-plugins\gstequalizer.dll"
Delete "$INSTDIR\gstreamer-plugins\gstfaac.dll"
Delete "$INSTDIR\gstreamer-plugins\gstfaad.dll"
@@ -1211,6 +1142,10 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\gstisomp4.dll"
Delete "$INSTDIR\gstreamer-plugins\gstlame.dll"
Delete "$INSTDIR\gstreamer-plugins\gstlibav.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmpegpsdemux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmpegpsmux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmpegtsdemux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmpegtsmux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmpg123.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmusepack.dll"
Delete "$INSTDIR\gstreamer-plugins\gstogg.dll"
@@ -1234,10 +1169,14 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\gstvorbis.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwasapi.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwasapi2.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwaveform.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwavenc.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwavpack.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwavparse.dll"
Delete "$INSTDIR\gstreamer-plugins\gstxingmux.dll"
!ifdef arch_x64
Delete "$INSTDIR\gstreamer-plugins\gstspotify.dll"
!endif
!endif ; msvc
Delete "$INSTDIR\Uninstall.exe"

View File

@@ -1,4 +1,4 @@
strawberry ICON "${CMAKE_CURRENT_SOURCE_DIR}/../dist/windows/strawberry.ico"
strawberry ICON "${CMAKE_SOURCE_DIR}/dist/windows/strawberry.ico"
1 VERSIONINFO
FILEVERSION ${STRAWBERRY_VERSION_MAJOR},${STRAWBERRY_VERSION_MINOR},${STRAWBERRY_VERSION_PATCH}
PRODUCTVERSION ${STRAWBERRY_VERSION_MAJOR},${STRAWBERRY_VERSION_MINOR},${STRAWBERRY_VERSION_PATCH}

View File

@@ -1,35 +0,0 @@
cmake_minimum_required(VERSION 3.7)
set(SOURCES gstfastspectrum.cpp gstmoodbarplugin.cpp)
link_directories(
${GLIB_LIBRARY_DIRS}
${GOBJECT_LIBRARY_DIRS}
${GSTREAMER_LIBRARY_DIRS}
${GSTREAMER_BASE_LIBRARY_DIRS}
${GSTREAMER_AUDIO_LIBRARY_DIRS}
${FFTW3_LIBRARY_DIRS}
)
add_library(gstmoodbar STATIC ${SOURCES})
target_include_directories(gstmoodbar SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${GOBJECT_INCLUDE_DIRS}
${GSTREAMER_INCLUDE_DIRS}
${GSTREAMER_BASE_INCLUDE_DIRS}
${GSTREAMER_AUDIO_INCLUDE_DIRS}
${FFTW3_INCLUDE_DIR}
)
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(gstmoodbar PRIVATE
${GLIB_LIBRARIES}
${GOBJECT_LIBRARIES}
${GSTREAMER_LIBRARIES}
${GSTREAMER_BASE_LIBRARIES}
${GSTREAMER_AUDIO_LIBRARIES}
${FFTW3_FFTW_LIBRARY}
Qt${QT_VERSION_MAJOR}::Core
)

View File

@@ -1,520 +0,0 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* <2006,2011> Stefan Kost <ensonic@users.sf.net>
* <2007-2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QtGlobal>
#include <cstring>
#include <cmath>
#include <glib.h>
#include <gst/gst.h>
#include <gst/audio/gstaudiofilter.h>
#include <QMutex>
#include "gstfastspectrum.h"
GST_DEBUG_CATEGORY_STATIC(gst_fastspectrum_debug);
namespace {
// Spectrum properties
constexpr auto DEFAULT_INTERVAL = (GST_SECOND / 10);
constexpr auto DEFAULT_BANDS = 128;
enum {
PROP_0,
PROP_INTERVAL,
PROP_BANDS
};
} // namespace
#define gst_fastspectrum_parent_class parent_class
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
G_DEFINE_TYPE(GstFastSpectrum, gst_fastspectrum, GST_TYPE_AUDIO_FILTER)
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
static void gst_fastspectrum_finalize(GObject *object);
static void gst_fastspectrum_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
static void gst_fastspectrum_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static gboolean gst_fastspectrum_start(GstBaseTransform *trans);
static gboolean gst_fastspectrum_stop(GstBaseTransform *trans);
static GstFlowReturn gst_fastspectrum_transform_ip(GstBaseTransform *trans, GstBuffer *buffer);
static gboolean gst_fastspectrum_setup(GstAudioFilter *base, const GstAudioInfo *info);
static void gst_fastspectrum_class_init(GstFastSpectrumClass *klass) {
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS(klass);
GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS(klass);
GstCaps *caps = nullptr;
gobject_class->set_property = gst_fastspectrum_set_property;
gobject_class->get_property = gst_fastspectrum_get_property;
gobject_class->finalize = gst_fastspectrum_finalize;
trans_class->start = GST_DEBUG_FUNCPTR(gst_fastspectrum_start);
trans_class->stop = GST_DEBUG_FUNCPTR(gst_fastspectrum_stop);
trans_class->transform_ip = GST_DEBUG_FUNCPTR(gst_fastspectrum_transform_ip);
trans_class->passthrough_on_same_caps = TRUE;
filter_class->setup = GST_DEBUG_FUNCPTR(gst_fastspectrum_setup);
g_object_class_install_property(gobject_class, PROP_INTERVAL, g_param_spec_uint64("interval", "Interval", "Interval of time between message posts (in nanoseconds)", 1, G_MAXUINT64, DEFAULT_INTERVAL, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_BANDS, g_param_spec_uint("bands", "Bands", "Number of frequency bands", 0, G_MAXUINT, DEFAULT_BANDS, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
GST_DEBUG_CATEGORY_INIT(gst_fastspectrum_debug, "spectrum", 0, "audio spectrum analyser element");
gst_element_class_set_static_metadata(element_class, "Spectrum analyzer",
"Filter/Analyzer/Audio",
"Run an FFT on the audio signal, output spectrum data",
"Erik Walthinsen <omega@cse.ogi.edu>, "
"Stefan Kost <ensonic@users.sf.net>, "
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
caps = gst_caps_from_string(GST_AUDIO_CAPS_MAKE("{ S16LE, S24LE, S32LE, F32LE, F64LE }") ", layout = (string) interleaved, channels = 1");
#else
caps = gst_caps_from_string(GST_AUDIO_CAPS_MAKE("{ S16BE, S24BE, S32BE, F32BE, F64BE }") ", layout = (string) interleaved, channels = 1");
#endif
gst_audio_filter_class_add_pad_templates(filter_class, caps);
gst_caps_unref(caps);
klass->fftw_lock = new QMutex;
}
static void gst_fastspectrum_init(GstFastSpectrum *spectrum) {
spectrum->interval = DEFAULT_INTERVAL;
spectrum->bands = DEFAULT_BANDS;
spectrum->channel_data_initialized = false;
g_mutex_init(&spectrum->lock);
}
static void gst_fastspectrum_alloc_channel_data(GstFastSpectrum *spectrum) {
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
spectrum->input_ring_buffer = new double[nfft];
spectrum->fft_input = reinterpret_cast<double*>(fftw_malloc(sizeof(double) * nfft));
spectrum->fft_output = reinterpret_cast<fftw_complex*>(fftw_malloc(sizeof(fftw_complex) * (nfft / 2 + 1)));
spectrum->spect_magnitude = new double[bands] {};
GstFastSpectrumClass *klass = reinterpret_cast<GstFastSpectrumClass*>(G_OBJECT_GET_CLASS(spectrum));
{
QMutexLocker l(klass->fftw_lock);
spectrum->plan = fftw_plan_dft_r2c_1d(static_cast<int>(nfft), spectrum->fft_input, spectrum->fft_output, FFTW_ESTIMATE);
}
spectrum->channel_data_initialized = true;
}
static void gst_fastspectrum_free_channel_data(GstFastSpectrum *spectrum) {
GstFastSpectrumClass *klass = reinterpret_cast<GstFastSpectrumClass*>(G_OBJECT_GET_CLASS(spectrum));
if (spectrum->channel_data_initialized) {
{
QMutexLocker l(klass->fftw_lock);
fftw_destroy_plan(spectrum->plan);
}
fftw_free(spectrum->fft_input);
fftw_free(spectrum->fft_output);
delete[] spectrum->input_ring_buffer;
delete[] spectrum->spect_magnitude;
spectrum->channel_data_initialized = false;
}
}
static void gst_fastspectrum_flush(GstFastSpectrum *spectrum) {
spectrum->num_frames = 0;
spectrum->num_fft = 0;
spectrum->accumulated_error = 0;
}
static void gst_fastspectrum_reset_state(GstFastSpectrum *spectrum) {
GST_DEBUG_OBJECT(spectrum, "resetting state");
gst_fastspectrum_free_channel_data(spectrum);
gst_fastspectrum_flush(spectrum);
}
static void gst_fastspectrum_finalize(GObject *object) {
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(object);
gst_fastspectrum_reset_state(spectrum);
g_mutex_clear(&spectrum->lock);
G_OBJECT_CLASS(parent_class)->finalize(object);
}
static void gst_fastspectrum_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
switch (prop_id) {
case PROP_INTERVAL: {
guint64 interval = g_value_get_uint64(value);
g_mutex_lock(&filter->lock);
if (filter->interval != interval) {
filter->interval = interval;
gst_fastspectrum_reset_state(filter);
}
g_mutex_unlock(&filter->lock);
break;
}
case PROP_BANDS: {
guint bands = g_value_get_uint(value);
g_mutex_lock(&filter->lock);
if (filter->bands != bands) {
filter->bands = bands;
gst_fastspectrum_reset_state(filter);
}
g_mutex_unlock(&filter->lock);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void gst_fastspectrum_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
switch (prop_id) {
case PROP_INTERVAL:
g_value_set_uint64(value, filter->interval);
break;
case PROP_BANDS:
g_value_set_uint(value, filter->bands);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static gboolean gst_fastspectrum_start(GstBaseTransform *trans) {
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
gst_fastspectrum_reset_state(spectrum);
return TRUE;
}
static gboolean gst_fastspectrum_stop(GstBaseTransform *trans) {
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
gst_fastspectrum_reset_state(spectrum);
return TRUE;
}
// Mixing data readers
static void input_data_mixed_float(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
Q_UNUSED(max_value);
const gfloat *in = reinterpret_cast<const gfloat*>(_in);
guint ip = 0;
for (guint j = 0; j < len; j++) {
out[op] = in[ip++];
op = (op + 1) % nfft;
}
}
static void input_data_mixed_double(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
Q_UNUSED(max_value);
const gdouble *in = reinterpret_cast<const gdouble*>(_in);
guint ip = 0;
for (guint j = 0; j < len; j++) {
out[op] = in[ip++];
op = (op + 1) % nfft;
}
}
static void input_data_mixed_int32_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
const gint32 *in = reinterpret_cast<const gint32*>(_in);
guint ip = 0;
for (guint j = 0; j < len; j++) {
out[op] = in[ip++] / max_value;
op = (op + 1) % nfft;
}
}
static void input_data_mixed_int24_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
for (guint j = 0; j < len; j++) {
#if G_BYTE_ORDER == G_BIG_ENDIAN
guint32 value = GST_READ_UINT24_BE(_in);
#else
guint32 value = GST_READ_UINT24_LE(_in);
#endif
if (value & 0x00800000) {
value |= 0xff000000;
}
out[op] = value / max_value;
op = (op + 1) % nfft;
_in += 3;
}
}
static void input_data_mixed_int16_max(const guint8 *_in, double *out, guint len, double max_value, guint op, guint nfft) {
const gint16 *in = reinterpret_cast<const gint16*>(_in);
guint ip = 0;
for (guint j = 0; j < len; j++) {
out[op] = in[ip++] / max_value;
op = (op + 1) % nfft;
}
}
static gboolean gst_fastspectrum_setup(GstAudioFilter *base, const GstAudioInfo *info) {
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(base);
GstFastSpectrumInputData input_data = nullptr;
g_mutex_lock(&spectrum->lock);
switch (GST_AUDIO_INFO_FORMAT(info)) {
case GST_AUDIO_FORMAT_S16:
input_data = input_data_mixed_int16_max;
break;
case GST_AUDIO_FORMAT_S24:
input_data = input_data_mixed_int24_max;
break;
case GST_AUDIO_FORMAT_S32:
input_data = input_data_mixed_int32_max;
break;
case GST_AUDIO_FORMAT_F32:
input_data = input_data_mixed_float;
break;
case GST_AUDIO_FORMAT_F64:
input_data = input_data_mixed_double;
break;
default:
g_assert_not_reached();
break;
}
spectrum->input_data = input_data;
gst_fastspectrum_reset_state(spectrum);
g_mutex_unlock(&spectrum->lock);
return TRUE;
}
static void gst_fastspectrum_run_fft(GstFastSpectrum *spectrum, guint input_pos) {
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
for (guint i = 0; i < nfft; i++) {
spectrum->fft_input[i] = spectrum->input_ring_buffer[(input_pos + i) % nfft];
}
// Should be safe to execute the same plan multiple times in parallel.
fftw_execute(spectrum->plan);
// Calculate magnitude in db
for (guint i = 0; i < bands; i++) {
gdouble val = spectrum->fft_output[i][0] * spectrum->fft_output[i][0];
val += spectrum->fft_output[i][1] * spectrum->fft_output[i][1];
val /= nfft * nfft;
spectrum->spect_magnitude[i] += val;
}
}
static GstFlowReturn gst_fastspectrum_transform_ip(GstBaseTransform *trans, GstBuffer *buffer) {
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
guint rate = GST_AUDIO_FILTER_RATE(spectrum);
guint bps = GST_AUDIO_FILTER_BPS(spectrum);
guint bpf = GST_AUDIO_FILTER_BPF(spectrum);
double max_value = static_cast<double>((1UL << ((bps << 3) - 1)) - 1);
guint bands = spectrum->bands;
guint nfft = 2 * bands - 2;
guint input_pos = 0;
GstMapInfo map;
const guint8 *data = nullptr;
gsize size = 0;
GstFastSpectrumInputData input_data = nullptr;
g_mutex_lock(&spectrum->lock);
gst_buffer_map(buffer, &map, GST_MAP_READ);
data = map.data;
size = map.size;
GST_LOG_OBJECT(spectrum, "input size: %" G_GSIZE_FORMAT " bytes", size);
if (GST_BUFFER_IS_DISCONT(buffer)) {
GST_DEBUG_OBJECT(spectrum, "Discontinuity detected -- flushing");
gst_fastspectrum_flush(spectrum);
}
// If we don't have a FFT context yet (or it was reset due to parameter changes) get one and allocate memory for everything
if (!spectrum->channel_data_initialized) {
GST_DEBUG_OBJECT(spectrum, "allocating for bands %u", bands);
gst_fastspectrum_alloc_channel_data(spectrum);
// Number of sample frames we process before posting a message interval is in ns
spectrum->frames_per_interval = gst_util_uint64_scale(spectrum->interval, rate, GST_SECOND);
spectrum->frames_todo = spectrum->frames_per_interval;
// Rounding error for frames_per_interval in ns, aggregated it in accumulated_error
spectrum->error_per_interval = (spectrum->interval * rate) % GST_SECOND;
if (spectrum->frames_per_interval == 0) {
spectrum->frames_per_interval = 1;
}
GST_INFO_OBJECT(spectrum, "interval %" GST_TIME_FORMAT ", fpi %" G_GUINT64_FORMAT ", error %" GST_TIME_FORMAT, GST_TIME_ARGS(spectrum->interval), spectrum->frames_per_interval, GST_TIME_ARGS(spectrum->error_per_interval));
spectrum->input_pos = 0;
gst_fastspectrum_flush(spectrum);
}
if (spectrum->num_frames == 0) {
spectrum->message_ts = GST_BUFFER_TIMESTAMP(buffer);
}
input_pos = spectrum->input_pos;
input_data = spectrum->input_data;
while (size >= bpf) {
// Run input_data for a chunk of data
guint fft_todo = nfft - (spectrum->num_frames % nfft);
guint msg_todo = spectrum->frames_todo - spectrum->num_frames;
GST_LOG_OBJECT(spectrum, "message frames todo: %u, fft frames todo: %u, input frames %" G_GSIZE_FORMAT, msg_todo, fft_todo, (size / bpf));
guint block_size = msg_todo;
if (block_size > (size / bpf)) {
block_size = (size / bpf);
}
if (block_size > fft_todo) {
block_size = fft_todo;
}
// Move the current frames into our ringbuffers
input_data(data, spectrum->input_ring_buffer, block_size, max_value, input_pos, nfft);
data += block_size * bpf;
size -= block_size * bpf;
input_pos = (input_pos + block_size) % nfft;
spectrum->num_frames += block_size;
gboolean have_full_interval = (spectrum->num_frames == spectrum->frames_todo);
GST_LOG_OBJECT(spectrum, "size: %" G_GSIZE_FORMAT ", do-fft = %d, do-message = %d", size, (spectrum->num_frames % nfft == 0), have_full_interval);
// If we have enough frames for an FFT or we have all frames required for the interval and we haven't run a FFT, then run an FFT
if ((spectrum->num_frames % nfft == 0) || (have_full_interval && !spectrum->num_fft)) {
gst_fastspectrum_run_fft(spectrum, input_pos);
spectrum->num_fft++;
}
// Do we have the FFTs for one interval?
if (have_full_interval) {
GST_DEBUG_OBJECT(spectrum, "nfft: %u frames: %" G_GUINT64_FORMAT " fpi: %" G_GUINT64_FORMAT " error: %" GST_TIME_FORMAT, nfft, spectrum->num_frames, spectrum->frames_per_interval, GST_TIME_ARGS(spectrum->accumulated_error));
spectrum->frames_todo = spectrum->frames_per_interval;
if (spectrum->accumulated_error >= GST_SECOND) {
spectrum->accumulated_error -= GST_SECOND;
spectrum->frames_todo++;
}
spectrum->accumulated_error += spectrum->error_per_interval;
if (spectrum->output_callback) {
// Calculate average
for (guint i = 0; i < spectrum->bands; i++) {
spectrum->spect_magnitude[i] /= static_cast<double>(spectrum->num_fft);
}
spectrum->output_callback(spectrum->spect_magnitude, static_cast<int>(spectrum->bands));
// Reset spectrum accumulators
memset(spectrum->spect_magnitude, 0, spectrum->bands * sizeof(double));
}
if (GST_CLOCK_TIME_IS_VALID(spectrum->message_ts)) {
spectrum->message_ts += gst_util_uint64_scale(spectrum->num_frames, GST_SECOND, rate);
}
spectrum->num_frames = 0;
spectrum->num_fft = 0;
}
}
spectrum->input_pos = input_pos;
gst_buffer_unmap(buffer, &map);
g_mutex_unlock(&spectrum->lock);
g_assert(size == 0);
return GST_FLOW_OK;
}

View File

@@ -1,50 +0,0 @@
/* This file was part of Clementine.
Copyright 2014, David Sansome <me@davidsansome.com>
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 <glib.h>
#include <gst/gst.h>
#include "gstfastspectrum.h"
#include "gstmoodbarplugin.h"
namespace {
static gboolean gst_moodbar_plugin_init(GstPlugin *plugin) {
if (!gst_element_register(plugin, "fastspectrum", GST_RANK_NONE, GST_TYPE_FASTSPECTRUM)) {
return FALSE;
}
return TRUE;
}
} // namespace
int gstfastspectrum_register_static() {
return gst_plugin_register_static(
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"fastspectrum",
"Fast spectrum analyzer for generating Moodbars",
gst_moodbar_plugin_init,
"0.1",
"GPL",
"FastSpectrum",
"FastSpectrum",
"https://www.strawberrymusicplayer.org");
}

View File

@@ -1,43 +0,0 @@
cmake_minimum_required(VERSION 3.7)
set(SOURCES
core/logging.cpp
core/messagehandler.cpp
core/messagereply.cpp
core/workerpool.cpp
)
set(HEADERS
core/logging.h
core/messagehandler.h
core/messagereply.h
core/workerpool.h
)
qt_wrap_cpp(MOC ${HEADERS})
link_directories(${GLIB_LIBRARY_DIRS})
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
target_include_directories(libstrawberry-common SYSTEM PRIVATE ${GLIB_INCLUDE_DIRS})
target_include_directories(libstrawberry-common PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}/src
)
if(Backtrace_FOUND)
target_include_directories(libstrawberry-common SYSTEM PRIVATE ${Backtrace_INCLUDE_DIRS})
endif()
target_link_libraries(libstrawberry-common PRIVATE
${CMAKE_THREAD_LIBS_INIT}
${GLIB_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
)
if(Backtrace_FOUND)
target_link_libraries(libstrawberry-common PRIVATE ${Backtrace_LIBRARIES})
endif()

View File

@@ -1,117 +0,0 @@
/* This file is part of Strawberry.
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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "messagehandler.h"
#include <QObject>
#include <QAbstractSocket>
#include <QDataStream>
#include <QIODevice>
#include <QLocalSocket>
#include <QByteArray>
#include "core/logging.h"
_MessageHandlerBase::_MessageHandlerBase(QIODevice *device, QObject *parent)
: QObject(parent),
device_(nullptr),
flush_abstract_socket_(nullptr),
flush_local_socket_(nullptr),
reading_protobuf_(false),
expected_length_(0),
is_device_closed_(false) {
if (device) {
SetDevice(device);
}
}
void _MessageHandlerBase::SetDevice(QIODevice *device) {
device_ = device;
buffer_.open(QIODevice::ReadWrite);
QObject::connect(device, &QIODevice::readyRead, this, &_MessageHandlerBase::DeviceReadyRead);
// Yeah I know.
if (QAbstractSocket *abstractsocket = qobject_cast<QAbstractSocket*>(device)) {
flush_abstract_socket_ = &QAbstractSocket::flush;
QObject::connect(abstractsocket, &QAbstractSocket::disconnected, this, &_MessageHandlerBase::DeviceClosed);
}
else if (QLocalSocket *localsocket = qobject_cast<QLocalSocket*>(device)) {
flush_local_socket_ = &QLocalSocket::flush;
QObject::connect(localsocket, &QLocalSocket::disconnected, this, &_MessageHandlerBase::DeviceClosed);
}
else {
qFatal("Unsupported device type passed to _MessageHandlerBase");
}
}
void _MessageHandlerBase::DeviceReadyRead() {
while (device_->bytesAvailable() > 0) {
if (!reading_protobuf_) {
// Read the length of the next message
QDataStream s(device_);
s >> expected_length_;
reading_protobuf_ = true;
}
// Read some of the message
buffer_.write(device_->read(expected_length_ - buffer_.size()));
// Did we get everything?
if (buffer_.size() == expected_length_) {
// Parse the message
if (!RawMessageArrived(buffer_.data())) {
qLog(Error) << "Malformed protobuf message";
device_->close();
return;
}
// Clear the buffer
buffer_.close();
buffer_.setData(QByteArray());
buffer_.open(QIODevice::ReadWrite);
reading_protobuf_ = false;
}
}
}
void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
QDataStream s(device_);
s << static_cast<quint32>(data.length());
s.writeRawData(data.data(), static_cast<int>(data.length()));
// Sorry.
if (flush_abstract_socket_) {
((qobject_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
}
else if (flush_local_socket_) {
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
}
}
void _MessageHandlerBase::DeviceClosed() {
is_device_closed_ = true;
AbortAll();
}

View File

@@ -1,176 +0,0 @@
/* This file is part of Strawberry.
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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H
#include <string>
#include <QtGlobal>
#include <QObject>
#include <QThread>
#include <QBuffer>
#include <QByteArray>
#include <QMap>
#include <QString>
#include <QLocalSocket>
#include <QAbstractSocket>
#include "core/messagereply.h"
class QIODevice;
// Reads and writes uint32 length encoded protobufs to a socket.
// This base QObject is separate from AbstractMessageHandler because moc can't handle templated classes.
// Use AbstractMessageHandler instead.
class _MessageHandlerBase : public QObject {
Q_OBJECT
public:
// device can be nullptr, in which case you must call SetDevice before writing any messages.
_MessageHandlerBase(QIODevice *device, QObject *parent);
void SetDevice(QIODevice *device);
// After this is true, messages cannot be sent to the handler any more.
bool is_device_closed() const { return is_device_closed_; }
protected slots:
void WriteMessage(const QByteArray &data);
void DeviceReadyRead();
virtual void DeviceClosed();
protected:
virtual bool RawMessageArrived(const QByteArray &data) = 0;
virtual void AbortAll() = 0;
protected:
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
typedef bool (QLocalSocket::*FlushLocalSocket)();
QIODevice *device_;
FlushAbstractSocket flush_abstract_socket_;
FlushLocalSocket flush_local_socket_;
bool reading_protobuf_;
quint32 expected_length_;
QBuffer buffer_;
bool is_device_closed_;
};
// Reads and writes uint32 length encoded MessageType messages to a socket.
// You should subclass this and implement the MessageArrived(MessageType) method.
template<typename MT>
class AbstractMessageHandler : public _MessageHandlerBase {
public:
AbstractMessageHandler(QIODevice *device, QObject *parent);
~AbstractMessageHandler() override { AbstractMessageHandler::AbortAll(); }
using MessageType = MT;
using ReplyType = MessageReply<MT>;
// Serialises the message and writes it to the socket.
// This version MUST be called from the thread in which the AbstractMessageHandler was created.
void SendMessage(const MessageType &message);
// Serialises the message and writes it to the socket.
// This version may be called from any thread.
void SendMessageAsync(const MessageType &message);
// Sends the request message inside and takes ownership of the MessageReply.
// The MessageReply's Finished() signal will be emitted when a reply arrives with the same ID. Must be called from my thread.
void SendRequest(ReplyType *reply);
// Sets the "id" field of reply to the same as the request, and sends the reply on the socket. Used on the worker side.
void SendReply(const MessageType &request, MessageType *reply);
protected:
// Called when a message is received from the socket.
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
// _MessageHandlerBase
bool RawMessageArrived(const QByteArray &data) override;
void AbortAll() override;
private:
QMap<int, ReplyType*> pending_replies_;
};
template<typename MT>
AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice *device, QObject *parent)
: _MessageHandlerBase(device, parent) {}
template<typename MT>
void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
Q_ASSERT(QThread::currentThread() == thread());
std::string data = message.SerializeAsString();
WriteMessage(QByteArray(data.data(), data.size()));
}
template<typename MT>
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
std::string data = message.SerializeAsString();
QMetaObject::invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
}
template<typename MT>
void AbstractMessageHandler<MT>::SendRequest(ReplyType *reply) {
pending_replies_[reply->id()] = reply;
SendMessage(reply->request_message());
}
template<typename MT>
void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageType *reply) {
reply->set_id(request.id());
SendMessage(*reply);
}
template<typename MT>
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
MessageType message;
if (!message.ParseFromArray(data.constData(), data.size())) {
return false;
}
if (pending_replies_.contains(message.id())) {
// This is a reply to a message that we created earlier.
ReplyType *reply = pending_replies_.take(message.id());
reply->SetReply(message);
}
else {
MessageArrived(message);
}
return true;
}
template<typename MT>
void AbstractMessageHandler<MT>::AbortAll() {
for (ReplyType *reply : pending_replies_) {
reply->Abort();
}
pending_replies_.clear();
}
#endif // MESSAGEHANDLER_H

View File

@@ -1,48 +0,0 @@
/* This file is part of Strawberry.
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
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 "messagereply.h"
#include <QObject>
#include <QtDebug>
#include "core/logging.h"
_MessageReplyBase::_MessageReplyBase(QObject *parent)
: QObject(parent), finished_(false), success_(false) {}
bool _MessageReplyBase::WaitForFinished() {
qLog(Debug) << "Waiting on ID" << id();
semaphore_.acquire();
qLog(Debug) << "Acquired ID" << id();
return success_;
}
void _MessageReplyBase::Abort() {
Q_ASSERT(!finished_);
finished_ = true;
success_ = false;
emit Finished();
qLog(Debug) << "Releasing ID" << id() << "(aborted)";
semaphore_.release();
}

View File

@@ -1,99 +0,0 @@
/* This file is part of Strawberry.
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
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 MESSAGEREPLY_H
#define MESSAGEREPLY_H
#include <QtGlobal>
#include <QObject>
#include <QSemaphore>
#include <QString>
#include <QTimer>
#include "core/logging.h"
// Base QObject for a reply future class that is returned immediately for requests that will occur in the background.
// Similar to QNetworkReply. Use MessageReply instead.
class _MessageReplyBase : public QObject {
Q_OBJECT
public:
explicit _MessageReplyBase(QObject *parent = nullptr);
virtual int id() const = 0;
bool is_finished() const { return finished_; }
bool is_successful() const { return success_; }
// Waits for the reply to finish by waiting on a semaphore. Never call this from the MessageHandler's thread or it will block forever.
// Returns true if the call was successful.
bool WaitForFinished();
void Abort();
signals:
void Finished();
protected:
bool finished_;
bool success_;
QSemaphore semaphore_;
};
// A reply future class that is returned immediately for requests that will occur in the background. Similar to QNetworkReply.
template<typename MessageType>
class MessageReply : public _MessageReplyBase {
public:
explicit MessageReply(const MessageType &request_message, QObject *parent = nullptr);
int id() const override { return request_message_.id(); }
const MessageType &request_message() const { return request_message_; }
const MessageType &message() const { return reply_message_; }
void SetReply(const MessageType &message);
private:
MessageType request_message_;
MessageType reply_message_;
};
template<typename MessageType>
MessageReply<MessageType>::MessageReply(const MessageType &request_message, QObject *parent) : _MessageReplyBase(parent) {
request_message_.MergeFrom(request_message);
}
template<typename MessageType>
void MessageReply<MessageType>::SetReply(const MessageType &message) {
Q_ASSERT(!finished_);
reply_message_.MergeFrom(message);
finished_ = true;
success_ = true;
qLog(Debug) << "Releasing ID" << id() << "(finished)";
// Delay the signal as workaround to fix the signal periodically not emitted.
QTimer::singleShot(1, this, &_MessageReplyBase::Finished);
semaphore_.release();
}
#endif // MESSAGEREPLY_H

View File

@@ -1,23 +0,0 @@
/* This file is part of Strawberry.
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
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Strawberry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QObject>
#include "workerpool.h"
_WorkerPoolBase::_WorkerPoolBase(QObject *parent) : QObject(parent) {}

View File

@@ -1,465 +0,0 @@
/* This file is part of Strawberry.
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
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 WORKERPOOL_H
#define WORKERPOOL_H
#include "config.h"
#include <cstdio>
#include <cstddef>
#include <QtGlobal>
#include <QObject>
#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QLocalServer>
#include <QProcess>
#include <QDir>
#include <QFile>
#include <QList>
#include <QQueue>
#include <QString>
#include <QStringList>
#include <QAtomicInt>
#include <QRandomGenerator>
#include "core/logging.h"
class QLocalSocket;
// Base class containing signals and slots - required because moc doesn't do templated objects.
class _WorkerPoolBase : public QObject {
Q_OBJECT
public:
explicit _WorkerPoolBase(QObject *parent = nullptr);
signals:
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
void WorkerFailedToStart();
protected slots:
virtual void DoStart() {}
virtual void NewConnection() {}
virtual void ProcessReadyReadStandardOutput() {}
virtual void ProcessReadyReadStandardError() {}
virtual void ProcessError(QProcess::ProcessError) {}
virtual void SendQueuedMessages() {}
};
// Manages a pool of one or more external processes.
// A local socket server is started for each process, and the address is passed to the process as argv[1].
// The process is expected to connect back to the socket server, and when it does a HandlerType is created for it.
// Instances of HandlerType are created in the WorkerPool's thread.
template<typename HandlerType>
class WorkerPool : public _WorkerPoolBase {
public:
explicit WorkerPool(QObject *parent = nullptr);
~WorkerPool() override;
using MessageType = typename HandlerType::MessageType;
using ReplyType = typename HandlerType::ReplyType;
// Sets the name of the worker executable. This is looked for first in the current directory, and then in $PATH.
// You must call this before calling Start().
void SetExecutableName(const QString &executable_name);
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
void SetWorkerCount(const int count);
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
// Defaults to QApplication::applicationName().
// A random number is appended to this name when creating each server.
void SetLocalServerName(const QString &local_server_name);
// Starts all workers.
void Start();
// Fills in the message's "id" field and creates a reply future.
// The message is queued and the WorkerPool's thread will send it to the next available worker.
// Can be called from any thread.
ReplyType *SendMessageWithReply(MessageType *message);
protected:
// These are all reimplemented slots, they are called on the WorkerPool's thread.
void DoStart() override;
void NewConnection() override;
void ProcessReadyReadStandardOutput() override;
void ProcessReadyReadStandardError() override;
void ProcessError(QProcess::ProcessError error) override;
void SendQueuedMessages() override;
private:
struct Worker {
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
QLocalServer *local_server_;
QLocalSocket *local_socket_;
QProcess *process_;
HandlerType *handler_;
};
// Must only ever be called on my thread.
void StartOneWorker(Worker *worker);
template<typename T>
Worker *FindWorker(T Worker::*member, T value) {
for (typename QList<Worker>::iterator it = workers_.begin(); it != workers_.end(); ++it) {
if ((*it).*member == value) {
return &(*it);
}
}
return nullptr;
}
template<typename T>
void DeleteQObjectPointerLater(T **p) {
if (*p) {
(*p)->deleteLater();
*p = nullptr;
}
}
// Creates a new reply future for the request with the next sequential ID,
// and sets the request's ID to the ID of the reply. Can be called from any thread
ReplyType *NewReply(MessageType *message);
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
HandlerType *NextHandler() const;
private:
QString local_server_name_;
QString executable_name_;
QString executable_path_;
int worker_count_;
mutable int next_worker_;
QList<Worker> workers_;
QAtomicInt next_id_;
QMutex message_queue_mutex_;
QQueue<ReplyType *> message_queue_;
};
template<typename HandlerType>
WorkerPool<HandlerType>::WorkerPool(QObject *parent)
: _WorkerPoolBase(parent),
worker_count_(1),
next_worker_(0),
next_id_(0) {
local_server_name_ = qApp->applicationName().toLower();
if (local_server_name_.isEmpty()) {
local_server_name_ = "workerpool";
}
}
template<typename HandlerType>
WorkerPool<HandlerType>::~WorkerPool() {
for (const Worker &worker : workers_) {
if (worker.local_socket_ && worker.process_) {
QObject::disconnect(worker.process_, &QProcess::errorOccurred, this, &WorkerPool::ProcessError);
QObject::disconnect(worker.process_, &QProcess::readyReadStandardOutput, this, &WorkerPool::ProcessReadyReadStandardOutput);
QObject::disconnect(worker.process_, &QProcess::readyReadStandardError, this, &WorkerPool::ProcessReadyReadStandardError);
// The worker is connected. Close his socket and wait for him to exit.
qLog(Debug) << "Closing worker socket";
worker.local_socket_->close();
worker.process_->waitForFinished(500);
}
if (worker.process_ && worker.process_->state() == QProcess::Running) {
// The worker is still running - kill it.
qLog(Debug) << "Killing worker process";
worker.process_->terminate();
if (!worker.process_->waitForFinished(500)) {
worker.process_->kill();
}
}
}
for (ReplyType *reply : message_queue_) {
reply->Abort();
}
}
template<typename HandlerType>
void WorkerPool<HandlerType>::SetWorkerCount(const int count) {
Q_ASSERT(workers_.isEmpty());
worker_count_ = count;
}
template<typename HandlerType>
void WorkerPool<HandlerType>::SetLocalServerName(const QString &local_server_name) {
Q_ASSERT(workers_.isEmpty());
local_server_name_ = local_server_name;
}
template<typename HandlerType>
void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name) {
Q_ASSERT(workers_.isEmpty());
executable_name_ = executable_name;
}
template<typename HandlerType>
void WorkerPool<HandlerType>::Start() {
QMetaObject::invokeMethod(this, &WorkerPool<HandlerType>::DoStart);
}
template<typename HandlerType>
void WorkerPool<HandlerType>::DoStart() {
Q_ASSERT(workers_.isEmpty());
Q_ASSERT(!executable_name_.isEmpty());
Q_ASSERT(QThread::currentThread() == thread());
// Find the executable if we can, default to searching $PATH
executable_path_ = executable_name_;
QStringList search_path;
search_path << QCoreApplication::applicationDirPath();
#if defined(Q_OS_UNIX)
search_path << "/usr/libexec";
search_path << "/usr/local/libexec";
#endif
#if defined(Q_OS_MACOS)
search_path << QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../PlugIns");
#endif
for (const QString &path_prefix : search_path) {
const QString executable_path = path_prefix + "/" + executable_name_;
if (QFile::exists(executable_path)) {
executable_path_ = executable_path;
qLog(Debug) << "Using worker" << executable_name_ << "from" << path_prefix;
break;
}
}
if (executable_path_ == executable_name_) {
qLog(Debug) << "Using worker" << executable_name_;
}
// Start all the workers
for (int i = 0; i < worker_count_; ++i) {
Worker worker;
StartOneWorker(&worker);
workers_ << worker;
}
}
template<typename HandlerType>
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
Q_ASSERT(QThread::currentThread() == thread());
DeleteQObjectPointerLater(&worker->local_server_);
DeleteQObjectPointerLater(&worker->local_socket_);
DeleteQObjectPointerLater(&worker->process_);
DeleteQObjectPointerLater(&worker->handler_);
worker->local_server_ = new QLocalServer(this);
worker->process_ = new QProcess(this);
QObject::connect(worker->local_server_, &QLocalServer::newConnection, this, &WorkerPool::NewConnection);
QObject::connect(worker->process_, &QProcess::errorOccurred, this, &WorkerPool::ProcessError);
QObject::connect(worker->process_, &QProcess::readyReadStandardOutput, this, &WorkerPool::ProcessReadyReadStandardOutput);
QObject::connect(worker->process_, &QProcess::readyReadStandardError, this, &WorkerPool::ProcessReadyReadStandardError);
// Create a server, find an unused name and start listening
forever {
const quint32 unique_number = QRandomGenerator::global()->bounded(static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number);
if (worker->local_server_->listen(name)) {
break;
}
}
qLog(Debug) << "Starting worker" << worker << executable_path_ << worker->local_server_->fullServerName();
#ifdef Q_OS_WIN32
worker->process_->setProcessChannelMode(QProcess::SeparateChannels);
#else
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
#endif
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
}
template<typename HandlerType>
void WorkerPool<HandlerType>::NewConnection() {
Q_ASSERT(QThread::currentThread() == thread());
QLocalServer *server = qobject_cast<QLocalServer*>(sender());
// Find the worker with this server.
Worker *worker = FindWorker(&Worker::local_server_, server);
if (!worker) return;
qLog(Debug) << "Worker" << worker << "connected to" << server->fullServerName();
// Accept the connection.
worker->local_socket_ = server->nextPendingConnection();
// We only ever accept one connection per worker, so destroy the server now.
worker->local_socket_->setParent(this);
worker->local_server_->deleteLater();
worker->local_server_ = nullptr;
// Create the handler.
worker->handler_ = new HandlerType(worker->local_socket_, this);
SendQueuedMessages();
}
template<typename HandlerType>
void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
Q_ASSERT(QThread::currentThread() == thread());
QProcess *process = qobject_cast<QProcess*>(sender());
// Find the worker with this process.
Worker *worker = FindWorker(&Worker::process_, process);
if (!worker) return;
switch (error) {
case QProcess::FailedToStart:
// Failed to start errors are bad - it usually means the worker isn't installed.
// Don't restart the process, but tell our owner, who will probably want to do something fatal.
qLog(Error) << "Worker failed to start";
emit WorkerFailedToStart();
break;
default:
// On any other error we just restart the process.
qLog(Debug) << "Worker" << worker << "failed with error" << error << "- restarting";
StartOneWorker(worker);
break;
}
}
template<typename HandlerType>
void WorkerPool<HandlerType>::ProcessReadyReadStandardOutput() {
Q_ASSERT(QThread::currentThread() == thread());
QProcess *process = qobject_cast<QProcess*>(sender());
QByteArray data = process->readAllStandardOutput();
fprintf(stdout, "%s", data.data());
fflush(stdout);
}
template<typename HandlerType>
void WorkerPool<HandlerType>::ProcessReadyReadStandardError() {
Q_ASSERT(QThread::currentThread() == thread());
QProcess *process = qobject_cast<QProcess*>(sender());
QByteArray data = process->readAllStandardError();
fprintf(stderr, "%s", data.data());
fflush(stderr);
}
template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::NewReply(MessageType *message) {
const int id = next_id_.fetchAndAddOrdered(1);
message->set_id(id);
return new ReplyType(*message);
}
template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
ReplyType *reply = NewReply(message);
// Add the pending reply to the queue
{
QMutexLocker l(&message_queue_mutex_);
message_queue_.enqueue(reply);
}
// Wake up the main thread
QMetaObject::invokeMethod(this, &WorkerPool<HandlerType>::SendQueuedMessages, Qt::QueuedConnection);
return reply;
}
template<typename HandlerType>
void WorkerPool<HandlerType>::SendQueuedMessages() {
QMutexLocker l(&message_queue_mutex_);
while (!message_queue_.isEmpty()) {
ReplyType *reply = message_queue_.dequeue();
// Find a worker for this message
HandlerType *handler = NextHandler();
if (!handler) {
// No available handlers - put the message on the front of the queue.
message_queue_.prepend(reply);
qLog(Debug) << "No available handlers to process request";
break;
}
handler->SendRequest(reply);
}
}
template<typename HandlerType>
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
for (int i = 0; i < workers_.count(); ++i) {
const int worker_index = (next_worker_ + i) % workers_.count();
if (workers_[worker_index].handler_ && !workers_[worker_index].handler_->is_device_closed()) {
next_worker_ = (worker_index + 1) % workers_.count();
return workers_[worker_index].handler_;
}
}
return nullptr;
}
#endif // WORKERPOOL_H

View File

@@ -1,69 +0,0 @@
cmake_minimum_required(VERSION 3.7)
# Workaround a bug in protobuf-generate.cmake (https://github.com/protocolbuffers/protobuf/issues/12450)
if(NOT protobuf_PROTOC_EXE)
set(protobuf_PROTOC_EXE "protobuf::protoc")
endif()
if(NOT Protobuf_LIBRARIES)
set(Protobuf_LIBRARIES protobuf::libprotobuf)
endif()
set(SOURCES tagreaderbase.cpp tagreadermessages.proto)
if(USE_TAGLIB AND TAGLIB_FOUND)
list(APPEND SOURCES tagreadertaglib.cpp tagreadergme.cpp)
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
list(APPEND SOURCES tagreadertagparser.cpp)
endif()
link_directories(
${GLIB_LIBRARY_DIRS}
${PROTOBUF_LIBRARY_DIRS}
)
if(USE_TAGLIB AND TAGLIB_FOUND)
link_directories(${TAGLIB_LIBRARY_DIRS})
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
link_directories(${TAGPARSER_LIBRARY_DIRS})
endif()
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-tagreader PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
)
target_link_libraries(libstrawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
${Protobuf_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Gui
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()
protobuf_generate(TARGET libstrawberry-tagreader)

View File

@@ -1,143 +0,0 @@
/* This file is part of Strawberry.
Copyright 2018-2023, 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 <QByteArray>
#include <QString>
#include <QIODevice>
#include <QFile>
#include <QBuffer>
#include <QImage>
#include <QMimeDatabase>
#include "core/logging.h"
#include "tagreaderbase.h"
TagReaderBase::TagReaderBase() = default;
TagReaderBase::~TagReaderBase() = default;
float TagReaderBase::ConvertPOPMRating(const int POPM_rating) {
if (POPM_rating < 0x01) return 0.0F;
else if (POPM_rating < 0x40) return 0.20F;
else if (POPM_rating < 0x80) return 0.40F;
else if (POPM_rating < 0xC0) return 0.60F;
else if (POPM_rating < 0xFC) return 0.80F;
return 1.0F;
}
int TagReaderBase::ConvertToPOPMRating(const float rating) {
if (rating < 0.20) return 0x00;
else if (rating < 0.40) return 0x01;
else if (rating < 0.60) return 0x40;
else if (rating < 0.80) return 0x80;
else if (rating < 1.0) return 0xC0;
return 0xFF;
}
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::SaveFileRequest &request) {
if (!request.has_save_cover() || !request.save_cover()) {
return Cover();
}
const QString song_filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
QString cover_filename;
if (request.has_cover_filename()) {
cover_filename = QString::fromUtf8(request.cover_filename().data(), static_cast<qint64>(request.cover_filename().size()));
}
QByteArray cover_data;
if (request.has_cover_data()) {
cover_data = QByteArray(request.cover_data().data(), static_cast<qint64>(request.cover_data().size()));
}
QString cover_mime_type;
if (request.has_cover_mime_type()) {
cover_mime_type = QByteArray(request.cover_mime_type().data(), static_cast<qint64>(request.cover_mime_type().size()));
}
return LoadCoverFromRequest(song_filename, cover_filename, cover_data, cover_mime_type);
}
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::SaveEmbeddedArtRequest &request) {
const QString song_filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
QString cover_filename;
if (request.has_cover_filename()) {
cover_filename = QString::fromUtf8(request.cover_filename().data(), static_cast<qint64>(request.cover_filename().size()));
}
QByteArray cover_data;
if (request.has_cover_data()) {
cover_data = QByteArray(request.cover_data().data(), static_cast<qint64>(request.cover_data().size()));
}
QString cover_mime_type;
if (request.has_cover_mime_type()) {
cover_mime_type = QByteArray(request.cover_mime_type().data(), static_cast<qint64>(request.cover_mime_type().size()));
}
return LoadCoverFromRequest(song_filename, cover_filename, cover_data, cover_mime_type);
}
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const QString &song_filename, const QString &cover_filename, QByteArray cover_data, QString cover_mime_type) {
if (cover_data.isEmpty() && !cover_filename.isEmpty()) {
qLog(Debug) << "Loading cover from" << cover_filename << "for" << song_filename;
QFile file(cover_filename);
if (!file.open(QIODevice::ReadOnly)) {
qLog(Error) << "Failed to open file" << cover_filename << "for reading:" << file.errorString();
return Cover();
}
cover_data = file.readAll();
file.close();
}
if (!cover_data.isEmpty()) {
if (cover_mime_type.isEmpty()) {
cover_mime_type = QMimeDatabase().mimeTypeForData(cover_data).name();
}
if (cover_mime_type == "image/jpeg") {
qLog(Debug) << "Using cover from JPEG data for" << song_filename;
return Cover(cover_data, cover_mime_type);
}
if (cover_mime_type == "image/png") {
qLog(Debug) << "Using cover from PNG data for" << song_filename;
return Cover(cover_data, cover_mime_type);
}
// Convert image to JPEG.
qLog(Debug) << "Converting cover to JPEG data for" << song_filename;
QImage cover_image(cover_data);
cover_data.clear();
QBuffer buffer(&cover_data);
if (buffer.open(QIODevice::WriteOnly)) {
cover_image.save(&buffer, "JPEG");
buffer.close();
}
return Cover(cover_data, "image/jpeg");
}
return Cover();
}

View File

@@ -1,70 +0,0 @@
/* This file is part of Strawberry.
Copyright 2018-2023, 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();
class Cover {
public:
explicit Cover(const QByteArray &_data = QByteArray(), const QString &_mime_type = QString()) : data(_data), mime_type(_mime_type) {}
QByteArray data;
QString mime_type;
QString error;
};
virtual bool IsMediaFile(const QString &filename) const = 0;
virtual bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
virtual bool SaveFile(const spb::tagreader::SaveFileRequest &request) const = 0;
virtual QByteArray LoadEmbeddedArt(const QString &filename) const = 0;
virtual bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const = 0;
virtual bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
virtual bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
static float ConvertPOPMRating(const int POPM_rating);
static int ConvertToPOPMRating(const float rating);
static Cover LoadCoverFromRequest(const spb::tagreader::SaveFileRequest &request);
static Cover LoadCoverFromRequest(const spb::tagreader::SaveEmbeddedArtRequest &request);
private:
static Cover LoadCoverFromRequest(const QString &song_filename, const QString &cover_filename, QByteArray cover_data, QString cover_mime_type);
Q_DISABLE_COPY(TagReaderBase)
};
#endif // TAGREADERBASE_H

View File

@@ -1,194 +0,0 @@
syntax = "proto2";
package spb.tagreader;
message SongMetadata {
enum FileType {
UNKNOWN = 0;
WAV = 1;
FLAC = 2;
WAVPACK = 3;
OGGFLAC = 4;
OGGVORBIS = 5;
OGGOPUS = 6;
OGGSPEEX = 7;
MPEG = 8;
MP4 = 9;
ASF = 10;
AIFF = 11;
MPC = 12;
TRUEAUDIO = 13;
DSF = 14;
DSDIFF = 15;
PCM = 16;
APE = 17;
MOD = 18;
S3M = 19;
XM = 20;
IT = 21;
SPC = 22;
VGM = 23;
CDDA = 90;
STREAM = 91;
}
optional bool valid = 1;
optional string title = 2;
optional string album = 3;
optional string artist = 4;
optional string albumartist = 5;
optional int32 track = 6;
optional int32 disc = 7;
optional int32 year = 8;
optional int32 originalyear = 9;
optional string genre = 10;
optional bool compilation = 11;
optional string composer = 12;
optional string performer = 13;
optional string grouping = 14;
optional string comment = 15;
optional string lyrics = 16;
optional uint64 length_nanosec = 17;
optional int32 bitrate = 18;
optional int32 samplerate = 19;
optional int32 bitdepth = 20;
optional string url = 21;
optional string basefilename = 22;
optional FileType filetype = 23;
optional int64 filesize = 24;
optional int64 mtime = 25;
optional int64 ctime = 26;
optional uint32 playcount = 27;
optional uint32 skipcount = 28;
optional int64 lastplayed = 29;
optional int64 lastseen = 30;
optional bool art_embedded = 31;
optional float rating = 32;
optional string acoustid_id = 33;
optional string acoustid_fingerprint = 34;
optional string musicbrainz_album_artist_id = 35; // MusicBrainz Release Artist ID (MUSICBRAINZ_ALBUMARTISTID)
optional string musicbrainz_artist_id = 36; // MusicBrainz Artist ID (MUSICBRAINZ_ARTISTID)
optional string musicbrainz_original_artist_id = 37; // MusicBrainz Original Artist ID (MUSICBRAINZ_ORIGINALARTISTID)
optional string musicbrainz_album_id = 38; // MusicBrainz Release ID (MUSICBRAINZ_ALBUMID)
optional string musicbrainz_original_album_id = 39; // MusicBrainz Original Release ID (MUSICBRAINZ_ORIGINALALBUMID)
optional string musicbrainz_recording_id = 40; // MusicBrainz Recording ID (MUSICBRAINZ_TRACKID)
optional string musicbrainz_track_id = 41; // MusicBrainz Track ID (MUSICBRAINZ_RELEASETRACKID)
optional string musicbrainz_disc_id = 42; // MusicBrainz Disc ID (MUSICBRAINZ_DISCID)
optional string musicbrainz_release_group_id = 43; // MusicBrainz Release Group ID (MUSICBRAINZ_RELEASEGROUPID)
optional string musicbrainz_work_id = 44; // MusicBrainz Work ID (MUSICBRAINZ_WORKID)
optional bool suspicious_tags = 50;
}
message IsMediaFileRequest {
optional string filename = 1;
}
message IsMediaFileResponse {
optional bool success = 1;
optional string error = 2;
}
message ReadFileRequest {
optional string filename = 1;
}
message ReadFileResponse {
optional SongMetadata metadata = 1;
optional string error = 2;
}
message SaveFileRequest {
optional string filename = 1;
optional bool save_tags = 2;
optional bool save_playcount = 3;
optional bool save_rating = 4;
optional bool save_cover = 5;
optional SongMetadata metadata = 6;
optional string cover_filename = 7;
optional bytes cover_data = 8;
optional string cover_mime_type = 9;
}
message SaveFileResponse {
optional bool success = 1;
optional string error = 2;
}
message LoadEmbeddedArtRequest {
optional string filename = 1;
}
message LoadEmbeddedArtResponse {
optional bytes data = 1;
optional string error = 2;
}
message SaveEmbeddedArtRequest {
optional string filename = 1;
optional string cover_filename = 2;
optional bytes cover_data = 3;
optional string cover_mime_type = 4;
}
message SaveEmbeddedArtResponse {
optional bool success = 1;
optional string error = 2;
}
message SaveSongPlaycountToFileRequest {
optional string filename = 1;
optional SongMetadata metadata = 2;
}
message SaveSongPlaycountToFileResponse {
optional bool success = 1;
optional string error = 2;
}
message SaveSongRatingToFileRequest {
optional string filename = 1;
optional SongMetadata metadata = 2;
}
message SaveSongRatingToFileResponse {
optional bool success = 1;
optional string error = 2;
}
message Message {
optional int32 id = 1;
optional ReadFileRequest read_file_request = 2;
optional ReadFileResponse read_file_response = 3;
optional SaveFileRequest save_file_request = 4;
optional SaveFileResponse save_file_response = 5;
optional IsMediaFileRequest is_media_file_request = 6;
optional IsMediaFileResponse is_media_file_response = 7;
optional LoadEmbeddedArtRequest load_embedded_art_request = 8;
optional LoadEmbeddedArtResponse load_embedded_art_response = 9;
optional SaveEmbeddedArtRequest save_embedded_art_request = 10;
optional SaveEmbeddedArtResponse save_embedded_art_response = 11;
optional SaveSongPlaycountToFileRequest save_song_playcount_to_file_request = 12;
optional SaveSongPlaycountToFileResponse save_song_playcount_to_file_response = 13;
optional SaveSongRatingToFileRequest save_song_rating_to_file_request = 14;
optional SaveSongRatingToFileResponse save_song_rating_to_file_response = 15;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,110 +0,0 @@
/* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018-2023, 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 TAGREADERTAGLIB_H
#define TAGREADERTAGLIB_H
#include "config.h"
#include <string>
#include <QByteArray>
#include <QString>
#include <taglib/tstring.h>
#include <taglib/fileref.h>
#include <taglib/xiphcomment.h>
#include <taglib/flacfile.h>
#include <taglib/mpegfile.h>
#include <taglib/mp4file.h>
#include <taglib/apetag.h>
#include <taglib/apefile.h>
#include <taglib/asffile.h>
#include <taglib/id3v2tag.h>
#include <taglib/popularimeterframe.h>
#include "tagreaderbase.h"
#include "tagreadermessages.pb.h"
class FileRefFactory;
/*
* This class holds all useful methods to read and write tags from/to files.
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
*/
class TagReaderTagLib : public TagReaderBase {
public:
explicit TagReaderTagLib();
~TagReaderTagLib();
bool IsMediaFile(const QString &filename) const override;
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
QByteArray LoadEmbeddedArt(const QString &filename) const override;
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
static void TStringToStdString(const TagLib::String &tag, std::string *output);
private:
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
void ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const;
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comment, const spb::tagreader::SongMetadata &song) const;
void SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
void SetUserTextFrame(const std::string &description, 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;
static TagLib::ID3v2::PopularimeterFrame *GetPOPMFrameFromTag(TagLib::ID3v2::Tag *tag);
void SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const;
void SetPlaycount(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetPlaycount(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetPlaycount(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetPlaycount(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetRating(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const;
void SetRating(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetRating(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetRating(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetRating(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SetEmbeddedArt(TagLib::FLAC::File *flac_file, TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
void SetEmbeddedArt(TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
void SetEmbeddedArt(TagLib::MPEG::File *file_mp3, TagLib::ID3v2::Tag *tag, const QByteArray &data, const QString &mime_type) const;
void SetEmbeddedArt(TagLib::MP4::File *aac_file, TagLib::MP4::Tag *tag, const QByteArray &data, const QString &mime_type) const;
private:
FileRefFactory *factory_;
Q_DISABLE_COPY(TagReaderTagLib)
};
#endif // TAGREADERTAGLIB_H

View File

@@ -1,548 +0,0 @@
/* 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 <vector>
#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 "utilities/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 (TagParser::AbstractTrack *track : tracks) {
if (track->mediaType() == TagParser::MediaType::Audio) {
taginfo.close();
return true;
}
}
taginfo.close();
}
catch(...) {}
return false;
}
bool 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 false;
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
const QByteArray basefilename = fileinfo.fileName().toUtf8();
song->set_basefilename(basefilename.constData(), basefilename.size());
song->set_url(url.constData(), url.size());
song->set_filesize(fileinfo.size());
song->set_mtime(fileinfo.lastModified().isValid() ? std::max(fileinfo.lastModified().toSecsSinceEpoch(), 0LL) : 0LL);
song->set_ctime(fileinfo.birthTime().isValid() ? std::max(fileinfo.birthTime().toSecsSinceEpoch(), 0LL) : fileinfo.lastModified().isValid() ? std::max(fileinfo.lastModified().toSecsSinceEpoch(), 0LL) : 0LL);
if (song->ctime() <= 0) {
song->set_ctime(song->mtime());
}
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 false;
}
taginfo.parseTracks(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
taginfo.parseTags(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return false;
}
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
std::vector<TagParser::AbstractTrack*> tracks = taginfo.tracks();
for (TagParser::AbstractTrack *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 false;
}
for (TagParser::Tag *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_embedded(true);
}
const float rating = ConvertPOPMRating(tag->value(TagParser::KnownField::Rating));
if (song->rating() <= 0 && rating > 0.0 && rating <= 1.0) {
song->set_rating(rating);
}
}
// Set integer fields to -1 if they're not valid
if (song->track() <= 0) { song->set_track(-1); }
if (song->disc() <= 0) { song->set_disc(-1); }
if (song->year() <= 0) { song->set_year(-1); }
if (song->originalyear() <= 0) { song->set_originalyear(-1); }
if (song->samplerate() <= 0) { song->set_samplerate(-1); }
if (song->bitdepth() <= 0) { song->set_bitdepth(-1); }
if (song->bitrate() <= 0) { song->set_bitrate(-1); }
if (song->lastplayed() <= 0) { song->set_lastplayed(-1); }
song->set_valid(true);
taginfo.close();
return true;
}
catch(...) {
return false;
}
}
bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request) const {
if (request.filename().empty()) return false;
const QString filename = QString::fromUtf8(request.filename().data(), request.filename().size());
const spb::tagreader::SongMetadata song = request.metadata();
const bool save_tags = request.has_save_tags() && request.save_tags();
const bool save_playcount = request.has_save_playcount() && request.save_playcount();
const bool save_rating = request.has_save_rating() && request.save_rating();
const bool save_cover = request.has_save_cover() && request.save_cover();
QStringList save_tags_options;
if (save_tags) {
save_tags_options << "tags";
}
if (save_playcount) {
save_tags_options << "playcount";
}
if (save_rating) {
save_tags_options << "rating";
}
if (save_cover) {
save_tags_options << "embedded cover";
}
qLog(Debug) << "Saving" << save_tags_options.join(", ") << "to" << filename;
const QByteArray cover_data = LoadCoverDataFromRequest(request);
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 (TagParser::Tag *tag : taginfo.tags()) {
if (save_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()));
}
if (save_playcount) {
SaveSongPlaycountToFile(tag, song);
}
if (save_rating) {
SaveSongRatingToFile(tag, song);
}
if (save_cover) {
SaveEmbeddedArt(tag, cover_data);
}
}
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 (TagParser::Tag *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();
}
void TagReaderTagParser::SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &data) const {
tag->setValue(TagParser::KnownField::Cover, TagParser::TagValue(data.toStdString()));
}
bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const {
if (request.filename().empty()) return false;
const QString filename = QString::fromUtf8(request.filename().data(), request.filename().size());
qLog(Debug) << "Saving art to" << filename;
const QByteArray cover_data = LoadCoverDataFromRequest(request);
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 (TagParser::Tag *tag : taginfo.tags()) {
SaveEmbeddedArt(tag, cover_data);
}
taginfo.applyChanges(diag, progress);
taginfo.close();
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
return true;
}
catch(...) {}
return false;
}
void TagReaderTagParser::SaveSongPlaycountToFile(TagParser::Tag*, const spb::tagreader::SongMetadata&) const {}
bool TagReaderTagParser::SaveSongPlaycountToFile(const QString&, const spb::tagreader::SongMetadata&) const { return false; }
void TagReaderTagParser::SaveSongRatingToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const {
tag->setValue(TagParser::KnownField::Rating, TagParser::TagValue(ConvertToPOPMRating(song.rating())));
}
bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
if (filename.isEmpty()) return false;
qLog(Debug) << "Saving song rating 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 (TagParser::Tag *tag : taginfo.tags()) {
SaveSongRatingToFile(tag, song);
}
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

@@ -1,62 +0,0 @@
/* 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 <tagparser/tag.h>
#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;
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
QByteArray LoadEmbeddedArt(const QString &filename) const override;
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
private:
void SaveSongPlaycountToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SaveSongRatingToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const;
void SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &data) const;
public:
Q_DISABLE_COPY(TagReaderTagParser)
};
#endif // TAGREADERTAGPARSER_H

View File

@@ -1,15 +0,0 @@
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}
Qt${QT_VERSION_MAJOR}::Core
)

View File

@@ -1,147 +0,0 @@
/* 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|, reexport)?\\)$"));
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::exists(real_path)) {
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::exists(real_path) && !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::exists(real_path)) {
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 {
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

@@ -1,64 +0,0 @@
cmake_minimum_required(VERSION 3.7)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(SOURCES main.cpp tagreaderworker.cpp)
set(HEADERS tagreaderworker.h)
qt_wrap_cpp(MOC ${HEADERS})
link_directories(${GLIB_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_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
)
target_include_directories(strawberry-tagreader PRIVATE
${CMAKE_SOURCE_DIR}/ext/libstrawberry-common
${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader
${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader
${CMAKE_BINARY_DIR}/src
)
target_link_libraries(strawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
libstrawberry-common
libstrawberry-tagreader
)
if(USE_TAGLIB AND TAGLIB_FOUND)
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
target_link_libraries(strawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
endif()
if(FREEBSD)
target_link_libraries(strawberry-tagreader PRIVATE execinfo)
endif()
if(APPLE)
target_link_libraries(strawberry-tagreader PRIVATE /System/Library/Frameworks/Foundation.framework)
endif()
if(APPLE)
install(TARGETS strawberry-tagreader DESTINATION ${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns)
else()
install(TARGETS strawberry-tagreader RUNTIME DESTINATION bin)
endif()

View File

@@ -1,61 +0,0 @@
/* This file is part of Strawberry.
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
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 <QtGlobal>
#include <iostream>
#include <QCoreApplication>
#include <QList>
#include <QString>
#include <QStringList>
#include <QLocalSocket>
#include "core/logging.h"
#include "tagreaderworker.h"
int main(int argc, char **argv) {
QCoreApplication a(argc, argv);
QStringList args(a.arguments());
if (args.count() != 2) {
std::cerr << "This program is used internally by Strawberry to parse tags in music files\n"
"without exposing the whole application to crashes caused by malformed\n"
"files. It is not meant to be run on its own.\n";
return 1;
}
logging::Init();
qLog(Info) << "TagReader worker connecting to" << args[1];
// Connect to the parent process.
QLocalSocket socket;
socket.connectToServer(args[1]);
if (!socket.waitForConnected(2000)) {
std::cerr << "Failed to connect to the parent process.\n";
return 1;
}
TagReaderWorker worker(&socket);
return a.exec();
}

View File

@@ -1,100 +0,0 @@
/* This file is part of Strawberry.
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
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 <QCoreApplication>
#include <QObject>
#include <QIODevice>
#include <QByteArray>
#include "tagreaderworker.h"
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
: AbstractMessageHandler<spb::tagreader::Message>(socket, parent) {}
void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
spb::tagreader::Message reply;
bool success = HandleMessage(message, reply, &tag_reader_);
if (!success) {
#if defined(USE_TAGLIB)
HandleMessage(message, reply, &tag_reader_gme_);
#endif
}
SendReply(message, &reply);
}
void TagReaderWorker::DeviceClosed() {
AbstractMessageHandler<spb::tagreader::Message>::DeviceClosed();
QCoreApplication::exit();
}
bool TagReaderWorker::HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply, TagReaderBase *reader) {
if (message.has_is_media_file_request()) {
const QString filename = QString::fromUtf8(message.is_media_file_request().filename().data(), static_cast<qint64>(message.is_media_file_request().filename().size()));
bool success = reader->IsMediaFile(filename);
reply.mutable_is_media_file_response()->set_success(success);
return success;
}
else if (message.has_read_file_request()) {
const QString filename = QString::fromUtf8(message.read_file_request().filename().data(), static_cast<qint64>(static_cast<qint64>(message.read_file_request().filename().size())));
bool success = reader->ReadFile(filename, reply.mutable_read_file_response()->mutable_metadata());
return success;
}
else if (message.has_save_file_request()) {
bool success = reader->SaveFile(message.save_file_request());
reply.mutable_save_file_response()->set_success(success);
return success;
}
else if (message.has_load_embedded_art_request()) {
const QString filename = QString::fromUtf8(message.load_embedded_art_request().filename().data(), static_cast<qint64>(static_cast<qint64>(message.load_embedded_art_request().filename().size())));
QByteArray data = reader->LoadEmbeddedArt(filename);
reply.mutable_load_embedded_art_response()->set_data(data.constData(), data.size());
return true;
}
else if (message.has_save_embedded_art_request()) {
bool success = reader->SaveEmbeddedArt(message.save_embedded_art_request());
reply.mutable_save_embedded_art_response()->set_success(success);
return success;
}
else if (message.has_save_song_playcount_to_file_request()) {
const QString filename = QString::fromUtf8(message.save_song_playcount_to_file_request().filename().data(), static_cast<qint64>(static_cast<qint64>(message.save_song_playcount_to_file_request().filename().size())));
bool success = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().metadata());
reply.mutable_save_song_playcount_to_file_response()->set_success(success);
return success;
}
else if (message.has_save_song_rating_to_file_request()) {
const QString filename = QString::fromUtf8(message.save_song_rating_to_file_request().filename().data(), static_cast<qint64>(message.save_song_rating_to_file_request().filename().size()));
bool success = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().metadata());
reply.mutable_save_song_rating_to_file_response()->set_success(success);
return success;
}
return false;
}

View File

@@ -1,60 +0,0 @@
/* This file is part of Strawberry.
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
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 TAGREADERWORKER_H
#define TAGREADERWORKER_H
#include "config.h"
#include <QObject>
#include "core/messagehandler.h"
#if defined(USE_TAGLIB)
# include "tagreadertaglib.h"
# include "tagreadergme.h"
#elif defined(USE_TAGPARSER)
# include "tagreadertagparser.h"
#endif
#include "tagreadermessages.pb.h"
class QIODevice;
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
Q_OBJECT
public:
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
protected:
void MessageArrived(const spb::tagreader::Message &message) override;
void DeviceClosed() override;
private:
// Handle message using specific TagReaderBase implementation. Returns true on successful message handle.
bool HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply, TagReaderBase* reader);
#if defined(USE_TAGLIB)
TagReaderTagLib tag_reader_;
TagReaderGME tag_reader_gme_;
#elif defined(USE_TAGPARSER)
TagReaderTagParser tag_reader_;
#endif
};
#endif // TAGREADERWORKER_H

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@
#include <algorithm>
#include <QWidget>
#include <QVector>
#include <QList>
#include <QPainter>
#include <QPalette>
#include <QBasicTimer>
@@ -67,11 +67,13 @@ AnalyzerBase::~AnalyzerBase() {
delete fht_;
}
void AnalyzerBase::showEvent(QShowEvent*) {
void AnalyzerBase::showEvent(QShowEvent *e) {
Q_UNUSED(e)
timer_.start(timeout(), this);
}
void AnalyzerBase::hideEvent(QHideEvent*) {
void AnalyzerBase::hideEvent(QHideEvent *e) {
Q_UNUSED(e)
timer_.stop();
}
@@ -87,8 +89,8 @@ void AnalyzerBase::ChangeTimeout(const int timeout) {
void AnalyzerBase::transform(Scope &scope) {
QVector<float> aux(fht_->size());
if (static_cast<unsigned long int>(aux.size()) >= scope.size()) {
QList<float> aux(fht_->size());
if (static_cast<quint64>(aux.size()) >= scope.size()) {
std::copy(scope.begin(), scope.end(), aux.begin());
}
else {
@@ -108,7 +110,7 @@ void AnalyzerBase::paintEvent(QPaintEvent *e) {
p.fillRect(e->rect(), palette().color(QPalette::Window));
switch (engine_->state()) {
case EngineBase::State::Playing: {
case EngineBase::State::Playing:{
const EngineBase::Scope &thescope = engine_->scope(timeout_);
int i = 0;

View File

@@ -31,14 +31,12 @@
#include <vector>
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QBasicTimer>
#include <QString>
#include <QPainter>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "analyzer/fht.h"
#include "engine/enginebase.h"
@@ -65,13 +63,13 @@ class AnalyzerBase : public QWidget {
using Scope = std::vector<float>;
explicit AnalyzerBase(QWidget*, const uint scopeSize = 7);
void hideEvent(QHideEvent*) override;
void showEvent(QShowEvent*) override;
void hideEvent(QHideEvent *e) override;
void showEvent(QShowEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void timerEvent(QTimerEvent *e) override;
int resizeExponent(int);
int resizeForBands(const int);
int resizeExponent(int exp);
int resizeForBands(const int bands);
virtual void init() {}
virtual void transform(Scope&);
virtual void analyze(QPainter &p, const Scope&, const bool new_frame) = 0;

View File

@@ -39,23 +39,29 @@
#include "analyzerbase.h"
#include "blockanalyzer.h"
#include "boomanalyzer.h"
#include "turbineanalyzer.h"
#include "sonogramanalyzer.h"
#include "waverubberanalyzer.h"
#include "rainbowanalyzer.h"
#include "sonogram.h"
#include "includes/shared_ptr.h"
#include "core/logging.h"
#include "core/shared_ptr.h"
#include "core/settings.h"
#include "engine/enginebase.h"
using namespace std::chrono_literals;
using namespace Qt::Literals::StringLiterals;
const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
const char *AnalyzerContainer::kSettingsFramerate = "framerate";
// Framerates
const int AnalyzerContainer::kLowFramerate = 20;
const int AnalyzerContainer::kMediumFramerate = 25;
const int AnalyzerContainer::kHighFramerate = 30;
const int AnalyzerContainer::kSuperHighFramerate = 60;
namespace {
constexpr int kLowFramerate = 20;
constexpr int kMediumFramerate = 25;
constexpr int kHighFramerate = 30;
constexpr int kSuperHighFramerate = 60;
} // namespace
AnalyzerContainer::AnalyzerContainer(QWidget *parent)
: QWidget(parent),
@@ -84,9 +90,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
AddAnalyzerType<BlockAnalyzer>();
AddAnalyzerType<BoomAnalyzer>();
AddAnalyzerType<NyanCatAnalyzer>();
AddAnalyzerType<TurbineAnalyzer>();
AddAnalyzerType<SonogramAnalyzer>();
AddAnalyzerType<WaveRubberAnalyzer>();
AddAnalyzerType<RainbowDashAnalyzer>();
AddAnalyzerType<Sonogram>();
AddAnalyzerType<NyanCatAnalyzer>();
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, &AnalyzerContainer::DisableAnalyzer);
disable_action_->setCheckable(true);
@@ -104,16 +112,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
if (engine_->type() != EngineBase::Type::GStreamer) {
return;
}
if (e->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
context_menu_->popup(e->globalPosition().toPoint());
#else
context_menu_->popup(e->globalPos());
#endif
}
}
@@ -123,7 +123,7 @@ void AnalyzerContainer::ShowPopupMenu() {
}
void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
emit WheelEvent(e->angleDelta().y());
Q_EMIT WheelEvent(e->angleDelta().y());
}
void AnalyzerContainer::SetEngine(SharedPtr<EngineBase> engine) {
@@ -134,15 +134,17 @@ void AnalyzerContainer::SetEngine(SharedPtr<EngineBase> engine) {
}
void AnalyzerContainer::DisableAnalyzer() {
delete current_analyzer_;
current_analyzer_ = nullptr;
Save();
}
void AnalyzerContainer::ChangeAnalyzer(const int id) {
QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
QObject *instance = analyzer_types_.at(id)->newInstance(Q_ARG(QWidget*, this));
if (!instance) {
qLog(Warning) << "Couldn't initialize a new" << analyzer_types_[id]->className();
@@ -178,9 +180,9 @@ void AnalyzerContainer::ChangeFramerate(int new_framerate) {
void AnalyzerContainer::Load() {
QSettings s;
Settings s;
s.beginGroup(kSettingsGroup);
QString type = s.value("type", "BlockAnalyzer").toString();
QString type = s.value("type", u"BlockAnalyzer"_s).toString();
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
s.endGroup();
@@ -191,20 +193,27 @@ void AnalyzerContainer::Load() {
}
else {
for (int i = 0; i < analyzer_types_.count(); ++i) {
if (type == analyzer_types_[i]->className()) {
if (type == QString::fromLatin1(analyzer_types_[i]->className())) {
ChangeAnalyzer(i);
actions_[i]->setChecked(true);
QAction *action = actions_.value(i);
action->setChecked(true);
break;
}
}
if (!current_analyzer_) {
ChangeAnalyzer(0);
QAction *action = actions_.value(0);
action->setChecked(true);
}
}
// Framerate
QList<QAction*> actions = group_framerate_->actions();
const QList<QAction*> actions = group_framerate_->actions();
for (int i = 0; i < framerate_list_.count(); ++i) {
if (current_framerate_ == framerate_list_[i]) {
if (current_framerate_ == framerate_list_.value(i)) {
ChangeFramerate(current_framerate_);
actions[i]->setChecked(true);
QAction *action = actions[i];
action->setChecked(true);
break;
}
}
@@ -215,7 +224,7 @@ void AnalyzerContainer::SaveFramerate(const int framerate) {
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
current_framerate_ = framerate;
QSettings s;
Settings s;
s.beginGroup(kSettingsGroup);
s.setValue(kSettingsFramerate, current_framerate_);
s.endGroup();
@@ -224,9 +233,9 @@ void AnalyzerContainer::SaveFramerate(const int framerate) {
void AnalyzerContainer::Save() {
QSettings s;
Settings s;
s.beginGroup(kSettingsGroup);
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
s.setValue("type", current_analyzer_ ? QString::fromLatin1(current_analyzer_->metaObject()->className()) : QVariant());
s.endGroup();
}

View File

@@ -30,7 +30,7 @@
#include <QAction>
#include <QActionGroup>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "engine/enginebase.h"
class QTimer;
@@ -46,30 +46,24 @@ class AnalyzerContainer : public QWidget {
explicit AnalyzerContainer(QWidget *parent);
void SetEngine(SharedPtr<EngineBase> engine);
void SetActions(QAction *visualisation);
static const char *kSettingsGroup;
static const char *kSettingsFramerate;
signals:
Q_SIGNALS:
void WheelEvent(const int delta);
protected:
void mouseReleaseEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void wheelEvent(QWheelEvent *e) override;
private slots:
private Q_SLOTS:
void ChangeAnalyzer(const int id);
void ChangeFramerate(int new_framerate);
void DisableAnalyzer();
void ShowPopupMenu();
private:
static const int kLowFramerate;
static const int kMediumFramerate;
static const int kHighFramerate;
static const int kSuperHighFramerate;
void Load();
void Save();
void SaveFramerate(const int framerate);

View File

@@ -36,12 +36,14 @@
#include "analyzerbase.h"
#include "fht.h"
const int BlockAnalyzer::kHeight = 2;
const int BlockAnalyzer::kWidth = 4;
const int BlockAnalyzer::kMinRows = 3; // arbitrary
const int BlockAnalyzer::kMinColumns = 32; // arbitrary
const int BlockAnalyzer::kMaxColumns = 256; // must be 2**n
const int BlockAnalyzer::kFadeSize = 90;
namespace {
constexpr int kHeight = 2;
constexpr int kWidth = 4;
constexpr int kMinRows = 3; // arbitrary
constexpr int kMinColumns = 32; // arbitrary
constexpr int kMaxColumns = 256; // must be 2**n
constexpr int kFadeSize = 90;
} // namespace
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
@@ -136,7 +138,7 @@ void BlockAnalyzer::transform(Scope &s) {
}
void BlockAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
void BlockAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
// y = 2 3 2 1 0 2
// . . . . # .
@@ -165,11 +167,12 @@ void BlockAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
for (int x = 0, y = 0; x < static_cast<int>(scope_.size()); ++x) {
// determine y
for (y = 0; scope_[x] < yscale_[y]; ++y);
for (y = 0; scope_[x] < yscale_.at(y); ++y);
// 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]) {
y = static_cast<int>(store_[x] += step_);
if (static_cast<double>(y) > store_.at(x)) {
store_[x] += step_;
y = static_cast<int>(store_.value(x));
}
else {
store_[x] = y;
@@ -177,18 +180,19 @@ void BlockAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
// 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 (y <= fade_pos_[x] /*|| fade_intensity_[x] < kFadeSize / 3*/) {
if (y <= fade_pos_.at(x) /*|| fade_intensity_[x] < kFadeSize / 3*/) {
fade_pos_[x] = y;
fade_intensity_[x] = kFadeSize;
}
if (fade_intensity_[x] > 0) {
const int offset = --fade_intensity_[x];
const int y2 = y_ + (fade_pos_[x] * (kHeight + 1));
if (fade_intensity_.at(x) > 0) {
--fade_intensity_[x];
const int offset = fade_intensity_.value(x);
const int y2 = y_ + (fade_pos_.value(x) * (kHeight + 1));
canvas_painter.drawPixmap(x * (kWidth + 1), y2, fade_bars_[offset], 0, 0, kWidth, height() - y2);
}
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
if (fade_intensity_.at(x) == 0) fade_pos_[x] = rows_;
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing, rows_ means none are
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
@@ -266,12 +270,12 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
// value is the best measure of contrast
// if there is enough difference in value already, return fg unchanged
if (dv > static_cast<int>(amount)) return fg;
if (dv > amount) return fg;
int ds = abs(bs - fs);
// saturation is good enough too. But not as good. TODO adapt this a little
if (ds > static_cast<int>(amount)) return fg;
if (ds > amount) return fg;
int dh = abs(bh - fh);
@@ -285,7 +289,7 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
if (ds > amount / 2 && (bs > 125 && fs > 125)) {
return fg;
}
else if (dv > amount / 2 && (bv > 125 && fv > 125)) {
if (dv > amount / 2 && (bv > 125 && fv > 125)) {
return fg;
}
}
@@ -294,7 +298,7 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
// low saturation on a low saturation is sad
const int tmp = 50 - fs;
fs = 50;
if (static_cast<int>(amount) > tmp) {
if (amount > tmp) {
amount -= tmp;
}
else {
@@ -310,25 +314,25 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
if (amount > 0) adjustToLimits(bs, fs, amount);
// see if we need to adjust the hue
if (static_cast<int>(amount) > 0)
fh += static_cast<int>(amount); // cycles around;
if (amount > 0)
fh += amount; // cycles around;
return QColor::fromHsv(fh, fs, fv);
}
if (fv > bv && bv > static_cast<int>(amount)) {
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
if (fv > bv && bv > amount) {
return QColor::fromHsv(fh, fs, bv - amount);
}
if (fv < bv && fv > static_cast<int>(amount)) {
if (fv < bv && fv > amount) {
return QColor::fromHsv(fh, fs, fv - amount);
}
if (fv > bv && (255 - fv > static_cast<int>(amount))) {
if (fv > bv && (255 - fv > amount)) {
return QColor::fromHsv(fh, fs, fv + amount);
}
if (fv < bv && (255 - bv > static_cast<int>(amount))) {
if (fv < bv && (255 - bv > amount)) {
return QColor::fromHsv(fh, fs, bv + amount);
}
@@ -336,7 +340,9 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, int amount) {
}
void BlockAnalyzer::paletteChange(const QPalette&) {
void BlockAnalyzer::paletteChange(const QPalette &_palette) {
Q_UNUSED(_palette)
const QColor bg = palette().color(QPalette::Window);
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));

View File

@@ -26,7 +26,7 @@
#include <QtGlobal>
#include <QObject>
#include <QVector>
#include <QList>
#include <QString>
#include <QPixmap>
#include <QPainter>
@@ -43,20 +43,13 @@ class BlockAnalyzer : public AnalyzerBase {
public:
Q_INVOKABLE explicit BlockAnalyzer(QWidget*);
static const int kHeight;
static const int kWidth;
static const int kMinRows;
static const int kMinColumns;
static const int kMaxColumns;
static const int kFadeSize;
static const char *kName;
protected:
void transform(Scope&) override;
void analyze(QPainter &p, const Scope&, bool new_frame) override;
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
void resizeEvent(QResizeEvent*) override;
virtual void paletteChange(const QPalette&);
virtual void paletteChange(const QPalette &_palette);
void framerateChanged() override;
void drawBackground();
@@ -72,12 +65,12 @@ class BlockAnalyzer : public AnalyzerBase {
QPixmap background_;
QPixmap canvas_;
Scope scope_; // so we don't create a vector every frame
QVector<double> store_; // current bar heights
QVector<double> yscale_;
QList<double> store_; // current bar heights
QList<double> yscale_;
QVector<QPixmap> fade_bars_;
QVector<int> fade_pos_;
QVector<int> fade_intensity_;
QList<QPixmap> fade_bars_;
QList<int> fade_pos_;
QList<int> fade_intensity_;
double step_; // rows to fall per frame
};

View File

@@ -78,7 +78,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent *e) {
bands_ = qMin(static_cast<int>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
scope_.resize(bands_);
F_ = static_cast<double>(HEIGHT) / (log10(256) * static_cast<double>(1.1) /*<- max. amplitude*/);
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size());

View File

@@ -45,9 +45,9 @@ class BoomAnalyzer : public AnalyzerBase {
static const char *kName;
void transform(Scope &s) override;
void analyze(QPainter &p, const Scope&, const bool new_frame) override;
void analyze(QPainter &p, const Scope &scope, const bool new_frame) override;
public slots:
public Q_SLOTS:
void changeK_barHeight(int);
void changeF_peakSpeed(int);

View File

@@ -25,10 +25,10 @@
#include <algorithm>
#include <cmath>
#include <QVector>
#include <QList>
#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(uint n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : static_cast<int>(n)) {
if (n > 3) {
buf_vector_.resize(num_);
@@ -47,7 +47,7 @@ float *FHT::buf_() { return buf_vector_.data(); }
float *FHT::tab_() { return tab_vector_.data(); }
int *FHT::log_() { return log_vector_.data(); }
void FHT::makeCasTable(void) {
void FHT::makeCasTable() {
float *costab = tab_();
float *sintab = tab_() + num_ / 2 + 1;

View File

@@ -23,7 +23,7 @@
#ifndef FHT_H
#define FHT_H
#include <QVector>
#include <QList>
/**
* Implementation of the Hartley Transform after Bracewell's discrete
@@ -37,9 +37,9 @@ class FHT {
const int num_;
const int exp2_;
QVector<float> buf_vector_;
QVector<float> tab_vector_;
QVector<int> log_vector_;
QList<float> buf_vector_;
QList<float> tab_vector_;
QList<int> log_vector_;
float *buf_();
float *tab_();

View File

@@ -41,18 +41,23 @@
#include "fht.h"
#include "analyzerbase.h"
const int RainbowAnalyzer::kHeight[] = { 21, 33 };
const int RainbowAnalyzer::kWidth[] = { 34, 53 };
const int RainbowAnalyzer::kFrameCount[] = { 6, 16 };
const int RainbowAnalyzer::kRainbowHeight[] = { 21, 16 };
const int RainbowAnalyzer::kRainbowOverlap[] = { 13, 15 };
const int RainbowAnalyzer::kSleepingHeight[] = { 24, 33 };
using namespace Qt::Literals::StringLiterals;
const char *NyanCatAnalyzer::kName = "Nyanalyzer Cat";
const char *RainbowDashAnalyzer::kName = "Rainbow Dash";
const float RainbowAnalyzer::kPixelScale = 0.02F;
RainbowAnalyzer::RainbowType RainbowAnalyzer::rainbowtype;
const int RainbowAnalyzer::kHeight[] = { 21, 33 };
const int RainbowAnalyzer::kWidth[] = { 34, 53 };
const int RainbowAnalyzer::kFrameCount[] = { 6, 16 };
const int RainbowAnalyzer::kSleepingHeight[] = { 24, 33 };
namespace {
constexpr int kFrameIntervalMs = 150;
constexpr int kRainbowHeight[] = { 21, 16 };
constexpr int kRainbowOverlap[] = { 13, 15 };
constexpr float kPixelScale = 0.02F;
} // namespace
RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
: AnalyzerBase(parent, 9),
@@ -65,8 +70,8 @@ RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
background_brush_(QColor(0x0f, 0x43, 0x73)) {
rainbowtype = rbtype;
cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
cat_dash_[1] = QPixmap(":/pictures/rainbowdash.png");
cat_dash_[0] = QPixmap(u":/pictures/nyancat.png"_s);
cat_dash_[1] = QPixmap(u":/pictures/rainbowdash.png"_s);
memset(history_, 0, sizeof(history_));
for (int i = 0; i < kRainbowBands; ++i) {
@@ -106,7 +111,7 @@ void RainbowAnalyzer::resizeEvent(QResizeEvent *e) {
}
void RainbowAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
void RainbowAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
// Discard the second half of the transform
const int scope_size = static_cast<int>(s.size() / 2);

View File

@@ -49,44 +49,37 @@ class RainbowAnalyzer : public AnalyzerBase {
Dash = 1
};
RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
explicit RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
protected:
void transform(Scope&) override;
void analyze(QPainter &p, const Scope&, bool new_frame) override;
void transform(Scope &s) override;
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
void timerEvent(QTimerEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
static const int kRainbowBands = 6;
static const int kHistorySize = 128;
static RainbowType rainbowtype;
static const int kHeight[];
static const int kWidth[];
static const int kFrameCount[];
static const int kRainbowHeight[];
static const int kRainbowOverlap[];
static const int kSleepingHeight[];
static const int kHistorySize = 128;
static const int kRainbowBands = 6;
static const float kPixelScale;
static const int kFrameIntervalMs = 150;
static RainbowType rainbowtype;
inline QRect SourceRect(RainbowType _rainbowtype) const {
inline QRect SourceRect(const RainbowType _rainbowtype) const {
return QRect(0, kHeight[_rainbowtype] * frame_, kWidth[_rainbowtype], kHeight[_rainbowtype]);
}
inline QRect SleepingSourceRect(RainbowType _rainbowtype) const {
inline QRect SleepingSourceRect(const RainbowType _rainbowtype) const {
return QRect(0, kHeight[_rainbowtype] * kFrameCount[_rainbowtype], kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
}
inline QRect DestRect(RainbowType _rainbowtype) const {
inline QRect DestRect(const RainbowType _rainbowtype) const {
return QRect(width() - kWidth[_rainbowtype], (height() - kHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kHeight[_rainbowtype]);
}
inline QRect SleepingDestRect(RainbowType _rainbowtype) const {
inline QRect SleepingDestRect(const RainbowType _rainbowtype) const {
return QRect(width() - kWidth[_rainbowtype], (height() - kSleepingHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
}

View File

@@ -26,14 +26,14 @@
#include "engine/enginebase.h"
#include "sonogram.h"
#include "sonogramanalyzer.h"
const char *Sonogram::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
const char *SonogramAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
Sonogram::Sonogram(QWidget *parent)
SonogramAnalyzer::SonogramAnalyzer(QWidget *parent)
: AnalyzerBase(parent, 9) {}
void Sonogram::resizeEvent(QResizeEvent *e) {
void SonogramAnalyzer::resizeEvent(QResizeEvent *e) {
Q_UNUSED(e)
@@ -42,7 +42,7 @@ void Sonogram::resizeEvent(QResizeEvent *e) {
}
void Sonogram::analyze(QPainter &p, const Scope &s, bool new_frame) {
void SonogramAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
p.drawPixmap(0, 0, canvas_);
@@ -81,7 +81,7 @@ void Sonogram::analyze(QPainter &p, const Scope &s, bool new_frame) {
}
void Sonogram::transform(Scope &scope) {
void SonogramAnalyzer::transform(Scope &scope) {
fht_->power2(scope.data());
fht_->scale(scope.data(), 1.0 / 256);
@@ -89,6 +89,6 @@ void Sonogram::transform(Scope &scope) {
}
void Sonogram::demo(QPainter &p) {
void SonogramAnalyzer::demo(QPainter &p) {
analyze(p, Scope(fht_->size(), 0), new_frame_);
}

View File

@@ -21,24 +21,25 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SONOGRAM_H
#define SONOGRAM_H
#ifndef SONOGRAMANALYZER_H
#define SONOGRAMANALYZER_H
#include <QPixmap>
#include <QPainter>
#include "analyzerbase.h"
class Sonogram : public AnalyzerBase {
class SonogramAnalyzer : public AnalyzerBase {
Q_OBJECT
public:
Q_INVOKABLE explicit Sonogram(QWidget *parent);
Q_INVOKABLE explicit SonogramAnalyzer(QWidget *parent);
static const char *kName;
protected:
void resizeEvent(QResizeEvent *e) override;
void analyze(QPainter &p, const Scope &s, bool new_frame) override;
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
void transform(Scope &scope) override;
void demo(QPainter &p) override;
@@ -46,4 +47,4 @@ class Sonogram : public AnalyzerBase {
QPixmap canvas_;
};
#endif // SONOGRAM_H
#endif // SONOGRAMANALYZER_H

View File

@@ -0,0 +1,100 @@
/*
Strawberry Music Player
This file was part of Clementine.
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
Copyright 2003, Max Howell <max.howell@methylblue.com>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include "turbineanalyzer.h"
#include "engine/enginebase.h"
const char *TurbineAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Turbine");
TurbineAnalyzer::TurbineAnalyzer(QWidget *parent) : BoomAnalyzer(parent) {}
void TurbineAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame) {
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
p.drawPixmap(0, 0, canvas_);
return;
}
const uint hd2 = height() / 2;
const uint kMaxHeight = hd2 - 1;
QPainter canvas_painter(&canvas_);
canvas_.fill(palette().color(QPalette::Window));
AnalyzerBase::interpolate(scope, scope_);
for (uint i = 0, x = 0, y = 0; i < static_cast<uint>(bands_); ++i, x += kColumnWidth + 1) {
float h = static_cast<float>(std::min(log10(scope_[i] * 256.0) * F_ * 0.5, kMaxHeight * 1.0));
if (h > bar_height_[i]) {
bar_height_[i] = h;
if (h > peak_height_[i]) {
peak_height_[i] = h;
peak_speed_[i] = 0.01;
}
else {
goto peak_handling;
}
}
else {
if (bar_height_[i] > 0.0) {
bar_height_[i] -= K_barHeight_; // 1.4
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
}
peak_handling:
if (peak_height_[i] > 0.0) {
peak_height_[i] -= peak_speed_[i];
peak_speed_[i] *= F_peakSpeed_; // 1.12
peak_height_[i] = std::max(0.0, std::max(bar_height_[i], peak_height_[i]));
}
}
y = hd2 - static_cast<uint>(bar_height_[i]);
canvas_painter.drawPixmap(static_cast<int>(x + 1), static_cast<int>(y), barPixmap_, 0, static_cast<int>(y), -1, -1);
canvas_painter.drawPixmap(static_cast<int>(x + 1), static_cast<int>(hd2), barPixmap_, 0, static_cast<int>(bar_height_[i]), -1, -1);
canvas_painter.setPen(fg_);
if (bar_height_[i] > 0) {
canvas_painter.drawRect(static_cast<int>(x), static_cast<int>(y), kColumnWidth - 1, static_cast<int>(bar_height_[i]) * 2 - 1);
}
const uint x2 = x + kColumnWidth - 1;
canvas_painter.setPen(palette().color(QPalette::Midlight));
y = hd2 - static_cast<uint>(peak_height_[i]);
canvas_painter.drawLine(static_cast<int>(x), static_cast<int>(y), static_cast<int>(x2), static_cast<int>(y));
y = hd2 + static_cast<uint>(peak_height_[i]);
canvas_painter.drawLine(static_cast<int>(x), static_cast<int>(y), static_cast<int>(x2), static_cast<int>(y));
}
p.drawPixmap(0, 0, canvas_);
}

View File

@@ -0,0 +1,41 @@
/*
Strawberry Music Player
This file was part of Clementine.
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TURBINEANALYZER_H
#define TURBINEANALYZER_H
#include "boomanalyzer.h"
class QPainter;
class TurbineAnalyzer : public BoomAnalyzer {
Q_OBJECT
public:
Q_INVOKABLE explicit TurbineAnalyzer(QWidget *parent);
void analyze(QPainter &p, const Scope &scope, const bool new_frame);
static const char *kName;
};
#endif // TURBINEANALYZER_H

View File

@@ -0,0 +1,92 @@
/*
Strawberry Music Player
Copyright 2024, Gustavo L Conte <suporte@gu.pro.br>
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 <QPainter>
#include <QResizeEvent>
#include "engine/enginebase.h"
#include "waverubberanalyzer.h"
const char *WaveRubberAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "WaveRubber");
WaveRubberAnalyzer::WaveRubberAnalyzer(QWidget *parent)
: AnalyzerBase(parent, 9) {}
void WaveRubberAnalyzer::resizeEvent(QResizeEvent *e) {
Q_UNUSED(e)
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::AlternateBase));
}
void WaveRubberAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
p.drawPixmap(0, 0, canvas_);
return;
}
// Clear the canvas
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Window));
QPainter canvas_painter(&canvas_);
// Set the pen color to the QT palette highlight color
canvas_painter.setPen(palette().color(QPalette::Highlight));
// Get pointer to amplitude data
const float *amplitude_data = s.data();
const int mid_y = height() / 4;
const int num_samples = static_cast<int>(s.size());
const float x_scale = static_cast<float>(width()) / static_cast<float>(num_samples);
float prev_y = static_cast<float>(mid_y);
// Draw the waveform
for (int i = 0; i < num_samples; ++i) {
// Normalize amplitude to 0-1 range
const float color_factor = amplitude_data[i] / 2.0F + 0.5F;
const int rgb_value = static_cast<int>(255 - color_factor * 255);
QColor highlight_color = palette().color(QPalette::Highlight);
// Blend blue and green with highlight color from QT palette based on amplitude
QColor blended_color = QColor(rgb_value, highlight_color.green(), highlight_color.blue());
canvas_painter.setPen(blended_color);
const int x = static_cast<int>(static_cast<float>(i) * x_scale);
const int y = static_cast<int>(static_cast<float>(mid_y) - (s[i] * static_cast<float>(mid_y)));
canvas_painter.drawLine(x, static_cast<int>(prev_y + static_cast<float>(mid_y)), static_cast<int>(static_cast<float>(x) + x_scale), static_cast<int>(static_cast<float>(y + mid_y))); // Draw
prev_y = static_cast<float>(y);
}
canvas_painter.end();
p.drawPixmap(0, 0, canvas_);
}
void WaveRubberAnalyzer::transform(Scope &scope) {
// No need transformation for waveform analyzer
Q_UNUSED(scope);
}
void WaveRubberAnalyzer::demo(QPainter &p) {
analyze(p, Scope(fht_->size(), 0), new_frame_);
}

View File

@@ -1,5 +1,6 @@
/* This file was part of Clementine.
Copyright 2014, David Sansome <me@davidsansome.com>
/*
Strawberry Music Player
Copyright 2024, Gustavo L Conte <suporte@gu.pro.br>
Strawberry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,11 +16,26 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GST_MOODBAR_PLUGIN_H
#define GST_MOODBAR_PLUGIN_H
extern "C" {
int gstfastspectrum_register_static();
}
#include <QPixmap>
#include <QPainter>
#endif // GST_MOODBAR_PLUGIN_H
#include "analyzerbase.h"
class WaveRubberAnalyzer : public AnalyzerBase {
Q_OBJECT
public:
Q_INVOKABLE explicit WaveRubberAnalyzer(QWidget *parent);
static const char *kName;
protected:
void resizeEvent(QResizeEvent *e) override;
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
void transform(Scope &scope) override;
void demo(QPainter &p) override;
private:
QPixmap canvas_;
};

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,7 +36,7 @@
#include <QUrl>
#include <QSqlDatabase>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "core/song.h"
#include "collectionfilteroptions.h"
#include "collectionquery.h"
@@ -45,7 +45,6 @@
class QThread;
class TaskManager;
class Database;
class SmartPlaylistSearch;
class CollectionBackendInterface : public QObject {
Q_OBJECT
@@ -80,12 +79,13 @@ class CollectionBackendInterface : public QObject {
using AlbumList = QList<Album>;
virtual QString songs_table() const = 0;
virtual QString fts_table() const = 0;
virtual Song::Source source() const = 0;
virtual SharedPtr<Database> db() const = 0;
virtual void GetAllSongsAsync(const int id = 0) = 0;
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
virtual void LoadDirectoriesAsync() = 0;
@@ -130,9 +130,10 @@ class CollectionBackendInterface : public QObject {
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
virtual Song GetSongByUrlAndTrack(const QUrl &url, const int track) = 0;
virtual void AddDirectory(const QString &path) = 0;
virtual void RemoveDirectory(const CollectionDirectory &dir) = 0;
virtual void AddDirectoryAsync(const QString &path) = 0;
virtual void RemoveDirectoryAsync(const CollectionDirectory &dir) = 0;
};
class CollectionBackend : public CollectionBackendInterface {
@@ -144,7 +145,8 @@ class CollectionBackend : public CollectionBackendInterface {
~CollectionBackend();
void Init(SharedPtr<Database> db, SharedPtr<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 Init(SharedPtr<Database> db, SharedPtr<TaskManager> task_manager, const Song::Source source, const QString &songs_table, const QString &dirs_table = QString(), const QString &subdirs_table = QString());
void Close();
void ExitAsync();
@@ -156,10 +158,11 @@ class CollectionBackend : public CollectionBackendInterface {
SharedPtr<Database> db() const override { return db_; }
QString songs_table() const override { return songs_table_; }
QString fts_table() const override { return fts_table_; }
QString dirs_table() const { return dirs_table_; }
QString subdirs_table() const { return subdirs_table_; }
void GetAllSongsAsync(const int id = 0) override;
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
void LoadDirectoriesAsync() override;
@@ -203,9 +206,10 @@ class CollectionBackend : public CollectionBackendInterface {
SongList GetSongsByUrl(const QUrl &url, const bool unavailable = false) override;
Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) override;
Song GetSongByUrlAndTrack(const QUrl &url, const int track) override;
void AddDirectory(const QString &path) override;
void RemoveDirectory(const CollectionDirectory &dir) override;
void AddDirectoryAsync(const QString &path) override;
void RemoveDirectoryAsync(const CollectionDirectory &dir) override;
bool ExecCollectionQuery(CollectionQuery *query, SongList &songs);
bool ExecCollectionQuery(CollectionQuery *query, SongMap &songs);
@@ -222,8 +226,7 @@ class CollectionBackend : public CollectionBackendInterface {
SongList GetSongsByFingerprint(const QString &fingerprint) override;
SongList SmartPlaylistsGetAllSongs();
SongList SmartPlaylistsFindSongs(const SmartPlaylistSearch &search);
SongList ExecuteQuery(const QString &sql);
void AddOrUpdateSongsAsync(const SongList &songs);
void UpdateSongsBySongIDAsync(const SongMap &new_songs);
@@ -231,12 +234,15 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateSongRatingAsync(const int id, const float rating, const bool save_tags = false);
void UpdateSongsRatingAsync(const QList<int> &ids, const float rating, const bool save_tags = false);
public slots:
public Q_SLOTS:
void Exit();
void GetAllSongs(const int id);
void LoadDirectories();
void UpdateTotalSongCount();
void UpdateTotalArtistCount();
void UpdateTotalAlbumCount();
void AddDirectory(const QString &path);
void RemoveDirectory(const CollectionDirectory &dir);
void AddOrUpdateSongs(const SongList &songs);
void UpdateSongsBySongID(const SongMap &new_songs);
void UpdateMTimesOnly(const SongList &songs);
@@ -248,7 +254,7 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateManualAlbumArt(const QString &effective_albumartist, const QString &album, const QUrl &art_manual);
void UnsetAlbumArt(const QString &effective_albumartist, const QString &album);
void ClearAlbumArt(const QString &effective_albumartist, const QString &album, const bool art_unset);
void ForceCompilation(const QString &album, const QList<QString> &artists, const bool on);
void ForceCompilation(const QString &album, const QStringList &artists, const bool on);
void IncrementPlayCount(const int id);
void IncrementSkipCount(const int id, const float progress);
void ResetPlayStatistics(const int id, const bool save_tags = false);
@@ -267,12 +273,14 @@ class CollectionBackend : public CollectionBackendInterface {
void UpdateLastSeen(const int directory_id, const int expire_unavailable_songs_days);
void ExpireSongs(const int directory_id, const int expire_unavailable_songs_days);
signals:
void DirectoryDiscovered(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdir);
Q_SIGNALS:
void DirectoryAdded(const CollectionDirectory &dir, const CollectionSubdirectoryList &subdir);
void DirectoryDeleted(const CollectionDirectory &dir);
void SongsDiscovered(const SongList &songs);
void GotSongs(const SongList &songs, const int id);
void SongsAdded(const SongList &songs);
void SongsDeleted(const SongList &songs);
void SongsChanged(const SongList &songs);
void SongsStatisticsChanged(const SongList &songs, const bool save_tags = false);
void DatabaseReset();
@@ -297,7 +305,7 @@ class CollectionBackend : public CollectionBackendInterface {
int has_not_compilation_detected;
};
bool UpdateCompilations(const QSqlDatabase &db, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
bool UpdateCompilations(const QSqlDatabase &db, SongList &changed_songs, const QUrl &url, const bool compilation_detected);
AlbumList GetAlbums(const QString &artist, const QString &album_artist, const bool compilation_required = false, const CollectionFilterOptions &opt = CollectionFilterOptions());
AlbumList GetAlbums(const QString &artist, const bool compilation_required, const CollectionFilterOptions &opt = CollectionFilterOptions());
CollectionSubdirectoryList SubdirsInDirectory(const int id, QSqlDatabase &db);
@@ -315,7 +323,6 @@ class CollectionBackend : public CollectionBackendInterface {
QString songs_table_;
QString dirs_table_;
QString subdirs_table_;
QString fts_table_;
QThread *original_thread_;
};

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,7 +29,7 @@
#include <QVariant>
#include <QString>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "core/filesystemmusicstorage.h"
#include "core/iconloader.h"
#include "core/musicstorage.h"
@@ -38,18 +39,22 @@
#include "collectiondirectorymodel.h"
using std::make_shared;
using namespace Qt::Literals::StringLiterals;
CollectionDirectoryModel::CollectionDirectoryModel(SharedPtr<CollectionBackend> backend, QObject *parent)
: QStandardItemModel(parent),
dir_icon_(IconLoader::Load("document-open-folder")),
dir_icon_(IconLoader::Load(u"document-open-folder"_s)),
backend_(backend) {
QObject::connect(&*backend_, &CollectionBackend::DirectoryDiscovered, this, &CollectionDirectoryModel::DirectoryDiscovered);
QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, this, &CollectionDirectoryModel::DirectoryDeleted);
QObject::connect(&*backend_, &CollectionBackend::DirectoryAdded, this, &CollectionDirectoryModel::AddDirectory);
QObject::connect(&*backend_, &CollectionBackend::DirectoryDeleted, this, &CollectionDirectoryModel::RemoveDirectory);
}
void CollectionDirectoryModel::DirectoryDiscovered(const CollectionDirectory &dir) {
void CollectionDirectoryModel::AddDirectory(const CollectionDirectory &dir) {
directories_.insert(dir.id, dir);
paths_.append(dir.path);
QStandardItem *item = new QStandardItem(dir.path);
item->setData(dir.id, kIdRole);
@@ -59,7 +64,10 @@ void CollectionDirectoryModel::DirectoryDiscovered(const CollectionDirectory &di
}
void CollectionDirectoryModel::DirectoryDeleted(const CollectionDirectory &dir) {
void CollectionDirectoryModel::RemoveDirectory(const CollectionDirectory &dir) {
directories_.remove(dir.id);
paths_.removeAll(dir.path);
for (int i = 0; i < rowCount(); ++i) {
if (item(i, 0)->data(kIdRole).toInt() == dir.id) {
@@ -71,26 +79,6 @@ void CollectionDirectoryModel::DirectoryDeleted(const CollectionDirectory &dir)
}
void CollectionDirectoryModel::AddDirectory(const QString &path) {
if (!backend_) return;
backend_->AddDirectory(path);
}
void CollectionDirectoryModel::RemoveDirectory(const QModelIndex &idx) {
if (!backend_ || !idx.isValid()) return;
CollectionDirectory dir;
dir.path = idx.data().toString();
dir.id = idx.data(kIdRole).toInt();
backend_->RemoveDirectory(dir);
}
QVariant CollectionDirectoryModel::data(const QModelIndex &idx, int role) const {
switch (role) {

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,15 +27,17 @@
#include <QObject>
#include <QStandardItemModel>
#include <QList>
#include <QMap>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QIcon>
#include "core/shared_ptr.h"
#include "includes/shared_ptr.h"
#include "collectiondirectory.h"
class QModelIndex;
struct CollectionDirectory;
class CollectionBackend;
class MusicStorage;
@@ -44,22 +47,24 @@ class CollectionDirectoryModel : public QStandardItemModel {
public:
explicit CollectionDirectoryModel(SharedPtr<CollectionBackend> collection_backend, QObject *parent = nullptr);
// To be called by GUIs
void AddDirectory(const QString &path);
void RemoveDirectory(const QModelIndex &idx);
QVariant data(const QModelIndex &idx, int role) const override;
private slots:
// To be called by the backend
void DirectoryDiscovered(const CollectionDirectory &directories);
void DirectoryDeleted(const CollectionDirectory &directories);
SharedPtr<CollectionBackend> backend() const { return backend_; }
QMap<int, CollectionDirectory> directories() const { return directories_; }
QStringList paths() const { return paths_; }
private Q_SLOTS:
void AddDirectory(const CollectionDirectory &directory);
void RemoveDirectory(const CollectionDirectory &directory);
private:
static const int kIdRole = Qt::UserRole + 1;
QIcon dir_icon_;
SharedPtr<CollectionBackend> backend_;
QMap<int, CollectionDirectory> directories_;
QStringList paths_;
QList<SharedPtr<MusicStorage>> storage_;
};

View File

@@ -0,0 +1,131 @@
/*
* Strawberry Music Player
* Copyright 2021-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <algorithm>
#include <functional>
#include <QSet>
#include <QList>
#include <QString>
#include <QUrl>
#include "core/song.h"
#include "core/songmimedata.h"
#include "filterparser/filterparser.h"
#include "filterparser/filtertree.h"
#include "collectionbackend.h"
#include "collectionfilter.h"
#include "collectionmodel.h"
#include "collectionitem.h"
CollectionFilter::CollectionFilter(QObject *parent) : QSortFilterProxyModel(parent), query_hash_(0) {
setSortLocaleAware(true);
setDynamicSortFilter(true);
setRecursiveFilteringEnabled(true);
}
bool CollectionFilter::filterAcceptsRow(const int source_row, const QModelIndex &source_parent) const {
CollectionModel *model = qobject_cast<CollectionModel*>(sourceModel());
if (!model) return false;
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
if (!idx.isValid()) return false;
CollectionItem *item = model->IndexToItem(idx);
if (!item) return false;
if (filter_string_.isEmpty()) return true;
if (item->type != CollectionItem::Type::Song) {
return item->type == CollectionItem::Type::LoadingIndicator;
}
size_t hash = qHash(filter_string_);
if (hash != query_hash_) {
FilterParser p(filter_string_);
filter_tree_.reset(p.parse());
query_hash_ = hash;
}
return item->metadata.is_valid() && filter_tree_->accept(item->metadata);
}
void CollectionFilter::SetFilterString(const QString &filter_string) {
filter_string_ = filter_string;
setFilterFixedString(filter_string);
}
QMimeData *CollectionFilter::mimeData(const QModelIndexList &indexes) const {
if (indexes.isEmpty()) return nullptr;
CollectionModel *collection_model = qobject_cast<CollectionModel*>(sourceModel());
SongMimeData *data = new SongMimeData;
data->backend = collection_model->backend();
QSet<int> song_ids;
QList<QUrl> urls;
for (const QModelIndex &idx : indexes) {
const QModelIndex source_index = mapToSource(idx);
CollectionItem *item = collection_model->IndexToItem(source_index);
GetChildSongs(item, song_ids, urls, data->songs);
}
data->setUrls(urls);
data->name_for_new_playlist_ = Song::GetNameForNewPlaylist(data->songs);
return data;
}
void CollectionFilter::GetChildSongs(CollectionItem *item, QSet<int> &song_ids, QList<QUrl> &urls, SongList &songs) const {
CollectionModel *collection_model = qobject_cast<CollectionModel*>(sourceModel());
switch (item->type) {
case CollectionItem::Type::Container:{
QList<CollectionItem*> children = item->children;
std::sort(children.begin(), children.end(), std::bind(&CollectionModel::CompareItems, collection_model, std::placeholders::_1, std::placeholders::_2));
for (CollectionItem *child : children) {
GetChildSongs(child, song_ids, urls, songs);
}
break;
}
case CollectionItem::Type::Song:{
const QModelIndex idx = collection_model->ItemToIndex(item);
if (filterAcceptsRow(idx.row(), idx.parent())) {
urls << item->metadata.url();
if (!song_ids.contains(item->metadata.id())) {
song_ids.insert(item->metadata.id());
songs << item->metadata;
}
}
break;
}
default:
break;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Strawberry Music Player
* Copyright 2021-2024, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef COLLECTIONFILTER_H
#define COLLECTIONFILTER_H
#include "config.h"
#include <QSortFilterProxyModel>
#include <QScopedPointer>
#include <QSet>
#include <QList>
#include <QUrl>
#include "core/song.h"
#include "filterparser/filtertree.h"
class CollectionItem;
class CollectionFilter : public QSortFilterProxyModel {
Q_OBJECT
public:
explicit CollectionFilter(QObject *parent = nullptr);
void SetFilterString(const QString &filter_string);
QString filter_string() const { return filter_string_; }
protected:
bool filterAcceptsRow(const int source_row, const QModelIndex &source_parent) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
private:
void GetChildSongs(CollectionItem *item, QSet<int> &song_ids, QList<QUrl> &urls, SongList &songs) const;
private:
mutable QScopedPointer<FilterTree> filter_tree_;
mutable size_t query_hash_;
QString filter_string_;
};
#endif // COLLECTIONFILTER_H

View File

@@ -29,7 +29,7 @@ CollectionFilterOptions::CollectionFilterOptions() : filter_mode_(FilterMode::Al
bool CollectionFilterOptions::Matches(const Song &song) const {
if (max_age_ != -1) {
const qint64 cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
const qint64 cutoff = QDateTime::currentSecsSinceEpoch() - max_age_;
if (song.ctime() <= cutoff) return false;
}

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