Compare commits

..

1056 Commits

Author SHA1 Message Date
Jonas Kvinge
eb6289dd1c Release 1.0.22 2023-12-09 23:21:35 +01:00
Jonas Kvinge
671df6eafb Update Changelog 2023-12-09 23:17:19 +01:00
Jonas Kvinge
579563b32c README: Fix emojis 2023-12-09 21:02:18 +01:00
Jonas Kvinge
135b93a5af StretchHeaderView: Set default section size
As of Qt 6.6.1, style changes are resetting the column sizes. To prevent this, we set a default section size.

Fixes #1328
2023-12-09 01:47:37 +01:00
BetterCallMolly
84c6e09c42 Player: Fix crossfade crash when decoding fails
When the decoding of a track fails, `current_item_` is set to an invalid address, if the Crossfade option is enabled, the `Player::TrackAboutToEnd` method does not check whether `current_item_` is a valid pointer or not, causing a segmentation fault.

Player: Removed extra space
2023-12-03 22:07:09 +01:00
Strawbs Bot
b4f9808d11 Update translations 2023-12-02 04:30:54 +01:00
Jonas Kvinge
d96d4224a2 Collection: Use normal pointer for watcher
Fixes #1316
2023-11-29 22:26:40 +01:00
Jonas Kvinge
f65927e308 metatypes: Register QAbstractSocket::SocketState 2023-11-29 22:25:03 +01:00
Jonas Kvinge
eeeea8566e nsi: Handle silent uninstall
Fixes #1323
2023-11-26 21:35:48 +01:00
Jonas Kvinge
54c42b276f GstEnginePipeline: Increase thread priority 2023-11-26 13:12:03 +01:00
Jonas Kvinge
8e4b4d6e41 nsi: Fix faad dll filename 2023-11-15 16:42:02 +01:00
Jonas Kvinge
ac9fd9070f GstEnginePipeline: Only set max size buffer if > 0
Fixes #1302
2023-11-12 22:04:31 +01:00
Jonas Kvinge
6348649bc6 GstEnginePipeline: Run QTimer::singleShot in main thread
Partial fix for #1302
2023-11-12 21:57:59 +01:00
Strawbs Bot
c95886d8db Update translations 2023-11-05 22:10:17 +01:00
Michał Walenciak
117b965a7b Translate number of tracks with nice plural forms 2023-11-05 19:37:56 +01:00
Michał Walenciak
1b6b5f9afa Use plurals 2023-11-05 19:37:56 +01:00
Michał Walenciak
83bc8d9e86 Select target language 2023-11-05 19:37:56 +01:00
Michał Walenciak
33f0421d3f Use 'n' for proper plurar form 2023-11-05 19:37:56 +01:00
Sergei B
661615e546 Allow drag and drop of songs to favorite playlists
- allows adding songs from active playlist
to any favorite by drag & drop
- after 500msec hovering with the songs over
desired playlist it becomes current
- drag & drop multiple songs is supported
2023-11-04 21:06:04 +01:00
Jonas Kvinge
c52fc90306 CI: Don't run SSH upload or macOS codesign on forks 2023-11-03 22:47:25 +01:00
Jonas Kvinge
eeb55fbc42 nsi: Update icu to 74 2023-11-01 23:07:47 +01:00
Jonas Kvinge
5d77eb1901 Change URL for KDSingleApplication submodule to https 2023-10-28 19:26:11 +02:00
Jonas Kvinge
654a94fe3d nsi: Add libabsl_kernel_timeout_internal.dll 2023-10-28 15:32:49 +02:00
Jonas Kvinge
4238708226 README: Update build instructions 2023-10-28 00:56:08 +02:00
Jonas Kvinge
5f02072bf3 CMake: Fix KDSingleApplication version check
Fixes #1300
2023-10-23 23:56:59 +02:00
Jonas Kvinge
eec5b448b6 CI: Add fftw3-devel for openSUSE 2023-10-23 19:45:50 +02:00
Jonas Kvinge
5cda756f92 CI: Remove get release version for macOS private 2023-10-23 19:37:40 +02:00
Jonas Kvinge
e9588dd85b CI: Remove SSH key setup from macOS private 2023-10-22 21:34:12 +02:00
Jonas Kvinge
dda0e4aa06 CI: Use MACOS_KEYCHAIN_PASSWORD 2023-10-22 21:13:28 +02:00
Jonas Kvinge
2282406166 CI: Build on self-hosted runner only on private repo 2023-10-22 18:51:23 +02:00
Jonas Kvinge
b3f0dee8e9 CI: Add "if: true" to easily disable steps 2023-10-22 17:20:28 +02:00
Jonas Kvinge
f9f7381247 CI: Fix APPLE_DEVELOPER_ID echo 2023-10-22 17:15:49 +02:00
Jonas Kvinge
48685325e6 Use KDSingleApplication as a submodule 2023-10-22 16:32:55 +02:00
Jonas Kvinge
e8bcaf415c CI: Only run SSH upload on this repo 2023-10-22 15:26:23 +02:00
Ondrej Mosnáček
c9197e8df7 CMake: Fix KDSingleApplication package name for Qt5
The name of the Qt5 KDSingleApplication CMake package is just
"KDSingleApplication", not "KDSingleApplication-qt".

Signed-off-by: Ondrej Mosnáček <omosnacek@gmail.com>
2023-10-22 14:24:52 +02:00
Jonas Kvinge
2bb09cf575 Song: Handle MP2 in Song::FiletypeByDescription 2023-10-21 05:07:25 +02:00
Jonas Kvinge
7bf4ad3884 Song: Handle MP2 in Song::FiletypeByExtension 2023-10-21 04:59:50 +02:00
Jonas Kvinge
5154d7ac84 Song: Rename MP3 to MPEG 2023-10-21 04:59:33 +02:00
Jonas Kvinge
9299653722 Turn on git revision 2023-10-21 04:57:53 +02:00
Jonas Kvinge
b0f0133d29 Release 1.0.21 2023-10-21 03:53:40 +02:00
Jonas Kvinge
9211b6f0c0 GstStartup: Remove macOS libsoup workarounds 2023-10-21 03:05:04 +02:00
Strawbs Bot
ab6a0ed6dd Update translations 2023-10-21 01:33:19 +02:00
Jonas Kvinge
c975c1e4aa CI: Remove unused homebrew and macports build 2023-10-20 00:53:49 +02:00
Jonas Kvinge
f9a593dc74 CI: Remove conflicting files for MSVC 2023-10-19 23:25:06 +02:00
Jonas Kvinge
9151520d50 CI: Remove uninstalling mingw 2023-10-18 23:19:50 +02:00
Jonas Kvinge
8f72d877bd CI: Manually sign libraries missing signature 2023-10-18 20:47:17 +02:00
Jonas Kvinge
e1990c9315 macgstcopy: Add rpath to gst plugin scanner 2023-10-18 20:46:24 +02:00
Jonas Kvinge
4cd5dcbfcf Update Changelog 2023-10-15 20:21:41 +02:00
Jonas Kvinge
3ee2125e8f CI: Verify code-signing 2023-10-15 16:51:13 +02:00
Jonas Kvinge
4652f3b449 CI: Manually codesign libsoup dependencies 2023-10-15 16:51:04 +02:00
Jonas Kvinge
697717eb1e CI: Use apple-actions/import-codesign-certs 2023-10-15 16:09:25 +02:00
Jonas Kvinge
cbde71f5f1 CI: Remove macOS lconvert and jenkins workaround 2023-10-15 07:48:20 +02:00
Jonas Kvinge
bf52afa21d GstStartup: Add back LIBSOUP3_LIBRARY_PATH 2023-10-15 07:46:49 +02:00
Jonas Kvinge
2083e008ab CI: Add macOS code-signing 2023-10-15 06:28:38 +02:00
Jonas Kvinge
2be0d23b1b macgstcopy: Change ID with install_name_tool 2023-10-14 23:24:56 +02:00
Jonas Kvinge
fda56dda25 main: Use Utilities::SetEnv 2023-10-14 23:18:16 +02:00
Jonas Kvinge
ed259781e9 CI: Remove rsync install and -DBUILD_WERROR 2023-10-14 23:17:58 +02:00
Jonas Kvinge
0c7fcd5a7a CMake: Fix USE_BUNDLE default 2023-10-14 22:09:14 +02:00
Jonas Kvinge
310b7b9065 CollectionQuery: Add F for float 2023-10-14 22:08:50 +02:00
Jonas Kvinge
cd534bbda7 CMake: Remove USE_BUNDLE_DIR 2023-10-14 03:30:09 +02:00
Jonas Kvinge
1a66eaf7bf GstStartup: Refactor environment code 2023-10-14 03:29:54 +02:00
Jonas Kvinge
a94d6e3dd8 CI: macgstcopy.sh copies libsoup now 2023-10-14 03:29:14 +02:00
Jonas Kvinge
54cfb2bbc4 main: Don't override library paths 2023-10-14 03:28:28 +02:00
Jonas Kvinge
00bc3f76cf macgstcopy: Copy libsoup 2023-10-14 03:27:57 +02:00
Jonas Kvinge
71a6d378d9 workerpool: Always search plugin path for tagreader on macOS 2023-10-14 03:27:32 +02:00
Jonas Kvinge
99a5aee8b3 GstEnginePipeline: Change debug logging for active/inactive 2023-10-13 23:38:19 +02:00
Jonas Kvinge
89d2a23dac CollectionBackend: Use QString::arg() 2023-10-13 23:06:29 +02:00
Jonas Kvinge
ee1bf47f5c DeviceInfo: Simplify hint 2023-10-13 22:58:53 +02:00
Jonas Kvinge
13ac20f8b3 Add/remove reference for parameters 2023-10-13 22:58:18 +02:00
Jonas Kvinge
adef05bbdf Use QString::arg() 2023-10-13 22:55:20 +02:00
Jonas Kvinge
f03ff452b8 SavePlaylistsDialog: Add parent to ctor 2023-10-13 22:53:27 +02:00
Jonas Kvinge
c39489060b Mpris2: Add static_cast 2023-10-13 22:52:36 +02:00
Jonas Kvinge
002fa8f4aa Fix mismatched definition 2023-10-13 22:49:20 +02:00
Jonas Kvinge
d2c747258c Song: Add MPC to FiletypeByMimetype and FiletypeByDescription 2023-10-12 01:16:40 +02:00
Jonas Kvinge
53e3664726 CI: Install hub in upload release step 2023-10-11 17:37:47 +02:00
Jonas Kvinge
26ff9f6b53 Update Changelog 2023-10-10 23:44:34 +02:00
Jonas Kvinge
f542f1c854 GstEnginePipeline: Remove volume sync for Auto
Workaround crash in #1123
2023-10-10 23:17:03 +02:00
Jonas Kvinge
33041ffa75 GstEnginePipeline: Delay seek when when resetting next URI
When seeking after the next URI is set, we set the state to READY to switch the URI back. The seek in after going to ready sometimes does not work, delay the seek to workaround this.

Fixes #1258
2023-10-10 23:00:11 +02:00
Jonas Kvinge
1493164df9 CollectionQuery: Strip off whitespaces after colon and simplify code
Fixes #1290
2023-10-10 19:15:20 +02:00
Strawbs Bot
8ffef558ff Update translations 2023-10-10 01:37:19 +02:00
Jonas Kvinge
8b3f44ffca Update Changelog 2023-10-10 01:32:33 +02:00
Jonas Kvinge
2706529006 DeviceDatabaseBackend: Add missing ebur128 fields 2023-10-10 01:27:39 +02:00
Jonas Kvinge
7e331a2055 DeviceManager: Fix creating connected device 2023-10-10 01:26:47 +02:00
Jonas Kvinge
505329730c Improve lyrics match 2023-10-08 23:55:05 +02:00
Jonas Kvinge
1a07404c10 Simplify RPM spec file 2023-10-08 16:15:59 +02:00
Jonas Kvinge
b02adc7758 Simplify RPM spec file 2023-10-08 13:54:36 +02:00
Jonas Kvinge
952252ebcd Simplify RPM spec file 2023-10-08 02:55:22 +02:00
Jonas Kvinge
eee0c40132 Playlist: Use InternetServicePtr 2023-10-07 17:05:51 +02:00
Jonas Kvinge
567bad33e1 Playlist: Use PlaylistItemPtr 2023-10-07 17:05:36 +02:00
Jonas Kvinge
b5c0e93989 FancyTabWidget: Use QApplication::style(), not style() 2023-10-07 15:36:49 +02:00
Jonas Kvinge
ac17df2a86 PlaylistContainer: Remove unused signals 2023-10-07 15:34:37 +02:00
Jonas Kvinge
a9a5899252 FancyTabWidgetProxyStyle: Create proxy style from application style 2023-10-07 15:23:41 +02:00
Jonas Kvinge
395d85c1b4 Move PlaylistProxyStyle to it's own file 2023-10-07 15:16:39 +02:00
Jonas Kvinge
52ba1ce17f PlaylistView: Fix build with Qt 5 2023-10-07 15:04:00 +02:00
Jonas Kvinge
604a246fe8 PlaylistProxyStyle: Use CE_HeaderLabel instead of CE_Header 2023-10-07 14:50:46 +02:00
Jonas Kvinge
e172c4871c PlaylistView: Create proxy style based on application style
Fixes #1275
2023-10-07 14:48:40 +02:00
Jonas Kvinge
2a9b32690d Update Changelog 2023-10-07 02:52:47 +02:00
Jonas Kvinge
76fa4745d0 GstEnginePipeline: Only update last known position when possible
Fixes flaky seeking where gst_element_query_position() returns -1 when seeking.
2023-10-07 02:47:12 +02:00
Strawbs Bot
6f4d26e9d3 Update translations 2023-10-04 00:33:42 +02:00
Jonas Kvinge
f40f43861d EngineBase: Use enum class for TrackChangeType 2023-10-03 20:18:52 +02:00
Jonas Kvinge
717ebbbb24 CI: Install openssh-clients for openSUSE 2023-10-02 21:32:46 +02:00
Jonas Kvinge
79c69e1b1e CollectionWatcher: Match extension case-insensitive 2023-10-02 17:39:10 +02:00
Jonas Kvinge
8fc95e08dc CollectionWatcher: Ignore compressed files
Fixes #1274
2023-10-02 17:23:47 +02:00
Jonas Kvinge
3f06528ba3 Fix version without git tags 2023-10-02 17:04:15 +02:00
Jonas Kvinge
0e44b10eec Add Ko-fi to funding options 2023-09-28 16:55:25 +02:00
Jonas Kvinge
d74fe92ce8 Use system KDSingleApplication when available 2023-09-27 20:19:43 +02:00
Jonas Kvinge
8037948f7f Dmg: Remove extra macdeployqt executable parameters
macdeployqt is patched and should handle .so libraries too now.
2023-09-26 23:46:11 +02:00
Jonas Kvinge
ab29170972 CI: Disable macOS homebrew build 2023-09-26 23:45:11 +02:00
Jonas Kvinge
6f843e2499 CI: Disable macports build
Issue #1278
2023-09-26 18:22:43 +02:00
Jonas Kvinge
6b8a816ce6 macgstcopy: Check for both .dylib and .so extensions for plugins
Require at least coreelements to be found in plugins directory
2023-09-26 17:04:20 +02:00
Jonas Kvinge
2c0541fb79 CI: Delete conflicting ICU case-insensitive 2023-09-26 00:34:13 +02:00
Jonas Kvinge
cb22890d79 CI: Simplify setting CMAKE_BUILD_TYPE and ENABLE_WIN32_CONSOLE 2023-09-26 00:34:13 +02:00
Jonas Kvinge
5a9346cc80 Add libebur128 to strawberry.spec 2023-09-25 20:50:43 +02:00
Jonas Kvinge
1c85220ffa Add libebur128-dev to debian control 2023-09-25 20:50:43 +02:00
Jonas Kvinge
39d4818def CI: Fix PPA upload 2023-09-25 20:16:26 +02:00
Jonas Kvinge
db0cc66ba0 CI: Fix Mageia rpm upload 2023-09-25 20:15:56 +02:00
Strawbs Bot
c10a64f08a Update translations 2023-09-25 02:37:58 +02:00
Jonas Kvinge
5a3d60b203 Turn on git revision 2023-09-25 00:10:41 +02:00
Jonas Kvinge
28b9bf1f76 Release 1.0.20 2023-09-25 00:03:45 +02:00
Jonas Kvinge
27dbb2dd9e Release 1.0.19 2023-09-24 23:19:15 +02:00
Jonas Kvinge
9b7b790f21 CI: Fix upload 2023-09-24 23:18:24 +02:00
Jonas Kvinge
e3666e5bf3 Remove OTHER_SOURCES 2023-09-24 19:53:49 +02:00
Jonas Kvinge
9e38cd8dc2 CI: Remove fixed path 2023-09-24 18:16:01 +02:00
Jonas Kvinge
b105b9fd8f CI: Use CREATEDMG_SKIP_JENKINS on macOS arm64 2023-09-24 17:45:18 +02:00
Jonas Kvinge
6a018f3e25 MainWindow: Add sponsorship mesage 2023-09-24 15:09:20 +02:00
Jonas Kvinge
ab26d422e9 MainWindow: Update rosetta message 2023-09-24 15:08:57 +02:00
Jonas Kvinge
f4e18fb87c MessageDialog: Set minimum width 2023-09-24 15:08:35 +02:00
Jonas Kvinge
2b20ff4e67 Update README.md 2023-09-24 13:19:05 +02:00
Jonas Kvinge
28d5cd481b Add macOS code-signing 2023-09-24 03:03:52 +02:00
Jonas Kvinge
42f3340190 Update Changelog 2023-09-23 23:56:36 +02:00
Jonas Kvinge
0ef50e1b6d Remove unused variables 2023-09-23 23:55:49 +02:00
Jonas Kvinge
01ddded603 CI: Use create-dmg with --skip-jenkins only for macOS arm64 2023-09-23 21:58:35 +02:00
Jonas Kvinge
fe7e0ffbba CI: Add macOS arm64 build and upload path 2023-09-23 03:08:51 +02:00
Jonas Kvinge
5df6066150 Update Changelog 2023-09-20 20:54:28 +02:00
Jonas Kvinge
8393cdb2de Add lyrics from elyrics.net and lyricsmode.com 2023-09-20 19:02:28 +02:00
Jonas Kvinge
da19272eb6 HtmlLyricsProvider: Rename GetUrl to Url 2023-09-20 17:39:44 +02:00
Jonas Kvinge
60fb83d770 HtmlLyricsProvider: Remove <script> tags and content between 2023-09-20 17:38:45 +02:00
Jonas Kvinge
1c90b03476 Add HTML lyrics provider 2023-09-20 01:09:08 +02:00
Jonas Kvinge
50502ce720 Add azlyrics.com lyrics provider 2023-09-19 22:47:07 +02:00
Jonas Kvinge
39f9d02454 Add songlyrics.com lyrics provider 2023-09-19 16:56:10 +02:00
Jonas Kvinge
ce627b0e18 Update Changelog 2023-09-19 00:30:56 +02:00
Jonas Kvinge
cdb4980337 CollectionBackend: Don't expire unavailable songs part of playlists 2023-09-17 21:54:27 +02:00
Jonas Kvinge
878148ac32 Use system macdeployqt 2023-09-17 01:23:53 +02:00
Jonas Kvinge
0bc29f0563 nsi: Disable wasapi2 2023-09-17 00:31:42 +02:00
Jonas Kvinge
e201f71a74 CMake: Simplify Qt detection 2023-09-16 14:55:42 +02:00
buckmelanoma
7e684885cf Capitalize column names
Capitalize column names for consistency with OS
2023-09-15 15:33:07 +02:00
dependabot[bot]
e600c1b50c Bump crazy-max/ghaction-import-gpg from 5 to 6
Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 5 to 6.
- [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases)
- [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v5...v6)

---
updated-dependencies:
- dependency-name: crazy-max/ghaction-import-gpg
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-14 21:13:53 +02:00
Jonas Kvinge
5da2faa400 CI: Replace actions-download-file with curl 2023-09-07 23:03:49 +02:00
Jonas Kvinge
05cd134a24 CI: Remove macOS homebrew upload 2023-09-07 21:19:34 +02:00
Jonas Kvinge
2f27a7d00b CI: Add new macOS build 2023-09-07 19:48:41 +02:00
Jonas Kvinge
d61d4d0a74 macdeploycheck: Use static QFile::exists 2023-09-06 22:59:16 +02:00
Jonas Kvinge
4033cd61c2 logging: Ignore -Wold-style-cast for glib.h 2023-09-05 23:42:56 +02:00
Jonas Kvinge
7cd6f372e6 MacOsDeviceLister: Move kind variable inside #ifdef HAVE_AUDIOCD 2023-09-05 23:42:32 +02:00
Jonas Kvinge
ffe6eb3de7 KDSingleApplicationLocalSocket: Ignore -Wgnu-zero-variadic-macro-arguments 2023-09-05 23:34:43 +02:00
Jonas Kvinge
e2b0b1b1fb CI: Use latest MSVC dependencies download URL 2023-09-05 18:29:59 +02:00
Jonas Kvinge
1f15ca70a3 CI: Remove install_name_tool step from macports 2023-09-05 18:15:54 +02:00
dependabot[bot]
904ee6a9b3 Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 20:24:34 +02:00
Jonas Kvinge
0461e98a4c nsi: Add libspeexd.dll for MSVC debug 2023-09-04 19:06:07 +02:00
Jonas Kvinge
dc01a18b87 Remove lyrics.com lyrics provider
Does not provider full lyrics in API. The URL we used only points to a page with "Get the lyrics for <song> at Lyrics.com" now.
2023-08-31 23:22:22 +02:00
Strawbs Bot
7be0e284c2 Update translations 2023-08-31 22:52:37 +02:00
Jonas Kvinge
6dd79d5b8f MainWindow: Bound volume between 0 and 100
Fixes #1262
2023-08-30 21:43:52 +02:00
Jonas Kvinge
f81b725d42 CI: Uninstall all homebrew packages 2023-08-30 19:37:10 +02:00
Jonas Kvinge
cfeecd98f6 Dmg: Remove DEPENDS deploy deploycheck 2023-08-30 16:22:33 +02:00
Jonas Kvinge
d054dd33e2 CI: Replace relative library @loader_path paths, macdeployqt does not understand these 2023-08-30 15:22:33 +02:00
Jonas Kvinge
45ad84a9bc Fix build with macOS < 12.0 2023-08-27 13:54:23 +02:00
Jonas Kvinge
a333662f56 CI: Add macOS brew tap step 2023-08-27 01:33:01 +02:00
Jonas Kvinge
59f716563f QSearchField: Use static_cast 2023-08-27 01:15:01 +02:00
Jonas Kvinge
6815f8c9b7 MacOsDeviceFinder: Rename kAudioObjectPropertyElementMaster to kAudioObjectPropertyElementMain 2023-08-27 01:14:42 +02:00
Jonas Kvinge
8e5360ac38 MacOsDeviceLister: Rename kIOMasterPortDefault to kIOMainPortDefault 2023-08-27 01:14:24 +02:00
Jonas Kvinge
aa6809ad5f DeviceManager: Use pointer directly 2023-08-27 01:14:00 +02:00
Jonas Kvinge
d8a7d427c3 BehaviourSettingsPage: Simplify Load 2023-08-27 01:12:38 +02:00
Jonas Kvinge
bc1b45d912 MainWindow: Fix build on macOS 2023-08-27 00:09:49 +02:00
Jonas Kvinge
50c5283599 BehaviourSettingsPage: Use QSystemTrayIcon::isSystemTrayAvailable directly 2023-08-26 21:21:31 +02:00
Jonas Kvinge
02ef65bcfd MainWindow: Only keep running when system tray icon is enabled 2023-08-26 21:20:50 +02:00
Jonas Kvinge
904245bb21 keymapper_x11: Qt::Key_mu renamed Qt::Key_micro from Qt 6.7 2023-08-26 21:15:26 +02:00
Jonas Kvinge
57e29b2be9 CI: Fix macOS rpaths 2023-08-13 12:19:14 +02:00
Jonas Kvinge
53a5603f64 CI: Fix dnf 2023-08-11 17:03:39 +02:00
Jonas Kvinge
8805a21567 CI: Remove Fedora workarounds 2023-08-11 00:17:45 +02:00
Jonas Kvinge
c6fee92450 CI: Fix macOS build 2023-08-10 01:40:51 +02:00
Jonas Kvinge
46c90f3712 CI: Check for missing Delete entries in NSI 2023-08-09 19:53:31 +02:00
Jonas Kvinge
e3c1c6ee9a nsi: Add missing double-quote 2023-08-09 19:52:01 +02:00
Jonas Kvinge
8bfbd69a2c nsi: Add libabsl_log_internal_conditions.dll 2023-08-09 19:41:06 +02:00
Jonas Kvinge
6a6649823e CI: List copied MinGW dependencies 2023-08-09 19:40:51 +02:00
Jonas Kvinge
bfb95d503a ErrorDialog: Clear messages on close instead of hide
Fixes an issue where the error dialog is cleared because the hide event gets triggered.
2023-08-07 19:38:48 +02:00
Jonas Kvinge
d1b4736ef9 ErrorDialog: Give name to layouts 2023-08-07 19:36:20 +02:00
Jonas Kvinge
e56e58b634 GstEnginePipeline: Always set use-buffering 2023-08-06 13:44:57 +02:00
Jonas Kvinge
fed5b6b695 GstEnginePipeline: Rename ebur128 volume variable 2023-08-06 13:36:25 +02:00
Jonas Kvinge
e96870cfbd nsi: Update to ffmpeg 6.0 for MSVC 2023-08-04 19:40:00 +02:00
Jonas Kvinge
acda7c13b2 GlobalShortcutsBackendMacOSPrivate: Replace boost::noncopyable with Q_DISABLE_COPY 2023-08-04 03:36:01 +02:00
Jonas Kvinge
7d5c7f8493 MacOsDeviceLister: Replace BOOST_SCOPE_EXIT with QScopeGuard 2023-08-04 03:36:01 +02:00
Jonas Kvinge
4ef3f3568d CI: Remove Ubuntu Kinetic 2023-08-03 20:14:54 +02:00
Jonas Kvinge
f81bd26649 MergedProxyModelPrivate: Formatting 2023-08-03 20:03:48 +02:00
Jonas Kvinge
2a407bfe47 ScopedTransaction: Replace boost::noncopyable with Q_DISABLE_COPY 2023-08-03 19:58:16 +02:00
Jonas Kvinge
f70f126f76 AlsaDeviceFinder: Replace BOOST_SCOPE_EXIT with QScopeGuard 2023-08-03 17:29:42 +02:00
Jonas Kvinge
f06591fde8 Database: Replace BOOST_SCOPE_EXIT with QScopeGuard 2023-08-03 17:29:11 +02:00
Strawbs Bot
e0c9a9dc17 Update translations 2023-08-03 01:03:54 +02:00
Jonas Kvinge
0f16fc2776 CI: Remove FreeBSD 2023-08-02 17:21:14 +02:00
Dakes
7aa7cdf6f3 Add filtering of numerical cols to collection
CollectionFilterWidget: Updated the tooltip, to reflect the changes.
CollectionQuery: Add parsing for SQL operators and insert right SQL
"where" searches.
Song: Add list of numerical columns
playlistfilterparser.cpp/FilterParser: move time and rating parsing
functions to new file:
searchparserutils.cpp: Contains common code used to parse search terms
in playlist and collection filters.
2023-08-02 16:52:27 +02:00
Dakes
82a8a890de PlaylistContainer: Add tooltip to search field 2023-08-02 16:52:27 +02:00
Dakes
f8df901963 PlaylistFilter: Add playcount and skipcount 2023-08-02 16:52:27 +02:00
Jonas Kvinge
8b08d1d599 Mpris2: Always use QGuiApplication::desktopFileName 2023-08-02 15:27:24 +02:00
Jonas Kvinge
f3ddba3edc main: Set desktop filename
Fixes #1254
2023-08-02 14:37:24 +02:00
Jonas Kvinge
acbec6db7e main: Set application display name 2023-08-02 14:25:40 +02:00
Jonas Kvinge
d2390473bc Update Changelog 2023-08-01 19:29:38 +02:00
Jonas Kvinge
e273d64be3 Player: Always stop after 100 errors
Fixes #1199
2023-08-01 16:59:43 +02:00
Jonas Kvinge
2a90256d32 GstEnginePipeline: Disable volume sync on Windows
Fixes #1220
2023-08-01 16:39:57 +02:00
Jonas Kvinge
560712db21 ebur128analysis: Check for valid channel-mask 2023-07-30 03:18:48 +02:00
Jonas Kvinge
483b42d2b8 GstStartup: Use directsoundsink as primary sink 2023-07-29 22:54:49 +02:00
Jonas Kvinge
d1a6e53f5c TagReaderTagLib: Read FMPS_Playcount
Fixes #1248
2023-07-29 18:33:50 +02:00
Dakes
f5a55abf58 Mpris2: Add new property to read/write the rating 2023-07-27 11:24:19 +02:00
Jonas Kvinge
0bc94b90d7 SmartPlaylistSearchTerm: Handle unrated (-1) as zero
Fixes #1244
2023-07-26 01:45:28 +02:00
Strawbs Bot
9ed4bd9366 Update translations 2023-07-24 09:46:20 +02:00
Jonas Kvinge
d3352e476f Remove < 0 check on unsigned 2023-07-21 07:17:58 +02:00
Jonas Kvinge
4b4c5fc0ab Use const reference for AlbumCoverLoaderOptions::Types 2023-07-21 07:17:26 +02:00
Jonas Kvinge
38b9c7c38a MusixmatchCoverProvider: Add const 2023-07-21 07:16:32 +02:00
Jonas Kvinge
c71ce41c83 LastFMImport: Move variable declaration 2023-07-21 07:16:23 +02:00
Jonas Kvinge
4cd030215d Transcoder: Remove useless empty check 2023-07-21 07:16:08 +02:00
Jonas Kvinge
2ce5d6f727 Database: Add missing const 2023-07-21 07:15:55 +02:00
Jonas Kvinge
b55a0df8e1 CollectionView: Remove useless variable 2023-07-21 07:15:42 +02:00
Jonas Kvinge
ee5fa23a7a LocalRedirectServer: Remove unused https variable 2023-07-21 07:15:08 +02:00
Jonas Kvinge
75ab6f25f4 Check return of QSqlQuery::prepare 2023-07-21 07:12:20 +02:00
Jonas Kvinge
eaed82c9b2 CollectionItemDelegate: Remove check for nullptr, already done 2023-07-21 07:11:21 +02:00
Jonas Kvinge
2a4be6fcd7 BoomAnalyzer: Move variable declaration 2023-07-21 07:10:31 +02:00
Jonas Kvinge
e6198500f7 BlockAnalyzer: Remove useless continue 2023-07-21 07:10:17 +02:00
Jonas Kvinge
7db36c83c1 MainWindow: Don't use our network manager for Qt Sparkle 2023-07-21 06:20:46 +02:00
Jonas Kvinge
0e1921698c TidalUrlHandler: service is already a pointer 2023-07-21 06:11:16 +02:00
Jonas Kvinge
95eed1ecec Fix QtConcurrent::run build with Qt 5 2023-07-21 06:10:44 +02:00
Jonas Kvinge
2e61235403 Application: Use shared pointers
Fixes #1239
2023-07-21 05:55:24 +02:00
Jonas Kvinge
d6b53f78ab Cleanup includes 2023-07-21 05:25:57 +02:00
Jonas Kvinge
a2c7ff63df Formatting 2023-07-21 05:11:27 +02:00
Roman Lebedev
9fb15545bd GstEnginePipeline: Perform EBU R 128 Loudness Normalization in floating-point 2023-07-19 03:07:22 +02:00
Jonas Kvinge
277e08b94a README: Add libebur128 to optional dependencies 2023-07-19 02:40:31 +02:00
Jonas Kvinge
46224fe9b8 nsi: Add gst-play-1.0.exe 2023-07-18 21:28:39 +02:00
Jonas Kvinge
56180ca419 LocalRedirectServer: Remove https option and gnutls dependency 2023-07-18 19:44:45 +02:00
Jonas Kvinge
dc65753a0b ebur128analysis: Remove extra semicolon 2023-07-16 23:26:17 +02:00
Jonas Kvinge
d8857d8e72 Add missing QMetaType include 2023-07-12 18:13:02 +02:00
Jonas Kvinge
fdc3e0a5f5 LyricsSearchResult: Add missing QList include 2023-07-12 18:12:48 +02:00
Jonas Kvinge
8f7180eb6c Song: Pass double by value 2023-07-12 18:12:08 +02:00
Jonas Kvinge
8945602eae Song: Add missing newlines between functions 2023-07-12 18:11:43 +02:00
Jonas Kvinge
7826f77425 Formatting 2023-07-12 16:27:59 +02:00
Jonas Kvinge
00372e85c5 FilterParser: Silence double / float warning 2023-07-12 16:27:28 +02:00
Jonas Kvinge
a1ffc5c33b ebur128analysis: Rename dsc variable 2023-07-12 16:26:39 +02:00
Jonas Kvinge
8a44a41abb ebur128analysis: Initialize variables to silence warnings 2023-07-12 16:26:17 +02:00
Jonas Kvinge
23f0c56eba Song: Move ebur128 functions 2023-07-12 16:23:27 +02:00
Jonas Kvinge
3d25863ccb CollectionWatcher: Make PerformEBUR128Analysis const 2023-07-12 16:22:17 +02:00
Jonas Kvinge
bb6daca735 GME: Add static_cast to silence warnings 2023-07-12 16:22:02 +02:00
Roman Lebedev
4bd993b1e3 GstEngine/GstEnginePipeline: support gap-less playback w/ loudness-normalizing gain
Ok, it does appear that it is that simple.

In principle this (even the non-update case) results in volume jumps,
so maybe we'll want gradual gain change...

Notably, i thought we'd always seek if the pipeline
was already operating on the same URL as the new one,
but apparently only for adjacent songs?
2023-07-12 14:34:04 +02:00
Roman Lebedev
f81816b0cd EBUR128Analysis: handle channel map
Loudness measurement is channel-dependent.
This perhaps matters most for mono audio.
2023-07-12 14:34:04 +02:00
Roman Lebedev
7ac605c038 EBU R 128: update ChangeLog/README 2023-07-12 14:34:04 +02:00
Roman Lebedev
2a8b67d11e Handle libebur to windows installers 2023-07-12 14:34:04 +02:00
Roman Lebedev
16893cca24 CI: install libebur128 package 2023-07-12 14:34:04 +02:00
Roman Lebedev
94ab788032 GstEnginePipeline: actually perform (EBU R 128) loudness normalization
The magic: if EBU R 128 loudness normalization is enabled,
just insert `volume` GST element into the pipeline
(where ReplayGain would be inserted) and configure it.

We currently don't support changing said gain after the pipeline
was created. We might need to, though, for a number of reasons.
2023-07-12 14:34:04 +02:00
Roman Lebedev
e3a333564a GstEngine::Load(): different loudness-normalizing gain means new pipeline
This is a bit of a gotcha, there should be a point (where we seek?)
where we'd be able to change said gain, but for now this is a simple[r]
stop-gap fix.
2023-07-12 14:34:04 +02:00
Roman Lebedev
13d6cf201f Engine: pipe-in the EBU R 128 loudness normalization gain stuff
The idea is that Integrated Loudness is an integral part
of the song, much like knowing it's beginning / ending
in the file, and we must handle it the exact same way,
and pipe it through all the way.

At the same time, `EngineBase` knows Target Level (from settings),
and these two combined tell us the Gain needed to normalize the
Loudness of the particular Song (`EngineBase::Load()` does that).
So the actual backend only needs to handle the Volume.

We don't currently support changing Target Level on the fly.
We don't currently support changing Loudness-normalizing Gain on the fly.

This does not handle the case when the song is loaded from URL
and thus the EBU R 128 measures, that exist, are not nessesairly correct.
2023-07-12 14:34:04 +02:00
Roman Lebedev
40ef3191fc EBUR128Analysis: place a queue before appsink
This improves the performance of the analysis (by 2x!),
by offloading non-`libebur128`-computations (i.e. decode-convert)
to a separate thread, thus reducing walltime.
2023-07-12 14:34:04 +02:00
Roman Lebedev
bda2b91c92 Collectionwatcher: sink PerformEBUR128Analysis() into ScanNewFile & friends 2023-07-12 14:34:04 +02:00
Roman Lebedev
1462bfa297 CollectionWatcher: support EBU R 128 analysis
Again, somewhat pretty similar to the existing fingerprint analysis,
we must support performing it both for the new files,
and re-performing it on (some of) already-existing songs,
because it might have been disabled before.

Admittedly, i quite don't like some of this code,
maybe this can be done in a more concise way.

NOTE: this only supports scanning each separate songs.
Should we ever want to support per-album loudness normalization,
this will need massive changes...
2023-07-12 14:34:04 +02:00
Roman Lebedev
bafcb97fa1 Implement EBUR128Analysis
The most juicy bit!

This is based on Song Fingerprint Analysis,
but here we must know the actual song, and not just the file.

The library supports only interleaved S16/S32/F32/F64,
so we must be sure we insert `audioconvert` into pipeline.

One point of contention here for me, is whether we should
feed the frames to the library the moment we get them
in `NewBufferCallback`, or collect them in a buffer
and pass them all at once. I've gone with the former,
because it seems like that is not the worst choice:
https://github.com/strawberrymusicplayer/strawberry/pull/1216#issuecomment-1610075876

In principle, the analysis *could* fail,
so we want to handle that gracefully.
2023-07-12 14:34:04 +02:00
Roman Lebedev
f905676b1c CollectionBackend/CollectionWatcher: add HasSongsWithMissingLoudnessCharacteristics logic
Exactly identical to the "missing fingerprint" logic,
just for the two new fields being added.
2023-07-12 14:34:04 +02:00
Roman Lebedev
0ea81b13b9 BackendSettingsPage: add "EBU R 128 loudness normalization"-related settings
Change `Use Replay Gain metadata if it is available` checkbox
into a radio button and add button to disable any loudness normalization.

Add second group(+radio button), for EBU R 128 loudness normalization.

There is only one tunable: Target Level,
defaulting to EBU R 128-recommended `-23 LUFS`.

Care should be taken when changing Target Level!
You probably don't want to go outside of `-30..-16` range!

At least as implemented, there is only support for per-song normalization,
i.e. no per-album normalization.

We do not do anything with loudness range,
although i have some further thoughts about compression.

We do not do anything about clipping / peak level.

NOTE: we do not need `libebur128` to *perform* the audio normalization,
      only for the initial analysis.


Co-authored-by: Jonas Kvinge <jonas@jkvinge.net>
2023-07-12 14:34:04 +02:00
Roman Lebedev
9a7949297e CollectionSettingsPage: add option to toggle libebur128-based song analysis
Much like song fingerprinting, performing EBU R 128 analysis is optional.

If you will want to enable EBU R 128 loudness normalization
(which does not depend on the presence of `libebur128`!)
you will probably want to enable it, but it is not enabled by default.

There will be support for rescanning songs for which it is missing.
2023-07-12 14:34:04 +02:00
Roman Lebedev
29342fa9ac CMake: when optional component EBUR128 is detected, link to libebur128 2023-07-12 14:34:04 +02:00
Roman Lebedev
bd4438d99b CMake: define new optional component EBUR128 (libebur128+gstreamer)
To perform the analysis using said library, we need to first somehow
get the PCM of the song, and it makes sense to use GStreamer for that.
2023-07-12 14:34:04 +02:00
Roman Lebedev
f8e14e8fd5 CMake: look for libebur128
We are going to use said library to calculate the two measures in question
from decoded audio.

I am adding this in default-on state, since this library
should be well-packaged everywhere, and this is something that,
i presume, we want.
2023-07-12 14:34:04 +02:00
Roman Lebedev
b2c66c9cda Playlist: add newly-added columns
Still mostly boilter-plate-y. It is somewhat interesting to see that info
in playlist view, so add the two fileds as columns.

At least for Integrated loudness, since it's normally negative,
we need to add a specialized Delegate.
2023-07-12 14:34:04 +02:00
Roman Lebedev
44e5c32bcb ContextView: show newly-added fields
And still very boilerplate-y, same as in previous change,
just show the two new Song fields in `Context` view.
2023-07-12 14:34:04 +02:00
Roman Lebedev
e7fc4e7f89 EditTagDialog: show newly-added fields (read-only)
Still very boilerplate-y. Add two placeholders to the UI
(in the middle of the existing table, so the diff is a mess),
and populate them with data.
2023-07-12 14:34:04 +02:00
Roman Lebedev
e589486907 Song: add pretty-printers for EBU R 128 Integrated Loudness and Loudness Range fields
They end up being used in a quite a number of places later on,
it makes sense to have them in a common place.

Integrated Loudness (LUFS) is *usually* negative, so we really want to
always print a sign. But Loudness Range is non-negative.

I think it makes sense to print one or at most two decimal places for these.
2023-07-12 14:34:04 +02:00
Roman Lebedev
459c4c5d86 Song: add EBU R 128 Integrated Loudness and Loudness Range fields, DB [de]serialization
Again, pretty boring boilerplate, rather identical to the handling of
other fields. We do need to be careful when [de]serializing it, though,
we don't want to accidentally loose the `NULL` (i.e. unknown) state!
2023-07-12 14:34:04 +02:00
Roman Lebedev
73c56f038e SqlQuery: add BindDoubleOrNullValue() method
To facilitate serializing of the two DB fields added by the previous change.
2023-07-12 14:34:04 +02:00
Roman Lebedev
0a4888f861 Database scheme: add EBU R 128 Integrated Loudness and Loudness Range columns
Nothing really ground-breaking, just add those two fields
to each table that already has `bitrate`/`samplerate`/`bitdepth` fields.

Those new fields do need to be able to represent an invalid state
which is their default state, thus they are non-`NOT NULL`.
In principle, the actual field type could be `INTEGER`
(i.e. fixed point w/ 2 fractional digits), but unless we really want to
save a few bytes, it doesn't seem worthwhile.

FIXME: i'm not sure if `device-schema` should be changed too.
2023-07-12 14:34:04 +02:00
Jonas Kvinge
da27ca98b3 CI: Only upload release when INCLUDE_GIT_REVISION is OFF 2023-07-12 14:25:59 +02:00
Jonas Kvinge
f1e1ccad28 CI: Fix stable PPA upload 2023-07-12 14:22:15 +02:00
Jonas Kvinge
7616c06ff9 Use find_package(Protobuf CONFIG) for macOS too 2023-07-12 01:25:41 +02:00
Jonas Kvinge
0c1f4750ea KDSingleApplication: Add LICENSES
Fixes #1219
2023-07-02 22:17:36 +02:00
Strawbs Bot
0a26c295a0 Update translations 2023-07-02 14:10:05 +02:00
Jonas Kvinge
32d23e0484 Turn on git revision 2023-07-02 04:00:38 +02:00
Jonas Kvinge
c028770f8e Release 1.0.18 2023-07-02 01:27:51 +02:00
Jonas Kvinge
8f1a99b37e CI: Remove Fedora 36 2023-07-02 01:02:10 +02:00
Jonas Kvinge
e66651a4cb Song: Remove unused application include 2023-07-02 00:29:30 +02:00
Jonas Kvinge
1d9a052870 AlbumCoverChoiceController: Move result declaration 2023-07-02 00:29:19 +02:00
Jonas Kvinge
d3ee749c14 CollectionBackendTest: Fix build 2023-07-01 22:34:51 +02:00
Jonas Kvinge
33076aa33a Update Changelog 2023-07-01 22:26:52 +02:00
Jonas Kvinge
2a2663eeb5 Update Changelog 2023-06-29 20:31:16 +02:00
Jonas Kvinge
fa04eb67db Convert old embedded and unset art in the new schema 2023-06-29 20:21:50 +02:00
Jonas Kvinge
f8ad8a7211 Disable clang-format 2023-06-29 19:06:27 +02:00
Jonas Kvinge
5f4d6dffef AlbumCoverLoader: Fix loading existing album covers from disk 2023-06-29 00:44:00 +02:00
Jonas Kvinge
b9c7510946 AlbumCoverLoader: Fix Handling default image in finish task 2023-06-28 23:52:39 +02:00
dependabot[bot]
c0e709a0e3 Bump vmactions/freebsd-vm from 0.3.0 to 0.3.1
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 0.3.0 to 0.3.1.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v0.3.0...v0.3.1)

---
updated-dependencies:
- dependency-name: vmactions/freebsd-vm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 20:16:20 +02:00
Jonas Kvinge
e690be1bdd DeviceDatabaseBackend: Fix device schema version mismatch
schema_version in device-schema.sql is 4.
2023-06-26 20:06:11 +02:00
Jonas Kvinge
6ed5190276 Remove AudD lyrics 2023-06-25 01:14:12 +02:00
Jonas Kvinge
4c4a351fbd CI: Add ubuntu mantic for ppa 2023-06-25 00:28:21 +02:00
Jonas Kvinge
435ffc75b6 CI: Add debian trixie 2023-06-24 03:40:23 +02:00
Jonas Kvinge
4dbc06bdd0 CI: Add ubuntu mantic 2023-06-24 03:38:32 +02:00
Jonas Kvinge
5a7cbb2f3d CI: Add OpenMandriva Cooker, remove Lx 4.2 2023-06-24 00:56:53 +02:00
Jonas Kvinge
354b55cbbc Use QFileInfo::path instead of QUrl::RemoveFilename 2023-06-15 21:06:18 +02:00
Jonas Kvinge
b87a950357 CoverUtils: Only create path if it doesn't exist 2023-06-15 21:04:11 +02:00
Jonas Kvinge
950c236720 AlbumCoverManager: Fix clear and unset actions 2023-06-15 20:35:41 +02:00
Jonas Kvinge
32982be4f2 EditTagDialog: Allow clearing unset cover 2023-06-15 20:34:03 +02:00
Jonas Kvinge
f467331934 Add ifdefs around #pragma GCC diagnostic 2023-06-15 20:10:25 +02:00
Jonas Kvinge
ec839e6aae KDSingleApplicationLocalSocket: Ignore missing declaration 2023-06-15 19:48:00 +02:00
Jonas Kvinge
45f1521da6 Update Changelog 2023-06-14 23:10:56 +02:00
Jonas Kvinge
841a44a18e KDSingleApplicationLocalSocket: Remove extra semicolon 2023-06-14 23:10:50 +02:00
Dakes
1deacaecf9 FilterParser: Add ability to filter by rating
Playlists can now be filtered by the rating from 0-5 like:
rating:0 rating:<3 rating:!=0
or by a float value, like:
rating:f0.1 rating:>=f0.5
2023-06-09 21:31:15 +02:00
Jonas Kvinge
6af8e6c25b Update Changelog 2023-06-09 00:16:46 +02:00
Jonas Kvinge
b0849d21f3 TidalStreamURLRequest: Ignore different track ID 2023-06-08 17:36:24 +02:00
Jonas Kvinge
5b60ea8e77 MainWindow: Remove F2 from edit tag shortcut
Fixes #1210
2023-06-07 19:38:59 +02:00
Andrei Stepanov
ec3f95a260 Add Russian translation to desktop file 2023-06-07 17:41:17 +02:00
Jonas Kvinge
0bfd4d2e04 CI: Only run SSH key setup step for master branch 2023-06-07 16:57:27 +02:00
Jonas Kvinge
a6ba0cfc97 KDSingleApplicationLocalSocket: Exclude XDG_SESSION_ID from socket name 2023-06-07 01:23:01 +02:00
Jonas Kvinge
6d55eb5974 CollectionModel: Fix icon disk cache 2023-06-07 00:51:53 +02:00
Jonas Kvinge
972053c699 MoodbarLoader: Fix loading cached moodbars 2023-06-07 00:50:35 +02:00
Jonas Kvinge
9db7896828 Prefix class for ReloadSettings 2023-06-06 23:20:42 +02:00
Jonas Kvinge
be6f93735d Simplify if statements 2023-06-06 23:19:45 +02:00
Jonas Kvinge
3bcea249ac TagReaderTagLib: Initialize cover_format 2023-06-06 23:19:25 +02:00
Jonas Kvinge
8ee32dfa88 Add static_cast to silence narrowing conversion warnings 2023-06-06 23:18:49 +02:00
Jonas Kvinge
e2f9411b46 EditTagDialog: Initialize cover_action 2023-06-06 23:17:29 +02:00
Jonas Kvinge
0bd68c3817 KDSingleApplicationLocalSocket: Add static_cast to silence narrowing conversion warnings 2023-06-06 22:25:19 +02:00
Jonas Kvinge
f03505ab67 KDSingleApplicationLocalSocket: Remove useless std::move 2023-06-06 22:24:10 +02:00
Jonas Kvinge
f1ae795c0f KDSingleApplicationLocalSocket: Initialize socket 2023-06-06 22:23:54 +02:00
Jonas Kvinge
50fcda2763 OSDDBus: Remove use of QImage::isGrayscale, Always expect RGB
Fixes #1205
2023-06-06 21:43:05 +02:00
Jonas Kvinge
331aa382f9 Rewrite album cover loader 2023-06-06 20:41:01 +02:00
Jonas Kvinge
3c160c2f13 CI: Fix /etc/dnf/dnf.conf for Fedora 39 2023-06-06 00:09:48 +02:00
Jonas Kvinge
4fd617a32f nsi: Add libgcc_s_sjlj-1.dll and libwinpthread-1.dll for MSVC x86 2023-06-05 00:41:34 +02:00
Jonas Kvinge
0d294eb218 CI: Add /opt/local/bin to PATH for macports build 2023-06-05 00:35:11 +02:00
Jonas Kvinge
a49e3b90b0 CI: Use macOS 12 for FreeBSD vm 2023-06-03 19:19:57 +02:00
Jonas Kvinge
201392f22e CI: Use latest macports release 2023-06-03 19:11:18 +02:00
Jonas Kvinge
3fa4ee772f CI: Bump MACOSX_DEPLOYMENT_TARGET 2023-06-03 19:10:57 +02:00
Jonas Kvinge
529c83c572 CI: Bump macOS version 2023-06-03 15:50:53 +02:00
Jonas Kvinge
6a9e56e9d9 nsi: Add gstwinrt-1.0-0.dll 2023-06-01 21:37:18 +02:00
Jonas Kvinge
716e80fb84 UWPDeviceFinder: Fix EngineDeviceList 2023-06-01 21:35:48 +02:00
Jonas Kvinge
80067b806d TranscodeDialog: Append number to filename if it already exists
Fixes #1200
2023-06-01 20:42:47 +02:00
Jonas Kvinge
315073f9a7 Add EngineDevice class 2023-06-01 19:31:19 +02:00
Jonas Kvinge
a1dbbba1a1 ContextView: Remove engine and device 2023-06-01 19:29:52 +02:00
Jonas Kvinge
cb8a0b6853 ContextSettingsPage: Remove engine and device 2023-06-01 19:29:52 +02:00
Jonas Kvinge
a5a29f7ad3 DeviceFinder: Add typedef DeviceList 2023-06-01 18:43:43 +02:00
Jonas Kvinge
8e14ef7c0c GstStartup: Set rank for directsoundsink higher than wasapisink for MinGW
Fixes #1204
2023-06-01 18:31:55 +02:00
Jonas Kvinge
c270391772 Add WASAPI2 plugin
Fixes #1204
2023-06-01 18:16:51 +02:00
Jonas Kvinge
e466cb6e30 Add UWP device finder 2023-06-01 18:11:30 +02:00
Jonas Kvinge
f0df9dc0fb GstEngine: Append "2" to wasapi2sink description 2023-06-01 17:22:11 +02:00
Strawbs Bot
b31c08083a Update translations 2023-05-30 01:02:07 +02:00
Jonas Kvinge
145a8d5b67 Bump LSMinimumSystemVersion in Info.plist
Fixes #1206
2023-05-29 11:25:43 +02:00
Jonas Kvinge
60d7a4e7ee AlbumCoverManager: Fix invalid reference 2023-05-29 11:24:41 +02:00
Jonas Kvinge
b52ffd09b2 Use find_package Protobuf CONFIG 2023-05-14 23:54:36 +02:00
Jonas Kvinge
58278ab1e4 Replace protobuf_generate_cpp with protobuf_generate 2023-05-14 20:07:36 +02:00
Strawbs Bot
000cf5fd5a Update translations 2023-05-13 01:01:35 +02:00
Jonas Kvinge
216b58e392 CI: Upload *.xz to source path 2023-05-07 12:39:35 +02:00
Jonas Kvinge
279cce49ba CI: Fix MSVC tarball filename 2023-05-07 11:27:56 +02:00
Jonas Kvinge
8184b77b13 CI: Improve MSVC build 2023-05-07 05:25:47 +02:00
Jonas Kvinge
b8b731ab04 CI: Upload Ubuntu PPA 2023-05-07 04:28:56 +02:00
Jonas Kvinge
1d4c39d13d CI: Upload release 2023-05-07 02:24:39 +02:00
Jonas Kvinge
ac26f5b2ef Add git to debian/control 2023-05-07 02:24:24 +02:00
Jonas Kvinge
c07447d8f5 CI: Upload artifacts 2023-05-07 01:09:18 +02:00
Jonas Kvinge
833ddf5f4c CI: Use -S and -B for cmake command 2023-05-06 14:18:35 +02:00
Jonas Kvinge
4dc4f468bb CI: Add Mageia 2023-05-06 14:08:34 +02:00
Strawbs Bot
1aff69d3cf Update translations 2023-05-06 01:02:19 +02:00
Robert Marshall
f2f63a703e class CueParser Process composer metadata in cue files
Use composer as an alternative to songwriter

..and move a misplaced comment
2023-05-05 17:47:37 +02:00
Jonas Kvinge
97e6b17f96 ContextView: QFontDatabase::families is not static in Qt 5 2023-05-05 17:46:02 +02:00
Jonas Kvinge
b90d284b08 ContextView: Check for default font family 2023-05-05 16:43:34 +02:00
Jonas Kvinge
840a65c630 ContextSettingsPage: Use constant for default font family 2023-05-05 16:43:01 +02:00
Jonas Kvinge
e5c89d4881 KDSingleApplicationLocalSocket: Use namespace and constexpr 2023-05-05 16:42:12 +02:00
Jonas Kvinge
0fc6638294 KDSingleApplicationLocalSocket: Formatting 2023-05-05 16:41:50 +02:00
Strawbs Bot
7c7724e41c Update translations 2023-05-05 01:29:26 +02:00
Jonas Kvinge
31319711dd Remove fixed font size on macOS
Possible fix for #1184
2023-05-05 00:34:48 +02:00
Jonas Kvinge
8954729d55 KDSingleApplicationPrivate: Remove unused initialize declaration 2023-05-04 19:42:27 +02:00
Jonas Kvinge
ae4dc442b9 Update Changelog 2023-05-04 19:22:00 +02:00
Jonas Kvinge
919ff414e6 Replace SingleApplication with KDSingleApplication 2023-05-04 09:44:54 +02:00
Strawbs Bot
b861703dad Update translations 2023-05-04 01:15:19 +02:00
Jonas Kvinge
2f17647cd3 Use const reference for AlbumCoverLoaderResult 2023-05-03 21:43:22 +02:00
Jonas Kvinge
f8d2c7eba3 Bump required Qt version to 5.12 2023-05-03 20:50:58 +02:00
Jonas Kvinge
e511b2faf9 Use new connect syntax for QMetaObject::invokeMethod 2023-05-03 20:08:51 +02:00
Jonas Kvinge
301e6b194a Replace 0 msec singleshot with invoke method 2023-05-03 19:55:58 +02:00
Jonas Kvinge
1208ca3ad4 ContextView: Simplify font code 2023-05-03 01:59:13 +02:00
Jonas Kvinge
84e7cd0df8 CollectionWatcher: Connect PathChanged signal once 2023-05-03 01:17:10 +02:00
Strawbs Bot
7e1077426e Update translations 2023-05-02 01:01:40 +02:00
Jonas Kvinge
1e2c437a08 CMakeLists: Remove hardcoded sqlite3 link with MSVC 2023-05-01 19:20:00 +02:00
Jonas Kvinge
a7ce1d1225 CI: Strip gdb.exe 2023-05-01 17:26:12 +02:00
Jonas Kvinge
1d3223e9c6 Add getopt supporting unicode
Fixes #1191
2023-05-01 16:44:18 +02:00
Jonas Kvinge
b01f3f4bb5 CI: Build on macOS with macports 2023-04-29 21:11:05 +02:00
Strawbs Bot
ee4cd11ae1 Update translations 2023-04-29 01:01:40 +02:00
Jonas Kvinge
154f8b307e CI: Add openSUSE Leap 15.5 2023-04-28 21:28:24 +02:00
Jonas Kvinge
7aac7872e3 CI: Disable D-Bus on macOS
Fixes #1163
2023-04-28 16:59:28 +02:00
Jonas Kvinge
b2630032e7 CI: Remove CodeQL 2023-04-27 21:39:25 +02:00
Jonas Kvinge
80136b602b CI: Check that all files are included in nsi 2023-04-26 19:04:59 +02:00
Strawbs Bot
3df29ed2a9 Update translations 2023-04-25 01:01:22 +02:00
Jonas Kvinge
69658f2022 AlbumCoverChoiceController: Check for nullptr in SearchForCover 2023-04-23 19:27:01 +02:00
Jonas Kvinge
5fd0a0831f GstEngine: Include all outputs that starts with "Sink/Audio" 2023-04-23 01:28:52 +02:00
Strawbs Bot
43b299ebd1 Update translations 2023-04-23 01:21:40 +02:00
Jonas Kvinge
9612304023 GstEngine: Use _stricmp with MSVC 2023-04-23 01:20:11 +02:00
Jonas Kvinge
3154e59b36 EngineBase: Remove extra semicolon 2023-04-22 20:41:22 +02:00
Jonas Kvinge
2d5a496678 Update debian/copyright 2023-04-22 19:48:51 +02:00
Jonas Kvinge
4c1c322b54 Rename AnalyzerBase 2023-04-22 19:45:21 +02:00
Jonas Kvinge
e9f3281694 Rename EngineBase 2023-04-22 19:13:42 +02:00
Jonas Kvinge
c3534affdb EngineBase: Remove PluginDetails 2023-04-22 17:18:29 +02:00
Jonas Kvinge
c6e62b3263 GstStartup: Remove setting rank for wasapisink and directsoundsink 2023-04-22 17:15:41 +02:00
Jonas Kvinge
d8ce177ce7 GstEngine: Remove class comment 2023-04-22 17:14:49 +02:00
Jonas Kvinge
726bfbefb0 Replace gst_element_factory_get_klass with gst_element_factory_get_metadata 2023-04-22 16:49:37 +02:00
Jonas Kvinge
5c4a6487c0 Update Changelog 2023-04-22 15:27:46 +02:00
Jonas Kvinge
3145433099 SystemTrayIcon: Simply icon loading 2023-04-22 15:27:34 +02:00
Jonas Kvinge
bdf6844b74 Database: Move error_reported 2023-04-22 14:38:45 +02:00
Jonas Kvinge
9ad430915c CddaDevice: Remove application.h include 2023-04-22 14:31:25 +02:00
Jonas Kvinge
60bbd71a22 Remove NetworkAccessManager from song loader 2023-04-22 14:05:48 +02:00
Jonas Kvinge
c96498758f Fix and improve gapless playback
If "about-to-finish" was emitted before the preload time was reached, we never set the next uri, so gapless playback was broken.
Make sure to always set the next uri, and increase preload gap from 5 to 8 seconds.
2023-04-22 03:54:11 +02:00
Strawbs Bot
f4600bd8eb Update translations 2023-04-22 01:21:46 +02:00
Jonas Kvinge
7202a5734c ListenBrainzScrobbler: Only send duration_ms when valid 2023-04-21 20:54:18 +02:00
Jonas Kvinge
8edb6eaa81 Song: Add missing const 2023-04-21 20:48:30 +02:00
Jonas Kvinge
b8eecc05fd AlbumCoverLoader: Use own NetworkAccessManager instance
Since AlbumCoverLoader runs in it's own thread.
2023-04-21 20:29:43 +02:00
Jonas Kvinge
7fc5aef553 Use one instance of NetworkAccessManager 2023-04-21 20:20:53 +02:00
Jonas Kvinge
bee6b7f946 Rename original_url to media_url 2023-04-21 16:20:00 +02:00
Jonas Kvinge
3bedfb6ac8 GstEngine: Formatting 2023-04-21 15:07:17 +02:00
Jonas Kvinge
f49bf0192b Engine::Base: Formatting 2023-04-21 15:06:44 +02:00
Jonas Kvinge
f36ac5272b Scrobbler: Simplify error handling 2023-04-21 02:11:26 +02:00
Strawbs Bot
f0fe446f7f Update translations 2023-04-21 01:01:44 +02:00
Jonas Kvinge
d9c4720a3e ListenBrainzScrobbler: Split artist mbids by slash 2023-04-20 21:55:11 +02:00
monochromec
4de5d8f2a3 Fixed typo and improved wording of Rosetta notice 2023-04-20 16:29:48 +02:00
Strawbs Bot
8651311016 Update translations 2023-04-19 01:08:38 +02:00
Jonas Kvinge
1d27c5a14c MainWindow: Add missing QSettings declaration 2023-04-18 20:17:01 +02:00
Jonas Kvinge
b0b8ff2d49 CollectionModel: URL percent encode disk cache keys
Fixes #1183
2023-04-18 18:42:37 +02:00
Jonas Kvinge
cd03e1fc74 Make it possible to not show Rosetta warning
Fixes #1180
2023-04-18 18:03:39 +02:00
Jonas Kvinge
b273a449e3 Add common message dialog 2023-04-18 17:44:42 +02:00
Jonas Kvinge
b637867f9e MainWindow: Hide checkbox, set size, center rosetta warning 2023-04-18 16:54:43 +02:00
Jonas Kvinge
41d5792b27 Add screen utilities for screen and center widget on screen 2023-04-18 16:54:35 +02:00
Jonas Kvinge
d22712a25b nsi: Update ICU to 73 2023-04-15 17:50:57 +02:00
Jonas Kvinge
33968ee5da CI: Fix macOS build 2023-04-15 17:50:31 +02:00
Strawbs Bot
9baf1774e0 Update translations 2023-04-15 01:01:23 +02:00
Strawbs Bot
9ca66e4061 Update translations 2023-04-12 01:01:23 +02:00
Jonas Kvinge
db2d34f840 FavoriteWidget: Use icon loader for star icons
Fixes #1178
2023-04-10 21:29:52 +02:00
Strawbs Bot
195cc61df7 Update translations 2023-04-10 01:22:37 +02:00
Jonas Kvinge
aaa530e72b Add const/references to all signal parameters 2023-04-09 20:23:42 +02:00
Jonas Kvinge
fa856ee905 Remove directory.h 2023-04-09 20:17:45 +02:00
Strawbs Bot
2bf7a5c4d3 Update translations 2023-04-08 01:27:44 +02:00
Jonas Kvinge
780b982635 SmartPlaylistSearchTermWidget: Fix loading search terms
Fixes #1172
2023-04-07 23:05:07 +02:00
Jonas Kvinge
74bbc1f19f PlaylistGenerator: Use std::make_shared 2023-04-07 22:41:10 +02:00
Jonas Kvinge
e314545f2e Database: Fix SQL query error message 2023-04-07 20:54:31 +02:00
Jonas Kvinge
f1a3a12c1c CollectionBackend: Fix SQL query error message 2023-04-07 20:54:15 +02:00
Jonas Kvinge
ef080c3cb1 PlaylistView: Fix resetting album cover 2023-04-07 20:07:40 +02:00
Jonas Kvinge
7313db5ac0 Add compile test for QX11Application
if Qt was compiled without XCB, globalshortcut-x11 failed to compile.
2023-04-07 15:57:39 +02:00
Jonas Kvinge
bf9aa524ed CI: Fix macOS build 2023-04-07 02:31:59 +02:00
Jonas Kvinge
b59aa0827e GstEnginePipeline: Always set initial volume 2023-04-07 02:30:41 +02:00
Strawbs Bot
2584f1293e Update translations 2023-04-07 01:13:27 +02:00
Jonas Kvinge
de9d28adea CI: Add Fedora 39 2023-04-06 23:37:23 +02:00
Jonas Kvinge
b660287779 Use std::shared_ptrfor AlbumCoverLoaderResult
Reduces memory fragmentation with Qt 6
2023-04-06 23:18:10 +02:00
Jonas Kvinge
962536bc83 AlbumCoverLoaderResult: Use enum class for type 2023-04-06 01:23:42 +02:00
Strawbs Bot
ee4fcf8100 Update translations 2023-04-04 01:01:21 +02:00
Strawbs Bot
9a4ed1a19d Update translations 2023-03-31 21:56:09 +02:00
Gaganpreet Arora
c1d0702b4d QobuzRequest: Handle multiple discs in integration
Albums with multiple discs are not being handled which results in tracks
being listed out of order. If the disc (media_number) is present in
JSON, we use it to set the disc in the Song object.
2023-03-31 21:21:58 +02:00
Jonas Kvinge
af1c46543b Turn on git revision 2023-03-30 17:50:03 +02:00
Jonas Kvinge
e2f5486987 Release 1.0.17 2023-03-29 18:54:51 +02:00
Jonas Kvinge
b0d390aaf1 Update Changelog 2023-03-29 18:49:35 +02:00
Jonas Kvinge
bae1b42394 OSDPretty: Respect device pixel ratio 2023-03-29 18:23:06 +02:00
Jonas Kvinge
a2533edd57 PlayingWidget: Make previous pixmap respect device pixel ratio 2023-03-29 00:24:38 +02:00
Jonas Kvinge
7b88c198fe SongSourceDelegate: Fix compile with Qt 5 2023-03-29 00:14:21 +02:00
Jonas Kvinge
c49cb0c119 ImageUtils: Formatting 2023-03-29 00:09:40 +02:00
Jonas Kvinge
03aabeb848 AlbumCoverManager: Respect device pixel ratio 2023-03-29 00:06:56 +02:00
Jonas Kvinge
1d3a837f7a SongSourceDelegate: Respect device pixel ratio 2023-03-29 00:04:34 +02:00
Jonas Kvinge
92adc18b8f ContextAlbum: Make size hint respect device pixel ratio 2023-03-28 18:21:19 +02:00
Fletcher Dostie
e4697c8ff1 Render context art at correct size 2023-03-28 17:54:56 +02:00
Jonas Kvinge
b741f1a580 Turn on git revision 2023-03-28 01:48:09 +02:00
Jonas Kvinge
8ee6de4162 Release 1.0.16 2023-03-27 22:28:02 +02:00
Jonas Kvinge
2b9e7db924 TagReaderTagLib: Touch file over saving cover art 2023-03-26 22:37:21 +02:00
Jonas Kvinge
49384ce294 TagReaderTagLib: Fix reading MusicBrainz from ID3v2 tags 2023-03-26 04:33:15 +02:00
Jonas Kvinge
ac59fff346 TagReaderTagLib: Formatting 2023-03-26 04:14:03 +02:00
Jonas Kvinge
a3e9f152d8 CollectionSettingsPage: Remove iopriority hide 2023-03-26 03:14:52 +02:00
Jonas Kvinge
ffe6a81b9a Collection: Always use Idle priority for watcher 2023-03-26 00:10:29 +01:00
Jonas Kvinge
9bb051b4eb CollectionSettingsPage: Remove settings for IO and thread priority and workers 2023-03-26 00:09:41 +01:00
Jonas Kvinge
c1465a890f TagReaderClient: Always use 1 tagreader worker 2023-03-26 00:08:55 +01:00
Jonas Kvinge
c3ee3318d7 WorkerPool: Default to 1 worker 2023-03-26 00:08:23 +01:00
Jonas Kvinge
272976f0b7 TagReaderClient: Fix default save options 2023-03-25 23:54:15 +01:00
Jonas Kvinge
4724b170b1 AuddLyricsProvider: Disable provider by default
Since we are currently missing API key.
2023-03-25 19:20:10 +01:00
Jonas Kvinge
337bd4bcef ListenBrainzScrobbler: Make sure we only add the same artist id once 2023-03-25 19:14:26 +01:00
Jonas Kvinge
0604c78453 CollectionBackend: Check for changed fingerprint 2023-03-25 18:35:14 +01:00
Jonas Kvinge
c8169adf7c CollectionModel: Guard against invalid disc and year 2023-03-25 18:32:41 +01:00
Jonas Kvinge
7b610d131c CollectionModel: Replace qMax with std::max 2023-03-25 18:31:21 +01:00
Jonas Kvinge
18da55453f EditTagDialog: Not set for track, disc, year, etc to -1 2023-03-25 18:29:12 +01:00
Jonas Kvinge
2aeab8b672 CollectionModel: Remove unused QByteArray include 2023-03-25 18:26:53 +01:00
Jonas Kvinge
9c21707a55 Update Changelog 2023-03-25 16:49:19 +01:00
Jonas Kvinge
b02ac833ad CollectionWatcher: Fix rescan songs feature 2023-03-25 16:38:03 +01:00
Jonas Kvinge
4de912cf41 Song: Fix missing colon in SQL bind value 2023-03-25 16:34:26 +01:00
Jonas Kvinge
ed260c6e20 CollectionWatcher: Check for changed AcoustID and MusicBrainz 2023-03-25 14:35:12 +01:00
Jonas Kvinge
fab38f693d Scrobbler: Refactor and add MusicBrainz integration 2023-03-25 14:25:21 +01:00
Jonas Kvinge
aedbd52e9d Add AcoustID 2023-03-24 22:48:22 +01:00
Jonas Kvinge
4cbcb9d99c Update list of lyrics providers in README.md and dist files 2023-03-24 21:23:10 +01:00
Jonas Kvinge
e967d15b4e Add AudD lyrics provider 2023-03-24 21:16:11 +01:00
Fletcher Dostie
bb43cc63ec Fix album art rendering on High DPI displays 2023-03-24 21:02:55 +01:00
Jonas Kvinge
ca176c319d Add Acoustid and MusicBrainz tags to database schema 2023-03-24 21:00:58 +01:00
Jonas Kvinge
17c960ecd4 Song: Add Acoustid fingerprint and MusicBrainz tags 2023-03-24 20:57:46 +01:00
Jonas Kvinge
b766ae3498 Song: Use static_cast from int to Source 2023-03-24 20:54:42 +01:00
Jonas Kvinge
faf4c817cd TagReaderTagLib: Read AcoustID fingerprint 2023-03-24 20:54:05 +01:00
Jonas Kvinge
b4e1f283c9 tagreadermessages: Add AcoustID fingerprint tags to protobuf 2023-03-24 19:52:45 +01:00
Jonas Kvinge
52c83d592c Rename Stands4 to Lyrics.com 2023-03-22 23:36:15 +01:00
Jonas Kvinge
c8caea0d30 GstEnginePipeline: Use constexpr 2023-03-22 23:29:56 +01:00
Jonas Kvinge
7a6c54d8e7 GstEnginePipeline: Remove hard-coded num-bands 2023-03-22 23:29:24 +01:00
Strawbs Bot
7082e52a4f Update translations 2023-03-21 01:01:49 +01:00
Strawbs Bot
6cdca617e0 Update translations 2023-03-20 01:14:38 +01:00
Jonas Kvinge
a1adc1a75a Add option for strict SSL mode in backend settings 2023-03-19 23:02:17 +01:00
Jonas Kvinge
b16bec704a GstEnginePipeline: Use "source-setup" instead of "notify::source" signal
This works with both playbin 2 and 3.

Possible fix for #1148
2023-03-19 22:28:12 +01:00
Jonas Kvinge
664a8c79a1 GstEnginePipeline: Check for ssl-strict property 2023-03-19 22:13:16 +01:00
Jonas Kvinge
bbf3d8e1a4 Update Changelog 2023-03-19 22:03:55 +01:00
Jonas Kvinge
a35146440c TagReaderTagLib: Read MusicBrainz tags 2023-03-19 21:55:26 +01:00
Jonas Kvinge
8e79fafca9 tagreadermessages: Add MusicBrainz tags to protobuf 2023-03-19 21:50:54 +01:00
Jonas Kvinge
6f8780d3cc TagReaderGME: Add header guard comment 2023-03-19 21:29:45 +01:00
Jonas Kvinge
264065a355 TagreaderGME: Don't call QByteArray::operator[]() on temporary 2023-03-19 20:42:30 +01:00
Jonas Kvinge
b606e4cd1a TagReaderTagLib: Rename Decode to TStringToStdString 2023-03-19 20:35:47 +01:00
Jonas Kvinge
01d0eeaed0 FileViewList: Add reserve 2023-03-19 20:32:06 +01:00
Jonas Kvinge
8c4e24e65d filemanagerutils: Add namespace comment 2023-03-19 20:31:46 +01:00
Jonas Kvinge
19c9f9698d QobuzService: Make DecodeAppSecret const 2023-03-19 20:31:31 +01:00
Jonas Kvinge
ae87c1b578 Stands4LyricsProvider: Remove unused variable 2023-03-19 20:24:07 +01:00
Jonas Kvinge
c42b1f5548 GstEnginePipeline: Cast guint to int 2023-03-19 19:38:36 +01:00
Jonas Kvinge
87ffbb0a85 Equalizer: Remove redundant QString cast 2023-03-19 19:38:12 +01:00
Jonas Kvinge
702851a958 TagReaderTagLib: Use constexpr 2023-03-19 19:34:05 +01:00
Jonas Kvinge
650f200a0b TagReaderBase: Remove unused Decode function 2023-03-19 19:33:34 +01:00
Jonas Kvinge
14d215bdf3 ContextView: Add static_cast for font size 2023-03-19 19:32:41 +01:00
Jonas Kvinge
12aebc2fe9 Sonogram: Remove unused destructor 2023-03-19 19:32:23 +01:00
Jonas Kvinge
f41b051ec7 LyricsSearchResult: Add missing const reference 2023-03-19 19:31:43 +01:00
Jonas Kvinge
25144eee89 Add CONTRIBUTING.md 2023-03-19 16:07:12 +01:00
Jonas Kvinge
0c62147536 SongLoader: Remove probe and bus watch 2023-03-19 05:08:35 +01:00
Jonas Kvinge
7b282e21de Fix compile with GStreamer disabled 2023-03-19 05:08:35 +01:00
Strawbs Bot
c1fbe6d84c Update translations 2023-03-19 01:20:59 +01:00
Jonas Kvinge
e20cbe4170 Save embedded cover in the same process as tags
Possible fix for #1158
2023-03-18 20:03:07 +01:00
Jonas Kvinge
394955a03f Replace QList<QByteArray> with QByteArrayList 2023-03-18 19:41:36 +01:00
Jonas Kvinge
16b4f5d065 DeviceDatabaseBackend: Remove use of QSqlDatabase::exec() 2023-03-18 02:20:03 +01:00
Jonas Kvinge
c95295d8b4 Console: Remove use of QSqlDatabase::exec() 2023-03-18 01:49:03 +01:00
Strawbs Bot
658dce2607 Update translations 2023-03-18 01:01:44 +01:00
Jonas Kvinge
01f8d0a27e SavedGroupingManager: Add missing collectionsettingspage include 2023-03-18 01:01:28 +01:00
Jonas Kvinge
39e1bfc84f CollectionBackend: Add default for ResetStatistics save_tags to false 2023-03-18 01:00:28 +01:00
Jonas Kvinge
e394416fa7 CollectionFilterWidget: Add missing collectionsettingspage include 2023-03-18 00:59:39 +01:00
Strawbs Bot
f1108bc0e2 Update translations 2023-03-15 01:01:28 +01:00
Jonas Kvinge
ff31815d49 SmartPlaylistSearchTerm: Revert if / else 2023-03-11 18:16:48 +01:00
Jonas Kvinge
604aa63b47 SmartPlaylists: Rename variable collection to collection_backend 2023-03-11 17:18:35 +01:00
Jonas Kvinge
ec4d036f50 SmartPlaylistsModel: Add missing endArray 2023-03-11 17:17:23 +01:00
Jonas Kvinge
1bf1c4ac63 nsi: Simplify macro code 2023-03-09 16:14:32 +01:00
Jonas Kvinge
981d46fbd4 Link abseil_dll for MSVC 2023-03-09 16:14:32 +01:00
Jonas Kvinge
eb6a353c31 nsi: Add libabsl 2023-03-09 16:14:32 +01:00
Strawbs Bot
ff673b1941 Update translations 2023-03-09 01:01:45 +01:00
Jonas Kvinge
8b55cf8a3a Check for policy 2023-03-08 19:01:18 +01:00
Jonas Kvinge
d8682b4403 Workaround absl linking errors on Windows with protobuf 3.22 2023-03-08 18:57:52 +01:00
Jonas Kvinge
40a4bf195a Link with Protobuf_LIBRARIES 2023-03-08 18:56:15 +01:00
Jonas Kvinge
312c5cbc3f macosutils: Formatting 2023-03-08 01:03:57 +01:00
Strawbs Bot
f314c56ef0 Update translations 2023-03-08 01:02:01 +01:00
Jonas Kvinge
ea8e5180ff Detect if running under Rosetta 2023-03-07 23:04:00 +01:00
Jonas Kvinge
7f76c3f2ce CI: Add Ubuntu Lunar 2023-03-07 20:53:49 +01:00
Jonas Kvinge
e4c5e99d0f MainWindow: Add macOS warning if running on different CPU 2023-03-07 20:14:37 +01:00
Jonas Kvinge
80cfca5de2 CI: Set HOMEBREW_NO_INSTALL_FROM_API for macOS 2023-03-07 01:15:38 +01:00
Strawbs Bot
9556a14de9 Update translations 2023-03-07 01:02:32 +01:00
Jonas Kvinge
2025fc8325 Update list of lyrics providers in README.md and dist files 2023-03-06 21:50:42 +01:00
Jonas Kvinge
2dd0f6a9ba Remove AudD lyrics provider 2023-03-06 21:46:00 +01:00
Jonas Kvinge
a42039d6e5 Lyrics: Match both album artist and artist in lyrics results 2023-03-06 21:42:59 +01:00
Jonas Kvinge
7fafa8adfb QtFSListener: Log watcher errors 2023-03-05 01:23:28 +01:00
Jonas Kvinge
f789657552 SingleApplication: Add boost include dirs 2023-03-05 01:04:57 +01:00
Jonas Kvinge
3e183bc10e SingleApplication: Change lambda capture order 2023-03-05 00:10:34 +01:00
Jonas Kvinge
16e5d93be3 SingleApplication: Add memory include 2023-03-05 00:08:45 +01:00
Jonas Kvinge
6515e06a13 SingleApplication: Refactor startup code 2023-03-04 23:47:36 +01:00
Jonas Kvinge
9ae0b32318 nsi: Update ffmpeg dlls for MinGW 2023-03-04 23:43:40 +01:00
Jonas Kvinge
4cb3bc4185 SingleApplication: Share code between SingleApplication and SingleCoreApplication 2023-03-04 20:48:21 +01:00
Jonas Kvinge
f9ca24598e Turn on git revision 2023-03-04 16:13:45 +01:00
Jonas Kvinge
3ccc892d6a Release 1.0.15 2023-03-04 15:46:51 +01:00
Jonas Kvinge
d7cacea843 Update Changelog 2023-03-04 15:45:33 +01:00
Strawbs Bot
e30233ac74 Update translations 2023-03-03 01:01:55 +01:00
Jonas Kvinge
7c57631fcf SingleApplication: Only include QNativeIpcKey with Qt 6.6 and higher 2023-03-02 23:03:33 +01:00
Jonas Kvinge
0adc084dad SingleApplication: Use new QSharedMemory constructor with Qt 6.6 and higher 2023-03-02 22:57:33 +01:00
Jonas Kvinge
e22199817c SingleApplicationPrivate: Move variable 2023-03-02 22:56:41 +01:00
Jonas Kvinge
78f691d006 SingleApplication: Remove semicolon 2023-03-02 22:56:17 +01:00
Jonas Kvinge
749bae1f16 SingleApplication: Use enum class for Mode 2023-03-02 22:55:50 +01:00
Jonas Kvinge
1043e24322 SingleApplication: Remove exit 2023-03-02 22:55:21 +01:00
Jonas Kvinge
a6d10b1fa7 GstEnginePipeline: Check that audio bin exists before unref 2023-02-27 18:50:09 +01:00
Jonas Kvinge
a3159423f8 nsi: Update libiconv for MSVC 2023-02-24 22:09:35 +01:00
Jonas Kvinge
ecb5ca321b CI: Use Windows 2022 and msvc toolset 14.3 2023-02-24 22:03:30 +01:00
Jonas Kvinge
fd827fdfd8 Add CodeQL 2023-02-22 18:53:21 +01:00
Jonas Kvinge
92d77b14d5 CI: Minor cleanup 2023-02-21 18:43:01 +01:00
dependabot[bot]
be67d89d8b Bump vmactions/freebsd-vm from 0.2.9 to 0.3.0
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 0.2.9 to 0.3.0.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v0.2.9...v0.3.0)

---
updated-dependencies:
- dependency-name: vmactions/freebsd-vm
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-21 01:59:45 +00:00
Jonas Kvinge
ddd1ce732a dependabot formatting 2023-02-20 20:32:04 +01:00
Jonas Kvinge
012d82183c Add dependabot 2023-02-20 20:28:41 +01:00
Jonas Kvinge
f9c98ebcb3 CI: Use build_type specific container image for mingw 2023-02-20 18:47:28 +01:00
Jonas Kvinge
10fe861dde nsi: Move protobuf to common files 2023-02-19 11:01:28 +01:00
Strawbs Bot
16c027ecab Update translations 2023-02-19 01:21:50 +01:00
Jonas Kvinge
cc578e7cc5 Sonogram: Remove unused parameter variable 2023-02-18 18:44:14 +01:00
Jonas Kvinge
6972d3c4f9 Sonogram: Change int to size_t 2023-02-18 17:52:01 +01:00
Jonas Kvinge
46b164f2fb nsi: Update protobuf dll 2023-02-18 17:30:38 +01:00
Jonas Kvinge
5431307527 CollectionFilterOptions: Match album artist too 2023-02-18 17:05:41 +01:00
Jonas Kvinge
79bf194ed6 CollectionModel: Fix compilation requirement query
Fixes #1140
2023-02-18 16:40:12 +01:00
Jonas Kvinge
506e670aa7 CI: Fix macOS build 2023-02-18 15:21:59 +01:00
Jonas Kvinge
fdfe164dd1 SmartPlaylistsModel: Save smart playlist type as int 2023-02-18 14:34:02 +01:00
Jonas Kvinge
af37056179 Change TrackChangeType to enum 2023-02-18 14:33:43 +01:00
Jonas Kvinge
b0fc7187cf PlaylistSettingsPage: Add Q_DECLARE_METATYPE for PathType 2023-02-18 14:33:19 +01:00
Jonas Kvinge
33ad1a7a86 Song: Add Q_DECLARE_METATYPE for Source and FileType 2023-02-18 14:33:01 +01:00
Jonas Kvinge
dd72fb4ca5 Use C++11 enum class 2023-02-18 14:09:30 +01:00
Jonas Kvinge
e6c5f76872 FancyTabWidget: Remove extra namespace 2023-02-18 14:09:30 +01:00
Jonas Kvinge
14aa22d590 Stands4LyricsProvider: Use direct URL if API usage limit exceeds 2023-02-18 14:09:30 +01:00
Strawbs Bot
5ed4293641 Update translations 2023-02-12 01:02:28 +01:00
Jonas Kvinge
f9fefcda57 Update Changelog 2023-02-11 01:49:10 +01:00
Jonas Kvinge
99b40293db Stands4LyricsProvider: Use percent encoding 2023-02-11 01:39:14 +01:00
Jonas Kvinge
9b06e85f94 Stands4LyricsProvider: Use API for search 2023-02-11 01:33:33 +01:00
Jonas Kvinge
93d1d40ea5 LyricsProvider: Default to empty results 2023-02-11 01:33:33 +01:00
Strawbs Bot
98597c047a Update translations 2023-02-11 01:19:51 +01:00
Jonas Kvinge
a5c1f4b0ee EditTagDialog: Fix saving play statistics
Fixes #1124
2023-02-10 22:51:48 +01:00
Jonas Kvinge
3d4c98d981 Playlist: Fix tag inline editing for steams
Fixes #1130
2023-02-10 22:49:57 +01:00
Jonas Kvinge
384e7dedb5 PlaylistView: Move ifdef 2023-02-10 22:47:55 +01:00
Jonas Kvinge
7df4453560 OrganizeFormat: Use suffix instead of complete suffix
Fixes #1136
2023-02-10 22:47:11 +01:00
Jonas Kvinge
d406a1c341 GstEnginePipeline: Use playbin3 with GStreamer 1.22.0 and higher 2023-02-10 22:43:55 +01:00
Jonas Kvinge
6671d97b4a GstEnginePipeline: Free audio bin in destructor
When the audio bin failed to initialize, we tried to disconnect signals and probes after the audio bin was already freed.
Instead, free the audio bin in the destructor after disconnecting signals and probes.

Fixes #1133 and #1123
2023-02-10 22:42:37 +01:00
Strawbs Bot
d02de72830 Update translations 2023-02-06 01:01:22 +01:00
Strawbs Bot
08f5172028 Update translations 2023-01-23 01:01:39 +01:00
4fury-c3440d8
04f062547d TagReaderTagParser: Fix timeconstants.h include 2023-01-22 13:22:02 +01:00
Jonas Kvinge
4717d783dc Stands4LyricsProvider: Finish search when no lyrics are found 2023-01-21 15:55:47 +01:00
Strawbs Bot
93af064b36 Update translations 2023-01-21 01:02:43 +01:00
Jonas Kvinge
c78d73d727 LastPlayedItemDelegate: Show blank for zero or invalid time 2023-01-20 23:45:33 +01:00
Jonas Kvinge
b69b3228be DateItemDelegate: Show blank for zero or invalid time 2023-01-20 23:45:19 +01:00
Jonas Kvinge
377f54700d TidalRequest: Make cover optional 2023-01-20 23:35:47 +01:00
Jonas Kvinge
d276339c80 Add lyrics from stands4 (lyrics.com) 2023-01-20 22:48:52 +01:00
Jonas Kvinge
b982a6a762 LyricsProvider: Improve parsing from HTML function 2023-01-20 22:45:05 +01:00
Jonas Kvinge
536fe637aa About: Remove e-mail addresses 2023-01-20 19:32:49 +01:00
Strawbs Bot
69f36eaa25 Update translations 2023-01-15 01:01:48 +01:00
Strawbs Bot
d6927a70bb Update translations 2023-01-14 01:01:51 +01:00
Jonas Kvinge
1b1892a187 Sonogram: Fix parameter mismatch in header 2023-01-13 23:46:38 +01:00
Sungrak Choi
5bea71cd5c Add Sonogram analyzer
Co-Authored-By: Jonas Kvinge <jonas@jkvinge.net>
2023-01-13 23:41:43 +01:00
Jonas Kvinge
bb8d4e70ae Turn on git revision 2023-01-13 22:53:39 +01:00
Jonas Kvinge
8d8c7e8b7b Release 1.0.14 2023-01-13 22:21:54 +01:00
Jonas Kvinge
9ff1f4d7b4 Update Changelog 2023-01-13 22:15:07 +01:00
Strawbs Bot
3a60dfe025 Update translations 2023-01-13 01:01:53 +01:00
Jonas Kvinge
d358854e16 Transcoder: Use static cast 2023-01-12 23:37:12 +01:00
Jonas Kvinge
129587e94a TranscoderOptionsMP3: Change default settings 2023-01-12 23:36:34 +01:00
Jonas Kvinge
0f575f4639 main: Remove explicitly enabling debug messages
Fixes #1106
2023-01-11 22:08:58 +01:00
Jonas Kvinge
7aac741571 GstEnginePipeline: Fix setting initial volume
Fixes #1104
2023-01-11 18:52:14 +01:00
Jonas Kvinge
b8a9da8a4e GstEngine: Use QUrl::isLocalFile 2023-01-10 18:26:42 +01:00
Jonas Kvinge
be6b974334 MoodbarPipeline: Fix saving moodbar if the URL contains host
Fixes #1101
2023-01-10 18:18:49 +01:00
knuxify
194e81205b CollectionBackendTest: Fix compile 2023-01-10 17:38:30 +01:00
Jonas Kvinge
d711dcc99d Turn on git revision 2023-01-10 00:46:49 +01:00
Jonas Kvinge
3b72a12540 Release 1.0.13 2023-01-09 22:46:59 +01:00
Strawbs Bot
38a1b7765a Update translations 2023-01-09 01:09:50 +01:00
Jonas Kvinge
02f2b8b6f0 CueParser: Detect encoding 2023-01-08 23:40:56 +01:00
Jonas Kvinge
7bfa75102c Remove QRegularExpressionMatch include 2023-01-08 23:24:25 +01:00
Jonas Kvinge
b0f3e7351c Update Changelog 2023-01-08 18:36:32 +01:00
Jonas Kvinge
b5fa401db9 Collection: Make sure RunQuery does not access collection items
- Rename `QueryOptions` to `CollectionFilterOptions`.
- Create new class `CollectionQueryOptions` for passing options from model to `CollectionQuery`.
- Rename `Directory` to `CollectionDirectory`.

Fixes #1095
2023-01-08 18:16:16 +01:00
Jonas Kvinge
41f2710dea GstEnginePipeline: Use linear volume 2023-01-08 15:25:24 +01:00
Jonas Kvinge
f19dda57f0 gstfastspectrum: Use static_cast 2023-01-08 06:21:34 +01:00
Jonas Kvinge
cc4a99ad80 CollectionModel: Close database with same mutex 2023-01-07 23:57:23 +01:00
Jonas Kvinge
1c1a3fc417 ResizableTextEdit: Don't delete setText 2023-01-07 23:38:59 +01:00
Jonas Kvinge
e97be5850b CollectionModel: Change while to for loop 2023-01-07 23:26:36 +01:00
Jonas Kvinge
2c302654b7 CollectionBackendInterface: Initialize filetype 2023-01-07 23:19:37 +01:00
Jonas Kvinge
3ee4dc77b2 Update Changelog 2023-01-06 23:14:28 +01:00
Jonas Kvinge
db55f314c9 Settings: Require tab icons to be minimum 32 2023-01-04 21:24:57 +01:00
Jonas Kvinge
0b536b287f MainWindow: Require tab icons to be minimum 32
Fixes #1084
2023-01-04 21:24:39 +01:00
Jonas Kvinge
6f298a9917 Use system icons for tabbar
Fixes #1093
2023-01-04 17:53:28 +01:00
Jonas Kvinge
70f829a2e5 PlaylistParser: Fix saving relative playlists to new files
Fixes #1092
2023-01-04 17:40:54 +01:00
Jonas Kvinge
1dfe07003f GstEnginePipeline: Set and notify volume only when changed
Another fix for #1089
2023-01-03 21:32:20 +01:00
Jonas Kvinge
286b908062 SliderSlider: Rename `value to new_value` 2023-01-03 19:53:53 +01:00
Jonas Kvinge
4ec028e736 GstEnginePipeline: Change callback ID's to long 2023-01-03 19:51:23 +01:00
Strawbs Bot
35a6d1437a Update translations 2023-01-03 01:01:50 +01:00
Jonas Kvinge
7e3cb3de89 OrganizeFormat: Parse the file path as a single block to avoid splitting brackets
Fixes #1091
2023-01-02 22:23:49 +01:00
Jonas Kvinge
bd945039f1 Turn on git revision 2023-01-02 22:10:13 +01:00
Jonas Kvinge
46743c8f15 Release 1.0.12 2023-01-02 18:56:51 +01:00
Jonas Kvinge
9b75284447 Update Changelog 2023-01-02 18:56:51 +01:00
Strawbs Bot
dca3b0052a Update translations 2023-01-02 01:02:18 +01:00
Jonas Kvinge
6d05bb2de5 GstEnginePipeline: Remove upstream events and buffer probes
Also rename some variables and callback functions

Possible fix for #1090
2023-01-02 00:06:18 +01:00
Jonas Kvinge
8b2e8d3804 OSDPretty: Change pensize back to 2
Removes workaround from commit 91eee99bab
2023-01-01 22:51:49 +01:00
Jonas Kvinge
1a2ab19ab4 GstEnginePipeline: Ignore core error when next song is set
Fixes #958
2023-01-01 20:25:57 +01:00
Jonas Kvinge
04f010aa7b Move SliderSlider and PrettySlider into own files 2023-01-01 18:45:21 +01:00
Jonas Kvinge
25323b4a3a VolumeSlider: Fix infinite loop when adjusting volume using mouse wheel
Fixes #1089
2023-01-01 17:46:26 +01:00
Jonas Kvinge
f353c022f6 LocalRedirectServer: Cast sizeof to int 2023-01-01 16:02:56 +01:00
Jonas Kvinge
9d8b3d3428 ContextView: Fix parameters for `IconLoader::Load()` 2023-01-01 16:02:17 +01:00
Strawbs Bot
e4b06772c0 Update translations 2023-01-01 01:08:45 +01:00
Jonas Kvinge
a2b5c3ea08 Remove settings for changing palette colors
Fixes #1087
2022-12-31 18:20:28 +01:00
Jonas Kvinge
f632e95600 Turn on git revision 2022-12-30 22:47:49 +01:00
Jonas Kvinge
4b5da8c6d4 Release 1.0.11 2022-12-30 18:52:59 +01:00
Jonas Kvinge
77d7ffd10a Update Changelog 2022-12-30 18:52:34 +01:00
Jonas Kvinge
d2f720011a macdeployqt: Disable libgstgme 2022-12-29 22:26:21 +01:00
Jonas Kvinge
6329a7711d Fix includes 2022-12-29 21:39:03 +01:00
Jonas Kvinge
fd14d044e3 CI: Bump macOS version to 11 2022-12-29 20:32:54 +01:00
Strawbs Bot
6368d507e1 Update translations 2022-12-29 01:06:59 +01:00
Jonas Kvinge
205b7f2401 Split utilities functions into separate files 2022-12-28 22:53:59 +01:00
Strawbs Bot
f7d10f9530 Update translations 2022-12-28 01:19:59 +01:00
Jonas Kvinge
66154bb51e Song: Add xesam:userRating in Song::ToXesam 2022-12-27 21:18:40 +01:00
Jonas Kvinge
fac4ad5313 Scrobbler: Allow album artists to be "Various Artists"
Fixes #1082
2022-12-27 21:15:20 +01:00
Jonas Kvinge
fee63891ac FilesystemMusicStorage: QFile::moveToTrash recursively moves files to trash 2022-12-27 20:59:09 +01:00
Strawbs Bot
efa979ee03 Update translations 2022-12-27 01:01:39 +01:00
Jonas Kvinge
c756346357 GstEnginePipeline: Add audio converter for sink 2022-12-11 01:14:17 +01:00
Strawbs Bot
cc95db25cc Update translations 2022-12-11 01:05:26 +01:00
Jonas Kvinge
44970c3321 GstEnginePipeline: Use converter for event probe 2022-12-11 00:21:16 +01:00
Jonas Kvinge
43c7934af7 Mpris2: Use art manual or automatic directly when available
Fixes #1079
2022-12-10 23:22:46 +01:00
Jonas Kvinge
3e7b51163c Don't use system icons for tabbar
Fixes #1084
2022-12-10 18:49:01 +01:00
Jonas Kvinge
4d5f8a53f0 CI: Remove unneeded "shell: bash" 2022-12-05 18:14:40 +01:00
Jonas Kvinge
0b73ca8318 CI: Add Remove Fedora 35 and add 38 2022-12-05 18:07:27 +01:00
Jonas Kvinge
754cfc3bfd Mpris2: Rename function parameter variable 2022-12-04 21:42:36 +01:00
Jonas Kvinge
2ed1a5c06a Add keywords to desktop file 2022-12-04 21:36:45 +01:00
Jonas Kvinge
b2073df3c3 GstEnginePipeline: Detect if autoaudiosink has volume
Fixes #1037
2022-12-04 08:37:33 +01:00
Jonas Kvinge
6267edaa81 Update Changelog 2022-12-04 03:18:04 +01:00
Jonas Kvinge
292f2de3e6 GstEnginePipeline: Add more info when ignoring error 2022-12-04 03:18:04 +01:00
Strawbs Bot
7803dc8be3 Update translations 2022-12-04 02:44:03 +01:00
Jonas Kvinge
2a1260b79e PlaylistParser: Resolve symbolic links when creating relative paths
Fixes #1071
2022-12-04 00:18:55 +01:00
Jonas Kvinge
f9ec438b7f Don't remove disc from album title when creating cover hash
Fixes #1069
2022-12-03 23:23:18 +01:00
Jonas Kvinge
de544c4856 CI: Fix Fedora 37 build 2022-12-03 13:06:21 +01:00
Jonas Kvinge
55e04dd520 Player: Fix volume adjustment 2022-12-03 05:29:39 +01:00
Jonas Kvinge
b2d06f900b GstEnginePipeline: Correct parameter name for SetFaderVolume function 2022-12-03 04:47:41 +01:00
Jonas Kvinge
b92ec71810 Use system volume when possible
Fixes #1037
2022-12-03 04:33:22 +01:00
Jonas Kvinge
3a4199240e Change constexpr to const 2022-11-27 03:27:40 +01:00
Jonas Kvinge
157de12bdf Improve Tidal and Qobuz request 2022-11-27 01:28:29 +01:00
Jonas Kvinge
7c0c9fccdb Organize: Don't allow organizing files without unique tags
Fixes #1077
2022-11-26 23:37:41 +01:00
Jonas Kvinge
2cb29d06b3 EditTagDialog: Disable fields for unsupported tags 2022-11-23 22:06:45 +01:00
Jonas Kvinge
1fdeb50d93 TagReaderTagLib: Don't use TPE1 as performer for ID3 tags
Fixes #1076
2022-11-23 00:03:23 +01:00
Jonas Kvinge
f59632ae59 TagReaderTest: Add tests for reading and writing compilation tag 2022-11-22 21:27:26 +01:00
Jonas Kvinge
67b503da44 TagReaderTagLib: Fix compilation tag read/write for MP4
Partial fix for #1076
2022-11-22 21:09:18 +01:00
Jonas Kvinge
a351de6904 TagReaderTagLib: Fix saving FMPS_Playcount
Fixes #1074
2022-11-22 19:34:11 +01:00
Jonas Kvinge
f94742abd5 EditTagDialog: Fix tab order
Fixes #1075
2022-11-22 19:06:40 +01:00
Jonas Kvinge
891182637e Update README.md 2022-11-21 23:40:30 +01:00
bhrgunatha
086681c915 Add spacefm support to core/utilities.cpp OpenInFileManager 2022-11-19 12:36:21 +01:00
Strawbs Bot
3970d2d02b Update translations 2022-11-17 01:01:49 +01:00
Jonas Kvinge
2b3fddd015 main: Set PULSE_PROP_media.role 2022-11-17 00:59:22 +01:00
Jonas Kvinge
30fa0480a3 main: Set PULSE_PROP_application.icon_name
Fixes #1066
2022-11-17 00:57:10 +01:00
Jonas Kvinge
320a81ce69 main: Use capitalized name in g_set_application_name()
Partial fix for #1066
2022-11-16 23:53:53 +01:00
Jonas Kvinge
950411ef56 ResizableTextEdit: Force updating geometry
Possible fix for
2022-11-16 21:10:30 +01:00
Strawbs Bot
cb9cf084c0 Update translations 2022-11-12 01:01:38 +01:00
Strawbs Bot
bb18af09ae Update translations 2022-11-11 01:12:18 +01:00
Jonas Kvinge
049bf0c7f9 TrackSlider: Remove extra parentheses 2022-11-10 19:19:33 +01:00
Jonas Kvinge
ebc73ef775 CI: Move macOS remove problematic files step 2022-11-10 01:03:50 +01:00
Strawbs Bot
1bd6f59355 Update translations 2022-11-10 01:01:50 +01:00
Jonas Kvinge
c16396a690 GstEnginePipeline: Log pipeline initialized 2022-11-09 22:42:43 +01:00
Strawbs Bot
32a9fe3e9c Update translations 2022-10-30 01:01:54 +02:00
Jonas Kvinge
f48d133bc6 TidalRequest: Fix returning empty results 2022-10-30 00:17:38 +02:00
Jonas Kvinge
0678526d7d CI: Build on FreeBSD with Qt 6 2022-10-29 22:16:15 +02:00
Jonas Kvinge
26d3e8371f CollectionWatcher: Only run periodic scan when monitoring is on 2022-10-29 19:12:04 +02:00
Jonas Kvinge
6768f614c7 GstEngine: Call SetState right before setFuture 2022-10-29 18:45:09 +02:00
Jonas Kvinge
171dc84df1 GstEngine: Use QUrl::clear() 2022-10-29 18:43:49 +02:00
Jonas Kvinge
5ca6513c04 TidalRequest: Only return error when no songs are received
Fixes #1061
2022-10-28 21:46:14 +02:00
Strawbs Bot
41aeb0ac80 Update translations 2022-10-27 01:01:24 +02:00
Strawbs Bot
f871c4adef Update translations 2022-10-25 01:01:27 +02:00
Jonas Kvinge
ad4d0e89b1 Turn off git revision 2022-10-22 10:14:20 +02:00
Jonas Kvinge
0d0c49caa1 Release 1.0.10 2022-10-21 23:39:59 +02:00
Jonas Kvinge
7cbc7bdcfb Update Changelog 2022-10-21 23:38:29 +02:00
Jonas Kvinge
6b46c8ed4a CI: Fix macOS build 2022-10-21 22:14:38 +02:00
Jonas Kvinge
e33ffd1c8a MusixmatchLyricsProvider: Fix error message 2022-10-21 20:54:16 +02:00
Strawbs Bot
8dce9cc938 Update translations 2022-10-21 01:02:47 +02:00
Jonas Kvinge
f06218f8e3 Qobuz: Add option for base64 app secret 2022-10-20 20:47:06 +02:00
Strawbs Bot
5711eef2be Update translations 2022-10-20 01:20:20 +02:00
Jonas Kvinge
db6aac603c nsi: Bump icu 2022-10-19 19:23:54 +02:00
Jonas Kvinge
f10e928106 Organize: Only load embedded cover if the destination is a device 2022-10-19 18:37:49 +02:00
Jonas Kvinge
99d963b99c MusicStorage: Add source 2022-10-19 18:36:33 +02:00
Jonas Kvinge
5d35c22238 OrganizeDialog: Use QUrl::isLocalFile 2022-10-19 18:34:30 +02:00
Jonas Kvinge
54fc11a235 CollectionBackend: Rename source accessor 2022-10-19 17:11:56 +02:00
Jonas Kvinge
ec99df3144 Create common class for Musixmatch 2022-10-18 22:49:15 +02:00
Jonas Kvinge
d722035883 VLCEngine: Only emit stopped when still playing
VLC sends the stopped signal too late, the player has already moved on based on the end reached signal. Emitting Stopped afterwards will interrupt the player in the middle of the song change.

Fixes #1054
2022-10-18 20:12:21 +02:00
Jonas Kvinge
52139fbaa0 Player: Reverse if 2022-10-18 20:09:38 +02:00
Jonas Kvinge
88854eb558 nsi: Bump libunistring 2022-10-17 21:41:14 +02:00
Jonas Kvinge
c82bba01ee MusixmatchLyricsProvider: Remove unused declaration 2022-10-17 20:34:51 +02:00
Jonas Kvinge
eaa33a03d7 MusixmatchLyricsProvider: Use API for lyrics search when possible 2022-10-17 20:29:44 +02:00
Jonas Kvinge
89e8518f31 GeniusLyricsProvider: Make private 2022-10-17 20:28:51 +02:00
Jonas Kvinge
c7bf2e1da8 LyricsProvider: Add missing newline 2022-10-16 22:52:06 +02:00
Jonas Kvinge
bf904a6afa LyricsProvider: Parse multiple sections of the same tag 2022-10-16 01:27:54 +02:00
Jonas Kvinge
43c14ae71b Replace pragma once with header guards for consistency
Only 2 headers have this.
2022-10-15 19:54:09 +02:00
Jonas Kvinge
4abb8ef3c9 Update Changelog 2022-10-15 14:39:49 +02:00
Jonas Kvinge
e46b92dd7d GeniusLyricsProvider: Use new function for parsing HTML 2022-10-15 14:34:09 +02:00
Jonas Kvinge
1a25faa5b9 LyricsProvider: Add function for parsing lyrics from HTML 2022-10-15 14:33:35 +02:00
Strawbs Bot
3f4bf5f512 Update translations 2022-10-14 01:18:29 +02:00
Jonas Kvinge
ae7dbf15ed nsi: Remove avresample-4.dll 2022-10-14 00:10:30 +02:00
Jonas Kvinge
b22320c48f Replace typedef with using 2022-10-13 22:39:31 +02:00
Jonas Kvinge
61204e8d35 CMakeLists: cmake_minimum_required before project 2022-10-13 21:41:46 +02:00
Jonas Kvinge
b0704c654c utilities_test: Fix QByteArray conversion 2022-10-13 21:37:11 +02:00
Strawbs Bot
f40f8a8873 Update translations 2022-10-13 01:01:31 +02:00
Jonas Kvinge
560a7db506 Update .clang-format 2022-10-12 21:28:14 +02:00
Strawbs Bot
15baa8f70e Update translations 2022-10-12 01:01:48 +02:00
Jonas Kvinge
97a7637294 FileView: Use QFileIconProvider instead of QAbstractFileIconProvider
Fixes compile with Qt 5
2022-10-12 00:36:16 +02:00
Jonas Kvinge
3454656207 PlaylistContainer: Fix search field crash on macOS
Fixes #1053
2022-10-12 00:11:12 +02:00
Iridias
7dff6f26bc FileView: Create new file icon provider if icons are missing 2022-10-11 23:52:00 +02:00
Jonas Kvinge
9e835a23fd Rename QSearchField 2022-10-11 23:34:22 +02:00
Jonas Kvinge
c0259fb6ce CI: Remove unused macOS brew packages 2022-10-11 00:27:26 +02:00
Jonas Kvinge
acf106220b CI: Upgrade packages for macOS 2022-10-10 22:31:35 +02:00
Strawbs Bot
5d4dc9c907 Update translations 2022-10-10 01:02:17 +02:00
Jonas Kvinge
2b9a56af7f Update Changelog 2022-10-09 22:18:20 +02:00
Jonas Kvinge
f1e3ac65ac TidalRequest: Simplify no results
Fixes #1047
2022-10-09 22:07:59 +02:00
Jonas Kvinge
143f72cf6b PlaylistView: Fix scaling mid bar for currently playing track
Fixes #1051
2022-10-09 21:41:16 +02:00
Jonas Kvinge
5c8e49296c CI: Add Fedora 37 2022-10-05 00:08:09 +02:00
Jonas Kvinge
b2558b703c nsi: Add jpeg62.dll for MSVC 2022-10-02 22:33:36 +02:00
Jonas Kvinge
bf9d87106e CI: Build Jammy with Qt 6 2022-10-02 19:38:22 +02:00
Jonas Kvinge
5b7f507d9b Windows7ThumbBar: Use QString length instead of count 2022-10-01 14:03:28 +02:00
Jonas Kvinge
59a7835ace CI: Fix if lines 2022-10-01 01:40:32 +02:00
Jonas Kvinge
2495838fe3 CI: Use matrix 2022-09-30 23:58:25 +02:00
Jonas Kvinge
bab687bedf Add USE_RPATH option 2022-09-30 01:00:29 +02:00
Jonas Kvinge
6467c3c8ee Update CMakeLists.txt 2022-09-30 01:00:15 +02:00
Jonas Kvinge
66c2b7aaa6 Update Version.cmake 2022-09-30 00:59:33 +02:00
Jonas Kvinge
ab72c52661 CI: Remove custom libsoup step for macOS 2022-09-29 22:33:47 +02:00
Jonas Kvinge
f0bf1b8a54 Rename CI 2022-09-29 22:32:04 +02:00
Strawbs Bot
119251719f Update translations 2022-09-24 01:01:45 +02:00
Jonas Kvinge
0348400132 ContextView: Remove use of fixed font
Fixes #1040
2022-09-23 18:10:01 +02:00
Jonas Kvinge
813473805b CI: Get latest MSVC dependencies 2022-09-17 01:45:42 +02:00
Strawbs Bot
fe6368561b Update translations 2022-09-17 01:01:19 +02:00
Strawbs Bot
7ff06f424d Update translations 2022-09-15 01:01:21 +02:00
Jonas Kvinge
31958592c7 main: Always create initial style settings 2022-09-13 22:44:24 +02:00
Jonas Kvinge
adc21f4f75 Remove subdir for generated dbus files 2022-09-13 17:53:57 +02:00
Strawbs Bot
9cecd89d6f Update translations 2022-09-13 01:26:32 +02:00
Jonas Kvinge
d866b9b4d4 Update definitions i CMakeLists 2022-09-12 23:20:07 +02:00
Jonas Kvinge
93f12baf51 Fix narrowing conversions in connects 2022-09-12 23:18:54 +02:00
Jonas Kvinge
78d6fd634b Fix casts from QByteArray 2022-09-12 22:39:08 +02:00
Strawbs Bot
b5fc19f08a Update translations 2022-09-12 01:01:20 +02:00
Jonas Kvinge
2dae6a6546 nsi: Update flac dll version 2022-09-10 23:52:27 +02:00
Jonas Kvinge
b6bba46391 CI: Update vmactions/freebsd-vm 2022-09-09 23:02:28 +02:00
Jonas Kvinge
b791d97116 CI: Use variable for workspace 2022-09-09 22:40:37 +02:00
Strawbs Bot
6aa8255f34 Update translations 2022-09-08 01:01:22 +02:00
Jonas Kvinge
98e2140761 Turn on git revision 2022-09-04 00:48:35 +02:00
Jonas Kvinge
a59e064778 Release 1.0.9 2022-09-03 20:40:14 +02:00
Jonas Kvinge
655c4c66a7 Update Changelog 2022-09-03 20:34:25 +02:00
Jonas Kvinge
407c128f65 GstStartup: Set LIBSOUP3_LIBRARY_PATH for macOS 2022-09-03 00:19:25 +02:00
Jonas Kvinge
59a261a5be main: Add Frameworks to library paths for macOS 2022-09-03 00:19:25 +02:00
Jonas Kvinge
ba4e1afefe CI: Compile entire gstreamer from source for macOS 2022-09-02 23:55:25 +02:00
Jonas Kvinge
98b53b81d8 Update Changelog 2022-09-01 22:06:00 +02:00
Jonas Kvinge
8270cc0aa2 macdeploycheck: Fail on parse error 2022-09-01 21:58:11 +02:00
Jonas Kvinge
74207b1a87 Update ISSUE_TEMPLATE.md 2022-09-01 21:35:33 +02:00
Jonas Kvinge
77983445ce debian: Fix icu dependency 2022-09-01 19:18:40 +02:00
Ondrej Mosnáček
162190bcb8 Mark collectionmodel_test as not requiring GUI
The test doesn't seem to acually need gui_required = true, so set it to
false.
2022-08-31 12:40:46 -07:00
Jonas Kvinge
86b92b20b7 macdeployqt: Add back extra changeInstallName 2022-08-30 21:35:11 +02:00
Jonas Kvinge
2f5b60d548 macdeployqt: Fix deploying libgcc
Fixes #1025
2022-08-30 19:33:59 +02:00
Jonas Kvinge
c19661c977 macdeploycheck: Fix check for libgcc 2022-08-30 19:32:59 +02:00
Jonas Kvinge
82d101ca27 macdeployqt: Merge changes from upstream 2022-08-30 17:48:16 +02:00
Jonas Kvinge
32f9c4e670 GstEnginePipeline: Parse album from stream title tag
Fixes #1023
2022-08-30 16:56:08 +02:00
Jonas Kvinge
34c7225ab7 Turn back git revision 2022-08-29 22:05:07 +02:00
Jonas Kvinge
b02eb502e4 Release 1.0.8 2022-08-29 17:54:05 +02:00
Jonas Kvinge
7d6b3dfd20 Update maketarball.sh.in 2022-08-29 17:53:49 +02:00
Strawbs Bot
3ac39968d2 Update translations 2022-08-29 01:16:24 +02:00
Jonas Kvinge
2b24ac54a0 Remove unused includes 2022-08-28 03:09:33 +02:00
Jonas Kvinge
6da6b794c7 VolumeSlider: Remove unused Background and Foreground 2022-08-28 02:52:40 +02:00
Jonas Kvinge
ed689e27c9 Transcoder: Change return type for QueuedJobsCount to qint64 2022-08-28 02:51:10 +02:00
Jonas Kvinge
186f9c3f18 SmartPlaylistWizard: Add missing override 2022-08-28 02:50:33 +02:00
Jonas Kvinge
42cdde3203 SmartPlaylistsViewContainer: Add missing override 2022-08-28 02:50:17 +02:00
Jonas Kvinge
b9091702e9 SmartPlaylistSearchTermWidget: Add missing override 2022-08-28 02:50:02 +02:00
Jonas Kvinge
d42ecbe74e SmartPlaylistQueryWizardPlugin: Add missing override 2022-08-28 02:49:45 +02:00
Jonas Kvinge
e6c5446d63 PlaylistQueryGenerator: Add missing override 2022-08-28 02:49:30 +02:00
Jonas Kvinge
cc277211b3 LibreFMScrobbler: Call base class for ReloadSettings 2022-08-28 02:48:47 +02:00
Jonas Kvinge
6703c15c52 RadioPlaylistItem: Fix header guard comment 2022-08-28 02:48:22 +02:00
Jonas Kvinge
4ecdaf573e MoodbarRenderer: Remove unused kNumHues 2022-08-28 02:47:45 +02:00
Jonas Kvinge
71ec3e61be signalchecker: Remove useless return 2022-08-28 02:46:23 +02:00
Jonas Kvinge
216fdb2393 FileSystemWatcherInterface: Change signal to non const 2022-08-28 02:45:56 +02:00
Jonas Kvinge
c39acc6e3c Database: Remove useless return 2022-08-28 02:45:34 +02:00
Jonas Kvinge
d97b0478a7 Fix typos 2022-08-28 02:44:37 +02:00
Jonas Kvinge
d15d64eb67 nsi: Add nghttp2.dll for MSVC 2022-08-27 23:43:39 +02:00
Jonas Kvinge
3acd2656ee Update Changelog 2022-08-27 23:14:56 +02:00
Jonas Kvinge
a4b003534a CI: Switch to libsoup 3 2022-08-27 19:36:29 +02:00
Jonas Kvinge
8752538a02 nsi: Switch to libsoup 3 2022-08-27 19:36:29 +02:00
Strawbs Bot
bde435753a Update translations 2022-08-25 01:02:52 +02:00
Jonas Kvinge
d347e6fc5f Add setting for turning off HTTP/2 2022-08-24 20:34:10 +02:00
Jonas Kvinge
27b01d3642 Analyzer::Base: Set Qt::WA_OpaquePaintEvent 2022-08-24 20:32:08 +02:00
Jonas Kvinge
b78677e285 nsi: Use libsoup 2 2022-08-22 23:19:49 +02:00
Jonas Kvinge
f8f6c74bcb CI: Use libsoup 2 2022-08-22 23:19:40 +02:00
Strawbs Bot
7ce123939b Update translations 2022-08-21 01:19:35 +02:00
Jonas Kvinge
e7b02cfb15 TagReaderGME: Use UTF-16 2022-08-21 00:36:06 +02:00
Jonas Kvinge
5931077b08 macdeployqt: Add GStreamer GME plugin 2022-08-20 19:52:32 +02:00
Jonas Kvinge
a521bdc0e3 TagReaderGME: Compare file extension case-insensitive 2022-08-20 19:08:58 +02:00
Eoin O'Neill
80da565609 Initial support for GME's VGM/SPC playback and tag management.
Co-Authored-By: Jonas Kvinge <jonas@jkvinge.net>
2022-08-20 18:33:13 +02:00
Jonas Kvinge
6bc46e4598 CollectionModel: Make separate_albums_by_grouping optional 2022-08-20 16:47:09 +02:00
Jonas Kvinge
6562258db5 CollectionModel: Make separating albums by grouping optional
Fixes #1018
2022-08-20 14:51:19 +02:00
Jonas Kvinge
c219995218 Update .gitignore 2022-08-19 21:15:08 +02:00
Jonas Kvinge
62035431ed Install Visual C++ runtime 2022-08-18 22:47:34 +02:00
Jonas Kvinge
fa1fbca7dc DirectSoundDeviceFinder: Remove __attribute__((stdcall)) 2022-08-15 19:55:55 +02:00
Strawbs Bot
391b7476b3 Update translations 2022-08-15 01:01:37 +02:00
Jonas Kvinge
9c04ce665f DirectSoundDeviceFinder: Add CALLBACK to EnumerateCallback 2022-08-14 10:37:31 +02:00
Jonas Kvinge
dd87268197 CI: Build Windows x86 2022-08-14 10:37:31 +02:00
Jonas Kvinge
8d9af59db2 MainWindow: Use different Sparkle URL for x86 2022-08-14 10:37:31 +02:00
Strawbs Bot
1b754a35ff Update translations 2022-08-14 01:01:17 +02:00
Jonas Kvinge
7230f91f61 CI: Update actions 2022-08-10 21:32:18 +02:00
Strawbs Bot
93edfc315c Update translations 2022-08-10 01:01:34 +02:00
Strawbs Bot
74c8269531 Update translations 2022-08-09 18:02:04 +02:00
Jonas Kvinge
acb6c0fc83 Use PlaylistFilter directly 2022-08-09 17:23:46 +02:00
Jonas Kvinge
553d4cce93 PlaylistContainer: Translate Undo/Redo
Fixes #1017
2022-08-09 17:04:59 +02:00
Jonas Kvinge
ca81f144e6 README: Add ICU to dependencies 2022-08-08 00:37:37 +02:00
Jonas Kvinge
ec84960347 nsi: Move icu to common files 2022-08-08 00:37:19 +02:00
Jonas Kvinge
38db0764af Require ICU 2022-08-07 20:23:23 +02:00
Jonas Kvinge
a647f63bb0 CI: Add macOS 2022-08-07 17:35:53 +02:00
Jonas Kvinge
5b7087cc9e nsi: Add files for MSVC 2022-08-07 12:32:31 +02:00
Jonas Kvinge
1a6fcd5da6 QSearchField: Replace use of C-style cast 2022-08-07 05:14:38 +02:00
Jonas Kvinge
e31dd9f553 MacSystemTrayIcon: Replace use of C-style cast 2022-08-07 05:14:05 +02:00
Jonas Kvinge
22844716d1 RadioView: Remove duplicate double clicked
Already done in AutoExpandingTreeView::ItemDoubleClicked

Fixes #1015
2022-08-07 02:40:09 +02:00
Jonas Kvinge
6abed76f70 nsi: Add gme 2022-08-06 17:02:00 +02:00
Jonas Kvinge
72ff502e0f OSDDBus: Set timeout 2022-07-31 03:28:13 +02:00
Jonas Kvinge
2b781df32b CI: Use openSUSE Leap 15.4 for source builder 2022-07-29 16:27:21 +02:00
Strawbs Bot
bf601c3906 Update translations 2022-07-29 01:25:43 +02:00
Jonas Kvinge
b753e0ebb0 CI: Remove macOS 2022-07-28 16:56:35 +02:00
Jonas Kvinge
564211aceb Prefer ICU to transliterate characters when available
Fixes #1008
2022-07-28 16:31:16 +02:00
Strawbs Bot
538c759fef Update translations 2022-07-28 01:02:19 +02:00
Jonas Kvinge
95edc34100 Update README and issue template 2022-07-27 20:43:03 +02:00
Jonas Kvinge
98682a2da9 Use C++17 fallthrough 2022-07-26 20:37:06 +02:00
Jonas Kvinge
33581fa61d PlaylistContainer: Fix filter text when switching playlist
Fixes #1005
2022-07-26 07:35:43 +02:00
Jonas Kvinge
b02e0aae98 PlaylistListSortFilterModel: Add header guard 2022-07-26 07:33:11 +02:00
Jonas Kvinge
6fa4e1cb6d Turn on git revision 2022-07-25 23:37:33 +02:00
Jonas Kvinge
9f2ec22e95 Release 1.0.7 2022-07-25 21:45:27 +02:00
Jonas Kvinge
65b2e506b1 CI: Remove Ubuntu Impish 2022-07-25 17:29:35 +02:00
Jonas Kvinge
8bc07b5286 nsi: Add libgpg-error-0.dll 2022-07-25 15:51:05 +02:00
Jonas Kvinge
fe0af63795 Update Changelog 2022-07-25 15:28:25 +02:00
Jonas Kvinge
44ce7f4c67 CI: Disable audio cd for Windows MinGW build 2022-07-25 15:27:17 +02:00
Jonas Kvinge
252936134b nsi: Remove gstwinrt-1.0-0.dll 2022-07-25 04:25:08 +02:00
Jonas Kvinge
e255b06945 macdeployqt: Update gstreamer plugins 2022-07-25 03:56:38 +02:00
Jonas Kvinge
d726568a23 nsi: Add gstwavenc 2022-07-25 03:55:38 +02:00
Jonas Kvinge
66880a6de7 nsi: Update gstreamer plugins 2022-07-25 03:23:34 +02:00
Jonas Kvinge
fda622a0c0 Update Changelog 2022-07-24 23:44:54 +02:00
Jonas Kvinge
efa7d10f40 nsi: Add twolame 2022-07-21 03:04:48 +02:00
Strawbs Bot
348a3fabab Update translations 2022-07-21 01:09:44 +02:00
Jonas Kvinge
1bbaee605c PlaylistParser: Check file extension case-insensitive 2022-07-20 14:56:55 +02:00
Jonas Kvinge
22edf7a2b3 TagReaderTagParser: Fix reading and writing rating 2022-07-20 11:44:46 +02:00
Jonas Kvinge
3ffcc29249 Add back save all playlists action 2022-07-20 01:09:00 +02:00
Jonas Kvinge
21f1fe52c0 Remove save all playlists action
It's hard-coded to m3u, needs a new select directory dialog with option to select different playlist formats.

Fixes #987
2022-07-18 22:56:32 +02:00
Jonas Kvinge
99840c9e4f Turn on git revision 2022-07-18 00:40:00 +02:00
Jonas Kvinge
3194fe7d8e Release 1.0.6 2022-07-17 23:36:45 +02:00
Jonas Kvinge
81df72b0cb Update debian/copyright 2022-07-17 21:47:35 +02:00
Jonas Kvinge
57b056ac43 WorkerPool: Search for tagreader in libexec 2022-07-17 17:21:11 +02:00
Jonas Kvinge
c5876d0d48 Update Changelog 2022-07-17 01:53:39 +02:00
Jonas Kvinge
b5e65401b6 Update Changelog 2022-07-17 01:52:21 +02:00
Jonas Kvinge
ebea5b48ea Update Changelog 2022-07-17 01:50:58 +02:00
Jonas Kvinge
7d081e581a CI: Remove extra Qt 6 repo 2022-07-17 01:16:06 +02:00
Jonas Kvinge
5e819cf04b Remove defining _WIN32_WINNT 2022-07-17 01:16:10 +02:00
Jonas Kvinge
fd9ab43681 Windows7ThumbBar: Create taskbar list once 2022-07-17 00:13:37 +02:00
Jonas Kvinge
4935cdc722 Transcoder: Formatting 2022-07-16 16:31:04 +02:00
Jonas Kvinge
26a3c7ad6a Transcoder: Fix caps leak 2022-07-16 16:10:49 +02:00
Strawbs Bot
c358216ad9 Update translations 2022-07-16 01:01:40 +02:00
Yaroslav Chvanov
bb9302143c Send the media player name and version to ListenBrainz 2022-07-15 20:50:00 +02:00
Yaroslav Chvanov
3a73553aac Send more information to ListenBrainz
- Track duration and number.
- Player name and version.
2022-07-15 20:50:00 +02:00
Strawbs Bot
6447a17e3e Update translations 2022-07-15 01:01:45 +02:00
Jonas Kvinge
4bd5c8ffb3 EditTagDialog: Remove useless HTML 2022-07-14 23:56:00 +02:00
Jonas Kvinge
89f137b211 SmartPlaylistSearchTermWidget: Remove tr from Strawberry 2022-07-14 23:41:38 +02:00
Strawbs Bot
3425572c66 Update translations
Fixes #994
2022-07-14 22:09:06 +02:00
Jonas Kvinge
ad50875f9c SnapDialog: Remove HTML tag from tr text 2022-07-14 22:04:23 +02:00
Strawbs Bot
34e5645dab Update translations 2022-07-14 20:54:57 +02:00
Jonas Kvinge
e8ae91c230 nsi: Add pcre2-8 2022-07-13 17:25:14 +02:00
Jonas Kvinge
44b83994fa Remove libpcre-1.dll 2022-07-13 16:58:28 +02:00
Jonas Kvinge
201f585350 CI: Use official Qt 6 repo for Leap 15.3 and 15.4 2022-07-11 23:12:26 +02:00
Strawbs Bot
e4d2f1925e Update translations 2022-07-11 01:04:46 +02:00
Jonas Kvinge
658c116eac Utilities: Remove unnecessary constData() in Sha1CoverHash 2022-07-10 19:14:05 +02:00
Jonas Kvinge
dbbedee77f CollectionBackendTest: Remove invalid song test 2022-07-10 19:09:09 +02:00
Jonas Kvinge
16ac9ea061 TagReaderTest: Remove use of deprecated QCryptographicHash constructor 2022-07-10 19:08:43 +02:00
Jonas Kvinge
6afbc71b4a Remove old schemas 2022-07-10 18:57:00 +02:00
Jonas Kvinge
71263863ad Allow invalid ctime/mtime
Possible fix for #815 and #947
2022-07-10 03:24:02 +02:00
Jonas Kvinge
05232065f8 schema: Remove DEFAULT for TEXT 2022-07-10 01:56:39 +02:00
Jonas Kvinge
f289ae4143 Add Ubuntu Kinetic 2022-07-08 19:36:43 +02:00
Jonas Kvinge
91703ecec4 macdeployqt: Add hls plugin 2022-07-07 21:47:05 +02:00
Strawbs Bot
6fb0858b87 Update translations 2022-07-04 01:01:17 +02:00
Jonas Kvinge
f1c6620df9 Add Wiki link to README 2022-07-02 22:38:55 +02:00
Jonas Kvinge
08fed9a5ee CI: Update libsoup dll 2022-07-02 10:21:15 +02:00
Jonas Kvinge
eb28b3b05b Update libsoup in nsi 2022-07-01 23:33:19 +02:00
Strawbs Bot
f0c354b0c9 Update translations 2022-07-01 01:01:44 +02:00
Yaroslav Chvanov
07d88e86a2 Add support for prefer album artist option to ListenBrainz 2022-06-30 21:59:30 +02:00
Jonas Kvinge
f41a3b9bb2 CommandlineOptions: Change quint64 to quint32 to match operator>>
Fixes #986
2022-06-24 17:18:54 +02:00
Jonas Kvinge
bfd9e76f40 Remove capture of ‘this’ via ‘[=]’ 2022-06-20 23:52:18 +02:00
Strawbs Bot
38ce208a43 Update translations 2022-06-18 01:01:29 +02:00
Jonas Kvinge
fcd148b8d5 Playlist: Fix "Stop after this track" greys out next track in dynamic mode
Fixes #912
2022-06-16 17:53:07 +02:00
Ondrej Mosnacek
fc6c3774b0 README: Exclude unsupported repos in the repology badge
Showing them provides little value, as they are no longer maintained
and thus expected to contain outdated packages.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
2022-06-15 17:12:43 +02:00
Chongo Bong
18023d2e18 Update desktop file action, use play/pause instead of play and pause
Condenses it a bit. looks cleaner, yet provides the same functionality as before.
2022-06-15 17:08:15 +02:00
Strawbs Bot
467e834ad6 Update translations 2022-06-15 01:01:41 +02:00
Jonas Kvinge
629d9e7ae0 FileViewList: Sort files with QCollator in numeric mode
Fixes #977
2022-06-14 17:21:59 +02:00
Jonas Kvinge
4487d292e8 Set C standard to C99 for MSVC 2022-06-14 16:16:49 +02:00
Jonas Kvinge
76711b9a66 Set C standard to C17 2022-06-13 21:20:41 +02:00
Jonas Kvinge
b54c749e43 Replace use of C-style casts 2022-06-13 00:40:31 +02:00
Jonas Kvinge
d82fd421ed Replace use of C-style casts 2022-06-13 00:23:42 +02:00
Jonas Kvinge
abdcadb5fa Flush correct queue 2022-06-12 02:28:02 +02:00
Jonas Kvinge
fa3891e383 Use list instead of map for songs in internet search
Fixes issues with sorting
2022-06-12 01:59:46 +02:00
Strawbs Bot
f1dc0f95c8 Update translations 2022-06-12 01:28:33 +02:00
Jonas Kvinge
5c721d243a Turn on git revision 2022-06-11 00:27:38 +02:00
Jonas Kvinge
2770384f1f Release 1.0.5 2022-06-10 21:50:35 +02:00
Jonas Kvinge
f4e5b83039 Replace use of C-style casts 2022-06-10 02:30:39 +02:00
Jonas Kvinge
124681605b Change -Wno-old-style-cast to -Wold-style-cast 2022-06-10 01:23:55 +02:00
Jonas Kvinge
a217faae7c OSDPretty: Remove use of C-style cast 2022-06-10 00:47:11 +02:00
Jonas Kvinge
184c39bbb7 Update Changelog 2022-06-10 00:01:21 +02:00
Jonas Kvinge
508e2a70bb Update Changelog 2022-06-09 23:38:25 +02:00
Jonas Kvinge
5aac56fe96 ContextView: Cleanup spacers 2022-06-09 23:09:41 +02:00
Strawbs Bot
dab7dad966 Update translations 2022-06-09 01:02:32 +02:00
Jonas Kvinge
25e48d6cae Add option for disabling bars on currently playing track
Fixes #972
2022-06-09 00:50:21 +02:00
Jonas Kvinge
9b743e55d1 ContextView: Update album width from context 2022-06-09 00:46:39 +02:00
Jonas Kvinge
dc1f9edfaf Organize: Only notify song path changed for collection and device songs 2022-06-07 22:43:39 +02:00
Strawbs Bot
b6b68edf1e Update translations 2022-06-07 01:01:51 +02:00
Jonas Kvinge
a2320b99ae ContextView: Use fixed size to avoid scrollbar issues 2022-06-06 20:54:15 +02:00
Jonas Kvinge
be01e28068 ContextAlbum: Use sizeHint() 2022-06-06 18:24:00 +02:00
Jonas Kvinge
d6084083a5 Update data/style/strawberry.css 2022-06-06 18:23:34 +02:00
Jonas Kvinge
019bf5102c ResizableTextEdit: Add Q_OBJECT macro 2022-06-06 18:23:12 +02:00
Jonas Kvinge
a1633224d1 CI: Add g++ for debian/ubuntu 2022-06-06 18:22:06 +02:00
Strawbs Bot
e62bd26cb9 Update translations 2022-06-06 01:26:37 +02:00
Jonas Kvinge
c29f517292 Update debian/control.in 2022-06-05 23:47:04 +02:00
Jonas Kvinge
d80144f80c Remove -fpermissive 2022-06-05 23:19:40 +02:00
Jonas Kvinge
4733c84a86 ContextAlbum: Move rescaling back to paintevent 2022-06-05 19:02:34 +02:00
Jonas Kvinge
23f3d2095b ContextAlbum: Improve album cover fading 2022-06-05 18:20:44 +02:00
Jonas Kvinge
4f1f2b6fc6 ContextView: Change condition for showing context menu 2022-06-05 18:19:14 +02:00
Jonas Kvinge
1ea70b085f Context: Remove albums 2022-06-05 11:58:53 +02:00
Jonas Kvinge
6c0b395f4a Add option for overwriting playcounts
Fixes #962
2022-06-05 04:59:50 +02:00
Jonas Kvinge
0ee67f186f Remove widgets/resizabletextedit.h from CMakeLists.txt 2022-06-05 04:57:58 +02:00
Jonas Kvinge
d133addbaa Context: Use custom textedits instead of labels
Also improve album cover scaling

Fixes #965
2022-06-05 04:41:36 +02:00
Jonas Kvinge
683991b1c9 Add ResizableTextEdit 2022-06-05 04:41:36 +02:00
Strawbs Bot
32ac02007b Update translations 2022-06-05 01:01:44 +02:00
Jonas Kvinge
d716617ae0 FancyTabWidget: Make sure context menu does not popup outside of tabbar 2022-06-05 00:25:48 +02:00
Jonas Kvinge
26e1f015d2 Add streaming to description in CMakeLists.txt 2022-06-04 16:16:19 +02:00
Jonas Kvinge
1c94093a4b Update debian/copyright 2022-06-04 16:16:19 +02:00
Strawbs Bot
27eccd456a Update translations 2022-06-04 01:02:03 +02:00
Jonas Kvinge
eed4447560 Update protobuf in nsi 2022-05-31 19:02:31 +02:00
Jonas Kvinge
3c8d0ebd52 CollectionModel: Use grouping in all album groupings 2022-05-31 00:53:47 +02:00
Jonas Kvinge
7ec0d2f2cc Update fftw3 in nsi 2022-05-22 02:14:07 +02:00
Strawbs Bot
35da91a997 Update translations 2022-05-22 01:02:14 +02:00
Jonas Kvinge
788747c071 MainWindow: Remove unnecessary hide() 2022-05-21 19:12:53 +02:00
Jonas Kvinge
36eb131289 MainWindow: Remove QEvent::spontaneous() check in close event
Fixes #964
2022-05-21 19:12:53 +02:00
Jonas Kvinge
f231f28818 Move QGuiApplication::setQuitOnLastWindowClosed before QApplication 2022-05-21 19:12:53 +02:00
Strawbs Bot
aebdc89f77 Update translations 2022-05-20 01:04:44 +02:00
Jonas Kvinge
4a3a379871 Increase icons to include 128x128 and set global application icon
Fixes #954
2022-05-19 22:02:35 +02:00
Jonas Kvinge
a7f27add2d Combine definitions 2022-05-17 23:40:53 +02:00
Strawbs Bot
cd1d4247cf Update translations 2022-05-16 01:01:29 +02:00
Strawbs Bot
6d618c4b78 Update translations 2022-05-14 01:11:35 +02:00
Jonas Kvinge
ad469531ff SPFParser: Use percent-encoding for image too 2022-05-13 23:36:38 +02:00
Jonas Kvinge
cf9b4b1246 XSPFParser: Use percent-encoding when loading and saving playlists
Fixes #821
2022-05-13 23:32:35 +02:00
Jonas Kvinge
18a2692dc1 PlaylistParser: Refactor code and exclude CUE from save playlist filters
Fixes #953
2022-05-13 23:14:56 +02:00
Jonas Kvinge
a2dad982f8 Move sqlrow to core 2022-05-13 18:15:04 +02:00
Jonas Kvinge
8f27b6a4c9 CI: Update Fedora 2022-05-10 01:06:04 +02:00
Jonas Kvinge
21c4022fca PlaylistView: Fix invalidating cached pixmap on scroll
Fixes #952
2022-05-09 23:46:33 +02:00
Jonas Kvinge
237933855a Remove unused PKGBUILD file 2022-05-06 18:31:32 +02:00
Jonas Kvinge
dff7068a03 Move debian configuration to separate CMakeLists.txt 2022-05-06 18:26:46 +02:00
Jonas Kvinge
8f28a85a6d RadioParadiseService: Change URLs to https 2022-05-06 18:07:20 +02:00
Jonas Kvinge
2414eb2598 GstEnginePipeline: Replace char with int8_t in HandoffCallback 2022-05-06 17:42:08 +02:00
Strawbs Bot
4851f6bffd Update translations 2022-05-06 01:01:57 +02:00
Jonas Kvinge
948be36d3c SmartPlaylistSearchTerm: Move value assigment
Mistake done in commit 3f3ae7c38f
2022-05-05 20:41:30 +02:00
Jonas Kvinge
edd088927d macdeployqt: List missing gstreamer plugins instead of failing
Fixes #936
2022-05-04 23:11:53 +02:00
Jonas Kvinge
4cfe8dd95e nsi: Fix pcre2 dll for debug 2022-05-03 20:08:40 +02:00
Jonas Kvinge
e1ce81c5bf Update pcre2 filename in nsi 2022-04-30 21:03:09 +02:00
Jonas Kvinge
0f40b5f022 VLCEngine: Fix track progress
Fixes #941
2022-04-29 00:09:29 +02:00
FireFragment
94c5ffa92e Allow setting blur amount of background image up to 100px 2022-04-27 20:53:29 +02:00
FireFragment
3c9bf56767 Improve CMake error message in case of missing libvlc
When running CMake with VLC installed, but without libvlc there pops out following error message:
 > You need to have eithoer GStreamer or VLC to compile!

This may be confusing for some users, because they might have VLC already installed, but not libvlc. This commit fixes it.
2022-04-26 17:39:07 +02:00
Jonas Kvinge
9024acb16e debian: Add qt6-qpa-plugins to depends
Fixes #937
2022-04-23 23:48:16 +02:00
Jonas Kvinge
7732402122 Add ffmpeg to MSVC 2022-04-23 18:06:38 +02:00
Jonas Kvinge
2059bce6a7 Add Qt 6 support to debian files 2022-04-23 02:15:22 +02:00
Jonas Kvinge
499c86a8b8 Add safe git directory 2022-04-20 01:23:02 +02:00
Strawbs Bot
ef084eb145 Update translations 2022-04-20 01:02:20 +02:00
Jonas Kvinge
3f3ae7c38f SmartPlaylistSearchTerm: Fix filetype search 2022-04-19 23:11:13 +02:00
Strawbs Bot
83d762287f Update translations 2022-04-18 01:01:58 +02:00
Strawbs Bot
5af0acf147 Update translations 2022-04-16 01:05:16 +02:00
Jonas Kvinge
907dfee6f7 Utilities: Use inconv with MSVC 2022-04-15 17:44:04 +02:00
Strawbs Bot
d587d24603 Update translations 2022-04-15 01:03:02 +02:00
Jonas Kvinge
c246b8f164 GstEngine: Show debug information in error dialog 2022-04-14 20:56:57 +02:00
Strawbs Bot
deecafa053 Update translations 2022-04-11 01:02:28 +02:00
Jonas Kvinge
f8810106a2 Turn on git revision 2022-04-10 17:34:26 +02:00
Jonas Kvinge
8e35fbe532 Release 1.0.4 2022-04-10 14:08:25 +02:00
Jonas Kvinge
b1fdccde6e ContextView: Specify alternative fonts for no song playing 2022-04-10 14:05:51 +02:00
Jonas Kvinge
6741f100a1 Update Changelog 2022-04-10 12:19:23 +02:00
Jonas Kvinge
e05e9ea1b2 timeconstants: Use constexpr 2022-04-08 17:35:44 +02:00
Jonas Kvinge
197341de5e Update debian/copyright-scan-patterns.yml 2022-04-08 17:35:31 +02:00
Jonas Kvinge
2933482be3 Update debian/copyright 2022-04-08 17:35:23 +02:00
Jonas Kvinge
c7ce23239f Disable gstwasapi2 in nsi 2022-04-07 23:18:08 +02:00
Jonas Kvinge
79115f7439 Update README and issue template 2022-04-07 01:22:21 +02:00
Jonas Kvinge
4457103672 Add pcre2 to nsi for MSVC 2022-04-06 23:23:20 +02:00
Jonas Kvinge
d204875f72 globalshortcut-x11: Fix minor code issues 2022-04-06 21:22:10 +02:00
Strawbs Bot
c690e73b1a Update translations 2022-04-05 01:02:10 +02:00
Jonas Kvinge
b5d39c5f21 Context: Remove use of custom font
Fixes #932
2022-04-04 20:26:38 +02:00
Strawbs Bot
7f8834cb04 Update translations 2022-04-04 01:02:50 +02:00
Jonas Kvinge
0dab7e293c GstEngine: Append "2" to wasapi2sink output description 2022-04-02 01:37:43 +02:00
Jonas Kvinge
8f016880af globalshortcut-win: Register 0-9 as num keys too 2022-04-02 00:43:41 +02:00
Jonas Kvinge
f471462a84 AnalyzerBase: Refactor code 2022-04-01 22:30:22 +02:00
Strawbs Bot
58eec8df1e Update translations 2022-03-30 01:02:21 +02:00
Jonas Kvinge
2655b8b43a TagReaderTagLib: Replace use of QByteArray::count() 2022-03-29 01:43:07 +02:00
Jonas Kvinge
ea86c043a4 AlsaPCMDeviceFinder: Fix use of deleted memory 2022-03-29 01:23:35 +02:00
Strawbs Bot
c1faa616bc Update translations 2022-03-29 01:04:21 +02:00
Strawbs Bot
a68bf5a30d Update translations 2022-03-28 01:02:14 +02:00
Jonas Kvinge
9568b8e0e5 Add hls to nsi 2022-03-28 00:34:20 +02:00
Jonas Kvinge
b34321ef87 GlobalShortcutsManager: Translate shortcuts
Fixes #928
2022-03-27 14:31:58 +02:00
Strawbs Bot
4971f1c5bf Update translations 2022-03-27 01:04:49 +01:00
Maxime Haselbauer
962b52bd5b Add save all playlists action 2022-03-26 22:38:21 +01:00
Jonas Kvinge
9449bfa414 Update protobuf in nsi 2022-03-26 21:02:56 +01:00
Strawbs Bot
5b8e9066c6 Update translations 2022-03-26 01:03:00 +01:00
Jonas Kvinge
8690be7fd2 Use separate sparkle feed for mingw and msvc 2022-03-25 21:38:37 +01:00
Jonas Kvinge
497267016b Turn on git revision 2022-03-25 01:22:31 +01:00
860 changed files with 67767 additions and 60728 deletions

View File

@@ -1,105 +1,216 @@
BasedOnStyle: Chromium
Language: Cpp
Standard: Cpp11
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -1
AlignAfterOpenBracket: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: true
AlignEscapedNewlines: true
AlignOperands: false
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: DontAlign
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: true
AllowShortLambdasOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: No
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BreakBeforeBraces: false
BreakBeforeBinaryOperators: false
BreakBeforeBraces: Stroustrup
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: BeforeColon
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 0
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: true
DerivePointerAlignment: false
DisableFormat: true
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: true
IncludeBlocks: Preserve
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: true
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 2
IndentExternBlock: AfterExternBlock
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 100
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 0
PenaltyBreakBeforeFirstCallParameter: 0
PenaltyBreakComment: 0
PenaltyBreakFirstLessLess: 0
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 0
PenaltyBreakTemplateDeclaration: 0
PenaltyExcessCharacter: 0
PenaltyReturnTypeOnItsOwnLine: 0
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
RequiresClausePosition: OwnLine
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: false
SortJavaStaticImport: Before
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
UseCRLF: false
UseTab: Never
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: false
AfterCaseLabel: false
AfterStruct: false
AfterClass: false
AfterEnum: false
AfterUnion: false
AfterControlStatement: Never
AfterNamespace: false
AfterObjCDeclaration: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: true
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

1
.github/FUNDING.yml vendored
View File

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

View File

@@ -7,12 +7,9 @@ assignees: ''
---
For technical issues, questions and feature suggestions/requests please use the forum on https://forum.strawberrymusicplayer.org/
Check the Changelog to see if the issue is already fixed:
https://github.com/strawberrymusicplayer/strawberry/blob/master/Changelog
If it's fixed, try the latest development build from: https://builds.strawberrymusicplayer.org/
- [ ] I have checked the [FAQ](https://wiki.strawberrymusicplayer.org/wiki/FAQ) for answers.
- [ ] I have checked the [Changelog](https://github.com/strawberrymusicplayer/strawberry/blob/master/Changelog) that the issue is not already fixed.
- [ ] I believe this issue is a bug, and not a general technical issue, question or feature requests that can be discussed on the [forum](https://forum.strawberrymusicplayer.org/).
**Describe the bug**
A clear and concise description of what the bug is.

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: daily

1673
.github/workflows/build.yml vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

40
.gitignore vendored
View File

@@ -65,19 +65,6 @@ ui_*.h
*.moc
*.qm
# QtCreator
CMakeLists.txt.user*
*.pro.user
*.pro.user.*
*creator.user*
target_wrapper.*
compile_commands.json
*.kdev4
*.vscode
*.code-workspace
*.sublime-workspace
# Temporary files
*~
*.autosave
@@ -105,22 +92,29 @@ Thumbs.db
# Stuff in dist
maketarball.sh
changelog
PKGBUILD
# Translations
translations.pot
zanata.xml
.zanata-cache/
# Snap
parts/
prime/
stage/
*.snap
/snap/.snapcraft/
/*_source.tar.bz2
# QtCreator
CMakeLists.txt.user*
*.pro.user
*.pro.user.*
*creator.user*
target_wrapper.*
compile_commands.json
*.kdev4
*.vscode
*.code-workspace
*.sublime-workspace
# MSVC
CMakeSettings.json
/.vs/
/out/
/.vs
/out
# CLion
/.idea

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "3rdparty/kdsingleapplication/KDSingleApplication"]
path = 3rdparty/kdsingleapplication/KDSingleApplication
url = https://github.com/KDAB/KDSingleApplication.git
branch = master

17
3rdparty/README.md vendored
View File

@@ -1,15 +1,13 @@
3rdparty libraries located in this directory
============================================
singleapplication
KDSingleApplication
-----------------
This is a small static 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.
If you dynamically link to your systems version, you'll need two versions, one defined as QApplication and
one as a QCoreApplication.
It is included here because it is not packed by distros and is also used on macOS and Windows.
It is also used to pass command-line options through to the first instance.
URL: https://github.com/itay-grudev/SingleApplication
URL: https://github.com/KDAB/KDSingleApplication/
SPMediaKeyTap
@@ -18,13 +16,6 @@ Used on macOS to exclusively enable strawberry to grab global media shortcuts.
Can safely be deleted on other platforms.
macdeployqt
-----------
A modified version of Qt's official macdeployqt utility that fixes some issues,
this version also deploys gstreamer plugins.
Can safely be deleted on other platforms.
getopt
------
getopt included only when compiling with MSVC on Windows.
getopt included only when compiling on Windows.

View File

@@ -1,2 +1,3 @@
add_library(getopt STATIC getopt.c)
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,562 +0,0 @@
/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
/*
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <windows.h>
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
#ifdef REPLACE_GETOPT
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
#undef optreset /* see getopt.h */
#define optreset __mingw_optreset
int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#endif
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#ifndef __CYGWIN__
#define __progname __argv[0]
#else
extern char __declspec(dllimport) *__progname;
#endif
#ifdef __CYGWIN__
static char EMSG[] = "";
#else
#define EMSG ""
#endif
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";
static void
_vwarnx(const char *fmt,va_list ap)
{
(void)fprintf(stderr,"%s: ",__progname);
if (fmt != NULL)
(void)vfprintf(stderr,fmt,ap);
(void)fprintf(stderr,"\n");
}
static void
warnx(const char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
_vwarnx(fmt,ap);
va_end(ap);
}
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -1 if short_too is set and the option does not match long_options.
*/
static int
parse_long_options(char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too)
{
char *current_argv, *has_equal;
size_t current_argv_len;
int i, ambiguous, match;
#define IDENTICAL_INTERPRETATION(_x, _y) \
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
long_options[(_x)].flag == long_options[(_y)].flag && \
long_options[(_x)].val == long_options[(_y)].val)
current_argv = place;
match = -1;
ambiguous = 0;
optind++;
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
ambiguous = 0;
break;
}
/*
* If this is a known short option, don't allow
* a partial match of a single character.
*/
if (short_too && current_argv_len == 1)
continue;
if (match == -1) /* partial match */
match = i;
else if (!IDENTICAL_INTERPRETATION(i, match))
ambiguous = 1;
}
if (ambiguous) {
/* ambiguous abbreviation */
if (PRINT_ERROR)
warnx(ambig, (int)current_argv_len,
current_argv);
optopt = 0;
return (BADCH);
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
if (PRINT_ERROR)
warnx(noarg, (int)current_argv_len,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return (BADARG);
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':' indicates no error
* should be generated.
*/
if (PRINT_ERROR)
warnx(recargstring,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else { /* unknown option */
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR)
warnx(illoptstring, current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
#undef IDENTICAL_INTERPRETATION
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
*/
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar, short_too;
static int posixly_correct = -1;
if (options == NULL)
return (-1);
/*
* XXX Some GNU programs (like cvs) set optind to 0 instead of
* XXX using optreset. Work around this braindamage.
*/
if (optind == 0)
optind = optreset = 1;
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*
* CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
* optreset != 0 for GNU compatibility.
*/
if (posixly_correct == -1 || optreset != 0)
posixly_correct = (GetEnvironmentVariableW(L"POSIXLY_CORRECT", NULL, 0) != 0);
if (*options == '-')
flags |= FLAG_ALLARGS;
else if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
if (*options == '+' || *options == '-')
options++;
optarg = NULL;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if (*(place = nargv[optind]) != '-' ||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
place = EMSG; /* found non-option */
if (flags & FLAG_ALLARGS) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
/*
* If we have "-" do nothing, if "--" we are done.
*/
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
optind++;
place = EMSG;
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
/*
* Check long options if:
* 1) we were passed some
* 2) the arg is not just "-"
* 3) either the arg starts with -- we are getopt_long_only()
*/
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (flags & FLAG_LONGONLY))) {
short_too = 0;
if (*place == '-')
place++; /* --foo long option */
else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
idx, short_too);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(optchar == (int)'-' && *place != '\0') ||
(oli = strchr(options, optchar)) == NULL) {
/*
* If the user specified "-" and '-' isn't listed in
* options, return -1 (non-option) as per POSIX.
* Otherwise, it is an unknown option character (or ':').
*/
if (optchar == (int)'-' && *place == '\0')
return (-1);
if (!*place)
++optind;
if (PRINT_ERROR)
warnx(illoptchar, optchar);
optopt = optchar;
return (BADCH);
}
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (*place) /* no space */
/* NOTHING */;
else if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else /* white space */
place = nargv[optind];
optchar = parse_long_options(nargv, options, long_options,
idx, 0);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return (optchar);
}
#ifdef REPLACE_GETOPT
/*
* getopt --
* Parse argc/argv argument vector.
*
* [eventually this will replace the BSD getopt]
*/
int
getopt(int nargc, char * const *nargv, const char *options)
{
/*
* We don't pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
#endif /* REPLACE_GETOPT */
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
* getopt_long_only --
* Parse argc/argv argument vector.
*/
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}

753
3rdparty/getopt/getopt.cpp vendored Normal file
View File

@@ -0,0 +1,753 @@
/* 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,95 +1,135 @@
#ifndef __GETOPT_H__
/**
* DISCLAIMER
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
*
* The mingw-w64 runtime package and its code is distributed in the hope that it
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#define __GETOPT_H__
/* All the headers include this file. */
#include <crtdefs.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int optind; /* index of first non-option in argv */
extern int optopt; /* single option character, as parsed */
extern int opterr; /* flag to enable built-in diagnostics... */
/* (user may set to zero, to suppress) */
extern char *optarg; /* pointer to argument of current option */
extern int getopt(int nargc, char * const *nargv, const char *options);
#ifdef _BSD_SOURCE
/*
* BSD adds the non-standard `optreset' feature, for reinitialisation
* of `getopt' parsing. We support this feature, for applications which
* proclaim their BSD heritage, before including this header; however,
* to maintain portability, developers are advised to avoid it.
*/
# define optreset __mingw_optreset
extern int optreset;
#endif
#ifdef __cplusplus
}
#endif
/*
* POSIX requires the `getopt' API to be specified in `unistd.h';
* thus, `unistd.h' includes this header. However, we do not want
* to expose the `getopt_long' or `getopt_long_only' APIs, when
* included in this manner. Thus, close the standard __GETOPT_H__
* declarations block, and open an additional __GETOPT_LONG_H__
* specific block, only when *not* __UNISTD_H_SOURCED__, in which
* to declare the extended API.
*/
#endif /* !defined(__GETOPT_H__) */
#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
#define __GETOPT_LONG_H__
#ifdef __cplusplus
extern "C" {
#endif
struct option /* specification for a long form option... */
{
const char *name; /* option name, without leading hyphens */
int has_arg; /* does it take an argument? */
int *flag; /* where to save its status, or NULL */
int val; /* its associated status value */
};
enum /* permitted values for its `has_arg' field... */
{
no_argument = 0, /* option never takes an argument */
required_argument, /* option always requires an argument */
optional_argument /* option may take an argument */
};
extern int getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx);
extern int getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx);
/*
* Previous MinGW implementation had...
*/
#ifndef HAVE_DECL_GETOPT
/*
* ...for the long form API only; keep this for compatibility.
*/
# define HAVE_DECL_GETOPT 1
#endif
#ifdef __cplusplus
}
#endif
#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
/* 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

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.7)
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})
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)

View File

@@ -1,7 +0,0 @@
add_executable(macdeployqt main.cpp shared.cpp)
target_link_libraries(macdeployqt PRIVATE
"-framework AppKit"
${QtCore_LIBRARIES}
)
#execute_process(COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/macdeployqt ${CMAKE_BINARY_DIR})

View File

@@ -1,286 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#undef QT_NO_DEBUG_OUTPUT
#undef QT_NO_WARNING_OUTPUT
#undef QT_NO_INFO_OUTPUT
#include <QCoreApplication>
#include <QDir>
#include <QLibraryInfo>
#include "shared.h"
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QString appBundlePath;
if (argc > 1)
appBundlePath = QString::fromLocal8Bit(argv[1]);
if (argc < 2 || appBundlePath.startsWith("-")) {
qDebug() << "Usage: macdeployqt app-bundle [options]";
qDebug() << "";
qDebug() << "Options:";
qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
qDebug() << " -no-plugins : Skip plugin deployment";
qDebug() << " -dmg : Create a .dmg disk image";
qDebug() << " -no-strip : Don't run 'strip' on the binaries";
qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
qDebug() << " -always-overwrite : Copy files even if the target file exists";
qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables";
qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
qDebug() << " -libpath=<path> : Add the given path to the library search path";
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
qDebug() << "";
qDebug() << "macdeployqt takes an application bundle as input and makes it";
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
qDebug() << "the application uses.";
qDebug() << "";
qDebug() << "Plugins related to a framework are copied in with the";
qDebug() << "framework. The accessibility, image formats, and text codec";
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
qDebug() << "";
qDebug() << "Qt plugins may use private API and will cause the app to be";
qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning";
qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
qDebug() << "";
qDebug() << "See the \"Deploying Applications on OS X\" topic in the";
qDebug() << "documentation for more information about deployment on OS X.";
return 1;
}
appBundlePath = QDir::cleanPath(appBundlePath);
if (!QDir(appBundlePath).exists()) {
qDebug() << "Error: Could not find app bundle" << appBundlePath;
return 1;
}
bool plugins = true;
bool dmg = false;
QByteArray filesystem("HFS+");
bool useDebugLibs = false;
extern bool runStripEnabled;
extern bool alwaysOwerwriteEnabled;
extern QStringList librarySearchPath;
QStringList additionalExecutables;
bool qmldirArgumentUsed = false;
QStringList qmlDirs;
QStringList qmlImportPaths;
extern bool runCodesign;
extern QString codesignIdentiy;
extern bool hardenedRuntime;
extern bool appstoreCompliant;
extern bool deployFramework;
extern bool secureTimestamp;
for (int i = 2; i < argc; ++i) {
QByteArray argument = QByteArray(argv[i]);
if (argument == QByteArray("-no-plugins")) {
LogDebug() << "Argument found:" << argument;
plugins = false;
} else if (argument == QByteArray("-dmg")) {
LogDebug() << "Argument found:" << argument;
dmg = true;
} else if (argument == QByteArray("-no-strip")) {
LogDebug() << "Argument found:" << argument;
runStripEnabled = false;
} else if (argument == QByteArray("-use-debug-libs")) {
LogDebug() << "Argument found:" << argument;
useDebugLibs = true;
runStripEnabled = false;
} else if (argument.startsWith(QByteArray("-verbose"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
bool ok = false;
int number = argument.mid(index+1).toInt(&ok);
if (!ok)
LogError() << "Could not parse verbose level";
else
logLevel = number;
} else if (argument.startsWith(QByteArray("-executable"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing executable path";
else
additionalExecutables << argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-qmldir"))) {
LogDebug() << "Argument found:" << argument;
qmldirArgumentUsed = true;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing qml directory path";
else
qmlDirs << argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-qmlimport"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing qml import path";
else
qmlImportPaths << argument.mid(index+1);
} else if (argument.startsWith(QByteArray("-libpath"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing library search path";
else
librarySearchPath << argument.mid(index+1);
} else if (argument == QByteArray("-always-overwrite")) {
LogDebug() << "Argument found:" << argument;
alwaysOwerwriteEnabled = true;
} else if (argument.startsWith(QByteArray("-codesign"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
if (index < 0 || index >= argument.size()) {
LogError() << "Missing code signing identity";
} else {
runCodesign = true;
codesignIdentiy = argument.mid(index+1);
}
} else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf("=");
if (index < 0 || index >= argument.size()) {
LogError() << "Missing code signing identity";
} else {
runCodesign = true;
hardenedRuntime = true;
secureTimestamp = true;
codesignIdentiy = argument.mid(index+1);
}
} else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
LogDebug() << "Argument found:" << argument;
hardenedRuntime = true;
} else if (argument.startsWith(QByteArray("-timestamp"))) {
LogDebug() << "Argument found:" << argument;
secureTimestamp = true;
} else if (argument == QByteArray("-appstore-compliant")) {
LogDebug() << "Argument found:" << argument;
appstoreCompliant = true;
// Undocumented option, may not work as intended
} else if (argument == QByteArray("-deploy-framework")) {
LogDebug() << "Argument found:" << argument;
deployFramework = true;
} else if (argument.startsWith(QByteArray("-fs"))) {
LogDebug() << "Argument found:" << argument;
int index = argument.indexOf('=');
if (index == -1)
LogError() << "Missing filesystem type";
else
filesystem = argument.mid(index+1);
} else if (argument.startsWith("-")) {
LogError() << "Unknown argument" << argument << "\n";
return 1;
}
}
DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
if (deploymentInfo.isDebug)
useDebugLibs = true;
if (deployFramework && deploymentInfo.isFramework)
fixupFramework(appBundlePath);
// Convenience: Look for .qml files in the current directory if no -qmldir specified.
if (qmlDirs.isEmpty()) {
QDir dir;
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
qmlDirs += QStringLiteral(".");
}
}
if (!qmlDirs.isEmpty()) {
bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
if (!ok && qmldirArgumentUsed)
return 1; // exit if the user explicitly asked for qml import deployment
// Update deploymentInfo.deployedFrameworks - the QML imports
// may have brought in extra frameworks as dependencies.
deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
deploymentInfo.deployedFrameworks =
QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
deploymentInfo.deployedFrameworks.end()).values();
}
// Handle plugins
if (plugins) {
// Set the plugins search directory
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
#else
deploymentInfo.pluginPath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
#endif
// Sanity checks
if (deploymentInfo.pluginPath.isEmpty()) {
LogError() << "Missing Qt plugins path\n";
return 1;
}
if (!QDir(deploymentInfo.pluginPath).exists()) {
LogError() << "Plugins path does not exist" << deploymentInfo.pluginPath << "\n";
return 1;
}
// Deploy plugins
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
if (!deploymentInfo.pluginPath.isEmpty()) {
LogNormal();
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
createQtConf(appBundlePath);
}
}
if (runStripEnabled)
stripAppBinary(appBundlePath);
if (runCodesign)
codesign(codesignIdentiy, appBundlePath);
if (dmg) {
LogNormal();
createDiskImage(appBundlePath, filesystem);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,141 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MAC_DEPLOMYMENT_SHARED_H
#define MAC_DEPLOMYMENT_SHARED_H
#include <QString>
#include <QStringList>
#include <QDebug>
#include <QSet>
#include <QVersionNumber>
extern int logLevel;
#define LogError() if (logLevel < 0) {} else qDebug() << "ERROR:"
#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:"
#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:"
#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:"
extern bool runStripEnabled;
class FrameworkInfo
{
public:
bool isDylib;
QString frameworkDirectory;
QString frameworkName;
QString frameworkPath;
QString binaryDirectory;
QString binaryName;
QString binaryPath;
QString rpathUsed;
QString version;
QString installName;
QString deployedInstallName;
QString sourceFilePath;
QString frameworkDestinationDirectory;
QString binaryDestinationDirectory;
bool isDebugLibrary() const
{
return binaryName.endsWith(QStringLiteral("_debug"));
}
};
class DylibInfo
{
public:
QString binaryPath;
QVersionNumber currentVersion;
QVersionNumber compatibilityVersion;
};
class OtoolInfo
{
public:
QString installName;
QString binaryPath;
QVersionNumber currentVersion;
QVersionNumber compatibilityVersion;
QList<DylibInfo> dependencies;
};
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
QDebug operator<<(QDebug debug, const FrameworkInfo &info);
class ApplicationBundleInfo
{
public:
QString path;
QString binaryPath;
QStringList libraryPaths;
};
class DeploymentInfo
{
public:
QString qtPath;
QString pluginPath;
QStringList deployedFrameworks;
QList<QString> rpathsUsed;
bool useLoaderPath;
bool isFramework;
bool isDebug;
bool containsModule(const QString &module, const QString &libInFix) const;
};
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
OtoolInfo findDependencyInfo(const QString &binaryPath);
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
QString findAppBinary(const QString &appBundlePath);
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QList<QString> &rpaths, bool useDebugLibs);
QString copyFramework(const FrameworkInfo &framework, const QString path);
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
void createQtConf(const QString &appBundlePath);
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths);
void changeIdentification(const QString &id, const QString &binaryPath);
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
void runStrip(const QString &binaryPath);
void stripAppBinary(const QString &bundlePath);
QString findAppBinary(const QString &appBundlePath);
QStringList findAppFrameworkNames(const QString &appBundlePath);
QStringList findAppFrameworkPaths(const QString &appBundlePath);
void codesignFile(const QString &identity, const QString &filePath);
QSet<QString> codesignBundle(const QString &identity,
const QString &appBundlePath,
QList<QString> additionalBinariesContainingRpaths);
void codesign(const QString &identity, const QString &appBundlePath);
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
void fixupFramework(const QString &appBundlePath);
#endif

View File

@@ -1,36 +0,0 @@
cmake_minimum_required(VERSION 3.0)
include(CheckIncludeFiles)
include(CheckFunctionExists)
check_function_exists(geteuid HAVE_GETEUID)
check_function_exists(getpwuid HAVE_GETPWUID)
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
qt_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
target_include_directories(singleapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(singleapplication PRIVATE
${QtCore_LIBRARIES}
${QtWidgets_LIBRARIES}
${QtNetwork_LIBRARIES}
)
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
qt_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
target_include_directories(singlecoreapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(singlecoreapplication PRIVATE
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
)
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")

View File

@@ -1,24 +0,0 @@
The MIT License (MIT)
Copyright (c) Itay Grudev 2015 - 2020
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Note: Some of the examples include code not distributed under the terms of the
MIT License.

View File

@@ -1,305 +0,0 @@
SingleApplication
=================
[![CI](https://github.com/itay-grudev/SingleApplication/workflows/CI:%20Build%20Test/badge.svg)](https://github.com/itay-grudev/SingleApplication/actions)
This is a replacement of the QtSingleApplication for `Qt5` and `Qt6`.
Keeps the Primary Instance of your Application and kills each subsequent
instances. It can (if enabled) spawn secondary (non-related to the primary)
instances and can send data to the primary instance from secondary instances.
Usage
-----
The `SingleApplication` class inherits from whatever `Q[Core|Gui]Application`
class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
default). Further usage is similar to the use of the `Q[Core|Gui]Application`
classes.
You can use the library as if you use any other `QCoreApplication` derived
class:
```cpp
#include <QApplication>
#include <SingleApplication.h>
int main( int argc, char* argv[] )
{
SingleApplication app( argc, argv );
return app.exec();
}
```
To include the library files I would recommend that you add it as a git
submodule to your project. Here is how:
```bash
git submodule add https://github.com/itay-grudev/SingleApplication.git singleapplication
```
**Qmake:**
Then include the `singleapplication.pri` file in your `.pro` project file.
```qmake
include(singleapplication/singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QApplication
```
**CMake:**
Then include the subdirectory in your `CMakeLists.txt` project file.
```cmake
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
add_subdirectory(src/third-party/singleapplication)
target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication)
```
The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
instance of your Application is your Primary Instance. It would check if the
shared memory block exists and if not it will start a `QLocalServer` and listen
for connections. Each subsequent instance of your application would check if the
shared memory block exists and if it does, it will connect to the QLocalServer
to notify the primary instance that a new instance had been started, after which
it would terminate with status code `0`. In the Primary Instance
`SingleApplication` would emit the `instanceStarted()` signal upon detecting
that a new instance had been started.
The library uses `stdlib` to terminate the program with the `exit()` function.
Also don't forget to specify which `QCoreApplication` class your app is using if it
is not `QCoreApplication` as in examples above.
The `Instance Started` signal
-----------------------------
The SingleApplication class implements a `instanceStarted()` signal. You can
bind to that signal to raise your application's window when a new instance had
been started, for example.
```cpp
// window is a QWindow instance
QObject::connect(
&app,
&SingleApplication::instanceStarted,
&window,
&QWindow::raise
);
```
Using `SingleApplication::instance()` is a neat way to get the
`SingleApplication` instance for binding to it's signals anywhere in your
program.
__Note:__ On Windows the ability to bring the application windows to the
foreground is restricted. See [Windows specific implementations](Windows.md)
for a workaround and an example implementation.
Secondary Instances
-------------------
If you want to be able to launch additional Secondary Instances (not related to
your Primary Instance) you have to enable that with the third parameter of the
`SingleApplication` constructor. The default is `false` meaning no Secondary
Instances. Here is an example of how you would start a Secondary Instance send
a message with the command line arguments to the primary instance and then shut
down.
```cpp
int main(int argc, char *argv[])
{
SingleApplication app( argc, argv, true );
if( app.isSecondary() ) {
app.sendMessage( app.arguments().join(' ')).toUtf8() );
app.exit( 0 );
}
return app.exec();
}
```
*__Note:__ A secondary instance won't cause the emission of the
`instanceStarted()` signal by default. See `SingleApplication::Mode` for more
details.*
You can check whether your instance is a primary or secondary with the following
methods:
```cpp
app.isPrimary();
// or
app.isSecondary();
```
*__Note:__ If your Primary Instance is terminated a newly launched instance
will replace the Primary one even if the Secondary flag has been set.*
Examples
--------
There are three examples provided in this repository:
* Basic example that prevents a secondary instance from starting [`examples/basic`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/basic)
* An example of a graphical application raising it's parent window [`examples/calculator`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/calculator)
* A console application sending the primary instance it's command line parameters [`examples/sending_arguments`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/sending_arguments)
API
---
### Members
```cpp
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100, QString userData = QString() )
```
Depending on whether `allowSecondary` is set, this constructor may terminate
your app if there is already a primary instance running. Additional `Options`
can be specified to set whether the SingleApplication block should work
user-wide or system-wide. Additionally the `Mode::SecondaryNotification` may be
used to notify the primary instance whenever a secondary instance had been
started (disabled by default). `timeout` specifies the maximum time in
milliseconds to wait for blocking operations. Setting `userData` provides additional data that will isolate this instance from other instances that do not have the same (or any) user data set.
*__Note:__ `argc` and `argv` may be changed as Qt removes arguments that it
recognizes.*
*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
and the secondary instance.*
*__Note:__ Operating system can restrict the shared memory blocks to the same
user, in which case the User/System modes will have no effect and the block will
be user wide.*
---
```cpp
bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
```
Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
in milliseconds for blocking functions. Returns `true` if the message has been sent
successfully. If the message can't be sent or the function timeouts - returns `false`.
---
```cpp
bool SingleApplication::isPrimary()
```
Returns if the instance is the primary instance.
---
```cpp
bool SingleApplication::isSecondary()
```
Returns if the instance is a secondary instance.
---
```cpp
quint32 SingleApplication::instanceId()
```
Returns a unique identifier for the current instance.
---
```cpp
qint64 SingleApplication::primaryPid()
```
Returns the process ID (PID) of the primary instance.
---
```cpp
QString SingleApplication::primaryUser()
```
Returns the username the primary instance is running as.
---
```cpp
QString SingleApplication::currentUser()
```
Returns the username the current instance is running as.
### Signals
```cpp
void SingleApplication::instanceStarted()
```
Triggered whenever a new instance had been started, except for secondary
instances if the `Mode::SecondaryNotification` flag is not specified.
---
```cpp
void SingleApplication::receivedMessage( quint32 instanceId, QByteArray message )
```
Triggered whenever there is a message received from a secondary instance.
---
### Flags
```cpp
enum SingleApplication::Mode
```
* `Mode::User` - The SingleApplication block should apply user wide. This adds
user specific data to the key used for the shared memory and server name.
This is the default functionality.
* `Mode::System` The SingleApplication block applies system-wide.
* `Mode::SecondaryNotification` Whether to trigger `instanceStarted()` even
whenever secondary instances are started.
* `Mode::ExcludeAppPath` Excludes the application path from the server name
(and memory block) hash.
* `Mode::ExcludeAppVersion` Excludes the application version from the server
name (and memory block) hash.
*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
and the secondary instance.*
*__Note:__ Operating system can restrict the shared memory blocks to the same
user, in which case the User/System modes will have no effect and the block will
be user wide.*
---
Versioning
----------
Each major version introduces either very significant changes or is not
backwards compatible with the previous version. Minor versions only add
additional features, bug fixes or performance improvements and are backwards
compatible with the previous release. See [`CHANGELOG.md`](CHANGELOG.md) for
more details.
Implementation
--------------
The library is implemented with a QSharedMemory block which is thread safe and
guarantees a race condition will not occur. It also uses a QLocalSocket to
notify the main process that a new instance had been spawned and thus invoke the
`instanceStarted()` signal and for messaging the primary instance.
Additionally the library can recover from being forcefully killed on *nix
systems and will reset the memory block given that there are no other
instances running.
License
-------
This library and it's supporting documentation are released under
`The MIT License (MIT)` with the exception of the Qt calculator examples which
is distributed under the BSD license.

View File

@@ -1,2 +0,0 @@
#cmakedefine HAVE_GETEUID
#cmakedefine HAVE_GETPWUID

View File

@@ -1,266 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#include <cstdlib>
#include <limits>
#include <QtGlobal>
#include <QApplication>
#include <QThread>
#include <QSharedMemory>
#include <QLocalSocket>
#include <QByteArray>
#include <QElapsedTimer>
#include <QtDebug>
#include "singleapplication.h"
#include "singleapplication_p.h"
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
* @param argc
* @param argv
* @param allowSecondary Whether to enable secondary instance support
* @param options Optional flags to toggle specific behaviour
* @param timeout Maximum time blocking functions are allowed during app load
*/
SingleApplication::SingleApplication(int &argc, char *argv[], const bool allowSecondary, const Options options, const int timeout)
: app_t(argc, argv),
d_ptr(new SingleApplicationPrivate(this)) {
Q_D(SingleApplication);
// Store the current mode of the program
d->options_ = options;
// Generating an application ID used for identifying the shared memory block and QLocalServer
d->genBlockServerName();
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
SingleApplicationPrivate::randomSleep();
#ifdef Q_OS_UNIX
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
d->memory_ = new QSharedMemory(d->blockServerName_);
d->memory_->attach();
delete d->memory_;
#endif
// Guarantee thread safe behaviour with a shared memory block.
d->memory_ = new QSharedMemory(d->blockServerName_);
// Create a shared memory block
if (d->memory_->create(sizeof(InstancesInfo))) {
// Initialize the shared memory block
if (!d->memory_->lock()) {
qCritical() << "SingleApplication: Unable to lock memory block after create.";
abortSafely();
}
d->initializeMemoryBlock();
}
else {
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
// Attempt to attach to the memory segment
if (!d->memory_->attach()) {
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
abortSafely();
}
if (!d->memory_->lock()) {
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
abortSafely();
}
}
else {
qCritical() << "SingleApplication: Unable to create block.";
abortSafely();
}
}
InstancesInfo *instance = static_cast<InstancesInfo*>(d->memory_->data());
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state
forever {
// If the shared memory block's checksum is valid continue
if (d->blockChecksum() == instance->checksum) break;
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
if (time.elapsed() > 5000) {
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
d->initializeMemoryBlock();
}
// Otherwise wait for a random period and try again.
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
if (!d->memory_->unlock()) {
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
qDebug() << d->memory_->errorString();
}
SingleApplicationPrivate::randomSleep();
if (!d->memory_->lock()) {
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
abortSafely();
}
}
if (!instance->primary) {
d->startPrimary();
if (!d->memory_->unlock()) {
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
qDebug() << d->memory_->errorString();
}
return;
}
// Check if another instance can be started
if (allowSecondary) {
d->startSecondary();
if (d->options_ & Mode::SecondaryNotification) {
d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance);
}
if (!d->memory_->unlock()) {
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
qDebug() << d->memory_->errorString();
}
return;
}
if (!d->memory_->unlock()) {
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
qDebug() << d->memory_->errorString();
}
d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance);
delete d;
::exit(EXIT_SUCCESS);
}
SingleApplication::~SingleApplication() {
Q_D(SingleApplication);
delete d;
}
/**
* Checks if the current application instance is primary.
* @return Returns true if the instance is primary, false otherwise.
*/
bool SingleApplication::isPrimary() const {
Q_D(const SingleApplication);
return d->server_ != nullptr;
}
/**
* Checks if the current application instance is secondary.
* @return Returns true if the instance is secondary, false otherwise.
*/
bool SingleApplication::isSecondary() const {
Q_D(const SingleApplication);
return d->server_ == nullptr;
}
/**
* Allows you to identify an instance by returning unique consecutive instance ids.
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
* @return Returns a unique instance id.
*/
quint32 SingleApplication::instanceId() const {
Q_D(const SingleApplication);
return d->instanceNumber_;
}
/**
* Returns the OS PID (Process Identifier) of the process running the primary instance.
* Especially useful when SingleApplication is coupled with OS. specific APIs.
* @return Returns the primary instance PID.
*/
qint64 SingleApplication::primaryPid() const {
Q_D(const SingleApplication);
return d->primaryPid();
}
/**
* Returns the username the primary instance is running as.
* @return Returns the username the primary instance is running as.
*/
QString SingleApplication::primaryUser() const {
Q_D(const SingleApplication);
return d->primaryUser();
}
/**
* Returns the username the current instance is running as.
* @return Returns the username the current instance is running as.
*/
QString SingleApplication::currentUser() const {
return SingleApplicationPrivate::getUsername();
}
/**
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfully, false otherwise.
*/
bool SingleApplication::sendMessage(const QByteArray &message, const int timeout) {
Q_D(SingleApplication);
// Nobody to connect to
if (isPrimary()) return false;
// Make sure the socket is connected
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect)) {
return false;
}
return d->writeConfirmedMessage(timeout, message);
}
/**
* Cleans up the shared memory block and exits with a failure.
* This function halts program execution.
*/
void SingleApplication::abortSafely() {
Q_D(SingleApplication);
qCritical() << "SingleApplication: " << d->memory_->error() << d->memory_->errorString();
delete d;
::exit(EXIT_FAILURE);
}

View File

@@ -1,152 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#ifndef SINGLEAPPLICATION_H
#define SINGLEAPPLICATION_H
#include <QtGlobal>
#include <QApplication>
#include <QFlags>
#include <QByteArray>
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multiple instances of the same Application
* @see QApplication
*/
class SingleApplication : public QApplication { // clazy:exclude=ctor-missing-parent-argument
Q_OBJECT
using app_t = QApplication;
public:
/**
* @brief Mode of operation of SingleApplication.
* Whether the block should be user-wide or system-wide and whether the
* primary instance should be notified when a secondary instance had been
* started.
* @note Operating system can restrict the shared memory blocks to the same
* user, in which case the User/System modes will have no effect and the
* block will be user wide.
* @enum
*/
enum Mode {
User = 1 << 0,
System = 1 << 1,
SecondaryNotification = 1 << 2,
ExcludeAppVersion = 1 << 3,
ExcludeAppPath = 1 << 4
};
Q_DECLARE_FLAGS(Options, Mode)
/**
* @brief Intitializes a SingleApplication instance with argc command line
* arguments in argv
* @arg {int &} argc - Number of arguments in argv
* @arg {const char *[]} argv - Supplied command line arguments
* @arg {bool} allowSecondary - Whether to start the instance as secondary
* if there is already a primary instance.
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
* User wide or System wide.
* @arg {int} timeout - Timeout to wait in milliseconds.
* @note argc and argv may be changed as Qt removes arguments that it
* recognizes
* @note Mode::SecondaryNotification only works if set on both the primary
* instance and the secondary instance.
* @note The timeout is just a hint for the maximum time of blocking
* operations. It does not guarantee that the SingleApplication
* initialisation will be completed in given time, though is a good hint.
* Usually 4*timeout would be the worst case (fail) scenario.
*/
explicit SingleApplication(int &argc, char *argv[], const bool allowSecondary = false, const Options options = Mode::User, const int timeout = 1000);
~SingleApplication() override;
/**
* @brief Returns if the instance is the primary instance
* @returns {bool}
*/
bool isPrimary() const;
/**
* @brief Returns if the instance is a secondary instance
* @returns {bool}
*/
bool isSecondary() const;
/**
* @brief Returns a unique identifier for the current instance
* @returns {qint32}
*/
quint32 instanceId() const;
/**
* @brief Returns the process ID (PID) of the primary instance
* @returns {qint64}
*/
qint64 primaryPid() const;
/**
* @brief Returns the username of the user running the primary instance
* @returns {QString}
*/
QString primaryUser() const;
/**
* @brief Returns the username of the current user
* @returns {QString}
*/
QString currentUser() const;
/**
* @brief Sends a message to the primary instance. Returns true on success.
* @param {int} timeout - Timeout for connecting
* @returns {bool}
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage(const QByteArray &message, const int timeout = 1000);
signals:
void instanceStarted();
void receivedMessage(quint32 instanceId, QByteArray message);
private:
SingleApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleApplication)
void abortSafely();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
#endif // SINGLEAPPLICATION_H

View File

@@ -1,526 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#include "config.h"
#include <QtGlobal>
#include <cstdlib>
#include <cstddef>
#ifdef Q_OS_UNIX
# include <unistd.h>
# include <sys/types.h>
# include <pwd.h>
#endif
#ifdef Q_OS_WIN
# ifndef NOMINMAX
# define NOMINMAX 1
# endif
# include <windows.h>
# include <lmcons.h>
#endif
#include <QObject>
#include <QThread>
#include <QIODevice>
#include <QSharedMemory>
#include <QByteArray>
#include <QDataStream>
#include <QCryptographicHash>
#include <QLocalServer>
#include <QLocalSocket>
#include <QElapsedTimer>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QRandomGenerator>
#else
# include <QDateTime>
#endif
#include "singleapplication.h"
#include "singleapplication_p.h"
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *ptr)
: q_ptr(ptr),
memory_(nullptr),
socket_(nullptr),
server_(nullptr),
instanceNumber_(-1) {}
SingleApplicationPrivate::~SingleApplicationPrivate() {
if (socket_ != nullptr) {
socket_->close();
delete socket_;
socket_ = nullptr;
}
if (memory_ != nullptr) {
memory_->lock();
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
if (server_ != nullptr) {
server_->close();
delete server_;
instance->primary = false;
instance->primaryPid = -1;
instance->primaryUser[0] = '\0';
instance->checksum = blockChecksum();
}
memory_->unlock();
delete memory_;
memory_ = nullptr;
}
}
QString SingleApplicationPrivate::getUsername() {
#ifdef Q_OS_UNIX
QString username;
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
struct passwd *pw = getpwuid(geteuid());
if (pw) {
username = QString::fromLocal8Bit(pw->pw_name);
}
#endif
if (username.isEmpty()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
username = qEnvironmentVariable("USER");
#else
username = QString::fromLocal8Bit(qgetenv("USER"));
#endif
}
return username;
#endif
#ifdef Q_OS_WIN
wchar_t username[UNLEN + 1];
// Specifies size of the buffer on input
DWORD usernameLength = UNLEN + 1;
if (GetUserNameW(username, &usernameLength)) {
return QString::fromWCharArray(username);
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
return qEnvironmentVariable("USERNAME");
#else
return QString::fromLocal8Bit(qgetenv("USERNAME"));
#endif
#endif
}
void SingleApplicationPrivate::genBlockServerName() {
QCryptographicHash appData(QCryptographicHash::Sha256);
appData.addData("SingleApplication");
appData.addData(SingleApplication::app_t::applicationName().toUtf8());
appData.addData(SingleApplication::app_t::organizationName().toUtf8());
appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
if (!(options_ & SingleApplication::Mode::ExcludeAppVersion)) {
appData.addData(SingleApplication::app_t::applicationVersion().toUtf8());
}
if (!(options_ & SingleApplication::Mode::ExcludeAppPath)) {
#if defined(Q_OS_UNIX)
const QByteArray appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
appData.addData(SingleApplication::app_t::applicationFilePath().toUtf8());
}
else {
appData.addData(appImagePath);
};
#elif defined(Q_OS_WIN)
appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
#else
appData.addData(SingleApplication::app_t::applicationFilePath().toUtf8());
#endif
}
// User level block requires a user specific data in the hash
if (options_ & SingleApplication::Mode::User) {
appData.addData(getUsername().toUtf8());
}
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
blockServerName_ = appData.result().toBase64().replace("/", "_");
}
void SingleApplicationPrivate::initializeMemoryBlock() const {
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
instance->primary = false;
instance->secondary = 0;
instance->primaryPid = -1;
instance->primaryUser[0] = '\0';
instance->checksum = blockChecksum();
}
void SingleApplicationPrivate::startPrimary() {
// Reset the number of connections
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
instance->primary = true;
instance->primaryPid = QCoreApplication::applicationPid();
qstrncpy(instance->primaryUser, getUsername().toUtf8().data(), sizeof(instance->primaryUser));
instance->checksum = blockChecksum();
instanceNumber_ = 0;
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer(blockServerName_);
server_ = new QLocalServer();
// Restrict access to the socket according to the SingleApplication::Mode::User flag on User level or no restrictions
if (options_ & SingleApplication::Mode::User) {
server_->setSocketOptions(QLocalServer::UserAccessOption);
}
else {
server_->setSocketOptions(QLocalServer::WorldAccessOption);
}
server_->listen(blockServerName_);
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
}
void SingleApplicationPrivate::startSecondary() {
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
instance->secondary += 1;
instance->checksum = blockChecksum();
instanceNumber_ = instance->secondary;
}
bool SingleApplicationPrivate::connectToPrimary(const int timeout, const ConnectionType connectionType) {
QElapsedTimer time;
time.start();
// Connect to the Local Server of the Primary Instance if not already connected.
if (socket_ == nullptr) {
socket_ = new QLocalSocket();
}
if (socket_->state() == QLocalSocket::ConnectedState) return true;
if (socket_->state() != QLocalSocket::ConnectedState) {
forever {
randomSleep();
if (socket_->state() != QLocalSocket::ConnectingState) {
socket_->connectToServer(blockServerName_);
}
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
}
// If connected break out of the loop
if (socket_->state() == QLocalSocket::ConnectedState) break;
// If elapsed time since start is longer than the method timeout return
if (time.elapsed() >= timeout) return false;
}
}
// Initialisation message according to the SingleApplication protocol
QByteArray initMsg;
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
writeStream.setVersion(QDataStream::Qt_5_8);
writeStream << blockServerName_.toLatin1();
writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber_;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
#else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif
writeStream << checksum;
return writeConfirmedMessage(static_cast<int>(timeout - time.elapsed()), initMsg);
}
void SingleApplicationPrivate::writeAck(QLocalSocket *sock) {
sock->putChar('\n');
}
bool SingleApplicationPrivate::writeConfirmedMessage(const int timeout, const QByteArray &msg) const {
QElapsedTimer time;
time.start();
// Frame 1: The header indicates the message length that follows
QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly);
headerStream.setVersion(QDataStream::Qt_5_8);
headerStream << static_cast<quint64>(msg.length());
if (!writeConfirmedFrame(static_cast<int>(timeout - time.elapsed()), header)) {
return false;
}
// Frame 2: The message
return writeConfirmedFrame(static_cast<int>(timeout - time.elapsed()), msg);
}
bool SingleApplicationPrivate::writeConfirmedFrame(const int timeout, const QByteArray &msg) const {
socket_->write(msg);
socket_->flush();
bool result = socket_->waitForReadyRead(timeout);
if (result) {
socket_->read(1);
return true;
}
return false;
}
quint16 SingleApplicationPrivate::blockChecksum() const {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
#else
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
#endif
return checksum;
}
qint64 SingleApplicationPrivate::primaryPid() const {
memory_->lock();
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
qint64 pid = instance->primaryPid;
memory_->unlock();
return pid;
}
QString SingleApplicationPrivate::primaryUser() const {
memory_->lock();
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
QByteArray username = instance->primaryUser;
memory_->unlock();
return QString::fromUtf8(username);
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplicationPrivate::slotConnectionEstablished() {
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
const ConnectionInfo &info = connectionMap_[nextConnSocket];
slotClientConnectionClosed(nextConnSocket, info.instanceId);
});
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this, [nextConnSocket, this]() {
connectionMap_.remove(nextConnSocket);
});
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
const ConnectionInfo &info = connectionMap_[nextConnSocket];
switch (info.stage) {
case StageInitHeader:
readMessageHeader(nextConnSocket, StageInitBody);
break;
case StageInitBody:
readInitMessageBody(nextConnSocket);
break;
case StageConnectedHeader:
readMessageHeader(nextConnSocket, StageConnectedBody);
break;
case StageConnectedBody:
this->slotDataAvailable(nextConnSocket, info.instanceId);
break;
default:
break;
};
});
}
void SingleApplicationPrivate::readMessageHeader(QLocalSocket *sock, const SingleApplicationPrivate::ConnectionStage nextStage) {
if (!connectionMap_.contains(sock)) {
return;
}
if (sock->bytesAvailable() < static_cast<qint64>(sizeof(quint64))) {
return;
}
QDataStream headerStream(sock);
headerStream.setVersion(QDataStream::Qt_5_8);
// Read the header to know the message length
quint64 msgLen = 0;
headerStream >> msgLen;
ConnectionInfo &info = connectionMap_[sock];
info.stage = nextStage;
info.msgLen = msgLen;
writeAck(sock);
}
bool SingleApplicationPrivate::isFrameComplete(QLocalSocket *sock) {
if (!connectionMap_.contains(sock)) {
return false;
}
const ConnectionInfo &info = connectionMap_[sock];
return (sock->bytesAvailable() >= static_cast<qint64>(info.msgLen));
}
void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
Q_Q(SingleApplication);
if (!isFrameComplete(sock)) {
return;
}
// Read the message body
QByteArray msgBytes = sock->readAll();
QDataStream readStream(msgBytes);
readStream.setVersion(QDataStream::Qt_5_8);
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast<ConnectionType>(connTypeVal);
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
if (!isValid) {
sock->close();
return;
}
ConnectionInfo &info = connectionMap_[sock];
info.instanceId = instanceId;
info.stage = StageConnectedHeader;
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleApplication::Mode::SecondaryNotification)) {
emit q->instanceStarted();
}
writeAck(sock);
}
void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const quint32 instanceId) {
Q_Q(SingleApplication);
if (!isFrameComplete(dataSocket)) {
return;
}
const QByteArray message = dataSocket->readAll();
writeAck(dataSocket);
ConnectionInfo &info = connectionMap_[dataSocket];
info.stage = StageConnectedHeader;
emit q->receivedMessage(instanceId, message);
}
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
if (closedSocket->bytesAvailable() > 0) {
slotDataAvailable(closedSocket, instanceId);
}
}
void SingleApplicationPrivate::randomSleep() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
#else
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
QThread::msleep(qrand() % 11 + 8);
#endif
}

View File

@@ -1,116 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#ifndef SINGLEAPPLICATION_P_H
#define SINGLEAPPLICATION_P_H
#include <QtGlobal>
#include <QObject>
#include <QString>
#include <QHash>
#include "singleapplication.h"
class QLocalServer;
class QLocalSocket;
class QSharedMemory;
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
char primaryUser[128];
quint16 checksum;
};
struct ConnectionInfo {
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
quint64 msgLen;
quint32 instanceId;
quint8 stage;
};
class SingleApplicationPrivate : public QObject {
Q_OBJECT
public:
enum ConnectionType : quint8 {
InvalidConnection = 0,
NewInstance = 1,
SecondaryInstance = 2,
Reconnect = 3
};
enum ConnectionStage : quint8 {
StageInitHeader = 0,
StageInitBody = 1,
StageConnectedHeader = 2,
StageConnectedBody = 3,
};
Q_DECLARE_PUBLIC(SingleApplication)
explicit SingleApplicationPrivate(SingleApplication *ptr);
~SingleApplicationPrivate() override;
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
bool isFrameComplete(QLocalSocket *sock);
void readMessageHeader(QLocalSocket *socket, const ConnectionStage nextStage);
void readInitMessageBody(QLocalSocket *socket);
void writeAck(QLocalSocket *sock);
bool writeConfirmedFrame(const int timeout, const QByteArray &msg) const;
bool writeConfirmedMessage(const int timeout, const QByteArray &msg) const;
static void randomSleep();
SingleApplication *q_ptr;
QSharedMemory *memory_;
QLocalSocket *socket_;
QLocalServer *server_;
quint32 instanceNumber_;
QString blockServerName_;
SingleApplication::Options options_;
QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
public slots:
void slotConnectionEstablished();
void slotDataAvailable(QLocalSocket*, const quint32);
void slotClientConnectionClosed(QLocalSocket*, const quint32);
};
#endif // SINGLEAPPLICATION_P_H

View File

@@ -1,266 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#include <cstdlib>
#include <limits>
#include <QtGlobal>
#include <QCoreApplication>
#include <QThread>
#include <QSharedMemory>
#include <QLocalSocket>
#include <QByteArray>
#include <QElapsedTimer>
#include <QtDebug>
#include "singlecoreapplication.h"
#include "singlecoreapplication_p.h"
/**
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
* @param argc
* @param argv
* @param allowSecondary Whether to enable secondary instance support
* @param options Optional flags to toggle specific behaviour
* @param timeout Maximum time blocking functions are allowed during app load
*/
SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], const bool allowSecondary, const Options options, const int timeout)
: app_t(argc, argv),
d_ptr(new SingleCoreApplicationPrivate(this)) {
Q_D(SingleCoreApplication);
// Store the current mode of the program
d->options_ = options;
// Generating an application ID used for identifying the shared memory block and QLocalServer
d->genBlockServerName();
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
SingleCoreApplicationPrivate::randomSleep();
#ifdef Q_OS_UNIX
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
d->memory_ = new QSharedMemory(d->blockServerName_);
d->memory_->attach();
delete d->memory_;
#endif
// Guarantee thread safe behaviour with a shared memory block.
d->memory_ = new QSharedMemory(d->blockServerName_);
// Create a shared memory block
if (d->memory_->create(sizeof(InstancesInfo))) {
// Initialize the shared memory block
if (!d->memory_->lock()) {
qCritical() << "SingleCoreApplication: Unable to lock memory block after create.";
abortSafely();
}
d->initializeMemoryBlock();
}
else {
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
// Attempt to attach to the memory segment
if (!d->memory_->attach()) {
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
abortSafely();
}
if (!d->memory_->lock()) {
qCritical() << "SingleCoreApplication: Unable to lock memory block after attach.";
abortSafely();
}
}
else {
qCritical() << "SingleCoreApplication: Unable to create block.";
abortSafely();
}
}
InstancesInfo *instance = static_cast<InstancesInfo*>(d->memory_->data());
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state
forever {
// If the shared memory block's checksum is valid continue
if (d->blockChecksum() == instance->checksum) break;
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
if (time.elapsed() > 5000) {
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
d->initializeMemoryBlock();
}
// Otherwise wait for a random period and try again.
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
if (!d->memory_->unlock()) {
qDebug() << "SingleCoreApplication: Unable to unlock memory for random wait.";
qDebug() << d->memory_->errorString();
}
SingleCoreApplicationPrivate::randomSleep();
if (!d->memory_->lock()) {
qCritical() << "SingleCoreApplication: Unable to lock memory after random wait.";
abortSafely();
}
}
if (!instance->primary) {
d->startPrimary();
if (!d->memory_->unlock()) {
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
qDebug() << d->memory_->errorString();
}
return;
}
// Check if another instance can be started
if (allowSecondary) {
d->startSecondary();
if (d->options_ & Mode::SecondaryNotification) {
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance);
}
if (!d->memory_->unlock()) {
qDebug() << "SingleCoreApplication: Unable to unlock memory after secondary start.";
qDebug() << d->memory_->errorString();
}
return;
}
if (!d->memory_->unlock()) {
qDebug() << "SingleCoreApplication: Unable to unlock memory at end of execution.";
qDebug() << d->memory_->errorString();
}
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance);
delete d;
::exit(EXIT_SUCCESS);
}
SingleCoreApplication::~SingleCoreApplication() {
Q_D(SingleCoreApplication);
delete d;
}
/**
* Checks if the current application instance is primary.
* @return Returns true if the instance is primary, false otherwise.
*/
bool SingleCoreApplication::isPrimary() const {
Q_D(const SingleCoreApplication);
return d->server_ != nullptr;
}
/**
* Checks if the current application instance is secondary.
* @return Returns true if the instance is secondary, false otherwise.
*/
bool SingleCoreApplication::isSecondary() const {
Q_D(const SingleCoreApplication);
return d->server_ == nullptr;
}
/**
* Allows you to identify an instance by returning unique consecutive instance ids.
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
* @return Returns a unique instance id.
*/
quint32 SingleCoreApplication::instanceId() const {
Q_D(const SingleCoreApplication);
return d->instanceNumber_;
}
/**
* Returns the OS PID (Process Identifier) of the process running the primary instance.
* Especially useful when SingleCoreApplication is coupled with OS. specific APIs.
* @return Returns the primary instance PID.
*/
qint64 SingleCoreApplication::primaryPid() const {
Q_D(const SingleCoreApplication);
return d->primaryPid();
}
/**
* Returns the username the primary instance is running as.
* @return Returns the username the primary instance is running as.
*/
QString SingleCoreApplication::primaryUser() const {
Q_D(const SingleCoreApplication);
return d->primaryUser();
}
/**
* Returns the username the current instance is running as.
* @return Returns the username the current instance is running as.
*/
QString SingleCoreApplication::currentUser() const {
return SingleCoreApplicationPrivate::getUsername();
}
/**
* Sends message to the Primary Instance.
* @param message The message to send.
* @param timeout the maximum timeout in milliseconds for blocking functions.
* @return true if the message was sent successfully, false otherwise.
*/
bool SingleCoreApplication::sendMessage(const QByteArray &message, const int timeout) {
Q_D(SingleCoreApplication);
// Nobody to connect to
if (isPrimary()) return false;
// Make sure the socket is connected
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect)) {
return false;
}
return d->writeConfirmedMessage(timeout, message);
}
/**
* Cleans up the shared memory block and exits with a failure.
* This function halts program execution.
*/
void SingleCoreApplication::abortSafely() {
Q_D(SingleCoreApplication);
qCritical() << "SingleCoreApplication: " << d->memory_->error() << d->memory_->errorString();
delete d;
::exit(EXIT_FAILURE);
}

View File

@@ -1,152 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#ifndef SINGLECOREAPPLICATION_H
#define SINGLECOREAPPLICATION_H
#include <QtGlobal>
#include <QCoreApplication>
#include <QFlags>
#include <QByteArray>
class SingleCoreApplicationPrivate;
/**
* @brief The SingleCoreApplication class handles multiple instances of the same Application
* @see QCoreApplication
*/
class SingleCoreApplication : public QCoreApplication { // clazy:exclude=ctor-missing-parent-argument
Q_OBJECT
using app_t = QCoreApplication;
public:
/**
* @brief Mode of operation of SingleCoreApplication.
* Whether the block should be user-wide or system-wide and whether the
* primary instance should be notified when a secondary instance had been
* started.
* @note Operating system can restrict the shared memory blocks to the same
* user, in which case the User/System modes will have no effect and the
* block will be user wide.
* @enum
*/
enum Mode {
User = 1 << 0,
System = 1 << 1,
SecondaryNotification = 1 << 2,
ExcludeAppVersion = 1 << 3,
ExcludeAppPath = 1 << 4
};
Q_DECLARE_FLAGS(Options, Mode)
/**
* @brief Intitializes a SingleCoreApplication instance with argc command line
* arguments in argv
* @arg {int &} argc - Number of arguments in argv
* @arg {const char *[]} argv - Supplied command line arguments
* @arg {bool} allowSecondary - Whether to start the instance as secondary
* if there is already a primary instance.
* @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied
* User wide or System wide.
* @arg {int} timeout - Timeout to wait in milliseconds.
* @note argc and argv may be changed as Qt removes arguments that it
* recognizes
* @note Mode::SecondaryNotification only works if set on both the primary
* instance and the secondary instance.
* @note The timeout is just a hint for the maximum time of blocking
* operations. It does not guarantee that the SingleCoreApplication
* initialisation will be completed in given time, though is a good hint.
* Usually 4*timeout would be the worst case (fail) scenario.
*/
explicit SingleCoreApplication(int &argc, char *argv[], const bool allowSecondary = false, const Options options = Mode::User, const int timeout = 1000);
~SingleCoreApplication() override;
/**
* @brief Returns if the instance is the primary instance
* @returns {bool}
*/
bool isPrimary() const;
/**
* @brief Returns if the instance is a secondary instance
* @returns {bool}
*/
bool isSecondary() const;
/**
* @brief Returns a unique identifier for the current instance
* @returns {qint32}
*/
quint32 instanceId() const;
/**
* @brief Returns the process ID (PID) of the primary instance
* @returns {qint64}
*/
qint64 primaryPid() const;
/**
* @brief Returns the username of the user running the primary instance
* @returns {QString}
*/
QString primaryUser() const;
/**
* @brief Returns the username of the current user
* @returns {QString}
*/
QString currentUser() const;
/**
* @brief Sends a message to the primary instance. Returns true on success.
* @param {int} timeout - Timeout for connecting
* @returns {bool}
* @note sendMessage() will return false if invoked from the primary
* instance.
*/
bool sendMessage(const QByteArray &message, const int timeout = 1000);
signals:
void instanceStarted();
void receivedMessage(quint32 instanceId, QByteArray message);
private:
SingleCoreApplicationPrivate *d_ptr;
Q_DECLARE_PRIVATE(SingleCoreApplication)
void abortSafely();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
#endif // SINGLECOREAPPLICATION_H

View File

@@ -1,526 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#include "config.h"
#include <QtGlobal>
#include <cstdlib>
#include <cstddef>
#ifdef Q_OS_UNIX
# include <unistd.h>
# include <sys/types.h>
# include <pwd.h>
#endif
#ifdef Q_OS_WIN
# ifndef NOMINMAX
# define NOMINMAX 1
# endif
# include <windows.h>
# include <lmcons.h>
#endif
#include <QObject>
#include <QThread>
#include <QIODevice>
#include <QSharedMemory>
#include <QByteArray>
#include <QDataStream>
#include <QCryptographicHash>
#include <QLocalServer>
#include <QLocalSocket>
#include <QElapsedTimer>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QRandomGenerator>
#else
# include <QDateTime>
#endif
#include "singlecoreapplication.h"
#include "singlecoreapplication_p.h"
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *ptr)
: q_ptr(ptr),
memory_(nullptr),
socket_(nullptr),
server_(nullptr),
instanceNumber_(-1) {}
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() {
if (socket_ != nullptr) {
socket_->close();
delete socket_;
socket_ = nullptr;
}
if (memory_ != nullptr) {
memory_->lock();
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
if (server_ != nullptr) {
server_->close();
delete server_;
instance->primary = false;
instance->primaryPid = -1;
instance->primaryUser[0] = '\0';
instance->checksum = blockChecksum();
}
memory_->unlock();
delete memory_;
memory_ = nullptr;
}
}
QString SingleCoreApplicationPrivate::getUsername() {
#ifdef Q_OS_UNIX
QString username;
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
struct passwd *pw = getpwuid(geteuid());
if (pw) {
username = QString::fromLocal8Bit(pw->pw_name);
}
#endif
if (username.isEmpty()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
username = qEnvironmentVariable("USER");
#else
username = QString::fromLocal8Bit(qgetenv("USER"));
#endif
}
return username;
#endif
#ifdef Q_OS_WIN
wchar_t username[UNLEN + 1];
// Specifies size of the buffer on input
DWORD usernameLength = UNLEN + 1;
if (GetUserNameW(username, &usernameLength)) {
return QString::fromWCharArray(username);
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
return qEnvironmentVariable("USERNAME");
#else
return QString::fromLocal8Bit(qgetenv("USERNAME"));
#endif
#endif
}
void SingleCoreApplicationPrivate::genBlockServerName() {
QCryptographicHash appData(QCryptographicHash::Sha256);
appData.addData("SingleApplication");
appData.addData(SingleCoreApplication::app_t::applicationName().toUtf8());
appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8());
appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8());
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppVersion)) {
appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8());
}
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppPath)) {
#if defined(Q_OS_UNIX)
const QByteArray appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toUtf8());
}
else {
appData.addData(appImagePath);
};
#elif defined(Q_OS_WIN)
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8());
#else
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toUtf8());
#endif
}
// User level block requires a user specific data in the hash
if (options_ & SingleCoreApplication::Mode::User) {
appData.addData(getUsername().toUtf8());
}
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
blockServerName_ = appData.result().toBase64().replace("/", "_");
}
void SingleCoreApplicationPrivate::initializeMemoryBlock() const {
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
instance->primary = false;
instance->secondary = 0;
instance->primaryPid = -1;
instance->primaryUser[0] = '\0';
instance->checksum = blockChecksum();
}
void SingleCoreApplicationPrivate::startPrimary() {
// Reset the number of connections
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
instance->primary = true;
instance->primaryPid = QCoreApplication::applicationPid();
qstrncpy(instance->primaryUser, getUsername().toUtf8().data(), sizeof(instance->primaryUser));
instance->checksum = blockChecksum();
instanceNumber_ = 0;
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer(blockServerName_);
server_ = new QLocalServer();
// Restrict access to the socket according to the SingleCoreApplication::Mode::User flag on User level or no restrictions
if (options_ & SingleCoreApplication::Mode::User) {
server_->setSocketOptions(QLocalServer::UserAccessOption);
}
else {
server_->setSocketOptions(QLocalServer::WorldAccessOption);
}
server_->listen(blockServerName_);
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
}
void SingleCoreApplicationPrivate::startSecondary() {
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
instance->secondary += 1;
instance->checksum = blockChecksum();
instanceNumber_ = instance->secondary;
}
bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const ConnectionType connectionType) {
QElapsedTimer time;
time.start();
// Connect to the Local Server of the Primary Instance if not already connected.
if (socket_ == nullptr) {
socket_ = new QLocalSocket();
}
if (socket_->state() == QLocalSocket::ConnectedState) return true;
if (socket_->state() != QLocalSocket::ConnectedState) {
forever {
randomSleep();
if (socket_->state() != QLocalSocket::ConnectingState) {
socket_->connectToServer(blockServerName_);
}
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
}
// If connected break out of the loop
if (socket_->state() == QLocalSocket::ConnectedState) break;
// If elapsed time since start is longer than the method timeout return
if (time.elapsed() >= timeout) return false;
}
}
// Initialisation message according to the SingleCoreApplication protocol
QByteArray initMsg;
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
writeStream.setVersion(QDataStream::Qt_5_8);
writeStream << blockServerName_.toLatin1();
writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber_;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
#else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif
writeStream << checksum;
return writeConfirmedMessage(static_cast<int>(timeout - time.elapsed()), initMsg);
}
void SingleCoreApplicationPrivate::writeAck(QLocalSocket *sock) {
sock->putChar('\n');
}
bool SingleCoreApplicationPrivate::writeConfirmedMessage(const int timeout, const QByteArray &msg) const {
QElapsedTimer time;
time.start();
// Frame 1: The header indicates the message length that follows
QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly);
headerStream.setVersion(QDataStream::Qt_5_8);
headerStream << static_cast<quint64>(msg.length());
if (!writeConfirmedFrame(static_cast<int>(timeout - time.elapsed()), header)) {
return false;
}
// Frame 2: The message
return writeConfirmedFrame(static_cast<int>(timeout - time.elapsed()), msg);
}
bool SingleCoreApplicationPrivate::writeConfirmedFrame(const int timeout, const QByteArray &msg) const {
socket_->write(msg);
socket_->flush();
bool result = socket_->waitForReadyRead(timeout);
if (result) {
socket_->read(1);
return true;
}
return false;
}
quint16 SingleCoreApplicationPrivate::blockChecksum() const {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
#else
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
#endif
return checksum;
}
qint64 SingleCoreApplicationPrivate::primaryPid() const {
memory_->lock();
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
qint64 pid = instance->primaryPid;
memory_->unlock();
return pid;
}
QString SingleCoreApplicationPrivate::primaryUser() const {
memory_->lock();
InstancesInfo *instance = static_cast<InstancesInfo*>(memory_->data());
QByteArray username = instance->primaryUser;
memory_->unlock();
return QString::fromUtf8(username);
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleCoreApplicationPrivate::slotConnectionEstablished() {
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
connectionMap_.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
const ConnectionInfo &info = connectionMap_[nextConnSocket];
slotClientConnectionClosed(nextConnSocket, info.instanceId);
});
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this, [nextConnSocket, this]() {
connectionMap_.remove(nextConnSocket);
});
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
const ConnectionInfo &info = connectionMap_[nextConnSocket];
switch (info.stage) {
case StageInitHeader:
readMessageHeader(nextConnSocket, StageInitBody);
break;
case StageInitBody:
readInitMessageBody(nextConnSocket);
break;
case StageConnectedHeader:
readMessageHeader(nextConnSocket, StageConnectedBody);
break;
case StageConnectedBody:
this->slotDataAvailable(nextConnSocket, info.instanceId);
break;
default:
break;
};
});
}
void SingleCoreApplicationPrivate::readMessageHeader(QLocalSocket *sock, SingleCoreApplicationPrivate::ConnectionStage nextStage) {
if (!connectionMap_.contains(sock)) {
return;
}
if (sock->bytesAvailable() < static_cast<qint64>(sizeof(quint64))) {
return;
}
QDataStream headerStream(sock);
headerStream.setVersion(QDataStream::Qt_5_8);
// Read the header to know the message length
quint64 msgLen = 0;
headerStream >> msgLen;
ConnectionInfo &info = connectionMap_[sock];
info.stage = nextStage;
info.msgLen = msgLen;
writeAck(sock);
}
bool SingleCoreApplicationPrivate::isFrameComplete(QLocalSocket *sock) {
if (!connectionMap_.contains(sock)) {
return false;
}
ConnectionInfo &info = connectionMap_[sock];
return (sock->bytesAvailable() >= static_cast<qint64>(info.msgLen));
}
void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
Q_Q(SingleCoreApplication);
if (!isFrameComplete(sock)) {
return;
}
// Read the message body
QByteArray msgBytes = sock->readAll();
QDataStream readStream(msgBytes);
readStream.setVersion(QDataStream::Qt_5_8);
// server name
QByteArray latin1Name;
readStream >> latin1Name;
// connection type
ConnectionType connectionType = InvalidConnection;
quint8 connTypeVal = InvalidConnection;
readStream >> connTypeVal;
connectionType = static_cast<ConnectionType>(connTypeVal);
// instance id
quint32 instanceId = 0;
readStream >> instanceId;
// checksum
quint16 msgChecksum = 0;
readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
if (!isValid) {
sock->close();
return;
}
ConnectionInfo &info = connectionMap_[sock];
info.instanceId = instanceId;
info.stage = StageConnectedHeader;
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleCoreApplication::Mode::SecondaryNotification)) {
emit q->instanceStarted();
}
writeAck(sock);
}
void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const quint32 instanceId) {
Q_Q(SingleCoreApplication);
if (!isFrameComplete(dataSocket)) {
return;
}
const QByteArray message = dataSocket->readAll();
writeAck(dataSocket);
ConnectionInfo &info = connectionMap_[dataSocket];
info.stage = StageConnectedHeader;
emit q->receivedMessage(instanceId, message);
}
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
if (closedSocket->bytesAvailable() > 0) {
slotDataAvailable(closedSocket, instanceId);
}
}
void SingleCoreApplicationPrivate::randomSleep() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QThread::msleep(QRandomGenerator::global()->bounded(8U, 18U));
#else
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
QThread::msleep(qrand() % 11 + 8);
#endif
}

View File

@@ -1,116 +0,0 @@
// The MIT License (MIT)
//
// Copyright (c) Itay Grudev 2015 - 2020
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// W A R N I N G !!!
// -----------------
//
// This is a modified version of SingleApplication,
// The original version is at:
//
// https://github.com/itay-grudev/SingleApplication
//
//
#ifndef SINGLECOREAPPLICATION_P_H
#define SINGLECOREAPPLICATION_P_H
#include <QtGlobal>
#include <QObject>
#include <QString>
#include <QHash>
#include "singlecoreapplication.h"
class QLocalServer;
class QLocalSocket;
class QSharedMemory;
struct InstancesInfo {
bool primary;
quint32 secondary;
qint64 primaryPid;
char primaryUser[128];
quint16 checksum;
};
struct ConnectionInfo {
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
quint64 msgLen;
quint32 instanceId;
quint8 stage;
};
class SingleCoreApplicationPrivate : public QObject {
Q_OBJECT
public:
enum ConnectionType : quint8 {
InvalidConnection = 0,
NewInstance = 1,
SecondaryInstance = 2,
Reconnect = 3
};
enum ConnectionStage : quint8 {
StageInitHeader = 0,
StageInitBody = 1,
StageConnectedHeader = 2,
StageConnectedBody = 3,
};
Q_DECLARE_PUBLIC(SingleCoreApplication)
explicit SingleCoreApplicationPrivate(SingleCoreApplication *ptr);
~SingleCoreApplicationPrivate() override;
static QString getUsername();
void genBlockServerName();
void initializeMemoryBlock() const;
void startPrimary();
void startSecondary();
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
quint16 blockChecksum() const;
qint64 primaryPid() const;
QString primaryUser() const;
bool isFrameComplete(QLocalSocket *sock);
void readMessageHeader(QLocalSocket *socket, const ConnectionStage nextStage);
void readInitMessageBody(QLocalSocket *socket);
void writeAck(QLocalSocket *sock);
bool writeConfirmedFrame(const int timeout, const QByteArray &msg) const;
bool writeConfirmedMessage(const int timeout, const QByteArray &msg) const;
static void randomSleep();
SingleCoreApplication *q_ptr;
QSharedMemory *memory_;
QLocalSocket *socket_;
QLocalServer *server_;
quint32 instanceNumber_;
QString blockServerName_;
SingleCoreApplication::Options options_;
QHash<QLocalSocket*, ConnectionInfo> connectionMap_;
public slots:
void slotConnectionEstablished();
void slotDataAvailable(QLocalSocket*, const quint32);
void slotClientConnectionClosed(QLocalSocket*, const quint32);
};
#endif // SINGLECOREAPPLICATION_P_H

View File

@@ -1,7 +1,13 @@
cmake_minimum_required(VERSION 3.7)
project(strawberry)
cmake_minimum_required(VERSION 3.0)
cmake_policy(SET CMP0054 NEW)
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif()
include(CheckCXXCompilerFlag)
include(CheckCXXSourceRuns)
@@ -12,13 +18,13 @@ include(cmake/Summary.cmake)
include(cmake/OptionalSource.cmake)
include(cmake/ParseArguments.cmake)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LINUX ON)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(FREEBSD ON)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
set(OPENBSD ON)
endif()
@@ -32,14 +38,22 @@ endif()
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
if(MSVC)
set(CMAKE_C_STANDARD 99)
else()
set(CMAKE_C_STANDARD 11)
endif()
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC)
list(APPEND COMPILE_OPTIONS /std:c++17 /MP)
list(APPEND COMPILE_OPTIONS /MP)
else()
list(APPEND COMPILE_OPTIONS
$<$<COMPILE_LANGUAGE:C>:-std=c99>
$<$<COMPILE_LANGUAGE:C>:-std=c11>
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
-Wall
-Wextra
@@ -58,26 +72,24 @@ else()
-Wformat=2
-Wdisabled-optimization
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
$<$<COMPILE_LANGUAGE:CXX>:-Wold-style-cast>
)
endif()
option(BUILD_WERROR "Build with -Werror" OFF)
if(BUILD_WERROR)
list(APPEND COMPILE_OPTIONS -Werror)
option(BUILD_WERROR "Build with -Werror" OFF)
if(BUILD_WERROR)
list(APPEND COMPILE_OPTIONS -Werror)
endif()
endif()
add_compile_options(${COMPILE_OPTIONS})
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
if(CMAKE_BUILD_TYPE MATCHES "Release")
add_definitions(-DNDEBUG)
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()
if(APPLE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
option(USE_RPATH "Use RPATH" APPLE)
if(USE_RPATH)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()
find_program(CCACHE_EXECUTABLE NAMES ccache)
@@ -87,18 +99,29 @@ if(CCACHE_EXECUTABLE)
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
endif()
option(USE_ICU "Use ICU" ON)
find_package(PkgConfig REQUIRED)
find_package(Boost REQUIRED)
find_package(Threads)
find_package(Threads REQUIRED)
find_package(Backtrace)
if(Backtrace_FOUND)
set(HAVE_BACKTRACE ON)
endif()
find_package(Iconv)
find_package(GnuTLS REQUIRED)
find_package(Protobuf REQUIRED)
if(NOT Protobuf_PROTOC_EXECUTABLE)
message(FATAL_ERROR "Missing protobuf compiler.")
find_package(Boost REQUIRED)
if(USE_ICU)
find_package(ICU COMPONENTS uc i18n REQUIRED)
if(ICU_FOUND)
set(HAVE_ICU ON)
endif()
else()
find_package(Iconv)
endif()
find_package(Protobuf CONFIG)
if(NOT Protobuf_FOUND)
find_package(Protobuf REQUIRED)
endif()
if(NOT TARGET protobuf::protoc)
message(FATAL_ERROR "Missing Protobuf compiler.")
endif()
if(LINUX)
find_package(ALSA REQUIRED)
@@ -139,84 +162,52 @@ find_package(FFTW3)
find_package(GTest)
find_library(GMOCK_LIBRARY gmock)
option(QT_VERSION_MAJOR "Qt version to use (5 or 6)")
option(BUILD_WITH_QT5 "Build with Qt 5" OFF)
option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
if(WITH_QT6)
set(BUILD_WITH_QT6 ON)
endif()
if(QT_MAJOR_VERSION)
set(QT_VERSION_MAJOR ${QT_MAJOR_VERSION})
if(BUILD_WITH_QT6)
set(QT_VERSION_MAJOR 6)
elseif(BUILD_WITH_QT5)
set(QT_VERSION_MAJOR 5)
endif()
if(QT_VERSION_MAJOR)
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
if(NOT QT_VERSION_MAJOR)
message(STATUS "QT_VERSION_MAJOR, BUILD_WITH_QT5 or BUILD_WITH_QT6 not set, detecting Qt version...")
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
endif()
if(QT_VERSION_MAJOR EQUAL 6)
set(QT_MIN_VERSION 6.0)
elseif(QT_VERSION_MAJOR EQUAL 5)
set(QT_MIN_VERSION 5.12)
else()
message(FATAL_ERROR "Invalid QT_VERSION_MAJOR.")
endif()
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
set(QT_OPTIONAL_COMPONENTS LinguistTools Test)
if(DBUS_FOUND AND NOT WIN32)
list(APPEND QT_COMPONENTS DBus)
endif()
set(QT_OPTIONAL_COMPONENTS Test)
set(QT_MIN_VERSION 5.9)
if(BUILD_WITH_QT6 OR QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
elseif(BUILD_WITH_QT5 OR QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
else()
# Automatically detect Qt version.
find_package(QT NAMES Qt6 Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED)
if(QT_FOUND AND QT_VERSION_MAJOR EQUAL 6)
set(BUILD_WITH_QT6 ON CACHE BOOL "" FORCE)
set(QT_VERSION_MAJOR 6 CACHE STRING "" FORCE)
elseif(QT_FOUND AND QT_VERSION_MAJOR EQUAL 5)
set(BUILD_WITH_QT5 ON CACHE BOOL "" FORCE)
set(QT_VERSION_MAJOR 5 CACHE STRING "" FORCE)
else()
message(FATAL_ERROR "Missing Qt.")
endif()
if(X11_FOUND AND QT_VERSION_MAJOR EQUAL 5)
list(APPEND QT_COMPONENTS X11Extras)
endif()
if(QT_VERSION_MAJOR)
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
endif()
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} COMPONENTS ${QT_COMPONENTS} REQUIRED OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
if(X11_FOUND AND BUILD_WITH_QT5)
list(APPEND QT_OPTIONAL_COMPONENTS X11Extras)
endif()
find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_OPTIONAL_COMPONENTS})
set(QtCore_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
set(QtConcurrent_LIBRARIES Qt${QT_VERSION_MAJOR}::Concurrent)
set(QtGui_LIBRARIES Qt${QT_VERSION_MAJOR}::Gui)
set(QtWidgets_LIBRARIES Qt${QT_VERSION_MAJOR}::Widgets)
set(QtNetwork_LIBRARIES Qt${QT_VERSION_MAJOR}::Network)
set(QtSql_LIBRARIES Qt${QT_VERSION_MAJOR}::Sql)
set(QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Sql)
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
set(QtDBus_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_VERSION_MAJOR}::qdbusxml2cpp LOCATION)
endif()
if(BUILD_WITH_QT5 AND Qt5X11Extras_FOUND)
set(HAVE_X11EXTRAS ON)
set(QtX11Extras_LIBRARIES Qt5::X11Extras)
list(APPEND QT_LIBRARIES Qt5::X11Extras)
endif()
if(Qt${QT_VERSION_MAJOR}Test_FOUND)
set(QtTest_LIBRARIES Qt${QT_VERSION_MAJOR}::Test)
endif()
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS LinguistTools CONFIG)
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt${QT_VERSION_MAJOR}::lconvert)
get_target_property(QT_LCONVERT_EXECUTABLE Qt${QT_VERSION_MAJOR}::lconvert LOCATION)
endif()
if(Qt${QT_VERSION_MAJOR}X11Extras_FOUND)
set(HAVE_X11EXTRAS ON)
endif()
if(BUILD_WITH_QT5 AND Qt5Core_VERSION VERSION_LESS 5.15.0)
if(QT_VERSION_MAJOR EQUAL 5 AND Qt5Core_VERSION VERSION_LESS 5.15.0)
macro(qt_add_resources)
qt5_add_resources(${ARGN})
endmacro()
@@ -255,6 +246,24 @@ if(X11_FOUND)
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif()
# Check for QX11Application (Qt 6 compiled with XCB).
if(QT_VERSION_MAJOR EQUAL 6 AND Qt6Gui_VERSION VERSION_GREATER_EQUAL 6.2.0)
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui)
check_cxx_source_compiles("
#include <QGuiApplication>
int main() {
(void)qApp->nativeInterface<QNativeInterface::QX11Application>();
return 0;
}
"
HAVE_QX11APPLICATION
)
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
endif()
endif(X11_FOUND)
option(USE_TAGLIB "Build with TagLib" OFF)
@@ -284,39 +293,60 @@ if(USE_TAGPARSER)
pkg_check_modules(TAGPARSER REQUIRED tagparser)
endif()
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
message(FATAL_ERROR "You need either TagLib or TagParser!")
endif()
# SingleApplication
add_subdirectory(3rdparty/singleapplication)
set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/singleapplication)
set(SINGLEAPPLICATION_LIBRARIES singleapplication)
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
if(QT_VERSION_MAJOR EQUAL 5)
set(KDSINGLEAPPLICATION_NAME "KDSingleApplication")
else()
set(KDSINGLEAPPLICATION_NAME "KDSingleApplication-qt${QT_VERSION_MAJOR}")
endif()
find_package(${KDSINGLEAPPLICATION_NAME})
if(TARGET KDAB::kdsingleapplication)
if(QT_VERSION_MAJOR EQUAL 5)
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication_VERSION}")
elseif(QT_VERSION_MAJOR EQUAL 6)
set(KDSINGLEAPPLICATION_VERSION "${KDSingleApplication-qt6_VERSION}")
endif()
if(KDSINGLEAPPLICATION_VERSION VERSION_GREATER_EQUAL 1.0.95)
set(HAVE_KDSINGLEAPPLICATION_OPTIONS ON)
else()
set(HAVE_KDSINGLEAPPLICATION_OPTIONS OFF)
endif()
message(STATUS "Using system KDSingleApplication (Version ${KDSINGLEAPPLICATION_VERSION})")
set(SINGLEAPPLICATION_LIBRARIES KDAB::kdsingleapplication)
else()
message(STATUS "Using 3rdparty KDSingleApplication")
set(HAVE_KDSINGLEAPPLICATION_OPTIONS ON)
add_subdirectory(3rdparty/kdsingleapplication)
set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/kdsingleapplication/KDSingleApplication/src)
set(SINGLEAPPLICATION_LIBRARIES kdsingleapplication)
add_definitions(-DKDSINGLEAPPLICATION_STATIC_BUILD)
endif()
if(APPLE)
add_subdirectory(3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/SPMediaKeyTap)
set(SPMEDIAKEYTAP_LIBRARIES SPMediaKeyTap)
add_subdirectory(3rdparty/macdeployqt)
add_subdirectory(ext/macdeploycheck)
endif()
if(WIN32)
if(BUILD_WITH_QT6)
pkg_check_modules(QTSPARKLE qtsparkle-qt6)
else()
pkg_check_modules(QTSPARKLE qtsparkle-qt5)
endif()
pkg_check_modules(QTSPARKLE qtsparkle-qt${QT_VERSION_MAJOR})
if(QTSPARKLE_FOUND)
set(HAVE_QTSPARKLE ON)
endif()
endif()
if(WIN32 AND MSVC)
if(WIN32)
add_subdirectory(3rdparty/getopt)
set(GETOPT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/getopt)
set(GETOPT_LIBRARIES getopt)
add_definitions(-DSTATIC_GETOPT -D_UNICODE)
endif()
if(WIN32 AND NOT MSVC)
@@ -340,6 +370,7 @@ optional_component(LIBPULSE ON "PulseAudio integration"
optional_component(DBUS ON "D-Bus support"
DEPENDS "D-Bus" DBUS_FOUND
DEPENDS "Qt D-Bus" Qt${QT_VERSION_MAJOR}DBus_FOUND
)
optional_component(GSTREAMER ON "Engine: GStreamer backend"
@@ -357,15 +388,15 @@ optional_component(VLC ON "Engine: VLC backend"
optional_component(SONGFINGERPRINTING ON "Song fingerprinting and tracking"
DEPENDS "chromaprint" CHROMAPRINT_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
DEPENDS "gstreamer" HAVE_GSTREAMER
)
optional_component(MUSICBRAINZ ON "MusicBrainz integration"
DEPENDS "chromaprint" CHROMAPRINT_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
DEPENDS "gstreamer" HAVE_GSTREAMER
)
if(X11_FOUND OR HAVE_DBUS OR APPLE OR WIN32)
if(X11_FOUND OR (HAVE_DBUS AND Qt${QT_VERSION_MAJOR}DBus_FOUND) OR APPLE OR WIN32)
set(HAVE_GLOBALSHORTCUTS_SUPPORT ON)
endif()
@@ -373,25 +404,22 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
)
if(BUILD_WITH_QT6 AND (Qt6Core_VERSION VERSION_EQUAL 6.2.0 OR Qt6Core_VERSION VERSION_GREATER 6.2.0))
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts" DEPENDS "X11" X11_FOUND)
else()
if(HAVE_X11EXTRAS OR HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
set(HAVE_X11EXTRAS_OR_QPA_QPLATFORMNATIVEINTERFACE_H ON)
endif()
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
DEPENDS "X11" X11_FOUND
DEPENDS "Qt >= 6.2, X11Extras or qpa/qplatformnativeinterface.h header" HAVE_X11EXTRAS_OR_QPA_QPLATFORMNATIVEINTERFACE_H
)
if(HAVE_QX11APPLICATION OR HAVE_X11EXTRAS OR HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
set(X11_GLOBALSHORTCUTS_REQUIREMENT_FOUND ON)
endif()
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
DEPENDS "X11" X11_FOUND
DEPENDS "QX11Application, X11Extras or qpa/qplatformnativeinterface.h header" X11_GLOBALSHORTCUTS_REQUIREMENT_FOUND
)
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" LIBCDIO_FOUND
DEPENDS "gstreamer" GSTREAMER_FOUND
DEPENDS "gstreamer" HAVE_GSTREAMER
)
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
DEPENDS "D-Bus support" DBUS_FOUND
DEPENDS "D-Bus" DBUS_FOUND
DEPENDS "Qt D-Bus" Qt${QT_VERSION_MAJOR}DBus_FOUND
)
optional_component(GIO ON "Devices: GIO device backend"
@@ -401,7 +429,7 @@ optional_component(GIO ON "Devices: GIO device backend"
optional_component(GIO_UNIX ON "Devices: GIO device backend (Unix support)"
DEPENDS "libgio-unix" GIO_UNIX_FOUND
DEPENDS "Unix" "UNIX"
DEPENDS "Unix or Windows" "NOT APPLE"
)
optional_component(LIBGPOD ON "Devices: iPod classic support"
@@ -413,48 +441,38 @@ optional_component(LIBMTP ON "Devices: MTP support"
DEPENDS "libmtp" LIBMTP_FOUND
)
if(BUILD_WITH_QT6)
optional_component(TRANSLATIONS ON "Translations"
DEPENDS "gettext" GETTEXT_FOUND
DEPENDS "Qt6LinguistTools" Qt6LinguistTools_FOUND
)
else()
optional_component(TRANSLATIONS ON "Translations"
DEPENDS "gettext" GETTEXT_FOUND
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
)
endif()
optional_component(TRANSLATIONS ON "Translations"
DEPENDS "gettext" GETTEXT_FOUND
DEPENDS "Qt LinguistTools" Qt${QT_VERSION_MAJOR}LinguistTools_FOUND
)
option(INSTALL_TRANSLATIONS "Install translations" OFF)
optional_component(SUBSONIC ON "Subsonic support")
optional_component(TIDAL ON "Tidal support")
optional_component(QOBUZ ON "Qobuz support")
optional_component(SUBSONIC ON "Streaming: Subsonic")
optional_component(TIDAL ON "Streaming: Tidal")
optional_component(QOBUZ ON "Streaming: Qobuz")
optional_component(MOODBAR ON "Moodbar"
DEPENDS "fftw3" FFTW3_FOUND
DEPENDS "gstreamer" HAVE_GSTREAMER
)
if(APPLE OR WIN32)
option(USE_BUNDLE "Bundle dependencies" ON)
else()
option(USE_BUNDLE "Bundle dependencies" OFF)
endif()
optional_component(EBUR128 ON "EBU R 128 loudness normalization"
DEPENDS "libebur128" LIBEBUR128_FOUND
DEPENDS "gstreamer" HAVE_GSTREAMER
)
if(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
if(LINUX)
set(USE_BUNDLE_DIR "../plugins")
endif()
if(APPLE)
set(USE_BUNDLE_DIR "../PlugIns")
endif()
if(APPLE OR WIN32)
set(USE_BUNDLE_DEFAULT ON)
else()
set(USE_BUNDLE_DEFAULT OFF)
endif()
option(USE_BUNDLE "Bundle dependencies" ${USE_BUNDLE_DEFAULT})
if(NOT CMAKE_CROSSCOMPILING)
# Check that we have Qt with sqlite driver
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
set(CMAKE_REQUIRED_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql)
check_cxx_source_runs("
#include <QSqlDatabase>
#include <QSqlQuery>
@@ -486,16 +504,22 @@ if(NOT CMAKE_CROSSCOMPILING)
SQLITE_FTS5_TEST
)
endif()
unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_LIBRARIES)
endif()
# Set up definitions
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
add_definitions(${QT_DEFINITIONS})
add_definitions(-DQT_STRICT_ITERATORS)
add_definitions(-DQT_USE_QSTRINGBUILDER)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DQT_NO_CAST_TO_ASCII)
add_definitions(
-DBOOST_BIND_NO_PLACEHOLDERS
-DQT_STRICT_ITERATORS
-DQT_NO_CAST_FROM_BYTEARRAY
-DQT_USE_QSTRINGBUILDER
-DQT_NO_URL_CAST_FROM_STRING
-DQT_NO_CAST_TO_ASCII
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
-DQT_NO_FOREACH
)
if(WIN32)
add_definitions(-DUNICODE)
@@ -514,10 +538,14 @@ if(HAVE_MOODBAR)
add_subdirectory(ext/gstmoodbar)
endif()
if(GTest_FOUND AND GMOCK_LIBRARY AND QtTest_LIBRARIES)
if(GTest_FOUND AND GMOCK_LIBRARY AND Qt${QT_VERSION_MAJOR}Test_FOUND)
add_subdirectory(tests)
endif()
if(LINUX AND LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
add_subdirectory(debian)
endif()
# Uninstall support
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
@@ -526,7 +554,7 @@ add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/c
# Show a summary of what we have enabled
summary_show()
if(NOT HAVE_GSTREAMER AND NOT HAVE_VLC)
message(FATAL_ERROR "You need to have either GStreamer or VLC to compile!")
message(FATAL_ERROR "You need to have either GStreamer or libvlc to compile!")
elseif(NOT HAVE_GSTREAMER)
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
endif()

112
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,112 @@
# Contribution guidelines
Strawberry is an free and open-source project, it is possible and encouraged to participate in the development.
You can also participate by answering questions, reporting bugs or helping with documentation.
## Submitting a pull request
You should start by creating a fork of the Strawberry repository using the GitHub
fork button, after that you can clone the repository from your fork.
Replace "username" with your own.
### Clone the repository
git clone git@github.com:username/strawberry.git
cd strawberry
### Setup the remote
git remote add upstream git@github.com:strawberrymusicplayer/strawberry.git
### Create a new branch
This creates a new branch from the master branch that you use for specific
changes.
git checkout -b your-branch
### Stage changes
Once you've finished working on a specific change, stage the changes for
a specific commit.
Always keep your commits relevant to the pull request, and each commit as
small as possible.
git add -p
### Commit changes
git commit
### 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
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.
An example of the expected format for git commit messages is as follows:
```
class: Short explanation of the commit
Longer explanation explaining exactly what's changed, why it's changed,
and what bugs were fixed.
Fixes #1234
```
### Push the changes to GitHub
Once you've finished working on the changes, push the branch
to the Git repository and open a new pull request.
git push origin your-branch
### Update your fork's master branch
git checkout master
git pull --rebase origin master
git fetch upstream
git merge upstream/master
git push origin master
### Update your branch
git checkout your-branch
git fetch upstream
git rebase upstream/master
git push origin your-branch --force-with-lease
### Rebase your branch
If you need fix any issues with your commits, you need to rebase your
branch to squash any commits, or to change the commit message.
git checkout your-branch
git log
git rebase -i commit_sha~
git push origin your-branch --force-with-lease
### Delete your fork
If you do not plan to work more on Strawberry, please delete your fork from GitHub
once the pull requests are merged.

319
Changelog
View File

@@ -2,6 +2,317 @@ Strawberry Music Player
=======================
ChangeLog
Version 1.0.22 (2023.12.09):
Bugfixes:
* Fixed KDSingleApplication cmake version check.
* Fixed KDSingleApplication Qt 5 detection (#1299).
* Fixed timer started in wrong thread (#1302).
* Fixed erratic seeking behaviour if buffer duration is set to zero (#1302).
* Fixed SCollection related crash on exit with Qt 5 (#1316).
* Fixed track about to end related crash on playback failure (#1332).
* Fixed playlist column widths not remembered if stretch mode is off with Qt 6.6.1 and higher (#1328).
* (Windows) Properly handle silent uninstall (#1323).
Enhancements:
* Increase thread priority for playback threads.
* Allow drag and drop of songs to favorite playlists.
Version 1.0.21 (2023.10.21):
Bugfixes:
* Fixed seekbar position resetting to zero before showing actual position when seeking.
* Fixed compressed files showing up in collection (#1274).
* Fixed connecting devices (#1288).
* Fixed device schema missing ebur128 fields.
* Fixed collection search by tag not working with space between colon and search term (#1290).
* Fixed seeking when 5 seconds is remaining of the song resetting position to beginning (#1258).
* Fixed intermittent crash when seeking with Auto as output (#1123).
* (Windows) Fixed playlist header colors in dark mode (#1275).
Enhancements:
* Support using system KDSingleApplication when available.
* Improved lyrics matching.
* (macOS) Fully codesign binaries and DMG.
Version 1.0.20 (2023.09.24):
Bugfixes:
* Fixed appdata validation.
Version 1.0.19 (2023.09.24):
Bugfixes:
* Use shared pointers for objects to fix potential crashes on exit (#1239).
* Fixed smart playlist search not matching unrated songs (#1244).
* Fixed reading FMPS_Playcount for MP3 ID3v2 tags (#1248).
* Always stop playing after 100 errors to prevent flooding the error dialog (#1220).
* Fixed volume going to 100% when decreasing volume beyond zero (#1262).
* Fixed error dialog sometimes showing empty.
* (Windows) Removed broken volume sync (#1220).
* (Windows) Fixed shuttering / choppy audio (#1227).
* (macOS) Fixed missing search bars (#1221).
Enhancements:
* Add Mpris2 property to read/write rating (#1246).
* Capitalize playlist column names (#1264).
* Added lyrics from songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com.
* (Windows) Add gst-play-1.0.exe for debugging purposes.
New features
* Support performing song loudness analysis using `libebur128` (#1216).
* Support song playback loudness normalization, as per EBU R 128 (#1216).
Other:
* Removed last.fm HTTPS workaround and GnuTLS dependency
* Removed broken lyrics.com lyrics provider.
* (Windows) Use DirectSound as default sink.
* (Windows) Remove WASPI2 plugin because of GStreamer bug.
Version 1.0.18 (2023.07.02):
Bugfixes:
* Fixed reading disc from QObuz songs (#1168).
* Fixed volume being reset on playback with PulseAudio (#1174).
* Fixed <br> tags in SQL query error message.
* Fixed compile with Qt 6 without XCB (QX11Application).
* Fixed smart playlist editor not properly loading search terms (#1172).
* Fixed use of fixed icon for playlist favorite star icon (#1178).
* Possible fix for collection thumbnails using disk cache having identical covers for albums with hashtag (#) in the album title (#1183).
* Fixed listenbrainz scrobbling for songs with multiple artist mbids.
* Fixed listenbrainz scrobbling for songs without duration.
* Fixed gapless playback sometimes not working.
* Fixed writing PNG images as embedded covers (#1209).
* Fixed greyscale album covers not working in OSD D-Bus (#1205).
* Fixed collection thumbnail disk cache with Qt 6.5.1 and newer.
* Fixed moodbar disk cache with Qt 6.5.1 and newer.
* Fixed playlist edit tag F2 shortcut only working for title tag (#1210).
* Append number to filename if the destination file already exist when transcoding audio (#1200).
* Fixed abseil linking issues with protobuf 1.22.0 and newer.
* (macOS) Fixed "Show this message" checkbox having no affect on Rosetta warning dialog (#1180).
* (macOS) Disable unused D-Bus.
* (Windows) Fixed command line options not working with diacritics (#1191).
* (Windows) Fixed issue with saving album covers in album directory being saved in temp directory instead.
* (Windows) Fixed crash when trying a play a song which doesn't exist, gstreamer issue #1683 (#1214).
Enhancements:
* Reduce memory overhead with album cover handling (#1046).
* Improved listenbrainz error handling.
* Show error dialog for listenbrainz errors similar to last.fm/libre.fm.
* Reduce NetworkAccessManager instances.
* Replace SingleApplication with KDSingleApplication.
* Require Qt 5.12 or higher.
* Add new database fields for art_embedded and art_unset.
* Rewrite album cover loader.
* Move cover filename settings from collection to covers settings.
* Add setting to set priorities for album cover types.
* Add rating filtering to playlist search (#1212).
* (Windows|MSVC) Add WSAPI2 plugin.
Version 1.0.17 (2023.03.29):
Bugfixes:
* Fixed over-sized context album cover with device pixel ratio higher than 1.0 (#1166).
* Fixed playing widget fading from a blurry previous cover with device pixel ratio higher than 1.0.
* Made playlist source icon, album cover manager and OSD pretty cover respect device pixel ratio.
Version 1.0.16 (2023.03.27):
Bugfixes:
* Fixed lyrics from Musixmatch.
* Fixed possible file corruption when saving both tags and embedded cover using the tag editor (#1158).
* Fixed compile without GStreamer.
* Fixed context and playing now album art rendering on High DPI displays (#1161).
* Fixed setting source properties (device, user-agent, ssl-strict) with GStreamer 1.22 (playbin3) and higher (#1148).
* Fixed rescan songs feature not ignoring mtime.
* Search lyrics by artist instead of album artist by default.
Code improvements:
* Replace use of deprecated QSqlDatabase::exec().
Added features:
* Added backend setting for strict SSL mode.
* Read AcoustID and MusicBrainz tags.
* Submit MusicBrainz tags with ListenBrainz.
Version 1.0.15 (2023.03.04):
Bugfixes:
* Fixed playlist column showing invalid last played date for streams.
* Fixed crash when the audio bin failed to initialize (#1123, #1133).
* Fixed duplicated filename when organizing files using dot in the filename (#1136).
* Fixed tag inline editing for streams (#1130).
* Fixed resetting play statistics using tag edit dialog (#1124).
* Fixed compilation songs not showing if group by was set to other than (Album) Artist / Album (#1140).
Enhancements:
* Added lyrics from stands4 (lyrics.com).
* Added Sonogram analyzer.
* Use GStreamer playbin3 with GStreamer 1.22.0 and higher.
Code improvements:
* Made use of C++11 enum class where possible.
* Use new QNativeIpcKey based QSharedMemory constructor with Qt 6.6 and higher.
Version 1.0.14 (2023.01.13):
Bugfixes:
* Fix initial volume not set when using Auto as output (#1104).
* Fix saving moodbar if the URL contains host, ie.: UNC paths for SMB (#1101).
* Fix CollectionBackendTest compile error (#1100).
* Remove explicitly enabling debug messages (#1106).
Version 1.0.13 (2023.01.09):
Bugfixes:
* Fixed volume synchronization leading to infinite loop resulting in crash when adjusting volume while playing (#1089).
* Fixed incorrect volume.
* Fixed collection organizing incorrectly handling slashes inside {} brackets for variables (#1091).
* Fixed saving relative playlists to non-existing playlist files (#1092).
* Fixed intermittent crash on collection model query (#1095).
* Require system icons for fancy tabbar and settings sidebar to be larger than 22x22 (#1084).
Version 1.0.12 (2023.01.02):
Bugfixes:
* Fixed crash when adjusting volume with mouse wheel (#1089).
* Fixed playback stopping in certain cases where the next track was unavailable (#958).
* (Windows) Apply patch for fonts too large on High DPI screen (QTBUG-108593).
Removed features:
* Removed appearance settings for changing palette colors, it was never properly implemented.
Version 1.0.11 (2022.12.30):
Bugfixes:
* Capitalize GLib application name so it appears nicely in GNOME and PulseAudio Volume Control (#1066).
* Fixed missing application icon for PulseAudio Volume Control (#1066).
* Ignore errors for missing albums when updating Tidal collection if there are results (#1061).
* Only run periodic collection scan when moitoring collection setting is on.
* Fixed an edge case where the context headline text was being cut short (#1067).
* Made "Show in file browser" support SpaceFM filemanager (#1073).
* Fixed incorrect tab order in edit tag dialog (#1075).
* Changed "FMPS_PlayCount" to "FMPS_Playcount" when saving tag (#1074).
* Fixed compilation tag read and write for MP4 (#1076).
* Removed incorrect use of "TPE1" for performer when reading ID3 tags (#1076).
* Disable tag fields for unsupported tags in tag editor.
* Don't allow organizing files without unique tags (track or title) for filename (#1077).
* Don't remove disc from album title when creating cover hash to allow different covers for each disc on an album (#1069).
* Fixed incorrect relative paths for song filenames when saving playlists if the saved playlist location is a symablic link to the song filename (#1071).
* Scrobble "Various Artists" as album artist (#1082).
Enhancements:
* Use system volume instead of own software volume when available (#1037).
* Improved Tidal and Qobuz support with timed requests.
* Support MPRIS2 xesam:userRating.
Version 1.0.10 (2022.10.21):
Bugfixes:
* Fixed "Could not open settings file for writing: No such file or directory" error before settings file is created.
* Fixed visual glitch on currently playing track (#1051).
* Fixed "Unknown error" on Tidal search (#1047).
* Fixed incomplete lyrics from Genius.
* Fixed icons not showing in the file view on some systems (#1024).
* Fixed issues with context and playing widget stopping when using VLC (#1054).
* (macOS) Fixed search field related crash when playlist toolbar is turned off.
Enhancements:
* Fixed narrowing conversions in connects.
* Fixed casts from QByteArray.
* Removed subdir for generated dbus files
* Removed use of fixed font in context (#1040).
* Improve Musixmatch lyrics search.
Version 1.0.9 (2022.09.03):
Bugfixes:
* Fixed parsing album title from radio stream metadata (#1023).
* (macOS) Fixed Strawberry not starting, incorrect rpath for libgcc_s.1.1.dylib (#1025).
* (macOS) Fixed HTTP streaming.
Version 1.0.8 (2022.08.29):
Bugfixes:
* Fixed backslash being appended to filter text when switching playlist (#1005).
* Fixed OSD notifications service registering taking too long to timeout when not available.
* Fixed radio stream added twice when double-clicked (#1015).
* Fixed translating undo and redo buttons (#1017).
Enhancements:
* Use ICU instead of iconv to transliterate characters for filenames.
* Make separating albums by grouping tag optional in collection group by album.
* Added support for video game music formats VGM and SPC.
* Added setting for explicitly turning on HTTP/2 for streaming. Strawberry will set the
libsoup SOUP_FORCE_HTTP1 environment variable when the HTTP/2 is not checked (#1016).
* (Windows|MSVC) Install Visual C++ runtime redistributable automatically in installer.
Version 1.0.7 (2022.07.25)
Bugfixes:
* Fixed checking file extension case-insensitive when loading and saving playlists.
* Fixed reading and saving rating with TagParser.
* (macOS/Windows) Fixed playlist column alignment. Applied patch for Qt bug QTBUG-103576 (#999).
* (Windows|MinGW) Fixed HLS streaming.
* (Windows|MSVC) Fixed MP3 encoding.
Enhancements
* Added option for selecting file extension when saving all playlists.
Version 1.0.6 (2022.07.17)
Bugfixes:
* Fixed certain albums not added to playlist in correct track order from search for Tidal and QObuz.
* Fixed songs not added to playlist in numeric order when added from file view with right click (#977).
* Fixed "Stop after this track" graying out next track in dynamic mode (#912).
* Fixed a gstreamer caps leak when transcoding songs.
* Fixed errors in translation files (#994).
Enhancements
* Add songs to the collection even when they have invalid ctime or mtime.
* Made ListenBrainz scrobbler respect "Prefer album artist" option (#989).
* Send track duration, number, player name and version when scrobbling to ListenBrainz (#995).
* (macOS) Added missing HLS streaming plugin.
Version 1.0.5 (2022.06.10)
Bugfixes:
* Fixed smart playlist filetype search.
* Fixed Radio Paradise URLs to use HTTPS instead of HTTP.
* Fixed horizontal scrolling not affecting currently playing track (#952).
* Fixed keep running in the background when window is closed with Wayland (#964).
* Fixed percent-encoding of URLs when loading and saving XSPF playlists (#821).
* Fixed fancy tabbar context menu showing on right clicks outside of tabbar when a song is playing.
* Fixed possible duplicating songs in the database when moving songs to the collection using the organize feature.
* (Windows|MSVC) Fixed moodbar fftw3 crash with (older) CPU's that does not support AVX2 (#944).
* (Windows|MSVC) Fixed using libiconv for converting characters when organizing files like with MinGW.
Enhancements
* Show more details in error dialog on GStreamer errors (#958).
* Allow setting blur amount of playlist background image up to 100px (#939).
* Include 128x128 icon sizes (#954).
* Show right click copy context menu in context view on top text and lyrics (#965).
* Improve fading between album covers in context view.
* Added option for overwriting database playcounts in collection settings (#962).
* Added option for disabling bar on currently playing track (#972).
* (Debian) Added Qt 6 support to debian files and build with Qt 6 for Debian Bookworm, Ubuntu Jammy and newer.
* (Windows|MSVC) Added libav/ffmpeg plugin.
Version 1.0.4 (2022.04.10)
Bugfixes:
* Fixed use-after-free memory in ALSA PCM device finder.
* Translate global shortcuts.
* (Windows) Fixed registering 0-9 numpad keys in global shortcuts.
Enhancements
* Added save all playlists action.
* (Windows) Made updater support both MSVC and MinGW.
* (Windows) Added HLS support.
Other:
* Removed use of custom font in context.
Version 1.0.3 (2022.03.24)
Bugfixes:
@@ -143,7 +454,7 @@ Version 0.9.3 (2021.04.18)
Bugfixes:
* Fix "Show in file browser" to work with thunar.
* Check that the clicked rating position is to the right or left of the rectangle.
* Fix rescan when collection directory is removed and readded.
* Fix rescan when collection directory is removed and re-added.
* Create GLib main event loop on non-glib systems to fix stream discoverer.
* (macOS) Fix intermittent abort on startup.
* (macOS) Fix Tidal and Qobuz search field not showing.
@@ -480,7 +791,7 @@ Version 0.6.10 (2020.05.01)
* Made font and font sizes in context configurable.
* Splitting artist and song title to the relevant metadata when artist and song title is sent as title separated by a dash in streams.
* Added label to show collection pixmap disk cache used in settings.
* Icreased default collection pixmap disk cache to 360.
* Increased default collection pixmap disk cache to 360.
New features:
* Added back Tidal streaming support.
@@ -548,7 +859,7 @@ Version 0.6.7 (2019.11.27)
* Fixed "Pressing Previous in player" behaviour setting
* Fixed updating compilations where there are spaces or special characters in filenames
* Fixed cases where songs were stuck in "Various Artists" because not all songs in
the same compilation was removed from the model before readded with actual artist.
the same compilation was removed from the model before re-added with actual artist.
* Fixed a bug when importing playlists where metadata was reset
* Fixed scrobbler to also scrobble songs without album title
* Fixed text for replay gain setting not loading in backend setting
@@ -901,7 +1212,7 @@ Version 0.1.5 (2018.05.16)
Version 0.1.4 (2018.05.14)
* Fixed compliation with clang compiler
* Fixed compilation with clang compiler
* This release is mainly to get it working on openbsd and freebsd.

View File

@@ -1,4 +1,4 @@
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/C/C++%20CI/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/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)
@@ -11,6 +11,7 @@ Strawberry is a music player and music collection organizer. It is a fork of Cle
Resources:
* Website: https://www.strawberrymusicplayer.org/
* Wiki: https://wiki.strawberrymusicplayer.org/
* Forum: https://forum.strawberrymusicplayer.org/
* Github: https://github.com/strawberrymusicplayer/strawberry
* Buildbot: https://buildbot.strawberrymusicplayer.org/
@@ -20,23 +21,27 @@ Resources:
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
* Translations: https://translate.zanata.org/iteration/view/strawberry/master
### :bangbang: Opening an issue:
### :bangbang: Opening an issue
* Read the FAQ: https://wiki.strawberrymusicplayer.org/wiki/FAQ
* Search for the issue to see if it is already solved, or if there is an open issue for it already. If there is an open issue already, you can comment on it if you have additional information that could be useful to us.
* For technical problems, questions and feature requests please use our forum on https://forum.strawberrymusicplayer.org/ that is better suited for discussion. It also better allows answers from the community instead of just the developers on GitHub.
* For technical problems, discussion, questions and feature suggestions use the forum (https://forum.strawberrymusicplayer.org/) instead. The forum is better suited for discussion.
* 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:
1. [GitHub Sponsors](https://github.com/sponsors/jonaski)
2. [Patreon](https://www.patreon.com/jonaskvinge)
3. [PayPal](https://paypal.me/jonaskvinge)
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
4. [PayPal](https://paypal.me/jonaskvinge)
Funding developers is a way to contribute to open source projects you appreciate, it helps developers get the resources they need, and recognize contributors working behind the scenes to make open source better for everyone.
### :heavy_check_mark: Features:
### :heavy_check_mark: Features
* Play and organize music
* Supports WAV, FLAC, WavPack, Ogg FLAC, Ogg Vorbis, Ogg Opus, Ogg Speex, MPC, TrueAudio, AIFF, MP4, MP3, ASF and Monkey's Audio.
@@ -45,10 +50,11 @@ Funding developers is a way to contribute to open source projects you appreciate
* Playlist management
* Smart and dynamic playlists
* Advanced audio output and device configuration for bit-perfect playback on Linux
* In-player song loudness analysis and song playback loudness normalization, as per EBU R 128
* Edit tags on audio files
* Fetch tags from MusicBrainz
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
* Song lyrics from [AudD](https://audd.io/), [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/) and [lololyrics.com](https://www.lololyrics.com/)
* 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/)
* Support for multiple backends
* Audio analyzer
* Audio equalizer
@@ -59,24 +65,25 @@ Funding developers is a way to contribute to open source projects you appreciate
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
**There currently isn't any macOS developers actively working on this project, so we might not be able to help you with issues related to macOS.**
**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.**
### :heavy_exclamation_mark: Requirements
To build Strawberry from source you need the following installed on your system with the additional development packages/headers:
* [CMake](https://cmake.org/)
* [GCC](https://gcc.gnu.org/), [Clang](https://clang.llvm.org/) or [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus/) compiler
* C/C++ compiler ([GCC](https://gcc.gnu.org/), [Clang](https://clang.llvm.org/) or [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus/))
* [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 5.9 or higher (or Qt 6) with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
* [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/)
* [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)
* [GnuTLS](https://www.gnutls.org/)
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
* [ICU](https://unicode-org.github.io/icu/)
Optional dependencies:
@@ -86,6 +93,7 @@ Optional dependencies:
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
* EBU R 128 loudness normalization [libebur128](https://github.com/jiixyj/libebur128)
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav to support all audio formats.
@@ -93,21 +101,23 @@ You should also install the gstreamer plugins base and good, and optionally bad,
### Get the code:
git clone https://github.com/strawberrymusicplayer/strawberry
git clone --recursive https://github.com/strawberrymusicplayer/strawberry
### Compile and install:
cd strawberry
mkdir build && cd build
mkdir build
cd build
cmake .. -DBUILD_WITH_QT6=ON
make -j$(nproc)
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
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg)](https://repology.org/metapackage/strawberry/versions)
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg?exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)

View File

@@ -2,18 +2,5 @@ find_program(LSB_RELEASE_EXEC lsb_release)
find_program(DPKG_BUILDPACKAGE dpkg-buildpackage)
if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
execute_process(COMMAND env LC_ALL=C date "+%a, %-d %b %Y %H:%M:%S %z" OUTPUT_VARIABLE DEB_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -cs"
OUTPUT_VARIABLE DEB_CODENAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (DEB_CODENAME AND DEB_DATE)
add_custom_target(deb
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us
)
endif()
add_custom_target(deb WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us)
endif()

View File

@@ -1,5 +1,4 @@
#find_program(MACDEPLOYQT_EXECUTABLE NAMES macdeployqt PATHS /usr/local/opt/qt6/bin /usr/local/opt/qt5/bin /usr/local/bin REQUIRED)
set(MACDEPLOYQT_EXECUTABLE "${CMAKE_BINARY_DIR}/3rdparty/macdeployqt/macdeployqt")
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)
if(MACDEPLOYQT_EXECUTABLE)
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
else()
@@ -14,16 +13,23 @@ else()
endif()
if(MACDEPLOYQT_EXECUTABLE)
add_custom_target(copy_gstreamer_plugins
#COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macgstcopy.sh strawberry.app
)
if(APPLE_DEVELOPER_ID)
set(MACDEPLOYQT_CODESIGN -codesign=${APPLE_DEVELOPER_ID})
set(CREATEDMG_CODESIGN --codesign ${APPLE_DEVELOPER_ID})
endif()
if(CREATEDMG_SKIP_JENKINS)
set(CREATEDMG_SKIP_JENKINS_ARG "--skip-jenkins")
endif()
add_custom_target(deploy
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/strawberry.app/Contents/{Frameworks,Resources}
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/Info.plist ${CMAKE_BINARY_DIR}/strawberry.app/Contents/
COMMAND cp -v ${CMAKE_SOURCE_DIR}/dist/macos/strawberry.icns ${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources/
COMMAND ${MACDEPLOYQT_EXECUTABLE} strawberry.app -verbose=3 -executable=${CMAKE_BINARY_DIR}/strawberry.app/Contents/PlugIns/strawberry-tagreader
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}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS strawberry strawberry-tagreader copy_gstreamer_plugins macdeployqt
DEPENDS strawberry strawberry-tagreader
)
add_custom_target(deploycheck
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
@@ -31,9 +37,8 @@ if(MACDEPLOYQT_EXECUTABLE)
)
if(CREATEDMG_EXECUTABLE)
add_custom_target(dmg
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}-${CMAKE_HOST_SYSTEM_PROCESSOR}.dmg strawberry.app
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
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS deploy deploycheck
)
endif()
endif()

View File

@@ -59,7 +59,7 @@ macro(add_po outfiles po_prefix)
# 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
COMMAND ${QT_LCONVERT_EXECUTABLE} ARGS ${_po_filepath} -o ${_qm_filepath} -of qm -target-language ${_lang}
DEPENDS ${_po_filepath} ${_po_filepath}
)

View File

@@ -1,6 +1,6 @@
set(STRAWBERRY_VERSION_MAJOR 1)
set(STRAWBERRY_VERSION_MINOR 0)
set(STRAWBERRY_VERSION_PATCH 3)
set(STRAWBERRY_VERSION_PATCH 22)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF)
@@ -14,10 +14,6 @@ set(STRAWBERRY_VERSION_RPM_R "1")
set(STRAWBERRY_VERSION_PAC_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_PAC_R "1")
if(${STRAWBERRY_VERSION_PATCH} EQUAL "0")
set(STRAWBERRY_VERSION_DISPLAY "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}")
endif(${STRAWBERRY_VERSION_PATCH} EQUAL "0")
if(STRAWBERRY_VERSION_PRERELEASE)
set(STRAWBERRY_VERSION_DISPLAY "${STRAWBERRY_VERSION_DISPLAY} ${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_RPM_R "0.${STRAWBERRY_VERSION_PRERELEASE}")
@@ -29,7 +25,7 @@ if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
find_program(GIT_EXECUTABLE git)
if(NOT GIT_EXECUTABLE OR GIT_EXECUTABLE-NOTFOUND)
message(FATAL_ERROR "Missing GIT executable." )
message(FATAL_ERROR "Missing Git executable." )
endif()
# Get the current working branch
@@ -52,7 +48,7 @@ if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
)
if(NOT ${GIT_CMD_RESULT_REVISION} EQUAL 0)
message(FATAL_ERROR "GIT command failed to get revision string '${GIT_REVISION}'")
message(FATAL_ERROR "Git command failed to get revision string '${GIT_REVISION}'")
endif()
endif()
@@ -71,7 +67,7 @@ if(GIT_REVISION)
list(LENGTH GIT_PARTS GIT_PARTS_LENGTH)
if(NOT GIT_PARTS_LENGTH EQUAL 3)
message(FATAL_ERROR "Failed to parse git revision string '${GIT_REVISION}'")
set(GIT_PARTS "${majorminorpatch};0;${GIT_REVISION}")
endif()
list(GET GIT_PARTS 0 GIT_TAGNAME)

View File

@@ -1,21 +1,15 @@
<RCC>
<qresource prefix="/">
<file>schema/schema.sql</file>
<file>schema/schema-1.sql</file>
<file>schema/schema-2.sql</file>
<file>schema/schema-3.sql</file>
<file>schema/schema-4.sql</file>
<file>schema/schema-5.sql</file>
<file>schema/schema-6.sql</file>
<file>schema/schema-7.sql</file>
<file>schema/schema-8.sql</file>
<file>schema/schema-9.sql</file>
<file>schema/schema-10.sql</file>
<file>schema/schema-11.sql</file>
<file>schema/schema-12.sql</file>
<file>schema/schema-13.sql</file>
<file>schema/schema-14.sql</file>
<file>schema/schema-15.sql</file>
<file>schema/schema-16.sql</file>
<file>schema/schema-17.sql</file>
<file>schema/schema-18.sql</file>
<file>schema/device-schema.sql</file>
<file>style/strawberry.css</file>
<file>style/smartplaylistsearchterm.css</file>
@@ -46,7 +40,6 @@
<file>pictures/rainbowdash.png</file>
<file>pictures/star-on.png</file>
<file>pictures/star-off.png</file>
<file>fonts/HumongousofEternitySt.ttf</file>
<file>mood/sample.mood</file>
<file>text/ghosts.txt</file>
</qresource>

Binary file not shown.

View File

@@ -24,6 +24,7 @@
<file>icons/128x128/document-open-folder.png</file>
<file>icons/128x128/document-open.png</file>
<file>icons/128x128/document-save.png</file>
<file>icons/128x128/document-save-all.png</file>
<file>icons/128x128/document-search.png</file>
<file>icons/128x128/download.png</file>
<file>icons/128x128/edit-clear-list.png</file>
@@ -120,6 +121,7 @@
<file>icons/64x64/document-open-folder.png</file>
<file>icons/64x64/document-open.png</file>
<file>icons/64x64/document-save.png</file>
<file>icons/64x64/document-save-all.png</file>
<file>icons/64x64/document-search.png</file>
<file>icons/64x64/download.png</file>
<file>icons/64x64/edit-clear-list.png</file>
@@ -218,6 +220,7 @@
<file>icons/48x48/document-open-remote.png</file>
<file>icons/48x48/document-open.png</file>
<file>icons/48x48/document-save.png</file>
<file>icons/48x48/document-save-all.png</file>
<file>icons/48x48/document-search.png</file>
<file>icons/48x48/download.png</file>
<file>icons/48x48/edit-clear-list.png</file>
@@ -319,6 +322,7 @@
<file>icons/32x32/document-open-remote.png</file>
<file>icons/32x32/document-open.png</file>
<file>icons/32x32/document-save.png</file>
<file>icons/32x32/document-save-all.png</file>
<file>icons/32x32/document-search.png</file>
<file>icons/32x32/download.png</file>
<file>icons/32x32/edit-clear-list.png</file>
@@ -420,6 +424,7 @@
<file>icons/22x22/document-open-remote.png</file>
<file>icons/22x22/document-open.png</file>
<file>icons/22x22/document-save.png</file>
<file>icons/22x22/document-save-all.png</file>
<file>icons/22x22/document-search.png</file>
<file>icons/22x22/download.png</file>
<file>icons/22x22/edit-clear-list.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@@ -1,35 +1,35 @@
CREATE TABLE device_%deviceid_directories (
path TEXT NOT NULL DEFAULT '',
path TEXT NOT NULL,
subdirs INTEGER NOT NULL
);
CREATE TABLE device_%deviceid_subdirectories (
directory_id INTEGER NOT NULL,
path TEXT NOT NULL DEFAULT '',
path TEXT NOT NULL,
mtime INTEGER NOT NULL
);
CREATE TABLE device_%deviceid_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 0,
genre TEXT DEFAULT '',
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -40,14 +40,14 @@ CREATE TABLE device_%deviceid_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -59,15 +59,34 @@ CREATE TABLE device_%deviceid_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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
);
@@ -80,4 +99,4 @@ CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
tokenize = "unicode61 remove_diacritics 1"
);
UPDATE devices SET schema_version=3 WHERE ROWID=%deviceid;
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;

View File

@@ -1,3 +0,0 @@
ALTER TABLE playlist_items ADD COLUMN internet_service TEXT;
UPDATE schema_version SET version=1;

View File

@@ -1,4 +1,4 @@
ALTER TABLE %allsongstables ADD COLUMN fingerprint TEXT DEFAULT '';
ALTER TABLE %allsongstables ADD COLUMN fingerprint TEXT;
ALTER TABLE %allsongstables ADD COLUMN lastseen INTEGER NOT NULL DEFAULT -1;

View File

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

217
data/schema/schema-16.sql Normal file
View File

@@ -0,0 +1,217 @@
ALTER TABLE songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE subsonic_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE tidal_artists_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE tidal_albums_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE tidal_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE qobuz_artists_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE qobuz_albums_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN acoustid_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE qobuz_songs ADD COLUMN musicbrainz_work_id TEXT;
ALTER TABLE playlist_items ADD COLUMN acoustid_id TEXT;
ALTER TABLE playlist_items ADD COLUMN acoustid_fingerprint TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_album_artist_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_artist_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_original_artist_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_album_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_original_album_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_recording_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_track_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_disc_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_release_group_id TEXT;
ALTER TABLE playlist_items ADD COLUMN musicbrainz_work_id TEXT;
UPDATE schema_version SET version=16;

45
data/schema/schema-17.sql Normal file
View File

@@ -0,0 +1,45 @@
ALTER TABLE songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE subsonic_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE subsonic_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE tidal_artists_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE tidal_artists_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE tidal_albums_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE tidal_albums_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE tidal_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE tidal_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE qobuz_artists_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE qobuz_artists_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE qobuz_albums_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE qobuz_albums_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE qobuz_songs ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE qobuz_songs ADD COLUMN art_unset INTEGER DEFAULT 0;
ALTER TABLE playlist_items ADD COLUMN art_embedded INTEGER DEFAULT 0;
ALTER TABLE playlist_items ADD COLUMN art_unset INTEGER DEFAULT 0;
UPDATE songs SET art_embedded = 1 WHERE art_automatic = 'file:(embedded)';
UPDATE songs SET art_automatic = '' WHERE art_automatic = 'file:(embedded)';
UPDATE songs SET art_unset = 1 WHERE art_manual = 'file:(unset)';
UPDATE songs SET art_manual = '' WHERE art_manual = 'file:(unset)';
UPDATE schema_version SET version=17;

37
data/schema/schema-18.sql Normal file
View File

@@ -0,0 +1,37 @@
ALTER TABLE songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE subsonic_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE subsonic_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE tidal_artists_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE tidal_artists_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE tidal_albums_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE tidal_albums_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE tidal_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE tidal_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE qobuz_artists_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE qobuz_artists_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE qobuz_albums_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE qobuz_albums_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE qobuz_songs ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE qobuz_songs ADD COLUMN ebur128_loudness_range_lu REAL;
ALTER TABLE playlist_items ADD COLUMN ebur128_integrated_loudness_lufs REAL;
ALTER TABLE playlist_items ADD COLUMN ebur128_loudness_range_lu REAL;
UPDATE schema_version SET version=18;

View File

@@ -1,5 +0,0 @@
ALTER TABLE songs ADD COLUMN lyrics TEXT;
ALTER TABLE playlist_items ADD COLUMN lyrics TEXT;
UPDATE schema_version SET version=2;

View File

@@ -1,65 +0,0 @@
ALTER TABLE songs ADD COLUMN source INTEGER NOT NULL DEFAULT 0;
UPDATE songs SET source = 2 WHERE source = 0;
DROP TABLE playlist_items;
CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL,
type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER,
url TEXT,
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER,
filename TEXT,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER,
mtime INTEGER,
ctime INTEGER,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
UPDATE schema_version SET version=3;

View File

@@ -1,205 +0,0 @@
CREATE TABLE IF NOT EXISTS tidal_artists_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE IF NOT EXISTS tidal_albums_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE IF NOT EXISTS tidal_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
UPDATE schema_version SET version=4;

View File

@@ -1,31 +0,0 @@
ALTER TABLE songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_artists_songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_artists_songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_artists_songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_albums_songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_albums_songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_albums_songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE tidal_songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE playlist_items ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE playlist_items ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1;
ALTER TABLE playlist_items ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1;
UPDATE schema_version SET version=5;

View File

@@ -1,73 +0,0 @@
CREATE TABLE IF NOT EXISTS subsonic_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id INTEGER NOT NULL DEFAULT -1,
song_id INTEGER NOT NULL DEFAULT -1,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE VIRTUAL TABLE IF NOT EXISTS subsonic_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
UPDATE schema_version SET version=6;

View File

@@ -1,217 +0,0 @@
CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id INTEGER NOT NULL DEFAULT -1,
song_id INTEGER NOT NULL DEFAULT -1,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id INTEGER NOT NULL DEFAULT -1,
song_id INTEGER NOT NULL DEFAULT -1,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE IF NOT EXISTS qobuz_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id INTEGER NOT NULL DEFAULT -1,
song_id INTEGER NOT NULL DEFAULT -1,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
bitrate INTEGER NOT NULL DEFAULT 0,
samplerate INTEGER NOT NULL DEFAULT 0,
bitdepth INTEGER NOT NULL DEFAULT 0,
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER NOT NULL DEFAULT 0,
mtime INTEGER NOT NULL DEFAULT 0,
ctime INTEGER NOT NULL DEFAULT 0,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT 0,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_artists_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_albums_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
CREATE VIRTUAL TABLE IF NOT EXISTS qobuz_songs_fts USING fts3(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize=unicode
);
UPDATE schema_version SET version=7;

View File

@@ -1,595 +0,0 @@
ALTER TABLE songs RENAME TO songs_old;
ALTER TABLE playlist_items RENAME TO playlist_items_old;
ALTER TABLE tidal_artists_songs RENAME TO tidal_artists_songs_old;
ALTER TABLE tidal_albums_songs RENAME TO tidal_albums_songs_old;
ALTER TABLE tidal_songs RENAME TO tidal_songs_old;
ALTER TABLE qobuz_artists_songs RENAME TO qobuz_artists_songs_old;
ALTER TABLE qobuz_albums_songs RENAME TO qobuz_albums_songs_old;
ALTER TABLE qobuz_songs RENAME TO qobuz_songs_old;
ALTER TABLE subsonic_songs RENAME TO subsonic_songs_old;
DROP INDEX idx_filename;
CREATE TABLE songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE tidal_artists_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE tidal_albums_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE tidal_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE subsonic_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE qobuz_artists_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE qobuz_albums_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE qobuz_songs (
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
CREATE TABLE playlist_items (
playlist INTEGER NOT NULL,
type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER,
playlist_url TEXT,
title TEXT NOT NULL,
album TEXT NOT NULL,
artist TEXT NOT NULL,
albumartist TEXT NOT NULL,
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
genre TEXT NOT NULL,
compilation INTEGER NOT NULL DEFAULT -1,
composer TEXT NOT NULL,
performer TEXT NOT NULL,
grouping TEXT NOT NULL,
comment TEXT NOT NULL,
lyrics TEXT NOT NULL,
artist_id INTEGER NOT NULL DEFAULT -1,
album_id TEXT NOT NULL,
song_id INTEGER NOT NULL DEFAULT -1,
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,
url TEXT,
filetype INTEGER NOT NULL DEFAULT 0,
filesize INTEGER,
mtime INTEGER,
ctime INTEGER,
unavailable INTEGER DEFAULT 0,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
lastplayed INTEGER NOT NULL DEFAULT -1,
compilation_detected INTEGER DEFAULT 0,
compilation_on INTEGER NOT NULL DEFAULT 0,
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT
);
INSERT INTO songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM songs_old;
DROP TABLE songs_old;
INSERT INTO tidal_artists_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM tidal_artists_songs_old;
DROP TABLE tidal_artists_songs_old;
INSERT INTO tidal_albums_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM tidal_albums_songs_old;
DROP TABLE tidal_albums_songs_old;
INSERT INTO tidal_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM tidal_songs_old;
DROP TABLE tidal_songs_old;
INSERT INTO qobuz_artists_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM qobuz_artists_songs_old;
DROP TABLE qobuz_artists_songs_old;
INSERT INTO qobuz_albums_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM qobuz_albums_songs_old;
DROP TABLE qobuz_albums_songs_old;
INSERT INTO qobuz_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM qobuz_songs_old;
DROP TABLE qobuz_songs_old;
INSERT INTO subsonic_songs (title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM subsonic_songs_old;
DROP TABLE subsonic_songs_old;
INSERT INTO playlist_items (playlist, type, collection_id, playlist_url, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, url, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path)
SELECT playlist, type, collection_id, url, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, artist_id, album_id, song_id, beginning, length, bitrate, samplerate, bitdepth, source, directory_id, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, compilation_detected, compilation_on, compilation_off, compilation_effective, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path
FROM playlist_items_old;
DROP TABLE playlist_items_old;
CREATE INDEX idx_url ON songs (url);
UPDATE schema_version SET version=8;

View File

@@ -1,41 +0,0 @@
DROP TABLE %allsongstables_fts;
DROP TABLE playlist_items_fts_;
CREATE VIRTUAL TABLE %allsongstables_fts USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
CREATE VIRTUAL TABLE playlist_items_fts_ USING fts5(
ftstitle,
ftsalbum,
ftsartist,
ftsalbumartist,
ftscomposer,
ftsperformer,
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
INSERT INTO %allsongstables_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment FROM %allsongstables;
INSERT INTO playlist_items_fts_ (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment FROM playlist_items;
UPDATE schema_version SET version=9;

View File

@@ -4,40 +4,40 @@ CREATE TABLE IF NOT EXISTS schema_version (
DELETE FROM schema_version;
INSERT INTO schema_version (version) VALUES (15);
INSERT INTO schema_version (version) VALUES (18);
CREATE TABLE IF NOT EXISTS directories (
path TEXT NOT NULL DEFAULT '',
path TEXT NOT NULL,
subdirs INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS subdirectories (
directory_id INTEGER NOT NULL,
path TEXT NOT NULL DEFAULT '',
path TEXT NOT NULL,
mtime INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -48,14 +48,14 @@ CREATE TABLE IF NOT EXISTS songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -67,39 +67,58 @@ CREATE TABLE IF NOT EXISTS songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 subsonic_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -110,14 +129,14 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -129,39 +148,58 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 tidal_artists_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -172,14 +210,14 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -191,39 +229,58 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 tidal_albums_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -234,14 +291,14 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -253,39 +310,58 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 tidal_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -296,14 +372,14 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -315,39 +391,58 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -358,14 +453,14 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -377,39 +472,58 @@ CREATE TABLE IF NOT EXISTS qobuz_artists_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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_albums_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -420,14 +534,14 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -439,39 +553,58 @@ CREATE TABLE IF NOT EXISTS qobuz_albums_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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_songs (
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
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 DEFAULT '',
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER NOT NULL DEFAULT 0,
length INTEGER NOT NULL DEFAULT 0,
@@ -482,14 +615,14 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
source INTEGER NOT NULL DEFAULT 0,
directory_id INTEGER NOT NULL DEFAULT -1,
url TEXT NOT NULL DEFAULT '',
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 DEFAULT '',
fingerprint TEXT,
playcount INTEGER NOT NULL DEFAULT 0,
skipcount INTEGER NOT NULL DEFAULT 0,
@@ -501,29 +634,48 @@ CREATE TABLE IF NOT EXISTS qobuz_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 playlists (
name TEXT NOT NULL DEFAULT '',
name TEXT NOT NULL,
last_played INTEGER NOT NULL DEFAULT -1,
ui_order INTEGER NOT NULL DEFAULT 0,
special_type TEXT DEFAULT '',
ui_path TEXT DEFAULT '',
special_type TEXT,
ui_path TEXT,
is_favorite INTEGER NOT NULL DEFAULT 0,
dynamic_playlist_type INTEGER,
dynamic_playlist_backend TEXT DEFAULT '',
dynamic_playlist_backend TEXT,
dynamic_playlist_data BLOB
);
@@ -533,27 +685,27 @@ CREATE TABLE IF NOT EXISTS playlist_items (
playlist INTEGER NOT NULL,
type INTEGER NOT NULL DEFAULT 0,
collection_id INTEGER,
playlist_url TEXT DEFAULT '',
playlist_url TEXT,
title TEXT DEFAULT '',
album TEXT DEFAULT '',
artist TEXT DEFAULT '',
albumartist TEXT DEFAULT '',
title TEXT,
album TEXT,
artist TEXT,
albumartist TEXT,
track INTEGER,
disc INTEGER,
year INTEGER,
originalyear INTEGER,
genre TEXT DEFAULT '',
genre TEXT,
compilation INTEGER DEFAULT 0,
composer TEXT DEFAULT '',
performer TEXT DEFAULT '',
grouping TEXT DEFAULT '',
comment TEXT DEFAULT '',
lyrics TEXT DEFAULT '',
composer TEXT,
performer TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
artist_id TEXT DEFAULT '',
album_id TEXT DEFAULT '',
song_id TEXT DEFAULT '',
artist_id TEXT,
album_id TEXT,
song_id TEXT,
beginning INTEGER,
length INTEGER,
@@ -564,14 +716,14 @@ CREATE TABLE IF NOT EXISTS playlist_items (
source INTEGER,
directory_id INTEGER,
url TEXT DEFAULT '',
url TEXT NOT NULL,
filetype INTEGER,
filesize INTEGER,
mtime INTEGER,
ctime INTEGER,
unavailable INTEGER DEFAULT 0,
fingerprint TEXT DEFAULT '',
fingerprint TEXT,
playcount INTEGER DEFAULT 0,
skipcount INTEGER DEFAULT 0,
@@ -583,23 +735,42 @@ CREATE TABLE IF NOT EXISTS playlist_items (
compilation_off INTEGER DEFAULT 0,
compilation_effective INTEGER DEFAULT 0,
art_automatic TEXT DEFAULT '',
art_manual TEXT DEFAULT '',
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT DEFAULT '',
effective_albumartist TEXT,
effective_originalyear INTEGER,
cue_path TEXT DEFAULT '',
cue_path TEXT,
rating INTEGER DEFAULT -1
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 devices (
unique_id TEXT NOT NULL DEFAULT '',
friendly_name TEXT DEFAULT '',
unique_id TEXT NOT NULL,
friendly_name TEXT,
size INTEGER,
icon TEXT DEFAULT '',
icon TEXT,
schema_version INTEGER NOT NULL DEFAULT 0,
transcode_mode NOT NULL DEFAULT 3,
transcode_format NOT NULL DEFAULT 5
@@ -607,9 +778,9 @@ CREATE TABLE IF NOT EXISTS devices (
CREATE TABLE IF NOT EXISTS radio_channels (
source INTEGER NOT NULL DEFAULT 0,
name TEXT DEFAULT '',
url TEXT DEFAULT '',
thumbnail_url TEXT DEFAULT ''
name TEXT,
url TEXT NOT NULL,
thumbnail_url TEXT
);
CREATE INDEX IF NOT EXISTS idx_url ON songs (url);

View File

@@ -31,6 +31,10 @@
background-color: %palette-base;
}
#context-scrollarea {
background-color: %palette-base;
}
QToolButton {
border: 2px solid transparent;
border-radius: 3px;
@@ -60,11 +64,3 @@ QToolButton::menu-button {
width: 16px;
border: none;
}
macos {
font-size: 11pt;
}
macos QMenu {
font-size: 13pt;
}

22
debian/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,22 @@
if(LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
execute_process(COMMAND env LC_ALL=C date "+%a, %-d %b %Y %H:%M:%S %z" OUTPUT_VARIABLE DEB_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
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

@@ -3,45 +3,43 @@ Section: sound
Priority: optional
Maintainer: Jonas Kvinge <jonas@jkvinge.net>
Build-Depends: debhelper (>= 11),
git,
make,
cmake,
gcc,
g++,
protobuf-compiler,
libglib2.0-dev,
libdbus-1-dev,
libgnutls28-dev,
libprotobuf-dev,
libboost-dev,
libsqlite3-dev,
libasound2-dev,
libpulse-dev,
libtag1-dev,
qtbase5-dev,
qtbase5-private-dev,
qtbase5-dev-tools,
qttools5-dev,
libqt5x11extras5-dev,
libicu-dev,
@DEBIAN_BUILD_DEPENDS_QT_PACKAGES@,
libgstreamer1.0-dev,
libgstreamer-plugins-base1.0-dev,
libcdio-dev,
libgpod-dev,
libmtp-dev,
libchromaprint-dev,
libfftw3-dev
Standards-Version: 4.2.1
libfftw3-dev,
libebur128-dev
Standards-Version: 4.6.1
Package: strawberry
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
libsqlite3-0,
libqt5sql5-sqlite,
@DEBIAN_DEPENDS_QT_PACKAGES@,
gstreamer1.0-plugins-base,
gstreamer1.0-plugins-good,
gstreamer1.0-alsa,
gstreamer1.0-pulseaudio
Homepage: http://www.strawberrymusicplayer.org/
Description: Audio player and music collection organizer
Description: music player and music collection organizer
Strawberry is a music player aimed at music collectors and audiophiles.
.
Features:
@@ -49,14 +47,13 @@ Description: Audio player and music collection organizer
- Supports WAV, FLAC, WavPack, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3 and ASF
- Audio CD playback
- Native desktop notifications
- Playlist management
- Playlists in multiple formats
- Playlist management and playlists in multiple formats
- Smart and dynamic playlists
- Advanced audio output and device configuration for bit-perfect playback on Linux
- Edit tags on audio files
- Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Support for multiple backends
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com
- Audio analyzer
- Audio equalizer
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic

205
debian/copyright vendored
View File

@@ -1,40 +1,42 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: strawberry
Upstream-Contact: Jonas Kvinge <jonas@jkvinge.net>
Source: https://www.strawberrymusicplayer.org/
Source: https://github.com/strawberrymusicplayer/strawberry
Files: *
Copyright: 2010-2015, David Sansome <me@davidsansome.com>
2012-2014, 2017-2023 Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/core/timeconstants.h
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
Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
2018-2022, Jonas Kvinge <jonas@jkvinge.net>
License: Apache-2.0
Files: src/core/main.h
src/core/iconloader.cpp
src/core/iconloader.h
src/core/iconmapper.h
src/config.h.in
src/version.h.in
src/context/contextview.cpp
src/context/contextview.h
src/context/contextalbum.cpp
src/context/contextalbum.h
src/engine/enginetype.cpp
src/engine/enginetype.h
src/engine/alsadevicefinder.cpp
src/engine/alsadevicefinder.h
src/engine/alsapcmdevicefinder.cpp
src/engine/alsapcmdevicefinder.h
src/engine/mmdevicefinder.cpp
src/engine/mmdevicefinder.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
@@ -69,11 +71,17 @@ Files: src/core/main.h
src/covermanager/spotifycoverprovider.h
src/covermanager/musixmatchcoverprovider.cpp
src/covermanager/musixmatchcoverprovider.h
src/globalshortcuts/globalshortcutsbackend-system.cpp
src/globalshortcuts/globalshortcutsbackend-system.h
src/globalshortcuts/globalshortcutsbackend-kde.cpp
src/globalshortcuts/globalshortcutsbackend-kde.h
src/globalshortcuts/globalshortcutsbackend-mate.cpp
src/globalshortcuts/globalshortcutsbackend-mate.h
src/globalshortcuts/globalshortcutsbackend-x11.cpp
src/globalshortcuts/globalshortcutsbackend-x11.h
src/globalshortcuts/globalshortcutsbackend-win.cpp
src/globalshortcuts/globalshortcutsbackend-win.h
src/globalshortcuts/globalshortcut.cpp
src/globalshortcuts/globalshortcut.h
src/globalshortcuts/globalshortcut-X11.cpp
src/globalshortcuts/globalshortcut-x11.cpp
src/globalshortcuts/globalshortcut-win.cpp
src/globalshortcuts/keymapper_x11.h
src/globalshortcuts/keymapper_win.h
@@ -81,132 +89,21 @@ Files: src/core/main.h
src/scrobbler/*
src/subsonic/*
src/tidal/*
src/qobuz/*
src/radios/*
src/transcoder/transcoderoptionswavpack.cpp
src/transcoder/transcoderoptionswavpack.h
Copyright: 2012-2014, 2017-2020, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/core/main.cpp
src/core/mainwindow.cpp
src/core/mainwindow.h
src/core/player.cpp
src/core/player.h
src/core/song.cpp
src/core/song.h
src/core/songloader.cpp
src/core/songloader.h
src/core/urlhandler.cpp
src/core/urlhandler.h
src/core/utilities.cpp
src/core/utilities.h
src/core/networkaccessmanager.cpp
src/core/networkaccessmanager.h
src/core/threadsafenetworkdiskcache.cpp
src/core/threadsafenetworkdiskcache.h
src/core/filesystemmusicstorage.cpp
src/core/filesystemmusicstorage.h
src/core/stylesheetloader.cpp
src/core/stylesheetloader.h
src/engine/gstenginepipeline.cpp
src/engine/gstenginepipeline.h
src/engine/vlcengine.cpp
src/engine/vlcengine.h
src/collection/collectionwatcher.cpp
src/collection/collectionwatcher.h
src/collection/collectionbackend.cpp
src/collection/collectionbackend.h
src/collection/collectionmodel.cpp
src/collection/collectionmodel.h
src/context/contextalbumsmodel.cpp
src/context/contextalbumsview.cpp
src/context/contextalbumsmodel.h
src/context/contextalbumsview.h
src/widgets/playingwidget.cpp
src/widgets/playingwidget.h
src/osd/osdbase.cpp
src/osd/osdbase.h
src/osd/osdpretty.cpp
src/osd/osdpretty.h
src/osd/osddbus.cpp
src/osd/osddbus.h
src/osd/osdmac.cpp
src/osd/osdmac.h
src/dialogs/about.cpp
src/dialogs/about.h
src/playlist/playlist.cpp
src/playlist/playlist.h
src/playlist/playlistitem.cpp
src/playlist/playlistitem.h
src/playlist/playlistdelegates.cpp
src/playlist/playlistdelegates.h
src/playlist/playlistbackend.cpp
src/playlist/playlistbackend.h
src/playlist/playlistview.cpp
src/playlist/playlistview.h
src/playlist/songplaylistitem.cpp
src/playlist/songplaylistitem.h
src/internet/internetplaylistitem.cpp
src/internet/internetsearch.cpp
src/internet/internetsearch.h
src/internet/internetsearchview.cpp
src/internet/internetsearchview.h
src/internet/internetservices.cpp
src/internet/internetservices.h
src/internet/internetsongsview.cpp
src/internet/internetsongsview.h
src/internet/internetcollectionview.cpp
src/internet/internetcollectionview.h
ext/libstrawberry-tagreader/tagreader.cpp
ext/libstrawberry-tagreader/tagreader.h
src/device/devicemanager.cpp
src/device/devicemanager.h
src/device/deviceinfo.cpp
src/device/deviceinfo.h
src/device/deviceproperties.cpp
src/device/deviceproperties.h
src/device/deviceview.cpp
src/device/deviceview.h
src/device/connecteddevice.cpp
src/device/connecteddevice.h
src/device/mtpconnection.cpp
src/device/mtpconnection.h
src/device/mtpdevice.cpp
src/device/mtpdevice.h
src/globalshortcuts/globalshortcutsmanager.cpp
src/globalshortcuts/globalshortcutsmanager.h
src/settings/shortcutssettingspage.cpp
src/settings/shortcutssettingspage.h
src/settings/appearancesettingspage.cpp
src/settings/appearancesettingspage.h
src/organize/organize.cpp
src/organize/organize.h
src/organize/organizedialog.cpp
src/organize/organizedialog.h
src/organize/organizeerrordialog.cpp
src/organize/organizeerrordialog.h
src/transcoder/transcoder.cpp
src/transcoder/transcoder.h
src/musicbrainz/musicbrainzclient.cpp
src/musicbrainz/musicbrainzclient.h
src/covermanager/albumcoverloader.cpp
src/covermanager/albumcoverloader.h
src/covermanager/currentalbumcoverloader.cpp
src/covermanager/currentalbumcoverloader.h
src/covermanager/albumcoverchoicecontroller.cpp
src/covermanager/albumcoverchoicecontroller.h
src/covermanager/albumcoverfetchersearch.cpp
src/covermanager/albumcoverfetchersearch.h
src/covermanager/coverproviders.cpp
src/covermanager/coverproviders.h
src/covermanager/coverprovider.cpp
src/covermanager/coverprovider.h
Copyright: 2010, 2012-2014 David Sansome <me@davidsansome.com>
2012-2014, 2017-2020 Jonas Kvinge <jonas@jkvinge.net>
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>
License: GPL-3+
Files: src/engine/enginebase.cpp
src/engine/enginebase.h
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
Copyright: 2017-2021, Jonas Kvinge <jonas@jkvinge.net>
2010, David Sansome <me@davidsansome.com>
2004, 2005, Max Howell, <max.howell@methylblue.com>
2003, Mark Kretschmann <markey@web.de>
@@ -214,7 +111,7 @@ License: GPL-3+
Files: src/engine/gstengine.cpp
src/engine/gstengine.h
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
Copyright: 2017-2021, Jonas Kvinge <jonas@jkvinge.net>
2006, Paul Cifarelli <paul@cifarelli.net>
2005, Jakub Stachowski <qbast@go2.pl>
2003-2005, Mark Kretschmann <markey@web.de>
@@ -230,12 +127,7 @@ Files: src/core/application.cpp
src/core/application.h
Copyright: 2012, David Sansome <me@davidsansome.com>
2012, 2014, John Maguire <john.maguire@gmail.com>
2018, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/core/appearance.cpp
src/core/appearance.h
Copyright: 2012, Arnaud Bienner <arnaud.bienner@gmail.com>
2018-2021, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/covermanager/discogscoverprovider.cpp
@@ -244,13 +136,7 @@ Copyright: 2012, Martin Björklund <mbj4668@gmail.com>
2016, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: ext/libstrawberry-common/core/arraysize.h
ext/libstrawberry-common/core/scoped_nsautorelease_pool.h
ext/libstrawberry-common/core/scoped_nsautorelease_pool.mm
Copyright: 2010, 2011, 2014, The Chromium Authors.
License: BSD-style
Files: ext/libstrawberry-common/core/lazy.h
Files: src/core/lazy.h
Copyright: 2016, John Maguire <john.maguire@gmail.com>
License: GPL-3+
@@ -298,12 +184,16 @@ Copyright: 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
License: GPL-2+
Files: src/collection/savedgroupingmanager.h
Files: src/collection/savedgroupingmanager.*
Copyright: 2015, Nick Lanham <nick@afternight.org>
2019-2021 Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/core/scoped_cftyperef.h
Files: src/core/arraysize.h
src/core/scoped_cftyperef.h
src/core/scoped_nsobject.h
src/core/scoped_nsautorelease_pool.h
src/core/scoped_nsautorelease_pool.mm
Copyright: 2010, 2011, 2014, The Chromium Authors.
License: BSD-style
@@ -322,7 +212,7 @@ Files: src/internet/localredirectserver.cpp
src/internet/localredirectserver.h
Copyright: 2012, 2014, John Maguire <john.maguire@gmail.com>
2014, Krzysztof Sobiecki <sobkas@gmail.com>
2018-2019, Jonas Kvinge <jonas@jkvinge.net>
2018-2021, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/transcoder/transcoderoptionsopus.cpp
@@ -337,12 +227,22 @@ Files: src/widgets/clickablelabel.cpp
Copyright: 2010, 2011, Andrea Decorte <adecorte@gmail.com>
License: GPL-3+
Files: src/widgets/sliderwidget.cpp
src/widgets/sliderwidget.h
Copyright: 2005, Gábor Lehel
Files: src/widgets/sliderslider.cpp
src/widgets/sliderslider.h
src/widgets/prettyslider.cpp
src/widgets/prettyslider.h
src/widgets/volumeslider.cpp
src/widgets/volumeslider.h
Copyright: 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
2005, Gábor Lehel
2003, Mark Kretschmann <markey@web.de>
License: GPL-2+
Files: src/scrobbler/subsonicscrobbler.*
Copyright: 2018-2021 Jonas Kvinge <jonas@jkvinge.net>
2020 Pascal Below <spezifisch@below.fr>
License: GPL-3+
Files: src/core/stylehelper.cpp
src/core/stylehelper.h
Copyright: 2016 The Qt Company Ltd.
@@ -358,6 +258,7 @@ License: GPL-2+
Files: src/widgets/qsearchfield_nonmac.cpp
src/widgets/qsearchfield_mac.mm
src/widgets/qsearchfield.h
src/widgets/qocoa_mac.h
Copyright: 2011, Mike McQuaid <mike@mikemcquaid.com>
License: Expat
@@ -366,8 +267,8 @@ Copyright: 2010, Spotify AB
2011, Joachim Bengtsson
License: BSD-3-clause
Files: 3rdparty/singleapplication/*
Copyright: 2015-2018, Itay Grudev
Files: 3rdparty/kdsingleapplication/*
Copyright: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
License: MIT
@@ -475,7 +376,7 @@ License: Apache-2.0
On Debian systems, the complete text of the Apache 2.0 license can be
found in the file
`/usr/share/common-licenses/Apache-2.0`.
License: BSD-3-clause
Copyright (c) The Regents of the University of California.
All rights reserved.

View File

@@ -4,14 +4,14 @@ ignore:
- Changelog
- COPYING
- CMakeLists.txt
- Dockerfile
- cmake_uninstall.cmake.in
- .clang-format
- .gitignore
- .travis.yml
- .github
- /debian/
- /cmake/
- /data/
- /dist/
- /snap/
suffixes:
- jpg

1
debian/rules vendored
View File

@@ -5,7 +5,6 @@
override_dh_auto_clean:
rm -f dist/macos/Info.plist
rm -f dist/unix/PKGBUILD
rm -f dist/unix/strawberry.spec
rm -f dist/scripts/maketarball.sh
rm -f dist/windows/strawberry.nsi

4
dist/CMakeLists.txt vendored
View File

@@ -2,10 +2,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CUR
if(RPM_DISTRO AND RPM_DATE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec @ONLY)
endif(RPM_DISTRO AND RPM_DATE)
if(DEB_CODENAME AND DEB_DATE)
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
endif(DEB_CODENAME AND DEB_DATE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD @ONLY)
if(APPLE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/sh
# Script to copy gstreamer plugins before macdeployqt is run.
# Script to copy gio modules and gstreamer plugins before macdeployqt is run.
if [ "$1" = "" ]; then
echo "Usage: $0 <bundledir>"
@@ -8,104 +8,141 @@ if [ "$1" = "" ]; then
fi
bundledir=$1
if [ "$GIO_EXTRA_MODULES" = "" ]; then
echo "Error: Set the GIO_EXTRA_MODULES environment variable to the path containing libgiognutls.so."
if [ "${GIO_EXTRA_MODULES}" = "" ]; then
echo "Error: Set the GIO_EXTRA_MODULES environment variable to the path containing gio modules."
exit 1
fi
if [ "$GST_PLUGIN_SCANNER" = "" ]; then
if [ "${GST_PLUGIN_SCANNER}" = "" ]; then
echo "Error: Set the GST_PLUGIN_SCANNER environment variable to the gst-plugin-scanner."
exit 1
fi
if [ "$GST_PLUGIN_PATH" = "" ]; then
if [ "${GST_PLUGIN_PATH}" = "" ]; then
echo "Error: Set the GST_PLUGIN_PATH environment variable to the path containing gstreamer plugins."
exit 1
fi
if ! [ -e "${GIO_EXTRA_MODULES}/libgiognutls.so" ] && ! [ -e "${GIO_EXTRA_MODULES}/libgioopenssl.so" ]; then
echo "Error: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so or ${GIO_EXTRA_MODULES}/libgioopenssl.so."
exit 1
fi
if ! [ -e "${GST_PLUGIN_SCANNER}" ]; then
echo "Error: Missing ${GST_PLUGIN_SCANNER}"
exit 1
fi
if ! [ -d "${GST_PLUGIN_PATH}" ]; then
echo "Error: GStreamer plugins path ${GST_PLUGIN_PATH} does not exist."
exit 1
fi
if ! [ -e "${GST_PLUGIN_PATH}/libgstcoreelements.so" ] && ! [ -e "${GST_PLUGIN_PATH}/libgstcoreelements.dylib" ]; then
echo "Error: Missing libgstcoreelements.{so,dylib} in GStreamer plugins path ${GST_PLUGIN_PATH}."
exit 1
fi
mkdir -p "${bundledir}/Contents/PlugIns/gio-modules" || exit 1
mkdir -p "${bundledir}/Contents/PlugIns/gstreamer" || exit 1
if ! [ -f "${GIO_EXTRA_MODULES}/libgiognutls.so" ]; then
echo "Error: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so."
exit 1
if [ -e "${GIO_EXTRA_MODULES}/libgiognutls.so" ]; then
cp -v -f "${GIO_EXTRA_MODULES}/libgiognutls.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
else
echo "Warning: Missing ${GIO_EXTRA_MODULES}/libgiognutls.so."
fi
cp -v -f "${GIO_EXTRA_MODULES}/libgiognutls.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
if ! [ -f "${GST_PLUGIN_SCANNER}" ]; then
echo "Error: Missing ${GST_PLUGIN_SCANNER}"
exit 1
if [ -e "${GIO_EXTRA_MODULES}/libgioopenssl.so" ]; then
cp -v -f "${GIO_EXTRA_MODULES}/libgioopenssl.so" "${bundledir}/Contents/PlugIns/gio-modules/" || exit 1
else
echo "Warning: Missing ${GIO_EXTRA_MODULES}/libgioopenssl.so"
fi
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="
libgstaes.dylib
libgstaiff.dylib
libgstapetag.dylib
libgstapp.dylib
libgstasf.dylib
libgstasfmux.dylib
libgstaudioconvert.dylib
libgstaudiofx.dylib
libgstaudiomixer.dylib
libgstaudioparsers.dylib
libgstaudiorate.dylib
libgstaudioresample.dylib
libgstaudiotestsrc.dylib
libgstautodetect.dylib
libgstbs2b.dylib
libgstcdio.dylib
libgstcoreelements.dylib
libgstdash.dylib
libgstequalizer.dylib
libgstflac.dylib
libgstfaac.dylib
libgstfaad.dylib
libgstfdkaac.dylib
libgstgio.dylib
libgsticydemux.dylib
libgstid3demux.dylib
libgstisomp4.dylib
libgstlame.dylib
libgstlibav.dylib
libgstmpg123.dylib
libgstmusepack.dylib
libgstogg.dylib
libgstopenmpt.dylib
libgstopus.dylib
libgstopusparse.dylib
libgstosxaudio.dylib
libgstpbtypes.dylib
libgstplayback.dylib
libgstreplaygain.dylib
libgstrtp.dylib
libgstrtsp.dylib
libgstsoup.dylib
libgstspectrum.dylib
libgstspeex.dylib
libgsttaglib.dylib
libgsttcp.dylib
libgsttypefindfunctions.dylib
libgstudp.dylib
libgstvolume.dylib
libgstvorbis.dylib
libgstwavpack.dylib
libgstwavparse.dylib
libgstxingmux.dylib;
libgstaes
libgstaiff
libgstapetag
libgstapp
libgstasf
libgstasfmux
libgstaudioconvert
libgstaudiofx
libgstaudiomixer
libgstaudioparsers
libgstaudiorate
libgstaudioresample
libgstaudiotestsrc
libgstautodetect
libgstbs2b
libgstcdio
libgstcoreelements
libgstdash
libgstequalizer
libgstfaac
libgstfaad
libgstfdkaac
libgstflac
libgstgio
libgsthls
libgsticydemux
libgstid3demux
libgstid3tag
libgstisomp4
libgstlame
libgstlibav
libgstmpg123
libgstmusepack
libgstogg
libgstopenmpt
libgstopus
libgstopusparse
libgstosxaudio
libgstpbtypes
libgstplayback
libgstreplaygain
libgstrtp
libgstrtsp
libgstsoup
libgstspectrum
libgstspeex
libgsttaglib
libgsttcp
libgsttwolame
libgsttypefindfunctions
libgstudp
libgstvolume
libgstvorbis
libgstwavenc
libgstwavpack
libgstwavparse
libgstxingmux
"
gst_plugins=$(echo "$gst_plugins" | tr '\n' ' ' | sed -e 's/^ //g' | sed -e 's/ / /g')
for gst_plugin in $gst_plugins
do
if [ -f "${GST_PLUGIN_PATH}/${gst_plugin}" ]; then
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
for gst_plugin in $gst_plugins; do
if [ -e "${GST_PLUGIN_PATH}/${gst_plugin}.dylib" ]; then
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}.dylib" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
install_name_tool -id "@rpath/${gst_plugin}.dylib" "${bundledir}/Contents/PlugIns/gstreamer/${gst_plugin}.dylib"
elif [ -e "${GST_PLUGIN_PATH}/${gst_plugin}.so" ]; then
cp -v -f "${GST_PLUGIN_PATH}/${gst_plugin}.so" "${bundledir}/Contents/PlugIns/gstreamer/" || exit 1
install_name_tool -id "@rpath/${gst_plugin}.so" "${bundledir}/Contents/PlugIns/gstreamer/${gst_plugin}.so"
else
echo "Warning: Missing gstreamer plugin ${GST_PLUGIN_PATH}/${gst_plugin}"
echo "Warning: Missing gstreamer plugin ${gst_plugin}."
fi
done
if [ -f "/usr/local/lib/libbrotlicommon.1.dylib" ]; then
mkdir -p ${bundledir}/Contents/Frameworks
cp -v -f "/usr/local/lib/libbrotlicommon.1.dylib" "${bundledir}/Contents/Frameworks/"
# libsoup is dynamically loaded by gstreamer, so it needs to be copied.
if [ "${LIBSOUP_LIBRARY_PATH}" = "" ]; then
echo "Warning: Set the LIBSOUP_LIBRARY_PATH environment variable for copying libsoup."
else
if [ -e "${LIBSOUP_LIBRARY_PATH}" ]; then
mkdir -p "${bundledir}/Contents/Frameworks" || exit 1
cp -v -f "${LIBSOUP_LIBRARY_PATH}" "${bundledir}/Contents/Frameworks/" || exit 1
install_name_tool -id "@rpath/$(basename ${LIBSOUP_LIBRARY_PATH})" "${bundledir}/Contents/Frameworks/$(basename ${LIBSOUP_LIBRARY_PATH})"
else
echo "Warning: Missing libsoup ${LIBSOUP_LIBRARY_PATH}."
fi
fi

View File

@@ -49,6 +49,7 @@ ${TAR} -cJf $name-$version.tar.xz \
--exclude="*.nsi" \
--exclude="*.kdev4" \
--exclude=".vscode" \
--exclude=".idea" \
--exclude="$root/.github" \
--exclude="$root/.travis.yml" \
--exclude="$root/.circleci" \
@@ -56,11 +57,11 @@ ${TAR} -cJf $name-$version.tar.xz \
--exclude="$root/CMakeLists.txt.user" \
--exclude="$root/.clang-format" \
--exclude="$root/build" \
--exclude="$root/cmake-build-debug" \
--exclude="$root/zanata.xml" \
--exclude="$root/.zanata-cache" \
--exclude="$root/debian/changelog" \
--exclude="$root/dist/scripts/maketarball.sh" \
--exclude="$root/dist/unix/PKGBUILD" \
--exclude="$root/dist/macos/Info.plist" \
--exclude="$root/dist/windows/windres.rc" \
--exclude="$root/src/translations/translations.pot" \

56
dist/unix/PKGBUILD.in vendored
View File

@@ -1,56 +0,0 @@
# Maintainer: Jonas Kvinge <jonas@jkvinge.net>
pkgname=strawberry
pkgver=@STRAWBERRY_VERSION_PAC_V@
pkgrel=@STRAWBERRY_VERSION_PAC_R@
pkgdesc="A music player aimed at audio enthusiasts and music collectors"
arch=(x86_64)
url="https://www.strawberrymusicplayer.org/"
license=(GPL3)
makedepends=(git cmake make gcc boost gettext qt5-tools)
depends=(
desktop-file-utils
hicolor-icon-theme
gnutls
udisks2
protobuf
qt5-base
qt5-x11extras
sqlite3
alsa-lib
libpulse
dbus
taglib
gstreamer
gst-plugins-base
gst-plugins-good
vlc
chromaprint
libgpod
libcdio
libmtp
fftw
)
optdepends=(
gst-plugins-bad
gst-plugins-ugly
gst-libav
)
provides=(strawberry)
conflicts=(strawberry)
source=("strawberry-@STRAWBERRY_VERSION_PACKAGE@.tar.xz")
sha256sums=('SKIP')
prepare() {
mkdir -p build
}
build() {
cd build
cmake ../${pkgname}-@STRAWBERRY_VERSION_PACKAGE@ -DCMAKE_INSTALL_PREFIX=/usr
make -j$(nproc)
}
package() {
cd build
make DESTDIR="${pkgdir}" install
}

View File

@@ -29,7 +29,7 @@
<li>Edit tags on audio files</li>
<li>Automatically retrieve tags from MusicBrainz</li>
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
<li>Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com</li>
<li>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>Audio analyzer and equalizer</li>
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
@@ -50,6 +50,10 @@
</screenshots>
<update_contact>eclipseo@fedoraproject.org</update_contact>
<releases>
<release version="1.0.0" date="2021-10-16"/>
<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"/>
<release version="1.0.19" date="2023-09-24"/>
<release version="1.0.18" date="2023-07-02"/>
</releases>
</component>

View File

@@ -3,37 +3,41 @@ Version=1.0
Type=Application
Name=Strawberry
GenericName=Strawberry Music Player
GenericName[ru]=Музыкальный проигрыватель Strawberry
Comment=Plays music
Comment[ru]=Прослушивание музыки
Exec=strawberry %U
TryExec=strawberry
Icon=strawberry
Terminal=false
Categories=AudioVideo;Player;Qt;Audio;
Keywords=Audio;Player;
StartupNotify=false
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
StartupWMClass=strawberry
Actions=Play;Pause;Stop;StopAfterCurrent;Previous;Next;
Actions=Play-Pause;Stop;StopAfterCurrent;Previous;Next;
[Desktop Action Play]
Name=Play
Exec=strawberry --play
[Desktop Action Pause]
Name=Pause
Exec=strawberry --pause
[Desktop Action Play-Pause]
Name=Play/Pause
Exec=strawberry --play-pause
Name[ru]=Играть/пауза
[Desktop Action Stop]
Name=Stop
Exec=strawberry --stop
Name[ru]=Стоп
[Desktop Action StopAfterCurrent]
Name=Stop after this track
Exec=strawberry --stop-after-current
Name[ru]=Стоп после этого трека
[Desktop Action Previous]
Name=Previous
Exec=strawberry --previous
Name[ru]=Предыдущий
[Desktop Action Next]
Name=Next
Exec=strawberry --next
Name[ru]=Следующий

View File

@@ -29,7 +29,7 @@ Features:
.br
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
.br
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Song lyrics from Lyrics.com, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
.br
- Support for multiple backends
.br

View File

@@ -1,9 +1,9 @@
Name: strawberry
Version: @STRAWBERRY_VERSION_RPM_V@
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} || "%{?_vendor}" == "openmandriva"
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
%else
%if 0%{?suse_version}
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
%else
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
%endif
Summary: A music player and music collection organizer
Group: Productivity/Multimedia/Sound/Players
@@ -11,7 +11,7 @@ License: GPL-3.0+
URL: https://www.strawberrymusicplayer.org/
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
%if 0%{?suse_version} && 0%{?suse_version} > 1325
%if 0%{?suse_version}
BuildRequires: libboost_headers-devel
%else
BuildRequires: boost-devel
@@ -25,15 +25,13 @@ BuildRequires: gettext
BuildRequires: desktop-file-utils
%if 0%{?suse_version}
BuildRequires: update-desktop-files
%endif
%if 0%{?suse_version}
BuildRequires: appstream-glib
%else
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
%endif
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
BuildRequires: libappstream-glib
%else
%endif
%if 0%{?mageia} || "%{?_vendor}" == "openmandriva"
BuildRequires: appstream-util
%endif
%endif
BuildRequires: pkgconfig
BuildRequires: pkgconfig(glib-2.0)
@@ -41,50 +39,34 @@ BuildRequires: pkgconfig(gio-2.0)
BuildRequires: pkgconfig(gio-unix-2.0)
BuildRequires: pkgconfig(gthread-2.0)
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(gnutls)
BuildRequires: pkgconfig(alsa)
BuildRequires: pkgconfig(protobuf)
BuildRequires: pkgconfig(sqlite3) >= 3.9
%if ! 0%{?centos} && ! 0%{?mageia}
BuildRequires: pkgconfig(taglib)
%endif
BuildRequires: pkgconfig(fftw3)
%if "@QT_VERSION_MAJOR@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Core)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Gui)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Widgets)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Concurrent)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Network)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Sql)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@DBus)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@Test)
BuildRequires: pkgconfig(Qt@QT_VERSION_MAJOR@X11Extras)
%else
BuildRequires: pkgconfig(icu-uc)
BuildRequires: pkgconfig(icu-i18n)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Gui)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Widgets)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Sql)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@DBus)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@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
%endif
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@LinguistTools)
%endif
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
BuildRequires: pkgconfig(gstreamer-audio-1.0)
BuildRequires: pkgconfig(gstreamer-base-1.0)
BuildRequires: pkgconfig(gstreamer-tag-1.0)
%if ! 0%{?centos}
BuildRequires: pkgconfig(libchromaprint)
%endif
BuildRequires: pkgconfig(libpulse)
BuildRequires: pkgconfig(libcdio)
BuildRequires: pkgconfig(libebur128)
BuildRequires: pkgconfig(libgpod-1.0)
BuildRequires: pkgconfig(libmtp)
%if 0%{?suse_version} || 0%{?fedora_version}
@@ -117,7 +99,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 AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net and lyricsmode.com
- Support for multiple backends
- Audio analyzer
- Audio equalizer
@@ -137,22 +119,19 @@ Features:
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
%endif
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7) || "%{?_vendor}" == "openmandriva"
%make_build
%if "%{?_vendor}" == "openmandriva"
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@ -DENABLE_TRANSLATIONS=OFF
%make_build
%else
%cmake_build
%{cmake} -DQT_VERSION_MAJOR=@QT_VERSION_MAJOR@
%cmake_build
%endif
%install
%if 0%{?centos}
%make_install
%if "%{?_vendor}" == "openmandriva"
%make_install -C build
%else
%if 0%{?mageia} || "%{?_vendor}" == "openmandriva"
%make_install -C build
%else
%cmake_install
%endif
%cmake_install
%endif
%if 0%{?suse_version}
@@ -161,10 +140,10 @@ Features:
%check
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicplayer.strawberry.desktop
%if 0%{?fedora_version}
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
%else
%if 0%{?suse_version}
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawberrymusicplayer.strawberry.appdata.xml
%else
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/org.strawberrymusicplayer.strawberry.appdata.xml
%endif
%files
@@ -175,13 +154,13 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawberrymusicpl
%{_bindir}/strawberry-tagreader
%{_datadir}/applications/*.desktop
%{_datadir}/icons/hicolor/*/apps/strawberry.*
%if 0%{?fedora_version}
%{_metainfodir}/*.appdata.xml
%else
%{_datadir}/metainfo/*.appdata.xml
%endif
%{_mandir}/man1/%{name}.1.*
%{_mandir}/man1/%{name}-tagreader.1.*
%if 0%{?suse_version}
%{_datadir}/metainfo/*.appdata.xml
%else
%{_metainfodir}/*.appdata.xml
%endif
%changelog
* @RPM_DATE@ Jonas Kvinge <jonas@jkvinge.net> - @STRAWBERRY_VERSION_RPM_V@

View File

@@ -1,53 +1,68 @@
!define build_type ""
!define compiler "unknown"
!define arch "unknown"
!if "@ARCH@" == "x86"
!define arch_x86
!undef arch
!define arch "x86"
!endif
!if "@ARCH@" == "i686-w64-mingw32.shared"
!define arch_x86
!undef arch
!define arch "x86"
!endif
!if "@ARCH@" == "x86_64"
!define arch_x64
!undef arch
!define arch "x64"
!endif
!if "@ARCH@" == "x86_64-w64-mingw32.shared"
!define arch_x64
!undef arch
!define arch "x64"
!endif
!if "@MINGW@" == "1"
!define mingw
!undef compiler
!define compiler "mingw"
!endif
!if "@MSVC@" == "1"
!define msvc
!undef compiler
!define compiler "msvc"
!endif
!if "@CMAKE_BUILD_TYPE@" == "Release"
!define release
!if "@ARCH@" == "x86"
!define arch_x86
!else if "@ARCH@" == "i686"
!define arch_x86
!else if "@ARCH@" == "i686-w64-mingw32.shared"
!define arch_x86
!else if "@ARCH@" == "x64"
!define arch_x64
!else if "@ARCH@" == "x86_64"
!define arch_x64
!else if "@ARCH@" == "x86_64-w64-mingw32.shared"
!define arch_x64
!endif
!if "@CMAKE_BUILD_TYPE@" == "Debug"
!ifdef arch_x86
!define arch "x86"
!endif
!ifdef arch_x64
!define arch "x64"
!endif
!if "@CMAKE_BUILD_TYPE@" == "Release"
!define release
!else if "@CMAKE_BUILD_TYPE@" == "RelWithDebInfo"
!define release
!else if "@CMAKE_BUILD_TYPE@" == "Debug"
!define debug
!undef build_type
!endif
!ifdef release
!define build_type ""
!endif
!ifdef debug
!define build_type "-Debug"
!endif
!ifndef compiler
!error "Missing compiler."
!endif
!ifndef build_type
!error "Missing build type."
!endif
!ifndef arch
!error "Missing arch."
!endif
!ifdef debug
!define PRODUCT_NAME "Strawberry Music Player Debug"
!define PRODUCT_NAME_SHORT "Strawberry"
@@ -121,13 +136,14 @@ SetCompressor /SOLID lzma
ReserveFile "${NSISDIR}\Plugins\x86-unicode\INetC.dll"
!endif
!define LockedListPATH $InstallDir
; Installer pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE COPYING
Page Custom LockedListPageShow
!insertmacro MUI_PAGE_DIRECTORY
!ifdef msvc
Page Custom InstallMSVCRuntime
!endif
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
@@ -166,22 +182,39 @@ FunctionEnd
Function CheckPreviousInstall
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
StrCmp $R0 "" done
StrCmp $R0 "" Done
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
"${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the \
previous version or `Cancel` to cancel this upgrade." \
IDOK uninst
${IfNot} ${Silent}
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "${PRODUCT_NAME} is already installed. $\n$\nClick `OK` to remove the previous version or `Cancel` to cancel this upgrade." IDOK Uninstall
Abort
; Run the uninstaller
uninst:
ClearErrors
ExecWait '$R0' ; Do not copy the uninstaller to a temp file
${EndIf}
done:
Uninstall:
ClearErrors
${If} ${Silent}
ExecWait '$R0 /S'
${Else}
ExecWait '$R0'
${EndIf}
Done:
FunctionEnd
!ifdef msvc
!define vc_redist_file "vc_redist.${arch}.exe"
Function InstallMSVCRuntime
${registry::Read} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${arch}" "Version" $R0 $R1
${If} $R0 == ""
SetDetailsView hide
inetc::get /caption "Downloading..." "https://aka.ms/vs/17/release/${vc_redist_file}" "$TEMP\${vc_redist_file}" /end
ExecWait '"$TEMP\${vc_redist_file}" /install /passive'
Delete "$TEMP\${vc_redist_file}"
SetDetailsView show
${EndIf}
FunctionEnd
!endif
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
@@ -203,6 +236,7 @@ Section "Strawberry" Strawberry
File "strawberry.ico"
File "sqlite3.exe"
File "gst-launch-1.0.exe"
File "gst-play-1.0.exe"
File "gst-discoverer-1.0.exe"
; MinGW specific files
@@ -220,31 +254,29 @@ Section "Strawberry" Strawberry
File "libssl-3-x64.dll"
!endif
File "avcodec-59.dll"
File "avfilter-8.dll"
File "avformat-59.dll"
File "avutil-57.dll"
File "libFLAC-12.dll"
File "libbrotlicommon.dll"
File "libbrotlidec.dll"
File "libbrotlienc.dll"
File "libbs2b-0.dll"
File "libbz2.dll"
File "libcdio-19.dll"
File "libchromaprint.dll"
File "libdl.dll"
File "libfdk-aac-2.dll"
File "libffi-8.dll"
File "libFLAC-8.dll"
File "libfreetype-6.dll"
File "libebur128.dll"
File "libfaac-0.dll"
File "libfaad-2.dll"
File "libfftw3-3.dll"
File "libfdk-aac-2.dll"
File "libffi-8.dll"
File "libfreetype-6.dll"
File "libgcrypt-20.dll"
File "libgio-2.0-0.dll"
File "libglib-2.0-0.dll"
File "libgme.dll"
File "libgmodule-2.0-0.dll"
File "libgmp-10.dll"
File "libgnutls-30.dll"
File "libgobject-2.0-0.dll"
File "libgpg-error-0.dll"
File "libgstadaptivedemux-1.0-0.dll"
File "libgstapp-1.0-0.dll"
File "libgstaudio-1.0-0.dll"
@@ -268,6 +300,7 @@ Section "Strawberry" Strawberry
File "libidn2-0.dll"
File "libintl-8.dll"
File "libjpeg-9.dll"
File "libkdsingleapplication-qt6.dll"
File "liblzma-5.dll"
File "libmp3lame-0.dll"
File "libmpcdec.dll"
@@ -278,20 +311,18 @@ Section "Strawberry" Strawberry
File "libopenmpt-0.dll"
File "libopus-0.dll"
File "liborc-0.4-0.dll"
File "libpcre-1.dll"
File "libpcre2-16-0.dll"
File "libpng16-16.dll"
File "libprotobuf-30.dll"
File "libpsl-5.dll"
File "libqtsparkle-qt6.dll"
File "libsoup-2.4-1.dll"
File "libsoup-3.0-0.dll"
File "libspeex-1.dll"
File "libsqlite3-0.dll"
File "libssp-0.dll"
File "libstdc++-6.dll"
File "libtag.dll"
File "libtasn1-6.dll"
File "libunistring-2.dll"
File "libtwolame-0.dll"
File "libunistring-5.dll"
File "libvorbis-0.dll"
File "libvorbisenc-2.dll"
File "libvorbisfile-3.dll"
@@ -299,18 +330,63 @@ Section "Strawberry" Strawberry
File "libwinpthread-1.dll"
File "libxml2-2.dll"
File "libzstd.dll"
File "postproc-56.dll"
File "swresample-4.dll"
File "swscale-6.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"
File "libmman.dll"
File "libmpfr-6.dll"
File "libpcre2-8d.dll"
File "libpcre2-16d.dll"
File "libreadline8.dll"
File "libtermcap.dll"
File "libabsl_graphcycles_internal.dll"
!else
File "libpcre2-8.dll"
File "libpcre2-16.dll"
!endif
!endif ; MinGW
@@ -328,17 +404,18 @@ Section "Strawberry" Strawberry
File "libssl-3-x64.dll"
!endif
File "FLAC.dll"
File "brotlicommon.dll"
File "brotlidec.dll"
File "chromaprint.dll"
File "faad.dll"
File "ebur128.dll"
File "faad-2.dll"
File "fdk-aac.dll"
File "ffi-7.dll"
File "FLAC.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"
@@ -358,47 +435,74 @@ Section "Strawberry" Strawberry
File "gsturidownloader-1.0-0.dll"
File "gstvideo-1.0-0.dll"
File "gstwinrt-1.0-0.dll"
File "fftw3.dll"
File "harfbuzz.dll"
File "intl-8.dll"
File "jpeg62.dll"
File "kdsingleapplication-qt6.dll"
File "libbs2b.dll"
File "libfaac_dll.dll"
File "libiconv.dll"
File "liblzma.dll"
File "libmp3lame.dll"
File "libopenmpt.dll"
File "libspeex.dll"
File "mpcdec.dll"
File "mpg123.dll"
File "nghttp2.dll"
File "ogg.dll"
File "opus.dll"
File "orc-0.4-0.dll"
File "psl-5.dll"
File "qtsparkle-qt6.dll"
File "soup-2.4-1.dll"
File "soup-3.0-0.dll"
File "sqlite3.dll"
File "tag.dll"
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 "libprotobuf.dll"
File "libspeex.dll"
File "libxml2.dll"
File "pcre2-8.dll"
File "pcre2-16.dll"
File "twolame.dll"
File "zlib.dll"
!endif
!ifdef debug
File "freetyped.dll"
File "libiconvd.dll"
File "libpng16d.dll"
File "libprotobufd.dll"
File "libspeexd.dll"
File "libxml2d.dll"
File "pcre2-8d.dll"
File "pcre2-16d.dll"
File "twolamed.dll"
File "zlibd.dll"
!endif
; Used by libfftw3-3.dll because fftw is compiled with MinGW.
!ifdef arch_x86
File "libgcc_s_sjlj-1.dll"
File "libwinpthread-1.dll"
!endif
!endif ; MSVC
; Common files
File "icudt74.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 "Qt6Concurrentd.dll"
File "Qt6Cored.dll"
File "Qt6Guid.dll"
@@ -406,6 +510,8 @@ Section "Strawberry" Strawberry
File "Qt6Sqld.dll"
File "Qt6Widgetsd.dll"
!else
File "icuin74.dll"
File "icuuc74.dll"
File "Qt6Concurrent.dll"
File "Qt6Core.dll"
File "Qt6Gui.dll"
@@ -414,6 +520,14 @@ 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"
; Register Strawberry with Default Programs
Var /GLOBAL AppIcon
Var /GLOBAL AppExe
@@ -479,8 +593,10 @@ SectionEnd
Section "Qt TLS plugins" tls
SetOutPath "$INSTDIR\tls"
!ifdef msvc && debug
File "/oname=qschannelbackendd.dll" "tls\qschannelbackendd.dll"
File "/oname=qopensslbackendd.dll" "tls\qopensslbackendd.dll"
!else
File "/oname=qschannelbackend.dll" "tls\qschannelbackend.dll"
File "/oname=qopensslbackend.dll" "tls\qopensslbackend.dll"
!endif
SectionEnd
@@ -526,7 +642,6 @@ Section "Gstreamer plugins" gstreamer-plugins
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=libgstcdio.dll" "gstreamer-plugins\libgstcdio.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"
@@ -536,8 +651,11 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstfdkaac.dll" "gstreamer-plugins\libgstfdkaac.dll"
File "/oname=libgstflac.dll" "gstreamer-plugins\libgstflac.dll"
File "/oname=libgstgio.dll" "gstreamer-plugins\libgstgio.dll"
File "/oname=libgstgme.dll" "gstreamer-plugins\libgstgme.dll"
File "/oname=libgsthls.dll" "gstreamer-plugins\libgsthls.dll"
File "/oname=libgsticydemux.dll" "gstreamer-plugins\libgsticydemux.dll"
File "/oname=libgstid3demux.dll" "gstreamer-plugins\libgstid3demux.dll"
File "/oname=libgstid3tag.dll" "gstreamer-plugins\libgstid3tag.dll"
File "/oname=libgstisomp4.dll" "gstreamer-plugins\libgstisomp4.dll"
File "/oname=libgstlame.dll" "gstreamer-plugins\libgstlame.dll"
File "/oname=libgstlibav.dll" "gstreamer-plugins\libgstlibav.dll"
@@ -557,17 +675,20 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=libgstspeex.dll" "gstreamer-plugins\libgstspeex.dll"
File "/oname=libgsttaglib.dll" "gstreamer-plugins\libgsttaglib.dll"
File "/oname=libgsttcp.dll" "gstreamer-plugins\libgsttcp.dll"
File "/oname=libgsttwolame.dll" "gstreamer-plugins\libgsttwolame.dll"
File "/oname=libgsttypefindfunctions.dll" "gstreamer-plugins\libgsttypefindfunctions.dll"
File "/oname=libgstudp.dll" "gstreamer-plugins\libgstudp.dll"
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=libgstwavenc.dll" "gstreamer-plugins\libgstwavenc.dll"
File "/oname=libgstwavpack.dll" "gstreamer-plugins\libgstwavpack.dll"
File "/oname=libgstwavparse.dll" "gstreamer-plugins\libgstwavparse.dll"
File "/oname=libgstxingmux.dll" "gstreamer-plugins\libgstxingmux.dll"
!endif ; MinGW
!ifdef msvc
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"
@@ -591,30 +712,39 @@ Section "Gstreamer plugins" gstreamer-plugins
File "/oname=gstfdkaac.dll" "gstreamer-plugins\gstfdkaac.dll"
File "/oname=gstflac.dll" "gstreamer-plugins\gstflac.dll"
File "/oname=gstgio.dll" "gstreamer-plugins\gstgio.dll"
File "/oname=gstgme.dll" "gstreamer-plugins\gstgme.dll"
File "/oname=gsthls.dll" "gstreamer-plugins\gsthls.dll"
File "/oname=gsticydemux.dll" "gstreamer-plugins\gsticydemux.dll"
File "/oname=gstid3demux.dll" "gstreamer-plugins\gstid3demux.dll"
File "/oname=gstid3tag.dll" "gstreamer-plugins\gstid3tag.dll"
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=gstmpg123.dll" "gstreamer-plugins\gstmpg123.dll"
File "/oname=gstmusepack.dll" "gstreamer-plugins\gstmusepack.dll"
File "/oname=gstogg.dll" "gstreamer-plugins\gstogg.dll"
File "/oname=gstopenmpt.dll" "gstreamer-plugins\gstopenmpt.dll"
File "/oname=gstopus.dll" "gstreamer-plugins\gstopus.dll"
File "/oname=gstopusparse.dll" "gstreamer-plugins\gstopusparse.dll"
File "/oname=gstpbtypes.dll" "gstreamer-plugins\gstpbtypes.dll"
File "/oname=gstplayback.dll" "gstreamer-plugins\gstplayback.dll"
File "/oname=gstreplaygain.dll" "gstreamer-plugins\gstreplaygain.dll"
File "/oname=gstrtp.dll" "gstreamer-plugins\gstrtp.dll"
File "/oname=gstrtsp.dll" "gstreamer-plugins\gstrtsp.dll"
File "/oname=gstspeex.dll" "gstreamer-plugins\gstspeex.dll"
File "/oname=gstsoup.dll" "gstreamer-plugins\gstsoup.dll"
File "/oname=gstspectrum.dll" "gstreamer-plugins\gstspectrum.dll"
File "/oname=gstspeex.dll" "gstreamer-plugins\gstspeex.dll"
File "/oname=gsttaglib.dll" "gstreamer-plugins\gsttaglib.dll"
File "/oname=gsttcp.dll" "gstreamer-plugins\gsttcp.dll"
File "/oname=gsttwolame.dll" "gstreamer-plugins\gsttwolame.dll"
File "/oname=gsttypefindfunctions.dll" "gstreamer-plugins\gsttypefindfunctions.dll"
File "/oname=gstudp.dll" "gstreamer-plugins\gstudp.dll"
File "/oname=gstvolume.dll" "gstreamer-plugins\gstvolume.dll"
File "/oname=gstvorbis.dll" "gstreamer-plugins\gstvorbis.dll"
File "/oname=gstwasapi.dll" "gstreamer-plugins\gstwasapi.dll"
File "/oname=gstwasapi2.dll" "gstreamer-plugins\gstwasapi2.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=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"
@@ -662,6 +792,7 @@ Section "Uninstall"
Delete "$INSTDIR\strawberry.ico"
Delete "$INSTDIR\sqlite3.exe"
Delete "$INSTDIR\gst-launch-1.0.exe"
Delete "$INSTDIR\gst-play-1.0.exe"
Delete "$INSTDIR\gst-discoverer-1.0.exe"
; MinGW specific files
@@ -680,31 +811,29 @@ Section "Uninstall"
Delete "$INSTDIR\libssl-3-x64.dll"
!endif
Delete "$INSTDIR\avcodec-59.dll"
Delete "$INSTDIR\avfilter-8.dll"
Delete "$INSTDIR\avformat-59.dll"
Delete "$INSTDIR\avutil-57.dll"
Delete "$INSTDIR\libFLAC-12.dll"
Delete "$INSTDIR\libbrotlicommon.dll"
Delete "$INSTDIR\libbrotlidec.dll"
Delete "$INSTDIR\libbrotlienc.dll"
Delete "$INSTDIR\libbs2b-0.dll"
Delete "$INSTDIR\libbz2.dll"
Delete "$INSTDIR\libcdio-19.dll"
Delete "$INSTDIR\libchromaprint.dll"
Delete "$INSTDIR\libdl.dll"
Delete "$INSTDIR\libfdk-aac-2.dll"
Delete "$INSTDIR\libffi-8.dll"
Delete "$INSTDIR\libFLAC-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libebur128.dll"
Delete "$INSTDIR\libfaac-0.dll"
Delete "$INSTDIR\libfaad-2.dll"
Delete "$INSTDIR\libfftw3-3.dll"
Delete "$INSTDIR\libfdk-aac-2.dll"
Delete "$INSTDIR\libffi-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libgcrypt-20.dll"
Delete "$INSTDIR\libgio-2.0-0.dll"
Delete "$INSTDIR\libglib-2.0-0.dll"
Delete "$INSTDIR\libgme.dll"
Delete "$INSTDIR\libgmodule-2.0-0.dll"
Delete "$INSTDIR\libgmp-10.dll"
Delete "$INSTDIR\libgnutls-30.dll"
Delete "$INSTDIR\libgobject-2.0-0.dll"
Delete "$INSTDIR\libgpg-error-0.dll"
Delete "$INSTDIR\libgstadaptivedemux-1.0-0.dll"
Delete "$INSTDIR\libgstapp-1.0-0.dll"
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
@@ -728,6 +857,7 @@ Section "Uninstall"
Delete "$INSTDIR\libidn2-0.dll"
Delete "$INSTDIR\libintl-8.dll"
Delete "$INSTDIR\libjpeg-9.dll"
Delete "$INSTDIR\libkdsingleapplication-qt6.dll"
Delete "$INSTDIR\liblzma-5.dll"
Delete "$INSTDIR\libmp3lame-0.dll"
Delete "$INSTDIR\libmpcdec.dll"
@@ -738,20 +868,18 @@ Section "Uninstall"
Delete "$INSTDIR\libopenmpt-0.dll"
Delete "$INSTDIR\libopus-0.dll"
Delete "$INSTDIR\liborc-0.4-0.dll"
Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-30.dll"
Delete "$INSTDIR\libpsl-5.dll"
Delete "$INSTDIR\libqtsparkle-qt6.dll"
Delete "$INSTDIR\libsoup-2.4-1.dll"
Delete "$INSTDIR\libsoup-3.0-0.dll"
Delete "$INSTDIR\libspeex-1.dll"
Delete "$INSTDIR\libsqlite3-0.dll"
Delete "$INSTDIR\libssp-0.dll"
Delete "$INSTDIR\libstdc++-6.dll"
Delete "$INSTDIR\libtag.dll"
Delete "$INSTDIR\libtasn1-6.dll"
Delete "$INSTDIR\libunistring-2.dll"
Delete "$INSTDIR\libtwolame-0.dll"
Delete "$INSTDIR\libunistring-5.dll"
Delete "$INSTDIR\libvorbis-0.dll"
Delete "$INSTDIR\libvorbisenc-2.dll"
Delete "$INSTDIR\libvorbisfile-3.dll"
@@ -759,19 +887,63 @@ Section "Uninstall"
Delete "$INSTDIR\libwinpthread-1.dll"
Delete "$INSTDIR\libxml2-2.dll"
Delete "$INSTDIR\libzstd.dll"
Delete "$INSTDIR\postproc-56.dll"
Delete "$INSTDIR\swresample-4.dll"
Delete "$INSTDIR\swscale-6.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"
Delete "$INSTDIR\libmman.dll"
Delete "$INSTDIR\libmpfr-6.dll"
Delete "$INSTDIR\libpcre2-8d.dll"
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"
!endif
!endif ; MinGW
@@ -789,17 +961,18 @@ Section "Uninstall"
Delete "$INSTDIR\libssl-3-x64.dll"
!endif
Delete "$INSTDIR\FLAC.dll"
Delete "$INSTDIR\brotlicommon.dll"
Delete "$INSTDIR\brotlidec.dll"
Delete "$INSTDIR\chromaprint.dll"
Delete "$INSTDIR\faad.dll"
Delete "$INSTDIR\ebur128.dll"
Delete "$INSTDIR\faad-2.dll"
Delete "$INSTDIR\fdk-aac.dll"
Delete "$INSTDIR\ffi-7.dll"
Delete "$INSTDIR\FLAC.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"
@@ -819,47 +992,73 @@ Section "Uninstall"
Delete "$INSTDIR\gsturidownloader-1.0-0.dll"
Delete "$INSTDIR\gstvideo-1.0-0.dll"
Delete "$INSTDIR\gstwinrt-1.0-0.dll"
Delete "$INSTDIR\fftw3.dll"
Delete "$INSTDIR\harfbuzz.dll"
Delete "$INSTDIR\intl-8.dll"
Delete "$INSTDIR\jpeg62.dll"
Delete "$INSTDIR\kdsingleapplication-qt6.dll"
Delete "$INSTDIR\libbs2b.dll"
Delete "$INSTDIR\libfaac_dll.dll"
Delete "$INSTDIR\libiconv.dll"
Delete "$INSTDIR\liblzma.dll"
Delete "$INSTDIR\libmp3lame.dll"
Delete "$INSTDIR\libopenmpt.dll"
Delete "$INSTDIR\libspeex.dll"
Delete "$INSTDIR\mpcdec.dll"
Delete "$INSTDIR\mpg123.dll"
Delete "$INSTDIR\nghttp2.dll"
Delete "$INSTDIR\ogg.dll"
Delete "$INSTDIR\opus.dll"
Delete "$INSTDIR\orc-0.4-0.dll"
Delete "$INSTDIR\psl-5.dll"
Delete "$INSTDIR\qtsparkle-qt6.dll"
Delete "$INSTDIR\soup-2.4-1.dll"
Delete "$INSTDIR\soup-3.0-0.dll"
Delete "$INSTDIR\sqlite3.dll"
Delete "$INSTDIR\tag.dll"
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\libprotobuf.dll"
Delete "$INSTDIR\libspeex.dll"
Delete "$INSTDIR\libxml2.dll"
Delete "$INSTDIR\pcre2-8.dll"
Delete "$INSTDIR\pcre2-16.dll"
Delete "$INSTDIR\twolame.dll"
Delete "$INSTDIR\zlib.dll"
!endif
!ifdef debug
Delete "$INSTDIR\freetyped.dll"
Delete "$INSTDIR\libiconvd.dll"
Delete "$INSTDIR\libpng16d.dll"
Delete "$INSTDIR\libprotobufd.dll"
Delete "$INSTDIR\libspeexd.dll"
Delete "$INSTDIR\libxml2d.dll"
Delete "$INSTDIR\pcre2-8d.dll"
Delete "$INSTDIR\pcre2-16d.dll"
Delete "$INSTDIR\twolamed.dll"
Delete "$INSTDIR\zlibd.dll"
!endif
!ifdef arch_x86
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libwinpthread-1.dll"
!endif
!endif ; MSVC
; Common files
Delete "$INSTDIR\icudt74.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\Qt6Concurrentd.dll"
Delete "$INSTDIR\Qt6Cored.dll"
Delete "$INSTDIR\Qt6Guid.dll"
@@ -867,6 +1066,8 @@ Section "Uninstall"
Delete "$INSTDIR\Qt6Sqld.dll"
Delete "$INSTDIR\Qt6Widgetsd.dll"
!else
Delete "$INSTDIR\icuin74.dll"
Delete "$INSTDIR\icuuc74.dll"
Delete "$INSTDIR\Qt6Concurrent.dll"
Delete "$INSTDIR\Qt6Core.dll"
Delete "$INSTDIR\Qt6Gui.dll"
@@ -875,6 +1076,14 @@ 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"
!ifdef mingw
Delete "$INSTDIR\gio-modules\libgiognutls.dll"
Delete "$INSTDIR\gio-modules\libgioopenssl.dll"
@@ -887,6 +1096,7 @@ Section "Uninstall"
!ifdef msvc && debug
Delete "$INSTDIR\platforms\qwindowsd.dll"
Delete "$INSTDIR\styles\qwindowsvistastyled.dll"
Delete "$INSTDIR\tls\qschannelbackendd.dll"
Delete "$INSTDIR\tls\qopensslbackendd.dll"
Delete "$INSTDIR\sqldrivers\qsqlited.dll"
Delete "$INSTDIR\imageformats\qgifd.dll"
@@ -895,6 +1105,7 @@ Section "Uninstall"
!else
Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\styles\qwindowsvistastyle.dll"
Delete "$INSTDIR\tls\qschannelbackend.dll"
Delete "$INSTDIR\tls\qopensslbackend.dll"
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
Delete "$INSTDIR\imageformats\qgif.dll"
@@ -920,7 +1131,6 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstaudiotestsrc.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstbs2b.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcdio.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdash.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
@@ -930,8 +1140,11 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstfdkaac.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstflac.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstgio.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstgme.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsthls.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsticydemux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstid3demux.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstid3tag.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
@@ -951,11 +1164,13 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\libgstspeex.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttaglib.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttcp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttwolame.dll"
Delete "$INSTDIR\gstreamer-plugins\libgsttypefindfunctions.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstudp.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvolume.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstvorbis.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavenc.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavpack.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstwavparse.dll"
Delete "$INSTDIR\gstreamer-plugins\libgstxingmux.dll"
@@ -964,6 +1179,7 @@ Section "Uninstall"
; MSVC GStreamer plugins
!ifdef msvc
Delete "$INSTDIR\gstreamer-plugins\gstaes.dll"
Delete "$INSTDIR\gstreamer-plugins\gstaiff.dll"
Delete "$INSTDIR\gstreamer-plugins\gstapetag.dll"
Delete "$INSTDIR\gstreamer-plugins\gstapp.dll"
@@ -987,36 +1203,44 @@ Section "Uninstall"
Delete "$INSTDIR\gstreamer-plugins\gstfdkaac.dll"
Delete "$INSTDIR\gstreamer-plugins\gstflac.dll"
Delete "$INSTDIR\gstreamer-plugins\gstgio.dll"
Delete "$INSTDIR\gstreamer-plugins\gstgme.dll"
Delete "$INSTDIR\gstreamer-plugins\gsthls.dll"
Delete "$INSTDIR\gstreamer-plugins\gsticydemux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstid3demux.dll"
Delete "$INSTDIR\gstreamer-plugins\gstid3tag.dll"
Delete "$INSTDIR\gstreamer-plugins\gstisomp4.dll"
Delete "$INSTDIR\gstreamer-plugins\gstlame.dll"
Delete "$INSTDIR\gstreamer-plugins\gstlibav.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmpg123.dll"
Delete "$INSTDIR\gstreamer-plugins\gstmusepack.dll"
Delete "$INSTDIR\gstreamer-plugins\gstogg.dll"
Delete "$INSTDIR\gstreamer-plugins\gstopenmpt.dll"
Delete "$INSTDIR\gstreamer-plugins\gstopus.dll"
Delete "$INSTDIR\gstreamer-plugins\gstopusparse.dll"
Delete "$INSTDIR\gstreamer-plugins\gstpbtypes.dll"
Delete "$INSTDIR\gstreamer-plugins\gstplayback.dll"
Delete "$INSTDIR\gstreamer-plugins\gstreplaygain.dll"
Delete "$INSTDIR\gstreamer-plugins\gstrtp.dll"
Delete "$INSTDIR\gstreamer-plugins\gstrtsp.dll"
Delete "$INSTDIR\gstreamer-plugins\gstspeex.dll"
Delete "$INSTDIR\gstreamer-plugins\gstsoup.dll"
Delete "$INSTDIR\gstreamer-plugins\gstspectrum.dll"
Delete "$INSTDIR\gstreamer-plugins\gstspeex.dll"
Delete "$INSTDIR\gstreamer-plugins\gsttaglib.dll"
Delete "$INSTDIR\gstreamer-plugins\gsttcp.dll"
Delete "$INSTDIR\gstreamer-plugins\gsttwolame.dll"
Delete "$INSTDIR\gstreamer-plugins\gsttypefindfunctions.dll"
Delete "$INSTDIR\gstreamer-plugins\gstudp.dll"
Delete "$INSTDIR\gstreamer-plugins\gstvolume.dll"
Delete "$INSTDIR\gstreamer-plugins\gstvorbis.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwasapi.dll"
Delete "$INSTDIR\gstreamer-plugins\gstwasapi2.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"
!endif ; msvc
Delete $INSTDIR\Uninstall.exe"
Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders.
RMDir "$INSTDIR\platforms"

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.7)
set(SOURCES gstfastspectrum.cpp gstmoodbarplugin.cpp)
@@ -31,5 +31,5 @@ target_link_libraries(gstmoodbar PRIVATE
${GSTREAMER_BASE_LIBRARIES}
${GSTREAMER_AUDIO_LIBRARIES}
${FFTW3_FFTW_LIBRARY}
${QtCore_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
)

View File

@@ -50,7 +50,14 @@ enum {
} // 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);
@@ -79,9 +86,9 @@ static void gst_fastspectrum_class_init(GstFastSpectrumClass *klass) {
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, GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
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, 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");

View File

@@ -47,7 +47,7 @@ class QMutex;
typedef void (*GstFastSpectrumInputData)(const guint8 *in, double *out, guint len, double max_value, guint op, guint nfft);
typedef std::function<void(double *magnitudes, int size)> OutputCallback;
using OutputCallback = std::function<void(double *magnitudes, int size)>;
struct GstFastSpectrum {
GstAudioFilter parent;

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.7)
set(SOURCES
core/logging.cpp
@@ -34,8 +34,8 @@ endif()
target_link_libraries(libstrawberry-common PRIVATE
${CMAKE_THREAD_LIBS_INIT}
${GLIB_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
)
if(Backtrace_FOUND)

View File

@@ -22,12 +22,22 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wold-style-cast"
#endif
#include <glib.h>
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#ifdef HAVE_BACKTRACE
# include <execinfo.h>
@@ -126,7 +136,8 @@ class LoggedDebug : public DebugBase<LoggedDebug> {
static void MessageHandler(QtMsgType type, const QMessageLogContext&, const QString &message) {
if (message.startsWith(kMessageHandlerMagic)) {
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout, "%s\n", message.toUtf8().data() + kMessageHandlerMagicLength);
QByteArray message_data = message.toUtf8();
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout, "%s\n", message_data.constData() + kMessageHandlerMagicLength);
fflush(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout);
return;
}

View File

@@ -98,7 +98,7 @@ void _MessageHandlerBase::DeviceReadyRead() {
void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
QDataStream s(device_);
s << quint32(data.length());
s << static_cast<quint32>(data.length());
s.writeRawData(data.data(), static_cast<int>(data.length()));
// Sorry.

View File

@@ -35,9 +35,6 @@
class QIODevice;
#define QStringFromStdString(x) QString::fromUtf8((x).data(), (x).size())
#define DataCommaSizeFromQString(x) (x).toUtf8().constData(), (x).toUtf8().length()
// 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.
@@ -85,8 +82,8 @@ class AbstractMessageHandler : public _MessageHandlerBase {
AbstractMessageHandler(QIODevice *device, QObject *parent);
~AbstractMessageHandler() override { AbstractMessageHandler::AbortAll(); }
typedef MT MessageType;
typedef MessageReply<MT> ReplyType;
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.

View File

@@ -31,15 +31,14 @@
#include <QMutex>
#include <QLocalServer>
#include <QProcess>
#include <QDir>
#include <QFile>
#include <QList>
#include <QQueue>
#include <QString>
#include <QStringList>
#include <QAtomicInt>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QRandomGenerator>
#endif
#include <QRandomGenerator>
#include "core/logging.h"
@@ -76,8 +75,8 @@ class WorkerPool : public _WorkerPoolBase {
explicit WorkerPool(QObject *parent = nullptr);
~WorkerPool() override;
typedef typename HandlerType::MessageType MessageType;
typedef typename HandlerType::ReplyType ReplyType;
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().
@@ -165,10 +164,10 @@ class WorkerPool : public _WorkerPoolBase {
template<typename HandlerType>
WorkerPool<HandlerType>::WorkerPool(QObject *parent)
: _WorkerPoolBase(parent),
worker_count_(1),
next_worker_(0),
next_id_(0) {
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4);
local_server_name_ = qApp->applicationName().toLower();
if (local_server_name_.isEmpty()) {
@@ -228,7 +227,7 @@ void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name)
template<typename HandlerType>
void WorkerPool<HandlerType>::Start() {
QMetaObject::invokeMethod(this, "DoStart");
QMetaObject::invokeMethod(this, &WorkerPool<HandlerType>::DoStart);
}
template<typename HandlerType>
@@ -243,8 +242,12 @@ void WorkerPool<HandlerType>::DoStart() {
QStringList search_path;
search_path << QCoreApplication::applicationDirPath();
#if defined(Q_OS_MACOS) && defined(USE_BUNDLE)
search_path << QCoreApplication::applicationDirPath() + "/" + USE_BUNDLE_DIR;
#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) {
@@ -290,11 +293,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
// Create a server, find an unused name and start listening
forever {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
const quint32 unique_number = QRandomGenerator::global()->bounded(static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
#else
const quint32 unique_number = qrand() ^ (static_cast<quint32>(quint64(this) & 0xFFFFFFFF));
#endif
const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number);
if (worker->local_server_->listen(name)) {
@@ -419,7 +418,7 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
}
// Wake up the main thread
QMetaObject::invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, &WorkerPool<HandlerType>::SendQueuedMessages, Qt::QueuedConnection);
return reply;

View File

@@ -1,18 +1,24 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.7)
set(MESSAGES tagreadermessages.proto)
set(SOURCES tagreaderbase.cpp)
# 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)
list(APPEND SOURCES tagreadertaglib.cpp tagreadergme.cpp)
endif()
if(USE_TAGPARSER AND TAGPARSER_FOUND)
list(APPEND SOURCES tagreadertagparser.cpp)
endif()
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
link_directories(
${GLIB_LIBRARY_DIRS}
${PROTOBUF_LIBRARY_DIRS}
@@ -43,9 +49,10 @@ target_include_directories(libstrawberry-tagreader PRIVATE
target_link_libraries(libstrawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
${PROTOBUF_LIBRARY}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
${Protobuf_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Gui
libstrawberry-common
)
@@ -58,3 +65,5 @@ 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,5 +1,5 @@
/* This file is part of Strawberry.
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
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
@@ -19,9 +19,125 @@
#include <string>
#include "tagreaderbase.h"
#include <QByteArray>
#include <QString>
#include <QIODevice>
#include <QFile>
#include <QBuffer>
#include <QImage>
#include <QMimeDatabase>
const std::string TagReaderBase::kEmbeddedCover = "(embedded)";
#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,5 +1,5 @@
/* This file is part of Strawberry.
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
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
@@ -36,19 +36,33 @@ class TagReaderBase {
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 void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
virtual bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
virtual 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 QString &filename, const QByteArray &data) = 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;
protected:
static const std::string kEmbeddedCover;
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)
};

View File

@@ -0,0 +1,300 @@
/*
* Strawberry Music Player
* Copyright 2022, Eoin O'Neill <eoinoneill1991@gmail.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 "tagreadergme.h"
#include <tag.h>
#include <apefile.h>
#include <QByteArray>
#include <QString>
#include <QChar>
#include <QFileInfo>
#include <QFile>
#include <QTextStream>
#include "utilities/timeconstants.h"
#include "core/logging.h"
#include "core/messagehandler.h"
#include "tagreaderbase.h"
#include "tagreadertaglib.h"
bool GME::IsSupportedFormat(const QFileInfo &file_info) {
return file_info.exists() && (file_info.completeSuffix().endsWith("spc", Qt::CaseInsensitive) || file_info.completeSuffix().endsWith("vgm"), Qt::CaseInsensitive);
}
bool GME::ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
if (file_info.completeSuffix().endsWith("spc"), Qt::CaseInsensitive) {
SPC::Read(file_info, song_info);
return true;
}
if (file_info.completeSuffix().endsWith("vgm", Qt::CaseInsensitive)) {
VGM::Read(file_info, song_info);
return true;
}
return false;
}
quint32 GME::UnpackBytes32(const char *const bytes, size_t length) {
Q_ASSERT(length <= 4 && length > 0);
quint32 value = 0;
for (size_t i = 0; i < length; i++) {
value |= static_cast<unsigned char>(bytes[i]) << (8 * i);
}
return value;
}
void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
QFile file(file_info.filePath());
if (!file.open(QIODevice::ReadOnly)) return;
qLog(Debug) << "Reading tags from SPC file" << file_info.fileName();
// Check for header -- more reliable than file name alone.
if (!file.read(33).startsWith(QString("SNES-SPC700").toLatin1())) return;
// First order of business -- get any tag values that exist within the core file information.
// These only allow for a certain number of bytes per field,
// so they will likely be overwritten either by the id666 standard or the APETAG format (as used by other players, such as foobar and winamp)
//
// Make sure to check id6 documentation before changing the read values!
file.seek(HAS_ID6_OFFSET);
const QByteArray id6_status = file.read(1);
const bool has_id6 = id6_status.length() >= 1 && id6_status[0] == static_cast<char>(xID6_STATUS::ON);
file.seek(SONG_TITLE_OFFSET);
song_info->set_title(QString::fromLatin1(file.read(32)).toStdString());
file.seek(GAME_TITLE_OFFSET);
song_info->set_album(QString::fromLatin1(file.read(32)).toStdString());
file.seek(ARTIST_OFFSET);
song_info->set_artist(QString::fromLatin1(file.read(32)).toStdString());
file.seek(INTRO_LENGTH_OFFSET);
QByteArray length_bytes = file.read(INTRO_LENGTH_SIZE);
if (length_bytes.size() >= INTRO_LENGTH_SIZE) {
quint64 length_in_sec = ConvertSPCStringToNum(length_bytes);
if (!length_in_sec || length_in_sec >= 0x1FFF) {
// This means that parsing the length as a string failed, so get value LE.
length_in_sec = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16);
}
if (length_in_sec < 0x1FFF) {
song_info->set_length_nanosec(length_in_sec * kNsecPerSec);
}
}
file.seek(FADE_LENGTH_OFFSET);
QByteArray fade_bytes = file.read(FADE_LENGTH_SIZE);
if (fade_bytes.size() >= FADE_LENGTH_SIZE) {
quint64 fade_length_in_ms = ConvertSPCStringToNum(fade_bytes);
if (fade_length_in_ms > 0x7FFF) {
fade_length_in_ms = fade_bytes[0] | (fade_bytes[1] << 8) | (fade_bytes[2] << 16) | (fade_bytes[3] << 24);
}
Q_UNUSED(fade_length_in_ms)
}
// Check for XID6 data -- this is infrequently used, but being able to fill in data from this is ideal before trying to rely on APETAG values.
// XID6 format follows EA's binary file format standard named "IFF"
file.seek(XID6_OFFSET);
if (has_id6 && file.read(4) == QString("xid6").toLatin1()) {
QByteArray xid6_head_data = file.read(4);
if (xid6_head_data.size() >= 4) {
qint64 xid6_size = xid6_head_data[0] | (xid6_head_data[1] << 8) | (xid6_head_data[2] << 16) | xid6_head_data[3];
// This should be the size remaining for entire ID6 block, but it seems that most files treat this as the size of the remaining header space...
qLog(Debug) << file_info.fileName() << "has ID6 tag.";
while ((file.pos()) + 4 < XID6_OFFSET + xid6_size) {
QByteArray arr = file.read(4);
if (arr.size() < 4) break;
qint8 id = arr[0];
qint8 type = arr[1];
Q_UNUSED(id);
Q_UNUSED(type);
qint16 length = static_cast<qint16>(arr[2] | (arr[3] << 8));
file.read(GetNextMemAddressAlign32bit(length));
}
}
}
// Music Players that support SPC tend to support additional tagging data as
// an APETAG entry at the bottom of the file instead of writing into the xid6 tagging space.
// This is where a lot of the extra data for a file is stored, such as genre or replaygain data.
// This data is currently supported by TagLib, so we will simply use that for the remaining values.
TagLib::APE::File ape(file_info.filePath().toStdString().data());
if (ape.hasAPETag()) {
TagLib::Tag *tag = ape.tag();
if (!tag) return;
TagReaderTagLib::TStringToStdString(tag->artist(), song_info->mutable_artist());
TagReaderTagLib::TStringToStdString(tag->album(), song_info->mutable_album());
TagReaderTagLib::TStringToStdString(tag->title(), song_info->mutable_title());
TagReaderTagLib::TStringToStdString(tag->genre(), song_info->mutable_genre());
song_info->set_track(static_cast<std::int32_t>(tag->track()));
song_info->set_year(static_cast<std::int32_t>(tag->year()));
}
song_info->set_valid(true);
song_info->set_filetype(spb::tagreader::SongMetadata_FileType_SPC);
}
qint16 GME::SPC::GetNextMemAddressAlign32bit(qint16 input) {
return static_cast<qint16>((input + 0x3) & ~0x3);
// Plus 0x3 for rounding up (not down), AND NOT to flatten out on a 32 bit level.
}
quint64 GME::SPC::ConvertSPCStringToNum(const QByteArray &arr) {
quint64 result = 0;
for (auto it = arr.begin(); it != arr.end(); it++) {
unsigned int num = *it - '0';
if (num > 9) break;
result = (result * 10) + num; // Shift Left and add.
}
return result;
}
void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
QFile file(file_info.filePath());
if (!file.open(QIODevice::ReadOnly)) return;
qLog(Debug) << "Reading tags from VGM file" << file_info.fileName();
if (!file.read(4).startsWith(QString("Vgm ").toLatin1())) return;
file.seek(GD3_TAG_PTR);
QByteArray gd3_head = file.read(4);
if (gd3_head.size() < 4) return;
quint64 pt = GME::UnpackBytes32(gd3_head.constData(), gd3_head.size());
file.seek(SAMPLE_COUNT);
QByteArray sample_count_bytes = file.read(4);
file.seek(LOOP_SAMPLE_COUNT);
QByteArray loop_count_bytes = file.read(4);
quint64 length = 0;
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) return;
file.seek(static_cast<qint64>(GD3_TAG_PTR + pt));
QByteArray gd3_version = file.read(4);
file.seek(file.pos() + 4);
QByteArray gd3_length_bytes = file.read(4);
quint32 gd3_length = GME::UnpackBytes32(gd3_length_bytes.constData(), gd3_length_bytes.size());
QByteArray gd3Data = file.read(gd3_length);
QTextStream fileTagStream(gd3Data, QIODevice::ReadOnly);
// Stored as 16 bit UTF string, two bytes per letter.
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
fileTagStream.setEncoding(QStringConverter::Utf16);
#else
fileTagStream.setCodec("UTF-16");
#endif
QStringList strings = fileTagStream.readLine(0).split(QChar('\0'));
if (strings.count() < 10) return;
// VGM standard dictates string tag data exist in specific order.
// Order alternates between English and Japanese version of data.
// Read GD3 tag standard for more details.
song_info->set_title(strings[0].toStdString());
song_info->set_album(strings[2].toStdString());
song_info->set_artist(strings[6].toStdString());
song_info->set_year(strings[8].left(4).toInt());
song_info->set_length_nanosec(length * kNsecPerMsec);
song_info->set_valid(true);
song_info->set_filetype(spb::tagreader::SongMetadata_FileType_VGM);
}
bool GME::VGM::GetPlaybackLength(const QByteArray &sample_count_bytes, const QByteArray &loop_count_bytes, quint64 &out_length) {
if (sample_count_bytes.size() != 4) return false;
if (loop_count_bytes.size() != 4) return false;
quint64 sample_count = GME::UnpackBytes32(sample_count_bytes.constData(), sample_count_bytes.size());
if (sample_count == 0) return false;
quint64 loop_sample_count = GME::UnpackBytes32(loop_count_bytes.constData(), loop_count_bytes.size());
if (loop_sample_count == 0) {
out_length = sample_count * 1000 / SAMPLE_TIMEBASE;
return true;
}
quint64 intro_length_ms = (sample_count - loop_sample_count) * 1000 / SAMPLE_TIMEBASE;
quint64 loop_length_ms = (loop_sample_count) * 1000 / SAMPLE_TIMEBASE;
out_length = intro_length_ms + (loop_length_ms * 2) + GST_GME_LOOP_TIME_MS;
return true;
}
TagReaderGME::TagReaderGME() = default;
TagReaderGME::~TagReaderGME() = default;
bool TagReaderGME::IsMediaFile(const QString &filename) const {
QFileInfo fileinfo(filename);
return GME::IsSupportedFormat(fileinfo);
}
bool TagReaderGME::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
QFileInfo fileinfo(filename);
return GME::ReadFile(fileinfo, song);
}
bool TagReaderGME::SaveFile(const spb::tagreader::SaveFileRequest&) const {
return false;
}
QByteArray TagReaderGME::LoadEmbeddedArt(const QString&) const {
return QByteArray();
}
bool TagReaderGME::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest&) const {
return false;
}
bool TagReaderGME::SaveSongPlaycountToFile(const QString&, const spb::tagreader::SongMetadata&) const {
return false;
}
bool TagReaderGME::SaveSongRatingToFile(const QString&, const spb::tagreader::SongMetadata&) const {
return false;
}

View File

@@ -0,0 +1,119 @@
/*
* Strawberry Music Player
* Copyright 2022, Eoin O'Neill <eoinoneill1991@gmail.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/>.
*
*/
#ifndef TAGREADERGME_H
#define TAGREADERGME_H
#include <taglib/tstring.h>
#include <QByteArray>
#include <QString>
#include <QFileInfo>
#include "tagreaderbase.h"
#include "tagreadermessages.pb.h"
namespace GME {
bool IsSupportedFormat(const QFileInfo &file_info);
bool ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
uint32_t UnpackBytes32(const char *const bytes, size_t length);
namespace SPC {
// SPC SPEC: http://vspcplay.raphnet.net/spc_file_format.txt
constexpr int HAS_ID6_OFFSET = 0x23;
constexpr int SONG_TITLE_OFFSET = 0x2E;
constexpr int GAME_TITLE_OFFSET = 0x4E;
constexpr int DUMPER_OFFSET = 0x6E;
constexpr int COMMENTS_OFFSET = 0x7E;
// It seems that intro length and fade length are inconsistent from file to file.
// It should be looked into within the GME source code to see how GStreamer gets its values for playback length.
constexpr int INTRO_LENGTH_OFFSET = 0xA9;
constexpr int INTRO_LENGTH_SIZE = 3;
constexpr int FADE_LENGTH_OFFSET = 0xAC;
constexpr int FADE_LENGTH_SIZE = 4;
constexpr int ARTIST_OFFSET = 0xB1;
constexpr int XID6_OFFSET = (0x101C0 + 64);
constexpr int NANO_PER_MS = 1000000;
enum class xID6_STATUS {
ON = 0x26,
OFF = 0x27
};
enum class xID6_ID {
SongName = 0x01,
GameName = 0x02,
ArtistName = 0x03
};
enum class xID6_TYPE {
Length = 0x0,
String = 0x1,
Integer = 0x4
};
void Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
qint16 GetNextMemAddressAlign32bit(qint16 input);
quint64 ConvertSPCStringToNum(const QByteArray &arr);
} // namespace SPC
namespace VGM {
// VGM SPEC:
// http://www.smspower.org/uploads/Music/vgmspec170.txt?sid=17c810c54633b6dd4982f92f718361c1
// GD3 TAG SPEC:
// http://www.smspower.org/uploads/Music/gd3spec100.txt
constexpr int GD3_TAG_PTR = 0x14;
constexpr int SAMPLE_COUNT = 0x18;
constexpr int LOOP_SAMPLE_COUNT = 0x20;
constexpr int SAMPLE_TIMEBASE = 44100;
constexpr int GST_GME_LOOP_TIME_MS = 8000;
void Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
// Takes in two QByteArrays, expected to be 4 bytes long. Desired length is returned via output parameter out_length. Returns false on error.
bool GetPlaybackLength(const QByteArray &sample_count_bytes, const QByteArray &loop_count_bytes, quint64 &out_length);
} // namespace VGM
} // namespace GME
// TagReaderGME
// Fulfills Strawberry's Intended interface for tag reading.
class TagReaderGME : public TagReaderBase {
public:
explicit TagReaderGME();
~TagReaderGME();
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;
};
#endif // TAGREADERGME_H

View File

@@ -27,6 +27,8 @@ message SongMetadata {
S3M = 19;
XM = 20;
IT = 21;
SPC = 22;
VGM = 23;
CDDA = 90;
STREAM = 91;
}
@@ -67,29 +69,26 @@ message SongMetadata {
optional int64 lastplayed = 29;
optional int64 lastseen = 30;
optional string art_automatic = 31;
optional bool art_embedded = 31;
optional float rating = 32;
optional bool suspicious_tags = 40;
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)
message ReadFileRequest {
optional string filename = 1;
}
optional bool suspicious_tags = 50;
message ReadFileResponse {
optional SongMetadata metadata = 1;
}
message SaveFileRequest {
optional string filename = 1;
optional SongMetadata metadata = 2;
}
message SaveFileResponse {
optional bool success = 1;
}
message IsMediaFileRequest {
@@ -98,6 +97,33 @@ message IsMediaFileRequest {
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 {
@@ -106,15 +132,19 @@ message LoadEmbeddedArtRequest {
message LoadEmbeddedArtResponse {
optional bytes data = 1;
optional string error = 2;
}
message SaveEmbeddedArtRequest {
optional string filename = 1;
optional bytes data = 2;
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 {
@@ -124,6 +154,7 @@ message SaveSongPlaycountToFileRequest {
message SaveSongPlaycountToFileResponse {
optional bool success = 1;
optional string error = 2;
}
message SaveSongRatingToFileRequest {
@@ -133,6 +164,7 @@ message SaveSongRatingToFileRequest {
message SaveSongRatingToFileResponse {
optional bool success = 1;
optional string error = 2;
}
message Message {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* This file is part of Strawberry.
Copyright 2013, David Sansome <me@davidsansome.com>
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
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
@@ -29,8 +29,12 @@
#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>
@@ -50,25 +54,24 @@ class TagReaderTagLib : public TagReaderBase {
bool IsMediaFile(const QString &filename) const override;
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
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 QString &filename, const QByteArray &data) 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;
static void Decode(const TagLib::String &tag, std::string *output);
static void Decode(const QString &tag, std::string *output);
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_comments, const 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;
@@ -79,10 +82,25 @@ class TagReaderTagLib : public TagReaderBase {
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
static float ConvertPOPMRating(const int POPM_rating);
static int ConvertToPOPMRating(const float rating);
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_;

View File

@@ -22,6 +22,7 @@
#include <string>
#include <memory>
#include <algorithm>
#include <vector>
#include <sys/stat.h>
#include <tagparser/mediafileinfo.h>
@@ -41,7 +42,7 @@
#include "core/logging.h"
#include "core/messagehandler.h"
#include "core/timeconstants.h"
#include "utilities/timeconstants.h"
TagReaderTagParser::TagReaderTagParser() = default;
@@ -79,7 +80,7 @@ bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
}
const auto tracks = taginfo.tracks();
for (const auto track : tracks) {
for (TagParser::AbstractTrack *track : tracks) {
if (track->mediaType() == TagParser::MediaType::Audio) {
taginfo.close();
return true;
@@ -93,25 +94,28 @@ bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
}
void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
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;
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(DataCommaSizeFromQString(fileinfo.fileName()));
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() ? fileinfo.lastModified().toSecsSinceEpoch() : 0);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
song->set_ctime(fileinfo.birthTime().isValid() ? fileinfo.birthTime().toSecsSinceEpoch() : fileinfo.lastModified().isValid() ? fileinfo.lastModified().toSecsSinceEpoch() : 0);
#else
song->set_ctime(fileinfo.created().isValid() ? fileinfo.created().toSecsSinceEpoch() : fileinfo.lastModified().isValid() ? fileinfo.lastModified().toSecsSinceEpoch() : 0);
#endif
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 {
@@ -130,27 +134,27 @@ void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
taginfo.parseContainerFormat(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return;
return false;
}
taginfo.parseTracks(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return;
return false;
}
taginfo.parseTags(diag, progress);
if (progress.isAborted()) {
taginfo.close();
return;
return false;
}
for (const TagParser::DiagMessage &msg : diag) {
qLog(Debug) << QString::fromStdString(msg.message());
}
const auto tracks = taginfo.tracks();
for (const auto track : tracks) {
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);
@@ -201,10 +205,10 @@ void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
taginfo.close();
return;
return false;
}
for (const auto tag : taginfo.tags()) {
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));
@@ -220,7 +224,11 @@ void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
song->set_track(tag->value(TagParser::KnownField::TrackPosition).toInteger());
song->set_disc(tag->value(TagParser::KnownField::DiskPosition).toInteger());
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
song->set_art_automatic(kEmbeddedCover);
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);
}
}
@@ -238,16 +246,43 @@ void TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
taginfo.close();
return true;
}
catch(...) {
return false;
}
catch(...) {}
}
bool TagReaderTagParser::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request) const {
if (filename.isEmpty()) return false;
if (request.filename().empty()) return false;
qLog(Debug) << "Saving tags to" << filename;
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;
@@ -282,22 +317,34 @@ bool TagReaderTagParser::SaveFile(const QString &filename, const spb::tagreader:
taginfo.createAppropriateTags();
}
for (const auto tag : taginfo.tags()) {
tag->setValue(TagParser::KnownField::AlbumArtist, TagParser::TagValue(song.albumartist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Artist, TagParser::TagValue(song.artist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Album, TagParser::TagValue(song.album(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Title, TagParser::TagValue(song.title(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Genre, TagParser::TagValue(song.genre(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Composer, TagParser::TagValue(song.composer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Performers, TagParser::TagValue(song.performer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Grouping, TagParser::TagValue(song.grouping(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Comment, TagParser::TagValue(song.comment(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::Lyrics, TagParser::TagValue(song.lyrics(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
tag->setValue(TagParser::KnownField::TrackPosition, TagParser::TagValue(song.track()));
tag->setValue(TagParser::KnownField::DiskPosition, TagParser::TagValue(song.disc()));
tag->setValue(TagParser::KnownField::RecordDate, TagParser::TagValue(song.year()));
tag->setValue(TagParser::KnownField::ReleaseDate, TagParser::TagValue(song.originalyear()));
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();
@@ -345,7 +392,7 @@ QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
return QByteArray();
}
for (const auto tag : taginfo.tags()) {
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();
@@ -366,12 +413,22 @@ QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
}
bool TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
void TagReaderTagParser::SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &data) const {
if (filename.isEmpty()) return false;
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;
@@ -402,8 +459,8 @@ bool TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const QByteArr
taginfo.createAppropriateTags();
}
for (const auto tag : taginfo.tags()) {
tag->setValue(TagParser::KnownField::Cover, TagParser::TagValue(data.toStdString()));
for (TagParser::Tag *tag : taginfo.tags()) {
SaveEmbeddedArt(tag, cover_data);
}
taginfo.applyChanges(diag, progress);
@@ -422,8 +479,16 @@ bool TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const QByteArr
}
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;
@@ -463,9 +528,10 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
taginfo.createAppropriateTags();
}
for (const auto tag : taginfo.tags()) {
tag->setValue(TagParser::KnownField::Rating, TagParser::TagValue(song.rating()));
for (TagParser::Tag *tag : taginfo.tags()) {
SaveSongRatingToFile(tag, song);
}
taginfo.applyChanges(diag, progress);
taginfo.close();

View File

@@ -22,6 +22,8 @@
#include <string>
#include <tagparser/tag.h>
#include <QByteArray>
#include <QString>
@@ -39,15 +41,21 @@ class TagReaderTagParser : public TagReaderBase {
bool IsMediaFile(const QString &filename) const override;
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
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 QString &filename, const QByteArray &data) 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)
};

View File

@@ -11,5 +11,5 @@ target_include_directories(macdeploycheck PUBLIC
target_link_libraries(macdeploycheck PUBLIC
"-framework AppKit"
${GLIB_LIBRARIES}
${QtCore_LIBRARIES}
Qt${QT_VERSION_MAJOR}::Core
)

View File

@@ -91,7 +91,7 @@ int main(int argc, char **argv) {
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)?\\)$"));
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;
@@ -105,7 +105,7 @@ int main(int argc, char **argv) {
else if (library.startsWith("@executable_path")) {
QString real_path = library;
real_path = real_path.replace("@executable_path", bundle_path + "/Contents/MacOS");
if (!QFile(real_path).exists()) {
if (!QFile::exists(real_path)) {
qLog(Error) << real_path << "does not exist for" << filepath;
success = false;
}
@@ -113,7 +113,7 @@ int main(int argc, char **argv) {
else if (library.startsWith("@rpath")) {
QString real_path = library;
real_path = real_path.replace("@rpath", bundle_path + "/Contents/Frameworks");
if (!QFile(real_path).exists() && !real_path.endsWith("QtSvg")) { // FIXME: Ignore broken svg image plugin.
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;
}
@@ -122,7 +122,7 @@ int main(int argc, char **argv) {
QString loader_path = QFileInfo(filepath).path();
QString real_path = library;
real_path = real_path.replace("@loader_path", loader_path);
if (!QFile(real_path).exists()) {
if (!QFile::exists(real_path)) {
qLog(Error) << real_path << "does not exist for" << filepath;
success = false;
}
@@ -130,9 +130,6 @@ int main(int argc, char **argv) {
else if (library.startsWith("/System/Library/") || library.startsWith("/usr/lib/")) { // System library
continue;
}
else if (library.endsWith("libgcc_s.1.dylib")) { // fftw points to it for some reason.
continue;
}
else {
qLog(Error) << "File" << filepath << "points to" << library;
success = false;

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