Compare commits

...

114 Commits

Author SHA1 Message Date
Jonas Kvinge
c435464972 Add visualizations 2025-12-29 00:33:14 +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
393 changed files with 13399 additions and 2295 deletions

View File

@@ -130,7 +130,10 @@ InsertBraces: false
InsertTrailingCommas: None InsertTrailingCommas: None
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true KeepEmptyLines:
AtEndOfFile: true
AtStartOfBlock: true
AtStartOfFile: true
LambdaBodyIndentation: Signature LambdaBodyIndentation: Signature
MacroBlockBegin: '' MacroBlockBegin: ''
MacroBlockEnd: '' MacroBlockEnd: ''

View File

@@ -62,6 +62,7 @@ jobs:
libchromaprint-devel libchromaprint-devel
fftw3-devel fftw3-devel
libebur128-devel libebur128-devel
projectM-devel
desktop-file-utils desktop-file-utils
update-desktop-files update-desktop-files
appstream-glib appstream-glib
@@ -78,6 +79,7 @@ jobs:
qt6-base-common-devel qt6-base-common-devel
qt6-sql-sqlite qt6-sql-sqlite
qt6-linguist-devel qt6-linguist-devel
qt6-openglwidgets-devel
gtest gtest
gmock gmock
sparsehash-devel sparsehash-devel
@@ -97,7 +99,7 @@ jobs:
cmake --build build --config Release --parallel 4 cmake --build build --config Release --parallel 4
cmake --install build cmake --install build
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -134,14 +136,14 @@ jobs:
run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT run: echo "subdir=$(echo ${{matrix.opensuse_version}} | sed 's/leap:/lp/g' | sed 's/\.//g')" > $GITHUB_OUTPUT
- name: Upload source - name: Upload source
if: matrix.opensuse_version == 'tumbleweed' if: matrix.opensuse_version == 'tumbleweed'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: source name: source
path: | path: |
/usr/src/packages/SOURCES/*.xz /usr/src/packages/SOURCES/*.xz
- name: Upload rpm - name: Upload rpm
if: matrix.opensuse_version != 'tumbleweed' if: matrix.opensuse_version != 'tumbleweed'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: opensuse-${{steps.set-subdir.outputs.subdir}} name: opensuse-${{steps.set-subdir.outputs.subdir}}
path: | path: |
@@ -200,6 +202,7 @@ jobs:
libchromaprint-devel libchromaprint-devel
libebur128-devel libebur128-devel
fftw-devel fftw-devel
libprojectM-devel
desktop-file-utils desktop-file-utils
libappstream-glib libappstream-glib
hicolor-icon-theme hicolor-icon-theme
@@ -209,7 +212,7 @@ jobs:
sparsehash-devel sparsehash-devel
rapidjson-devel rapidjson-devel
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -234,7 +237,7 @@ jobs:
working-directory: build working-directory: build
run: rpmbuild -ba strawberry.spec run: rpmbuild -ba strawberry.spec
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: fedora-${{matrix.fedora_version}} name: fedora-${{matrix.fedora_version}}
path: | path: |
@@ -290,6 +293,7 @@ jobs:
lib64Qt6DBus-devel lib64Qt6DBus-devel
lib64Qt6Gui-devel lib64Qt6Gui-devel
lib64Qt6Widgets-devel lib64Qt6Widgets-devel
lib64Qt6OpenGLWidgets-devel
lib64Qt6Test-devel lib64Qt6Test-devel
lib64kdsingleapplication-devel lib64kdsingleapplication-devel
lib64xkbcommon-devel lib64xkbcommon-devel
@@ -307,7 +311,7 @@ jobs:
- name: Remove files - name: Remove files
run: rm -rf /usr/lib64/qt6/lib/cmake/Qt6Sql/{Qt6QMYSQL*,Qt6QODBCD*,Qt6QPSQL*,Qt6QIBase*} run: rm -rf /usr/lib64/qt6/lib/cmake/Qt6Sql/{Qt6QMYSQL*,Qt6QODBCD*,Qt6QPSQL*,Qt6QIBase*}
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -333,7 +337,7 @@ jobs:
run: rpmbuild -ba strawberry.spec run: rpmbuild -ba strawberry.spec
- name: Upload artifacts - name: Upload artifacts
if: matrix.openmandriva_version != 'cooker' if: matrix.openmandriva_version != 'cooker'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: openmandriva-${{matrix.openmandriva_version}} name: openmandriva-${{matrix.openmandriva_version}}
path: | path: |
@@ -384,6 +388,7 @@ jobs:
lib64fftw-devel lib64fftw-devel
lib64dbus-devel lib64dbus-devel
lib64appstream-devel lib64appstream-devel
lib64projectm-devel
lib64qt6core-devel lib64qt6core-devel
lib64qt6gui-devel lib64qt6gui-devel
lib64qt6widgets-devel lib64qt6widgets-devel
@@ -393,6 +398,7 @@ jobs:
lib64qt6dbus-devel lib64qt6dbus-devel
lib64qt6help-devel lib64qt6help-devel
lib64qt6test-devel lib64qt6test-devel
lib64qt6openglwidgets-devel
lib64sparsehash-devel lib64sparsehash-devel
lib64kdsingleapplication-devel lib64kdsingleapplication-devel
desktop-file-utils desktop-file-utils
@@ -409,7 +415,7 @@ jobs:
cmake --build build --config Release --parallel 4 cmake --build build --config Release --parallel 4
cmake --install build cmake --install build
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -434,7 +440,7 @@ jobs:
working-directory: build working-directory: build
run: rpmbuild -ba strawberry.spec run: rpmbuild -ba strawberry.spec
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: mageia-${{matrix.mageia_version}} name: mageia-${{matrix.mageia_version}}
path: | path: |
@@ -499,6 +505,7 @@ jobs:
qt6-tools-dev-tools qt6-tools-dev-tools
qt6-l10n-tools qt6-l10n-tools
rapidjson-dev rapidjson-dev
libprojectm-dev
- name: Install KDSingleApplication - name: Install KDSingleApplication
if: matrix.debian_version != 'bookworm' if: matrix.debian_version != 'bookworm'
run: apt install -y libkdsingleapplication-qt6-dev run: apt install -y libkdsingleapplication-qt6-dev
@@ -511,7 +518,7 @@ jobs:
cmake --build build --config Release --parallel 4 cmake --build build --config Release --parallel 4
cmake --install build cmake --install build
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -528,7 +535,7 @@ jobs:
- name: Copy deb - name: Copy deb
run: cp ../*.deb . run: cp ../*.deb .
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: debian-${{matrix.debian_version}} name: debian-${{matrix.debian_version}}
path: | path: |
@@ -595,6 +602,7 @@ jobs:
qt6-tools-dev-tools qt6-tools-dev-tools
qt6-l10n-tools qt6-l10n-tools
rapidjson-dev rapidjson-dev
libprojectm-dev
- name: Install KDSingleApplication - name: Install KDSingleApplication
if: matrix.ubuntu_version != 'noble' && matrix.ubuntu_version != 'plucky' if: matrix.ubuntu_version != 'noble' && matrix.ubuntu_version != 'plucky'
run: apt install -y libkdsingleapplication-qt6-dev run: apt install -y libkdsingleapplication-qt6-dev
@@ -607,7 +615,7 @@ jobs:
cmake --build build --config Release --parallel 4 cmake --build build --config Release --parallel 4
cmake --install build cmake --install build
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -624,7 +632,7 @@ jobs:
- name: Copy deb - name: Copy deb
run: cp ../*.deb ../*.ddeb . run: cp ../*.deb ../*.ddeb .
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: ubuntu-${{matrix.ubuntu_version}} name: ubuntu-${{matrix.ubuntu_version}}
path: | path: |
@@ -693,13 +701,14 @@ jobs:
gstreamer1.0-pulseaudio gstreamer1.0-pulseaudio
libkdsingleapplication-qt6-dev libkdsingleapplication-qt6-dev
rapidjson-dev rapidjson-dev
libprojectm-dev
- name: Install keyboxd - name: Install keyboxd
if: matrix.ubuntu_version == 'noble' if: matrix.ubuntu_version == 'noble'
env: env:
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
run: apt install -y keyboxd run: apt install -y keyboxd
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -735,16 +744,22 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
- name: Free disk space
run: |
df -h
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
sudo apt-get clean
df -h
- name: Build FreeBSD - name: Build FreeBSD
id: build-freebsd id: build-freebsd
uses: vmactions/freebsd-vm@v1.2.4 uses: vmactions/freebsd-vm@v1.3.2
with: with:
usesh: true usesh: true
mem: 4096 mem: 8192
prepare: pkg install -y git cmake pkgconf boost-libs alsa-lib glib qt6-base qt6-tools sqlite gstreamer1 gstreamer1-plugins chromaprint libebur128 taglib libcdio libmtp gdk-pixbuf2 libgpod fftw3 icu kdsingleapplication googletest pulseaudio sparsehash rapidjson prepare: pkg install -y git cmake pkgconf boost-libs alsa-lib glib qt6-base qt6-tools sqlite gstreamer1 gstreamer1-plugins chromaprint libebur128 taglib libcdio libmtp gdk-pixbuf2 libgpod fftw3 icu kdsingleapplication googletest pulseaudio sparsehash rapidjson
run: | run: |
set -e set -e
@@ -760,13 +775,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
- name: Build OpenBSD - name: Build OpenBSD
id: build-openbsd id: build-openbsd
uses: vmactions/openbsd-vm@v1.2.1 uses: vmactions/openbsd-vm@v1.2.9
with: with:
usesh: true usesh: true
mem: 4096 mem: 4096
@@ -787,7 +802,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
runner: [ 'macos-13', 'macos-15' ] runner: [ 'macos-15-intel', 'macos-15' ]
buildtype: [ 'release' ] buildtype: [ 'release' ]
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
@@ -826,20 +841,20 @@ jobs:
rm -f uninstall.sh rm -f uninstall.sh
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
- name: Import certificate file - name: Import certificate file
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false
uses: apple-actions/import-codesign-certs@v5 uses: apple-actions/import-codesign-certs@v6
with: with:
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE }} p12-file-base64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }} p12-password: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }}
- name: Download macOS dependencies - name: Download macOS dependencies
run: curl -f -O -L https://github.com/strawberrymusicplayer/strawberry-macos-dependencies/releases/latest/download/strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz run: curl -f -O -L https://github.com/strawberrymusicplayer/strawberry-macos-dependencies$(test "${{env.arch}}" = "x86_64" && echo "-intel" || echo "")/releases/latest/download/strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz
- name: Extract macOS dependencies - name: Extract macOS dependencies
run: sudo tar -C / -xf strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz run: sudo tar -C / -xf strawberry-macos-${{env.arch}}-${{env.buildtype}}.tar.xz
@@ -890,9 +905,9 @@ jobs:
run: make deploy run: make deploy
- name: Manually Codesign - name: Manually Codesign
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-13' if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-15-intel'
working-directory: build working-directory: build
run: codesign -s 383J84DVB6 -f strawberry.app/Contents/Frameworks/{libpcre2-8.0.dylib,libpcre2-16.0.dylib,libpng16.16.dylib,libfreetype.6.dylib,libzstd.1.dylib} strawberry.app/Contents/Frameworks/png.framework/png strawberry.app run: codesign -s 383J84DVB6 -f strawberry.app/Contents/Frameworks/{libpcre2-8.0.dylib,libpcre2-16.0.dylib,libpng16.16.dylib,libfreetype.6.dylib,libzstd.1.dylib,libbrotlicommon.1.dylib,libbrotlienc.1.dylib} strawberry.app/Contents/Frameworks/png.framework/png strawberry.app
- name: Manually Codesign - name: Manually Codesign
if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-15' if: github.repository == 'strawberrymusicplayer/strawberry' && github.event.pull_request.head.repo.fork == false && matrix.runner == 'macos-15'
@@ -977,7 +992,7 @@ jobs:
run: echo "cmake_buildtype=$(echo ${{env.buildtype}} | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}')" >> $GITHUB_ENV run: echo "cmake_buildtype=$(echo ${{env.buildtype}} | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}')" >> $GITHUB_ENV
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -1080,7 +1095,7 @@ jobs:
run: echo "cmake_buildtype=$(echo ${{matrix.buildtype}} | sed 's/.*/\u&/')" >> $GITHUB_ENV run: echo "cmake_buildtype=$(echo ${{matrix.buildtype}} | sed 's/.*/\u&/')" >> $GITHUB_ENV
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -1310,7 +1325,7 @@ jobs:
- name: Set SDK version - name: Set SDK version
if: matrix.arch != 'arm64' if: matrix.arch != 'arm64'
shell: bash shell: bash
run: echo "sdk_version=10.0.19041.0" >> $GITHUB_ENV run: echo "sdk_version=10.0.22621.0" >> $GITHUB_ENV
- name: Set SDK version - name: Set SDK version
if: matrix.arch == 'arm64' if: matrix.arch == 'arm64'
@@ -1321,10 +1336,6 @@ jobs:
shell: cmd shell: cmd
run: choco install --no-progress rsync run: choco install --no-progress rsync
- name: Set SSH command
shell: bash
run: echo "ssh_command=/c/ProgramData/chocolatey/lib/rsync/tools/$(ls -1 /c/ProgramData/chocolatey/lib/rsync/tools/ | grep -v '\.zip$' | grep '^cwrsync' | tail -1)/bin/ssh.exe" >> $GITHUB_ENV
- name: Cleanup PATH - name: Cleanup PATH
uses: egor-tensin/cleanup-path@v4 uses: egor-tensin/cleanup-path@v4
with: with:
@@ -1409,7 +1420,7 @@ jobs:
vsversion: 2022 vsversion: 2022
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
@@ -1623,7 +1634,7 @@ jobs:
- name: rsync - name: rsync
if: steps.set-upload-path.outputs.upload_path != '' if: steps.set-upload-path.outputs.upload_path != ''
shell: bash shell: bash
run: rsync -e "${{env.ssh_command}} -p ${{secrets.SSH_PORT}} -o StrictHostKeyChecking=no" -var build/StrawberrySetup*.exe ${{secrets.SSH_USER}}@${{secrets.SSH_HOST}}:${{steps.set-upload-path.outputs.upload_path}}/ run: rsync -e "/c/ProgramData/chocolatey/lib/rsync/tools/bin/ssh.exe -p ${{secrets.SSH_PORT}} -o StrictHostKeyChecking=no" -var build/StrawberrySetup*.exe ${{secrets.SSH_USER}}@${{secrets.SSH_HOST}}:${{steps.set-upload-path.outputs.upload_path}}/
upload: upload:
@@ -1642,11 +1653,11 @@ jobs:
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
run: sudo apt install -y git rsync run: sudo apt install -y git rsync
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v5 uses: actions/download-artifact@v7
with: with:
path: artifacts path: artifacts
- name: SSH Setup - name: SSH Setup
@@ -1690,7 +1701,7 @@ jobs:
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
run: sudo apt install -y git jq gh run: sudo apt install -y git jq gh
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Show release assets - name: Show release assets
@@ -1698,7 +1709,7 @@ jobs:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: gh release view "${{github.event.release.tag_name}}" --json assets | jq -r '.assets[].name' run: gh release view "${{github.event.release.tag_name}}" --json assets | jq -r '.assets[].name'
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v5 uses: actions/download-artifact@v7
with: with:
path: artifacts path: artifacts
- name: Add artifacts to release - name: Add artifacts to release

2
.gitignore vendored
View File

@@ -1,6 +1,7 @@
/build /build
/bin /bin
/CMakeLists.txt.user /CMakeLists.txt.user
/.qtcreator
/.kdev4 /.kdev4
/strawberry.kdev4 /strawberry.kdev4
/.vscode /.vscode
@@ -12,3 +13,4 @@
/CMakeSettings.json /CMakeSettings.json
/dist/scripts/maketarball.sh /dist/scripts/maketarball.sh
/debian/changelog /debian/changelog
_codeql_detected_source_root

View File

@@ -35,7 +35,7 @@ int GetProcessId();
struct BaseConnection { struct BaseConnection {
static BaseConnection *Create(); static BaseConnection *Create();
static void Destroy(BaseConnection *&); static void Destroy(BaseConnection*&);
bool isOpen = false; bool isOpen = false;
bool Open(); bool Open();
bool Close(); bool Close();

View File

@@ -39,11 +39,11 @@ int GetProcessId() {
} }
struct BaseConnectionUnix : public BaseConnection { struct BaseConnectionUnix : public BaseConnection {
int sock { -1 }; int sock{ -1 };
}; };
static BaseConnectionUnix Connection; static BaseConnectionUnix Connection;
static sockaddr_un PipeAddr {}; static sockaddr_un PipeAddr{};
#ifdef MSG_NOSIGNAL #ifdef MSG_NOSIGNAL
static int MsgFlags = MSG_NOSIGNAL; static int MsgFlags = MSG_NOSIGNAL;
#else #else
@@ -105,7 +105,7 @@ bool BaseConnection::Open() {
bool BaseConnection::Close() { bool BaseConnection::Close() {
auto self = reinterpret_cast<BaseConnectionUnix *>(this); auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) { if (self->sock == -1) {
return false; return false;
} }

View File

@@ -38,7 +38,7 @@ int GetProcessId() {
} }
struct BaseConnectionWin : public BaseConnection { struct BaseConnectionWin : public BaseConnection {
HANDLE pipe { INVALID_HANDLE_VALUE }; HANDLE pipe{ INVALID_HANDLE_VALUE };
}; };
static BaseConnectionWin Connection; static BaseConnectionWin Connection;
@@ -57,10 +57,10 @@ void BaseConnection::Destroy(BaseConnection *&c) {
bool BaseConnection::Open() { bool BaseConnection::Open() {
wchar_t pipeName[] { L"\\\\?\\pipe\\discord-ipc-0" }; wchar_t pipeName[]{ L"\\\\?\\pipe\\discord-ipc-0" };
const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2; const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2;
pipeName[pipeDigit] = L'0'; pipeName[pipeDigit] = L'0';
auto self = reinterpret_cast<BaseConnectionWin *>(this); auto self = reinterpret_cast<BaseConnectionWin*>(this);
for (;;) { for (;;) {
self->pipe = ::CreateFileW(pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); self->pipe = ::CreateFileW(pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (self->pipe != INVALID_HANDLE_VALUE) { if (self->pipe != INVALID_HANDLE_VALUE) {
@@ -88,7 +88,7 @@ bool BaseConnection::Open() {
bool BaseConnection::Close() { bool BaseConnection::Close() {
auto self = reinterpret_cast<BaseConnectionWin *>(this); auto self = reinterpret_cast<BaseConnectionWin*>(this);
::CloseHandle(self->pipe); ::CloseHandle(self->pipe);
self->pipe = INVALID_HANDLE_VALUE; self->pipe = INVALID_HANDLE_VALUE;
self->isOpen = false; self->isOpen = false;
@@ -102,7 +102,7 @@ bool BaseConnection::Write(const void *data, size_t length) {
if (length == 0) { if (length == 0) {
return true; return true;
} }
auto self = reinterpret_cast<BaseConnectionWin *>(this); auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self); assert(self);
if (!self) { if (!self) {
return false; return false;
@@ -127,7 +127,7 @@ bool BaseConnection::Read(void *data, size_t length) {
if (!data) { if (!data) {
return false; return false;
} }
auto self = reinterpret_cast<BaseConnectionWin *>(this); auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self); assert(self);
if (!self) { if (!self) {
return false; return false;

View File

@@ -34,9 +34,9 @@ namespace discord_rpc {
template<typename ElementType, std::size_t QueueSize> template<typename ElementType, std::size_t QueueSize>
class MsgQueue { class MsgQueue {
ElementType queue_[QueueSize]; ElementType queue_[QueueSize];
std::atomic_uint nextAdd_ { 0 }; std::atomic_uint nextAdd_{ 0 };
std::atomic_uint nextSend_ { 0 }; std::atomic_uint nextSend_{ 0 };
std::atomic_uint pendingSends_ { 0 }; std::atomic_uint pendingSends_{ 0 };
public: public:
MsgQueue() {} MsgQueue() {}

View File

@@ -163,4 +163,3 @@ extern "C" void Discord_Register(const char *applicationId, const char *command)
Discord_RegisterW(appId, wcommand); Discord_RegisterW(appId, wcommand);
} }

View File

@@ -40,9 +40,9 @@ static void Discord_UpdateConnection();
namespace { namespace {
constexpr size_t MaxMessageSize { 16 * 1024 }; constexpr size_t MaxMessageSize{ 16 * 1024 };
constexpr size_t MessageQueueSize { 8 }; constexpr size_t MessageQueueSize{ 8 };
constexpr size_t JoinQueueSize { 8 }; constexpr size_t JoinQueueSize{ 8 };
struct QueuedMessage { struct QueuedMessage {
size_t length; size_t length;
@@ -70,24 +70,24 @@ struct User {
// Rounded way up because I'm paranoid about games breaking from future changes in these sizes // Rounded way up because I'm paranoid about games breaking from future changes in these sizes
}; };
static RpcConnection *Connection { nullptr }; static RpcConnection *Connection{ nullptr };
static DiscordEventHandlers QueuedHandlers {}; static DiscordEventHandlers QueuedHandlers{};
static DiscordEventHandlers Handlers {}; static DiscordEventHandlers Handlers{};
static std::atomic_bool WasJustConnected { false }; static std::atomic_bool WasJustConnected{ false };
static std::atomic_bool WasJustDisconnected { false }; static std::atomic_bool WasJustDisconnected{ false };
static std::atomic_bool GotErrorMessage { false }; static std::atomic_bool GotErrorMessage{ false };
static std::atomic_bool WasJoinGame { false }; static std::atomic_bool WasJoinGame{ false };
static std::atomic_bool WasSpectateGame { false }; static std::atomic_bool WasSpectateGame{ false };
static std::atomic_bool UpdatePresence { false }; static std::atomic_bool UpdatePresence{ false };
static char JoinGameSecret[256]; static char JoinGameSecret[256];
static char SpectateGameSecret[256]; static char SpectateGameSecret[256];
static int LastErrorCode { 0 }; static int LastErrorCode{ 0 };
static char LastErrorMessage[256]; static char LastErrorMessage[256];
static int LastDisconnectErrorCode { 0 }; static int LastDisconnectErrorCode{ 0 };
static char LastDisconnectErrorMessage[256]; static char LastDisconnectErrorMessage[256];
static std::mutex PresenceMutex; static std::mutex PresenceMutex;
static std::mutex HandlerMutex; static std::mutex HandlerMutex;
static QueuedMessage QueuedPresence {}; static QueuedMessage QueuedPresence{};
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue; static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
static MsgQueue<User, JoinQueueSize> JoinAskQueue; static MsgQueue<User, JoinQueueSize> JoinAskQueue;
static User connectedUser; static User connectedUser;
@@ -95,12 +95,12 @@ 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 // 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 Backoff ReconnectTimeMs(500, 60 * 1000);
static auto NextConnect = std::chrono::system_clock::now(); static auto NextConnect = std::chrono::system_clock::now();
static int Pid { 0 }; static int Pid{ 0 };
static int Nonce { 1 }; static int Nonce{ 1 };
class IoThreadHolder { class IoThreadHolder {
private: private:
std::atomic_bool keepRunning { true }; std::atomic_bool keepRunning{ true };
std::mutex waitForIOMutex; std::mutex waitForIOMutex;
std::condition_variable waitForIOActivity; std::condition_variable waitForIOActivity;
std::thread ioThread; std::thread ioThread;
@@ -109,14 +109,14 @@ class IoThreadHolder {
void Start() { void Start() {
keepRunning.store(true); keepRunning.store(true);
ioThread = std::thread([&]() { ioThread = std::thread([&]() {
const std::chrono::duration<int64_t, std::milli> maxWait { 500LL }; 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(); 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 Notify() { waitForIOActivity.notify_all(); }
@@ -132,7 +132,7 @@ class IoThreadHolder {
~IoThreadHolder() { Stop(); } ~IoThreadHolder() { Stop(); }
}; };
static IoThreadHolder *IoThread { nullptr }; static IoThreadHolder *IoThread{ nullptr };
static void UpdateReconnectTime() { static void UpdateReconnectTime() {
@@ -429,7 +429,7 @@ extern "C" void Discord_RunCallbacks() {
if (WasJustConnected.exchange(false)) { if (WasJustConnected.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex); std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.ready) { if (Handlers.ready) {
DiscordUser du { connectedUser.userId, connectedUser.username, connectedUser.discriminator, connectedUser.avatar }; DiscordUser du{ connectedUser.userId, connectedUser.username, connectedUser.discriminator, connectedUser.avatar };
Handlers.ready(&du); Handlers.ready(&du);
} }
} }
@@ -465,7 +465,7 @@ extern "C" void Discord_RunCallbacks() {
{ {
std::lock_guard<std::mutex> guard(HandlerMutex); std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.joinRequest) { if (Handlers.joinRequest) {
DiscordUser du { req->userId, req->username, req->discriminator, req->avatar }; DiscordUser du{ req->userId, req->username, req->discriminator, req->avatar };
Handlers.joinRequest(&du); Handlers.joinRequest(&du);
} }
} }
@@ -486,12 +486,12 @@ extern "C" void Discord_UpdateHandlers(DiscordEventHandlers *newHandlers) {
if (newHandlers) { if (newHandlers) {
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \ #define HANDLE_EVENT_REGISTRATION(handler_name, event) \
if (!Handlers.handler_name && newHandlers->handler_name) { \ if (!Handlers.handler_name && newHandlers->handler_name) { \
RegisterForEvent(event); \ RegisterForEvent(event); \
} \ } \
else if (Handlers.handler_name && !newHandlers->handler_name) { \ else if (Handlers.handler_name && !newHandlers->handler_name) { \
DeregisterForEvent(event); \ DeregisterForEvent(event); \
} }
std::lock_guard<std::mutex> guard(HandlerMutex); std::lock_guard<std::mutex> guard(HandlerMutex);
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN") HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")

View File

@@ -63,17 +63,17 @@ struct RpcConnection {
Connected, Connected,
}; };
BaseConnection *connection { nullptr }; BaseConnection *connection{ nullptr };
State state { State::Disconnected }; State state{ State::Disconnected };
void (*onConnect)(JsonDocument &message) { nullptr }; void (*onConnect)(JsonDocument &message) { nullptr };
void (*onDisconnect)(int errorCode, const char *message) { nullptr }; void (*onDisconnect)(int errorCode, const char *message) { nullptr };
char appId[64] {}; char appId[64]{};
int lastErrorCode { 0 }; int lastErrorCode{ 0 };
char lastErrorMessage[256] {}; char lastErrorMessage[256]{};
RpcConnection::MessageFrame sendFrame; RpcConnection::MessageFrame sendFrame;
static RpcConnection *Create(const char *applicationId); static RpcConnection *Create(const char *applicationId);
static void Destroy(RpcConnection *&); static void Destroy(RpcConnection*&);
inline bool IsOpen() const { return state == State::Connected; } inline bool IsOpen() const { return state == State::Connected; }

View File

@@ -208,6 +208,15 @@ else()
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib>=1.12) pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib>=1.12)
endif() endif()
find_package(projectM4 COMPONENTS Playlist)
if(projectM4_FOUND)
set(LIBPROJECTM_FOUND ON)
set(HAVE_PROJECTM4 ON)
set(LIBPROJECTM_LIBRARIES libprojectM::projectM libprojectM::playlist)
else()
pkg_check_modules(LIBPROJECTM libprojectM)
endif()
find_package(GTest) find_package(GTest)
pkg_check_modules(LIBSPARSEHASH IMPORTED_TARGET libsparsehash) pkg_check_modules(LIBSPARSEHASH IMPORTED_TARGET libsparsehash)
@@ -218,7 +227,7 @@ set(QT_VERSION_MAJOR 6)
set(QT_MIN_VERSION 6.4.0) set(QT_MIN_VERSION 6.4.0)
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR}) set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR})
set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql) set(QT_COMPONENTS Core Concurrent Gui Widgets Network Sql)
set(QT_OPTIONAL_COMPONENTS GuiPrivate LinguistTools Test) set(QT_OPTIONAL_COMPONENTS GuiPrivate OpenGLWidgets LinguistTools Test)
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
list(APPEND QT_OPTIONAL_COMPONENTS DBus) list(APPEND QT_OPTIONAL_COMPONENTS DBus)
endif() endif()
@@ -266,6 +275,8 @@ if(WIN32)
set(GETOPT_LIBRARIES getopt-win::getopt) set(GETOPT_LIBRARIES getopt-win::getopt)
elseif(TARGET getopt::getopt_shared) elseif(TARGET getopt::getopt_shared)
set(GETOPT_LIBRARIES getopt::getopt_shared) set(GETOPT_LIBRARIES getopt::getopt_shared)
elseif(TARGET unofficial::getopt-win32::getopt)
set(GETOPT_LIBRARIES unofficial::getopt-win32::getopt)
else() else()
message(FATAL_ERROR "Missing getopt") message(FATAL_ERROR "Missing getopt")
endif() endif()
@@ -387,6 +398,11 @@ if(HAVE_X11_GLOBALSHORTCUTS OR HAVE_KGLOBALACCEL_GLOBALSHORTCUTS OR APPLE OR WIN
set(HAVE_GLOBALSHORTCUTS ON) set(HAVE_GLOBALSHORTCUTS ON)
endif() endif()
optional_component(VISUALIZATIONS ON "Visualizations"
DEPENDS "libprojectm" LIBPROJECTM_FOUND
DEPENDS "QtOpenGLWidgets" Qt${QT_VERSION_MAJOR}OpenGLWidgets_FOUND
)
if(NOT CMAKE_CROSSCOMPILING) if(NOT CMAKE_CROSSCOMPILING)
# Check that we have Qt with sqlite driver # Check that we have Qt with sqlite driver
set(CMAKE_REQUIRED_FLAGS "-std=c++17") set(CMAKE_REQUIRED_FLAGS "-std=c++17")
@@ -699,6 +715,7 @@ set(SOURCES
src/lyrics/elyricsnetlyricsprovider.cpp src/lyrics/elyricsnetlyricsprovider.cpp
src/lyrics/letraslyricsprovider.cpp src/lyrics/letraslyricsprovider.cpp
src/lyrics/lyricfindlyricsprovider.cpp src/lyrics/lyricfindlyricsprovider.cpp
src/lyrics/lrcliblyricsprovider.cpp
src/settings/settingsdialog.cpp src/settings/settingsdialog.cpp
src/settings/settingspage.cpp src/settings/settingspage.cpp
@@ -995,6 +1012,7 @@ set(HEADERS
src/lyrics/elyricsnetlyricsprovider.h src/lyrics/elyricsnetlyricsprovider.h
src/lyrics/letraslyricsprovider.h src/lyrics/letraslyricsprovider.h
src/lyrics/lyricfindlyricsprovider.h src/lyrics/lyricfindlyricsprovider.h
src/lyrics/lrcliblyricsprovider.h
src/settings/settingsdialog.h src/settings/settingsdialog.h
src/settings/settingspage.h src/settings/settingspage.h
@@ -1459,6 +1477,7 @@ optional_source(HAVE_QOBUZ
src/qobuz/qobuzrequest.cpp src/qobuz/qobuzrequest.cpp
src/qobuz/qobuzstreamurlrequest.cpp src/qobuz/qobuzstreamurlrequest.cpp
src/qobuz/qobuzfavoriterequest.cpp src/qobuz/qobuzfavoriterequest.cpp
src/qobuz/qobuzcredentialfetcher.cpp
src/settings/qobuzsettingspage.cpp src/settings/qobuzsettingspage.cpp
src/covermanager/qobuzcoverprovider.cpp src/covermanager/qobuzcoverprovider.cpp
HEADERS HEADERS
@@ -1468,12 +1487,33 @@ optional_source(HAVE_QOBUZ
src/qobuz/qobuzrequest.h src/qobuz/qobuzrequest.h
src/qobuz/qobuzstreamurlrequest.h src/qobuz/qobuzstreamurlrequest.h
src/qobuz/qobuzfavoriterequest.h src/qobuz/qobuzfavoriterequest.h
src/qobuz/qobuzcredentialfetcher.h
src/settings/qobuzsettingspage.h src/settings/qobuzsettingspage.h
src/covermanager/qobuzcoverprovider.h src/covermanager/qobuzcoverprovider.h
UI UI
src/settings/qobuzsettingspage.ui src/settings/qobuzsettingspage.ui
) )
optional_source(HAVE_VISUALIZATIONS
SOURCES
src/visualizations/projectmpresetmodel.cpp
src/visualizations/projectmvisualization.cpp
src/visualizations/visualizationcontainer.cpp
src/visualizations/visualizationoverlay.cpp
src/visualizations/visualizationselector.cpp
src/visualizations/visualizationopenglwidget.cpp
HEADERS
src/visualizations/projectmpresetmodel.h
src/visualizations/projectmvisualization.h
src/visualizations/visualizationcontainer.h
src/visualizations/visualizationoverlay.h
src/visualizations/visualizationselector.h
src/visualizations/visualizationopenglwidget.h
UI
src/visualizations/visualizationoverlay.ui
src/visualizations/visualizationselector.ui
)
qt_wrap_cpp(SOURCES ${HEADERS}) qt_wrap_cpp(SOURCES ${HEADERS})
qt_wrap_ui(SOURCES ${UI}) qt_wrap_ui(SOURCES ${UI})
qt_add_resources(SOURCES data/data.qrc data/icons.qrc) qt_add_resources(SOURCES data/data.qrc data/icons.qrc)
@@ -1544,6 +1584,7 @@ target_link_libraries(strawberry_lib PUBLIC
Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Sql
$<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus> $<$<BOOL:${HAVE_DBUS}>:Qt${QT_VERSION_MAJOR}::DBus>
$<$<BOOL:${HAVE_QPA_QPLATFORMNATIVEINTERFACE}>:Qt${QT_VERSION_MAJOR}::GuiPrivate> $<$<BOOL:${HAVE_QPA_QPLATFORMNATIVEINTERFACE}>:Qt${QT_VERSION_MAJOR}::GuiPrivate>
$<$<BOOL:${HAVE_VISUALIZATIONS}>:Qt${QT_VERSION_MAJOR}::OpenGLWidgets>
ICU::uc ICU::uc
ICU::i18n ICU::i18n
$<$<BOOL:${HAVE_STREAMTAGREADER}>:PkgConfig::LIBSPARSEHASH> $<$<BOOL:${HAVE_STREAMTAGREADER}>:PkgConfig::LIBSPARSEHASH>
@@ -1559,6 +1600,7 @@ target_link_libraries(strawberry_lib PUBLIC
$<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP> $<$<BOOL:${HAVE_MTP}>:PkgConfig::LIBMTP>
$<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF> $<$<BOOL:${HAVE_GPOD}>:PkgConfig::LIBGPOD PkgConfig::GDK_PIXBUF>
$<$<BOOL:${HAVE_QTSPARKLE}>:qtsparkle-qt${QT_VERSION_MAJOR}::qtsparkle> $<$<BOOL:${HAVE_QTSPARKLE}>:qtsparkle-qt${QT_VERSION_MAJOR}::qtsparkle>
$<$<BOOL:${HAVE_VISUALIZATIONS}>:${LIBPROJECTM_LIBRARIES}>
$<$<BOOL:${WIN32}>:dsound dwmapi ${GETOPT_LIBRARIES}> $<$<BOOL:${WIN32}>:dsound dwmapi ${GETOPT_LIBRARIES}>
$<$<BOOL:${MSVC}>:WindowsApp> $<$<BOOL:${MSVC}>:WindowsApp>
KDAB::kdsingleapplication KDAB::kdsingleapplication

View File

@@ -2,6 +2,31 @@ Strawberry Music Player
======================= =======================
ChangeLog 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): Version 1.2.14 (2025.10.25):
Bugfixes: Bugfixes:

View File

@@ -17,7 +17,9 @@ Its written in **C++ using the Qt framework**, designed for **audiophiles and
- **Forum:** https://forum.strawberrymusicplayer.org - **Forum:** https://forum.strawberrymusicplayer.org
- **GitHub:** https://github.com/strawberrymusicplayer/strawberry - **GitHub:** https://github.com/strawberrymusicplayer/strawberry
- **Latest builds:** https://builds.strawberrymusicplayer.org - **Latest builds:** https://builds.strawberrymusicplayer.org
- **openSUSE Build Service:** https://build.opensuse.org/package/show/home:jonaski:audio/strawberry - **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:** - **Ubuntu PPAs:**
- Stable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry - Stable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry
- Unstable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable - Unstable: https://launchpad.net/~jonaski/+archive/ubuntu/strawberry-unstable
@@ -64,7 +66,7 @@ Supporting open-source developers helps ensure continued maintenance and improve
- Loudness analysis and EBU R128 normalization - Loudness analysis and EBU R128 normalization
- Editing tags and fetching missing tags via [MusicBrainz](https://musicbrainz.org/) - 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/) - 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) - 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 - Audio analyzer and equalizer
- Transfer music to USB, MTP and iPod devices - Transfer music to USB, MTP and iPod devices
- Scrobbling to [Last.fm](https://www.last.fm/) and [ListenBrainz](https://listenbrainz.org/) - Scrobbling to [Last.fm](https://www.last.fm/) and [ListenBrainz](https://listenbrainz.org/)

View File

@@ -1,9 +1,9 @@
set(STRAWBERRY_VERSION_MAJOR 1) set(STRAWBERRY_VERSION_MAJOR 1)
set(STRAWBERRY_VERSION_MINOR 2) set(STRAWBERRY_VERSION_MINOR 2)
set(STRAWBERRY_VERSION_PATCH 14) set(STRAWBERRY_VERSION_PATCH 16)
#set(STRAWBERRY_VERSION_PRERELEASE rc1) #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(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")

5
debian/control vendored
View File

@@ -32,7 +32,8 @@ Build-Depends: debhelper-compat (= 12),
libfftw3-dev, libfftw3-dev,
libebur128-dev, libebur128-dev,
libsparsehash-dev, libsparsehash-dev,
rapidjson-dev rapidjson-dev,
libprojectm-dev
Standards-Version: 4.7.0 Standards-Version: 4.7.0
Package: strawberry Package: strawberry
@@ -60,7 +61,7 @@ Description: music player and music collection organizer
- Edit tags on audio files - Edit tags on audio files
- Automatically retrieve tags from MusicBrainz - Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify - Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind - Lyrics from multiple sources
- Audio analyzer - Audio analyzer
- Audio equalizer - Audio equalizer
- Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic - Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic

View File

@@ -31,7 +31,7 @@
<li>Edit tags on audio files</li> <li>Edit tags on audio files</li>
<li>Automatically retrieve tags from MusicBrainz</li> <li>Automatically retrieve tags from MusicBrainz</li>
<li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li> <li>Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify</li>
<li>Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind</li> <li>Lyrics from multiple sources</li>
<li>Audio analyzer and equalizer</li> <li>Audio analyzer and equalizer</li>
<li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li> <li>Transfer music to mass-storage USB players, MTP compatible devices and iPod Nano/Classic</li>
<li>Scrobbler with support for Last.fm and ListenBrainz</li> <li>Scrobbler with support for Last.fm and ListenBrainz</li>
@@ -51,6 +51,8 @@
</screenshots> </screenshots>
<update_contact>eclipseo@fedoraproject.org</update_contact> <update_contact>eclipseo@fedoraproject.org</update_contact>
<releases> <releases>
<release version="1.2.16" date="2025-12-16"/>
<release version="1.2.15" date="2025-11-25"/>
<release version="1.2.14" date="2025-10-25"/> <release version="1.2.14" date="2025-10-25"/>
<release version="1.2.13" date="2025-08-31"/> <release version="1.2.13" date="2025-08-31"/>
<release version="1.2.12" date="2025-08-12"/> <release version="1.2.12" date="2025-08-12"/>

View File

@@ -29,9 +29,7 @@ Features:
.br .br
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify - Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
.br .br
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind - Lyrics from multiple sources
.br
- Support for multiple backends
.br .br
- Audio analyzer - Audio analyzer
.br .br

View File

@@ -43,6 +43,7 @@ BuildRequires: pkgconfig(taglib)
BuildRequires: pkgconfig(fftw3) BuildRequires: pkgconfig(fftw3)
BuildRequires: pkgconfig(icu-uc) BuildRequires: pkgconfig(icu-uc)
BuildRequires: pkgconfig(icu-i18n) BuildRequires: pkgconfig(icu-i18n)
BuildRequires: pkgconfig(libprojectM)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core) BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Core)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent) BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Concurrent)
BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network) BuildRequires: cmake(Qt@QT_VERSION_MAJOR@Network)
@@ -93,8 +94,7 @@ Features:
- Edit tags on audio files - Edit tags on audio files
- Automatically retrieve tags from MusicBrainz - Automatically retrieve tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify - Album cover art from Last.fm, Musicbrainz, Discogs, Musixmatch, Deezer, Tidal, Qobuz and Spotify
- Song lyrics from Genius, Musixmatch, ChartLyrics, lyrics.ovh, lololyrics.com, songlyrics.com, azlyrics.com, elyrics.net, letras.mus.br and LyricFind - Lyrics from multiple sources
- Support for multiple backends
- Audio analyzer - Audio analyzer
- Audio equalizer - Audio equalizer
- Scrobbler with support for Last.fm and ListenBrainz - Scrobbler with support for Last.fm and ListenBrainz

View File

@@ -477,15 +477,15 @@ Section "Strawberry" Strawberry
; Common files ; Common files
File "icudt77.dll" File "icudt78.dll"
!ifdef msvc && arch_arm64 !ifdef msvc && arch_arm64
File "fftw3.dll" File "fftw3.dll"
!else !else
File "libfftw3-3.dll" File "libfftw3-3.dll"
!endif !endif
!ifdef msvc && debug !ifdef msvc && debug
File "icuin77d.dll" File "icuin78d.dll"
File "icuuc77d.dll" File "icuuc78d.dll"
File "libxml2d.dll" File "libxml2d.dll"
File "Qt6Concurrentd.dll" File "Qt6Concurrentd.dll"
File "Qt6Cored.dll" File "Qt6Cored.dll"
@@ -494,8 +494,8 @@ Section "Strawberry" Strawberry
File "Qt6Sqld.dll" File "Qt6Sqld.dll"
File "Qt6Widgetsd.dll" File "Qt6Widgetsd.dll"
!else !else
File "icuin77.dll" File "icuin78.dll"
File "icuuc77.dll" File "icuuc78.dll"
File "libxml2.dll" File "libxml2.dll"
File "Qt6Concurrent.dll" File "Qt6Concurrent.dll"
File "Qt6Core.dll" File "Qt6Core.dll"
@@ -505,6 +505,7 @@ Section "Strawberry" Strawberry
File "Qt6Widgets.dll" File "Qt6Widgets.dll"
!endif !endif
!ifdef msvc && arch_x86
File "avcodec-61.dll" File "avcodec-61.dll"
File "avfilter-10.dll" File "avfilter-10.dll"
File "avformat-61.dll" File "avformat-61.dll"
@@ -512,6 +513,14 @@ Section "Strawberry" Strawberry
File "postproc-58.dll" File "postproc-58.dll"
File "swresample-5.dll" File "swresample-5.dll"
File "swscale-8.dll" File "swscale-8.dll"
!else
File "avcodec-62.dll"
File "avfilter-11.dll"
File "avformat-62.dll"
File "avutil-60.dll"
File "swresample-6.dll"
File "swscale-9.dll"
!endif
; Register Strawberry with Default Programs ; Register Strawberry with Default Programs
Var /GLOBAL AppIcon Var /GLOBAL AppIcon
@@ -1019,15 +1028,15 @@ Section "Uninstall"
; Common files ; Common files
Delete "$INSTDIR\icudt77.dll" Delete "$INSTDIR\icudt78.dll"
!ifdef msvc && arch_arm64 !ifdef msvc && arch_arm64
Delete "$INSTDIR\fftw3.dll" Delete "$INSTDIR\fftw3.dll"
!else !else
Delete "$INSTDIR\libfftw3-3.dll" Delete "$INSTDIR\libfftw3-3.dll"
!endif !endif
!ifdef msvc && debug !ifdef msvc && debug
Delete "$INSTDIR\icuin77d.dll" Delete "$INSTDIR\icuin78d.dll"
Delete "$INSTDIR\icuuc77d.dll" Delete "$INSTDIR\icuuc78d.dll"
Delete "$INSTDIR\libxml2d.dll" Delete "$INSTDIR\libxml2d.dll"
Delete "$INSTDIR\Qt6Concurrentd.dll" Delete "$INSTDIR\Qt6Concurrentd.dll"
Delete "$INSTDIR\Qt6Cored.dll" Delete "$INSTDIR\Qt6Cored.dll"
@@ -1036,8 +1045,8 @@ Section "Uninstall"
Delete "$INSTDIR\Qt6Sqld.dll" Delete "$INSTDIR\Qt6Sqld.dll"
Delete "$INSTDIR\Qt6Widgetsd.dll" Delete "$INSTDIR\Qt6Widgetsd.dll"
!else !else
Delete "$INSTDIR\icuin77.dll" Delete "$INSTDIR\icuin78.dll"
Delete "$INSTDIR\icuuc77.dll" Delete "$INSTDIR\icuuc78.dll"
Delete "$INSTDIR\libxml2.dll" Delete "$INSTDIR\libxml2.dll"
Delete "$INSTDIR\Qt6Concurrent.dll" Delete "$INSTDIR\Qt6Concurrent.dll"
Delete "$INSTDIR\Qt6Core.dll" Delete "$INSTDIR\Qt6Core.dll"
@@ -1047,6 +1056,7 @@ Section "Uninstall"
Delete "$INSTDIR\Qt6Widgets.dll" Delete "$INSTDIR\Qt6Widgets.dll"
!endif !endif
!ifdef msvc && arch_x86
Delete "$INSTDIR\avcodec-61.dll" Delete "$INSTDIR\avcodec-61.dll"
Delete "$INSTDIR\avfilter-10.dll" Delete "$INSTDIR\avfilter-10.dll"
Delete "$INSTDIR\avformat-61.dll" Delete "$INSTDIR\avformat-61.dll"
@@ -1054,6 +1064,14 @@ Section "Uninstall"
Delete "$INSTDIR\postproc-58.dll" Delete "$INSTDIR\postproc-58.dll"
Delete "$INSTDIR\swresample-5.dll" Delete "$INSTDIR\swresample-5.dll"
Delete "$INSTDIR\swscale-8.dll" Delete "$INSTDIR\swscale-8.dll"
!else
Delete "$INSTDIR\avcodec-62.dll"
Delete "$INSTDIR\avfilter-11.dll"
Delete "$INSTDIR\avformat-62.dll"
Delete "$INSTDIR\avutil-60.dll"
Delete "$INSTDIR\swresample-6.dll"
Delete "$INSTDIR\swscale-9.dll"
!endif
!ifdef mingw !ifdef mingw
Delete "$INSTDIR\gio-modules\libgiognutls.dll" Delete "$INSTDIR\gio-modules\libgiognutls.dll"

View File

@@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h" #include "config.h"

View File

@@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANALYZERBASE_H #ifndef ANALYZERBASE_H
#define ANALYZERBASE_H #define ANALYZERBASE_H
@@ -90,4 +90,3 @@ class AnalyzerBase : public QWidget {
}; };
#endif // ANALYZERBASE_H #endif // ANALYZERBASE_H

View File

@@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h" #include "config.h"
@@ -61,7 +61,7 @@ constexpr int kLowFramerate = 20;
constexpr int kMediumFramerate = 25; constexpr int kMediumFramerate = 25;
constexpr int kHighFramerate = 30; constexpr int kHighFramerate = 30;
constexpr int kSuperHighFramerate = 60; constexpr int kSuperHighFramerate = 60;
} // namespace } // namespace
AnalyzerContainer::AnalyzerContainer(QWidget *parent) AnalyzerContainer::AnalyzerContainer(QWidget *parent)
: QWidget(parent), : QWidget(parent),
@@ -73,7 +73,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
double_click_timer_(new QTimer(this)), double_click_timer_(new QTimer(this)),
ignore_next_click_(false), ignore_next_click_(false),
current_analyzer_(nullptr), current_analyzer_(nullptr),
engine_(nullptr) { engine_(nullptr),
action_visualization_(nullptr) {
QHBoxLayout *layout = new QHBoxLayout(this); QHBoxLayout *layout = new QHBoxLayout(this);
setLayout(layout); setLayout(layout);
@@ -118,6 +119,17 @@ void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
} }
void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent *e) {
Q_UNUSED(e);
double_click_timer_->stop();
ignore_next_click_ = true;
if (action_visualization_) action_visualization_->trigger();
}
void AnalyzerContainer::ShowPopupMenu() { void AnalyzerContainer::ShowPopupMenu() {
context_menu_->popup(last_click_pos_); context_menu_->popup(last_click_pos_);
} }
@@ -249,3 +261,10 @@ void AnalyzerContainer::AddFramerate(const QString &name, const int framerate) {
QObject::connect(action, &QAction::triggered, this, [this, framerate]() { ChangeFramerate(framerate); } ); QObject::connect(action, &QAction::triggered, this, [this, framerate]() { ChangeFramerate(framerate); } );
} }
void AnalyzerContainer::SetVisualizationsAction(QAction *visualization) {
action_visualization_ = visualization;
context_menu_->addAction(action_visualization_);
}

View File

@@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANALYZERCONTAINER_H #ifndef ANALYZERCONTAINER_H
#define ANALYZERCONTAINER_H #define ANALYZERCONTAINER_H
@@ -46,6 +46,7 @@ class AnalyzerContainer : public QWidget {
explicit AnalyzerContainer(QWidget *parent); explicit AnalyzerContainer(QWidget *parent);
void SetEngine(SharedPtr<EngineBase> engine); void SetEngine(SharedPtr<EngineBase> engine);
void SetVisualizationsAction(QAction *visualization);
static const char *kSettingsGroup; static const char *kSettingsGroup;
static const char *kSettingsFramerate; static const char *kSettingsFramerate;
@@ -55,6 +56,7 @@ class AnalyzerContainer : public QWidget {
protected: protected:
void mouseReleaseEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
void wheelEvent(QWheelEvent *e) override; void wheelEvent(QWheelEvent *e) override;
private Q_SLOTS: private Q_SLOTS:
@@ -89,6 +91,8 @@ class AnalyzerContainer : public QWidget {
AnalyzerBase *current_analyzer_; AnalyzerBase *current_analyzer_;
SharedPtr<EngineBase> engine_; SharedPtr<EngineBase> engine_;
QAction *action_visualization_;
}; };
template<typename T> template<typename T>
@@ -101,8 +105,7 @@ void AnalyzerContainer::AddAnalyzerType() {
group_->addAction(action); group_->addAction(action);
action->setCheckable(true); action->setCheckable(true);
actions_ << action; actions_ << action;
QObject::connect(action, &QAction::triggered, [this, id]() { ChangeAnalyzer(id); } ); QObject::connect(action, &QAction::triggered, [this, id]() { ChangeAnalyzer(id); });
} }
#endif // ANALYZERCONTAINER_H #endif // ANALYZERCONTAINER_H

View File

@@ -19,7 +19,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "blockanalyzer.h" #include "blockanalyzer.h"
@@ -61,11 +61,12 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
fade_intensity_(1 << 8, 32), fade_intensity_(1 << 8, 32),
step_(0) { step_(0) {
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); // -1 is padding, no drawing takes place there
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1); setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
// mxcl says null pixmaps cause crashes, so let's play it safe // mxcl says null pixmaps cause crashes, so let's play it safe
std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(1, 1)); std::fill(fade_bars_.begin(), fade_bars_.end(), QPixmap(1, 1));
} }
void BlockAnalyzer::resizeEvent(QResizeEvent *e) { void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
@@ -237,7 +238,7 @@ static inline void adjustToLimits(const int b, int &f, int &amount) {
* Clever contrast function * Clever contrast function
* *
* It will try to adjust the foreground color such that it contrasts well with * It will try to adjust the foreground color such that it contrasts well with
*the background * the background
* It won't modify the hue of fg unless absolutely necessary * It won't modify the hue of fg unless absolutely necessary
* @return the adjusted form of fg * @return the adjusted form of fg
*/ */

View File

@@ -19,7 +19,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef BLOCKANALYZER_H #ifndef BLOCKANALYZER_H
#define BLOCKANALYZER_H #define BLOCKANALYZER_H
@@ -41,14 +41,14 @@ class BlockAnalyzer : public AnalyzerBase {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE explicit BlockAnalyzer(QWidget*); Q_INVOKABLE explicit BlockAnalyzer(QWidget *parent);
static const char *kName; static const char *kName;
protected: protected:
void transform(Scope&) override; void transform(Scope &s) override;
void analyze(QPainter &p, const Scope &s, const bool new_frame) override; void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
void resizeEvent(QResizeEvent*) override; void resizeEvent(QResizeEvent *e) override;
virtual void paletteChange(const QPalette &_palette); virtual void paletteChange(const QPalette &_palette);
void framerateChanged() override; void framerateChanged() override;

View File

@@ -20,7 +20,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "boomanalyzer.h" #include "boomanalyzer.h"
@@ -143,7 +143,7 @@ void BoomAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame
bar_height_[i] = std::max(0.0, bar_height_[i]); bar_height_[i] = std::max(0.0, bar_height_[i]);
} }
peak_handling: peak_handling:
if (peak_height_[i] > 0.0) { if (peak_height_[i] > 0.0) {
peak_height_[i] -= peak_speed_[i]; peak_height_[i] -= peak_speed_[i];

View File

@@ -20,7 +20,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef BOOMANALYZER_H #ifndef BOOMANALYZER_H
#define BOOMANALYZER_H #define BOOMANALYZER_H
@@ -40,7 +40,7 @@ class BoomAnalyzer : public AnalyzerBase {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE explicit BoomAnalyzer(QWidget*); Q_INVOKABLE explicit BoomAnalyzer(QWidget *parent);
static const char *kName; static const char *kName;
@@ -70,7 +70,6 @@ class BoomAnalyzer : public AnalyzerBase {
QPixmap barPixmap_; QPixmap barPixmap_;
QPixmap canvas_; QPixmap canvas_;
}; };
#endif // BOOMANALYZER_H #endif // BOOMANALYZER_H

View File

@@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "fht.h" #include "fht.h"

View File

@@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef FHT_H #ifndef FHT_H
#define FHT_H #define FHT_H
@@ -55,20 +55,20 @@ class FHT {
/** /**
* Recursive in-place Hartley transform. For internal use only! * Recursive in-place Hartley transform. For internal use only!
*/ */
void _transform(float*, int, int); void _transform(float *p, int n, int k);
public: public:
/** /**
* Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$ * Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$
* should be at least 3. Values of more than 3 need a trigonometry table. * should be at least 3. Values of more than 3 need a trigonometry table.
* @see makeCasTable() * @see makeCasTable()
*/ */
explicit FHT(uint); explicit FHT(uint);
~FHT(); ~FHT();
int sizeExp() const; int sizeExp() const;
int size() const; int size() const;
void scale(float*, float) const; void scale(float *p, float d) const;
/** /**
* Exponentially Weighted Moving Average (EWMA) filter. * Exponentially Weighted Moving Average (EWMA) filter.
@@ -90,12 +90,12 @@ class FHT {
/** /**
* Semi-logarithmic audio spectrum. * Semi-logarithmic audio spectrum.
*/ */
void semiLogSpectrum(float*); void semiLogSpectrum(float *p);
/** /**
* Fourier spectrum. * Fourier spectrum.
*/ */
void spectrum(float*); void spectrum(float *p);
/** /**
* Calculates a mathematically correct FFT power spectrum. * Calculates a mathematically correct FFT power spectrum.
@@ -103,7 +103,7 @@ class FHT {
* and factor the 0.5 in the final scaling factor. * and factor the 0.5 in the final scaling factor.
* @see FHT::power2() * @see FHT::power2()
*/ */
void power(float*); void power(float *p);
/** /**
* Calculates an FFT power spectrum with doubled values as a * Calculates an FFT power spectrum with doubled values as a
@@ -112,14 +112,14 @@ class FHT {
* of @f$2^n@f$ input values. This is the fastest transform. * of @f$2^n@f$ input values. This is the fastest transform.
* @see FHT::power() * @see FHT::power()
*/ */
void power2(float*); void power2(float *p);
/** /**
* Discrete Hartley transform of data sets with 8 values. * Discrete Hartley transform of data sets with 8 values.
*/ */
static void transform8(float*); static void transform8(float *p);
void transform(float*); void transform(float *p);
}; };
#endif // FHT_H #endif // FHT_H

View File

@@ -21,7 +21,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "rainbowanalyzer.h" #include "rainbowanalyzer.h"

View File

@@ -21,7 +21,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef RAINBOWANALYZER_H #ifndef RAINBOWANALYZER_H
#define RAINBOWANALYZER_H #define RAINBOWANALYZER_H
@@ -85,7 +85,7 @@ class RainbowAnalyzer : public AnalyzerBase {
private: private:
// "constants" that get initialized in the constructor // "constants" that get initialized in the constructor
float band_scale_[kRainbowBands] {}; float band_scale_[kRainbowBands]{};
QPen colors_[kRainbowBands]; QPen colors_[kRainbowBands];
// Rainbow Nyancat & Dash // Rainbow Nyancat & Dash
@@ -96,7 +96,7 @@ class RainbowAnalyzer : public AnalyzerBase {
int frame_; int frame_;
// The y positions of each point on the rainbow. // The y positions of each point on the rainbow.
float history_[kHistorySize * kRainbowBands] {}; float history_[kHistorySize * kRainbowBands]{};
// A cache of the last frame's rainbow, // A cache of the last frame's rainbow,
// so it can be used in the next frame. // so it can be used in the next frame.

View File

@@ -19,7 +19,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef SONOGRAMANALYZER_H #ifndef SONOGRAMANALYZER_H
#define SONOGRAMANALYZER_H #define SONOGRAMANALYZER_H

View File

@@ -20,7 +20,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>. along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h" #include "config.h"
@@ -70,7 +70,7 @@ void TurbineAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_fr
bar_height_[i] = std::max(0.0, bar_height_[i]); bar_height_[i] = std::max(0.0, bar_height_[i]);
} }
peak_handling: peak_handling:
if (peak_height_[i] > 0.0) { if (peak_height_[i] > 0.0) {
peak_height_[i] -= peak_speed_[i]; peak_height_[i] -= peak_speed_[i];
peak_speed_[i] *= F_peakSpeed_; // 1.12 peak_speed_[i] *= F_peakSpeed_; // 1.12

View File

@@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>. along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef TURBINEANALYZER_H #ifndef TURBINEANALYZER_H
#define TURBINEANALYZER_H #define TURBINEANALYZER_H

View File

@@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QPixmap> #include <QPixmap>

View File

@@ -140,7 +140,6 @@ class CollectionBackend : public CollectionBackendInterface {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr); Q_INVOKABLE explicit CollectionBackend(QObject *parent = nullptr);
~CollectionBackend(); ~CollectionBackend();
@@ -331,4 +330,3 @@ class CollectionBackend : public CollectionBackendInterface {
}; };
#endif // COLLECTIONBACKEND_H #endif // COLLECTIONBACKEND_H

View File

@@ -26,7 +26,6 @@
class CollectionFilterOptions { class CollectionFilterOptions {
public: public:
explicit CollectionFilterOptions(); explicit CollectionFilterOptions();
// Filter mode: // Filter mode:

View File

@@ -135,4 +135,3 @@ class CollectionFilterWidget : public QWidget {
}; };
#endif // COLLECTIONFILTERWIDGET_H #endif // COLLECTIONFILTERWIDGET_H

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -189,6 +189,26 @@ void CollectionLibrary::ReloadSettings() {
} }
void CollectionLibrary::CurrentSongChanged(const Song &song) {
current_song_url_ = song.url();
if (!pending_song_saves_.isEmpty()) {
SavePendingPlaycountsAndRatings();
}
}
void CollectionLibrary::Stopped() {
current_song_url_ = QUrl();
if (!pending_song_saves_.isEmpty()) {
SavePendingPlaycountsAndRatings();
}
}
void CollectionLibrary::SyncPlaycountAndRatingToFilesAsync() { void CollectionLibrary::SyncPlaycountAndRatingToFilesAsync() {
(void)QtConcurrent::run(&CollectionLibrary::SyncPlaycountAndRatingToFiles, this); (void)QtConcurrent::run(&CollectionLibrary::SyncPlaycountAndRatingToFiles, this);
@@ -212,18 +232,85 @@ void CollectionLibrary::SyncPlaycountAndRatingToFiles() {
} }
void CollectionLibrary::SongsPlaycountChanged(const SongList &songs, const bool save_tags) const { void CollectionLibrary::SongsPlaycountChanged(const SongList &songs, const bool save_tags) {
if (save_tags || save_playcounts_to_files_) { if (save_tags || save_playcounts_to_files_) {
tagreader_client_->SaveSongsPlaycountAsync(songs); SongList songs_to_save_now;
for (const Song &song : songs) {
if (song.url().isLocalFile() && song.url() == current_song_url_ &&
(song.filetype() == Song::FileType::OggFlac || song.filetype() == Song::FileType::OggVorbis || song.filetype() == Song::FileType::OggOpus)) {
qLog(Debug) << "Deferring playcount save for currently playing file" << song.url().toLocalFile();
if (pending_song_saves_.contains(song.url())) {
SharedPtr<PendingSongSave> pending_song_save = pending_song_saves_[song.url()];
pending_song_save->save_playcount = true;
pending_song_save->song.set_playcount(song.playcount());
}
else {
SharedPtr<PendingSongSave> pending_song_save = make_shared<PendingSongSave>();
pending_song_save->save_playcount = true;
pending_song_save->song = song;
pending_song_saves_.insert(song.url(), pending_song_save);
}
}
else {
songs_to_save_now << song;
}
}
if (!songs_to_save_now.isEmpty()) {
tagreader_client_->SaveSongsPlaycountAsync(songs_to_save_now);
}
} }
} }
void CollectionLibrary::SongsRatingChanged(const SongList &songs, const bool save_tags) const { void CollectionLibrary::SongsRatingChanged(const SongList &songs, const bool save_tags) {
if (save_tags || save_ratings_to_files_) { if (save_tags || save_ratings_to_files_) {
tagreader_client_->SaveSongsRatingAsync(songs); SongList songs_to_save_now;
for (const Song &song : songs) {
if (song.url().isLocalFile() && song.url() == current_song_url_ &&
(song.filetype() == Song::FileType::OggFlac || song.filetype() == Song::FileType::OggVorbis || song.filetype() == Song::FileType::OggOpus)) {
qLog(Debug) << "Deferring rating save for currently playing file" << song.url().toLocalFile();
if (pending_song_saves_.contains(song.url())) {
SharedPtr<PendingSongSave> pending_song_save = pending_song_saves_[song.url()];
pending_song_save->save_rating = true;
pending_song_save->song.set_rating(song.rating());
}
else {
SharedPtr<PendingSongSave> pending_song_save = make_shared<PendingSongSave>();
pending_song_save->save_rating = true;
pending_song_save->song = song;
pending_song_saves_.insert(song.url(), pending_song_save);
}
}
else {
songs_to_save_now << song;
}
}
if (!songs_to_save_now.isEmpty()) {
tagreader_client_->SaveSongsRatingAsync(songs_to_save_now);
}
}
}
void CollectionLibrary::SavePendingPlaycountsAndRatings() {
for (auto it = pending_song_saves_.constBegin(); it != pending_song_saves_.constEnd();) {
const QUrl url = it.key();
SharedPtr<PendingSongSave> pending_song_save = it.value();
if (url == current_song_url_) {
++it;
continue;
}
qLog(Debug) << "Saving deferred playcount/rating for" << url.toLocalFile();
if (pending_song_save->save_playcount) {
tagreader_client_->SaveSongsPlaycountAsync(SongList() << pending_song_save->song);
}
if (pending_song_save->save_rating) {
tagreader_client_->SaveSongsRatingAsync(SongList() << pending_song_save->song);
}
it = pending_song_saves_.erase(it);
} }
} }

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include <QObject> #include <QObject>
#include <QList> #include <QList>
#include <QHash> #include <QHash>
#include <QMap>
#include <QString> #include <QString>
#include "includes/shared_ptr.h" #include "includes/shared_ptr.h"
@@ -71,6 +72,7 @@ class CollectionLibrary : public QObject {
private: private:
void SyncPlaycountAndRatingToFiles(); void SyncPlaycountAndRatingToFiles();
void SavePendingPlaycountsAndRatings();
public Q_SLOTS: public Q_SLOTS:
void ReloadSettings(); void ReloadSettings();
@@ -84,16 +86,26 @@ class CollectionLibrary : public QObject {
void IncrementalScan(); void IncrementalScan();
void CurrentSongChanged(const Song &song);
void Stopped();
private Q_SLOTS: private Q_SLOTS:
void ExitReceived(); void ExitReceived();
void SongsPlaycountChanged(const SongList &songs, const bool save_tags = false) const; void SongsPlaycountChanged(const SongList &songs, const bool save_tags = false);
void SongsRatingChanged(const SongList &songs, const bool save_tags = false) const; void SongsRatingChanged(const SongList &songs, const bool save_tags = false);
Q_SIGNALS: Q_SIGNALS:
void Error(const QString &error); void Error(const QString &error);
void ExitFinished(); void ExitFinished();
private: private:
class PendingSongSave {
public:
Song song;
bool save_playcount = false;
bool save_rating = false;
};
const SharedPtr<TaskManager> task_manager_; const SharedPtr<TaskManager> task_manager_;
const SharedPtr<TagReaderClient> tagreader_client_; const SharedPtr<TagReaderClient> tagreader_client_;
@@ -111,6 +123,10 @@ class CollectionLibrary : public QObject {
bool save_playcounts_to_files_; bool save_playcounts_to_files_;
bool save_ratings_to_files_; bool save_ratings_to_files_;
QUrl current_song_url_;
QMap<QUrl, SharedPtr<PendingSongSave>> pending_song_saves_;
}; };
#endif #endif

View File

@@ -212,6 +212,7 @@ void CollectionModel::ReloadSettings() {
const bool show_various_artists = settings.value(CollectionSettings::kVariousArtists, true).toBool(); const bool show_various_artists = settings.value(CollectionSettings::kVariousArtists, true).toBool();
const bool sort_skip_articles_for_artists = settings.value(CollectionSettings::kSkipArticlesForArtists, true).toBool(); const bool sort_skip_articles_for_artists = settings.value(CollectionSettings::kSkipArticlesForArtists, true).toBool();
const bool sort_skip_articles_for_albums = settings.value(CollectionSettings::kSkipArticlesForAlbums, false).toBool(); const bool sort_skip_articles_for_albums = settings.value(CollectionSettings::kSkipArticlesForAlbums, false).toBool();
const bool use_sort_tags = settings.value(CollectionSettings::kUseSortTags, true).toBool();
use_disk_cache_ = settings.value(CollectionSettings::kSettingsDiskCacheEnable, false).toBool(); use_disk_cache_ = settings.value(CollectionSettings::kSettingsDiskCacheEnable, false).toBool();
QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettings::kSettingsCacheSize, CollectionSettings::kSettingsCacheSizeUnit, CollectionSettings::kSettingsCacheSizeDefault) / 1024)); QPixmapCache::setCacheLimit(static_cast<int>(MaximumCacheSize(&settings, CollectionSettings::kSettingsCacheSize, CollectionSettings::kSettingsCacheSizeUnit, CollectionSettings::kSettingsCacheSizeDefault) / 1024));
@@ -227,12 +228,14 @@ void CollectionModel::ReloadSettings() {
show_dividers != options_current_.show_dividers || show_dividers != options_current_.show_dividers ||
show_various_artists != options_current_.show_various_artists || show_various_artists != options_current_.show_various_artists ||
sort_skip_articles_for_artists != options_current_.sort_skip_articles_for_artists || sort_skip_articles_for_artists != options_current_.sort_skip_articles_for_artists ||
sort_skip_articles_for_albums != options_current_.sort_skip_articles_for_albums) { sort_skip_articles_for_albums != options_current_.sort_skip_articles_for_albums ||
use_sort_tags != options_current_.use_sort_tags) {
options_current_.show_pretty_covers = show_pretty_covers; options_current_.show_pretty_covers = show_pretty_covers;
options_current_.show_dividers = show_dividers; options_current_.show_dividers = show_dividers;
options_current_.show_various_artists = show_various_artists; options_current_.show_various_artists = show_various_artists;
options_current_.sort_skip_articles_for_artists = sort_skip_articles_for_artists; options_current_.sort_skip_articles_for_artists = sort_skip_articles_for_artists;
options_current_.sort_skip_articles_for_albums = sort_skip_articles_for_albums; options_current_.sort_skip_articles_for_albums = sort_skip_articles_for_albums;
options_current_.use_sort_tags = use_sort_tags;
ScheduleReset(); ScheduleReset();
} }
@@ -686,8 +689,8 @@ void CollectionModel::RemoveSongsInternal(const SongList &songs) {
if (!divider_nodes_.contains(divider_key)) continue; if (!divider_nodes_.contains(divider_key)) continue;
// Look to see if there are any other items still under this divider // Look to see if there are any other items still under this divider
QList<CollectionItem*> container_nodes = container_nodes_[0].values(); QList<CollectionItem *> container_nodes = container_nodes_[0].values();
if (std::any_of(container_nodes.begin(), container_nodes.end(), [this, divider_key](CollectionItem *node){ return DividerKey(options_active_.group_by[0], node->metadata, node->sort_text) == divider_key; })) { if (std::any_of(container_nodes.begin(), container_nodes.end(), [this, divider_key](CollectionItem *node) { return DividerKey(options_active_.group_by[0], node->metadata, node->sort_text) == divider_key; })) {
continue; continue;
} }
@@ -705,7 +708,7 @@ CollectionItem *CollectionModel::CreateContainerItem(const GroupBy group_by, con
QString divider_key; QString divider_key;
if (options_active_.show_dividers && container_level == 0) { if (options_active_.show_dividers && container_level == 0) {
divider_key = DividerKey(group_by, song, SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums)); divider_key = DividerKey(group_by, song, SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums, options_active_.use_sort_tags));
if (!divider_key.isEmpty()) { if (!divider_key.isEmpty()) {
if (!divider_nodes_.contains(divider_key)) { if (!divider_nodes_.contains(divider_key)) {
CreateDividerItem(divider_key, DividerDisplayText(group_by, divider_key), parent); CreateDividerItem(divider_key, DividerDisplayText(group_by, divider_key), parent);
@@ -719,7 +722,7 @@ CollectionItem *CollectionModel::CreateContainerItem(const GroupBy group_by, con
item->container_level = container_level; item->container_level = container_level;
item->container_key = container_key; item->container_key = container_key;
item->display_text = DisplayText(group_by, song); item->display_text = DisplayText(group_by, song);
item->sort_text = SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums); item->sort_text = SortText(group_by, song, options_active_.sort_skip_articles_for_artists, options_active_.sort_skip_articles_for_albums, options_active_.use_sort_tags);
if (!divider_key.isEmpty()) { if (!divider_key.isEmpty()) {
item->sort_text.prepend(divider_key + QLatin1Char(' ')); item->sort_text.prepend(divider_key + QLatin1Char(' '));
} }
@@ -1074,37 +1077,37 @@ QString CollectionModel::PrettyFormat(const Song &song) {
} }
QString CollectionModel::SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums) { QString CollectionModel::SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums, const bool use_sort_tags) {
switch (group_by) { switch (group_by) {
case GroupBy::AlbumArtist: case GroupBy::AlbumArtist:
return SortTextForName(song.effective_albumartistsort(), sort_skip_articles_for_artists); return SortTextForName(use_sort_tags ? song.effective_albumartistsort() : song.effective_albumartist(), sort_skip_articles_for_artists);
case GroupBy::Artist: case GroupBy::Artist:
return SortTextForName(song.effective_artistsort(), sort_skip_articles_for_artists); return SortTextForName(use_sort_tags ? song.effective_artistsort() : song.artist(), sort_skip_articles_for_artists);
case GroupBy::Album: case GroupBy::Album:
return SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums); return SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
case GroupBy::AlbumDisc: case GroupBy::AlbumDisc:
return SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc())); return SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
case GroupBy::YearAlbum: case GroupBy::YearAlbum:
return SortTextForNumber(std::max(0, song.year())) + song.grouping() + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums); return SortTextForYear(song.year()) + song.grouping() + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
case GroupBy::YearAlbumDisc: case GroupBy::YearAlbumDisc:
return SortTextForNumber(std::max(0, song.year())) + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc())); return SortTextForYear(song.year()) + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
case GroupBy::OriginalYearAlbum: case GroupBy::OriginalYearAlbum:
return SortTextForNumber(std::max(0, song.effective_originalyear())) + song.grouping() + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums); return SortTextForYear(song.effective_originalyear()) + song.grouping() + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums);
case GroupBy::OriginalYearAlbumDisc: case GroupBy::OriginalYearAlbumDisc:
return SortTextForNumber(std::max(0, song.effective_originalyear())) + SortTextForName(song.effective_albumsort(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc())); return SortTextForYear(song.effective_originalyear()) + SortTextForName(use_sort_tags ? song.effective_albumsort() : song.album(), sort_skip_articles_for_albums) + SortTextForNumber(std::max(0, song.disc()));
case GroupBy::Disc: case GroupBy::Disc:
return SortTextForNumber(std::max(0, song.disc())); return SortTextForNumber(std::max(0, song.disc()));
case GroupBy::Year: case GroupBy::Year:
return SortTextForNumber(std::max(0, song.year())) + QLatin1Char(' '); return SortTextForYear(song.year()) + QLatin1Char(' ');
case GroupBy::OriginalYear: case GroupBy::OriginalYear:
return SortTextForNumber(std::max(0, song.effective_originalyear())) + QLatin1Char(' '); return SortTextForYear(song.effective_originalyear()) + QLatin1Char(' ');
case GroupBy::Genre: case GroupBy::Genre:
return SortText(song.genre()); return SortText(song.genre());
case GroupBy::Composer: case GroupBy::Composer:
return SortTextForName(song.effective_composersort(), sort_skip_articles_for_artists); return SortTextForName(use_sort_tags ? song.effective_composersort() : song.composer(), sort_skip_articles_for_artists);
case GroupBy::Performer: case GroupBy::Performer:
return SortTextForName(song.effective_performersort(), sort_skip_articles_for_artists); return SortTextForName(use_sort_tags ? song.effective_performersort() : song.performer(), sort_skip_articles_for_artists);
case GroupBy::Grouping: case GroupBy::Grouping:
return SortText(song.grouping()); return SortText(song.grouping());
case GroupBy::FileType: case GroupBy::FileType:
@@ -1162,14 +1165,14 @@ QString CollectionModel::SortTextForSong(const Song &song) {
QString CollectionModel::SortTextForYear(const int year) { QString CollectionModel::SortTextForYear(const int year) {
QString str = QString::number(year); const QString str = QString::number(std::max(year, 0));
return QStringLiteral("0").repeated(qMax(0, 4 - str.length())) + str; return QStringLiteral("0").repeated(qMax(0, 4 - str.length())) + str;
} }
QString CollectionModel::SortTextForBitrate(const int bitrate) { QString CollectionModel::SortTextForBitrate(const int bitrate) {
QString str = QString::number(bitrate); const QString str = QString::number(bitrate);
return QStringLiteral("0").repeated(qMax(0, 3 - str.length())) + str; return QStringLiteral("0").repeated(qMax(0, 3 - str.length())) + str;
} }
@@ -1218,27 +1221,30 @@ QString CollectionModel::ContainerKey(const GroupBy group_by, const Song &song,
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping()); if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
break; break;
case GroupBy::AlbumDisc: case GroupBy::AlbumDisc:
key = PrettyAlbumDisc(song.album(), song.disc()); key = TextOrUnknown(song.album());
key.append(QLatin1Char('-') + SortTextForNumber(song.disc()));
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id()); if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping()); if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
break; break;
case GroupBy::YearAlbum: case GroupBy::YearAlbum:
key = PrettyYearAlbum(song.year(), song.album()); key = SortTextForYear(song.year()) + QLatin1Char('-') + TextOrUnknown(song.album());
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id()); if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping()); if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
break; break;
case GroupBy::YearAlbumDisc: case GroupBy::YearAlbumDisc:
key = PrettyYearAlbumDisc(song.year(), song.album(), song.disc()); key = SortTextForYear(song.year()) + QLatin1Char('-') + TextOrUnknown(song.album());
key.append(QLatin1Char('-') + SortTextForNumber(song.disc()));
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id()); if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping()); if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
break; break;
case GroupBy::OriginalYearAlbum: case GroupBy::OriginalYearAlbum:
key = PrettyYearAlbum(song.effective_originalyear(), song.album()); key = SortTextForYear(song.effective_originalyear()) + QLatin1Char('-') + TextOrUnknown(song.album());
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id()); if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping()); if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
break; break;
case GroupBy::OriginalYearAlbumDisc: case GroupBy::OriginalYearAlbumDisc:
key = PrettyYearAlbumDisc(song.effective_originalyear(), song.album(), song.disc()); key = SortTextForYear(song.effective_originalyear()) + QLatin1Char('-') + TextOrUnknown(song.album());
key.append(QLatin1Char('-') + SortTextForNumber(song.disc()));
if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id()); if (!song.album_id().isEmpty()) key.append(QLatin1Char('-') + song.album_id());
if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping()); if (options_active_.separate_albums_by_grouping && !song.grouping().isEmpty()) key.append(QLatin1Char('-') + song.grouping());
break; break;
@@ -1246,10 +1252,10 @@ QString CollectionModel::ContainerKey(const GroupBy group_by, const Song &song,
key = PrettyDisc(song.disc()); key = PrettyDisc(song.disc());
break; break;
case GroupBy::Year: case GroupBy::Year:
key = QString::number(std::max(0, song.year())); key = SortTextForYear(song.year());
break; break;
case GroupBy::OriginalYear: case GroupBy::OriginalYear:
key = QString::number(std::max(0, song.effective_originalyear())); key = SortTextForYear(song.effective_originalyear());
break; break;
case GroupBy::Genre: case GroupBy::Genre:
key = TextOrUnknown(song.genre()); key = TextOrUnknown(song.genre());
@@ -1341,7 +1347,7 @@ QString CollectionModel::DividerKey(const GroupBy group_by, const Song &song, co
case GroupBy::Bitdepth: case GroupBy::Bitdepth:
return SortTextForNumber(song.bitdepth()); return SortTextForNumber(song.bitdepth());
case GroupBy::Bitrate: case GroupBy::Bitrate:
return SortTextForNumber(song.bitrate()); return SortTextForBitrate(song.bitrate());
case GroupBy::None: case GroupBy::None:
case GroupBy::GroupByCount: case GroupBy::GroupByCount:
return QString(); return QString();

View File

@@ -131,6 +131,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
show_various_artists(true), show_various_artists(true),
sort_skip_articles_for_artists(false), sort_skip_articles_for_artists(false),
sort_skip_articles_for_albums(false), sort_skip_articles_for_albums(false),
use_sort_tags(true),
separate_albums_by_grouping(false) {} separate_albums_by_grouping(false) {}
Grouping group_by; Grouping group_by;
@@ -139,6 +140,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
bool show_various_artists; bool show_various_artists;
bool sort_skip_articles_for_artists; bool sort_skip_articles_for_artists;
bool sort_skip_articles_for_albums; bool sort_skip_articles_for_albums;
bool use_sort_tags;
bool separate_albums_by_grouping; bool separate_albums_by_grouping;
CollectionFilterOptions filter_options; CollectionFilterOptions filter_options;
}; };
@@ -178,14 +180,14 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
QMimeData *mimeData(const QModelIndexList &indexes) const override; QMimeData *mimeData(const QModelIndexList &indexes) const override;
// Utility functions for manipulating text // Utility functions for manipulating text
QString DisplayText(const GroupBy group_by, const Song &song); static QString DisplayText(const GroupBy group_by, const Song &song);
static QString TextOrUnknown(const QString &text); static QString TextOrUnknown(const QString &text);
static QString PrettyYearAlbum(const int year, const QString &album); static QString PrettyYearAlbum(const int year, const QString &album);
static QString PrettyAlbumDisc(const QString &album, const int disc); static QString PrettyAlbumDisc(const QString &album, const int disc);
static QString PrettyYearAlbumDisc(const int year, const QString &album, const int disc); static QString PrettyYearAlbumDisc(const int year, const QString &album, const int disc);
static QString PrettyDisc(const int disc); static QString PrettyDisc(const int disc);
static QString PrettyFormat(const Song &song); static QString PrettyFormat(const Song &song);
QString SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums); static QString SortText(const GroupBy group_by, const Song &song, const bool sort_skip_articles_for_artists, const bool sort_skip_articles_for_albums, const bool use_sort_tags);
static QString SortText(QString text); static QString SortText(QString text);
static QString SortTextForName(const QString &name, const bool sort_skip_articles); static QString SortTextForName(const QString &name, const bool sort_skip_articles);
static QString SortTextForNumber(const int number); static QString SortTextForNumber(const int number);

View File

@@ -58,4 +58,3 @@ class CollectionPlaylistItem : public PlaylistItem {
}; };
#endif // COLLECTIONPLAYLISTITEM_H #endif // COLLECTIONPLAYLISTITEM_H

View File

@@ -101,9 +101,9 @@ void CollectionQuery::AddCompilationRequirement(const bool compilation) {
QString CollectionQuery::GetInnerQuery() const { QString CollectionQuery::GetInnerQuery() const {
return duplicates_only_ return duplicates_only_
? QStringLiteral(" INNER JOIN (select * from duplicated_songs) dsongs " ? QStringLiteral(" INNER JOIN (select * from duplicated_songs) dsongs "
"ON (%songs_table.artist = dsongs.dup_artist " "ON (%songs_table.artist = dsongs.dup_artist "
"AND %songs_table.album = dsongs.dup_album " "AND %songs_table.album = dsongs.dup_album "
"AND %songs_table.title = dsongs.dup_title) ") "AND %songs_table.title = dsongs.dup_title) ")
: QString(); : QString();
} }

View File

@@ -706,8 +706,14 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
qLog(Debug) << file << "is missing EBU R 128 loudness characteristics."; qLog(Debug) << file << "is missing EBU R 128 loudness characteristics.";
} }
// If the song is unavailable and nothing has changed, just mark it as available without re-scanning
// For CUE files with multiple sections, all sections share the same file and would have the same availability status
if (matching_song.unavailable() && !changed && !missing_fingerprint && !missing_loudness_characteristics) {
qLog(Debug) << "Unavailable song" << file << "restored without re-scanning.";
t->readded_songs << matching_songs;
}
// The song's changed or missing fingerprint - create fingerprint and reread the metadata from file. // The song's changed or missing fingerprint - create fingerprint and reread the metadata from file.
if (t->ignores_mtime() || changed || missing_fingerprint || missing_loudness_characteristics) { else if (t->ignores_mtime() || changed || missing_fingerprint || missing_loudness_characteristics) {
QString fingerprint; QString fingerprint;
#ifdef HAVE_SONGFINGERPRINTING #ifdef HAVE_SONGFINGERPRINTING
@@ -728,12 +734,6 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const CollectionSu
} }
} }
// Nothing has changed - mark the song available without re-scanning
else if (matching_song.unavailable()) {
qLog(Debug) << "Unavailable song" << file << "restored.";
t->readded_songs << matching_songs;
}
} }
else { // Search the DB by fingerprint. else { // Search the DB by fingerprint.
QString fingerprint; QString fingerprint;

View File

@@ -137,7 +137,7 @@ class CollectionWatcher : public QObject {
QStringList files_changed_path_; QStringList files_changed_path_;
private: private:
ScanTransaction &operator=(const ScanTransaction&) { return *this; } ScanTransaction &operator=(const ScanTransaction &transaction) { Q_UNUSED(transaction); return *this; }
int task_id_; int task_id_;
quint64 progress_; quint64 progress_;
@@ -261,7 +261,6 @@ class CollectionWatcher : public QObject {
static QStringList sValidImages; static QStringList sValidImages;
qint64 last_scan_time_; qint64 last_scan_time_;
}; };
inline QString CollectionWatcher::NoExtensionPart(const QString &fileName) { inline QString CollectionWatcher::NoExtensionPart(const QString &fileName) {

View File

@@ -48,4 +48,7 @@
#cmakedefine ENABLE_WIN32_CONSOLE #cmakedefine ENABLE_WIN32_CONSOLE
#cmakedefine HAVE_VISUALIZATIONS
#cmakedefine HAVE_PROJECTM4
#endif // CONFIG_H_IN #endif // CONFIG_H_IN

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef APPEARANCESETTINGS_H #ifndef APPEARANCESETTINGS_H
#define APPEARANCESETTINGS_H #define APPEARANCESETTINGS_H
@@ -70,6 +70,6 @@ enum class BackgroundImagePosition {
BottomRight = 5 BottomRight = 5
}; };
} // namespace } // namespace AppearanceSettings
#endif // APPEARANCESETTINGS_H #endif // APPEARANCESETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef BACKENDSETTINGS_H #ifndef BACKENDSETTINGS_H
#define BACKENDSETTINGS_H #define BACKENDSETTINGS_H
@@ -63,6 +63,6 @@ constexpr qint64 kDefaultBufferDuration = 4000;
constexpr double kDefaultBufferLowWatermark = 0.33; constexpr double kDefaultBufferLowWatermark = 0.33;
constexpr double kDefaultBufferHighWatermark = 0.99; constexpr double kDefaultBufferHighWatermark = 0.99;
} // namespace } // namespace BackendSettings
#endif // BACKENDSETTINGS_H #endif // BACKENDSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef BEHAVIOURSETTINGS_H #ifndef BEHAVIOURSETTINGS_H
#define BEHAVIOURSETTINGS_H #define BEHAVIOURSETTINGS_H
@@ -71,6 +71,6 @@ constexpr char kDoubleClickPlaylistAddMode[] = "doubleclick_playlist_addmode";
constexpr char kSeekStepSec[] = "seek_step_sec"; constexpr char kSeekStepSec[] = "seek_step_sec";
constexpr char kVolumeIncrement[] = "volume_increment"; constexpr char kVolumeIncrement[] = "volume_increment";
} // namespace } // namespace BehaviourSettings
#endif // BEHAVIOURSETTINGS_H #endif // BEHAVIOURSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024-2025, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024-2025, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef COLLECTIONSETTINGS_H #ifndef COLLECTIONSETTINGS_H
#define COLLECTIONSETTINGS_H #define COLLECTIONSETTINGS_H
@@ -37,7 +37,7 @@ constexpr char kPrettyCovers[] = "pretty_covers";
constexpr char kVariousArtists[] = "various_artists"; constexpr char kVariousArtists[] = "various_artists";
constexpr char kSkipArticlesForArtists[] = "skip_articles_for_artists"; constexpr char kSkipArticlesForArtists[] = "skip_articles_for_artists";
constexpr char kSkipArticlesForAlbums[] = "skip_articles_for_albums"; constexpr char kSkipArticlesForAlbums[] = "skip_articles_for_albums";
constexpr char kShowSortText[] = "show_sort_text"; constexpr char kUseSortTags[] = "use_short_tags";
constexpr char kSettingsCacheSize[] = "cache_size"; constexpr char kSettingsCacheSize[] = "cache_size";
constexpr char kSettingsCacheSizeUnit[] = "cache_size_unit"; constexpr char kSettingsCacheSizeUnit[] = "cache_size_unit";
constexpr char kSettingsDiskCacheEnable[] = "disk_cache_enable"; constexpr char kSettingsDiskCacheEnable[] = "disk_cache_enable";
@@ -59,6 +59,6 @@ enum class CacheSizeUnit {
TB TB
}; };
} // namespace } // namespace CollectionSettings
#endif // COLLECTIONSETTINGS_H #endif // COLLECTIONSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef CONTEXTSETTINGS_H #ifndef CONTEXTSETTINGS_H
#define CONTEXTSETTINGS_H #define CONTEXTSETTINGS_H
@@ -43,6 +43,6 @@ constexpr char kSettingsSummaryFmt[] = "SummaryFmt";
constexpr char kDefaultFontFamily[] = "Noto Sans"; constexpr char kDefaultFontFamily[] = "Noto Sans";
constexpr qreal kDefaultFontSizeHeadline = 11; constexpr qreal kDefaultFontSizeHeadline = 11;
} // namespace } // namespace ContextSettings
#endif // CONTEXTSETTINGS_H #endif // CONTEXTSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef COVERSSETTINGS_H #ifndef COVERSSETTINGS_H
#define COVERSSETTINGS_H #define COVERSSETTINGS_H
@@ -32,6 +32,6 @@ constexpr char kSaveOverwrite[] = "save_overwrite";
constexpr char kSaveLowercase[] = "save_lowercase"; constexpr char kSaveLowercase[] = "save_lowercase";
constexpr char kSaveReplaceSpaces[] = "save_replace_spaces"; constexpr char kSaveReplaceSpaces[] = "save_replace_spaces";
} // namespace } // namespace CoversSettings
#endif // COVERSSETTINGS_H #endif // COVERSSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef FILEFILTERCONSTANTS_H #ifndef FILEFILTERCONSTANTS_H
#define FILEFILTERCONSTANTS_H #define FILEFILTERCONSTANTS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2023, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2023, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef FILENAMECONSTANTS_H #ifndef FILENAMECONSTANTS_H
#define FILENAMECONSTANTS_H #define FILENAMECONSTANTS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2025, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2025, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef FILESYSTEMCONSTANTS_H #ifndef FILESYSTEMCONSTANTS_H
#define FILESYSTEMCONSTANTS_H #define FILESYSTEMCONSTANTS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef GLOBALSHORTCUTSSETTINGS_H #ifndef GLOBALSHORTCUTSSETTINGS_H
#define GLOBALSHORTCUTSSETTINGS_H #define GLOBALSHORTCUTSSETTINGS_H
@@ -26,6 +26,6 @@ constexpr char kSettingsGroup[] = "GlobalShortcuts";
constexpr char kUseKGlobalAccel[] = "use_kglobalaccel"; constexpr char kUseKGlobalAccel[] = "use_kglobalaccel";
constexpr char kUseX11[] = "use_x11"; constexpr char kUseX11[] = "use_x11";
} // namespace } // namespace GlobalShortcutsSettings
#endif // GLOBALSHORTCUTSSETTINGS_H #endif // GLOBALSHORTCUTSSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef LYRICSSETTINGS_H #ifndef LYRICSSETTINGS_H
#define LYRICSSETTINGS_H #define LYRICSSETTINGS_H
@@ -25,6 +25,6 @@ namespace LyricsSettings {
constexpr char kSettingsGroup[] = "Lyrics"; constexpr char kSettingsGroup[] = "Lyrics";
constexpr char kProviders[] = "providers"; constexpr char kProviders[] = "providers";
} // namespace } // namespace LyricsSettings
#endif // LYRICSSETTINGS_H #endif // LYRICSSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef MAINWINDOWSETTINGS_H #ifndef MAINWINDOWSETTINGS_H
#define MAINWINDOWSETTINGS_H #define MAINWINDOWSETTINGS_H
@@ -32,6 +32,6 @@ constexpr char kGeometry[] = "geometry";
constexpr char kSplitterState[] = "splitter_state"; constexpr char kSplitterState[] = "splitter_state";
constexpr char kDoNotShowSponsorMessage[] = "do_not_show_sponsor_message"; constexpr char kDoNotShowSponsorMessage[] = "do_not_show_sponsor_message";
} // namespace } // namespace MainWindowSettings
#endif // MAINWINDOWSETTINGS_H #endif // MAINWINDOWSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef MOODBARSETTINGS_H #ifndef MOODBARSETTINGS_H
#define MOODBARSETTINGS_H #define MOODBARSETTINGS_H
@@ -38,6 +38,6 @@ constexpr char kShow[] = "show";
constexpr char kStyle[] = "style"; constexpr char kStyle[] = "style";
constexpr char kSave[] = "save"; constexpr char kSave[] = "save";
} // namespace } // namespace MoodbarSettings
#endif // MOODBARSETTINGS_H #endif // MOODBARSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef NETWORKPROXYSETTINGS_H #ifndef NETWORKPROXYSETTINGS_H
#define NETWORKPROXYSETTINGS_H #define NETWORKPROXYSETTINGS_H
@@ -32,6 +32,6 @@ constexpr char kUsername[] = "username";
constexpr char kPassword[] = "password"; constexpr char kPassword[] = "password";
constexpr char kEngine[] = "engine"; constexpr char kEngine[] = "engine";
} // namespace } // namespace NetworkProxySettings
#endif // NETWORKPROXYSETTINGS_H #endif // NETWORKPROXYSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef NOTIFICATIONSSETTINGS_H #ifndef NOTIFICATIONSSETTINGS_H
#define NOTIFICATIONSSETTINGS_H #define NOTIFICATIONSSETTINGS_H
@@ -45,7 +45,7 @@ constexpr char kCustomTextEnabled[] = "CustomTextEnabled";
constexpr char kCustomText1[] = "CustomText1"; constexpr char kCustomText1[] = "CustomText1";
constexpr char kCustomText2[] = "CustomText2"; constexpr char kCustomText2[] = "CustomText2";
} // namespace } // namespace OSDSettings
namespace OSDPrettySettings { namespace OSDPrettySettings {
@@ -63,7 +63,7 @@ constexpr char kFading[] = "fading";
constexpr QRgb kPresetBlue = qRgb(102, 150, 227); constexpr QRgb kPresetBlue = qRgb(102, 150, 227);
constexpr QRgb kPresetRed = qRgb(202, 22, 16); constexpr QRgb kPresetRed = qRgb(202, 22, 16);
} // namespace } // namespace OSDPrettySettings
namespace DiscordRPCSettings { namespace DiscordRPCSettings {
@@ -79,6 +79,6 @@ enum class StatusDisplayType {
Song Song
}; };
} // namespace } // namespace DiscordRPCSettings
#endif // NOTIFICATIONSSETTINGS_H #endif // NOTIFICATIONSSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef PLAYLISTSETTINGS_H #ifndef PLAYLISTSETTINGS_H
#define PLAYLISTSETTINGS_H #define PLAYLISTSETTINGS_H
@@ -63,7 +63,7 @@ constexpr char kLastSaveExtension[] = "last_save_extension";
constexpr char kLastSaveAllPath[] = "last_save_all_path"; constexpr char kLastSaveAllPath[] = "last_save_all_path";
constexpr char kLastSaveAllExtension[] = "last_save_all_extension"; constexpr char kLastSaveAllExtension[] = "last_save_all_extension";
} // namespace } // namespace PlaylistSettings
Q_DECLARE_METATYPE(PlaylistSettings::PathType) Q_DECLARE_METATYPE(PlaylistSettings::PathType)

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef QOBUZSETTINGS_H #ifndef QOBUZSETTINGS_H
#define QOBUZSETTINGS_H #define QOBUZSETTINGS_H
@@ -43,6 +43,6 @@ constexpr char kCredentialsId[] = "credentials_id";
constexpr char kDeviceId[] = "device_id"; constexpr char kDeviceId[] = "device_id";
constexpr char kUserAuthToken[] = "user_auth_token"; constexpr char kUserAuthToken[] = "user_auth_token";
} // namespace } // namespace QobuzSettings
#endif // QOBUZSETTINGS_H #endif // QOBUZSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef SCROBBLERSETTINGS_H #ifndef SCROBBLERSETTINGS_H
#define SCROBBLERSETTINGS_H #define SCROBBLERSETTINGS_H
@@ -35,6 +35,6 @@ constexpr char kStripRemastered[] = "strip_remastered";
constexpr char kSources[] = "sources"; constexpr char kSources[] = "sources";
constexpr char kUserToken[] = "user_token"; constexpr char kUserToken[] = "user_token";
} // namespace } // namespace ScrobblerSettings
#endif // SCROBBLERSETTINGS_H #endif // SCROBBLERSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef SPOTIFYSETTINGS_H #ifndef SPOTIFYSETTINGS_H
#define SPOTIFYSETTINGS_H #define SPOTIFYSETTINGS_H
@@ -38,6 +38,6 @@ constexpr char kRefreshToken[] = "refresh_token";
constexpr char kExpiresIn[] = "expires_in"; constexpr char kExpiresIn[] = "expires_in";
constexpr char kLoginTime[] = "login_time"; constexpr char kLoginTime[] = "login_time";
} // namespace } // namespace SpotifySettings
#endif // SPOTIFYSETTINGS_H #endif // SPOTIFYSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef SUBSONICETTINGS_H #ifndef SUBSONICETTINGS_H
@@ -41,6 +41,6 @@ constexpr char kUseAlbumIdForAlbumCovers[] = "usealbumidforalbumcovers";
constexpr char kServerSideScrobbling[] = "serversidescrobbling"; constexpr char kServerSideScrobbling[] = "serversidescrobbling";
constexpr char kAuthMethod[] = "authmethod"; constexpr char kAuthMethod[] = "authmethod";
} // namespace } // namespace SubsonicSettings
#endif // SUBSONICETTINGS_H #endif // SUBSONICETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef TIDALSETTINGS_H #ifndef TIDALSETTINGS_H
#define TIDALSETTINGS_H #define TIDALSETTINGS_H
@@ -48,6 +48,6 @@ enum class StreamUrlMethod {
PlaybackInfoPostPaywall PlaybackInfoPostPaywall
}; };
} } // namespace TidalSettings
#endif // TIDALSETTINGS_H #endif // TIDALSETTINGS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it wiLL be useful, * Strawberry is distributed in the hope that it wiLL be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef TIMECONSTANTS_H #ifndef TIMECONSTANTS_H
#define TIMECONSTANTS_H #define TIMECONSTANTS_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2024, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2024, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it wiLL be useful, * Strawberry is distributed in the hope that it wiLL be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef TRANSCODERSETTINGS_H #ifndef TRANSCODERSETTINGS_H
@@ -26,4 +26,3 @@ constexpr char kSettingsGroup[] = "Transcoder";
} }
#endif // TRANSCODERSETTINGS_H #endif // TRANSCODERSETTINGS_H

View File

@@ -61,7 +61,6 @@ class ContextAlbum : public QWidget {
void contextMenuEvent(QContextMenuEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override;
private: private:
struct PreviousCover { struct PreviousCover {
explicit PreviousCover() : opacity(0.0) {} explicit PreviousCover() : opacity(0.0) {}
QImage image; QImage image;

View File

@@ -59,6 +59,7 @@
#include "covermanager/albumcoverchoicecontroller.h" #include "covermanager/albumcoverchoicecontroller.h"
#include "lyrics/lyricsfetcher.h" #include "lyrics/lyricsfetcher.h"
#include "constants/contextsettings.h" #include "constants/contextsettings.h"
#include "constants/timeconstants.h"
#include "contextview.h" #include "contextview.h"
#include "contextalbum.h" #include "contextalbum.h"
@@ -353,7 +354,7 @@ void ContextView::SearchLyrics() {
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked() && action_search_lyrics_->isChecked() && !song_playing_.artist().isEmpty() && !song_playing_.title().isEmpty() && !lyrics_tried_ && lyrics_id_ == -1) { if (lyrics_.isEmpty() && action_show_lyrics_->isChecked() && action_search_lyrics_->isChecked() && !song_playing_.artist().isEmpty() && !song_playing_.title().isEmpty() && !lyrics_tried_ && lyrics_id_ == -1) {
lyrics_fetcher_->Clear(); lyrics_fetcher_->Clear();
lyrics_tried_ = true; lyrics_tried_ = true;
lyrics_id_ = static_cast<qint64>(lyrics_fetcher_->Search(song_playing_.effective_albumartist(), song_playing_.artist(), song_playing_.album(), song_playing_.title())); lyrics_id_ = static_cast<qint64>(lyrics_fetcher_->Search(song_playing_.effective_albumartist(), song_playing_.artist(), song_playing_.album(), song_playing_.title(), song_playing_.length_nanosec() / kNsecPerSec));
} }
} }

View File

@@ -65,9 +65,9 @@ class ContextView : public QWidget {
protected: protected:
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;
void contextMenuEvent(QContextMenuEvent*) override; void contextMenuEvent(QContextMenuEvent *e) override;
void dragEnterEvent(QDragEnterEvent*) override; void dragEnterEvent(QDragEnterEvent *e) override;
void dropEvent(QDropEvent*) override; void dropEvent(QDropEvent *e) override;
private: private:
void AddActions(); void AddActions();

View File

@@ -74,6 +74,7 @@
#include "lyrics/elyricsnetlyricsprovider.h" #include "lyrics/elyricsnetlyricsprovider.h"
#include "lyrics/letraslyricsprovider.h" #include "lyrics/letraslyricsprovider.h"
#include "lyrics/lyricfindlyricsprovider.h" #include "lyrics/lyricfindlyricsprovider.h"
#include "lyrics/lrcliblyricsprovider.h"
#include "scrobbler/audioscrobbler.h" #include "scrobbler/audioscrobbler.h"
#include "scrobbler/lastfmscrobbler.h" #include "scrobbler/lastfmscrobbler.h"
@@ -117,8 +118,8 @@ using namespace std::chrono_literals;
class ApplicationImpl { class ApplicationImpl {
public: public:
explicit ApplicationImpl(Application *app) : explicit ApplicationImpl(Application *app)
tagreader_client_([app](){ : tagreader_client_([app]() {
TagReaderClient *client = new TagReaderClient(); TagReaderClient *client = new TagReaderClient();
app->MoveToNewThread(client); app->MoveToNewThread(client);
return client; return client;
@@ -182,6 +183,7 @@ class ApplicationImpl {
lyrics_providers->AddProvider(new ElyricsNetLyricsProvider(lyrics_providers->network())); lyrics_providers->AddProvider(new ElyricsNetLyricsProvider(lyrics_providers->network()));
lyrics_providers->AddProvider(new LetrasLyricsProvider(lyrics_providers->network())); lyrics_providers->AddProvider(new LetrasLyricsProvider(lyrics_providers->network()));
lyrics_providers->AddProvider(new LyricFindLyricsProvider(lyrics_providers->network())); lyrics_providers->AddProvider(new LyricFindLyricsProvider(lyrics_providers->network()));
lyrics_providers->AddProvider(new LrcLibLyricsProvider(lyrics_providers->network()));
lyrics_providers->ReloadSettings(); lyrics_providers->ReloadSettings();
return lyrics_providers; return lyrics_providers;
}), }),
@@ -264,7 +266,7 @@ Application::Application(QObject *parent)
Application::~Application() { Application::~Application() {
qLog(Debug) << "Terminating application"; qLog(Debug) << "Terminating application";
for (QThread *thread : std::as_const(threads_)) { for (QThread *thread : std::as_const(threads_)) {
thread->quit(); thread->quit();

View File

@@ -204,7 +204,7 @@ bool CommandlineOptions::Parse() {
{ "version", no_argument, nullptr, LongOptions::Version }, { "version", no_argument, nullptr, LongOptions::Version },
{ nullptr, 0, nullptr, 0 } { nullptr, 0, nullptr, 0 }
#endif #endif
}; };
// Parse the arguments // Parse the arguments
bool ok = false; bool ok = false;

View File

@@ -61,8 +61,8 @@ constexpr char kMagicAllSongsTables[] = "%allsongstables";
int Database::sNextConnectionId = 1; int Database::sNextConnectionId = 1;
QMutex Database::sNextConnectionIdMutex; QMutex Database::sNextConnectionIdMutex;
Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const QString &database_name) : Database::Database(SharedPtr<TaskManager> task_manager, QObject *parent, const QString &database_name)
QObject(parent), : QObject(parent),
task_manager_(task_manager), task_manager_(task_manager),
injected_database_name_(database_name), injected_database_name_(database_name),
query_hash_(0), query_hash_(0),
@@ -134,7 +134,7 @@ QSqlDatabase Database::Connect() {
return db; return db;
} }
db.setConnectOptions(u"QSQLITE_BUSY_TIMEOUT=30000"_s); db.setConnectOptions(u"QSQLITE_BUSY_TIMEOUT=30000"_s);
//qLog(Debug) << "Opened database with connection id" << connection_id; // qLog(Debug) << "Opened database with connection id" << connection_id;
if (injected_database_name_.isNull()) { if (injected_database_name_.isNull()) {
db.setDatabaseName(directory_ + u'/' + QLatin1String(kDatabaseFilename)); db.setDatabaseName(directory_ + u'/' + QLatin1String(kDatabaseFilename));
@@ -210,7 +210,7 @@ void Database::Close() {
QSqlDatabase db = QSqlDatabase::database(connection_id); QSqlDatabase db = QSqlDatabase::database(connection_id);
if (db.isOpen()) { if (db.isOpen()) {
db.close(); db.close();
//qLog(Debug) << "Closed database with connection id" << connection_id; // qLog(Debug) << "Closed database with connection id" << connection_id;
} }
} }
QSqlDatabase::removeDatabase(connection_id); QSqlDatabase::removeDatabase(connection_id);
@@ -503,7 +503,9 @@ bool Database::IntegrityCheck(const QSqlDatabase &db) {
break; break;
} }
else { else {
if (!error_reported) { Q_EMIT Error(tr("Database corruption detected.")); } if (!error_reported) {
Q_EMIT Error(tr("Database corruption detected."));
}
Q_EMIT Error(u"Database: "_s + message); Q_EMIT Error(u"Database: "_s + message);
error_reported = true; error_reported = true;
} }
@@ -594,8 +596,7 @@ void Database::BackupFile(const QString &filename) {
ret = sqlite3_backup_step(backup, 16); ret = sqlite3_backup_step(backup, 16);
const int page_count = sqlite3_backup_pagecount(backup); const int page_count = sqlite3_backup_pagecount(backup);
task_manager_->SetTaskProgress(task_id, static_cast<quint64>(page_count - sqlite3_backup_remaining(backup)), static_cast<quint64>(page_count)); task_manager_->SetTaskProgress(task_id, static_cast<quint64>(page_count - sqlite3_backup_remaining(backup)), static_cast<quint64>(page_count));
} } while (ret == SQLITE_OK);
while (ret == SQLITE_OK);
if (ret != SQLITE_DONE) { if (ret != SQLITE_DONE) {
qLog(Error) << "Database backup failed"; qLog(Error) << "Database backup failed";

View File

@@ -128,7 +128,6 @@ class Database : public QObject {
int startup_schema_version_; int startup_schema_version_;
QThread *original_thread_; QThread *original_thread_;
}; };
#endif // DATABASE_H #endif // DATABASE_H

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#include "enginemetadata.h" #include "enginemetadata.h"
#include "core/song.h" #include "core/song.h"

View File

@@ -1,21 +1,21 @@
/* /*
* Strawberry Music Player * Strawberry Music Player
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Strawberry is distributed in the hope that it will be useful, * Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>. * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef ENGINEMETADATA_H #ifndef ENGINEMETADATA_H
#define ENGINEMETADATA_H #define ENGINEMETADATA_H

View File

@@ -110,21 +110,32 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob &job, QString &error_te
bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob &job) { bool FilesystemMusicStorage::DeleteFromStorage(const DeleteJob &job) {
QString path = job.metadata_.url().toLocalFile(); const QString path = job.metadata_.url().toLocalFile();
QFileInfo fileInfo(path); const QFileInfo fileInfo(path);
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
if (job.use_trash_ && QFile::supportsMoveToTrash()) { if (job.use_trash_ && QFile::supportsMoveToTrash()) {
#else #else
if (job.use_trash_) { if (job.use_trash_) {
#endif #endif
return QFile::moveToTrash(path); if (QFile::moveToTrash(path)) {
return true;
}
qLog(Warning) << "Moving file to trash failed for" << path << ", falling back to direct deletion";
} }
bool success = false;
if (fileInfo.isDir()) { if (fileInfo.isDir()) {
return Utilities::RemoveRecursive(path); success = Utilities::RemoveRecursive(path);
}
else {
success = QFile::remove(path);
} }
return QFile::remove(path); if (!success) {
qLog(Error) << "Failed to delete file" << path;
}
return success;
} }

View File

@@ -93,7 +93,7 @@ QNetworkReply *HttpBaseRequest::CreateGetRequest(const QUrl &url, const QUrlQuer
QObject::connect(reply, &QNetworkReply::sslErrors, this, &HttpBaseRequest::HandleSSLErrors); QObject::connect(reply, &QNetworkReply::sslErrors, this, &HttpBaseRequest::HandleSSLErrors);
replies_ << reply; replies_ << reply;
//qLog(Debug) << service_name() << "Sending get request" << request_url; // qLog(Debug) << service_name() << "Sending get request" << request_url;
return reply; return reply;
@@ -111,7 +111,7 @@ QNetworkReply *HttpBaseRequest::CreatePostRequest(const QUrl &url, const QByteAr
QObject::connect(reply, &QNetworkReply::sslErrors, this, &HttpBaseRequest::HandleSSLErrors); QObject::connect(reply, &QNetworkReply::sslErrors, this, &HttpBaseRequest::HandleSSLErrors);
replies_ << reply; replies_ << reply;
//qLog(Debug) << service_name() << "Sending post request" << url << data; // qLog(Debug) << service_name() << "Sending post request" << url << data;
return reply; return reply;

View File

@@ -27,6 +27,7 @@ class IconLoader {
public: public:
static void Init(); static void Init();
static QIcon Load(const QString &name, const bool system_icon = true, const int fixed_size = 0, const int min_size = 0, const int max_size = 0); static QIcon Load(const QString &name, const bool system_icon = true, const int fixed_size = 0, const int min_size = 0, const int max_size = 0);
private: private:
explicit IconLoader() {} explicit IconLoader() {}
static bool system_icons_; static bool system_icons_;

View File

@@ -155,11 +155,7 @@ void LocalRedirectServer::WriteTemplate() const {
QBuffer image_buffer; QBuffer image_buffer;
if (image_buffer.open(QIODevice::ReadWrite)) { if (image_buffer.open(QIODevice::ReadWrite)) {
QApplication::style() QApplication::style()->standardIcon(QStyle::SP_DialogOkButton).pixmap(16).toImage().save(&image_buffer, "PNG");
->standardIcon(QStyle::SP_DialogOkButton)
.pixmap(16)
.toImage()
.save(&image_buffer, "PNG");
page_data.replace("@IMAGE_DATA@"_L1, QString::fromUtf8(image_buffer.data().toBase64())); page_data.replace("@IMAGE_DATA@"_L1, QString::fromUtf8(image_buffer.data().toBase64()));
image_buffer.close(); image_buffer.close();
} }

View File

@@ -13,7 +13,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include <QtGlobal> #include <QtGlobal>
@@ -257,7 +257,7 @@ static QString ParsePrettyFunction(const char *pretty_function) {
} }
template <class T> template<class T>
static T CreateLogger(Level level, const QString &class_name, int line, const char *category) { static T CreateLogger(Level level, const QString &class_name, int line, const char *category) {
// Map the level to a string // Map the level to a string

View File

@@ -13,7 +13,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#ifndef LOGGING_H #ifndef LOGGING_H
#define LOGGING_H #define LOGGING_H
@@ -67,10 +67,10 @@ enum Level {
Level_Debug, Level_Debug,
}; };
void Init(); void Init();
void SetLevels(const QString &levels); void SetLevels(const QString &levels);
void DumpStackTrace(); void DumpStackTrace();
QDebug CreateLoggerFatal(const int line, const char *pretty_function, const char *category); QDebug CreateLoggerFatal(const int line, const char *pretty_function, const char *category);
QDebug CreateLoggerError(const int line, const char *pretty_function, const char *category); QDebug CreateLoggerError(const int line, const char *pretty_function, const char *category);

View File

@@ -58,7 +58,6 @@
#include <QShortcut> #include <QShortcut>
#include <QMessageBox> #include <QMessageBox>
#include <QErrorMessage> #include <QErrorMessage>
#include <QSettings>
#include <QColor> #include <QColor>
#include <QFrame> #include <QFrame>
#include <QItemSelectionModel> #include <QItemSelectionModel>
@@ -207,6 +206,11 @@
#include "organize/organizeerrordialog.h" #include "organize/organizeerrordialog.h"
#ifdef HAVE_VISUALIZATIONS
# include "visualizations/visualizationcontainer.h"
# include "engine/gstengine.h"
#endif
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
# include "core/windows7thumbbar.h" # include "core/windows7thumbbar.h"
#endif #endif
@@ -220,7 +224,7 @@
#endif #endif
#ifdef HAVE_SPARKLE #ifdef HAVE_SPARKLE
#include "core/sparkleupdater.h" # include "core/sparkleupdater.h"
#endif #endif
#ifdef HAVE_QTSPARKLE #ifdef HAVE_QTSPARKLE
@@ -228,7 +232,7 @@
#endif // HAVE_QTSPARKLE #endif // HAVE_QTSPARKLE
#ifdef HAVE_DISCORD_RPC #ifdef HAVE_DISCORD_RPC
#include "discord/richpresence.h" # include "discord/richpresence.h"
#endif #endif
using std::make_unique; using std::make_unique;
@@ -272,8 +276,8 @@ constexpr char QTSPARKLE_URL[] = "https://www.strawberrymusicplayer.org/sparkle-
# endif # endif
# else # else
# error "Unsupported OS for QtSparkle" # error "Unsupported OS for QtSparkle"
# endif // OS # endif // OS
} // namespace } // namespace
#endif // HAVE_QTSPARKLE #endif // HAVE_QTSPARKLE
@@ -296,9 +300,6 @@ MainWindow::MainWindow(Application *app,
#ifdef HAVE_DISCORD_RPC #ifdef HAVE_DISCORD_RPC
discord_rich_presence_(discord_rich_presence), discord_rich_presence_(discord_rich_presence),
#endif #endif
error_dialog_([this]() {
return new ErrorDialog(this);
}),
console_([app, this]() { console_([app, this]() {
Console *console = new Console(app->database()); Console *console = new Console(app->database());
QObject::connect(console, &Console::Error, this, &MainWindow::ShowErrorDialog); QObject::connect(console, &Console::Error, this, &MainWindow::ShowErrorDialog);
@@ -627,6 +628,12 @@ MainWindow::MainWindow(Application *app,
stop_menu->addAction(ui_->action_stop_after_this_track); stop_menu->addAction(ui_->action_stop_after_this_track);
ui_->stop_button->setMenu(stop_menu); ui_->stop_button->setMenu(stop_menu);
#ifdef HAVE_VISUALIZATIONS
QObject::connect(ui_->action_visualizations, &QAction::triggered, this, &MainWindow::ShowVisualizations);
#else
ui_->action_visualizations->setEnabled(false);
#endif
// Player connections // Player connections
QObject::connect(ui_->volume, &VolumeSlider::valueChanged, &*app_->player(), &Player::SetVolumeFromSlider); QObject::connect(ui_->volume, &VolumeSlider::valueChanged, &*app_->player(), &Player::SetVolumeFromSlider);
@@ -700,6 +707,9 @@ MainWindow::MainWindow(Application *app,
QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher); QObject::connect(&*app_->task_manager(), &TaskManager::PauseCollectionWatchers, &*app_->collection(), &CollectionLibrary::PauseWatcher);
QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &CollectionLibrary::ResumeWatcher); QObject::connect(&*app_->task_manager(), &TaskManager::ResumeCollectionWatchers, &*app_->collection(), &CollectionLibrary::ResumeWatcher);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->collection(), &CollectionLibrary::CurrentSongChanged);
QObject::connect(&*app_->player(), &Player::Stopped, &*app_->collection(), &CollectionLibrary::Stopped);
QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::LoadAlbumCover); QObject::connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::LoadAlbumCover);
QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded); QObject::connect(&*app_->current_albumcover_loader(), &CurrentAlbumCoverLoader::AlbumCoverLoaded, this, &MainWindow::AlbumCoverLoaded);
QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog); QObject::connect(album_cover_choice_controller_, &AlbumCoverChoiceController::Error, this, &MainWindow::ShowErrorDialog);
@@ -908,6 +918,7 @@ MainWindow::MainWindow(Application *app,
// Analyzer // Analyzer
QObject::connect(ui_->analyzer, &AnalyzerContainer::WheelEvent, this, &MainWindow::VolumeWheelEvent); QObject::connect(ui_->analyzer, &AnalyzerContainer::WheelEvent, this, &MainWindow::VolumeWheelEvent);
ui_->analyzer->SetVisualizationsAction(ui_->action_visualizations);
// Statusbar widgets // Statusbar widgets
ui_->playlist_summary->setMinimumWidth(QFontMetrics(font()).horizontalAdvance(u"WW selected of WW tracks - [ WW:WW ]"_s)); ui_->playlist_summary->setMinimumWidth(QFontMetrics(font()).horizontalAdvance(u"WW selected of WW tracks - [ WW:WW ]"_s));
@@ -980,27 +991,28 @@ MainWindow::MainWindow(Application *app,
// Load settings // Load settings
qLog(Debug) << "Loading settings"; qLog(Debug) << "Loading settings";
settings_.beginGroup(MainWindowSettings::kSettingsGroup); Settings settings;
settings.beginGroup(MainWindowSettings::kSettingsGroup);
// Set last used geometry to position window on the correct monitor // Set last used geometry to position window on the correct monitor
// Set window state only if the window was last maximized // Set window state only if the window was last maximized
if (settings_.contains("geometry")) { if (settings.contains("geometry")) {
restoreGeometry(settings_.value("geometry").toByteArray()); restoreGeometry(settings.value("geometry").toByteArray());
} }
if (!settings_.contains(MainWindowSettings::kSplitterState) || !ui_->splitter->restoreState(settings_.value(MainWindowSettings::kSplitterState).toByteArray())) { if (!settings.contains(MainWindowSettings::kSplitterState) || !ui_->splitter->restoreState(settings.value(MainWindowSettings::kSplitterState).toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20)); ui_->splitter->setSizes(QList<int>() << 20 << (width() - 20));
} }
ui_->tabs->setCurrentIndex(settings_.value("current_tab", 1).toInt()); ui_->tabs->setCurrentIndex(settings.value("current_tab", 1).toInt());
FancyTabWidget::Mode default_mode = FancyTabWidget::Mode::LargeSidebar; FancyTabWidget::Mode default_mode = FancyTabWidget::Mode::LargeSidebar;
FancyTabWidget::Mode tab_mode = static_cast<FancyTabWidget::Mode>(settings_.value("tab_mode", static_cast<int>(default_mode)).toInt()); FancyTabWidget::Mode tab_mode = static_cast<FancyTabWidget::Mode>(settings.value("tab_mode", static_cast<int>(default_mode)).toInt());
if (tab_mode == FancyTabWidget::Mode::None) tab_mode = default_mode; if (tab_mode == FancyTabWidget::Mode::None) tab_mode = default_mode;
ui_->tabs->SetMode(tab_mode); ui_->tabs->SetMode(tab_mode);
TabSwitched(); TabSwitched();
file_view_->SetPath(settings_.value("file_path", QDir::homePath()).toString()); file_view_->SetPath(settings.value("file_path", QDir::homePath()).toString());
// Users often collapse one side of the splitter by mistake and don't know how to restore it. This must be set after the state is restored above. // Users often collapse one side of the splitter by mistake and don't know how to restore it. This must be set after the state is restored above.
ui_->splitter->setChildrenCollapsible(false); ui_->splitter->setChildrenCollapsible(false);
@@ -1043,13 +1055,13 @@ MainWindow::MainWindow(Application *app,
case BehaviourSettings::StartupBehaviour::Remember: case BehaviourSettings::StartupBehaviour::Remember:
default:{ default:{
was_maximized_ = settings_.value(MainWindowSettings::kMaximized, true).toBool(); was_maximized_ = settings.value(MainWindowSettings::kMaximized, true).toBool();
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized); if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
was_minimized_ = settings_.value(MainWindowSettings::kMinimized, false).toBool(); was_minimized_ = settings.value(MainWindowSettings::kMinimized, false).toBool();
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized); if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
if (!systemtrayicon_->IsSystemTrayAvailable() || !systemtrayicon_->isVisible() || !settings_.value(MainWindowSettings::kHidden, false).toBool()) { if (!systemtrayicon_->IsSystemTrayAvailable() || !systemtrayicon_->isVisible() || !settings.value(MainWindowSettings::kHidden, false).toBool()) {
show(); show();
} }
break; break;
@@ -1057,7 +1069,7 @@ MainWindow::MainWindow(Application *app,
} }
#endif #endif
bool show_sidebar = settings_.value(MainWindowSettings::kShowSidebar, true).toBool(); bool show_sidebar = settings.value(MainWindowSettings::kShowSidebar, true).toBool();
ui_->sidebar_layout->setVisible(show_sidebar); ui_->sidebar_layout->setVisible(show_sidebar);
ui_->action_toggle_show_sidebar->setChecked(show_sidebar); ui_->action_toggle_show_sidebar->setChecked(show_sidebar);
@@ -1228,7 +1240,9 @@ void MainWindow::ReloadSettings() {
osd_->ReloadSettings(); osd_->ReloadSettings();
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value(MainWindowSettings::kSearchForCoverAuto, true).toBool()); s.beginGroup(MainWindowSettings::kSettingsGroup);
album_cover_choice_controller_->search_cover_auto_action()->setChecked(s.value(MainWindowSettings::kSearchForCoverAuto, true).toBool());
s.endGroup();
#ifdef HAVE_SUBSONIC #ifdef HAVE_SUBSONIC
s.beginGroup(SubsonicSettings::kSettingsGroup); s.beginGroup(SubsonicSettings::kSettingsGroup);
@@ -1345,8 +1359,11 @@ void MainWindow::SaveSettings() {
ui_->playlist->view()->SaveSettings(); ui_->playlist->view()->SaveSettings();
app_->scrobbler()->WriteCache(); app_->scrobbler()->WriteCache();
settings_.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->isChecked()); Settings s;
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, album_cover_choice_controller_->search_cover_auto_action()->isChecked()); s.beginGroup(MainWindowSettings::kSettingsGroup);
s.setValue(MainWindowSettings::kShowSidebar, ui_->action_toggle_show_sidebar->isChecked());
s.setValue(MainWindowSettings::kSearchForCoverAuto, album_cover_choice_controller_->search_cover_auto_action()->isChecked());
s.endGroup();
} }
@@ -1587,23 +1604,35 @@ void MainWindow::ToggleSidebar(const bool checked) {
ui_->sidebar_layout->setVisible(checked); ui_->sidebar_layout->setVisible(checked);
TabSwitched(); TabSwitched();
settings_.setValue(MainWindowSettings::kShowSidebar, checked);
Settings s;
s.beginGroup(MainWindowSettings::kSettingsGroup);
s.setValue(MainWindowSettings::kShowSidebar, checked);
s.endGroup();
} }
void MainWindow::ToggleSearchCoverAuto(const bool checked) { void MainWindow::ToggleSearchCoverAuto(const bool checked) {
settings_.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
Settings s;
s.beginGroup(MainWindowSettings::kSettingsGroup);
s.setValue(MainWindowSettings::kSearchForCoverAuto, checked);
s.endGroup();
} }
void MainWindow::SaveGeometry() { void MainWindow::SaveGeometry() {
if (!initialized_) return; if (!initialized_) return;
settings_.setValue(MainWindowSettings::kMaximized, isMaximized()); Settings s;
settings_.setValue(MainWindowSettings::kMinimized, isMinimized()); s.beginGroup(MainWindowSettings::kSettingsGroup);
settings_.setValue(MainWindowSettings::kHidden, isHidden()); s.setValue(MainWindowSettings::kMaximized, isMaximized());
settings_.setValue(MainWindowSettings::kGeometry, saveGeometry()); s.setValue(MainWindowSettings::kMinimized, isMinimized());
settings_.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState()); s.setValue(MainWindowSettings::kHidden, isHidden());
s.setValue(MainWindowSettings::kGeometry, saveGeometry());
s.setValue(MainWindowSettings::kSplitterState, ui_->splitter->saveState());
s.endGroup();
} }
@@ -1745,7 +1774,12 @@ void MainWindow::SetHiddenInTray(const bool hidden) {
} }
void MainWindow::FilePathChanged(const QString &path) { void MainWindow::FilePathChanged(const QString &path) {
settings_.setValue("file_path", path);
Settings s;
s.beginGroup(MainWindowSettings::kSettingsGroup);
s.setValue("file_path", path);
s.endGroup();
} }
void MainWindow::Seeked(const qint64 microseconds) { void MainWindow::Seeked(const qint64 microseconds) {
@@ -2327,7 +2361,9 @@ void MainWindow::EditValue() {
void MainWindow::AddFile() { void MainWindow::AddFile() {
// Last used directory // Last used directory
QString directory = settings_.value("add_media_path", QDir::currentPath()).toString(); Settings s;
s.beginGroup(MainWindowSettings::kSettingsGroup);
QString directory = s.value("add_media_path", QDir::currentPath()).toString();
PlaylistParser parser(app_->tagreader_client(), app_->collection_backend()); PlaylistParser parser(app_->tagreader_client(), app_->collection_backend());
@@ -2337,7 +2373,7 @@ void MainWindow::AddFile() {
if (filenames.isEmpty()) return; if (filenames.isEmpty()) return;
// Save last used directory // Save last used directory
settings_.setValue("add_media_path", filenames[0]); s.setValue("add_media_path", filenames[0]);
// Convert to URLs // Convert to URLs
QList<QUrl> urls; QList<QUrl> urls;
@@ -2355,14 +2391,16 @@ void MainWindow::AddFile() {
void MainWindow::AddFolder() { void MainWindow::AddFolder() {
// Last used directory // Last used directory
QString directory = settings_.value("add_folder_path", QDir::currentPath()).toString(); Settings s;
s.beginGroup(MainWindowSettings::kSettingsGroup);
QString directory = s.value("add_folder_path", QDir::currentPath()).toString();
// Show dialog // Show dialog
directory = QFileDialog::getExistingDirectory(this, tr("Add folder"), directory); directory = QFileDialog::getExistingDirectory(this, tr("Add folder"), directory);
if (directory.isEmpty()) return; if (directory.isEmpty()) return;
// Save last used directory // Save last used directory
settings_.setValue("add_folder_path", directory); s.setValue("add_folder_path", directory);
// Add media // Add media
MimeData *mimedata = new MimeData; MimeData *mimedata = new MimeData;
@@ -3321,7 +3359,7 @@ void MainWindow::PlaylistDelete() {
if (DeleteConfirmationDialog::warning(files) != QDialogButtonBox::Yes) return; if (DeleteConfirmationDialog::warning(files) != QDialogButtonBox::Yes) return;
if (app_->player()->GetState() == EngineBase::State::Playing && app_->playlist_manager()->current()->rowCount() == selected_songs.count()) { if (app_->player()->GetState() == EngineBase::State::Playing && app_->playlist_manager()->current() == app_->playlist_manager()->active() && app_->playlist_manager()->current()->rowCount() == selected_songs.count()) {
app_->player()->Stop(); app_->player()->Stop();
} }
@@ -3378,3 +3416,24 @@ void MainWindow::FocusSearchField() {
} }
} }
void MainWindow::ShowVisualizations() {
#ifdef HAVE_VISUALIZATIONS
if (!visualization_) {
visualization_.reset(new VisualizationContainer);
visualization_->SetActions(ui_->action_previous_track, ui_->action_play_pause, ui_->action_stop, ui_->action_next_track);
connect(&*app_->player(), &Player::Stopped, &*visualization_, &VisualizationContainer::Stopped);
connect(&*app_->player(), &Player::ForceShowOSD, &*visualization_, &VisualizationContainer::SongMetadataChanged);
connect(&*app_->playlist_manager(), &PlaylistManager::CurrentSongChanged, &*visualization_, &VisualizationContainer::SongMetadataChanged);
visualization_->SetEngine(qobject_cast<GstEngine*>(&*app_->player()->engine()));
}
visualization_->show();
#endif // HAVE_VISUALIZATIONS
}

View File

@@ -45,7 +45,6 @@
#include <QImage> #include <QImage>
#include <QPixmap> #include <QPixmap>
#include <QTimer> #include <QTimer>
#include <QSettings>
#include <QtEvents> #include <QtEvents>
#include "includes/scoped_ptr.h" #include "includes/scoped_ptr.h"
@@ -53,7 +52,6 @@
#include "includes/lazy.h" #include "includes/lazy.h"
#include "core/platforminterface.h" #include "core/platforminterface.h"
#include "core/song.h" #include "core/song.h"
#include "core/settings.h"
#include "core/commandlineoptions.h" #include "core/commandlineoptions.h"
#include "tagreader/tagreaderclient.h" #include "tagreader/tagreaderclient.h"
#include "osd/osdbase.h" #include "osd/osdbase.h"
@@ -99,6 +97,7 @@ class Windows7ThumbBar;
class AddStreamDialog; class AddStreamDialog;
class LastFMImportDialog; class LastFMImportDialog;
class RadioViewContainer; class RadioViewContainer;
class VisualizationContainer;
#ifdef HAVE_DISCORD_RPC #ifdef HAVE_DISCORD_RPC
namespace discord { namespace discord {
@@ -283,9 +282,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
public Q_SLOTS: public Q_SLOTS:
void CommandlineOptionsReceived(const QByteArray &string_options); void CommandlineOptionsReceived(const QByteArray &string_options);
void Raise(); void Raise();
void ShowVisualizations();
private: private:
void SaveSettings(); void SaveSettings();
static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata); static void ApplyAddBehaviour(const BehaviourSettings::AddBehaviour b, MimeData *mimedata);
@@ -363,6 +362,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
LastFMImportDialog *lastfm_import_dialog_; LastFMImportDialog *lastfm_import_dialog_;
#ifdef HAVE_VISUALIZATIONS
ScopedPtr<VisualizationContainer> visualization_;
#endif
QAction *collection_show_all_; QAction *collection_show_all_;
QAction *collection_show_duplicates_; QAction *collection_show_duplicates_;
QAction *collection_show_untagged_; QAction *collection_show_untagged_;
@@ -391,7 +394,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
QTimer *track_position_timer_; QTimer *track_position_timer_;
QTimer *track_slider_timer_; QTimer *track_slider_timer_;
Settings settings_;
bool keep_running_; bool keep_running_;
bool playing_widget_; bool playing_widget_;
@@ -415,7 +417,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
bool playlists_loaded_; bool playlists_loaded_;
bool delete_files_; bool delete_files_;
std::optional<CommandlineOptions> options_; std::optional<CommandlineOptions> options_;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@@ -517,6 +517,7 @@
<addaction name="action_cover_manager"/> <addaction name="action_cover_manager"/>
<addaction name="action_equalizer"/> <addaction name="action_equalizer"/>
<addaction name="action_transcoder"/> <addaction name="action_transcoder"/>
<addaction name="action_visualizations"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_update_collection"/> <addaction name="action_update_collection"/>
<addaction name="action_full_collection_scan"/> <addaction name="action_full_collection_scan"/>
@@ -863,6 +864,11 @@
<string>Import data from last.fm...</string> <string>Import data from last.fm...</string>
</property> </property>
</action> </action>
<action name="action_visualizations">
<property name="text">
<string>Visualizations</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

View File

@@ -22,7 +22,7 @@
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
MemoryDatabase::MemoryDatabase(SharedPtr<TaskManager> task_manager, QObject *parent) MemoryDatabase::MemoryDatabase(SharedPtr<TaskManager> task_manager, QObject *parent)
: Database(task_manager, parent, u":memory:"_s) {} : Database(task_manager, parent, u":memory:"_s) {}
MemoryDatabase::~MemoryDatabase() { MemoryDatabase::~MemoryDatabase() {
// Make sure Qt doesn't reuse the same database // Make sure Qt doesn't reuse the same database

View File

@@ -26,13 +26,13 @@
#include "mimedata.h" #include "mimedata.h"
MimeData::MimeData(const bool clear, const bool play_now, const bool enqueue, const bool enqueue_next_now, const bool open_in_new_playlist, QObject *parent) MimeData::MimeData(const bool clear, const bool play_now, const bool enqueue, const bool enqueue_next_now, const bool open_in_new_playlist, QObject *parent)
: override_user_settings_(false), : override_user_settings_(false),
clear_first_(clear), clear_first_(clear),
play_now_(play_now), play_now_(play_now),
enqueue_now_(enqueue), enqueue_now_(enqueue),
enqueue_next_now_(enqueue_next_now), enqueue_next_now_(enqueue_next_now),
open_in_new_playlist_(open_in_new_playlist), open_in_new_playlist_(open_in_new_playlist),
from_doubleclick_(false) { from_doubleclick_(false) {
Q_UNUSED(parent); Q_UNUSED(parent);

View File

@@ -57,7 +57,7 @@ class MusicStorage {
Transcode_Unsupported = 3, Transcode_Unsupported = 3,
}; };
using ProgressFunction = std::function<void(float progress)>; using ProgressFunction = std::function<void (float progress)>;
struct CopyJob { struct CopyJob {
CopyJob() : overwrite_(false), remove_original_(false), albumcover_(false) {} CopyJob() : overwrite_(false), remove_original_(false), albumcover_(false) {}

View File

@@ -29,6 +29,7 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkInformation>
#include "networkaccessmanager.h" #include "networkaccessmanager.h"
#include "threadsafenetworkdiskcache.h" #include "threadsafenetworkdiskcache.h"
@@ -41,6 +42,22 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent)
setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
setCache(new ThreadSafeNetworkDiskCache(this)); setCache(new ThreadSafeNetworkDiskCache(this));
// Handle network state changes after system suspend/resume
// QNetworkInformation provides cross-platform network reachability monitoring in Qt 6
if (QNetworkInformation::loadDefaultBackend()) {
QNetworkInformation *network_info = QNetworkInformation::instance();
if (network_info) {
QObject::connect(network_info, &QNetworkInformation::reachabilityChanged, this, [this](QNetworkInformation::Reachability reachability) {
if (reachability == QNetworkInformation::Reachability::Online) {
// Clear connection cache to force reconnection after network becomes available
// This fixes issues after system suspend/resume
clearConnectionCache();
clearAccessCache();
}
});
}
}
} }
QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &network_request, QIODevice *outgoing_data) { QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &network_request, QIODevice *outgoing_data) {
@@ -50,7 +67,7 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
user_agent = network_request.header(QNetworkRequest::UserAgentHeader).toByteArray(); user_agent = network_request.header(QNetworkRequest::UserAgentHeader).toByteArray();
} }
else { else {
user_agent = QStringLiteral("%1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()).toUtf8(); user_agent = "Strawberry Music Player";
} }
QNetworkRequest new_network_request(network_request); QNetworkRequest new_network_request(network_request);

View File

@@ -121,8 +121,8 @@ class Player : public PlayerInterface {
void PlayPlaylistInternal(const EngineBase::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name); void PlayPlaylistInternal(const EngineBase::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
void FatalError(); void FatalError();
void ValidSongRequested(const QUrl&); void ValidSongRequested(const QUrl &url);
void InvalidSongRequested(const QUrl&); void InvalidSongRequested(const QUrl &url);
void HandleLoadResult(const UrlHandler::LoadResult &result); void HandleLoadResult(const UrlHandler::LoadResult &result);

View File

@@ -40,7 +40,6 @@ class QtFSListener : public FileSystemWatcherInterface {
private: private:
QFileSystemWatcher watcher_; QFileSystemWatcher watcher_;
}; };
#endif // QTFSLISTENER_H #endif // QTFSLISTENER_H

View File

@@ -9,7 +9,7 @@
#if defined(__OBJC__) #if defined(__OBJC__)
@class NSAutoreleasePool; @class NSAutoreleasePool;
#else // __OBJC__ #else // __OBJC__
class NSAutoreleasePool; class NSAutoreleasePool;
#endif // __OBJC__ #endif // __OBJC__
@@ -28,8 +28,9 @@ class ScopedNSAutoreleasePool {
// Only use then when you're certain the items currently in the pool are // Only use then when you're certain the items currently in the pool are
// no longer needed. // no longer needed.
void Recycle(); void Recycle();
private: private:
NSAutoreleasePool* autorelease_pool_; NSAutoreleasePool *autorelease_pool_;
private: private:
Q_DISABLE_COPY(ScopedNSAutoreleasePool); Q_DISABLE_COPY(ScopedNSAutoreleasePool);

View File

@@ -70,7 +70,7 @@ QModelIndex SimpleTreeModel<T>::ItemToIndex(T *item) const {
return createIndex(item->row, 0, item); return createIndex(item->row, 0, item);
} }
template <typename T> template<typename T>
int SimpleTreeModel<T>::columnCount(const QModelIndex&) const { int SimpleTreeModel<T>::columnCount(const QModelIndex&) const {
return 1; return 1;
} }

View File

@@ -353,6 +353,8 @@ struct Song::Private : public QSharedData {
std::optional<double> ebur128_integrated_loudness_lufs_; std::optional<double> ebur128_integrated_loudness_lufs_;
std::optional<double> ebur128_loudness_range_lu_; std::optional<double> ebur128_loudness_range_lu_;
int id3v2_version_; // ID3v2 tag version (3 or 4), 0 if not applicable or unknown
bool init_from_file_; // Whether this song was loaded from a file using taglib. bool init_from_file_; // Whether this song was loaded from a file using taglib.
bool suspicious_tags_; // Whether our encoding guesser thinks these tags might be incorrectly encoded. bool suspicious_tags_; // Whether our encoding guesser thinks these tags might be incorrectly encoded.
@@ -400,6 +402,8 @@ Song::Private::Private(const Source source)
rating_(-1), rating_(-1),
bpm_(-1), bpm_(-1),
id3v2_version_(0),
init_from_file_(false), init_from_file_(false),
suspicious_tags_(false) suspicious_tags_(false)
@@ -510,6 +514,8 @@ const QString &Song::musicbrainz_work_id() const { return d->musicbrainz_work_id
std::optional<double> Song::ebur128_integrated_loudness_lufs() const { return d->ebur128_integrated_loudness_lufs_; } std::optional<double> Song::ebur128_integrated_loudness_lufs() const { return d->ebur128_integrated_loudness_lufs_; }
std::optional<double> Song::ebur128_loudness_range_lu() const { return d->ebur128_loudness_range_lu_; } std::optional<double> Song::ebur128_loudness_range_lu() const { return d->ebur128_loudness_range_lu_; }
int Song::id3v2_version() const { return d->id3v2_version_; }
QString *Song::mutable_title() { return &d->title_; } QString *Song::mutable_title() { return &d->title_; }
QString *Song::mutable_album() { return &d->album_; } QString *Song::mutable_album() { return &d->album_; }
QString *Song::mutable_artist() { return &d->artist_; } QString *Song::mutable_artist() { return &d->artist_; }
@@ -624,6 +630,8 @@ void Song::set_musicbrainz_work_id(const QString &v) { d->musicbrainz_work_id_ =
void Song::set_ebur128_integrated_loudness_lufs(const std::optional<double> v) { d->ebur128_integrated_loudness_lufs_ = v; } void Song::set_ebur128_integrated_loudness_lufs(const std::optional<double> v) { d->ebur128_integrated_loudness_lufs_ = v; }
void Song::set_ebur128_loudness_range_lu(const std::optional<double> v) { d->ebur128_loudness_range_lu_ = v; } void Song::set_ebur128_loudness_range_lu(const std::optional<double> v) { d->ebur128_loudness_range_lu_ = v; }
void Song::set_id3v2_version(const int v) { d->id3v2_version_ = v; }
void Song::set_init_from_file(const bool v) { d->init_from_file_ = v; } void Song::set_init_from_file(const bool v) { d->init_from_file_ = v; }
void Song::set_stream_url(const QUrl &v) { d->stream_url_ = v; } void Song::set_stream_url(const QUrl &v) { d->stream_url_ = v; }
@@ -720,17 +728,17 @@ bool Song::write_tags_supported() const {
bool Song::additional_tags_supported() const { bool Song::additional_tags_supported() const {
return d->filetype_ == FileType::FLAC || return d->filetype_ == FileType::FLAC ||
d->filetype_ == FileType::WavPack || d->filetype_ == FileType::WavPack ||
d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggFlac ||
d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggVorbis ||
d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::OggOpus ||
d->filetype_ == FileType::OggSpeex || d->filetype_ == FileType::OggSpeex ||
d->filetype_ == FileType::MPEG || d->filetype_ == FileType::MPEG ||
d->filetype_ == FileType::MP4 || d->filetype_ == FileType::MP4 ||
d->filetype_ == FileType::MPC || d->filetype_ == FileType::MPC ||
d->filetype_ == FileType::APE || d->filetype_ == FileType::APE ||
d->filetype_ == FileType::WAV || d->filetype_ == FileType::WAV ||
d->filetype_ == FileType::AIFF; d->filetype_ == FileType::AIFF;
} }
@@ -745,16 +753,16 @@ bool Song::composer_supported() const {
bool Song::performer_supported() const { bool Song::performer_supported() const {
return d->filetype_ == FileType::FLAC || return d->filetype_ == FileType::FLAC ||
d->filetype_ == FileType::WavPack || d->filetype_ == FileType::WavPack ||
d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggFlac ||
d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggVorbis ||
d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::OggOpus ||
d->filetype_ == FileType::OggSpeex || d->filetype_ == FileType::OggSpeex ||
d->filetype_ == FileType::MPEG || d->filetype_ == FileType::MPEG ||
d->filetype_ == FileType::MPC || d->filetype_ == FileType::MPC ||
d->filetype_ == FileType::APE || d->filetype_ == FileType::APE ||
d->filetype_ == FileType::WAV || d->filetype_ == FileType::WAV ||
d->filetype_ == FileType::AIFF; d->filetype_ == FileType::AIFF;
} }
@@ -773,18 +781,18 @@ bool Song::compilation_supported() const {
bool Song::rating_supported() const { bool Song::rating_supported() const {
return d->filetype_ == FileType::FLAC || return d->filetype_ == FileType::FLAC ||
d->filetype_ == FileType::WavPack || d->filetype_ == FileType::WavPack ||
d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggFlac ||
d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggVorbis ||
d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::OggOpus ||
d->filetype_ == FileType::OggSpeex || d->filetype_ == FileType::OggSpeex ||
d->filetype_ == FileType::MPEG || d->filetype_ == FileType::MPEG ||
d->filetype_ == FileType::MP4 || d->filetype_ == FileType::MP4 ||
d->filetype_ == FileType::ASF || d->filetype_ == FileType::ASF ||
d->filetype_ == FileType::MPC || d->filetype_ == FileType::MPC ||
d->filetype_ == FileType::APE || d->filetype_ == FileType::APE ||
d->filetype_ == FileType::WAV || d->filetype_ == FileType::WAV ||
d->filetype_ == FileType::AIFF; d->filetype_ == FileType::AIFF;
} }
@@ -797,19 +805,19 @@ bool Song::lyrics_supported() const {
} }
bool Song::albumartistsort_supported() const { bool Song::albumartistsort_supported() const {
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG; return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
} }
bool Song::albumsort_supported() const { bool Song::albumsort_supported() const {
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG; return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
} }
bool Song::artistsort_supported() const { bool Song::artistsort_supported() const {
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG; return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
} }
bool Song::composersort_supported() const { bool Song::composersort_supported() const {
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG; return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
} }
bool Song::performersort_supported() const { bool Song::performersort_supported() const {
@@ -818,21 +826,25 @@ bool Song::performersort_supported() const {
} }
bool Song::titlesort_supported() const { bool Song::titlesort_supported() const {
return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::MPEG; return d->filetype_ == FileType::FLAC || d->filetype_ == FileType::OggFlac || d->filetype_ == FileType::OggVorbis || d->filetype_ == FileType::OggOpus || d->filetype_ == FileType::MPEG;
} }
bool Song::save_embedded_cover_supported(const FileType filetype) { bool Song::save_embedded_cover_supported(const FileType filetype) {
return filetype == FileType::FLAC || return filetype == FileType::FLAC ||
filetype == FileType::OggVorbis || filetype == FileType::OggVorbis ||
filetype == FileType::OggOpus || filetype == FileType::OggOpus ||
filetype == FileType::MPEG || filetype == FileType::MPEG ||
filetype == FileType::MP4 || filetype == FileType::MP4 ||
filetype == FileType::WAV || filetype == FileType::WAV ||
filetype == FileType::AIFF; filetype == FileType::AIFF;
} }
bool Song::id3v2_tags_supported() const {
return d->filetype_ == FileType::MPEG || d->filetype_ == FileType::WAV || d->filetype_ == FileType::AIFF;
}
int Song::ColumnIndex(const QString &field) { int Song::ColumnIndex(const QString &field) {
return static_cast<int>(kRowIdColumns.indexOf(field)); return static_cast<int>(kRowIdColumns.indexOf(field));
@@ -1444,7 +1456,7 @@ Song::FileType Song::FiletypeByExtension(const QString &ext) {
if (ext.compare("ape"_L1, Qt::CaseInsensitive) == 0) return FileType::APE; if (ext.compare("ape"_L1, Qt::CaseInsensitive) == 0) return FileType::APE;
if (ext.compare("mod"_L1, Qt::CaseInsensitive) == 0 || if (ext.compare("mod"_L1, Qt::CaseInsensitive) == 0 ||
ext.compare("module"_L1, Qt::CaseInsensitive) == 0 || ext.compare("module"_L1, Qt::CaseInsensitive) == 0 ||
ext.compare("nst"_L1, Qt::CaseInsensitive) == 0|| ext.compare("nst"_L1, Qt::CaseInsensitive) == 0 ||
ext.compare("wow"_L1, Qt::CaseInsensitive) == 0) return FileType::MOD; ext.compare("wow"_L1, Qt::CaseInsensitive) == 0) return FileType::MOD;
if (ext.compare("s3m"_L1, Qt::CaseInsensitive) == 0) return FileType::S3M; if (ext.compare("s3m"_L1, Qt::CaseInsensitive) == 0) return FileType::S3M;
if (ext.compare("xm"_L1, Qt::CaseInsensitive) == 0) return FileType::XM; if (ext.compare("xm"_L1, Qt::CaseInsensitive) == 0) return FileType::XM;
@@ -1689,7 +1701,7 @@ void Song::InitFromItdb(Itdb_Track *track, const QString &prefix) {
d->bitrate_ = track->bitrate; d->bitrate_ = track->bitrate;
d->samplerate_ = track->samplerate; d->samplerate_ = track->samplerate;
d->bitdepth_ = -1; //track->bitdepth; d->bitdepth_ = -1; // track->bitdepth;
d->source_ = Source::Device; d->source_ = Source::Device;
QString filename = QString::fromLocal8Bit(track->ipod_path); QString filename = QString::fromLocal8Bit(track->ipod_path);
@@ -2150,4 +2162,3 @@ QString Song::GetNameForNewPlaylist(const SongList &songs) {
return result; return result;
} }

View File

@@ -234,6 +234,8 @@ class Song {
std::optional<double> ebur128_integrated_loudness_lufs() const; std::optional<double> ebur128_integrated_loudness_lufs() const;
std::optional<double> ebur128_loudness_range_lu() const; std::optional<double> ebur128_loudness_range_lu() const;
int id3v2_version() const;
QString *mutable_title(); QString *mutable_title();
QString *mutable_album(); QString *mutable_album();
QString *mutable_artist(); QString *mutable_artist();
@@ -349,6 +351,8 @@ class Song {
void set_ebur128_integrated_loudness_lufs(const std::optional<double> v); void set_ebur128_integrated_loudness_lufs(const std::optional<double> v);
void set_ebur128_loudness_range_lu(const std::optional<double> v); void set_ebur128_loudness_range_lu(const std::optional<double> v);
void set_id3v2_version(const int v);
void set_init_from_file(const bool v); void set_init_from_file(const bool v);
void set_stream_url(const QUrl &v); void set_stream_url(const QUrl &v);
@@ -439,6 +443,8 @@ class Song {
static bool save_embedded_cover_supported(const FileType filetype); static bool save_embedded_cover_supported(const FileType filetype);
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); }; bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
bool id3v2_tags_supported() const;
static int ColumnIndex(const QString &field); static int ColumnIndex(const QString &field);
static QString JoinSpec(const QString &table); static QString JoinSpec(const QString &table);

View File

@@ -98,6 +98,9 @@ SongLoader::SongLoader(const SharedPtr<UrlHandlers> url_handlers,
QObject::connect(timeout_timer_, &QTimer::timeout, this, &SongLoader::Timeout); QObject::connect(timeout_timer_, &QTimer::timeout, this, &SongLoader::Timeout);
QObject::connect(playlist_parser_, &PlaylistParser::Error, this, &SongLoader::ParserError);
QObject::connect(cue_parser_, &CueParser::Error, this, &SongLoader::ParserError);
} }
SongLoader::~SongLoader() { SongLoader::~SongLoader() {
@@ -106,6 +109,10 @@ SongLoader::~SongLoader() {
} }
void SongLoader::ParserError(const QString &error) {
errors_ << error;
}
SongLoader::Result SongLoader::Load(const QUrl &url) { SongLoader::Result SongLoader::Load(const QUrl &url) {
if (url.isEmpty()) return Result::Error; if (url.isEmpty()) return Result::Error;
@@ -287,6 +294,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
} }
if (parser) { // It's a playlist! if (parser) { // It's a playlist!
QObject::connect(parser, &ParserBase::Error, this, &SongLoader::ParserError, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
qLog(Debug) << "Parsing using" << parser->name(); qLog(Debug) << "Parsing using" << parser->name();
LoadPlaylist(parser, filename); LoadPlaylist(parser, filename);
return Result::Success; return Result::Success;
@@ -667,9 +675,9 @@ void SongLoader::EndOfStreamReached() {
// Do the magic on the data we have already // Do the magic on the data we have already
MagicReady(); MagicReady();
if (state_ == State::Finished) break; if (state_ == State::Finished) break;
// It looks like a playlist, so parse it // It looks like a playlist, so parse it
[[fallthrough]]; [[fallthrough]];
case State::WaitingForData: case State::WaitingForData:
// It's a playlist and we've got all the data - finish and parse it // It's a playlist and we've got all the data - finish and parse it
StopTypefindAsync(true); StopTypefindAsync(true);
@@ -706,6 +714,10 @@ void SongLoader::MagicReady() {
StopTypefindAsync(true); StopTypefindAsync(true);
} }
if (parser_) {
QObject::connect(parser_, &ParserBase::Error, this, &SongLoader::ParserError, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
}
state_ = State::WaitingForData; state_ = State::WaitingForData;
if (!IsPipelinePlaying()) { if (!IsPipelinePlaying()) {
@@ -784,4 +796,3 @@ void SongLoader::CleanupPipeline() {
state_ = State::Finished; state_ = State::Finished;
} }

View File

@@ -99,6 +99,7 @@ class SongLoader : public QObject {
void ScheduleTimeout(); void ScheduleTimeout();
void Timeout(); void Timeout();
void StopTypefind(); void StopTypefind();
void ParserError(const QString &error);
#ifdef HAVE_AUDIOCD #ifdef HAVE_AUDIOCD
void AudioCDTracksLoadErrorSlot(const QString &error); void AudioCDTracksLoadErrorSlot(const QString &error);
@@ -171,7 +172,6 @@ class SongLoader : public QObject {
QStringList errors_; QStringList errors_;
bool success_; bool success_;
}; };
#endif // SONGLOADER_H #endif // SONGLOADER_H

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