Compare commits

..

1916 Commits

Author SHA1 Message Date
Jonas Kvinge
5c57c923d9 Add Dropbox 2025-12-29 00:48:02 +01:00
Strawberry Bot
da2f28811a New translations 2025-12-29 00:02:45 +01:00
Jonas Kvinge
0bfa736081 GstEnginePipeline: Add audioresample elements 2025-12-28 22:01:42 +01:00
Jonas Kvinge
1392bcbbe1 FilesystemMusicStorage: Fallback to delete if moving to trash fails
Fixes #1679
2025-12-28 21:28:49 +01:00
Jonas Kvinge
11705889f1 Show playlist load errors
Fixes #1470
2025-12-28 20:54:36 +01:00
dependabot[bot]
604dd2dbde Bump vmactions/freebsd-vm from 1.3.1 to 1.3.2
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.3.1...v1.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-28 19:00:35 +01:00
Stickman
25065ba98f Song: Include Opus for supported sort tags 2025-12-28 18:57:52 +01:00
Jonas Kvinge
7b16ec62bb Defer playcount and rating tag writes for currently playing Ogg songs
Fixes #1816
2025-12-28 18:33:49 +01:00
Jonas Kvinge
d8f31592b9 Remove settings member variables 2025-12-28 00:39:22 +01:00
Jonas Kvinge
80bb0f476d CollectionModel: Remove sort tags from container keys
Fixes #1899
2025-12-27 21:25:54 +01:00
dependabot[bot]
b7222ac85c Bump vmactions/openbsd-vm from 1.2.8 to 1.2.9
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.8 to 1.2.9.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.8...v1.2.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-27 20:02:32 +01:00
dependabot[bot]
241bca0828 Bump vmactions/openbsd-vm from 1.2.7 to 1.2.8
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.7 to 1.2.8.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.7...v1.2.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-25 19:51:35 +01:00
dependabot[bot]
90d86b10a3 Bump vmactions/freebsd-vm from 1.3.0 to 1.3.1
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.3.0...v1.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-25 16:51:53 +01:00
dependabot[bot]
4130c6670f Bump vmactions/openbsd-vm from 1.2.5 to 1.2.7
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.5 to 1.2.7.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.5...v1.2.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 21:10:30 +01:00
Jonas Kvinge
8d262959c1 GstEnginePipeline: Fix buffering issue near track end during gapless playback
Ignore buffering messages when within 5 seconds of track end and about-to-finish has been signaled. This prevents spurious buffering from blocking playback during track transitions with local files.

Fixes #1725
2025-12-20 01:36:49 +01:00
Jonas Kvinge
b9b70399d8 GstEnginePipeline: Fix possible race condition in pipeline destructor
Wait for ongoing state changes to complete before setting pipeline to NULL.
This prevents race conditions with async state transitions that can cause crashes in GStreamer elements.

Fixes #1875
2025-12-20 01:28:53 +01:00
Jonas Kvinge
527ccd212a SmartPlaylistsViewContainer: Ask for confirmation before resetting smart playlists 2025-12-19 01:03:46 +01:00
Jonas Kvinge
4a5afbeb1e SmartPlaylists: Add option to restore smart playlists to the defaults
Fixes #1848
2025-12-19 00:49:05 +01:00
Jonas Kvinge
63c14e014b EditTagDialog: Ignore unused const variables 2025-12-19 00:47:35 +01:00
Jonas Kvinge
801658c6b9 MainWindow: Check that current is the active playlist
Fixes #1783
2025-12-19 00:38:32 +01:00
Jonas Kvinge
16fe665295 TagReaderTagLib: Remove unused constants 2025-12-19 00:35:02 +01:00
Rob Stanfield
2bb0dbada2 Qobuz: Fix authentication and add automatic credential fetching
Qobuz API now requires intent=stream parameter for stream URL requests,
and the app_secret must be extracted using the Spoofbuz decoding method
from bundle.js rather than plain-text values.

Changes:
- Add intent=stream parameter to stream URL requests
- Add QobuzCredentialFetcher class to extract credentials from web player
- Add "Fetch Credentials" button to Qobuz settings page
- Decode obfuscated app secrets using seed/timezone/info/extras method

This fixes "Invalid Request Signature" errors that prevented playback.
2025-12-18 23:12:52 +01:00
Jonas Kvinge
2cd9498469 Add option to select ID3v2 version
Fixes #1861
2025-12-18 22:18:26 +01:00
Jonas Kvinge
d1ee27fff9 QobuzService: Remove QNetworkReply 2025-12-18 20:39:21 +01:00
Jonas Kvinge
91adf5ba32 NetworkAccessManager: Handle network state changes after system suspend/resume
Fixes #1521
2025-12-18 20:32:07 +01:00
Jonas Kvinge
d68f464269 Playlist: Don't automatically sort playlist before it's fully loaded
Fixes #1690
2025-12-18 20:14:36 +01:00
Jonas Kvinge
c684a95f89 GstEnginePipeline: Fix file descriptor exhaustion by using shared thread pool
Replace per-pipeline QThreadPool with a shared static pool to prevent
file descriptor and thread exhaustion. Each GstEnginePipeline was creating
its own thread pool, leading to resource accumulation during frequent
pipeline creation/destruction (track changes, seeking, crossfade).

The shared pool is limited to 2 threads max since state changes are
typically sequential per pipeline. This prevents the crash in g_wakeup_new()
when creating eventfd for new thread event dispatchers.

Fixes #1687
2025-12-18 19:58:23 +01:00
copilot-swe-agent[bot]
1d03bb2178 GstEnginePipeline: Fix crash in GStreamer decodebin3 when switching tracks
Add guard in AboutToFinishCallback to prevent race condition when pipeline is being torn down. This prevents the callback from trying to set next URL while the pipeline is being destroyed, which caused crashes in GStreamer's decodebin3.

Fixes issue where rapidly switching tracks could cause segmentation fault in gst_decodebin_input_link_to_slot.

See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4626

Fixes #1863

Co-Authored-By: Jonas Kvinge <jonas@jkvinge.net>
2025-12-18 19:44:03 +01:00
Jonas Kvinge
39f9128ecf gitignore: Add _codeql_detected_source_root 2025-12-18 19:39:10 +01:00
Jonas Kvinge
ca2e802239 GstEngine: Make sure device is set for pipeline
Fixes #1852
2025-12-18 00:21:00 +01:00
Jonas Kvinge
9a513a9a56 AutoExpandingTreeView: Scroll if cursor is out of visible area
Fixes #1489
2025-12-17 23:14:57 +01:00
Jonas Kvinge
1c2e87b741 Organize: Skip existing files if not overwriting
Fixes #1484
2025-12-17 22:58:17 +01:00
Jonas Kvinge
fe4d9979ce CollectionWatcher: Avoid re-scan of restored songs unless mtime is changed
Fixes #1819
2025-12-17 22:15:21 +01:00
Jonas Kvinge
d8ae790ebf Turn on git revision 2025-12-17 01:05:45 +01:00
Jonas Kvinge
ac31d79294 Release 1.2.16 2025-12-17 00:08:06 +01:00
Jonas Kvinge
4ffebd77b1 Update Changelog 2025-12-17 00:07:41 +01:00
Strawberry Bot
6682efae2f New translations 2025-12-16 22:41:10 +01:00
Jonas Kvinge
480161c6b7 Update Changelog 2025-12-16 22:38:55 +01:00
Jonas Kvinge
a8ba420d72 ErrorDialog: Use QApplication::activeWindow 2025-12-16 22:32:11 +01:00
dependabot[bot]
fc0ec91652 Bump actions/download-artifact from 6 to 7
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 18:15:02 +01:00
dependabot[bot]
0701b97324 Bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 18:14:44 +01:00
Jonas Kvinge
3867932e1e PlaylistView: Don't automatically scroll on dynamic playlists
Fixes #1427
2025-12-14 04:55:20 +01:00
Jonas Kvinge
e2907f6051 PlaylistView: Use Qt::CopyAction for drag
Fixes #1815
2025-12-14 04:41:58 +01:00
Jonas Kvinge
0827ec7f16 PlaylistSequence: Fix icon size
Fixes #1838
2025-12-14 04:28:25 +01:00
Jonas Kvinge
24d2adf363 PlaylistView: Set current index when automatically selecting track
Fixes #1825
2025-12-14 04:02:40 +01:00
Jonas Kvinge
592729d00b SliderSlider: Use SC_SliderGroove
Fixes #1675
2025-12-14 01:57:47 +01:00
Jonas Kvinge
c4a564bb56 NetworkAccessManager: Use full application name for user agent 2025-12-14 01:36:53 +01:00
Jonas Kvinge
812a02a3a1 Update Changelog 2025-12-14 01:04:45 +01:00
Strawberry Bot
944936914b New translations 2025-12-14 01:01:06 +01:00
Jonas Kvinge
e7c901d4f3 Update Changelog 2025-12-14 00:59:42 +01:00
Jonas Kvinge
8e996119af Make using sort tags optional 2025-12-14 00:52:19 +01:00
Jonas Kvinge
4348a654ca TagReaderResult: Fix file save error message 2025-12-14 00:52:19 +01:00
dependabot[bot]
f0be1c782a Bump vmactions/openbsd-vm from 1.2.4 to 1.2.5
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.4...v1.2.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-11 00:36:57 +01:00
dependabot[bot]
e9898d08bc Bump vmactions/freebsd-vm from 1.2.9 to 1.3.0
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.9 to 1.3.0.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.9...v1.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-11 00:36:24 +01:00
Jonas Kvinge
1ad13cd3b0 Add lyrics from lrclib.net 2025-12-09 18:45:57 +01:00
Jonas Kvinge
5c640e0e36 LyricsFetcherSearch: Improve handling timeout 2025-12-09 18:41:55 +01:00
Jonas Kvinge
059def8d0c Add duration to lyrics search request 2025-12-09 18:40:45 +01:00
Jonas Kvinge
cf15a1f423 CDDALister: Add Q_UNUSED 2025-12-09 01:33:13 +01:00
Jonas Kvinge
5d35b0eedd BlockAnalyzer: Formatting 2025-12-09 01:19:02 +01:00
Jonas Kvinge
5fcb71d08f Formatting 2025-12-09 01:16:41 +01:00
Jonas Kvinge
15c2237d4a AlbumCoverChoiceController: Fix incorrectly formatted switch 2025-12-08 23:55:13 +01:00
Jonas Kvinge
93af866185 Formatting 2025-12-08 23:49:48 +01:00
dependabot[bot]
109ff90401 Bump vmactions/freebsd-vm from 1.2.8 to 1.2.9
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.8 to 1.2.9.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.8...v1.2.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 17:11:17 +01:00
Jonas Kvinge
d4b06289c3 clang-format: Add new KeepEmptyLines option 2025-12-07 01:20:39 +01:00
dependabot[bot]
4351c555a0 Bump apple-actions/import-codesign-certs from 5 to 6
Bumps [apple-actions/import-codesign-certs](https://github.com/apple-actions/import-codesign-certs) from 5 to 6.
- [Release notes](https://github.com/apple-actions/import-codesign-certs/releases)
- [Commits](https://github.com/apple-actions/import-codesign-certs/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-07 00:51:50 +01:00
Strawberry Bot
ce4db40983 New translations 2025-12-07 00:06:44 +01:00
Jonas Kvinge
d37fb7410c ResizableTextEdit: Set word wrap 2025-12-01 23:23:23 +01:00
Jonas Kvinge
f1cdd71494 ResizableTextEdit: Move updateGeometry after resize 2025-12-01 23:23:05 +01:00
Jonas Kvinge
000ba997fb Playlist: Preserve track order in album shuffle mode
Fixes #1623
2025-12-01 22:47:12 +01:00
dependabot[bot]
579efffd14 Bump vmactions/openbsd-vm from 1.2.3 to 1.2.4
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.3...v1.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:47:08 +01:00
dependabot[bot]
3a098c8a0c Bump vmactions/freebsd-vm from 1.2.7 to 1.2.8
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.7 to 1.2.8.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.7...v1.2.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 18:46:03 +01:00
bastimeyer
5bce0ae87f GlobalShortcutsBackendKGlobalAccel: Add globalShortcutRepeated signal 2025-11-30 18:25:26 +01:00
Jonas Kvinge
afe6967c46 GstEnginePipeline: Handle "missing-plugin" messages 2025-11-30 18:13:30 +01:00
Jonas Kvinge
e91bab6d42 GstEngine: Only emit error for debugstr if it's set 2025-11-30 18:13:30 +01:00
Jonas Kvinge
5a64247761 PlaylistView: Use lamda in sort 2025-11-30 16:58:08 +01:00
Jonas Kvinge
9ed89afdb2 SpotifyService: Use 127.0.0.1 in redirect URL 2025-11-30 16:32:56 +01:00
Jonas Kvinge
0ac338026c About: Update sponsor info 2025-11-30 02:47:39 +01:00
Jonas Kvinge
4e5f84a7b7 RichPresence: Use pretty title 2025-11-26 19:14:16 +01:00
Jonas Kvinge
320a3c6815 RichPresence: Add copyright 2025-11-26 19:14:05 +01:00
Jonas Kvinge
72dd1d783a Turn on git revision 2025-11-25 21:13:00 +01:00
Jonas Kvinge
d2205cfe81 Release 1.2.15 2025-11-25 02:50:34 +01:00
Jonas Kvinge
5830f247f6 Update Changelog 2025-11-25 02:05:26 +01:00
Jonas Kvinge
8b4c57d933 GroupedIconView: Remove deprecated QStyle::State_Editing 2025-11-23 03:14:57 +01:00
Jonas Kvinge
67cec09176 gitignore: Add .qtcreator 2025-11-23 03:14:24 +01:00
Jonas Kvinge
2df658e1f3 ListenBrainzScrobbler: Ignore connection closed 2025-11-23 01:11:59 +01:00
Jonas Kvinge
f3bc9b151c README: Update OBS URL's 2025-11-23 00:47:56 +01:00
Jonas Kvinge
b06b59d0c5 nsi: Bump ffmpeg for msvc x86_64 2025-11-22 14:20:40 +01:00
Jonas Kvinge
99d75ade06 CI: Bump MSVC SDK version 2025-11-22 02:15:20 +01:00
Jonas Kvinge
3f63246068 Add macos-15-intel runner 2025-11-22 00:47:30 +01:00
dependabot[bot]
b205a5f964 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [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/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 19:46:25 +01:00
dependabot[bot]
aeaef12dd4 Bump vmactions/freebsd-vm from 1.2.6 to 1.2.7
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.6 to 1.2.7.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.6...v1.2.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 19:45:43 +01:00
Jonas Kvinge
02d76f17f7 CollectionModel: Make SortText static 2025-11-15 22:10:55 +01:00
Strawberry Bot
e4e12c6fa6 New translations 2025-11-13 23:25:27 +01:00
uninstall-your-browser
270ae6085b FilterParser: Convert number to nanoseconds for length filter 2025-11-13 23:23:19 +01:00
Jonas Kvinge
7065a405a5 CI: Remove macos-13 2025-11-12 01:10:19 +01:00
Jonas Kvinge
d8c72c3dd9 ParserBase: Remove use of QString::removeFirst 2025-11-11 21:37:19 +01:00
Jonas Kvinge
b65502e167 XSPFParser: Handle platform and url 2025-11-11 00:56:36 +01:00
Jonas Kvinge
132f8df853 ParserBase: Convert spotify URLs 2025-11-11 00:56:13 +01:00
Jonas Kvinge
12e3cffe63 CI: Fix ssh command 2025-11-10 20:43:09 +01:00
dependabot[bot]
56a637682d Bump vmactions/freebsd-vm from 1.2.5 to 1.2.6
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.5...v1.2.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 20:01:38 +01:00
dependabot[bot]
d9b105f89e Bump vmactions/openbsd-vm from 1.2.2 to 1.2.3
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.2 to 1.2.3.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.2...v1.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 17:26:57 +01:00
dependabot[bot]
bd6b45e43f Bump vmactions/freebsd-vm from 1.2.4 to 1.2.5
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.4...v1.2.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 18:54:16 +01:00
dependabot[bot]
539172fb70 Bump vmactions/openbsd-vm from 1.2.1 to 1.2.2
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.1...v1.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 18:11:33 +01:00
Jonas Kvinge
ebd92b3a7f nsi: Bump ffmpeg 2025-11-01 12:22:43 +01:00
Jonas Kvinge
b00ae5b210 nsi: Bump icu 2025-10-31 23:47:49 +01:00
Jonas Kvinge
c8e3cf981b main: Try different language codes for QtSparkle 2025-10-31 23:06:00 +01:00
Jonas Kvinge
038f69779f CI: Manually codesign libbrotli 2025-10-30 23:56:17 +01:00
Jonas Kvinge
a4de7559ac Fix loading language from system UI languages
Fixes #1847
2025-10-30 23:54:42 +01:00
dependabot[bot]
0537b072fe Bump actions/download-artifact from 5 to 6
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-30 01:36:11 +01:00
dependabot[bot]
2657c9f96a Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-30 01:35:56 +01:00
Jonas Kvinge
7e178b1f1a PlaylistView: Always keep EditKeyPressed 2025-10-30 01:35:30 +01:00
Jonas Kvinge
dd8513d02c PlaylistView: Disable EditKeyPressed when inline metadata editing is disabled 2025-10-27 20:19:50 +01:00
Andrew Tribick
5f0175094b Support unofficial::getopt-win32::getopt as a getopt library target
Handles the vcpkg case
2025-10-26 13:06:11 +01:00
Jonas Kvinge
b4c5b9e1e1 Turn on git revision 2025-10-26 13:05:56 +01:00
Jonas Kvinge
2ce0ed2ef8 Release 1.2.14 2025-10-25 23:03:51 +02:00
dependabot[bot]
176768f7f8 Bump vmactions/openbsd-vm from 1.2.0 to 1.2.1
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.2.0...v1.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-25 02:03:03 +02:00
Jonas Kvinge
50b954034c OpenTidalCoverProvider: Check for already finished 2025-10-23 00:39:40 +02:00
Jonas Kvinge
cab7b6c335 Update Changelog 2025-10-23 00:34:46 +02:00
Jonas Kvinge
fce1dacafc OpenTidalCoverProvider: Adapt to new API 2025-10-23 00:34:06 +02:00
Jonas Kvinge
94aa6fb940 MusicBrainzClient: Add missing clear 2025-10-22 20:44:22 +02:00
Jonas Kvinge
0cfd4aaad1 Update Changelog 2025-10-22 20:29:54 +02:00
Strawberry Bot
9e72b4fe80 New translations strawberry_en_us.ts (Swedish) 2025-10-20 21:28:23 +02:00
Jiří Pinkava
1151443372 Add support to play webm media files 2025-10-20 21:28:00 +02:00
Jonas Kvinge
8f6993e7c8 nsi: Add libgstwasapi2 for mingw 2025-10-20 20:39:38 +02:00
Jonas Kvinge
a6ab1a7689 GstEngine: Enable exclusive mode for wasapi2sink 2025-10-19 19:09:49 +02:00
Jonas Kvinge
098b21d818 Use MMDeviceFinder for wasapi2sink 2025-10-19 18:32:42 +02:00
Jonas Kvinge
d61adeb595 Add option not to remove "Remastered", etc from song titles 2025-10-18 19:57:38 +02:00
Cesar Enrique Garcia Dabo
8bfc3bc41c SubsonicRequest: Use coverArt from album 2025-10-08 21:49:04 +02:00
Jonas Kvinge
0dda2feec3 Improve README.md 2025-10-06 00:17:29 +02:00
Jonas Kvinge
1d0d03ed83 Rewrite MusicBrainzClient
Use Json instead of XML, make Disc ID requests respect rate limiting, handle sort names.
2025-10-05 21:42:14 +02:00
Jonas Kvinge
330284f03e CollectionModel: Log when song already exists 2025-10-05 21:33:41 +02:00
Jonas Kvinge
fc3ed3a2ce CollectionModel: Avoid duplicate resets 2025-10-05 21:33:22 +02:00
dependabot[bot]
6a656036fe Bump vmactions/freebsd-vm from 1.2.3 to 1.2.4
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.3...v1.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 18:26:55 +02:00
Jonas Kvinge
5c76c633a5 CI: Use copydlldeps for msvc arm64 too 2025-09-21 20:59:16 +02:00
Jonas Kvinge
d487c3ea07 nsi: Remove gstwinrt-1.0-0.dll 2025-09-21 20:58:39 +02:00
Jonas Kvinge
83dca405af CI: Remove copy of liblzma.dll 2025-09-19 21:29:34 +02:00
Strawberry Bot
acd0b6d3ea New translations 2025-09-19 20:43:20 +02:00
Jonas Kvinge
159242aff4 nsi: Remove liblzma.dll 2025-09-19 01:51:28 +02:00
Jonas Kvinge
4b014253cf Remove libre.fm 2025-09-18 00:22:11 +02:00
Jonas Kvinge
1ec6b5582e nsi: Move files 2025-09-12 22:02:39 +02:00
Jonas Kvinge
08b8d04500 nsi: Use gnutls with static deps 2025-09-12 21:28:49 +02:00
7xnl
54679b1d57 discord: fixed timestamp update when seeking
When seeking in a song, `RichPresence::Seeked()` receives the new
position in microseconds and is supposed to update the RPC timestamp
with the new position in seconds. However, it actually converts the
value to milliseconds, meaning that if, for example, you seek to 0:05 in
a song, Discord will think you seeked to 83:20, or 5000 seconds from the
beginning of the song.

This commit fixes this by simply dividing the microseconds value by one
million instead of one thousand.
2025-09-11 23:40:40 +02:00
Jonas Kvinge
8d648e668e nsi: Update to new MSVC gnutls dependencies 2025-09-10 18:30:51 +02:00
Jonas Kvinge
5897e786dc Remove unused macversion script 2025-09-08 22:06:32 +02:00
Jonas Kvinge
7f549aa991 CI: Fix ssh command for MSVC rsync 2025-09-07 17:08:19 +02:00
Jonas Kvinge
792e7b6274 BackendSettingsPage: Remove unused errordialog.h include 2025-09-07 16:46:41 +02:00
Jonas Kvinge
92c58b0b60 Fix showing error dialog minimized when main window is not active
Fixes #1739
2025-09-07 15:46:26 +02:00
Jonas Kvinge
5fac9a1c8d BackendSettingsPage: Remove unused ErrorDialog 2025-09-07 14:22:42 +02:00
Jonas Kvinge
7f4f715003 ContextView: Remove EBU R 128
It breaks wordwrap
2025-09-07 13:53:59 +02:00
Jonas Kvinge
75d1d4098e CI: Install KDSingleApplication on Ubuntu 2025-09-01 23:50:16 +02:00
Jonas Kvinge
30e80068a3 CI: Add Ubuntu Questing 2025-09-01 23:49:50 +02:00
Jonas Kvinge
5fe9a1528f CI: Only build KDSingleApplication on bookworm 2025-09-01 23:45:49 +02:00
Jonas Kvinge
7777eda115 CI: Add Debian Forky 2025-09-01 23:45:17 +02:00
Jonas Kvinge
ce4f2ece93 CI: Add openSUSE Leap 16.0 2025-09-01 21:36:46 +02:00
Strawberry Bot
52399d73fe New translations 2025-09-01 00:27:28 +02:00
Jonas Kvinge
6e98166148 Turn on git revision 2025-09-01 00:26:22 +02:00
Jonas Kvinge
c658a77b05 Release 1.2.13 2025-08-31 22:33:48 +02:00
Jonas Kvinge
1880dc8153 Update Changelog 2025-08-31 22:27:00 +02:00
7xnl
b5fd3d5717 Add settings customize Discord status text
The new settings let you customize the "Listening to" status text, according to the [status display types](https://discord.com/developers/docs/events/gateway-events#activity-object).

Fixes #1796.
2025-08-31 22:11:59 +02:00
Jonas Kvinge
3c3480fb84 SystemTrayIcon: Respect device aspect ratio
Fixes #1782
2025-08-31 02:34:13 +02:00
Jonas Kvinge
f628914173 MainWindow: Rename systemtrayicon 2025-08-31 00:37:09 +02:00
Jonas Kvinge
c100fb1bb8 TagReaderTagLib: Fallback to "Other" cover type
Fixes #1793
2025-08-31 00:20:00 +02:00
Jonas Kvinge
8c804c4fba Refactor CDDA loading signal/slots
Fixes #1803
2025-08-31 00:01:55 +02:00
Jonas Kvinge
912a7c7da9 MusicBrainzClient: Fix typo 2025-08-30 23:55:27 +02:00
Jonas Kvinge
0a5815c82e StyleSheetLoader: Set alpha on other platforms than macOS
Fixes #1806
2025-08-26 22:48:58 +02:00
Jonas Kvinge
6513b3032b CMake: Check additional names for getopt 2025-08-24 22:36:13 +02:00
Jonas Kvinge
8c51401bdc MacOsDeviceLister: Fix build without MTP
Fixes #1804
2025-08-24 01:28:22 +02:00
dependabot[bot]
45fc9c83d4 Bump vmactions/openbsd-vm from 1.1.8 to 1.2.0
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.1.8 to 1.2.0.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.1.8...v1.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 23:57:47 +02:00
dependabot[bot]
be57d8147a Bump vmactions/freebsd-vm from 1.2.1 to 1.2.3
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.1 to 1.2.3.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.1...v1.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 18:25:13 +02:00
Jonas Kvinge
a97908fb6b CI: Bump msvc sdk 2025-08-17 13:44:57 +02:00
Lars Wendler
c0417d4bb3 cdda: fix build without musicbrainz
With -DENABLE_MUSICBRAINZ=NO the following build error occurs since 1.2.12:

/var/tmp/portage/media-sound/strawberry-1.2.12_pre/work/strawberry-1.2.12/src/de
vice/cddasongloader.cpp:58:91: error: ‘LoadMusicBrainzCDTags’ is not a member of
 ‘CDDASongLoader’
   58 |   QObject::connect(this, &CDDASongLoader::MusicBrainzDiscIdLoaded, this,
 &CDDASongLoader::LoadMusicBrainzCDTags);
      |
                  ^~~~~~~~~~~~~~~~~~~~~
2025-08-13 19:49:41 +02:00
Jonas Kvinge
062e2cfb84 Turn on git revision 2025-08-13 00:20:05 +02:00
Jonas Kvinge
700f7dbe36 Release 1.2.12 2025-08-12 22:57:10 +02:00
Jonas Kvinge
0487118dad Update Changelog 2025-08-12 22:54:54 +02:00
Jonas Kvinge
f3d088e48b Rename sort functions 2025-08-12 22:14:22 +02:00
Jonas Kvinge
f8afd49fcf Update Changelog 2025-08-12 01:46:46 +02:00
dependabot[bot]
363fcb5aba Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [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/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 22:52:06 +02:00
Jonas Kvinge
183aba4181 main: Workaround crash on exit on mingw with win32 threads 2025-08-10 22:41:38 +02:00
Jonas Kvinge
742be01aa6 nsi: Add /norestart to vc redist install 2025-08-10 18:34:12 +02:00
Jonas Kvinge
38c8054873 nsi: Only include gstwinrt-1.0-0.dll on arm64 2025-08-10 02:13:44 +02:00
Jonas Kvinge
da9e9840b8 Add BPM, mood and initial key support 2025-08-10 01:34:44 +02:00
Jonas Kvinge
c4646531b0 Refactor use of sort tags 2025-08-10 00:11:28 +02:00
dependabot[bot]
65d9b6a9e9 Bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-06 23:31:07 +02:00
Jonas Kvinge
046f40fbca CollectionModel: Remove const reference on SortBehaviour enum 2025-08-04 22:54:18 +02:00
Strawberry Bot
a0ca50ac30 New translations 2025-08-04 22:43:33 +02:00
Jonas Kvinge
d939733675 CI: Remove Ubuntu oracular 2025-08-04 22:42:13 +02:00
Mark
61a8a3a84a SmartPlaylists: Add sort fields 2025-08-04 22:24:50 +02:00
Mark
d4858a338c Propose collection rescan on upgrade 2025-08-04 22:24:21 +02:00
Mark
31380a5bd4 CDDASongLoader: Add sort tags 2025-08-04 22:24:12 +02:00
Mark
e45b9aabeb Add sort tags to context view 2025-08-04 22:23:52 +02:00
Mark
27e782d8cf Allow editing new sort tags 2025-08-04 22:23:33 +02:00
Mark
0bfc2ee198 Add sort columns to playlists
Increment playlist state version from 1 to 2 to get sort columns next to
their "original" column. Discard old stored playlist state in config file.
2025-08-04 22:23:19 +02:00
Mark
e7fc4b1706 Collection: Use sort tags and add sort behaviour 2025-08-04 22:21:49 +02:00
Mark
6dea1a2149 Add support for sort tags 2025-08-04 22:21:33 +02:00
Jonas Kvinge
7844a2b932 Update Spotify access token
Fixes #1769
2025-08-04 22:11:44 +02:00
Jonas Kvinge
96a53bfbe5 SavedGroupingManager: Fix removing saved grouping 2025-07-30 00:47:54 +02:00
Jonas Kvinge
fe5fbae4b4 Use percent encoding for saved groupings
Fixes #1758
2025-07-30 00:41:12 +02:00
Jonas Kvinge
a9140232e5 Add workaround for QTBUG-135641
Fixes #1594
2025-07-29 23:42:38 +02:00
Jonas Kvinge
835090dd96 RichPresence: Disable Discord desktop file creation
Fixes #1771
2025-07-29 00:45:36 +02:00
Jonas Kvinge
af5590dcb1 NetworkAccessManager: Fix setting prefer cache setting 2025-07-28 22:40:26 +02:00
Jonas Kvinge
26b5588d7d NetworkAccessManager: Rename variables 2025-07-28 22:39:32 +02:00
Jonas Kvinge
390bf049f2 Don't set window icon on Wayland
Fixes #1753
2025-07-27 14:40:01 +02:00
Jonas Kvinge
321272b695 MainWindow: Remove hard-coded icon 2025-07-27 14:39:22 +02:00
Jonas Kvinge
342805e0f3 MainWindow: Automatically added UI changes 2025-07-27 14:39:05 +02:00
Jonas Kvinge
e614626913 TidalStreamURLRequest: Fix parsing manifest urls 2025-07-19 21:38:55 +02:00
Mark
2ddacf2f98 Database: Add *sort fields, bpm, mood, initial_key
Upgrade from schema version 20 to 21. This includes:

- six fields for sort tags
- new fields bpm, mood, initial_key

See https://github.com/strawberrymusicplayer/strawberry/pull/1779#pullrequestreview-3003042802
2025-07-12 18:27:32 +02:00
Jonas Kvinge
a47531d4ce Database: Remove FTS hack 2025-07-09 22:45:52 +02:00
Jonas Kvinge
84b758e395 README: Fix broken md link 2025-07-09 22:37:52 +02:00
Jonas Kvinge
51b69a85c4 GeniusLyricsProvider: Remove unused includes 2025-07-09 22:35:47 +02:00
Jonas Kvinge
52774a3222 ChartLyricsProvider: Fix empty results 2025-07-09 22:34:35 +02:00
gitlost
9030b2567b GeniusLyrics: update to parse latest HTML of returned lyrics,
devolving the removal of various crud to `HtmlLyricsProvider`;
  log initial query and use new `StartsOrEndsMatch()` static to
  match JSON replies, log each request, and break if full match;
  `StartsOrEndsMatch()` ignores some common punctuation variations
   & normalizes single quotes and allows match at beginning or end
HtmlLyricsProvider: fix `multiple` mode not to terminate on first
  batch, and defer processing till have whole HTML (avoids issues
  with tags spanning batches);
  add param to take list of regular expressions to remove from HTML
  prior to general processing (used only by `GeniusLyrics` for now)
README.md etc: update list of lyrics providers supported
2025-07-09 22:32:17 +02:00
gitlost
ee7bb449a5 Revert: Remove Genius lyrics [d9e38fb] 2025-07-09 22:32:17 +02:00
Madeline Schreiber
d901258f11 GstEnginePipeline: Ignore about-to-finish when position is 0 2025-07-07 01:05:47 +02:00
Madeline Schreiber
6372c5ee7d TagReaderClient: Call TagReaderGME when reading files 2025-07-07 01:05:47 +02:00
Madeline Schreiber
75f0402793 Add space to fix broken file filters 2025-07-04 17:25:34 -04:00
Ty
20e5c014ef PlaylistView: support alpha channel in background images 2025-07-04 20:42:32 +02:00
Strawberry Bot
1ebc32c3aa New translations 2025-07-03 21:06:02 +02:00
Piper McCorkle
a5f94b608b ListenBrainzScrobbler: Report more info to ListenBrainz
Report music service, URL, and Spotify ID to ListenBrainz.
ListenBrainz accepts the music service in listen reports, in both a canonical domain format and a human-readable display name format. This commit makes Strawberry report both, for maximum flexibility. I've also set it up to report a shareable track URL for supported streaming services. I am already using this data in my homepage's "Now Playing" widget.

Fixes #1768
2025-06-30 22:54:51 +02:00
Jonas Kvinge
e0d61223a4 CDDASongLoader: Fix freeing tag 2025-06-30 20:04:39 +02:00
Jonas Kvinge
459eea5bc4 FreeSpaceBar: Make sure bar size isn't negative
Fixes crash with CD drives.
2025-06-28 19:33:04 +02:00
Jonas Kvinge
09d02c53a3 StyleSheetLoader: Add back macOS hack 2025-06-23 21:12:54 +02:00
Jonas Kvinge
61a701554e style: Add back customized playlist background style 2025-06-23 20:44:00 +02:00
Jonas Kvinge
d280e6426f StyleSheetLoader: Add back alternate base color handling 2025-06-23 20:43:12 +02:00
Jonas Kvinge
5b9bb3efa7 Update Changelog 2025-06-23 20:06:35 +02:00
Jonas Kvinge
b8cbe49f8c StyleSheetLoader: Remove alternate base color handling 2025-06-23 20:05:35 +02:00
Jonas Kvinge
633e5707ef style: Remove customized playlist background style 2025-06-23 20:04:23 +02:00
Jonas Kvinge
d54290c3a7 Update Changelog 2025-06-23 19:01:55 +02:00
Jonas Kvinge
3ef2b53e46 Add back device view on Windows 2025-06-22 20:40:43 +02:00
Jonas Kvinge
d3a4dd6da6 CollectionView: Remove unused declaration 2025-06-22 20:36:57 +02:00
Jonas Kvinge
0158f7f08a Port DeviceManager to enum class 2025-06-22 17:35:19 +02:00
Jonas Kvinge
8cea020fac DeviceManager: Move creating device info to main thread 2025-06-22 17:21:12 +02:00
Jonas Kvinge
f6b38fecb0 DeviceManager: Set database ID when existing device info is found 2025-06-22 16:30:28 +02:00
Jonas Kvinge
5e2729fafe DeviceManager: Formatting 2025-06-22 16:29:27 +02:00
Jonas Kvinge
19dce1c25d DeviceInfo: Rename variables 2025-06-22 16:27:04 +02:00
Jonas Kvinge
00bb722e25 CDDALister: Trim friendly name 2025-06-22 16:26:35 +02:00
Jonas Kvinge
cbaf4d3121 DeviceManager: Rename variables 2025-06-22 00:49:01 +02:00
Jonas Kvinge
4b5370044b CDDASongLoader: Use cdiocddasrc 2025-06-22 00:39:09 +02:00
Jonas Kvinge
ffbe1ec9fd CDDASongLoader: Load tags from CD 2025-06-22 00:27:23 +02:00
Jonas Kvinge
53e43db91b CI: Add Fedora 43 2025-06-19 01:18:56 +02:00
dependabot[bot]
2858cdabc2 Bump vmactions/freebsd-vm from 1.2.0 to 1.2.1
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.2.0...v1.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-18 00:04:29 +02:00
Jonas Kvinge
cf74eeb120 CollectionSettingsPage: Remove edit triggers
Fixes #1767
2025-06-17 23:54:41 +02:00
Jonas Kvinge
790a1b4dbf ListenBrainzScrobbler: Use std::max 2025-06-17 23:47:46 +02:00
Jonas Kvinge
ee6332af1e ScrobblingAPI20: Replace std::min with std::max
Mistakenly written std::min instead of std::max here causing streams to never be scrobbled.
2025-06-17 23:47:37 +02:00
Jonas Kvinge
bf0704f6b2 Rename Cdda to CDDA 2025-06-09 04:21:17 +02:00
Jonas Kvinge
ae13fe7f52 Fix loading CD tracks in devices
Fixes #1676
2025-06-09 04:16:07 +02:00
Jonas Kvinge
90678e72ac DeviceManager: Remove device refresh 2025-06-09 04:12:23 +02:00
Jonas Kvinge
a0ec244008 CddaSongLoader: Fix some leaks 2025-06-09 02:27:11 +02:00
Jonas Kvinge
fba4f84fb6 CollectionModel: Move model reset to regular model updates 2025-06-09 02:24:53 +02:00
gitlost
950774f1c8 ExtendedEditor: padding for TextEdit & RTL LineEdit
`UpdateButtonGeometry()`: specify "QPlainTextEdit" for `TextEdit`
  padding (Comment and Lyrics) and invert left/right padding for
  `LineEdit` if layout direction RTL
2025-06-03 22:34:22 +02:00
gitlost
340bc21537 EditTagDialog: Make reset feedback work by calling
`set_reset_button()` in `UpdateModifiedField()` and catering for
  non-text in `IsValueModified()` (-1 original being same as 0);
  use new `ExtendedEditor::set_font()`;
  connect reset for "rating".
  Make "comment" `tabChangesFocus` to keep tab chain.
ExtendedEditor: New `set_font()` to get emboldened font to work and
  make reset feedback work for `CheckBox` and `RatingBox` by
  overriding `Resize()`.
RatingWidget: Allow tabbed focus and implement keyboard input.
2025-05-25 03:20:18 +02:00
Paper
a86ba4dffc GPodDevice: Add ALAC to supported file types for iPods
There are some iPods which do not support ALAC, but they are quite rare. Anything 3rd gen
and newer, which most people are likely to be using, will work if upgraded to the latest
firmware (they probably are already on it...)
2025-05-20 22:13:12 +02:00
Paper
d6bc6e33c0 Transcoder: Allow transcoding to ALAC 2025-05-20 22:13:12 +02:00
Paper
7e128a9af5 Song: Add ALAC song type 2025-05-20 22:13:12 +02:00
Jonas Kvinge
0f0746be9d CI: Remove Fedora 39 and 40 2025-05-15 22:39:15 +02:00
Jonas Kvinge
bec3fe9fd5 Turn on git revision 2025-05-15 22:38:32 +02:00
Jonas Kvinge
83c666baf9 Release 1.2.11 2025-05-15 21:09:19 +02:00
Strawberry Bot
b9b54e6e96 New translations 2025-05-13 22:11:19 +02:00
Jonas Kvinge
b2ff6240eb Update Changelog 2025-05-13 22:10:30 +02:00
Jonas Kvinge
26a7c74a24 nsi: Remove gioopenssl, except for msvc arm64 2025-05-13 22:10:25 +02:00
Jonas Kvinge
a34954ec4a PlaylistListContainer: Always check that playlist is open
Fixes #1741
2025-05-13 19:48:01 +02:00
Jonas Kvinge
349ab62e75 PlaylistListView: Check for valid current index 2025-05-13 19:42:25 +02:00
Jonas Kvinge
65e960f2c5 Update Changelog 2025-05-12 22:21:27 +02:00
Jonas Kvinge
e22fef8ca4 ContextView: Fix album cover visible check
Fixes #1744
2025-05-12 18:52:12 +02:00
Jonas Kvinge
3e99045e2c nsi: Update sqlite3 dll name 2025-05-08 22:31:43 +02:00
Jonas Kvinge
4fcade273e Update Changelog 2025-05-08 21:20:53 +02:00
Strawberry Bot
5eaff0d26e New translations 2025-05-08 21:17:36 +02:00
Strawberry Bot
5b22f12b4a New translations 2025-05-01 23:50:07 +02:00
OlegAckbar
5f85c2e7a5 Linux: enable startup notify
It was very odd for me why Strawberry doesn't have any feedback when launching from application menu. Turns out its desktop file had "StartupNotify=false" for some reason?
2025-05-01 23:41:35 +02:00
dependabot[bot]
4fb5a7b6bc Bump vmactions/openbsd-vm from 1.1.7 to 1.1.8
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.1.7 to 1.1.8.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.1.7...v1.1.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-30 18:59:21 +02:00
Jonas Kvinge
04c6c862c4 Refactor playlist items
Fix a bug where playlist items cover is not updated
2025-04-27 03:03:58 +02:00
Jonas Kvinge
baec45f742 CollectionBackend: Add updating collection database task 2025-04-27 02:27:46 +02:00
Jonas Kvinge
9efdbd2c10 CollectionWatcher: Add missing updates 2025-04-27 02:25:42 +02:00
Jonas Kvinge
d8800b80d5 CMake: Move discord-rpc to same target_link_libraries 2025-04-23 19:23:01 +02:00
Jonas Kvinge
ec715abb0d CI: Use macOS 12 SDK when available 2025-04-21 14:40:12 +02:00
Jonas Kvinge
1485801efb CI: Add support for Windows arm64 2025-04-20 02:14:42 +02:00
Jonas Kvinge
4f9ac3d33a nsi: Add support for arm64 2025-04-20 02:13:58 +02:00
Jonas Kvinge
1577ce4d67 Turn on git revision 2025-04-18 21:59:18 +02:00
Jonas Kvinge
7eee74a2e9 Release 1.2.10 2025-04-18 20:04:22 +02:00
Jonas Kvinge
d9e38fb3be Remove Genius lyrics
No longer working properly because of website changes.
2025-04-18 15:56:30 +02:00
Jonas Kvinge
81cc90e54a Update Changelog 2025-04-18 02:38:37 +02:00
Jonas Kvinge
bd9771a88f TagReaderTagLib: Use TagLib::Tag::comment
Makes it use only commercial frames without description for comments, reading other commercial frames picks different iTunes tags we don't want.
2025-04-18 02:15:17 +02:00
Jonas Kvinge
f5cd81fe09 nsi: Re-enable Spotify 2025-04-16 23:25:03 +02:00
Gregor Santner
277e2cff59 Linux: Add Clementine search keyword to .desktop shortcut 2025-04-15 21:46:15 +02:00
Jonas Kvinge
6fa9514059 RichPresence: Only initialize discord when enabled 2025-04-13 21:45:55 +02:00
Jonas Kvinge
c5e38b71f7 discord_rpc: Use anonymous namespace 2025-04-13 21:34:40 +02:00
Jonas Kvinge
3746915ae7 RichPresence: Always include album 2025-04-13 19:19:53 +02:00
Jonas Kvinge
21bdf88d09 RichPresence: Remove unused variable 2025-04-13 12:16:57 +02:00
Jonas Kvinge
ff032c3cd7 RichPresence: Remove rate limit 2025-04-13 12:01:56 +02:00
Jonas Kvinge
c083110051 RichPresence: Move variable declaration
Fixes #1718
2025-04-13 11:52:16 +02:00
Jonas Kvinge
a7dbeb5d76 discord-rpc: Add copyright 2025-04-12 13:17:13 +02:00
Jonas Kvinge
634f6ea9f5 discord-rpc: Formatting 2025-04-12 13:17:00 +02:00
Jonas Kvinge
f9e4f9a09a discord-rpc: Formatting 2025-04-11 22:50:14 +02:00
Jonas Kvinge
aab9889174 Turn on git revision 2025-04-09 19:59:48 +02:00
Jonas Kvinge
3b560e4e4f Release 1.2.9 2025-04-08 23:52:00 +02:00
Jonas Kvinge
9e327c9556 Update Changelog 2025-04-08 23:52:00 +02:00
Jonas Kvinge
1ec640e088 LastFMImport: Fix progress 2025-04-08 23:05:44 +02:00
Jonas Kvinge
463aaf6942 Update Changelog 2025-04-08 21:19:29 +02:00
Jonas Kvinge
71287dd77e Add option to turn off playbin3 2025-04-08 21:19:29 +02:00
dependabot[bot]
b66c0f5573 Bump vmactions/openbsd-vm from 1.1.6 to 1.1.7
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.1.6 to 1.1.7.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.1.6...v1.1.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-08 20:53:01 +02:00
Jonas Kvinge
a9f2c384fa RichPresence: Use class for activity 2025-04-08 20:52:34 +02:00
Jonas Kvinge
ae9584c213 Rename is_enabled to enabled 2025-04-08 20:33:54 +02:00
Jonas Kvinge
4db1c5ceb8 AlbumCoverFetcherSearch: Add debug for results 2025-04-08 20:33:27 +02:00
Jonas Kvinge
1738259467 SubsonicBaseRequest: Fix parsing
Fixes #1719
2025-04-08 20:18:54 +02:00
Jonas Kvinge
fcee02edc1 DeezerCoverProvider: Fix parsing
Fixes #1716
2025-04-08 20:04:02 +02:00
dependabot[bot]
4fff5820c5 Bump vmactions/freebsd-vm from 1.1.9 to 1.2.0
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.1.9 to 1.2.0.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.1.9...v1.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 19:32:24 +02:00
Jonas Kvinge
9aa6da2faf Mpris2: Ignore -Warray-bounds 2025-04-05 19:10:28 +02:00
Jonas Kvinge
279934411c Add Ubuntu Plucky 2025-04-05 18:31:08 +02:00
Jonas Kvinge
fd829551e8 Turn on git revision 2025-04-05 18:13:40 +02:00
Jonas Kvinge
be8f515388 Release 1.2.8 2025-04-05 12:38:22 +02:00
Jonas Kvinge
dd12462fbb Update Changelog 2025-04-05 12:38:22 +02:00
Strawberry Bot
5c3ded0099 New translations 2025-04-05 12:24:00 +02:00
Jonas Kvinge
2c9b14f5f2 GstEnginePipeline: Simplify version checks 2025-04-04 22:12:38 +02:00
Jonas Kvinge
8c195382c4 nsi: Disable spotify plugin 2025-04-03 22:51:12 +02:00
Jonas Kvinge
6e5198799c OAuthenticator: Rename function 2025-04-03 22:37:50 +02:00
Jonas Kvinge
389d04bbec OAuthenticator: Don't clear refresh token 2025-04-03 22:34:55 +02:00
Jonas Kvinge
6f0f88dd01 RichPresence: Simplify code 2025-04-03 22:21:51 +02:00
Jonas Kvinge
3dce84c73c MainWindow: Delay command line options until playlists are loaded
Fixes #1546
2025-03-30 16:45:34 +02:00
Jonas Kvinge
e798349aca Mpris2: Emit notifications when playlists are loaded
Fixes #1546
2025-03-30 16:43:29 +02:00
Jonas Kvinge
41ecf8e535 main: Add missing ifdef 2025-03-30 01:56:58 +01:00
Jonas Kvinge
70c96ded28 rpm: Add BuildRequires for cmake(RapidJSON) 2025-03-30 01:22:57 +01:00
Jonas Kvinge
d9cc6bf238 debian/control: Add rapidjson-dev 2025-03-30 01:22:57 +01:00
Jonas Kvinge
0a70ac1c95 Song: Update supported tags 2025-03-30 01:22:20 +01:00
Jonas Kvinge
e3a3f44513 TagReaderTagLib: Add full support for AIFF 2025-03-30 01:22:07 +01:00
Jonas Kvinge
c2e42ebf3a Song: Include fingerprints in IsSimilar
Fixes #1710
2025-03-30 00:34:43 +01:00
Jonas Kvinge
d71b344e77 MainWindow: Add missing ifdef 2025-03-30 00:07:22 +01:00
Jonas Kvinge
d5f7a4b883 RichPresence: Formatting and add settings reload 2025-03-30 00:06:05 +01:00
Jonas Kvinge
34fb289e33 serialization: Remove unneeded pragams 2025-03-29 23:01:41 +01:00
Jonas Kvinge
6d4d8251ad Update Changelog 2025-03-29 22:53:37 +01:00
Jonas Kvinge
fc69ef27e8 README: Add Discord rich presence 2025-03-29 22:46:50 +01:00
Jonas Kvinge
bbd8a24b75 discord: Add namespace and cleanup CMakeLists 2025-03-29 22:41:58 +01:00
fruityloops1
9fa9012c70 Discord RPC implementation 2025-03-29 22:41:58 +01:00
fruityloops1
2a4fc185ac Add discord-rpc library 2025-03-29 22:41:58 +01:00
Jonas Kvinge
fa0703246b Equalizer: Ignore -Warray-bounds 2025-03-28 23:09:49 +01:00
corubba
954c21e21e AlsaDeviceFinder: Use card id instead of card index
Like the card index, the card id is guaranteed to be unique. While card
index can easily change between reboots, the card id is based on the
actual audio hardware and does not change between reboots; or even
hardware changes. This makes using the card id preferable, because it
will "just work" 99% of the time, and removes the need to force cards to
have a specific index.

There is a corner case where card ids may change between reboots: If you
have two (or more) of the same audio hardware in the system. But that
should be rare enough, and requires explicit system configuration
anyway, so using the "custom" option should work here.

If there is an previously-saved index-based ALSA device in the config,
it will continue to work as-is and does not need to be migrated. There
is only a small UI side-effect: Because the index-based device will no
longer match any found id-based device, the settings window will show it
as "custom". Simply selecting the ALSA device from the drop-down again
will change it to the id-based device.
2025-03-28 19:54:32 +01:00
Jonas Kvinge
8954dfb0aa CMake: Add -W4 with MSVC 2025-03-25 18:05:41 +01:00
Jonas Kvinge
5e031be42c Fix cast warnings with MSVC 2025-03-25 18:05:41 +01:00
dependabot[bot]
d5281abb22 Bump apple-actions/import-codesign-certs from 3 to 5
Bumps [apple-actions/import-codesign-certs](https://github.com/apple-actions/import-codesign-certs) from 3 to 5.
- [Release notes](https://github.com/apple-actions/import-codesign-certs/releases)
- [Commits](https://github.com/apple-actions/import-codesign-certs/compare/v3...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 17:22:05 +01:00
Jonas Kvinge
f5368d8108 Revert "Bump apple-actions/import-codesign-certs from 3 to 4"
This reverts commit e2dc22c2c8.
2025-03-21 20:35:36 +01:00
dependabot[bot]
e2dc22c2c8 Bump apple-actions/import-codesign-certs from 3 to 4
Bumps [apple-actions/import-codesign-certs](https://github.com/apple-actions/import-codesign-certs) from 3 to 4.
- [Release notes](https://github.com/apple-actions/import-codesign-certs/releases)
- [Commits](https://github.com/apple-actions/import-codesign-certs/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-21 18:21:12 +01:00
Strawberry Bot
817ca828e6 New translations 2025-03-18 22:44:29 +01:00
dependabot[bot]
61dc2cc640 Bump vmactions/freebsd-vm from 1.1.8 to 1.1.9
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.1.8 to 1.1.9.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.1.8...v1.1.9)

---
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>
2025-03-18 17:58:35 +01:00
Jonas Kvinge
cd4adf6f89 ebur128analysis: Handle extra enums with GStreamer 1.25 and higher 2025-03-17 22:57:36 +01:00
Jonas Kvinge
91ebcc948e debian/rules: Build with -DBUILD_WERROR=ON 2025-03-17 22:28:24 +01:00
Jonas Kvinge
e7e6d59172 Remove KDSingleApplication from 3rdparty 2025-03-17 21:38:11 +01:00
Jonas Kvinge
95b12a01a4 OAuthenticator: Fix logging 2025-03-17 19:42:59 +01:00
Jonas Kvinge
5947aeae24 nsi: Bump icu 2025-03-15 23:29:12 +01:00
Jonas Kvinge
0298fa0b73 TidalBaseRequest: Don't clear session 2025-03-14 20:18:53 +01:00
Jonas Kvinge
05d72c8bd6 nsi: Add asio for mingw 2025-03-13 00:10:37 +01:00
Roman Lebedev
70b7c4560d gst_channel_to_ebur_channel(): handle new top-surround channels
These seem to have appeared in gstreamer 1.26,
which is the version we need to use to guard the handling.

These are effectively geometrically located on the same azimuth,
but on the layer above than the non-top (i.e. middle layer)
surround channels. But they are still surround channels,
which ebur128 does not bias loudness-wise.

At least this is my understanding.
2025-03-12 22:20:56 +01:00
Roman Lebedev
2687dc31cc Support arbitrarily large EBU R 128 loudness normalization
While i have fixed gstreamer's `volume` in
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5063
i did not see anything that followed after it was merged, namely, in
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6222,
the feature was moved, `"volume"` was reverted to only handle `x10` gain,
and one needs to use `"volume-full-range"` instead to do arbitrary gain.
So let's do that.

This, of course, requires run-time detection of the version
of gstreamer base plugins that we are running with,
specifically, we need version `1.24`.
2025-03-12 22:20:56 +01:00
Jonas Kvinge
cabf1cb78d CI: Bump macOS runner 2025-03-11 22:54:34 +01:00
Jonas Kvinge
d9f68ab944 Application: Add QTimer include
Fixes #1695
2025-03-09 11:29:51 +01:00
Jonas Kvinge
8630c5329d LyricsFetcherSearch: Fix authentication check 2025-03-09 02:11:12 +01:00
Jonas Kvinge
66e175f6d1 PlaylistItem: Add dtor 2025-03-08 23:46:12 +01:00
Jonas Kvinge
1173d5f865 Lyrics: Refactor 2025-03-08 23:26:44 +01:00
Jonas Kvinge
b02b114caf Scrobbler: Refactor 2025-03-08 23:19:42 +01:00
Jonas Kvinge
cd516c37b9 Refactor Tidal, Spotify, Qobuz, Subsonic and cover providers
Use common HTTP, Json and OAuthenticator class
2025-03-08 23:11:07 +01:00
Jonas Kvinge
7de8a44709 Add OAuthenticator 2025-03-08 22:46:59 +01:00
Jonas Kvinge
bb345b14de Add base classes for HTTP and Json 2025-03-08 22:46:46 +01:00
Jonas Kvinge
baa82966d8 Move SearchType to StreamingService 2025-03-08 22:41:43 +01:00
Jonas Kvinge
f85d60f5cd Formatting 2025-03-08 22:31:00 +01:00
Jonas Kvinge
6f731fcf4a MainWindow: Add const 2025-03-08 22:30:27 +01:00
Jonas Kvinge
bdbe66b116 Support more collections 2025-03-08 22:24:28 +01:00
Jonas Kvinge
5ae0320911 CollectionBackend: Add delete songs by URLs function 2025-03-08 21:46:27 +01:00
Jonas Kvinge
31671af5f5 MultiLoadingIndicator: Only emit task count change when needed 2025-03-08 21:45:34 +01:00
Jonas Kvinge
27184eb001 Utilities: Add StringListToHTML 2025-03-08 21:45:00 +01:00
Jonas Kvinge
2e30f5c585 StreamTagReader: Rename variable 2025-03-08 21:44:33 +01:00
Jonas Kvinge
109d3f9ec3 PlaylistBackend: Move query to static function 2025-03-08 21:36:42 +01:00
Jonas Kvinge
e9d413c7dc QTcpServer: Add success and port 2025-03-08 21:26:47 +01:00
Jonas Kvinge
ee60191b6c MusicBrainzClient: Formatting 2025-03-08 21:24:59 +01:00
Jonas Kvinge
a6d8627129 AcoustidClient: Formatting 2025-03-08 21:24:29 +01:00
Jonas Kvinge
d317c9158b UrlHandler: Formatting 2025-03-08 21:23:02 +01:00
Jonas Kvinge
3716e8c3ef CollectionModel: Rename variable 2025-03-08 21:22:40 +01:00
Jonas Kvinge
ce0e1e900e Update README.md 2025-03-04 23:31:23 +01:00
Jonas Kvinge
f1aa92ec9f Update README.md 2025-03-04 21:25:35 +01:00
Jonas Kvinge
25bdfcdb76 nsi: Update sqlite3 dll name 2025-02-20 22:37:43 +01:00
Jonas Kvinge
6a3de3937a AppearanceSettingsPage: Add tooltip about restart
You need to restart Strawberry for this setting to take affect.
2025-02-20 16:11:24 +01:00
Jonas Kvinge
5f775e87ae BackendSettingsPage: Add tooltip for HTTP/2
You need to restart Strawberry for this setting to take affect
2025-02-20 16:10:21 +01:00
Jonas Kvinge
1fd83c55ee Equalizer: Add tooltip that playback must be restarted 2025-02-20 16:09:10 +01:00
Jonas Kvinge
e6e9edef7d tests: Remove unused TestObjectDecorators 2025-02-18 23:00:01 +01:00
Jonas Kvinge
1bae665f76 CI: Fix OpenMandriva build 2025-02-18 22:02:22 +01:00
Jonas Kvinge
5bfc8b74f5 CI: Fix Mageia build 2025-02-18 22:02:12 +01:00
Jonas Kvinge
e588896729 FilterParser: Update tooltip
Fixes #1680
2025-02-18 17:08:17 +01:00
Jonas Kvinge
0cd0f7f2e7 FilesystemMusicStorage: Use QFile::supportsMoveToTrash 2025-02-18 16:55:02 +01:00
Jonas Kvinge
6db540a3a7 nsi: Bump libFLAC version 2025-02-12 01:25:12 +01:00
Jonas Kvinge
82679e0cea Fix issue template 2025-02-12 01:07:04 +01:00
Jonas Kvinge
d571bc3305 TagReaderTagLib: Fix build without stream tagreader
Fixes #1672
2025-02-10 23:29:40 +01:00
Jonas Kvinge
2b52553864 Add stream tagreader 2025-02-08 02:53:10 +01:00
Jonas Kvinge
215627b0e4 TagReaderGME: Fix use of Qt::CaseInsensitive and length check 2025-02-08 01:17:44 +01:00
Jonas Kvinge
b17cae6ec7 rpm: Exclude %debug_package on tumbleweed 2025-02-07 22:23:04 +01:00
Jonas Kvinge
30ac9697ea BackendSettingsPage: Increase device lineedit height
Bottom of the text was cut off with the breeze style
2025-02-07 21:27:17 +01:00
Jonas Kvinge
61e3ea249d Turn off "Grey out unavailable songs in playlists on startup" by default 2025-02-02 23:48:05 +01:00
Jonas Kvinge
d1986eeae2 Tidal: Save token type 2025-02-01 22:25:53 +01:00
Jonas Kvinge
ba354207d2 Tidal: Remove deprecated username/password login 2025-02-01 22:10:53 +01:00
Jonas Kvinge
eac5674891 TidalService: Clear refresh token on sign out 2025-02-01 00:50:17 +01:00
Jonas Kvinge
8349a8b0ee Port back to "output" and "device" settings in lowercase
Was accidentally changed to capitalized.
2025-02-01 00:48:57 +01:00
Jonas Kvinge
b9b4e9f831 TidalSettingsPage: Add HI_RES_LOSSLESS 2025-01-31 23:17:19 +01:00
Jonas Kvinge
98ff2525f0 Turn on git revision 2025-01-31 18:26:03 +01:00
Jonas Kvinge
3fd29c6dcc Release 1.2.7 2025-01-31 16:35:19 +01:00
Jonas Kvinge
4429e9973f StandardItemIconLoader : Rename LoadIcon
Avoids conflict on Windows where windows headers define LoadIcon to LoadIconW with unicode.
2025-01-31 16:20:03 +01:00
Jonas Kvinge
1572d241d5 Replace Windows conflicting "LoadIcon" with "SetIcon"
Windows headers defines LoadIcon to LoadIconW when UNICODE is defined.
2025-01-31 16:10:23 +01:00
Jonas Kvinge
eae7e2a9e6 Update Changelog 2025-01-31 13:09:38 +01:00
Jonas Kvinge
251e5b379b Disable OSD Pretty on Wayland 2025-01-29 22:12:29 +01:00
Jonas Kvinge
0db082fca0 Replace Q_OS_WIN with Q_OS_WIN32 2025-01-28 20:30:43 +01:00
Jonas Kvinge
2799e55076 OSDPretty: Set window title 2025-01-28 20:24:27 +01:00
Jonas Kvinge
440ee43a91 Update Changelog 2025-01-27 14:56:06 +01:00
Jonas Kvinge
98c72ec1f8 import-from-clementine: Minor tidy 2025-01-27 14:55:51 +01:00
Jonas Kvinge
759488ae1a CMake: Reword QPlatformNativeInterface option 2025-01-27 14:55:30 +01:00
Jonas Kvinge
055cb413c9 import-from-clementine: Handle NULL filename and remove FTS
Fixes #1660
2025-01-27 14:41:14 +01:00
Jonas Kvinge
f760b87b58 CI: Add missing dependencies for PPA 2025-01-25 19:37:58 +01:00
Jonas Kvinge
39f228f862 CMake: Add QPA Platform interface as optional component 2025-01-25 18:31:22 +01:00
Jonas Kvinge
a683a279f5 CMake: Define NOMINMAX on Windows 2025-01-24 12:46:49 +01:00
Jonas Kvinge
e800926f57 Ignore -Wcpp for gtest.h/gmock.h 2025-01-24 12:46:48 +01:00
Strawberry Bot
dd9f80d539 New translations 2025-01-24 10:58:02 +01:00
Jonas Kvinge
8484cac4ed Add strawberry_fr_BE.ts 2025-01-24 10:42:40 +01:00
Strawberry Bot
e24097582f New translations 2025-01-24 10:40:12 +01:00
Jonas Kvinge
58fc8c82bb MainWindow: Maximize error dialog when window is shown 2025-01-22 17:51:16 +01:00
Jonas Kvinge
02bb875bb3 ErrorDialog: Only raise window if parent is maximized
Fixes #1627
2025-01-22 17:50:41 +01:00
Jonas Kvinge
5db01482eb Lazy: Fix bool 2025-01-22 17:49:52 +01:00
Jonas Kvinge
719fa6ffb3 MainWindow: Only hide window when system tray and keep running is enabled 2025-01-22 17:26:08 +01:00
Jonas Kvinge
159be5d79e MainWindow: Change close() to hide() in SetHiddenInTray
Otherwise close event is triggered causing Strawberry to quit.
2025-01-19 09:45:13 +01:00
Jonas Kvinge
911237e281 AnalyzerBase: Add missing parameter names 2025-01-19 09:42:00 +01:00
Jonas Kvinge
ae89ca8123 Turn on git revision 2025-01-17 12:34:43 +01:00
Jonas Kvinge
b832675893 Release 1.2.6 2025-01-17 10:27:48 +01:00
Jonas Kvinge
b4cfe636c9 TranscodeDialog: Fix mismatched definition 2025-01-17 09:15:55 +01:00
Jonas Kvinge
e6a0945dfa Call QObject::metaObject 2025-01-17 09:08:59 +01:00
Jonas Kvinge
726c105ed6 MoodbarItemDelegate: Remove delete of data
Memory is deleted in QCache::insert
2025-01-17 08:29:17 +01:00
Jonas Kvinge
d73cbc3a1d EngineBase: Fix mismatched definition 2025-01-17 08:26:11 +01:00
Jonas Kvinge
121f45d3b6 Playlist: Use sizeof playlist pointer to pointer 2025-01-17 07:22:49 +01:00
Jonas Kvinge
3a9ea81929 Queue: Fix sizeof, should be the pointer not the class 2025-01-17 07:12:25 +01:00
Jonas Kvinge
b919472241 Playlist: Correct sizeof 2025-01-17 06:56:08 +01:00
Jonas Kvinge
e8c8b39410 Turn on git revision 2025-01-17 04:29:05 +01:00
Jonas Kvinge
6904efef47 Release 1.2.5 2025-01-17 01:40:38 +01:00
Jonas Kvinge
fafa89baff CI: Disable GIO on Windows 2025-01-16 07:18:31 +01:00
Strawberry Bot
d9062446f5 New translations 2025-01-16 06:39:23 +01:00
Jonas Kvinge
9256b92d8f Update Changelog 2025-01-16 06:32:35 +01:00
Jonas Kvinge
f66459f3cb Make optional feature required unless disabled, add QtSparkle for macOS 2025-01-16 06:22:13 +01:00
Jonas Kvinge
f8ea9631ca Add sparkle 2025-01-15 23:03:40 +01:00
Jonas Kvinge
ab558f87b5 GstEnginePipeline: Use SetStateAsync in finish if needed 2025-01-15 07:01:43 +01:00
Jonas Kvinge
ab73eda2be Add mutex_protected_test 2025-01-15 07:00:28 +01:00
Jonas Kvinge
92f34ff36e mutex_protected: Add more operators 2025-01-15 07:00:16 +01:00
Strawberry Bot
d0bf2d7a9c New translations 2025-01-14 07:10:07 +01:00
Jonas Kvinge
b2cd3afe55 TagReaderClient: Add [[nodiscard]] 2025-01-14 06:36:15 +01:00
Jonas Kvinge
decd0a1dc6 TagReaderClient: Connect TagReaderReplyPtr
Makes sure TagReaderReplyPtr is not deleted too early.

Partial fix for #1633
2025-01-14 06:35:51 +01:00
Jonas Kvinge
3e0a9fa388 SettingsProvider: Use Settings
Fixes #1649
2025-01-13 12:22:31 +01:00
Jonas Kvinge
e5b6c5959f CMake: Use find_package for qtsparkle 2025-01-12 04:16:17 +01:00
Jonas Kvinge
8a9db5440d rpm: Enable unit tests on Fedora 2025-01-11 01:39:26 +01:00
Jonas Kvinge
28a25c5763 CMake: Fix add_test 2025-01-11 01:37:06 +01:00
dependabot[bot]
ea49fbcbee Bump vmactions/openbsd-vm from 1.1.5 to 1.1.6
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.1.5 to 1.1.6.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.1.5...v1.1.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 00:37:52 +01:00
Jonas Kvinge
d9807b358e GioLister: Fix use of deprecated functions 2025-01-11 00:35:24 +01:00
Jonas Kvinge
3e53d2b237 CI: Add -Wno-maybe-uninitialized for Fedora 2025-01-11 00:35:24 +01:00
Jonas Kvinge
9122881f74 CI: Bump GCC on openSUSE Leap 2025-01-11 00:35:24 +01:00
Jonas Kvinge
9c36d7fd43 rpm: Add BUILD_WERROR 2025-01-11 00:35:24 +01:00
Jonas Kvinge
a4a365cbee CI: Set RPM_BUILD_NCPUS to 4 2025-01-11 00:35:24 +01:00
Jonas Kvinge
00f06b22b8 CMake: Check for gmock 2025-01-11 00:35:24 +01:00
Jonas Kvinge
e2d8838fca rpm: Run unit tests 2025-01-11 00:35:24 +01:00
Jonas Kvinge
9427691f39 CI: Install gtest and gmock for openSUSE, Fedora and Mageia 2025-01-11 00:35:24 +01:00
Jonas Kvinge
bebdcc4e7f CollectionModel: Simply data function 2025-01-10 17:47:30 +01:00
Jonas Kvinge
041f761921 test_utils: Fix Q_ASSERT 2025-01-10 15:35:15 +01:00
Jonas Kvinge
1435ae6dc0 Turn on git revision 2025-01-10 15:08:04 +01:00
Jonas Kvinge
33ae53a90f Release 1.2.4 2025-01-10 02:26:41 +01:00
Strawberry Bot
01c28867b7 New translations 2025-01-10 02:10:44 +01:00
Jonas Kvinge
08fb2ae331 Update Changelog 2025-01-10 01:58:19 +01:00
Jonas Kvinge
99970f9e52 Update strawberry_en_US.ts 2025-01-10 01:58:19 +01:00
Jonas Kvinge
bc206f43b4 TranscodeDialog: Minor adjustments 2025-01-10 01:58:19 +01:00
dependabot[bot]
018448159c Bump vmactions/freebsd-vm from 1.1.7 to 1.1.8
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.1.7 to 1.1.8.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.1.7...v1.1.8)

---
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>
2025-01-10 01:27:57 +01:00
Jonas Kvinge
81fe90bdef Update Changelog 2025-01-10 01:07:32 +01:00
Jonas Kvinge
319558c535 tests: Fixed ignored return value 2025-01-10 00:58:41 +01:00
Jonas Kvinge
9095b0d6b2 Always run MSVC runtime installer 2025-01-08 19:34:27 +01:00
Jonas Kvinge
558eae1ca1 Remove unneeded slots 2025-01-08 04:57:17 +01:00
Jonas Kvinge
2c64f05cea MoodbarProxyStyle: Add const 2025-01-08 04:54:45 +01:00
Jonas Kvinge
c80e7071a1 StandardPaths: Fix namespace 2025-01-07 22:13:24 +01:00
Jonas Kvinge
038e679000 SmartPlaylistWizard: Apply dark mode workaround for all styles except vista
Fixes #1639
2025-01-07 22:04:28 +01:00
Jonas Kvinge
72447fecfb StandardPaths: Remove inheritance 2025-01-07 21:40:06 +01:00
Jonas Kvinge
c7830f6f05 MoodbarPipeline: Cleanup on finish 2025-01-07 01:09:49 +01:00
Jonas Kvinge
af525e42b6 MoodbarPipeline: Remove Q_ASSERT 2025-01-06 21:28:44 +01:00
Jonas Kvinge
eb83f23125 MoodbarPipeline: Use bytearray directly 2025-01-06 21:28:33 +01:00
Jonas Kvinge
7527d2ea9a MoodbarLoader: Set object name 2025-01-06 18:11:38 +01:00
Jonas Kvinge
69d38879d2 Use QCoreApplication::applicationName() directly 2025-01-06 00:38:51 +01:00
Jonas Kvinge
cbce9f7191 Override config, data and cache location 2025-01-05 23:45:29 +01:00
Jonas Kvinge
f938129d81 CI: Fix deb copy command 2025-01-05 21:22:08 +01:00
Jonas Kvinge
415a40ea04 FilterParser: Ignore space after operator 2025-01-05 20:25:48 +01:00
Jonas Kvinge
6e7aaed4ee Use QSharedPointer for GstEnginePipeline 2025-01-05 19:03:16 +01:00
Jonas Kvinge
7afae70bb0 GstEnginePipeline: Make sure all set states are finished before finishing pipeline 2025-01-05 18:58:03 +01:00
Jonas Kvinge
be8097919b mutex_protected: Add operator ++ and -- 2025-01-05 18:56:22 +01:00
Jonas Kvinge
1990a42e1d CI: Don't run build on l10n_master branch 2025-01-05 18:53:19 +01:00
Kyle Hopkins
8fcee4511d TranscodeDialog: update to optionally preserve directory structure 2025-01-05 18:34:14 +01:00
Leandro Matheus
24af1be666 Subsonic: Add support for using album id to retrieve album covers 2025-01-05 18:30:24 +01:00
Jonas Kvinge
8302a95bc1 Use shared pointers for moodbar pipelines
Possible fix for #1633
2025-01-05 18:28:41 +01:00
Jonas Kvinge
36a8ab49a0 MoodbarItemDelegate: Format comment 2025-01-05 03:51:00 +01:00
Jonas Kvinge
5c64dc9c4d MoodbarItemDelegate: Delete data if it fails to insert to cache 2025-01-05 03:50:51 +01:00
Strawberry Bot
c271743208 New translations 2025-01-05 00:22:50 +01:00
Jonas Kvinge
ee49b1ddc8 Update strawberry_en_US.ts 2025-01-04 04:56:08 +01:00
Jonas Kvinge
bf98633f16 Load XSPF title as playlist name 2025-01-04 04:52:17 +01:00
Jonas Kvinge
e2a928f2dc Save XSPF playlist with title
Fixes #1624
2025-01-04 03:48:53 +01:00
Jonas Kvinge
47d3312a6b PlaylistManager: Remove slash from playlist filename 2025-01-04 03:46:03 +01:00
Jonas Kvinge
4f97325953 CollectionSettingsPage: Fix string conversion 2025-01-04 03:32:50 +01:00
Jonas Kvinge
91e8fe0943 ScrobblerSettingsPage: Add tooltip 2025-01-04 03:15:38 +01:00
Jonas Kvinge
dc5894b38a CollectionWatcher: Ignore special filesystem paths
Fixes #1615
2025-01-04 03:06:46 +01:00
Jonas Kvinge
52ee50a2a4 CollectionSettingsPage: Add check for filesystem type 2025-01-04 02:58:59 +01:00
Jonas Kvinge
f545b028ee Add filesystem constants 2025-01-04 02:58:17 +01:00
Jonas Kvinge
a96627c5a9 Playlist: Invalidate deleted songs in main thread
Fixes #1625
2025-01-04 00:16:36 +01:00
Jonas Kvinge
a13fc31f83 PlaylistManager: Remove unused InvalidateDeletedSongs function 2025-01-04 00:09:26 +01:00
Jonas Kvinge
9ee5c8dc17 Playlist: Update copyright 2025-01-03 23:41:43 +01:00
Jonas Kvinge
82cd425ece Playlist: Add const 2025-01-03 23:41:30 +01:00
Jonas Kvinge
3b02d364ba Playlist: Rename some variables 2025-01-03 23:41:15 +01:00
dependabot[bot]
73e7947487 Bump vmactions/openbsd-vm from 1.1.4 to 1.1.5
Bumps [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm) from 1.1.4 to 1.1.5.
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](https://github.com/vmactions/openbsd-vm/compare/v1.1.4...v1.1.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-30 17:38:02 +01:00
dependabot[bot]
cfc9a43b88 Bump vmactions/freebsd-vm from 1.1.6 to 1.1.7
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.1.6 to 1.1.7.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.1.6...v1.1.7)

---
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>
2024-12-30 17:37:32 +01:00
dependabot[bot]
969500023a Bump vmactions/freebsd-vm from 1.1.5 to 1.1.6
Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 1.1.5 to 1.1.6.
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.1.5...v1.1.6)

---
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>
2024-12-26 00:44:32 +01:00
Jonas Kvinge
53c72d4f8e CollectionModel: Don't use artist sort text for album 2024-12-17 22:38:04 +01:00
Jonas Kvinge
f971c92f32 Move debian back to source dir
PPA failed to build
2024-12-17 21:57:37 +01:00
Strawberry Bot
82156e8a13 New translations 2024-12-17 00:01:04 +01:00
dependabot[bot]
6a6285861e 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>
2024-12-16 23:59:25 +01:00
Jonas Kvinge
dae8c8730b debian: Remove whitespaces 2024-12-14 18:33:40 +01:00
Jonas Kvinge
20b47eae8f CI: Use -j 4 for Ubuntu 2024-12-14 18:33:29 +01:00
Jonas Kvinge
2ce8220d88 Move generated files to binary directory 2024-12-14 17:59:26 +01:00
Jonas Kvinge
35b0b5df57 CMake: Fix backtrace linking 2024-12-14 15:47:45 +01:00
Jonas Kvinge
63631d6b0c CMake: Fix backtrace linking 2024-12-14 05:30:31 +01:00
Jonas Kvinge
21ab2ef1a7 CI: Add FreeBSD and OpenBSD 2024-12-14 05:01:40 +01:00
Jonas Kvinge
a3f96d2b85 CMake: Link Backtrace 2024-12-14 05:01:14 +01:00
Jonas Kvinge
e2c1cb0116 Formatting 2024-12-14 00:55:53 +01:00
Jonas Kvinge
07e295776b Add Spotify to scrobbler 2024-12-10 01:29:57 +01:00
Jonas Kvinge
8604a39d94 Turn on git revision 2024-12-08 17:29:14 +01:00
Jonas Kvinge
315240fec7 Release 1.2.3 2024-12-08 16:21:28 +01:00
Jonas Kvinge
fbc6d326f8 Update Changelog 2024-12-08 16:15:24 +01:00
Strawberry Bot
c082377e7a New translations 2024-12-07 19:21:29 +01:00
Jonas Kvinge
448445a38a CI: Replace redhat-lsb-core with lsb_release 2024-12-07 14:42:15 +01:00
Jonas Kvinge
18f835d7e5 Mpris2: Check for valid current row 2024-12-07 14:36:44 +01:00
Jonas Kvinge
fd427dac29 Handle missing HTTP status code 2024-12-07 14:02:59 +01:00
Jonas Kvinge
e1afe03d51 Check for valid http status code 2024-12-07 00:32:06 +01:00
Jonas Kvinge
1b49653974 Update Changelog 2024-12-06 23:55:25 +01:00
Jonas Kvinge
d66126f998 GstEngine: Add missing seek
Fixes #1568
2024-12-06 23:44:27 +01:00
Jonas Kvinge
0fff5f672a Rename variables 2024-12-06 23:43:44 +01:00
Strawberry Bot
2726f01fb3 New translations 2024-12-04 22:41:27 +01:00
Jonas Kvinge
9a74fce53d DiscogsCoverProvider: Use anonymous namespace for constants 2024-12-02 22:01:00 +01:00
Jonas Kvinge
b3be8387f1 Song: Add .zst to rejected file extensions
Fixes #1612
2024-11-29 23:37:49 +01:00
Jonas Kvinge
d396cb515d Include cstddef before libcdio includes
Fixes #1610
2024-11-29 22:50:49 +01:00
Jonas Kvinge
2548b4648e Turn on git revision 2024-11-23 19:34:13 +01:00
Jonas Kvinge
df0ec6b709 Release 1.2.2 2024-11-23 17:25:42 +01:00
Jonas Kvinge
376af26f0e Playlist: Move new QMimeData 2024-11-23 15:19:31 +01:00
Jonas Kvinge
9bff55e1ee PlaylistView: Ignore invalid QHeaderView::sectionResized
Workaround a possible Qt bug: moving a song in the playlist triggers `QHeaderView::sectionResized` with the state reset for each column, this causes an an invalid state for the last column since SetHeaderState is called before the last column state is restored, which again is used when restoring the playlist columns after switching to different playlist. To workaround this, only call SetHeaderState when the new column size is not zero.
2024-11-23 11:07:15 +01:00
Jonas Kvinge
8f7e29f503 PlaylistView: Use constants 2024-11-23 10:56:29 +01:00
Jonas Kvinge
c3aa885a0f SmartPlaylistSearchPreview: Remove early SetItemDelegates
It's called too early before MoodbarLoader is set, Init() already calls SetItemDelegates.

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

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

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

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

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

This fixes a huge memory leak.

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

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

WaveRubber: a kinda elastic waveform analyzer

Update src/analyzer/waverubber.cpp

better pointer declaration

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -86,7 +86,7 @@ ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
DisableFormat: true
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
@@ -130,7 +130,10 @@ InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLines:
AtEndOfFile: true
AtStartOfBlock: true
AtStartOfFile: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''

1
.github/FUNDING.yml vendored
View File

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

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

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

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

File diff suppressed because it is too large Load Diff

132
.gitignore vendored
View File

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

0
.gitmodules vendored Normal file
View File

30
3rdparty/README.md vendored
View File

@@ -1,30 +0,0 @@
3rdparty libraries located in this directory
============================================
singleapplication
-----------------
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.
URL: https://github.com/itay-grudev/SingleApplication
SPMediaKeyTap
-------------
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.

View File

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

View File

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

View File

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

View File

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

View File

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

41
3rdparty/discord-rpc/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,41 @@
set(DISCORD_RPC_SOURCES
discord_rpc.h
discord_register.h
discord_rpc.cpp
discord_rpc_connection.h
discord_rpc_connection.cpp
discord_serialization.h
discord_serialization.cpp
discord_connection.h
discord_backoff.h
discord_msg_queue.h
)
if(UNIX)
list(APPEND DISCORD_RPC_SOURCES discord_connection_unix.cpp)
if(APPLE)
list(APPEND DISCORD_RPC_SOURCES discord_register_osx.m)
add_definitions(-DDISCORD_OSX)
else()
list(APPEND DISCORD_RPC_SOURCES discord_register_linux.cpp)
add_definitions(-DDISCORD_LINUX)
endif()
endif()
if(WIN32)
list(APPEND DISCORD_RPC_SOURCES discord_connection_win.cpp discord_register_win.cpp)
add_definitions(-DDISCORD_WINDOWS)
endif()
add_library(discord-rpc STATIC ${DISCORD_RPC_SOURCES})
if(APPLE)
target_link_libraries(discord-rpc PRIVATE "-framework AppKit")
endif()
if(WIN32)
target_link_libraries(discord-rpc PRIVATE psapi advapi32)
endif()
target_include_directories(discord-rpc SYSTEM PRIVATE ${RapidJSON_INCLUDE_DIRS})
target_include_directories(discord-rpc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

19
3rdparty/discord-rpc/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright 2017 Discord, Inc.
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.

162
3rdparty/discord-rpc/README.md vendored Normal file
View File

@@ -0,0 +1,162 @@
# Discord RPC
## Fork Notice
This library was slightly modified for Strawberry Music Player with some extra features from the new API and shared library support/more unnecessary components removed. The original repository is [here](https://github.com/discord/discord-rpc)
## Deprecation Notice
This library has been deprecated in favor of Discord's GameSDK. [Learn more here](https://discordapp.com/developers/docs/game-sdk/sdk-starter-guide)
---
This is a library for interfacing your game with a locally running Discord desktop client. It's known to work on Windows, macOS, and Linux. You can use the lib directly if you like, or use it as a guide to writing your own if it doesn't suit your game as is. PRs/feedback welcome if you have an improvement everyone might want, or can describe how this doesn't meet your needs.
Included here are some quick demos that implement the very minimal subset to show current status, and
have callbacks for where a more complete game would do more things (joining, spectating, etc).
## Documentation
The most up to date documentation for Rich Presence can always be found on our [developer site](https://discordapp.com/developers/docs/rich-presence/how-to)! If you're interested in rolling your own native implementation of Rich Presence via IPC sockets instead of using our SDK—hey, you've got free time, right?—check out the ["Hard Mode" documentation](https://github.com/discordapp/discord-rpc/blob/master/documentation/hard-mode.md).
## Basic Usage
Zeroith, you should be set up to build things because you are a game developer, right?
First, head on over to the [Discord developers site](https://discordapp.com/developers/applications/me) and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init function.
### Unreal Engine 4 Setup
To use the Rich Presense plugin with Unreal Engine Projects:
1. Download the latest [release](https://github.com/discordapp/discord-rpc/releases) for each operating system you are targeting and the zipped source code
2. In the source code zip, copy the UE plugin—`examples/unrealstatus/Plugins/discordrpc`—to your project's plugin directory
3. At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create an `Include` folder and copy `discord_rpc.h` and `discord_register.h` to it from the zip
4. Follow the steps below for each OS
5. Build your UE4 project
6. Launch the editor, and enable the Discord plugin.
#### Windows
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Win64` folder
- Copy `lib/discord-rpc.lib` and `bin/discord-rpc.dll` from `[RELEASE_ZIP]/win64-dynamic` to the `Win64` folder
#### Mac
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Mac` folder
- Copy `libdiscord-rpc.dylib` from `[RELEASE_ZIP]/osx-dynamic/lib` to the `Mac` folder
#### Linux
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Linux` folder
- Inside, create another folder `x86_64-unknown-linux-gnu`
- Copy `libdiscord-rpc.so` from `[RELEASE_ZIP]/linux-dynamic/lib` to `Linux/x86_64-unknown-linux-gnu`
### Unity Setup
If you're a Unity developer looking to integrate Rich Presence into your game, follow this simple guide to get started towards success:
1. Download the DLLs for any platform that you need from [our releases](https://github.com/discordapp/discord-rpc/releases)
2. In your Unity project, create a `Plugins` folder inside your `Assets` folder if you don't already have one
3. Copy the file `DiscordRpc.cs` from [here](https://github.com/discordapp/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordRpc.cs) into your `Assets` folder. This is basically your header file for the SDK
We've got our `Plugins` folder ready, so let's get platform-specific!
#### Windows
4. Create `x86` and `x86_64` folders inside `Assets/Plugins/`
5. Copy `discord-rpc-win/win64-dynamic/bin/discord-rpc.dll` to `Assets/Plugins/x86_64/`
6. Copy `discord-rpc-win/win32-dynamic/bin/discord-rpc.dll` to `Assets/Plugins/x86/`
7. Click on both DLLs and make sure they are targetting the correct architectures in the Unity editor properties pane
8. Done!
#### MacOS
4. Copy `discord-rpc-osx/osx-dynamic/lib/libdiscord-rpc.dylib` to `Assets/Plugins/`
5. Rename `libdiscord-rpc.dylib` to `discord-rpc.bundle`
6. Done!
#### Linux
4. Copy `discord-rpc-linux/linux-dynamic-lib/libdiscord-rpc.so` to `Assets/Plugins/`
5. Done!
You're ready to roll! For code examples on how to interact with the SDK using the `DiscordRpc.cs` header file, check out [our example](https://github.com/discordapp/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordController.cs)
### From package
Download a release package for your platform(s) -- they have subdirs with various prebuilt options, select the one you need add `/include` to your compile includes, `/lib` to your linker paths, and link with `discord-rpc`. For the dynamically linked builds, you'll need to ship the associated file along with your game.
### From repo
First-eth, you'll want `CMake`. There's a few different ways to install it on your system, and you should refer to [their website](https://cmake.org/install/). Many package managers provide ways of installing CMake as well.
To make sure it's installed correctly, type `cmake --version` into your flavor of terminal/cmd. If you get a response with a version number, you're good to go!
There's a [CMake](https://cmake.org/download/) file that should be able to generate the lib for you; Sometimes I use it like this:
```sh
cd <path to discord-rpc>
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to>
cmake --build . --config Release --target install
```
There is a wrapper build script `build.py` that runs `cmake` with a few different options.
Usually, I run `build.py` to get things started, then use the generated project files as I work on things. It does depend on `click` library, so do a quick `pip install click` to make sure you have it if you want to run `build.py`.
There are some CMake options you might care about:
| flag | default | does |
| ---------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENABLE_IO_THREAD` | `ON` | When enabled, we start up a thread to do io processing, if disabled you should call `Discord_UpdateConnection` yourself. |
| `USE_STATIC_CRT` | `OFF` | (Windows) Enable to statically link the CRT, avoiding requiring users install the redistributable package. (The prebuilt binaries enable this option) |
| [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/v3.7/variable/BUILD_SHARED_LIBS.html) | `OFF` | Build library as a DLL |
| `WARNINGS_AS_ERRORS` | `OFF` | When enabled, compiles with `-Werror` (on \*nix platforms). |
## Continuous Builds
Why do we have three of these? Three times the fun!
| CI | badge |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| TravisCI | [![Build status](https://travis-ci.org/discordapp/discord-rpc.svg?branch=master)](https://travis-ci.org/discordapp/discord-rpc) |
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/qvkoc0w1c4f4b8tj?svg=true)](https://ci.appveyor.com/project/crmarsh/discord-rpc) |
| Buildkite (internal) | [![Build status](https://badge.buildkite.com/e103d79d247f6776605a15246352a04b8fd83d69211b836111.svg)](https://buildkite.com/discord/discord-rpc) |
## Sample: send-presence
This is a text adventure "game" that inits/deinits the connection to Discord, and sends a presence update on each command.
## Sample: button-clicker
This is a sample [Unity](https://unity3d.com/) project that wraps a DLL version of the library, and sends presence updates when you click on a button. Run `python build.py unity` in the root directory to build the correct library files and place them in their respective folders.
## Sample: unrealstatus
This is a sample [Unreal](https://www.unrealengine.com) project that wraps the DLL version of the library with an Unreal plugin, exposes a blueprint class for interacting with it, and uses that to make a very simple UI. Run `python build.py unreal` in the root directory to build the correct library files and place them in their respective folders.
## Wrappers and Implementations
Below is a table of unofficial, community-developed wrappers for and implementations of Rich Presence in various languages. If you would like to have yours added, please make a pull request adding your repository to the table. The repository should include:
- The code
- A brief ReadMe of how to use it
- A working example
###### Rich Presence Wrappers and Implementations
| Name | Language |
| ------------------------------------------------------------------------- | --------------------------------- |
| [Discord RPC C#](https://github.com/Lachee/discord-rpc-csharp) | C# |
| [Discord RPC D](https://github.com/voidblaster/discord-rpc-d) | [D](https://dlang.org/) |
| [discord-rpc.jar](https://github.com/Vatuu/discord-rpc 'Discord-RPC.jar') | Java |
| [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java |
| [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java |
| [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript |
| [drpc4k](https://github.com/Bluexin/drpc4k) | [Kotlin](https://kotlinlang.org/) |
| [lua-discordRPC](https://github.com/pfirsich/lua-discordRPC) | LuaJIT (FFI) |
| [pypresence](https://github.com/qwertyquerty/pypresence) | [Python](https://python.org/) |
| [SwordRPC](https://github.com/Azoy/SwordRPC) | [Swift](https://swift.org) |

63
3rdparty/discord-rpc/discord_backoff.h vendored Normal file
View File

@@ -0,0 +1,63 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_BACKOFF_H
#define DISCORD_BACKOFF_H
#include <algorithm>
#include <random>
#include <cstdint>
#include <ctime>
namespace discord_rpc {
struct Backoff {
int64_t minAmount;
int64_t maxAmount;
int64_t current;
int fails;
std::mt19937_64 randGenerator;
std::uniform_real_distribution<> randDistribution;
double rand01() { return randDistribution(randGenerator); }
Backoff(int64_t min, int64_t max)
: minAmount(min), maxAmount(max), current(min), fails(0), randGenerator(static_cast<uint64_t>(time(0))) {
}
void reset() {
fails = 0;
current = minAmount;
}
int64_t nextDelay() {
++fails;
int64_t delay = static_cast<int64_t>(static_cast<double>(current) * 2.0 * rand01());
current = std::min(current + delay, maxAmount);
return current;
}
};
} // namespace discord_rpc
#endif // DISCORD_BACKOFF_H

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_CONNECTION_H
#define DISCORD_CONNECTION_H
// This is to wrap the platform specific kinds of connect/read/write.
#include <cstdlib>
namespace discord_rpc {
// not really connectiony, but need per-platform
int GetProcessId();
struct BaseConnection {
static BaseConnection *Create();
static void Destroy(BaseConnection*&);
bool isOpen = false;
bool Open();
bool Close();
bool Write(const void *data, size_t length);
bool Read(void *data, size_t length);
};
} // namespace discord_rpc
#endif // DISCORD_CONNECTION_H

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include "discord_connection.h"
#include <cerrno>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
namespace discord_rpc {
int GetProcessId() {
return ::getpid();
}
struct BaseConnectionUnix : public BaseConnection {
int sock{ -1 };
};
static BaseConnectionUnix Connection;
static sockaddr_un PipeAddr{};
#ifdef MSG_NOSIGNAL
static int MsgFlags = MSG_NOSIGNAL;
#else
static int MsgFlags = 0;
#endif
static const char *GetTempPath() {
const char *temp = getenv("XDG_RUNTIME_DIR");
temp = temp ? temp : getenv("TMPDIR");
temp = temp ? temp : getenv("TMP");
temp = temp ? temp : getenv("TEMP");
temp = temp ? temp : "/tmp";
return temp;
}
BaseConnection *BaseConnection::Create() {
PipeAddr.sun_family = AF_UNIX;
return &Connection;
}
void BaseConnection::Destroy(BaseConnection *&c) {
auto self = reinterpret_cast<BaseConnectionUnix*>(c);
self->Close();
c = nullptr;
}
bool BaseConnection::Open() {
const char *tempPath = GetTempPath();
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
self->sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (self->sock == -1) {
return false;
}
fcntl(self->sock, F_SETFL, O_NONBLOCK);
#ifdef SO_NOSIGPIPE
int optval = 1;
setsockopt(self->sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
#endif
for (int pipeNum = 0; pipeNum < 10; ++pipeNum) {
snprintf(PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s/discord-ipc-%d", tempPath, pipeNum);
int err = connect(self->sock, reinterpret_cast<const sockaddr*>(&PipeAddr), sizeof(PipeAddr));
if (err == 0) {
self->isOpen = true;
return true;
}
}
self->Close();
return false;
}
bool BaseConnection::Close() {
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) {
return false;
}
close(self->sock);
self->sock = -1;
self->isOpen = false;
return true;
}
bool BaseConnection::Write(const void *data, size_t length) {
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) {
return false;
}
ssize_t sentBytes = send(self->sock, data, length, MsgFlags);
if (sentBytes < 0) {
Close();
}
return sentBytes == static_cast<ssize_t>(length);
}
bool BaseConnection::Read(void *data, size_t length) {
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) {
return false;
}
long res = recv(self->sock, data, length, MsgFlags);
if (res < 0) {
if (errno == EAGAIN) {
return false;
}
Close();
}
else if (res == 0) {
Close();
}
return static_cast<size_t>(res) == length;
}
} // namespace discord_rpc

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include "discord_connection.h"
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOSERVICE
#define NOIME
#include <cassert>
#include <windows.h>
namespace discord_rpc {
int GetProcessId() {
return static_cast<int>(::GetCurrentProcessId());
}
struct BaseConnectionWin : public BaseConnection {
HANDLE pipe{ INVALID_HANDLE_VALUE };
};
static BaseConnectionWin Connection;
BaseConnection *BaseConnection::Create() {
return &Connection;
}
void BaseConnection::Destroy(BaseConnection *&c) {
auto self = reinterpret_cast<BaseConnectionWin*>(c);
self->Close();
c = nullptr;
}
bool BaseConnection::Open() {
wchar_t pipeName[]{ L"\\\\?\\pipe\\discord-ipc-0" };
const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2;
pipeName[pipeDigit] = L'0';
auto self = reinterpret_cast<BaseConnectionWin*>(this);
for (;;) {
self->pipe = ::CreateFileW(pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (self->pipe != INVALID_HANDLE_VALUE) {
self->isOpen = true;
return true;
}
auto lastError = GetLastError();
if (lastError == ERROR_FILE_NOT_FOUND) {
if (pipeName[pipeDigit] < L'9') {
pipeName[pipeDigit]++;
continue;
}
}
else if (lastError == ERROR_PIPE_BUSY) {
if (!WaitNamedPipeW(pipeName, 10000)) {
return false;
}
continue;
}
return false;
}
}
bool BaseConnection::Close() {
auto self = reinterpret_cast<BaseConnectionWin*>(this);
::CloseHandle(self->pipe);
self->pipe = INVALID_HANDLE_VALUE;
self->isOpen = false;
return true;
}
bool BaseConnection::Write(const void *data, size_t length) {
if (length == 0) {
return true;
}
auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self);
if (!self) {
return false;
}
if (self->pipe == INVALID_HANDLE_VALUE) {
return false;
}
assert(data);
if (!data) {
return false;
}
const DWORD bytesLength = static_cast<DWORD>(length);
DWORD bytesWritten = 0;
return ::WriteFile(self->pipe, data, bytesLength, &bytesWritten, nullptr) == TRUE && bytesWritten == bytesLength;
}
bool BaseConnection::Read(void *data, size_t length) {
assert(data);
if (!data) {
return false;
}
auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self);
if (!self) {
return false;
}
if (self->pipe == INVALID_HANDLE_VALUE) {
return false;
}
DWORD bytesAvailable = 0;
if (::PeekNamedPipe(self->pipe, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
if (bytesAvailable >= length) {
DWORD bytesToRead = static_cast<DWORD>(length);
DWORD bytesRead = 0;
if (::ReadFile(self->pipe, data, bytesToRead, &bytesRead, nullptr) == TRUE) {
assert(bytesToRead == bytesRead);
return true;
}
else {
Close();
}
}
}
else {
Close();
}
return false;
}
} // namespace discord_rpc

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_MSG_QUEUE_H
#define DISCORD_MSG_QUEUE_H
#include <atomic>
// A simple queue. No locks, but only works with a single thread as producer and a single thread as
// a consumer. Mutex up as needed.
namespace discord_rpc {
template<typename ElementType, std::size_t QueueSize>
class MsgQueue {
ElementType queue_[QueueSize];
std::atomic_uint nextAdd_{ 0 };
std::atomic_uint nextSend_{ 0 };
std::atomic_uint pendingSends_{ 0 };
public:
MsgQueue() {}
ElementType *GetNextAddMessage() {
// if we are falling behind, bail
if (pendingSends_.load() >= QueueSize) {
return nullptr;
}
auto index = (nextAdd_++) % QueueSize;
return &queue_[index];
}
void CommitAdd() { ++pendingSends_; }
bool HavePendingSends() const { return pendingSends_.load() != 0; }
ElementType *GetNextSendMessage() {
auto index = (nextSend_++) % QueueSize;
return &queue_[index];
}
void CommitSend() { --pendingSends_; }
};
} // namespace discord_rpc
#endif // DISCORD_MSG_QUEUE_H

37
3rdparty/discord-rpc/discord_register.h vendored Normal file
View File

@@ -0,0 +1,37 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_REGISTER_H
#define DISCORD_REGISTER_H
#ifdef __cplusplus
extern "C" {
#endif
void Discord_Register(const char *applicationId, const char *command);
#ifdef __cplusplus
}
#endif
#endif // DISCORD_REGISTER_H

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include "discord_rpc.h"
#include "discord_register.h"
#include <cstdio>
#include <errno.h>
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
namespace {
static bool Mkdir(const char *path) {
int result = mkdir(path, 0755);
if (result == 0) {
return true;
}
if (errno == EEXIST) {
return true;
}
return false;
}
} // namespace
// We want to register games so we can run them from Discord client as discord-<appid>://
extern "C" void Discord_Register(const char *applicationId, const char *command) {
// Add a desktop file and update some mime handlers so that xdg-open does the right thing.
const char *home = getenv("HOME");
if (!home) {
return;
}
char exePath[1024]{};
if (!command || !command[0]) {
const ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath));
if (size <= 0 || size >= static_cast<ssize_t>(sizeof(exePath))) {
return;
}
exePath[size] = '\0';
command = exePath;
}
constexpr char desktopFileFormat[] = "[Desktop Entry]\n"
"Name=Game %s\n"
"Exec=%s %%u\n" // note: it really wants that %u in there
"Type=Application\n"
"NoDisplay=true\n"
"Categories=Discord;Games;\n"
"MimeType=x-scheme-handler/discord-%s;\n";
char desktopFile[2048]{};
int fileLen = snprintf(desktopFile, sizeof(desktopFile), desktopFileFormat, applicationId, command, applicationId);
if (fileLen <= 0) {
return;
}
char desktopFilename[256]{};
(void)snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId);
char desktopFilePath[1024]{};
(void)snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home);
if (!Mkdir(desktopFilePath)) {
return;
}
strcat(desktopFilePath, "/share");
if (!Mkdir(desktopFilePath)) {
return;
}
strcat(desktopFilePath, "/applications");
if (!Mkdir(desktopFilePath)) {
return;
}
strcat(desktopFilePath, desktopFilename);
FILE *fp = fopen(desktopFilePath, "w");
if (fp) {
fwrite(desktopFile, 1, fileLen, fp);
fclose(fp);
}
else {
return;
}
char xdgMimeCommand[1024]{};
snprintf(xdgMimeCommand,
sizeof(xdgMimeCommand),
"xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s",
applicationId,
applicationId);
if (system(xdgMimeCommand) < 0) {
fprintf(stderr, "Failed to register mime handler\n");
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include <stdio.h>
#include <sys/stat.h>
#import <AppKit/AppKit.h>
#include "discord_register.h"
static void RegisterCommand(const char *applicationId, const char *command) {
// There does not appear to be a way to register arbitrary commands on OSX, so instead we'll save the command
// to a file in the Discord config path, and when it is needed, Discord can try to load the file there, open
// the command therein (will pass to js's window.open, so requires a url-like thing)
// Note: will not work for sandboxed apps
NSString *home = NSHomeDirectory();
if (!home) {
return;
}
NSString *path = [[[[[[home stringByAppendingPathComponent:@"Library"]
stringByAppendingPathComponent:@"Application Support"]
stringByAppendingPathComponent:@"discord"]
stringByAppendingPathComponent:@"games"]
stringByAppendingPathComponent:[NSString stringWithUTF8String:applicationId]]
stringByAppendingPathExtension:@"json"];
[[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
NSString *jsonBuffer = [NSString stringWithFormat:@"{\"command\": \"%s\"}", command];
[jsonBuffer writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:nil];
}
static void RegisterURL(const char *applicationId) {
char url[256];
snprintf(url, sizeof(url), "discord-%s", applicationId);
CFStringRef cfURL = CFStringCreateWithCString(NULL, url, kCFStringEncodingUTF8);
NSString* myBundleId = [[NSBundle mainBundle] bundleIdentifier];
if (!myBundleId) {
fprintf(stderr, "No bundle id found\n");
return;
}
NSURL* myURL = [[NSBundle mainBundle] bundleURL];
if (!myURL) {
fprintf(stderr, "No bundle url found\n");
return;
}
OSStatus status = LSSetDefaultHandlerForURLScheme(cfURL, (__bridge CFStringRef)myBundleId);
if (status != noErr) {
fprintf(stderr, "Error in LSSetDefaultHandlerForURLScheme: %d\n", (int)status);
return;
}
status = LSRegisterURL((__bridge CFURLRef)myURL, true);
if (status != noErr) {
fprintf(stderr, "Error in LSRegisterURL: %d\n", (int)status);
}
}
void Discord_Register(const char *applicationId, const char *command) {
if (command) {
RegisterCommand(applicationId, command);
}
else {
// raii lite
@autoreleasepool {
RegisterURL(applicationId);
}
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include "discord_rpc.h"
#include "discord_register.h"
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOSERVICE
#define NOIME
#include <windows.h>
#include <psapi.h>
#include <cstdio>
/**
* Updated fixes for MinGW and WinXP
* This block is written the way it does not involve changing the rest of the code
* Checked to be compiling
* 1) strsafe.h belongs to Windows SDK and cannot be added to MinGW
* #include guarded, functions redirected to <string.h> substitutes
* 2) RegSetKeyValueW and LSTATUS are not declared in <winreg.h>
* The entire function is rewritten
*/
#ifdef __MINGW32__
# include <wchar.h>
/// strsafe.h fixes
static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...) {
HRESULT ret;
va_list va;
va_start(va, pszFormat);
cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault
// othervise
ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned
va_end(va);
return ret;
}
#else
# include <cwchar>
# include <strsafe.h>
#endif // __MINGW32__
/// winreg.h fixes
#ifndef LSTATUS
# define LSTATUS LONG
#endif
#ifdef RegSetKeyValueW
# undefine RegSetKeyValueW
#endif
#define RegSetKeyValueW regset
static LSTATUS regset(HKEY hkey, LPCWSTR subkey, LPCWSTR name, DWORD type, const void *data, DWORD len) {
HKEY htkey = hkey, hsubkey = nullptr;
LSTATUS ret;
if (subkey && subkey[0]) {
if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) !=
ERROR_SUCCESS)
return ret;
htkey = hsubkey;
}
ret = RegSetValueExW(htkey, name, 0, type, static_cast<const BYTE*>(data), len);
if (hsubkey && hsubkey != hkey)
RegCloseKey(hsubkey);
return ret;
}
static void Discord_RegisterW(const wchar_t *applicationId, const wchar_t *command) {
// https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
// we want to register games so we can run them as discord-<appid>://
// Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions.
wchar_t exeFilePath[MAX_PATH]{};
DWORD exeLen = GetModuleFileNameW(nullptr, exeFilePath, MAX_PATH);
wchar_t openCommand[1024]{};
if (command && command[0]) {
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
}
else {
// StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath);
}
wchar_t protocolName[64]{};
StringCbPrintfW(protocolName, sizeof(protocolName), L"discord-%s", applicationId);
wchar_t protocolDescription[128]{};
StringCbPrintfW(protocolDescription, sizeof(protocolDescription), L"URL:Run game %s protocol", applicationId);
wchar_t urlProtocol = 0;
wchar_t keyName[256]{};
StringCbPrintfW(keyName, sizeof(keyName), L"Software\\Classes\\%s", protocolName);
HKEY key;
auto status = RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr);
if (status != ERROR_SUCCESS) {
fprintf(stderr, "Error creating key\n");
return;
}
DWORD len;
LSTATUS result;
len = static_cast<DWORD>(lstrlenW(protocolDescription) + 1);
result = RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing description\n");
}
len = static_cast<DWORD>(lstrlenW(protocolDescription) + 1);
result = RegSetKeyValueW(key, nullptr, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing description\n");
}
result = RegSetKeyValueW(key, L"DefaultIcon", nullptr, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing icon\n");
}
len = static_cast<DWORD>(lstrlenW(openCommand) + 1);
result = RegSetKeyValueW(key, L"shell\\open\\command", nullptr, REG_SZ, openCommand, len * sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing command\n");
}
RegCloseKey(key);
}
extern "C" void Discord_Register(const char *applicationId, const char *command) {
wchar_t appId[32]{};
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
wchar_t openCommand[1024]{};
const wchar_t *wcommand = nullptr;
if (command && command[0]) {
const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand);
MultiByteToWideChar(CP_UTF8, 0, command, -1, openCommand, commandBufferLen);
wcommand = openCommand;
}
Discord_RegisterW(appId, wcommand);
}

510
3rdparty/discord-rpc/discord_rpc.cpp vendored Normal file
View File

@@ -0,0 +1,510 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include <atomic>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <thread>
#include "discord_rpc.h"
#include "discord_backoff.h"
#include "discord_register.h"
#include "discord_msg_queue.h"
#include "discord_rpc_connection.h"
#include "discord_serialization.h"
using namespace discord_rpc;
static void Discord_UpdateConnection();
namespace {
constexpr size_t MaxMessageSize{ 16 * 1024 };
constexpr size_t MessageQueueSize{ 8 };
constexpr size_t JoinQueueSize{ 8 };
struct QueuedMessage {
size_t length;
char buffer[MaxMessageSize];
void Copy(const QueuedMessage &other) {
length = other.length;
if (length) {
memcpy(buffer, other.buffer, length);
}
}
};
struct User {
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
// terminator = 21
char userId[32];
// 32 unicode glyphs is max name size => 4 bytes per glyph in the worst case, +1 for null
// terminator = 129
char username[344];
// 4 decimal digits + 1 null terminator = 5
char discriminator[8];
// optional 'a_' + md5 hex digest (32 bytes) + null terminator = 35
char avatar[128];
// Rounded way up because I'm paranoid about games breaking from future changes in these sizes
};
static RpcConnection *Connection{ nullptr };
static DiscordEventHandlers QueuedHandlers{};
static DiscordEventHandlers Handlers{};
static std::atomic_bool WasJustConnected{ false };
static std::atomic_bool WasJustDisconnected{ false };
static std::atomic_bool GotErrorMessage{ false };
static std::atomic_bool WasJoinGame{ false };
static std::atomic_bool WasSpectateGame{ false };
static std::atomic_bool UpdatePresence{ false };
static char JoinGameSecret[256];
static char SpectateGameSecret[256];
static int LastErrorCode{ 0 };
static char LastErrorMessage[256];
static int LastDisconnectErrorCode{ 0 };
static char LastDisconnectErrorMessage[256];
static std::mutex PresenceMutex;
static std::mutex HandlerMutex;
static QueuedMessage QueuedPresence{};
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
static MsgQueue<User, JoinQueueSize> JoinAskQueue;
static User connectedUser;
// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential backoff from 0.5 seconds to 1 minute
static Backoff ReconnectTimeMs(500, 60 * 1000);
static auto NextConnect = std::chrono::system_clock::now();
static int Pid{ 0 };
static int Nonce{ 1 };
class IoThreadHolder {
private:
std::atomic_bool keepRunning{ true };
std::mutex waitForIOMutex;
std::condition_variable waitForIOActivity;
std::thread ioThread;
public:
void Start() {
keepRunning.store(true);
ioThread = std::thread([&]() {
const std::chrono::duration<int64_t, std::milli> maxWait { 500LL };
Discord_UpdateConnection();
while (keepRunning.load()) {
std::unique_lock<std::mutex> lock(waitForIOMutex);
waitForIOActivity.wait_for(lock, maxWait);
Discord_UpdateConnection();
}
});
}
void Notify() { waitForIOActivity.notify_all(); }
void Stop() {
keepRunning.exchange(false);
Notify();
if (ioThread.joinable()) {
ioThread.join();
}
}
~IoThreadHolder() { Stop(); }
};
static IoThreadHolder *IoThread{ nullptr };
static void UpdateReconnectTime() {
NextConnect = std::chrono::system_clock::now() + std::chrono::duration<int64_t, std::milli> { ReconnectTimeMs.nextDelay() };
}
static void SignalIOActivity() {
if (IoThread != nullptr) {
IoThread->Notify();
}
}
static bool RegisterForEvent(const char *evtName) {
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
qmessage->length = JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
SendQueue.CommitAdd();
SignalIOActivity();
return true;
}
return false;
}
static bool DeregisterForEvent(const char *evtName) {
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
qmessage->length = JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
SendQueue.CommitAdd();
SignalIOActivity();
return true;
}
return false;
}
} // namespace
static void Discord_UpdateConnection() {
if (!Connection) {
return;
}
if (!Connection->IsOpen()) {
if (std::chrono::system_clock::now() >= NextConnect) {
UpdateReconnectTime();
Connection->Open();
}
}
else {
// reads
for (;;) {
JsonDocument message;
if (!Connection->Read(message)) {
break;
}
const char *evtName = GetStrMember(&message, "evt");
const char *nonce = GetStrMember(&message, "nonce");
if (nonce) {
// in responses only -- should use to match up response when needed.
if (evtName && strcmp(evtName, "ERROR") == 0) {
auto data = GetObjMember(&message, "data");
LastErrorCode = GetIntMember(data, "code");
StringCopy(LastErrorMessage, GetStrMember(data, "message", ""));
GotErrorMessage.store(true);
}
}
else {
// should have evt == name of event, optional data
if (evtName == nullptr) {
continue;
}
auto data = GetObjMember(&message, "data");
if (strcmp(evtName, "ACTIVITY_JOIN") == 0) {
auto secret = GetStrMember(data, "secret");
if (secret) {
StringCopy(JoinGameSecret, secret);
WasJoinGame.store(true);
}
}
else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0) {
auto secret = GetStrMember(data, "secret");
if (secret) {
StringCopy(SpectateGameSecret, secret);
WasSpectateGame.store(true);
}
}
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0) {
auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id");
auto username = GetStrMember(user, "username");
auto avatar = GetStrMember(user, "avatar");
auto joinReq = JoinAskQueue.GetNextAddMessage();
if (userId && username && joinReq) {
StringCopy(joinReq->userId, userId);
StringCopy(joinReq->username, username);
auto discriminator = GetStrMember(user, "discriminator");
if (discriminator) {
StringCopy(joinReq->discriminator, discriminator);
}
if (avatar) {
StringCopy(joinReq->avatar, avatar);
}
else {
joinReq->avatar[0] = 0;
}
JoinAskQueue.CommitAdd();
}
}
}
}
// writes
if (UpdatePresence.exchange(false) && QueuedPresence.length) {
QueuedMessage local;
{
std::lock_guard<std::mutex> guard(PresenceMutex);
local.Copy(QueuedPresence);
}
if (!Connection->Write(local.buffer, local.length)) {
// if we fail to send, requeue
std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.Copy(local);
UpdatePresence.exchange(true);
}
}
while (SendQueue.HavePendingSends()) {
auto qmessage = SendQueue.GetNextSendMessage();
Connection->Write(qmessage->buffer, qmessage->length);
SendQueue.CommitSend();
}
}
}
extern "C" void Discord_Initialize(const char *applicationId, DiscordEventHandlers *handlers, const int autoRegister) {
IoThread = new (std::nothrow) IoThreadHolder();
if (IoThread == nullptr) {
return;
}
if (autoRegister) {
Discord_Register(applicationId, nullptr);
}
Pid = GetProcessId();
{
std::lock_guard<std::mutex> guard(HandlerMutex);
if (handlers) {
QueuedHandlers = *handlers;
}
else {
QueuedHandlers = {};
}
Handlers = {};
}
if (Connection) {
return;
}
Connection = RpcConnection::Create(applicationId);
Connection->onConnect = [](JsonDocument &readyMessage) {
Discord_UpdateHandlers(&QueuedHandlers);
if (QueuedPresence.length > 0) {
UpdatePresence.exchange(true);
SignalIOActivity();
}
auto data = GetObjMember(&readyMessage, "data");
auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id");
auto username = GetStrMember(user, "username");
auto avatar = GetStrMember(user, "avatar");
if (userId && username) {
StringCopy(connectedUser.userId, userId);
StringCopy(connectedUser.username, username);
auto discriminator = GetStrMember(user, "discriminator");
if (discriminator) {
StringCopy(connectedUser.discriminator, discriminator);
}
if (avatar) {
StringCopy(connectedUser.avatar, avatar);
}
else {
connectedUser.avatar[0] = 0;
}
}
WasJustConnected.exchange(true);
ReconnectTimeMs.reset();
};
Connection->onDisconnect = [](int err, const char *message) {
LastDisconnectErrorCode = err;
StringCopy(LastDisconnectErrorMessage, message);
WasJustDisconnected.exchange(true);
UpdateReconnectTime();
};
IoThread->Start();
}
extern "C" void Discord_Shutdown() {
if (!Connection) {
return;
}
Connection->onConnect = nullptr;
Connection->onDisconnect = nullptr;
Handlers = {};
QueuedPresence.length = 0;
UpdatePresence.exchange(false);
if (IoThread != nullptr) {
IoThread->Stop();
delete IoThread;
IoThread = nullptr;
}
RpcConnection::Destroy(Connection);
}
extern "C" void Discord_UpdatePresence(const DiscordRichPresence *presence) {
{
std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.length = JsonWriteRichPresenceObj(QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence);
UpdatePresence.exchange(true);
}
SignalIOActivity();
}
extern "C" void Discord_ClearPresence(void) {
Discord_UpdatePresence(nullptr);
}
extern "C" void Discord_Respond(const char *userId, /* DISCORD_REPLY_ */ int reply) {
// if we are not connected, let's not batch up stale messages for later
if (!Connection || !Connection->IsOpen()) {
return;
}
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
qmessage->length = JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++);
SendQueue.CommitAdd();
SignalIOActivity();
}
}
extern "C" void Discord_RunCallbacks() {
// Note on some weirdness: internally we might connect, get other signals, disconnect any number
// of times inbetween calls here. Externally, we want the sequence to seem sane, so any other
// signals are book-ended by calls to ready and disconnect.
if (!Connection) {
return;
}
const bool wasDisconnected = WasJustDisconnected.exchange(false);
const bool isConnected = Connection->IsOpen();
if (isConnected) {
// if we are connected, disconnect cb first
std::lock_guard<std::mutex> guard(HandlerMutex);
if (wasDisconnected && Handlers.disconnected) {
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
}
}
if (WasJustConnected.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.ready) {
DiscordUser du{ connectedUser.userId, connectedUser.username, connectedUser.discriminator, connectedUser.avatar };
Handlers.ready(&du);
}
}
if (GotErrorMessage.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.errored) {
Handlers.errored(LastErrorCode, LastErrorMessage);
}
}
if (WasJoinGame.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.joinGame) {
Handlers.joinGame(JoinGameSecret);
}
}
if (WasSpectateGame.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.spectateGame) {
Handlers.spectateGame(SpectateGameSecret);
}
}
// Right now this batches up any requests and sends them all in a burst; I could imagine a world
// where the implementer would rather sequentially accept/reject each one before the next invite
// is sent. I left it this way because I could also imagine wanting to process these all and
// maybe show them in one common dialog and/or start fetching the avatars in parallel, and if
// not it should be trivial for the implementer to make a queue themselves.
while (JoinAskQueue.HavePendingSends()) {
const auto req = JoinAskQueue.GetNextSendMessage();
{
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.joinRequest) {
DiscordUser du{ req->userId, req->username, req->discriminator, req->avatar };
Handlers.joinRequest(&du);
}
}
JoinAskQueue.CommitSend();
}
if (!isConnected) {
// if we are not connected, disconnect message last
std::lock_guard<std::mutex> guard(HandlerMutex);
if (wasDisconnected && Handlers.disconnected) {
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
}
}
}
extern "C" void Discord_UpdateHandlers(DiscordEventHandlers *newHandlers) {
if (newHandlers) {
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
if (!Handlers.handler_name && newHandlers->handler_name) { \
RegisterForEvent(event); \
} \
else if (Handlers.handler_name && !newHandlers->handler_name) { \
DeregisterForEvent(event); \
}
std::lock_guard<std::mutex> guard(HandlerMutex);
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")
HANDLE_EVENT_REGISTRATION(spectateGame, "ACTIVITY_SPECTATE")
HANDLE_EVENT_REGISTRATION(joinRequest, "ACTIVITY_JOIN_REQUEST")
#undef HANDLE_EVENT_REGISTRATION
Handlers = *newHandlers;
}
else {
std::lock_guard<std::mutex> guard(HandlerMutex);
Handlers = {};
}
}

94
3rdparty/discord-rpc/discord_rpc.h vendored Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_RPC_H
#define DISCORD_RPC_H
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DiscordRichPresence {
int type;
int status_display_type;
const char *name; /* max 128 bytes */
const char *state; /* max 128 bytes */
const char *details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char *largeImageKey; /* max 32 bytes */
const char *largeImageText; /* max 128 bytes */
const char *smallImageKey; /* max 32 bytes */
const char *smallImageText; /* max 128 bytes */
const char *partyId; /* max 128 bytes */
int partySize;
int partyMax;
int partyPrivacy;
const char *matchSecret; /* max 128 bytes */
const char *joinSecret; /* max 128 bytes */
const char *spectateSecret; /* max 128 bytes */
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordUser {
const char *userId;
const char *username;
const char *discriminator;
const char *avatar;
} DiscordUser;
typedef struct DiscordEventHandlers {
void (*ready)(const DiscordUser *request);
void (*disconnected)(int errorCode, const char *message);
void (*errored)(int errorCode, const char *message);
void (*joinGame)(const char *joinSecret);
void (*spectateGame)(const char *spectateSecret);
void (*joinRequest)(const DiscordUser *request);
} DiscordEventHandlers;
#define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2
#define DISCORD_PARTY_PRIVATE 0
#define DISCORD_PARTY_PUBLIC 1
void Discord_Initialize(const char *applicationId, DiscordEventHandlers *handlers, const int autoRegister);
void Discord_Shutdown(void);
// checks for incoming messages, dispatches callbacks
void Discord_RunCallbacks(void);
void Discord_UpdatePresence(const DiscordRichPresence *presence);
void Discord_ClearPresence(void);
void Discord_Respond(const char *userid, /* DISCORD_REPLY_ */ int reply);
void Discord_UpdateHandlers(DiscordEventHandlers *handlers);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // DISCORD_RPC_H

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include "discord_rpc_connection.h"
#include "discord_serialization.h"
namespace discord_rpc {
static const int RpcVersion = 1;
static RpcConnection Instance;
RpcConnection *RpcConnection::Create(const char *applicationId) {
Instance.connection = BaseConnection::Create();
StringCopy(Instance.appId, applicationId);
return &Instance;
}
void RpcConnection::Destroy(RpcConnection *&c) {
c->Close();
BaseConnection::Destroy(c->connection);
c = nullptr;
}
void RpcConnection::Open() {
if (state == State::Connected) {
return;
}
if (state == State::Disconnected && !connection->Open()) {
return;
}
if (state == State::SentHandshake) {
JsonDocument message;
if (Read(message)) {
auto cmd = GetStrMember(&message, "cmd");
auto evt = GetStrMember(&message, "evt");
if (cmd && evt && !strcmp(cmd, "DISPATCH") && !strcmp(evt, "READY")) {
state = State::Connected;
if (onConnect) {
onConnect(message);
}
}
}
}
else {
sendFrame.opcode = Opcode::Handshake;
sendFrame.length = static_cast<uint32_t>(JsonWriteHandshakeObj(sendFrame.message, sizeof(sendFrame.message), RpcVersion, appId));
if (connection->Write(&sendFrame, sizeof(MessageFrameHeader) + sendFrame.length)) {
state = State::SentHandshake;
}
else {
Close();
}
}
}
void RpcConnection::Close() {
if (onDisconnect && (state == State::Connected || state == State::SentHandshake)) {
onDisconnect(lastErrorCode, lastErrorMessage);
}
connection->Close();
state = State::Disconnected;
}
bool RpcConnection::Write(const void *data, size_t length) {
sendFrame.opcode = Opcode::Frame;
memcpy(sendFrame.message, data, length);
sendFrame.length = static_cast<uint32_t>(length);
if (!connection->Write(&sendFrame, sizeof(MessageFrameHeader) + length)) {
Close();
return false;
}
return true;
}
bool RpcConnection::Read(JsonDocument &message) {
if (state != State::Connected && state != State::SentHandshake) {
return false;
}
MessageFrame readFrame{};
for (;;) {
bool didRead = connection->Read(&readFrame, sizeof(MessageFrameHeader));
if (!didRead) {
if (!connection->isOpen) {
lastErrorCode = static_cast<int>(ErrorCode::PipeClosed);
StringCopy(lastErrorMessage, "Pipe closed");
Close();
}
return false;
}
if (readFrame.length > 0) {
didRead = connection->Read(readFrame.message, readFrame.length);
if (!didRead) {
lastErrorCode = static_cast<int>(ErrorCode::ReadCorrupt);
StringCopy(lastErrorMessage, "Partial data in frame");
Close();
return false;
}
readFrame.message[readFrame.length] = 0;
}
switch (readFrame.opcode) {
case Opcode::Close: {
message.ParseInsitu(readFrame.message);
lastErrorCode = GetIntMember(&message, "code");
StringCopy(lastErrorMessage, GetStrMember(&message, "message", ""));
Close();
return false;
}
case Opcode::Frame:
message.ParseInsitu(readFrame.message);
return true;
case Opcode::Ping:
readFrame.opcode = Opcode::Pong;
if (!connection->Write(&readFrame, sizeof(MessageFrameHeader) + readFrame.length)) {
Close();
}
break;
case Opcode::Pong:
break;
case Opcode::Handshake:
default:
// something bad happened
lastErrorCode = static_cast<int>(ErrorCode::ReadCorrupt);
StringCopy(lastErrorMessage, "Bad ipc frame");
Close();
return false;
}
}
}
} // namespace discord_rpc

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_RPC_CONNECTION_H
#define DISCORD_RPC_CONNECTION_H
#include "discord_connection.h"
#include "discord_serialization.h"
namespace discord_rpc {
// I took this from the buffer size libuv uses for named pipes; I suspect ours would usually be much smaller.
constexpr size_t MaxRpcFrameSize = 64 * 1024;
struct RpcConnection {
enum class ErrorCode : int {
Success = 0,
PipeClosed = 1,
ReadCorrupt = 2,
};
enum class Opcode : uint32_t {
Handshake = 0,
Frame = 1,
Close = 2,
Ping = 3,
Pong = 4,
};
struct MessageFrameHeader {
Opcode opcode;
uint32_t length;
};
struct MessageFrame : public MessageFrameHeader {
char message[MaxRpcFrameSize - sizeof(MessageFrameHeader)];
};
enum class State : uint32_t {
Disconnected,
SentHandshake,
AwaitingResponse,
Connected,
};
BaseConnection *connection{ nullptr };
State state{ State::Disconnected };
void (*onConnect)(JsonDocument &message) { nullptr };
void (*onDisconnect)(int errorCode, const char *message) { nullptr };
char appId[64]{};
int lastErrorCode{ 0 };
char lastErrorMessage[256]{};
RpcConnection::MessageFrame sendFrame;
static RpcConnection *Create(const char *applicationId);
static void Destroy(RpcConnection*&);
inline bool IsOpen() const { return state == State::Connected; }
void Open();
void Close();
bool Write(const void *data, size_t length);
bool Read(JsonDocument &message);
};
} // namespace discord_rpc
#endif // DISCORD_RPC_CONNECTION_H

View File

@@ -0,0 +1,285 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#include "discord_serialization.h"
#include "discord_connection.h"
#include "discord_rpc.h"
namespace discord_rpc {
template<typename T>
void NumberToString(char *dest, T number) {
if (!number) {
*dest++ = '0';
*dest++ = 0;
return;
}
if (number < 0) {
*dest++ = '-';
number = -number;
}
char temp[32];
int place = 0;
while (number) {
auto digit = number % 10;
number = number / 10;
temp[place++] = '0' + static_cast<char>(digit);
}
for (--place; place >= 0; --place) {
*dest++ = temp[place];
}
*dest = 0;
}
// it's ever so slightly faster to not have to strlen the key
template<typename T>
void WriteKey(JsonWriter &w, T &k) {
w.Key(k, sizeof(T) - 1);
}
struct WriteObject {
JsonWriter &writer;
WriteObject(JsonWriter &w)
: writer(w) {
writer.StartObject();
}
template<typename T>
WriteObject(JsonWriter &w, T &name)
: writer(w) {
WriteKey(writer, name);
writer.StartObject();
}
~WriteObject() { writer.EndObject(); }
};
struct WriteArray {
JsonWriter &writer;
template<typename T>
WriteArray(JsonWriter &w, T &name)
: writer(w) {
WriteKey(writer, name);
writer.StartArray();
}
~WriteArray() { writer.EndArray(); }
};
template<typename T>
void WriteOptionalString(JsonWriter &w, T &k, const char *value) {
if (value && value[0]) {
w.Key(k, sizeof(T) - 1);
w.String(value);
}
}
static void JsonWriteNonce(JsonWriter &writer, const int nonce) {
WriteKey(writer, "nonce");
char nonceBuffer[32];
NumberToString(nonceBuffer, nonce);
writer.String(nonceBuffer);
}
size_t JsonWriteRichPresenceObj(char *dest, const size_t maxLen, const int nonce, const int pid, const DiscordRichPresence *presence) {
JsonWriter writer(dest, maxLen);
{
WriteObject top(writer);
JsonWriteNonce(writer, nonce);
WriteKey(writer, "cmd");
writer.String("SET_ACTIVITY");
{
WriteObject args(writer, "args");
WriteKey(writer, "pid");
writer.Int(pid);
if (presence != nullptr) {
WriteObject activity(writer, "activity");
if (presence->type >= 0 && presence->type <= 5) {
WriteKey(writer, "type");
writer.Int(presence->type);
WriteKey(writer, "status_display_type");
writer.Int(presence->status_display_type);
}
WriteOptionalString(writer, "name", presence->name);
WriteOptionalString(writer, "state", presence->state);
WriteOptionalString(writer, "details", presence->details);
if (presence->startTimestamp || presence->endTimestamp) {
WriteObject timestamps(writer, "timestamps");
if (presence->startTimestamp) {
WriteKey(writer, "start");
writer.Int64(presence->startTimestamp);
}
if (presence->endTimestamp) {
WriteKey(writer, "end");
writer.Int64(presence->endTimestamp);
}
}
if ((presence->largeImageKey && presence->largeImageKey[0]) ||
(presence->largeImageText && presence->largeImageText[0]) ||
(presence->smallImageKey && presence->smallImageKey[0]) ||
(presence->smallImageText && presence->smallImageText[0])) {
WriteObject assets(writer, "assets");
WriteOptionalString(writer, "large_image", presence->largeImageKey);
WriteOptionalString(writer, "large_text", presence->largeImageText);
WriteOptionalString(writer, "small_image", presence->smallImageKey);
WriteOptionalString(writer, "small_text", presence->smallImageText);
}
if ((presence->partyId && presence->partyId[0]) || presence->partySize ||
presence->partyMax || presence->partyPrivacy) {
WriteObject party(writer, "party");
WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize && presence->partyMax) {
WriteArray size(writer, "size");
writer.Int(presence->partySize);
writer.Int(presence->partyMax);
}
if (presence->partyPrivacy) {
WriteKey(writer, "privacy");
writer.Int(presence->partyPrivacy);
}
}
if ((presence->matchSecret && presence->matchSecret[0]) ||
(presence->joinSecret && presence->joinSecret[0]) ||
(presence->spectateSecret && presence->spectateSecret[0])) {
WriteObject secrets(writer, "secrets");
WriteOptionalString(writer, "match", presence->matchSecret);
WriteOptionalString(writer, "join", presence->joinSecret);
WriteOptionalString(writer, "spectate", presence->spectateSecret);
}
writer.Key("instance");
writer.Bool(presence->instance != 0);
}
}
}
return writer.Size();
}
size_t JsonWriteHandshakeObj(char *dest, size_t maxLen, int version, const char *applicationId) {
JsonWriter writer(dest, maxLen);
{
WriteObject obj(writer);
WriteKey(writer, "v");
writer.Int(version);
WriteKey(writer, "client_id");
writer.String(applicationId);
}
return writer.Size();
}
size_t JsonWriteSubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName) {
JsonWriter writer(dest, maxLen);
{
WriteObject obj(writer);
JsonWriteNonce(writer, nonce);
WriteKey(writer, "cmd");
writer.String("SUBSCRIBE");
WriteKey(writer, "evt");
writer.String(evtName);
}
return writer.Size();
}
size_t JsonWriteUnsubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName) {
JsonWriter writer(dest, maxLen);
{
WriteObject obj(writer);
JsonWriteNonce(writer, nonce);
WriteKey(writer, "cmd");
writer.String("UNSUBSCRIBE");
WriteKey(writer, "evt");
writer.String(evtName);
}
return writer.Size();
}
size_t JsonWriteJoinReply(char *dest, size_t maxLen, const char *userId, const int reply, const int nonce) {
JsonWriter writer(dest, maxLen);
{
WriteObject obj(writer);
WriteKey(writer, "cmd");
if (reply == DISCORD_REPLY_YES) {
writer.String("SEND_ACTIVITY_JOIN_INVITE");
}
else {
writer.String("CLOSE_ACTIVITY_JOIN_REQUEST");
}
WriteKey(writer, "args");
{
WriteObject args(writer);
WriteKey(writer, "user_id");
writer.String(userId);
}
JsonWriteNonce(writer, nonce);
}
return writer.Size();
}
} // namespace discord_rpc

View File

@@ -0,0 +1,213 @@
/*
* Copyright 2017 Discord, Inc.
*
* 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.
*
*/
#ifndef DISCORD_SERIALIZATION_H
#define DISCORD_SERIALIZATION_H
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
struct DiscordRichPresence;
namespace discord_rpc {
// if only there was a standard library function for this
template<size_t Len>
inline size_t StringCopy(char (&dest)[Len], const char *src) {
if (!src || !Len) {
return 0;
}
size_t copied;
char *out = dest;
for (copied = 1; *src && copied < Len; ++copied) {
*out++ = *src++;
}
*out = 0;
return copied - 1;
}
size_t JsonWriteHandshakeObj(char *dest, size_t maxLen, int version, const char *applicationId);
// Commands
size_t JsonWriteRichPresenceObj(char *dest, const size_t maxLen, const int nonce, const int pid, const DiscordRichPresence *presence);
size_t JsonWriteSubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName);
size_t JsonWriteUnsubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName);
size_t JsonWriteJoinReply(char *dest, size_t maxLen, const char *userId, int reply, int nonce);
// I want to use as few allocations as I can get away with, and to do that with RapidJson, you need
// to supply some of your own allocators for stuff rather than use the defaults
class LinearAllocator {
public:
char *buffer_;
char *end_;
LinearAllocator() {
assert(0); // needed for some default case in rapidjson, should not use
}
LinearAllocator(char *buffer, size_t size)
: buffer_(buffer), end_(buffer + size) {
}
static const bool kNeedFree = false;
void *Malloc(size_t size) {
char *res = buffer_;
buffer_ += size;
if (buffer_ > end_) {
buffer_ = res;
return nullptr;
}
return res;
}
void *Realloc(void *originalPtr, size_t originalSize, size_t newSize) {
if (newSize == 0) {
return nullptr;
}
// allocate how much you need in the first place
assert(!originalPtr && !originalSize);
// unused parameter warning
(void)(originalPtr);
(void)(originalSize);
return Malloc(newSize);
}
static void Free(void *ptr) {
/* shrug */
(void)ptr;
}
};
template<size_t Size>
class FixedLinearAllocator : public LinearAllocator {
public:
char fixedBuffer_[Size];
FixedLinearAllocator()
: LinearAllocator(fixedBuffer_, Size) {
}
static const bool kNeedFree = false;
};
// wonder why this isn't a thing already, maybe I missed it
class DirectStringBuffer {
public:
using Ch = char;
char *buffer_;
char *end_;
char *current_;
DirectStringBuffer(char *buffer, size_t maxLen)
: buffer_(buffer), end_(buffer + maxLen), current_(buffer) {
}
void Put(char c) {
if (current_ < end_) {
*current_++ = c;
}
}
void Flush() {}
size_t GetSize() const { return static_cast<size_t>(current_ - buffer_); }
};
using MallocAllocator = rapidjson::CrtAllocator;
using PoolAllocator = rapidjson::MemoryPoolAllocator<MallocAllocator>;
using UTF8 = rapidjson::UTF8<char>;
// Writer appears to need about 16 bytes per nested object level (with 64bit size_t)
using StackAllocator = FixedLinearAllocator<2048>;
constexpr size_t WriterNestingLevels = 2048 / (2 * sizeof(size_t));
using JsonWriterBase =
rapidjson::Writer<DirectStringBuffer, UTF8, UTF8, StackAllocator, rapidjson::kWriteNoFlags>;
class JsonWriter : public JsonWriterBase {
public:
DirectStringBuffer stringBuffer_;
StackAllocator stackAlloc_;
JsonWriter(char *dest, size_t maxLen)
: JsonWriterBase(stringBuffer_, &stackAlloc_, WriterNestingLevels), stringBuffer_(dest, maxLen), stackAlloc_() {
}
size_t Size() const { return stringBuffer_.GetSize(); }
};
using JsonDocumentBase = rapidjson::GenericDocument<UTF8, PoolAllocator, StackAllocator>;
class JsonDocument : public JsonDocumentBase {
public:
static const int kDefaultChunkCapacity = 32 * 1024;
// json parser will use this buffer first, then allocate more if needed; I seriously doubt we
// send any messages that would use all of this, though.
char parseBuffer_[32 * 1024];
MallocAllocator mallocAllocator_;
PoolAllocator poolAllocator_;
StackAllocator stackAllocator_;
JsonDocument()
: JsonDocumentBase(rapidjson::kObjectType,
&poolAllocator_,
sizeof(stackAllocator_.fixedBuffer_),
&stackAllocator_),
poolAllocator_(parseBuffer_, sizeof(parseBuffer_), kDefaultChunkCapacity, &mallocAllocator_), stackAllocator_() {
}
};
using JsonValue = rapidjson::GenericValue<UTF8, PoolAllocator>;
inline JsonValue *GetObjMember(JsonValue *obj, const char *name) {
if (obj) {
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsObject()) {
return &member->value;
}
}
return nullptr;
}
inline int GetIntMember(JsonValue *obj, const char *name, int notFoundDefault = 0) {
if (obj) {
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsInt()) {
return member->value.GetInt();
}
}
return notFoundDefault;
}
inline const char *GetStrMember(JsonValue *obj, const char *name, const char *notFoundDefault = nullptr) {
if (obj) {
auto member = obj->FindMember(name);
if (member != obj->MemberEnd() && member->value.IsString()) {
return member->value.GetString();
}
}
return notFoundDefault;
}
} // namespace discord_rpc
#endif // DISCORD_SERIALIZATION_H

View File

@@ -1,2 +0,0 @@
add_library(getopt STATIC getopt.c)
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));
}

View File

@@ -1,95 +0,0 @@
#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__) */

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.7)
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,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

File diff suppressed because it is too large Load Diff

115
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,115 @@
# 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 the name of the class that is changed
followed by a colon then a short explanation of the commit.
Don't use a trailing period after the first line.
If this change affects more than one class, omit the class name and write a
more general message.
You only need to include a main description (body) for larger changes
where the one line is not enough to describe everything.
The main description starts after two newlines, it is normal prose and
should use normal punctuation and capital letters where appropriate.
It should explain exactly what's changed, why it's changed,
and what bugs were fixed.
An example of the expected format for git commit messages is as follows:
```
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
```
### 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.

524
Changelog
View File

@@ -2,6 +2,530 @@ Strawberry Music Player
=======================
ChangeLog
Version 1.2.16 (2025.12.16):
* Make Discord Rich presence use filename if song title is missing
* Added better error message when a GStreamer plugin is missing
* Preserve track order in album shuffle mode when restarting playback (#1623)
* Possible fixes for context word wrap
* Added lyrics from lrclib.net
* Added option to turn off the use of sort tags for the collection
* Fixed Spotify login
* Fixed error dialog shown minimized if another Strawberry window than the mainwindow was active
* Fixed seeking to the end of the track and back causing seeking to stop working (#1675)
* Set current index when automatically selecting track (#1825)
* Make icon size for shuffle and repeat buttons adjust to screen resolution (#1838)
* Fixed song being removed from playlist when dragging to another application (#1815)
* Don't automatically scroll on dynamic playlists (#1427)
Version 1.2.15 (2025.11.25):
* Fixed system default language not respected
* Fixed length filter search
* Fixed playlist parser converting Spotify URL's
* Removed use of deprecated QStyle::State_Editing
* Ignore connection closed errors for ListenBrainz
* (Windows) Support building with vcpkg unofficial::getopt-win32::getopt
Version 1.2.14 (2025.10.25):
Bugfixes:
* Fixed showing error dialog minimized when main window is not current active window (#1739)
* Fixed Discord timestamp update when seeking (#1813)
* Fixed CD metadata lookup to respect MusicBrainz rate limiting
* Fixed Tidal Open API cover provider
* (Windows) Fixed device selection with WASAPI2
Enhancements/Other:
* Removed libre.fm support
* Rewrote MusicBrainzClient to use Json instead of XML
* Subsonic will now use cover art from album when available
* Added option to remove "Remastered", etc from song titles for Tidal, Qobuz and Spotify
* Added webm to supported file extensions
* (Windows|MinGW) Added WASAPI2 support
* (Windows) Added experimental exclusive mode for WASAPI2
Version 1.2.13 (2025.08.31):
Bugfixes:
* Fixed playlist alternating row colors no longer working with some styles (#1806)
* Fixed "Open Audio CD" no longer working (#1803)
* Fixed systemtray icon playback status not working with scaling (#1782)
* Fixed build without MusicBrainz (#1799)
* Fixed build without MTP (#1804)
Enhancements:
* Added Discord status text option (#1796)
* Read Vorbis/FLAC "Other" embedded covers if front cover is not available (#1793)
Version 1.2.12 (2025.08.12):
Bugfixes:
* Fixed scrobbling for radio streams.
* Fixed CDDA memory leaks.
* Fixed device view CDDA loading (#1676).
* Fixed collection directory editing (#1767).
* Fixed devices sometimes being duplicated in the database.
* Fixed alternating playlist row colors with Windows 11 style.
* Fixed broken file filter for GME formats.
* Fixed collection scanning for GME formats.
* Fixed Chartlyrics.
* Fixed network cache file descriptor leak on lyrics search with workaround for QTBUG-135641.
* Fixed parsing Tidal urls with certain stream URL replies.
* Fixed pixelated window icon on Wayland (#1753).
* Fixed saving collection grouping with special characters in the name (#1758).
* Fixed Spotify token not automatically updated on renewal when playing (#1769).
* (macOS/Windows) Fixed network cache file descriptor leak with patch for QTBUG-135641.
* (Windows|MSVC) Fixed installer to not restart the computer after installing Visual C++ Redistributable.
Enhancements:
* Implemented edit tag dialog reset for year, track, disc and rating.
* Added ALAC to supported filetypes for iPods.
* Added CD-TEXT support.
* Added back Genius lyrics.
* Added support for reporting more info to ListenBrainz.
* Added support for BPM, mood and initial key tags.
* Added support for sort tags to collection, playlists and smart playlists.
Version 1.2.11 (2025.05.15):
* Fixed playlist songs sometimes not updated with new cover.
* Fixed context album cover showing even when it's disabled in the setting (#1744).
* Fixed crash when dragging songs to a closed playlist (#1741).
* Enable startup notify in desktop file.
* (Windows|MSVC) Add experimental support for native ARM64 builds.
* (Windows|MinGW) Fixed crash on exit.
Version 1.2.10 (2025.04.18):
Bugfixes:
* Fixed Discord rich presence showing bogus artist and album.
* Fixed incorrect ID3v2 comment tag.
* (macOS|Windows MSVC) Fixed stuck playback of some streams.
Enhancements:
* Removed Genius lyrics (longer working properly because of website changes).
* (macOS|Windows MSVC) Added back Spotify
Version 1.2.9 (2025.04.08):
Bugfixes:
* Fixed subsonic parse error (#1719).
* Fixed Deezer cover provider parse error (#1716).
* Fixed last.fm import progress.
* (Windows|MinGW) Switched from winpthreads to win32 threads, winpthreads are no longer working with Qt as of version 6.9 (QTBUG-131892).
Enhancements:
* Added option to disable playbin3.
Version 1.2.8 (2025.04.05):
Bugfixes:
* Added "HI_RES_LOSSLESS" for Tidal quality setting.
* Increased backend settings device lineedit height.
* Possible fix for KGlobalAccel shortcuts sometimes not working.
Enhancements:
* Removed deprecated Tidal username/password login.
* Turned off "Grey out unavailable songs in playlists on startup" by default.
* Added support for reading tags from streams.
* Added tooltips in equalizer, backend and appearance settings.
* Added full tag support for AIFF including embedded covers.
* Use card ID instead of index for ALSA devices.
* Removed KDSingleApplication from 3rdparty.
* Support arbitrarily large EBU R 128 loudness normalization.
New features:
* Added Discord rich presence support.
Version 1.2.7 (2025.01.31):
Bugfixes:
* Fixed strawberry exiting when clicking tray icon.
* Fixed Clementine import script errors.
* Disabled OSD Pretty on Wayland since it's not working properly.
Enhancements:
* Only maximize error dialog if Strawberry is the active window (#1627).
* Added QPA Platform Native Interface as optional component.
Version 1.2.6 (2025.01.17):
Bugfixes:
* Fixed dragging songs from playlist to queue.
Version 1.2.5 (2025.01.17):
Bugfixes:
* Fixed crash when saving playcount or rating to file (#1633).
* Fixed QFile::open failing in unit tests.
* Fixed playlist sequence settings saved to wrong configuration file (#1649).
Enhancements:
* Fixed use of deprecated GIO functions with GLib 2.84 and newer.
* (macOS) Added back Sparkle updater to check for new releases.
Version 1.2.4 (2025.01.10):
Bugfixes:
* Fixed Spotify songs not being available for scrobbling.
* Fixed leading "A" and "The" articles being skipped for album sort text.
* Fixed thread safety issue when validating playlist songs on startup.
* Fixed filter search not ignoring space after colon when using column based search.
* Fixed KGlobalAccel to use capitalized application name.
* Fixed slash not properly handled when saving a playlist (#1624).
* (Unix) Fixed collection scanner so it ignores special filesystem paths (/sys, /proc, /run, etc) (#1615).
* (Windows) Fixed smart playlist wizard not respecting dark mode with Windows 11 style (#1639).
Enhancements:
* Use XSPF "title" as playlist name when loading and saving playlists (#1624).
* Added support for using album ID when receving album covers for Subsonic songs (#1636).
* Added option for preserving directory structure when trascoding songs (#1637).
* (Windows) Always run MSVC runtime installer to possible fix issues when there is an older runtime installed.
Version 1.2.3 (2024.12.08):
Bugfixes:
* Fixed libcdio NULL related compilation error on FreeBSD (#1610).
* Fixed missing seek when starting playback of a CUE song (#1568).
* Fixed "QDBusObjectPath: invalid path" error.
Version 1.2.2 (2024.11.23):
Bugfixes:
* Fixed crash when creating a new smart playlist (#1609).
* Fixed last playlist column being added when dragging a song and switching playlists.
Version 1.2.1 (2024.11.21):
This release features major restructuring of the codebase, moving source files,
rewriting CMake build files, dropping Qt 5 support, external tagreader,
and dropping some unmaintained parts such as VLC.
Bugfixes:
* Fixed playback of CUE continuing to play from the same file after the song has finished playing (#1568).
* Fixed updating collection song sort text when disc is changed.
* Fixed current playing file left open when the next track errored (#1582).
* Fixed filter search not finding song containing uppercase "A" (#1599).
* Fixed crash when removing album from playlist when using shuffle albums (#1588).
* Fixed IDv3 MBID's tags with multiple entries being ignored.
* Fixed crash when enabling Tidal, Spotify, Qobuz or Subsonic services.
* Fixed passing filenames to strawberry on command line not resolving to absolute paths.
* (macOS) Fixed program not starting for users with long usernames.
* (macoS) Fixed crash when pressing caps lock (#1606).
* (macOS) Remove "song progress on taskbar" option in behaviour settings.
Enhancements:
* Resolve symbolic links when dragging files to the playlist to match collection song.
* Replaced Spotify username/password with access token.
* Require Qt 6.4 or higher and drop support for Qt 5.
* Require TagLib 1.12 or higher.
* Use Qt stringliterals.
* Move gstfastspectrum to src.
* Use standard user temp location for current album cover.
* Removed old MacFSListener.
* Removed external tagreader and protobuf dependency.
* Removed VLC support.
* Ported to Qt translation (.ts) files and removed gettext dependency.
* Removed deprecated Gnome/Mate SettingsDaemon global shortcuts.
Version 1.1.3 (2024.09.21):
Bugfixes:
* Fixed gstreamer registry lookup leak in Spotify settings.
* Fixed all songs in a CUE sheet starting playback at the zero position (#1549).
* Fixed playback going to pause and back to play on song change.
* Fixed Genius Lyrics login not working (#1554).
* Fixed slow collection filter search.
Version 1.1.2 (2024.09.12):
Bugfixes:
* Fixed Tidal Open API cover provider to only login when needed instead of on startup.
* Fixed KDE added keyboard accelerator characters (ampersands) appearing in sidebar (#1400, #1389, #1476).
* Fixed KDE added keyboard accelerator characters (ampersands) appearing when editing playlist name (#1499).
* Fixed collection "Search for this" adding prefix without value (#1510).
* Fixed play (-p) command line option not working on startup (#1465).
* Fixed scan transaction being started when "Update the collection when Strawberry starts" option is unchecked (#1469)
* Fixed Spotify bitrate being limited 128kbit/s.
* Fixed Spotify returning too many artists and albums.
* Fixed manually switching Spotify songs blocking UI.
* Fixed analyzer not being set.
* Fixed context top text being updated causing selected text to be unselected.
* Fixed filter search to use filename for songs with empty title.
* Fixed missing developer in Appstream appdata file.
* Fixed MPRIS2 DesktopEntry to return desktop file entry without ".desktop" (#1516)
* Fixed WavPack .wvc accepted as valid audio files (#1525).
* Fixed dynamic playlist controls not following system colors (#1483).
* Fixed freeze on playlist right click (#1478).
* Fixed copying songs to a iPod device keeping too many files open (#1527).
* Fixed MBIDs from MP4 being parsed incorrectly causing ListenBrainz errors (#1531).
* Fixed playlist sorting after filename (#1538).
Enhancements:
* Improved volume adjustment and track seeking using touchpad (#1498).
* Use own thread for lyrics parsing.
* Added url and filename columns to collection and playlist filter search.
* (macOS) Added Spotify.
Version 1.1.1 (2024.07.22):
Bugfixes:
* Fixed compilation songs being split into different albums when using album grouping.
* Fixed adding playlist columns not working when stretch mode is disabled (#1085).
* Fixed resetting playlist columns.
* Fixed adding songs to playlist adding all songs instead of filtered songs.
* Fixed collection filter matching entire text instead of individual words.
Enhancements:
* Use same code for collection and playlist filter search.
Version 1.1.0 (2024.07.14):
Bugfixes:
* Fixed crash when pressing CTRL + C (#1359).
* Pass on scroll events to page in settings to avoid changing settings when scrolling with mouse (#1380).
* Fixed misredered playlist search field with Wayland using scaling (#1255).
* Fixed Azlyrics lyrics provider because of website changes.
* Fixed Musixmatch lyrics provider because of website changes.
* Fixed application exiting when closing file dialog (#1401).
* Fixed playlist shuffle randomness (#707).
* Fixed playlist shuffle order always the same when restarting playback (#1381).
* Fixed dynamic random mix not always ignoring shuffle and repeat mode (#1366).
* Fixed manual shuffle while playing not setting current song to new index (##1353).
* Fixed volume sync with PA when output is set to auto (#1123).
* Fixed mpris:trackid type with KDE 6 (#1397).
* Fixed open in file manager feature not handling missing XDG_DATA_DIRS variable.
* Fixed collection pixmap disk cache and moodbar cache with newer Qt versions.
* Fixed reading common metadata CUE's with multiple files (#1463).
* Fixed playlist header stretch mode to only resize the right column of the column being resized.
* Fixed adding columns to playlist header not working when not using stretch mode (#1085).
* Fixed severe memory leak (!) in context album cover fading (#1464).
* Separate albums by different artist in album groupings (#1276).
* Removed -new-window parameter from dolphin command for open in file manager (#1412).
* Only use playbin3 with GStreamer 1.24 and higher, not with GStreamer 1.22 or lower.
* (macOS/Windows) Fixed dash and hls streaming, plugins were missing.
* (Windows) Fixed incorrect colors in smart playlist wizard with Fusion in dark mode (#1399).
* (Windows) Fixed update window blocking sponsor window on startup.
Enhancements:
* Improve error messages when connecting and copying to devices.
* Allow enter to be used with multiselection to add songs to playlist (#1360)
* Add song progress to taskbar using D-Bus.
* Use API to receive Radio Paradise channels.
* Added button for fetching lyrics to tag editor (#1391).
* Added option not to skip "A", "An" and "The” when sorting artist names in collection (#1393).
* Improved album and title disc, remastered, etc matching and stripping (#1387).
* Save volume to settings when adjusting (#1272).
* Resolve song from collection using track with Cue in XSPF (#1181).
* Added background image to sidebar.
* Read metadata from RIFF WAV files (#1424).
* Use original path instead of canonical path when adding directories to the collection.
* Only apply added/removed collection directories when settings are saved.
* Detect and handle different text encodings when reading CUE files (#1429).
* The collection has been rewritten and improved (model/filter/search) (#392).
* Improve error messages from tag reader.
* (Unix) Add experimental GStreamer pipewire support.
* (Windows) Add experimental exclusive mode for WASAPI.
* (Windows MSVC) Added experimental ASIO support.
* (Windows MSVC) Add back WASAPI2.
New features:
* Letras lyrics provider.
* Open Tidal API (openapi.tidal.com) cover provider.
* Turbine analyzer.
* WaveRubber analyzer.
* Spotify streaming support.
Removed features:
* Removed now broken lyricsmode.com lyrics provider because of website changes.
Version 1.0.23 (2024.01.11):
Bugfixes:
* Fixed possible duplication of song entries after organizing (#1341).
* Fixed possible crash when connecting devices (#1313).
* Fixed playlist sorting of original year (#1349).
* (macOS) Fixed crash when adding collection directory (QTBUG-120469) (#1350).
Enhancements:
* Treat all stream errors as non-fatal (#1347).
* Require KDSingleApplication 1.1.0.
* Fix logging of restored unavailable songs.
Version 1.0.22 (2023.12.09):
Bugfixes:
* 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:

190
README.md
View File

@@ -1,121 +1,137 @@
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/build/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
=======================
# :strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/Build/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
[![Sponsor](https://img.shields.io/badge/-Sponsor-green?logo=github)](https://github.com/sponsors/jonaski)
[![Patreon](https://img.shields.io/badge/patreon-donate-green.svg)](https://patreon.com/jonaskvinge)
[![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt toolkit.
Strawberry is a **music player and music collection organizer**, originally forked from *Clementine* in 2018.
Its written in **C++ using the Qt framework**, designed for **audiophiles and music collectors**.
![Browse](https://raw.githubusercontent.com/strawberrymusicplayer/strawberry/master/data/screenshot/screenshot.png)
![Screenshot of Strawberry Music Player](https://raw.githubusercontent.com/strawberrymusicplayer/strawberry/master/data/screenshot/screenshot.png)
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/
* Latest builds: https://builds.strawberrymusicplayer.org/
* openSUSE buildservice: https://build.opensuse.org/package/show/home:jonaski:audio/strawberry
* Ubuntu PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
* Ubuntu Unstable PPA: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
* Translations: https://translate.zanata.org/iteration/view/strawberry/master
## :globe_with_meridians: Resources
### :bangbang: Opening an issue:
- **Website:** https://www.strawberrymusicplayer.org
- **Wiki:** https://wiki.strawberrymusicplayer.org
- **Forum:** https://forum.strawberrymusicplayer.org
- **GitHub:** https://github.com/strawberrymusicplayer/strawberry
- **Latest builds:** https://builds.strawberrymusicplayer.org
- **openSUSE Build Service:**
- Stable: https://build.opensuse.org/package/show/home:jonaski:strawberry/strawberry
- Unstable: https://build.opensuse.org/package/show/home:jonaski:strawberry-dev/strawberry
- **Ubuntu PPAs:**
- Stable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
- Unstable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
- **Translations:** https://crowdin.com/project/strawberrymusicplayer
* 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, 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/).
---
### :moneybag: Sponsoring:
## :warning: Opening an Issue
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:
Before creating a new GitHub issue:
1. [GitHub Sponsors](https://github.com/sponsors/jonaski)
2. [Patreon](https://www.patreon.com/jonaskvinge)
3. [PayPal](https://paypal.me/jonaskvinge)
1. **Read the [FAQ](https://wiki.strawberrymusicplayer.org/wiki/FAQ)**.
2. **Search existing issues** to avoid duplicates. If one already exists, comment there with any additional information.
3. **Use the [forum](https://forum.strawberrymusicplayer.org/)** for technical problems, discussions or feature suggestions — its better suited for back-and-forth conversation.
4. **Feature requests are not accepted on GitHub.** Issues created for feature requests will be closed. You can still discuss ideas on the forum.
5. **Flatpak users:** We do **not** maintain the Flatpak package. Report Flatpak-specific issues via [Flatpak support](https://flatpak.org/about/).
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:
## :moneybag: Sponsoring
* 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.
* Audio CD playback
* Native desktop notifications
* Playlist management
* Smart and dynamic playlists
* Advanced audio output and device configuration for bit-perfect playback on Linux
* Edit tags on audio files
* Fetch tags from MusicBrainz
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Musixmatch](https://www.musixmatch.com/), [Deezer](https://www.deezer.com/), [Tidal](https://www.tidal.com/), [Qobuz](https://www.qobuz.com/) and [Spotify](https://www.spotify.com/)
* Song lyrics from [AudD](https://audd.io/), [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/) and [lololyrics.com](https://www.lololyrics.com/)
* Support for multiple backends
* Audio analyzer
* Audio equalizer
* Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
* Subsonic, Tidal and Qobuz streaming support
Strawberry is **free software released under the GPL**.
If you enjoy using it, please consider **supporting development** through sponsorship or donation.
**Sponsorship options:**
1. [Patreon](https://www.patreon.com/jonaskvinge)
2. [GitHub](https://github.com/sponsors/jonaski)
3. [Ko-fi](https://ko-fi.com/jonaskvinge)
4. [PayPal](https://paypal.me/jonaskvinge)
It has so far been tested to work on Linux, OpenBSD, FreeBSD, macOS and Windows.
Supporting open-source developers helps ensure continued maintenance and improvements.
**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.**
---
### :heavy_exclamation_mark: Requirements
## :white_check_mark: Features
To build Strawberry from source you need the following installed on your system with the additional development packages/headers:
- Play and organize your music collection
- Supports formats: WAV, FLAC, WavPack, Ogg Vorbis, Opus, MPC, TrueAudio, AIFF, MP4, MP3, ASF, and Monkeys Audio
- Audio CD playback
- Bit-perfect playback on Linux
- Native desktop notifications
- Advanced playlist management
- Smart and dynamic playlists
- Loudness analysis and EBU R128 normalization
- Editing tags and fetching missing tags via [MusicBrainz](https://musicbrainz.org/)
- Album 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/), [Spotify](https://www.spotify.com/)
- Lyrics from: [Genius](https://genius.com/), [Musixmatch](https://www.musixmatch.com/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/), [lololyrics](https://www.lololyrics.com/), [songlyrics](https://www.songlyrics.com/), [azlyrics](https://www.azlyrics.com/), [elyrics](https://www.elyrics.net/), [letras](https://www.letras.mus.br), [LyricFind](https://lyrics.lyricfind.com) and [lrclib.net](https://lrclib.net/)
- Audio analyzer and equalizer
- Transfer music to USB, MTP and iPod devices
- Scrobbling to [Last.fm](https://www.last.fm/) and [ListenBrainz](https://listenbrainz.org/)
- Streaming from Subsonic-compatible servers
- Unofficial integrations: Tidal, Spotify, and Qobuz
- Discord Rich Presence
* [CMake](https://cmake.org/)
* 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 6 or Qt 5.9 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:
:white_check_mark: Tested on **Linux**, **OpenBSD**, **FreeBSD**, **macOS**, and **Windows**.
* Song fingerprinting and MusicBrainz tagging: [Chromaprint](https://acoustid.org/chromaprint)
* Moodbar: [fftw3](http://www.fftw.org/)
* PulseAudio integration: [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
> **Note:** macOS and Windows releases are currently **available to sponsors only**.
> A monthly sponsorship via [Patreon](https://www.patreon.com/jonaskvinge) grants direct access to new releases.
You should also install the gstreamer plugins base and good, and optionally bad, ugly and libav to support all audio formats.
---
### :wrench: Compiling from source
## :gear: Requirements
### Get the code:
To build Strawberry from source, youll need:
git clone https://github.com/strawberrymusicplayer/strawberry
**Dependencies:**
- [CMake ≥= 3.13](https://cmake.org/)
- 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 ≥= 6.4](https://www.qt.io/) (Core, Concurrent, Gui, Widgets, Network, SQL, D-Bus)
- [SQLite ≥= 3.9](https://www.sqlite.org)
- [ALSA (Linux only)](https://www.alsa-project.org/)
- [GStreamer](https://gstreamer.freedesktop.org/)
- [TagLib ≥= 1.12](https://www.taglib.org/)
- [ICU](https://unicode-org.github.io/icu/)
- [KDSingleApplication ≥= 1.1.0](https://github.com/KDAB/KDSingleApplication)
### Compile and install:
**Dependencies for optional features:**
- Fingerprinting & tagging: [Chromaprint](https://acoustid.org/chromaprint)
- Moodbar: [FFTW3](http://www.fftw.org/)
- PulseAudio integration: [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/)
- Audio CD support: [libcdio](https://www.gnu.org/software/libcdio/)
- MTP devices: [libmtp](http://libmtp.sourceforge.net/)
- iPod Classic: [libgpod](http://www.gtkpod.org/libgpod/)
- EBU R128 normalization: [libebur128](https://github.com/jiixyj/libebur128)
- Discord presence: [RapidJSON](https://rapidjson.org/)
Also install GStreamer plugins **base**, **good**, and optionally **bad**, **ugly** and **libav** for full codec support.
---
## :wrench: Build from Source
**Get the code:**
git clone --recursive https://github.com/strawberrymusicplayer/strawberry
**Build and install:**
cd strawberry
mkdir build
cd build
cmake .. -DBUILD_WITH_QT6=ON
make -j $(nproc)
sudo make install
cmake -S . -B build
cmake --build build --parallel $(nproc)
sudo cmake --install build
Strawberry is backwards compatible with Qt 5, to compile with Qt 5 use:
For building on Windows with Visual Studio 2022, see: :point_right: https://github.com/strawberrymusicplayer/strawberry-msvc
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?exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)
## :package: Packaging status
[![Packaging status](https://repology.org/badge/vertical-allrepos/strawberry.svg?columns=3&header=Strawberry&exclude_unsupported=1)](https://repology.org/metapackage/strawberry/versions)

View File

@@ -2,5 +2,5 @@ find_program(LSB_RELEASE_EXEC lsb_release)
find_program(DPKG_BUILDPACKAGE dpkg-buildpackage)
if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
add_custom_target(deb WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us)
add_custom_target(deb WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us -nc -j4)
endif()

View File

@@ -1,11 +1,17 @@
#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 REQUIRED)
if(MACDEPLOYQT_EXECUTABLE)
message(STATUS "Found macdeployqt: ${MACDEPLOYQT_EXECUTABLE}")
else()
message(WARNING "Missing macdeployqt executable.")
endif()
find_program(MACDEPLOYCHECK_EXECUTABLE NAMES macdeploycheck PATHS /usr/bin /usr/local/bin /opt/local/bin /usr/local/opt/qt6/bin REQUIRED)
if(MACDEPLOYCHECK_EXECUTABLE)
message(STATUS "Found macdeploycheck: ${MACDEPLOYCHECK_EXECUTABLE}")
else()
message(WARNING "Missing macdeploycheck executable.")
endif()
find_program(CREATEDMG_EXECUTABLE NAMES create-dmg REQUIRED)
if(CREATEDMG_EXECUTABLE)
message(STATUS "Found create-dmg: ${CREATEDMG_EXECUTABLE}")
@@ -14,26 +20,33 @@ 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_BINARY_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/gst-plugin-scanner ${MACDEPLOYQT_CODESIGN}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS strawberry strawberry-tagreader copy_gstreamer_plugins macdeployqt
)
add_custom_target(deploycheck
COMMAND ${CMAKE_BINARY_DIR}/ext/macdeploycheck/macdeploycheck strawberry.app
DEPENDS macdeploycheck
DEPENDS strawberry
)
if(MACDEPLOYCHECK_EXECUTABLE)
add_custom_target(deploycheck
COMMAND ${MACDEPLOYCHECK_EXECUTABLE} strawberry.app
)
endif()
if(CREATEDMG_EXECUTABLE)
add_custom_target(dmg
COMMAND ${CREATEDMG_EXECUTABLE} --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 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

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

View File

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

View File

@@ -1,15 +1,15 @@
set(summary_willbuild "")
set(summary_willnotbuild "")
macro(summary_add name test)
macro(optional_component_summary_add name test)
if (${test})
list(APPEND summary_willbuild ${name})
else (${test})
list(APPEND summary_willnotbuild "${name}")
endif (${test})
endmacro(summary_add)
endmacro(optional_component_summary_add)
macro(summary_show_part variable title)
macro(optional_component_summary_show_part variable title)
list(LENGTH ${variable} _len)
if (_len)
message("")
@@ -18,19 +18,20 @@ macro(summary_show_part variable title)
message(" ${_item}")
endforeach (_item)
endif (_len)
endmacro(summary_show_part)
endmacro(optional_component_summary_show_part)
macro(summary_show)
macro(optional_component_summary_show)
list(SORT summary_willbuild)
list(SORT summary_willnotbuild)
message("")
message("Building strawberry version: ${STRAWBERRY_VERSION_DISPLAY}, Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
summary_show_part(summary_willbuild "The following components will be built:")
summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
optional_component_summary_show_part(summary_willbuild "The following components will be built:")
optional_component_summary_show_part(summary_willnotbuild "The following components WILL NOT be built:")
message("")
endmacro(summary_show)
endmacro(optional_component_summary_show)
function(optional_component name default description)
set(option_variable "ENABLE_${name}")
set(have_variable "HAVE_${name}")
set(${have_variable} OFF)
@@ -79,6 +80,9 @@ function(optional_component name default description)
set(text "${description} (missing ${deplist_text})")
set(summary_willnotbuild "${summary_willnotbuild};${text}" PARENT_SCOPE)
message(FATAL_ERROR "${text}, to disable this optional feature, pass -D${option_variable}=OFF to CMake")
else()
set(${have_variable} ON PARENT_SCOPE)
set(summary_willbuild "${summary_willbuild};${description}" PARENT_SCOPE)

View File

@@ -64,7 +64,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
add_custom_target(rpm
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
COMMAND ${RPMBUILD_EXEC} -ba ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
COMMAND ${RPMBUILD_EXEC} -ba ${CMAKE_BINARY_DIR}/strawberry.spec
)
endif()

View File

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

View File

@@ -1,31 +1,19 @@
set(STRAWBERRY_VERSION_MAJOR 1)
set(STRAWBERRY_VERSION_MINOR 0)
set(STRAWBERRY_VERSION_PATCH 12)
set(STRAWBERRY_VERSION_MINOR 2)
set(STRAWBERRY_VERSION_PATCH 16)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF)
set(INCLUDE_GIT_REVISION ON)
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_R "1")
set(STRAWBERRY_VERSION_PAC_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_PAC_R "1")
if(STRAWBERRY_VERSION_PRERELEASE)
set(STRAWBERRY_VERSION_DISPLAY "${STRAWBERRY_VERSION_DISPLAY} ${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_RPM_R "0.${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_PACKAGE "${STRAWBERRY_VERSION_PACKAGE}${STRAWBERRY_VERSION_PRERELEASE}")
endif(STRAWBERRY_VERSION_PRERELEASE)
if(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
if(FORCE_GIT_REVISION)
set(GIT_REVISION ${FORCE_GIT_REVISION})
elseif(INCLUDE_GIT_REVISION AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
find_program(GIT_EXECUTABLE git)
if(NOT GIT_EXECUTABLE OR GIT_EXECUTABLE-NOTFOUND)
message(FATAL_ERROR "Missing GIT executable." )
message(FATAL_ERROR "Missing Git executable." )
endif()
# Get the current working branch
@@ -48,15 +36,11 @@ 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()
if(FORCE_GIT_REVISION)
set(GIT_REVISION ${FORCE_GIT_REVISION})
endif()
if(GIT_REVISION)
string(REGEX REPLACE "^(.+)-([0-9]+)-(g[a-f0-9]+)$" "\\1;\\2;\\3" GIT_PARTS ${GIT_REVISION})
@@ -67,7 +51,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)
@@ -78,15 +62,23 @@ if(GIT_REVISION)
set(STRAWBERRY_VERSION_DISPLAY "${GIT_REVISION}")
set(STRAWBERRY_VERSION_PACKAGE "${GIT_TAGNAME}.${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
string(REPLACE "-" "~" STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
set(STRAWBERRY_VERSION_RPM_R "2.${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_PAC_V "${GIT_TAGNAME}.r${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_PAC_R "1")
else()
if(STRAWBERRY_VERSION_PRERELEASE)
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}-${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}~${STRAWBERRY_VERSION_PRERELEASE}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}${STRAWBERRY_VERSION_PRERELEASE}")
else()
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
endif()
set(STRAWBERRY_VERSION_RPM_R "1")
endif()
message(STATUS "Strawberry Version:")
message(STATUS "Display: ${STRAWBERRY_VERSION_DISPLAY}")
message(STATUS "Package: ${STRAWBERRY_VERSION_PACKAGE}")
message(STATUS "RPM: ${STRAWBERRY_VERSION_RPM_V}-${STRAWBERRY_VERSION_RPM_R}")
message(STATUS "PAC: ${STRAWBERRY_VERSION_PAC_V}-${STRAWBERRY_VERSION_PAC_R}")

3
crowdin.yml Normal file
View File

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

View File

@@ -7,6 +7,12 @@
<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/schema-19.sql</file>
<file>schema/schema-20.sql</file>
<file>schema/schema-21.sql</file>
<file>schema/device-schema.sql</file>
<file>style/strawberry.css</file>
<file>style/smartplaylistsearchterm.css</file>
@@ -39,5 +45,7 @@
<file>pictures/star-off.png</file>
<file>mood/sample.mood</file>
<file>text/ghosts.txt</file>
<file>pictures/sidebar-background.png</file>
<file>style/dynamicplaylistcontrols.css</file>
</qresource>
</RCC>

View File

@@ -91,12 +91,14 @@
<file>icons/128x128/love.png</file>
<file>icons/128x128/subsonic.png</file>
<file>icons/128x128/tidal.png</file>
<file>icons/128x128/spotify.png</file>
<file>icons/128x128/qobuz.png</file>
<file>icons/128x128/multimedia-player-ipod-standard-black.png</file>
<file>icons/128x128/radio.png</file>
<file>icons/128x128/somafm.png</file>
<file>icons/128x128/radioparadise.png</file>
<file>icons/128x128/musicbrainz.png</file>
<file>icons/128x128/dropbox.png</file>
<file>icons/64x64/albums.png</file>
<file>icons/64x64/alsa.png</file>
<file>icons/64x64/application-exit.png</file>
@@ -189,12 +191,14 @@
<file>icons/64x64/love.png</file>
<file>icons/64x64/subsonic.png</file>
<file>icons/64x64/tidal.png</file>
<file>icons/64x64/spotify.png</file>
<file>icons/64x64/qobuz.png</file>
<file>icons/64x64/multimedia-player-ipod-standard-black.png</file>
<file>icons/64x64/radio.png</file>
<file>icons/64x64/somafm.png</file>
<file>icons/64x64/radioparadise.png</file>
<file>icons/64x64/musicbrainz.png</file>
<file>icons/64x64/dropbox.png</file>
<file>icons/48x48/albums.png</file>
<file>icons/48x48/alsa.png</file>
<file>icons/48x48/application-exit.png</file>
@@ -290,13 +294,15 @@
<file>icons/48x48/moodbar.png</file>
<file>icons/48x48/love.png</file>
<file>icons/48x48/subsonic.png</file>
<file>icons/48x48/tidal.png</file>
<file>icons/48x48/qobuz.png</file>
<file>icons/48x48/tidal.png</file>
<file>icons/48x48/spotify.png</file>
<file>icons/48x48/qobuz.png</file>
<file>icons/48x48/multimedia-player-ipod-standard-black.png</file>
<file>icons/48x48/radio.png</file>
<file>icons/48x48/somafm.png</file>
<file>icons/48x48/radioparadise.png</file>
<file>icons/48x48/musicbrainz.png</file>
<file>icons/48x48/dropbox.png</file>
<file>icons/32x32/albums.png</file>
<file>icons/32x32/alsa.png</file>
<file>icons/32x32/application-exit.png</file>
@@ -393,12 +399,14 @@
<file>icons/32x32/love.png</file>
<file>icons/32x32/subsonic.png</file>
<file>icons/32x32/tidal.png</file>
<file>icons/32x32/spotify.png</file>
<file>icons/32x32/qobuz.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-black.png</file>
<file>icons/32x32/radio.png</file>
<file>icons/32x32/somafm.png</file>
<file>icons/32x32/radioparadise.png</file>
<file>icons/32x32/musicbrainz.png</file>
<file>icons/32x32/dropbox.png</file>
<file>icons/22x22/albums.png</file>
<file>icons/22x22/alsa.png</file>
<file>icons/22x22/application-exit.png</file>
@@ -495,11 +503,13 @@
<file>icons/22x22/love.png</file>
<file>icons/22x22/subsonic.png</file>
<file>icons/22x22/tidal.png</file>
<file>icons/22x22/spotify.png</file>
<file>icons/22x22/qobuz.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-black.png</file>
<file>icons/22x22/radio.png</file>
<file>icons/22x22/somafm.png</file>
<file>icons/22x22/radioparadise.png</file>
<file>icons/22x22/musicbrainz.png</file>
<file>icons/22x22/dropbox.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -12,17 +12,23 @@ CREATE TABLE device_%deviceid_subdirectories (
CREATE TABLE device_%deviceid_songs (
title TEXT,
titlesort TEXT,
album TEXT,
albumsort TEXT,
artist TEXT,
artistsort TEXT,
albumartist TEXT,
albumartistsort 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,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
composersort TEXT,
performer TEXT,
performersort TEXT,
grouping TEXT,
comment TEXT,
lyrics TEXT,
@@ -59,15 +65,38 @@ CREATE TABLE device_%deviceid_songs (
compilation_off INTEGER NOT NULL DEFAULT 0,
compilation_effective INTEGER NOT NULL DEFAULT 0,
art_embedded INTEGER DEFAULT 0,
art_automatic TEXT,
art_manual TEXT,
art_unset INTEGER DEFAULT 0,
effective_albumartist TEXT,
effective_originalyear INTEGER NOT NULL DEFAULT 0,
cue_path TEXT,
rating INTEGER DEFAULT -1
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,
bpm REAL,
mood TEXT,
initial_key TEXT
);
@@ -75,9 +104,4 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
tokenize = "unicode61 remove_diacritics 1"
);
UPDATE devices SET schema_version=3 WHERE ROWID=%deviceid;
UPDATE devices SET schema_version=6 WHERE ROWID=%deviceid;

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;

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

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

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

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

43
data/schema/schema-21.sql Normal file
View File

@@ -0,0 +1,43 @@
DROP INDEX IF EXISTS idx_albumartistsort;
DROP INDEX IF EXISTS idx_albumsort;
DROP INDEX IF EXISTS idx_artistsort;
DROP INDEX IF EXISTS idx_composersort;
DROP INDEX IF EXISTS idx_performersort;
DROP INDEX IF EXISTS idx_titlesort;
ALTER TABLE %allsongstables ADD COLUMN albumartistsort TEXT;
ALTER TABLE %allsongstables ADD COLUMN albumsort TEXT;
ALTER TABLE %allsongstables ADD COLUMN artistsort TEXT;
ALTER TABLE %allsongstables ADD COLUMN composersort TEXT;
ALTER TABLE %allsongstables ADD COLUMN performersort TEXT;
ALTER TABLE %allsongstables ADD COLUMN titlesort TEXT;
ALTER TABLE %allsongstables ADD COLUMN bpm REAL;
ALTER TABLE %allsongstables ADD COLUMN mood TEXT;
ALTER TABLE %allsongstables ADD COLUMN initial_key TEXT;
CREATE INDEX IF NOT EXISTS idx_albumartistsort ON songs (albumartistsort);
CREATE INDEX IF NOT EXISTS idx_albumsort ON songs (album);
CREATE INDEX IF NOT EXISTS idx_artistsort ON songs (artistsort);
CREATE INDEX IF NOT EXISTS idx_composersort ON songs (title);
CREATE INDEX IF NOT EXISTS idx_performersort ON songs (title);
CREATE INDEX IF NOT EXISTS idx_titlesort ON songs (title);
UPDATE schema_version SET version=21;

82
data/schema/schema-22.sql Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

12
debian/CMakeLists.txt vendored
View File

@@ -4,19 +4,7 @@ if(LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
execute_process(COMMAND /bin/sh "-c" "${LSB_RELEASE_EXEC} -cs" OUTPUT_VARIABLE DEB_CODENAME OUTPUT_STRIP_TRAILING_WHITESPACE)
if(DEB_CODENAME AND DEB_DATE)
if(BUILD_WITH_QT5)
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(BUILD_WITH_QT6)
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()

3
debian/clean vendored Normal file
View File

@@ -0,0 +1,3 @@
dist/scripts/maketarball.sh
CMakeCache.txt
CMakeFiles/

1
debian/compat vendored
View File

@@ -1 +0,0 @@
11

View File

@@ -2,37 +2,45 @@ Source: strawberry
Section: sound
Priority: optional
Maintainer: Jonas Kvinge <jonas@jkvinge.net>
Build-Depends: debhelper (>= 11),
Build-Depends: debhelper-compat (= 12),
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,
libicu-dev,
@DEBIAN_BUILD_DEPENDS_QT_PACKAGES@,
libxkbcommon-dev,
qt6-base-dev,
qt6-base-private-dev,
qt6-base-dev-tools,
qt6-tools-dev,
qt6-tools-dev-tools,
qt6-l10n-tools,
libkdsingleapplication-qt6-dev,
libgstreamer1.0-dev,
libgstreamer-plugins-base1.0-dev,
libcdio-dev,
libgpod-dev,
libmtp-dev,
libchromaprint-dev,
libfftw3-dev
Standards-Version: 4.6.1
libfftw3-dev,
libebur128-dev,
libsparsehash-dev,
rapidjson-dev
Standards-Version: 4.7.0
Package: strawberry
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
@DEBIAN_DEPENDS_QT_PACKAGES@,
libqt6sql6-sqlite,
qt6-qpa-plugins,
gstreamer1.0-plugins-base,
gstreamer1.0-plugins-good,
gstreamer1.0-alsa,
@@ -52,11 +60,11 @@ Description: music player and music collection organizer
- Edit tags on audio files
- Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from AudD, Genius, Musixmatch, ChartLyrics, lyrics.ovh and lololyrics.com
- Lyrics from multiple sources
- Audio analyzer
- Audio equalizer
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
- Scrobbler with support for Last.fm and ListenBrainz
- Streaming support for Subsonic-compatible servers
- Unofficial streaming support for Tidal and Qobuz
.

71
debian/copyright vendored
View File

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

17
debian/rules vendored
View File

@@ -1,17 +1,14 @@
#!/usr/bin/make -f
%:
dh $@ --buildsystem=cmake -builddirectory=build
export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS=hardening=+all
override_dh_auto_clean:
rm -f dist/macos/Info.plist
rm -f dist/unix/strawberry.spec
rm -f dist/scripts/maketarball.sh
rm -f dist/windows/strawberry.nsi
rm -f src/translations/translations.pot
dh_auto_clean
override_dh_auto_configure:
dh_auto_configure -- \
-DBUILD_WERROR=ON
override_dh_installchangelogs:
dh_installchangelogs Changelog
override_dh_auto_test:
%:
dh $@

24
dist/CMakeLists.txt vendored
View File

@@ -1,15 +1,21 @@
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/scripts/maketarball.sh @ONLY)
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)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/strawberry.spec.in ${CMAKE_BINARY_DIR}/strawberry.spec @ONLY)
endif()
if(APPLE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
endif(APPLE)
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
set(LSMinimumSystemVersion $ENV{MACOSX_DEPLOYMENT_TARGET})
else()
set(LSMinimumSystemVersion 12.0)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist)
endif()
if(WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY)
endif(WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/windres.rc.in ${CMAKE_BINARY_DIR}/windres.rc)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_BINARY_DIR}/strawberry.nsi @ONLY)
endif()
if(UNIX AND NOT APPLE)
install(FILES ../data/icons/48x48/strawberry.png DESTINATION share/icons/hicolor/48x48/apps/)
@@ -17,10 +23,10 @@ if(UNIX AND NOT APPLE)
install(FILES ../data/icons/128x128/strawberry.png DESTINATION share/icons/hicolor/128x128/apps/)
install(FILES unix/org.strawberrymusicplayer.strawberry.desktop DESTINATION share/applications)
install(FILES unix/org.strawberrymusicplayer.strawberry.appdata.xml DESTINATION share/metainfo)
install(FILES unix/strawberry.1 unix/strawberry-tagreader.1 DESTINATION share/man/man1)
endif(UNIX AND NOT APPLE)
install(FILES unix/strawberry.1 DESTINATION share/man/man1)
endif()
if(APPLE)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents")
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/macos/strawberry.icns" DESTINATION "${CMAKE_BINARY_DIR}/strawberry.app/Contents/Resources")
endif()

View File

@@ -33,11 +33,11 @@
<key>LSApplicationCategoryType</key>
<string>public.app-category.music</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.4</string>
<string>@LSMinimumSystemVersion@</string>
<key>SUFeedURL</key>
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
<string>https://www.strawberrymusicplayer.org/sparkle-macos-@ARCH@</string>
<key>SUPublicEDKey</key>
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
<string>/OydhYVfypuO2Mf7G6DUqVZWW9G19eFV74qaDCBTOUk=</string>
<key>CFBundleURLTypes</key>
<array>
<dict>

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,145 @@ 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;
libgstadaptivedemux2
libgstaes
libgstaiff
libgstapetag
libgstapp
libgstasf
libgstasfmux
libgstaudioconvert
libgstaudiofx
libgstaudioparsers
libgstaudioresample
libgstautodetect
libgstbs2b
libgstcdio
libgstcoreelements
libgstdash
libgstdsd
libgstequalizer
libgstfaac
libgstfaad
libgstfdkaac
libgstflac
libgstgio
libgsthls
libgsticydemux
libgstid3demux
libgstid3tag
libgstisomp4
libgstlame
libgstmpegpsdemux
libgstmpegpsmux
libgstmpegtsdemux
libgstmpegtsmux
libgstlibav
libgstmpg123
libgstmusepack
libgstogg
libgstopenmpt
libgstopus
libgstopusparse
libgstosxaudio
libgstpbtypes
libgstplayback
libgstreplaygain
libgstrtp
libgstrtsp
libgstsoup
libgstspectrum
libgstspeex
libgstspotify
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

@@ -1,22 +0,0 @@
#!/bin/sh
macos_version=$(sw_vers -productVersion)
macos_version_major=$(echo $macos_version | awk -F '[.]' '{print $1}')
macos_version_minor=$(echo $macos_version | awk -F '[.]' '{print $2}')
if [ "${macos_version_major}" = "10" ]; then
macos_codenames=(
["13"]="highsierra"
["14"]="mojave"
["15"]="catalina"
)
if [[ -n "${macos_codenames[$macos_version_minor]}" ]]; then
echo "${macos_codenames[$macos_version_minor]}"
else
echo "unknown"
fi
elif [ "${macos_version_major}" = "11" ]; then
echo "bigsur"
else
echo "unknown"
fi

View File

@@ -2,7 +2,7 @@
# Strawberry Music Player
# Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
# 2021 Alexey Vazhnov
# Copyright 2021, Alexey Vazhnov
#
# 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,7 +19,7 @@
# SPDX-License-Identifier: GPL-3.0-only
# Based on https://github.com/strawberrymusicplayer/strawberry/wiki/Import-collection-library-and-playlists-data-from-Clementine
# Based on https://wiki.strawberrymusicplayer.org/wiki/Import_collection_library_and_playlists_from_Clementine
set -o nounset
set -o errexit
@@ -35,8 +35,8 @@ test -f "$FILE_DST" || { echo "No such file: $FILE_DST"; exit 1; }
echo "Will try to copy information from $FILE_SRC to $FILE_DST."
echo
echo 'This script will **delete all information** from Strawberry database!'
read -r -p 'Do you want to continue? (the only YES is accepted) ' answer
echo 'This script will **delete all data** from the Strawberry database!'
read -r -p 'Do you want to continue? (Only YES is accepted) ' answer
if [ "$answer" != "YES" ]; then exit 1; fi
# 'heredoc' with substitution of variables, see `man bash`, "Here Documents":
@@ -62,9 +62,9 @@ INSERT INTO strawberry.subdirectories (directory_id, path, mtime) SELECT directo
INSERT INTO strawberry.songs (ROWID, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, beginning, length, bitrate, samplerate, 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, rating)
SELECT ROWID, title, album, artist, albumartist, track, disc, year, originalyear, genre, compilation, composer, performer, grouping, comment, lyrics, beginning, length, bitrate, samplerate, directory, filename, filetype, filesize, mtime, ctime, unavailable, playcount, skipcount, lastplayed, sampler, forced_compilation_on, forced_compilation_off, effective_compilation, art_automatic, art_manual, effective_albumartist, effective_originalyear, cue_path, rating FROM clementine.songs WHERE unavailable = 0;
UPDATE strawberry.songs SET source = 2;
UPDATE strawberry.songs SET artist_id = "";
UPDATE strawberry.songs SET album_id = "";
UPDATE strawberry.songs SET song_id = "";
UPDATE strawberry.songs SET artist_id = '';
UPDATE strawberry.songs SET album_id = '';
UPDATE strawberry.songs SET song_id = '';
/* Import playlists */
@@ -140,7 +140,7 @@ SELECT ROWID,
bitrate,
samplerate,
directory,
filename,
CASE WHEN filename IS NULL THEN '' ELSE filename END,
filetype,
filesize,
mtime,
@@ -162,16 +162,9 @@ SELECT ROWID,
UPDATE strawberry.playlist_items SET source = 2;
UPDATE strawberry.playlist_items SET type = 2;
UPDATE strawberry.playlist_items SET artist_id = "";
UPDATE strawberry.playlist_items SET album_id = "";
UPDATE strawberry.playlist_items SET song_id = "";
/* Recreate the FTS tables */
DELETE FROM strawberry.songs_fts;
INSERT INTO strawberry.songs_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment
FROM strawberry.songs;
UPDATE strawberry.playlist_items SET artist_id = '';
UPDATE strawberry.playlist_items SET album_id = '';
UPDATE strawberry.playlist_items SET song_id = '';
EOF

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