Compare commits

...

176 Commits

Author SHA1 Message Date
Jonas Kvinge
47b5dea95c Release 0.7.2 2020-08-15 23:01:56 +02:00
Jonas Kvinge
b0df63f1e8 Fix translations dir 2020-08-15 23:01:28 +02:00
Jonas Kvinge
3c4209b676 Add more compilation titles 2020-08-15 17:33:54 +02:00
Jonas Kvinge
d51b9a8e0e Add more compilation titles 2020-08-15 17:29:30 +02:00
Jonas Kvinge
3b56125bd2 Increase maximum time step for seeking to 120
Fixes #509
2020-08-15 15:18:58 +02:00
Jonas Kvinge
6e69e39007 Use static_cast instead for destroyed object 2020-08-15 15:16:06 +02:00
Jonas Kvinge
97208cb329 Add --auto to urpmi command for Mageia CI 2020-08-15 11:55:49 +02:00
Jonas Kvinge
414a4a97fb Use unicode option when replacing non-words
Fixes #513
2020-08-15 11:43:14 +02:00
Jonas Kvinge
2a809f96c4 Update Changelog 2020-08-15 11:31:29 +02:00
Jonas Kvinge
17799b03f3 Fix installation directory for translations
Fixes #512
2020-08-15 11:08:47 +02:00
Jonas Kvinge
efc55fc648 Fix typo in snapcraft.yaml 2020-08-15 02:36:54 +02:00
Jonas Kvinge
22bd41211a Turn back git revision 2020-08-15 02:36:37 +02:00
Jonas Kvinge
4cb0171bd0 Release 0.7.1 2020-08-15 00:44:28 +02:00
Strawbs Bot
ce6c5af72c Update translations 2020-08-15 00:25:52 +02:00
Jonas Kvinge
171575256c Remove broken iPhone (libimobiledevice) support
Fixes #212
2020-08-14 21:38:08 +02:00
Jonas Kvinge
d3664dcf78 Set QNetworkRequest::RedirectPolicyAttribute with Qt >= 5.9 2020-08-14 20:31:04 +02:00
Jonas Kvinge
0788981783 Set QNetworkRequest::RedirectPolicyAttribute with Qt >= 5.9 2020-08-14 20:20:41 +02:00
Jonas Kvinge
aeee7c02d5 Update Changelog 2020-08-14 18:39:56 +02:00
Jonas Kvinge
6e49a50461 Update Changelog 2020-08-14 18:38:50 +02:00
Jonas Kvinge
fbc99827ab Revert "Turn off sort indicators for playlist"
This reverts commit 7b50ec4630.
2020-08-14 17:30:27 +02:00
Jonas Kvinge
3b134320c4 Fix minor issue in cue parser with date and genre 2020-08-13 21:14:12 +02:00
Jonas Kvinge
c315e5016d Change mtime and ctime to qint64 2020-08-13 21:09:06 +02:00
Jonas Kvinge
7aebd6ed57 Only install translations if HAVE_TRANSLATIONS is set 2020-08-13 21:05:09 +02:00
Jonas Kvinge
1a8ca06495 Only install translations when INSTALL_TRANSLATIONS is set 2020-08-13 20:55:53 +02:00
Jonas Kvinge
a27ae7e4a6 Add CMake option to install translations
Fixes #485
2020-08-13 19:53:36 +02:00
Strawbs Bot
dd0ab897aa Update translations 2020-08-13 01:03:32 +02:00
Jonas Kvinge
00ad92fb6d Hide unavailable collection context menu actions 2020-08-12 21:34:42 +02:00
Jonas Kvinge
f84128ecbd Remove unused collection playlist container type 2020-08-12 21:33:38 +02:00
Jonas Kvinge
ba89e0f4e3 Fix MockPlaylistItem 2020-08-12 20:03:45 +02:00
Jonas Kvinge
832d36a4d4 Add Qt 6 option to strawberry.nsi 2020-08-12 18:07:21 +02:00
Jonas Kvinge
0b437b3bfb Use standard text color for links in about
Fixes #508
2020-08-12 17:27:08 +02:00
Jonas Kvinge
7b50ec4630 Turn off sort indicators for playlist
Fixes #511
2020-08-12 16:56:28 +02:00
Jonas Kvinge
4ddb13abac Increase maximum time step for seeking to 60
Fixes #509
2020-08-12 16:31:32 +02:00
Jonas Kvinge
d2ac081177 Fix taglib cmake type size checks 2020-08-11 16:23:07 +02:00
Strawbs Bot
9692fbf15b Update translations 2020-08-11 01:02:03 +02:00
Jonas Kvinge
be966488e8 Fix OSD D-Bus assertion with Qt 6 2020-08-10 23:05:07 +02:00
Jonas Kvinge
0ce613264f Make sure to always use original metadata when editing tags 2020-08-10 21:32:14 +02:00
Jonas Kvinge
34634d776e Make sure to always use original metadata when editing tags 2020-08-10 21:27:56 +02:00
Jonas Kvinge
673ded3819 Add proper check for collection song in edit tag dialog 2020-08-10 21:27:27 +02:00
Jonas Kvinge
b9a94ad3ae Default originalyear to -1 2020-08-10 18:06:20 +02:00
Jonas Kvinge
3f80b330cc Log artist and album name 2020-08-10 18:05:52 +02:00
Jonas Kvinge
01632d538c Decrease score for more compilation albums 2020-08-10 17:39:40 +02:00
Strawbs Bot
b0a9b1cd09 Update translations 2020-08-10 01:05:14 +02:00
Jonas Kvinge
1f772081fd Only update temporary metadata when set
Fixes #507
2020-08-10 00:32:57 +02:00
Jonas Kvinge
4ae54dbaad Decrease score for more compilation albums 2020-08-09 20:58:27 +02:00
Jonas Kvinge
e47f4ff731 Fix musixmatch cover size 2020-08-09 20:15:24 +02:00
Jonas Kvinge
465369d79e Base initial score on album cover sizes retrieved from API 2020-08-09 20:10:53 +02:00
Jonas Kvinge
15ddf6ff20 Save and restore playlist scrollbar position when switching between playlists 2020-08-09 14:00:56 +02:00
Jonas Kvinge
16a753bd95 Treat erors returned by the URL handler as non fatal
Fixes #505
2020-08-09 02:52:18 +02:00
Jonas Kvinge
c15103636c Fix NotificationPreview signal slot 2020-08-09 02:07:22 +02:00
Jonas Kvinge
8a5f82ee7d Tidal: Only return streamable songs in result
Fixes #505
2020-08-09 01:59:28 +02:00
Jonas Kvinge
5ec33ec821 Tidal: Show API error instead of network error when available 2020-08-09 01:50:03 +02:00
Jonas Kvinge
ab7d383cf1 Use virtual functions for OSD 2020-08-09 01:37:00 +02:00
Strawbs Bot
184e9a5c93 Update translations 2020-08-09 01:01:43 +02:00
Jonas Kvinge
c4f5363cde Properly enable/disable queue buttons depending on selection 2020-08-08 20:36:03 +02:00
Jonas Kvinge
002882cebf Build Qt 6 without linguist, qtbase code keeps updating breaking qt-tools 2020-08-08 19:20:27 +02:00
Jonas Kvinge
1cb3ec0c7b Only add autocomplete tags to playlist menu when we have chromaprint and gstreamer 2020-08-08 19:05:14 +02:00
Jonas Kvinge
6bf325c6f6 Fix QSslSocket::ignoreSslErrors compile error with Qt 6 2020-08-08 19:04:44 +02:00
Strawbs Bot
09c7ff9e8b Update translations 2020-08-08 01:01:46 +02:00
Jonas Kvinge
c2a94b61bf Fixes to playlist context menu
- Add all playlist actions to initialization list
- Make rescan songs work for non-collection songs by using playlist item reload
- Only show add to another playlist and remove from playlist when songs are selected
- Add some missing icons

Fixes #503
2020-08-07 22:13:02 +02:00
Jonas Kvinge
1db16232de Only show rescan songs for collection songs
Fixes #503
2020-08-07 21:18:48 +02:00
Jonas Kvinge
3da681a6b1 Add fatal error for missing protobuf compiler 2020-08-07 19:43:03 +02:00
Strawbs Bot
a79b3e7852 Update translations 2020-08-07 01:03:20 +02:00
Jonas Kvinge
b1099e6974 Handle metadata with tilde in title 2020-08-07 00:52:09 +02:00
Jonas Kvinge
4f6e06131c Change allow album cover search check 2020-08-07 00:28:46 +02:00
Jonas Kvinge
19f69e9e6c Allow cover search only using either artist, album or title 2020-08-07 00:18:31 +02:00
Jonas Kvinge
01481da773 Use Qt::QueuedConnection for cover fetcher 2020-08-06 23:55:44 +02:00
Jonas Kvinge
3e8f7e1cf1 Register CoverSearchStatistics metatype 2020-08-06 23:54:54 +02:00
Jonas Kvinge
5da69646f2 Add authentication for Qobuz cover provider 2020-08-06 22:57:44 +02:00
Jonas Kvinge
3cac01583b Add username password dialog 2020-08-06 22:54:21 +02:00
Jonas Kvinge
d16a26605e Fix updating playlist songs when there are multiple files with the same URL
Fixes #501
2020-08-06 21:40:42 +02:00
Jonas Kvinge
a4f692c788 Only show playlist add file(s) to transcoder when songs are selected 2020-08-06 18:37:17 +02:00
Jonas Kvinge
9f01206c57 Only show open in file browser when songs are selected 2020-08-06 18:36:52 +02:00
Jonas Kvinge
d34fc551ed Add playlist right click option to copy URL 2020-08-06 18:29:35 +02:00
Jonas Kvinge
7aa5f0d258 Only show delete and save playlist button when item is selected
Fixes #500
2020-08-06 16:00:03 +02:00
Jonas Kvinge
276a34bb66 Fix parsing Tidal track duration with Qt 6 2020-08-06 15:58:53 +02:00
Jonas Kvinge
0d820eda12 Remove diacritics in FTS search 2020-08-05 23:31:52 +02:00
Strawbs Bot
1991c1b677 Update translations 2020-08-05 01:02:22 +02:00
Jonas Kvinge
459404e3f0 Rename organise to organize
Prefer US spelling
2020-08-04 21:18:14 +02:00
Jonas Kvinge
badc623a3c Update Changelog 2020-08-03 21:53:31 +02:00
Jonas Kvinge
8e39f92cb7 Make album optional when reading scrobbles from cache 2020-08-03 21:50:26 +02:00
Jonas Kvinge
3ff4885973 Fix reading ASF comment tag 2020-08-03 21:03:14 +02:00
Jonas Kvinge
f9d45f7657 Fix reading and saving MP4 lyrics tag 2020-08-03 20:44:25 +02:00
Jonas Kvinge
789ff9df5c Add SUPublicEDKey Info.plist 2020-08-03 19:31:28 +02:00
Jonas Kvinge
a2064ed16b Always bundle libraries provided by homebrew
Fixes #343
2020-08-03 00:14:17 +02:00
Jonas Kvinge
b3d06c0868 Make macdeploy properly handle loader_path and libicudata 2020-08-02 17:02:28 +02:00
Jonas Kvinge
db1a6b3e38 Manually copy libicudata for macOS deploy
Fixes #498
2020-08-02 15:47:39 +02:00
Jonas Kvinge
8390237cc4 Fix Sparkle integration for macOS 2020-08-02 06:32:01 +02:00
Jonas Kvinge
9967eae7bb Decrease album cover score if artist doesn't match and cover isn't requested using album title 2020-08-02 04:34:15 +02:00
Jonas Kvinge
b3be7d1c6f Add CI for Qt 6 2020-08-02 04:19:39 +02:00
Jonas Kvinge
33ccb5dbb2 Remove duplicate check for X11 2020-08-02 04:18:40 +02:00
Strawbs Bot
5b90c0d695 Update translations 2020-08-02 01:07:53 +02:00
Jonas Kvinge
472a660239 Update README.md 2020-08-01 23:46:59 +02:00
Jonas Kvinge
ab67536d9a Prevent compilation and live albums from being picked before studio albums for album cover searches based on artist + song title 2020-08-01 23:17:35 +02:00
Jonas Kvinge
ee85fb3aec Use QString() on non-translated text in collection filter widget 2020-08-01 22:50:02 +02:00
Jonas Kvinge
214b6f4358 Use correct qt sparkle include for Qt 6 2020-08-01 03:41:48 +02:00
Jonas Kvinge
af0d092054 Use sparkle to check for updates on macOS and Windows 2020-08-01 03:37:16 +02:00
Jonas Kvinge
b07903c3e9 Register QVector<int> 2020-08-01 03:32:25 +02:00
Jonas Kvinge
ffa4c6bf09 Add SUFeedURL to Info.plist 2020-08-01 03:31:29 +02:00
Jonas Kvinge
b4125fa56c Remove create-dmg.sh script and use create-dmg directly from CMake 2020-08-01 03:31:01 +02:00
Jonas Kvinge
2c72302087 Install sparkle for macOS builds 2020-08-01 03:28:43 +02:00
Jonas Kvinge
0fa52bc64f Fix macdeploy script 2020-08-01 03:28:01 +02:00
Jonas Kvinge
f55a80b15a Use Q_UNUSED 2020-08-01 03:23:50 +02:00
Strawbs Bot
0735483321 Update translations 2020-08-01 01:01:36 +02:00
Jonas Kvinge
53fc2c7c21 Add extra safety for overwriting files for filesystem storage 2020-07-31 21:45:01 +02:00
Jonas Kvinge
f22133c3c5 Fix translations for Qt 6 2020-07-30 20:49:24 +02:00
Jonas Kvinge
2d5a6d6583 Use album artist for album repeat mode 2020-07-30 20:46:30 +02:00
Strawbs Bot
dc4adf2836 Update translations 2020-07-30 01:01:45 +02:00
Jonas Kvinge
dd6e254e4f Use quotes in collection query to allow special characters
Fixes #492
2020-07-29 21:41:35 +02:00
Jonas Kvinge
4c028c1659 Use position().toPoint() with Qt 6 2020-07-29 21:40:03 +02:00
Jonas Kvinge
d332a6777a Use QSortFilterProxyModel::filterRegularExpression only with Qt 6 2020-07-29 21:39:02 +02:00
Strawbs Bot
378251f229 Update translations 2020-07-29 01:01:35 +02:00
Strawbs Bot
b6b9b903ed Update translations 2020-07-28 01:01:37 +02:00
Strawbs Bot
143d68cfd5 Update translations 2020-07-27 01:01:53 +02:00
Jonas Kvinge
a31eac1426 Base warning for show in file browser on unique directories
Fixes #484
2020-07-26 15:10:00 +02:00
Jonas Kvinge
797196f7fc Register column aligment as int too 2020-07-26 15:05:00 +02:00
Chongo Bong
c9d0bc81dd simple addition of playlist actions to icon (#490)
very simple addition of playlist/playback actions to strawberry's icon. this re-introduces some nice little polish that was present in clementine's .desktop file.
2020-07-24 20:38:17 +02:00
Strawbs Bot
b5448ff607 Update translations 2020-07-22 01:01:41 +02:00
Jonas Kvinge
5ebd363d5d Fixes to last.fm scrobbling
- Start array notation for parameters at 0
- Correctly send trackNumber
2020-07-21 03:14:02 +02:00
Strawbs Bot
1d439e673e Update translations 2020-07-20 01:05:14 +02:00
Jonas Kvinge
0b7b7656b2 Replace use of QRegExp 2020-07-20 00:57:42 +02:00
Jonas Kvinge
eb270df835 Use std::bind in QtConcurrent::run() to fix compile with Qt 6 2020-07-19 22:43:58 +02:00
Jonas Kvinge
ff73dd2183 Partial revert commit af67de8 2020-07-19 19:07:12 +02:00
Strawbs Bot
e043a03eb6 Update translations 2020-07-19 15:23:03 +02:00
Jonas Kvinge
3cb4e8e373 Fix OSD Pretty margin 2020-07-19 04:09:34 +02:00
Jonas Kvinge
7e6de528b4 Update Last.fm error codes 2020-07-19 03:47:21 +02:00
Jonas Kvinge
df901c30ef Use QString() for html codes in about dialog 2020-07-19 03:46:41 +02:00
Jonas Kvinge
13856b33ec Fix playlist filter with Qt 5 2020-07-18 22:37:49 +02:00
Jonas Kvinge
a3a1c6f4c8 Fix saving playlist column alignment 2020-07-18 18:18:34 +02:00
Jonas Kvinge
638998a861 Replace QTimeLine::CurveShape with QEasingCurve 2020-07-18 17:53:14 +02:00
Jonas Kvinge
6e2ec89a05 Use QMouseEvent::pos() 2020-07-18 17:35:03 +02:00
Jonas Kvinge
af67de8aa6 Use lambdas for QtConcurrent::run instead of NewClosure 2020-07-18 16:28:39 +02:00
Jonas Kvinge
425dac478e Replace QProcess::error() with QProcess::errorOccurred() 2020-07-18 16:23:39 +02:00
Jonas Kvinge
b15c4ecd10 Fix check for context tab in TabSwitched
Broken with Qt 6
2020-07-18 15:52:36 +02:00
Jonas Kvinge
d7f88cf3a4 Register QItemSelection 2020-07-18 06:02:54 +02:00
Jonas Kvinge
5b7fbcd9b8 Remove debian stretch 2020-07-18 05:28:26 +02:00
Jonas Kvinge
7af64b0782 Fix QRegularExpressionMatch in FMPSParser 2020-07-18 05:02:34 +02:00
Jonas Kvinge
b84c70e811 Link Qt6::Core5Compat in libstrawberry-tagreader 2020-07-18 05:01:54 +02:00
Jonas Kvinge
f5b245c72d Add option to compile with Qt 6 2020-07-18 04:47:54 +02:00
Jonas Kvinge
4307183817 Fix utilities test 2020-07-18 04:32:37 +02:00
Jonas Kvinge
aeb32783d6 Only use QTextStream::setCodec() with Qt < 6 2020-07-18 04:27:21 +02:00
Jonas Kvinge
3927b3bf27 Remove QPainter::HighQualityAntialiasing 2020-07-18 04:26:19 +02:00
Jonas Kvinge
da9d2f9417 Replace QPalette::Background with QPalette::Window 2020-07-18 04:25:29 +02:00
Jonas Kvinge
1cec48e8f8 Use static_cast 2020-07-18 04:25:15 +02:00
Jonas Kvinge
f1105393da Replace QDateTime::toTime_t() with QDateTime::toSecsSinceEpoch() 2020-07-18 04:24:16 +02:00
Jonas Kvinge
dc7047e3c2 Use QLocale::LongFormat 2020-07-18 04:22:59 +02:00
Jonas Kvinge
08b2945623 Remove Qt 5.6 backward compatibility 2020-07-18 04:21:45 +02:00
Jonas Kvinge
cbcc223150 Replace QRegExp with QRegularExpression 2020-07-18 04:21:19 +02:00
Jonas Kvinge
9830f21e4a Use setContentsMargins() on layout 2020-07-18 04:20:20 +02:00
Jonas Kvinge
5f49567bf7 Make GlobalShortcut::nativeEventFilter compatible with Qt 6 2020-07-18 04:18:46 +02:00
Jonas Kvinge
6154ae7342 Move QDialogButtonBox signal/slot connect from UI file to class 2020-07-18 04:17:27 +02:00
Jonas Kvinge
978f3a3682 Add QSslError include 2020-07-18 04:16:31 +02:00
Jonas Kvinge
1f0961d574 Make MainWindow::nativeEvent compatible with Qt 6 2020-07-18 04:15:42 +02:00
Jonas Kvinge
a101252701 Make OSDPretty compatible with Qt 6 2020-07-18 04:15:19 +02:00
Jonas Kvinge
c96c29b1e3 Replace QRegExp with QRegularExpression 2020-07-18 04:14:51 +02:00
Jonas Kvinge
3b0fc180ff Make QListWidget::mimeData compatible with Qt 6 2020-07-18 04:13:53 +02:00
Jonas Kvinge
9b8bfdf33c Replace QPalette::Background with QPalette::Window 2020-07-18 04:12:50 +02:00
Jonas Kvinge
4328831fcd Use globalPosition() 2020-07-18 04:09:36 +02:00
Jonas Kvinge
e5b3df41e9 Replace QRegExp with QRegularExpression 2020-07-18 04:05:07 +02:00
Jonas Kvinge
cf5259e218 Add QActionGroup include 2020-07-18 03:54:52 +02:00
Jonas Kvinge
f24b6a520c Replace QDateTime::toTime_t() with QDateTime::toSecsSinceEpoch() 2020-07-18 03:53:30 +02:00
Jonas Kvinge
4140163ab2 Mark unused parameters 2020-07-17 16:36:24 +02:00
Jonas Kvinge
7afde0e93f Fix compile warning in qsearchfield_mac.mm 2020-07-17 16:35:57 +02:00
Jonas Kvinge
d27a571882 Ignore compile warning in SBSystemPreferences.h 2020-07-17 16:35:11 +02:00
Jonas Kvinge
1c70e3be25 Ignore format nonliteral in tutils.h 2020-07-17 16:34:37 +02:00
Jonas Kvinge
1819f64467 Disable deprecation warning for QMacCocoaViewContainer 2020-07-17 16:33:10 +02:00
Jonas Kvinge
9852e588c1 Change compile options 2020-07-17 16:32:37 +02:00
Jonas Kvinge
71a1ea481b Replace some uses of static_cast with qobject_cast 2020-07-17 01:32:07 +02:00
Jonas Kvinge
9e32f0d778 Silence some compile warnings with reinterpret cast 2020-07-16 22:46:31 +02:00
Jonas Kvinge
4478174dc2 Fix incorrectly mapped keys
Fixes #483
2020-07-16 22:28:35 +02:00
Jonas Kvinge
07553476d4 Remove xine 2020-07-16 00:59:46 +02:00
Jonas Kvinge
1773283456 Remove xine metronom.pts_per_smpls check 2020-07-16 00:44:41 +02:00
Jonas Kvinge
221ab51d90 Simply startup behaviour 2020-07-16 00:06:51 +02:00
Jonas Kvinge
43e0dd922b Hide settings that are unavailable on macOS in the settings 2020-07-16 00:05:49 +02:00
Jonas Kvinge
b3262652c3 Turn back git revision 2020-07-14 18:40:48 +02:00
Strawbs Bot
0cfa2b8c20 Update translations 2020-07-14 01:01:50 +02:00
264 changed files with 7812 additions and 9465 deletions

View File

@@ -80,7 +80,6 @@ commands:
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
libxine-devel
vlc-devel
libQt5Core-devel
libQt5Gui-devel
@@ -96,9 +95,7 @@ commands:
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libplist-devel
libmtp-devel
libusbmuxd-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
@@ -150,8 +147,6 @@ commands:
taglib-devel
libcdio-devel
libgpod-devel
libplist-devel
libusbmuxd-devel
libmtp-devel
libchromaprint-devel
fftw-devel
@@ -214,8 +209,6 @@ commands:
libchromaprint-devel
libcdio-devel
libgpod-devel
libplist-devel
libusbmuxd-devel
libmtp-devel
libjpeg-devel
cairo-devel
@@ -238,14 +231,14 @@ commands:
steps:
- run:
name: Update packages
command: urpmi.update -a
command: urpmi.update --auto -a
- run:
name: Configure auto update
command: urpmi --auto-update
command: urpmi --auto --auto-update
- run:
name: Install dependencies
command: >
urpmi --force
urpmi --auto --force
urpmi-debuginfo-install
git
tar
@@ -281,8 +274,6 @@ commands:
lib64gstreamer-plugins-base1.0-devel
lib64cdio-devel
lib64gpod-devel
lib64plist-devel
lib64usbmuxd-devel
lib64mtp-devel
lib64raw1394-devel
lib64chromaprint-devel
@@ -336,9 +327,6 @@ commands:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
install_ubuntu_dependencies:
@@ -390,9 +378,6 @@ commands:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
jobs:
@@ -501,15 +486,6 @@ jobs:
- build_rpm
build_debian_stretch:
docker:
- image: debian:stretch
steps:
- install_debian_dependencies
- checkout
- cmake
- build_deb
build_debian_buster:
docker:
- image: debian:buster
@@ -603,10 +579,6 @@ workflows:
only: /.*/
- build_debian_stretch:
filters:
tags:
only: /.*/
- build_debian_buster:
filters:
tags:

View File

@@ -38,7 +38,6 @@ jobs:
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
libxine-devel
vlc-devel
libQt5Core-devel
libQt5Gui-devel
@@ -54,9 +53,7 @@ jobs:
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libplist-devel
libmtp-devel
libusbmuxd-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
@@ -109,7 +106,6 @@ jobs:
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
libxine-devel
vlc-devel
libQt5Core-devel
libQt5Gui-devel
@@ -125,9 +121,7 @@ jobs:
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libplist-devel
libmtp-devel
libusbmuxd-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
@@ -188,7 +182,6 @@ jobs:
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
libxine-devel
vlc-devel
libQt5Core-devel
libQt5Gui-devel
@@ -204,9 +197,7 @@ jobs:
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libplist-devel
libmtp-devel
libusbmuxd-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
@@ -269,7 +260,6 @@ jobs:
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
libxine-devel
vlc-devel
libQt5Core-devel
libQt5Gui-devel
@@ -285,9 +275,7 @@ jobs:
libqt5-linguist-devel
libcdio-devel
libgpod-devel
libplist-devel
libmtp-devel
libusbmuxd-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
@@ -313,6 +301,78 @@ jobs:
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_qt6:
name: Build openSUSE Qt 6
runs-on: ubuntu-latest
container:
image: opensuse/tumbleweed
steps:
- uses: actions/checkout@v1.2.0
- name: Add Qt 6 repository
run: zypper -n ar -c -f -n 'repo-qt6' https://download.opensuse.org/repositories/home:/jonaski:/qt6/openSUSE_Tumbleweed/ repo-qt6
- name: Update packages
run: zypper --non-interactive --gpg-auto-import-keys ref
- name: Upgrade packages
run: zypper --non-interactive --gpg-auto-import-keys dup
- name: Install openSUSE dependencies
run: >
zypper --non-interactive --gpg-auto-import-keys install
lsb-release
rpm-build
git
tar
make
cmake
gcc
gcc-c++
gettext-tools
glibc-devel
libboost_headers-devel
boost-devel
glib2-devel
glib2-tools
dbus-1-devel
alsa-devel
libnotify-devel
libgnutls-devel
protobuf-devel
sqlite3-devel
libpulse-devel
gstreamer-devel
gstreamer-plugins-base-devel
vlc-devel
qt6-core-devel
qt6-gui-devel
qt6-widgets-devel
qt6-concurrent-devel
qt6-network-devel
qt6-sql-devel
qt6-dbus-devel
qt6-test-devel
qt6-x11extras-devel
qt6-base-common-devel
qt6-sql-sqlite
qt6-qt5compat-devel
libcdio-devel
libgpod-devel
libmtp-devel
libchromaprint-devel
desktop-file-utils
update-desktop-files
appstream-glib
hicolor-icon-theme
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DWITH_QT6=ON
- name: Build
working-directory: build
run: cmake --build . --config $BUILD_TYPE
build_fedora_32:
name: Build Fedora 32
runs-on: ubuntu-latest
@@ -364,8 +424,6 @@ jobs:
taglib-devel
libcdio-devel
libgpod-devel
libplist-devel
libusbmuxd-devel
libmtp-devel
libchromaprint-devel
fftw-devel
@@ -448,8 +506,6 @@ jobs:
libchromaprint-devel
libcdio-devel
libgpod-devel
libplist-devel
libusbmuxd-devel
libmtp-devel
libjpeg-devel
cairo-devel
@@ -494,14 +550,14 @@ jobs:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: urpmi.update -a
run: urpmi.update --auto -a
- name: Configure auto update
run: urpmi --auto-update
run: urpmi --auto --auto-update
- name: Install Mageia dependencies
run: >
urpmi --force
urpmi --auto --force
urpmi-debuginfo-install
git
tar
@@ -537,8 +593,6 @@ jobs:
lib64gstreamer-plugins-base1.0-devel
lib64cdio-devel
lib64gpod-devel
lib64plist-devel
lib64usbmuxd-devel
lib64mtp-devel
lib64raw1394-devel
lib64chromaprint-devel
@@ -570,65 +624,6 @@ jobs:
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_debian_stretch:
name: Build Debian Stretch
runs-on: ubuntu-latest
container:
image: debian:stretch
steps:
- uses: actions/checkout@v1.2.0
- name: Install Debian dependencies
run: >
apt-get update && apt-get install -y
build-essential
ssh
git
make
cmake
gcc
pkg-config
fakeroot
gettext
lsb-release
libglib2.0-dev
dpkg-dev
libdbus-1-dev
libboost-dev
libprotobuf-dev
protobuf-compiler
libsqlite3-dev
libgnutls28-dev
libasound2-dev
libpulse-dev
qtbase5-dev
qtbase5-dev-tools
qtbase5-private-dev
libqt5x11extras5-dev
qttools5-dev
libgstreamer1.0-dev
libgstreamer-plugins-base1.0-dev
gstreamer1.0-alsa
gstreamer1.0-pulseaudio
libchromaprint-dev
libfftw3-dev
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: make deb
shell: bash
run: dpkg-buildpackage -b -d -uc -us -nc -j2
build_debian_buster:
name: Build Debian Buster
runs-on: ubuntu-latest
@@ -673,9 +668,6 @@ jobs:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
@@ -732,9 +724,6 @@ jobs:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
@@ -796,9 +785,6 @@ jobs:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
@@ -860,9 +846,6 @@ jobs:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
@@ -924,9 +907,6 @@ jobs:
libcdio-dev
libmtp-dev
libgpod-dev
libimobiledevice-dev
libplist-dev
libusbmuxd-dev
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
@@ -969,8 +949,6 @@ jobs:
gst-libav
libcdio
libmtp
libimobiledevice
libplist
create-dmg
taglib
@@ -1034,9 +1012,7 @@ jobs:
-DENABLE_WIN32_CONSOLE=OFF
-DENABLE_DBUS=OFF
-DENABLE_LIBGPOD=OFF
-DENABLE_IMOBILEDEVICE=OFF
-DENABLE_LIBMTP=OFF
-DENABLE_XINE=OFF
-DProtobuf_PROTOC_EXECUTABLE=/usr/src/strawberry-mxe/usr/x86_64-pc-linux-gnu/bin/protoc
- name: Run Make
@@ -1049,7 +1025,7 @@ jobs:
- name: Create directories
working-directory: build
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins xine-plugins nsisplugins
run: mkdir -p gio-modules platforms sqldrivers imageformats styles gstreamer-plugins nsisplugins
- name: Copy GIO modules
working-directory: build

1
.gitignore vendored
View File

@@ -104,7 +104,6 @@ Thumbs.db
# Stuff in dist
maketarball.sh
create-dmg.sh
changelog
PKGBUILD

View File

@@ -23,8 +23,11 @@ before_install:
brew unlink python@2 || travis_terminate 1;
brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib;
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav;
brew install libcdio libmtp libimobiledevice libplist;
brew install libcdio libmtp;
brew install create-dmg;
brew cask install sparkle;
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework /Library/Frameworks/Sparkle.framework;
sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1)/Sparkle.framework.dSYM /Library/Frameworks/Sparkle.framework.dSYM;
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
export Qt5LinguistTools_DIR=/usr/local/opt/qt5/lib/cmake/Qt5LinguistTools;
export PATH="/usr/local/opt/gettext/bin:$PATH";

View File

@@ -10,38 +10,46 @@ endif()
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
if(WITH_QT6)
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
else()
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
endif()
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
target_include_directories(singleapplication SYSTEM PRIVATE
${Qt5Core_INCLUDE_DIRS}
${Qt5Widgets_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtWidgets_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(singleapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(singleapplication PRIVATE
${Qt5Core_LIBRARIES}
${Qt5Widgets_LIBRARIES}
${Qt5Network_LIBRARIES}
${QtCore_LIBRARIES}
${QtWidgets_LIBRARIES}
${QtNetwork_LIBRARIES}
)
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
QT5_WRAP_CPP(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
if(WITH_QT6)
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
else()
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
endif()
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
target_include_directories(singlecoreapplication SYSTEM PRIVATE
${Qt5Core_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(singlecoreapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(singlecoreapplication PRIVATE
${Qt5Core_LIBRARIES}
${Qt5Network_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
)
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")

View File

@@ -6,32 +6,32 @@ include(CheckCXXSourceCompiles)
# Check if the size of numeric types are suitable.
check_type_size("short" SIZEOF_SHORT)
if(NOT ${SIZEOF_SHORT} EQUAL 2)
if(NOT SIZEOF_SHORT EQUAL 2)
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
endif()
check_type_size("int" SIZEOF_INT)
if(NOT ${SIZEOF_INT} EQUAL 4)
if(NOT SIZEOF_INT EQUAL 4)
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
endif()
check_type_size("long long" SIZEOF_LONGLONG)
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
if(NOT SIZEOF_LONGLONG EQUAL 8)
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
endif()
check_type_size("wchar_t" SIZEOF_WCHAR_T)
if(${SIZEOF_WCHAR_T} LESS 2)
if(SIZEOF_WCHAR_T LESS 2)
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
check_type_size("float" SIZEOF_FLOAT)
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
if(NOT SIZEOF_FLOAT EQUAL 4)
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
endif()
check_type_size("double" SIZEOF_DOUBLE)
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
if(NOT SIZEOF_DOUBLE EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
@@ -212,5 +212,5 @@ endif()
# Detect WinRT mode
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(PLATFORM WINRT 1)
set(PLATFORM WINRT 1)
endif()

View File

@@ -160,6 +160,9 @@ inline String formatString(const char *format, ...) {
char buf[BufferSize];
int length;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
# if defined(HAVE_VSNPRINTF)
length = vsnprintf(buf, BufferSize, format, args);
@@ -180,6 +183,8 @@ inline String formatString(const char *format, ...) {
# endif
#pragma GCC diagnostic pop
va_end(args);
if (length > 0)

View File

@@ -50,16 +50,11 @@ list(APPEND COMPILE_OPTIONS
-Wunused-parameter
-Wformat=2
-Wdisabled-optimization
-Wno-sign-conversion
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
$<$<COMPILE_LANGUAGE:CXX>:-Wno-old-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
)
if(APPLE)
list(APPEND COMPILE_OPTIONS -Wno-unused-parameter)
endif()
option(BUILD_WERROR "Build with -Werror" OFF)
if(BUILD_WERROR)
list(APPEND COMPILE_OPTIONS -Werror)
@@ -77,6 +72,11 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(DEBUG ON)
endif()
if(APPLE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
endif()
find_program(CCACHE_EXECUTABLE NAMES ccache)
if (CCACHE_EXECUTABLE)
message(STATUS "ccache found: will be used for compilation and linkage")
@@ -93,7 +93,9 @@ if(Backtrace_FOUND)
endif()
find_package(GnuTLS REQUIRED)
find_package(Protobuf REQUIRED)
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
if (NOT Protobuf_PROTOC_EXECUTABLE)
message(FATAL_ERROR "Missing protobuf compiler.")
endif()
if(LINUX)
find_package(ALSA REQUIRED)
pkg_check_modules(DBUS REQUIRED dbus-1)
@@ -118,23 +120,17 @@ pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
pkg_check_modules(LIBXINE libxine)
pkg_check_modules(LIBVLC libvlc)
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.9)
pkg_check_modules(LIBPULSE libpulse)
pkg_check_modules(CHROMAPRINT libchromaprint)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
pkg_search_module(LIBUSBMUXD libusbmuxd-2.0 libusbmuxd)
pkg_search_module(LIBPLIST libplist-2.0 libplist)
find_package(Gettext)
find_package(FFTW3)
# QT
set(QT_MIN_VERSION 5.6)
option(WITH_QT6 "Use Qt 6" OFF)
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
if(X11_FOUND)
list(APPEND QT_COMPONENTS X11Extras)
endif()
@@ -148,32 +144,71 @@ if(WIN32)
list(APPEND QT_COMPONENTS WinExtras)
endif()
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
set(QT_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Sql_LIBRARIES})
set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
if(Qt5DBus_FOUND)
list(APPEND QT_LIBRARIES ${Qt5DBus_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5DBus_INCLUDE_DIRS})
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
endif()
if(Qt5X11Extras_FOUND)
list(APPEND QT_LIBRARIES ${Qt5X11Extras_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5X11Extras_INCLUDE_DIRS})
endif()
if(Qt5MacExtras_FOUND)
list(APPEND QT_LIBRARIES ${Qt5MacExtras_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5MacExtras_INCLUDE_DIRS})
endif()
if(Qt5WinExtras_FOUND)
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5WinExtras_INCLUDE_DIRS})
endif()
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
if (Qt5LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
if(WITH_QT6)
list(APPEND QT_COMPONENTS Core5Compat)
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
set(QtCore_LIBRARIES Qt6::Core)
set(QtConcurrent_LIBRARIES Qt6::Concurrent)
set(QtWidgets_LIBRARIES Qt6::Widgets)
set(QtNetwork_LIBRARIES Qt6::Network)
set(QtSql_LIBRARIES Qt6::Sql)
set(QT_LIBRARIES Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Network Qt6::Sql Qt6::Core5Compat)
if(Qt6DBus_FOUND)
set(QtDBus_LIBRARIES Qt6::DBus)
list(APPEND QT_LIBRARIES Qt6::DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt6::qdbusxml2cpp LOCATION)
endif()
if(Qt6X11Extras_FOUND)
set(QtX11Extras_LIBRARIES Qt6::X11Extras)
list(APPEND QT_LIBRARIES Qt6::X11Extras)
endif()
if(Qt6MacExtras_FOUND)
set(QtMacExtras_LIBRARIES Qt6::MacExtras)
list(APPEND QT_LIBRARIES Qt6::MacExtras)
endif()
if(Qt6WinExtras_FOUND)
set(QtWinExtras_LIBRARIES Qt6::WinExtras)
list(APPEND QT_LIBRARIES Qt6::WinExtras)
endif()
find_package(Qt6 QUIET COMPONENTS LinguistTools CONFIG)
if (Qt6LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt6::lconvert)
endif()
else()
set(QT_MIN_VERSION 5.8)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
set(QtCore_LIBRARIES ${Qt5Core_LIBRARIES})
set(QtConcurrent_LIBRARIES ${Qt5Concurrent_LIBRARIES})
set(QtWidgets_LIBRARIES ${Qt5Widgets_LIBRARIES})
set(QtNetwork_LIBRARIES ${Qt5Network_LIBRARIES})
set(QtSql_LIBRARIES ${Qt5Sql_LIBRARIES})
set(QT_LIBRARIES ${QtCore_LIBRARIES} ${QtConcurrent_LIBRARIES} ${QtWidgets_LIBRARIES} ${QtNetwork_LIBRARIES} ${QtSql_LIBRARIES})
set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
if(Qt5DBus_FOUND)
set(QtDBus_LIBRARIES ${Qt5DBus_LIBRARIES})
list(APPEND QT_LIBRARIES ${Qt5DBus_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5DBus_INCLUDE_DIRS})
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt5::qdbusxml2cpp LOCATION)
endif()
if(Qt5X11Extras_FOUND)
set(QtX11Extras_LIBRARIES ${Qt5X11Extras_LIBRARIES})
list(APPEND QT_LIBRARIES ${Qt5X11Extras_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5X11Extras_INCLUDE_DIRS})
endif()
if(Qt5MacExtras_FOUND)
set(QtMacExtras_LIBRARIES ${Qt5MacExtras_LIBRARIES})
list(APPEND QT_LIBRARIES ${Qt5MacExtras_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5MacExtras_INCLUDE_DIRS})
endif()
if(Qt5WinExtras_FOUND)
set(QtWinExtras_LIBRARIES ${Qt5WinExtras_LIBRARIES})
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
list(APPEND QT_INCLUDE_DIRS ${Qt5WinExtras_INCLUDE_DIRS})
endif()
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
if (Qt5LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
endif()
endif()
if(X11_FOUND)
@@ -221,9 +256,20 @@ set(SINGLEAPPLICATION_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/singleap
set(SINGLEAPPLICATION_LIBRARIES singleapplication)
set(SINGLECOREAPPLICATION_LIBRARIES singlecoreapplication)
if (APPLE)
if(APPLE)
find_library(SPARKLE Sparkle)
endif (APPLE)
endif(APPLE)
if(NOT SPARKLE AND (APPLE OR WIN32))
if(WITH_QT6)
pkg_check_modules(QTSPARKLE qtsparkle-qt6)
else()
pkg_check_modules(QTSPARKLE qtsparkle-qt5)
endif()
if(QTSPARKLE_FOUND)
set(HAVE_QTSPARKLE ON)
endif()
endif()
if (WIN32)
# RC compiler
@@ -258,10 +304,6 @@ optional_component(GSTREAMER ON "Engine: GStreamer backend"
DEPENDS "gstreamer-pbutils-1.0" GSTREAMER_PBUTILS_FOUND
)
optional_component(XINE ON "Engine: Xine backend"
DEPENDS "libxine" LIBXINE_FOUND
)
optional_component(VLC ON "Engine: VLC backend"
DEPENDS "libvlc" LIBVLC_FOUND
)
@@ -299,22 +341,24 @@ optional_component(LIBMTP ON "Devices: MTP support"
DEPENDS "libmtp" LIBMTP_FOUND
)
optional_component(IMOBILEDEVICE ON "Devices: iPhone, iPod Touch, iPad and Apple TV support"
DEPENDS "libimobiledevice" LIBIMOBILEDEVICE_FOUND
DEPENDS "libplist" LIBPLIST_FOUND
DEPENDS "libusbmuxd" LIBUSBMUXD_FOUND
DEPENDS "libgpod" HAVE_LIBGPOD
)
optional_component(SPARKLE ON "Sparkle integration"
DEPENDS "macOS" APPLE
DEPENDS "Sparkle" SPARKLE
)
optional_component(TRANSLATIONS ON "Translations"
DEPENDS "gettext" GETTEXT_FOUND
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
)
if(WITH_QT6)
optional_component(TRANSLATIONS ON "Translations"
DEPENDS "gettext" GETTEXT_FOUND
DEPENDS "Qt6LinguistTools" Qt6LinguistTools_FOUND
)
else()
optional_component(TRANSLATIONS ON "Translations"
DEPENDS "gettext" GETTEXT_FOUND
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
)
endif()
option(INSTALL_TRANSLATIONS "Install translations" OFF)
optional_component(SUBSONIC ON "Subsonic support")
optional_component(TIDAL ON "Tidal support")
@@ -343,8 +387,8 @@ endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
if(NOT CMAKE_CROSSCOMPILING)
set(CMAKE_REQUIRED_FLAGS "--std=c++11")
set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Sql_LIBRARIES})
set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS})
set(CMAKE_REQUIRED_LIBRARIES ${QtCore_LIBRARIES} ${QtSql_LIBRARIES})
set(CMAKE_REQUIRED_INCLUDES ${QtCore_INCLUDE_DIRS} ${QtSql_INCLUDE_DIRS})
check_cxx_source_runs("
#include <QSqlDatabase>
#include <QSqlQuery>
@@ -361,20 +405,6 @@ if(NOT CMAKE_CROSSCOMPILING)
)
endif()
if(HAVE_XINE)
check_cxx_source_compiles("
#define METRONOM_INTERNAL
#include <iostream>
#include <xine/metronom.h>
int main() {
metronom_t metronom;
std::cout << metronom.pts_per_smpls;
return 0;
}
"
XINE_ANALYZER)
endif()
# Set up definitions
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
@@ -415,8 +445,8 @@ add_custom_target(uninstall
# Show a summary of what we have enabled
summary_show()
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC)
message(FATAL_ERROR "You need to have either GStreamer, Xine or VLC to compile!")
if(NOT HAVE_GSTREAMER AND NOT HAVE_VLC)
message(FATAL_ERROR "You need to have either GStreamer or VLC to compile!")
elseif(NOT HAVE_GSTREAMER)
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
endif()

View File

@@ -2,6 +2,52 @@ Strawberry Music Player
=======================
ChangeLog
0.7.2:
BugFixes:
* Fixed installation directory for translations.
* Fixed collection sorting for non-ASCII characters.
* Fixed closing connected devices on exit.
0.7.1:
Bugfixes:
* Fixed incorrectly mapped global shortcuts keys "2" and "3".
* Fixed Last.fm scrobbling to correctly start array notation for parameters at 0 and not 1.
* Fixed sending trackNumber correctly for Last.fm and Libre.fm scrobbling.
* Fixed collection search when using special characters in the search query.
* Fixed reading and saving MP4 lyrics tag.
* Fixed reading ASF comment tag.
* Fixed adding playlist songs outside the collection when there are multiple files with the same URL.
* Fixed the rescan songs option to work with local songs outside of the collection.
* Fixed problems with editing song metadata in the playlists.
* Fixed saving and restoring playlist scrollbar position when switching between playlists.
* Fixed minor issue in cue parser with date and genre.
* (macOS) Fixed gst-libav plugin issue resulting in MP3 not working.
Enhancements:
* Simplified and improved startup behaviour code.
* Adapted all source code to be compatible with Qt 6, and increased required Qt version to 5.8.
* Added option to compile with Qt 6 (-DWITH_QT6=ON).
* Base warning for show in file browser on unique directories to avoid unneeded warning about opening many files.
* Use album artist instead of artist for album repeat mode when available.
* Added extra safety for overwriting files for filesystem storages when organizing files.
* Remove diacritics in FTS search.
* Improved playlist context menu.
* Added fatal CMake error for missing protobuf compiler.
* Added support for parsing radio streams metadata with tilde in title.
* Added CMake option to install translation files.
* Increased maximum time step for seeking to 60.
* (Unix) Added playback actions to desktop file.
* (macOS) Hide behaviour settings that are unavailable on macOS.
* (macOS) Fixed compile warnings.
* (macOS) Added Sparkle integration to notify on new versions.
* (Windows) Added QtSparkle support to notify on new versions.
Removed features:
* Removed Xine engine support.
* Removed broken imobiledevice (iPhone) support.
0.6.13:
Bugfixes:

View File

@@ -2,7 +2,7 @@
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
=======================
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt 5 framework.
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt 5 or 6 toolkit.
![Browse](https://www.strawberrymusicplayer.org/pictures/screenshot-002-large.png)
@@ -44,7 +44,7 @@ You can also make a one-time payment through [paypal.me/jonaskvinge](https://pay
* Support for multiple backends
* Audio analyzer
* Audio equalizer
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
* Transfer music to iPod, MTP or mass-storage USB player
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
* Subsonic and Tidal streaming support
@@ -63,14 +63,14 @@ To build Strawberry from source you need the following installed on your system
* [POSIX thread (pthread)](http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html)
* [GLib](https://developer.gnome.org/glib/)
* [Protobuf library and compiler](https://developers.google.com/protocol-buffers/)
* [Qt 5.6 or higher with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
* [Qt 5 components X11Extras and DBus for Linux/BSD, MacExtras for macOS and WinExtras for Windows](https://www.qt.io/)
* [Qt 5.8 or higher (or Qt 6) with components Core, Gui, Widgets, Concurrent, Network and Sql](https://www.qt.io/)
* [Qt components X11Extras and DBus for Linux/BSD, MacExtras for macOS and WinExtras for Windows](https://www.qt.io/)
* [SQLite 3.9 or newer with FTS5](https://www.sqlite.org)
* [Chromaprint library](https://acoustid.org/chromaprint)
* [ALSA library (linux)](https://www.alsa-project.org/)
* [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org) or [VLC](https://www.videolan.org)
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
* [GnuTLS](https://www.gnutls.org/)
Optional dependencies:
@@ -78,12 +78,13 @@ Optional dependencies:
* Audio CD: [libcdio](https://www.gnu.org/software/libcdio/)
* MTP devices: [libmtp](http://libmtp.sourceforge.net/)
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
* iPhone, iPod Touch, iPad and Apple TV devices: [libimobiledevice, libplist and libusbmuxd](https://www.libimobiledevice.org/)
* Moodbar: [fftw3](http://www.fftw.org/)
Either GStreamer, Xine or VLC engine is required, but only GStreamer is fully implemented so far.
Either GStreamer or VLC engine is required, but only GStreamer is fully implemented so far.
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
With Qt 6 we also depend on the Core5Compat module for QTextCodec.
### :wrench: Compiling from source
### Get the code:
@@ -97,6 +98,8 @@ You should also install the gstreamer plugins base and good, and optionally bad
cmake ..
make -j4
sudo make install
To compile with Qt 6 use: cmake .. -DWITH_QT6=ON
### :penguin: Packaging status

View File

@@ -1,6 +1,6 @@
add_custom_target(dmg
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
COMMAND create-dmg --volname strawberry --background "${CMAKE_SOURCE_DIR}/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon strawberry.app 150 218 --window-size 600 450 strawberry-${STRAWBERRY_VERSION_PACKAGE}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

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

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.11)
cmake_minimum_required(VERSION 3.0)
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
@@ -19,6 +19,8 @@ set (XGETTEXT_OPTIONS
--from-code=utf-8
)
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations)
macro(add_pot outfiles header pot)
# Make relative filenames for all source files
set(add_pot_sources)
@@ -64,14 +66,21 @@ macro(add_po outfiles po_prefix)
)
list(APPEND ${outfiles} ${_qm_filepath})
list(APPEND INSTALL_TRANSLATIONS_FILES ${_qm_filepath})
endforeach (_lang)
# Generate a qrc file for the translations
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
foreach(_lang ${ADD_PO_LANGUAGES})
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
endforeach(_lang)
file(APPEND ${_qrc} "</qresource></RCC>")
qt5_add_resources(${outfiles} ${_qrc})
if(NOT INSTALL_TRANSLATIONS)
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
foreach(_lang ${ADD_PO_LANGUAGES})
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
endforeach(_lang)
file(APPEND ${_qrc} "</qresource></RCC>")
if(WITH_QT6)
qt6_add_resources(${outfiles} ${_qrc})
else()
qt5_add_resources(${outfiles} ${_qrc})
endif()
endif()
endmacro(add_po)

View File

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

View File

@@ -81,7 +81,6 @@
<file>icons/128x128/view-refresh.png</file>
<file>icons/128x128/library-music.png</file>
<file>icons/128x128/vlc.png</file>
<file>icons/128x128/xine.png</file>
<file>icons/128x128/zoom-in.png</file>
<file>icons/128x128/zoom-out.png</file>
<file>icons/128x128/scrobble.png</file>
@@ -172,7 +171,6 @@
<file>icons/64x64/view-refresh.png</file>
<file>icons/64x64/library-music.png</file>
<file>icons/64x64/vlc.png</file>
<file>icons/64x64/xine.png</file>
<file>icons/64x64/zoom-in.png</file>
<file>icons/64x64/zoom-out.png</file>
<file>icons/64x64/scrobble.png</file>
@@ -267,7 +265,6 @@
<file>icons/48x48/view-refresh.png</file>
<file>icons/48x48/library-music.png</file>
<file>icons/48x48/vlc.png</file>
<file>icons/48x48/xine.png</file>
<file>icons/48x48/zoom-in.png</file>
<file>icons/48x48/zoom-out.png</file>
<file>icons/48x48/scrobble.png</file>
@@ -362,7 +359,6 @@
<file>icons/32x32/view-refresh.png</file>
<file>icons/32x32/library-music.png</file>
<file>icons/32x32/vlc.png</file>
<file>icons/32x32/xine.png</file>
<file>icons/32x32/zoom-in.png</file>
<file>icons/32x32/zoom-out.png</file>
<file>icons/32x32/scrobble.png</file>
@@ -457,7 +453,6 @@
<file>icons/22x22/view-refresh.png</file>
<file>icons/22x22/library-music.png</file>
<file>icons/22x22/vlc.png</file>
<file>icons/22x22/xine.png</file>
<file>icons/22x22/zoom-in.png</file>
<file>icons/22x22/zoom-out.png</file>
<file>icons/22x22/scrobble.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -72,7 +72,7 @@ CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (c
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
UPDATE devices SET schema_version=1 WHERE ROWID=%deviceid;

View File

@@ -180,7 +180,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -195,7 +195,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -210,7 +210,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);

View File

@@ -13,7 +13,7 @@ CREATE VIRTUAL TABLE %allsongstables_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -28,7 +28,7 @@ CREATE VIRTUAL TABLE playlist_items_fts_ USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);

View File

@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS songs (
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
@@ -83,7 +83,7 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs (
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
@@ -140,7 +140,7 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs (
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
@@ -197,7 +197,7 @@ CREATE TABLE IF NOT EXISTS tidal_songs (
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
@@ -254,7 +254,7 @@ CREATE TABLE IF NOT EXISTS subsonic_songs (
track INTEGER NOT NULL DEFAULT -1,
disc INTEGER NOT NULL DEFAULT -1,
year INTEGER NOT NULL DEFAULT -1,
originalyear INTEGER NOT NULL DEFAULT 0,
originalyear INTEGER NOT NULL DEFAULT -1,
genre TEXT,
compilation INTEGER NOT NULL DEFAULT 0,
composer TEXT,
@@ -410,7 +410,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -425,7 +425,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_artists_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -440,7 +440,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_albums_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -455,7 +455,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS tidal_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -470,7 +470,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS subsonic_songs_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -485,7 +485,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS playlist_items_fts_ USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);
@@ -500,6 +500,6 @@ CREATE VIRTUAL TABLE IF NOT EXISTS %allsongstables_fts USING fts5(
ftsgrouping,
ftsgenre,
ftscomment,
tokenize = "unicode61 remove_diacritics 0"
tokenize = "unicode61 remove_diacritics 1"
);

3
debian/control vendored
View File

@@ -24,10 +24,7 @@ Build-Depends: debhelper (>= 11),
libgstreamer-plugins-base1.0-dev,
libcdio-dev,
libgpod-dev,
libimobiledevice-dev,
libmtp-dev,
libplist-dev,
libusbmuxd-dev,
libchromaprint-dev,
libfftw3-dev
Standards-Version: 4.2.1

37
debian/copyright vendored
View File

@@ -122,8 +122,14 @@ Files: src/core/main.cpp
src/context/contextalbumsview.h
src/widgets/playingwidget.cpp
src/widgets/playingwidget.h
src/widgets/osdpretty.cpp
src/widgets/osdpretty.h
src/osd/osdbase.cpp
src/osd/osdbase.h
src/osd/osdpretty.cpp
src/osd/osdpretty.h
src/osd/osddbus.cpp
src/osd/osddbus.h
src/osd/osdmac.cpp
src/osd/osdmac.h
src/dialogs/about.cpp
src/dialogs/about.h
src/playlist/playlist.cpp
@@ -171,12 +177,12 @@ Files: src/core/main.cpp
src/settings/shortcutssettingspage.h
src/settings/appearancesettingspage.cpp
src/settings/appearancesettingspage.h
src/organise/organise.cpp
src/organise/organise.h
src/organise/organisedialog.cpp
src/organise/organisedialog.h
src/organise/organiseerrordialog.cpp
src/organise/organiseerrordialog.h
src/organize/organize.cpp
src/organize/organize.h
src/organize/organizedialog.cpp
src/organize/organizedialog.h
src/organize/organizeerrordialog.cpp
src/organize/organizeerrordialog.h
src/transcoder/transcoder.cpp
src/transcoder/transcoder.h
src/musicbrainz/musicbrainzclient.cpp
@@ -213,21 +219,6 @@ Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
2003-2005, Mark Kretschmann <markey@web.de>
License: GPL-2+
Files: src/engine/xineengine.cpp
src/engine/xineengine.h
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
2005, Ian Monroe <ian@monroe.nu>
2005, Christophe Thommeret <hftom@free.fr>
2005, 2006, Mark Kretschmann <markey@web.de>
2004, 2005, Max Howell <max.howell@methylblue.com>
2003, 2004, J. Kofler <kaffeine@gmx.net>
License: GPL-2+
Files: src/engine/xinescope.c
src/engine/xinescope.h
Copyright: 2004, Max Howell <max.howell@methylblue.com>
License: GPL-2+
Files: src/widgets/fancytabwidget.cpp
src/widgets/fancytabwidget.h
Copyright: 2018, Vikram Ambrose <ambroseworks@gmail.com>

1
debian/rules vendored
View File

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

1
dist/CMakeLists.txt vendored
View File

@@ -3,7 +3,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/unix/PKGBUILD.in ${CMAKE_CURRENT_SOUR
if (APPLE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/create-dmg.sh.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/create-dmg.sh)
endif (APPLE)
if (WIN32)

View File

@@ -34,6 +34,10 @@
<string>public.app-category.music</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.4</string>
<key>SUFeedURL</key>
<string>https://www.strawberrymusicplayer.org/sparkle-macos</string>
<key>SUPublicEDKey</key>
<string>3IRScV8YtNVnx7zoeJAXvg28Kh1gN/Pyl2iPM467pG8=</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>

View File

@@ -1,24 +0,0 @@
#!/bin/sh
version="@STRAWBERRY_VERSION_PACKAGE@"
if [ -z "$1" ]; then
echo "Usage: $0 <bundle.app> (append)"
exit 1
fi
name=$(basename "$1" | perl -pe 's/(.*).app/\1/')
bundle_dir="$1"
temp_dir="dmg/$name"
if [ -z "$2" ]; then
output_file="$name-$version.dmg"
else
output_file="$name-$2-$version.dmg"
fi
rm -rf "$temp_dir"
rm -f "$output_file"
mkdir -p "$temp_dir"
/usr/local/bin/create-dmg --volname "$name" --background "@CMAKE_SOURCE_DIR@/dist/macos/dmg_background.png" --app-drop-link 450 218 --icon $bundle_dir 150 218 --window-size 600 450 $output_file $bundle_dir

View File

@@ -27,11 +27,12 @@ import traceback
LOGGER = logging.getLogger('macdeploy')
LIBRARY_SEARCH_PATH = ['/usr/local/lib']
LIBRARY_SEARCH_PATH = ['/usr/local/lib', '/usr/local/opt/icu4c/lib']
FRAMEWORK_SEARCH_PATH = [
'/Library/Frameworks',
os.path.join(os.environ['HOME'], 'Library/Frameworks')
os.path.join(os.environ['HOME'], 'Library/Frameworks'),
'/Library/Frameworks/Sparkle.framework/Versions'
]
QT_PLUGINS = [
@@ -154,12 +155,6 @@ class InstallNameToolError(Error):
class CouldNotFindGstreamerPluginError(Error):
pass
class CouldNotFindXinePluginError(Error):
pass
class CouldNotFindVLCPluginError(Error):
pass
if len(sys.argv) < 2:
print 'Usage: %s <bundle.app>' % sys.argv[0]
@@ -196,11 +191,22 @@ def GetBrokenLibraries(binary):
elif re.match(r'^\s*/usr/lib/', line):
#print "unix style system lib"
continue # unix style system library
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@loader_path', line):
elif re.match(r'^\s*@executable_path', line) or re.match(r'^\s*@rpath', line) or re.match(r'^\s*@loader_path', line):
# Potentially already fixed library
relative_path = os.path.join(*line.split('/')[3:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['frameworks'].append(relative_path)
if line.count('/') == 1:
relative_path = os.path.join(*line.split('/')[1:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['libs'].append(relative_path)
elif line.count('/') == 2:
relative_path = os.path.join(*line.split('/')[2:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['libs'].append(relative_path)
elif line.count('/') >= 3:
relative_path = os.path.join(*line.split('/')[3:])
if not os.path.exists(os.path.join(frameworks_dir, relative_path)):
broken_libs['frameworks'].append(relative_path)
else:
print "GetBrokenLibraries Error: %s" % line
elif re.search(r'\w+\.framework', line):
broken_libs['frameworks'].append(line)
else:
@@ -261,14 +267,21 @@ def FixFramework(path):
def FixLibrary(path):
if path in fixed_libraries or FindSystemLibrary(os.path.basename(path)) is not None:
if path in fixed_libraries:
return
else:
fixed_libraries.add(path)
# Always bundle libraries provided by homebrew (/usr/local).
if not re.match(r'^\s*/usr/local', path) and FindSystemLibrary(os.path.basename(path)) is not None:
return
fixed_libraries.add(path)
abs_path = FindLibrary(path)
if abs_path == "":
print "Could not resolve %s, not fixing!" % path
return
broken_libs = GetBrokenLibraries(abs_path)
FixAllLibraries(broken_libs)
@@ -416,7 +429,7 @@ def FindSystemLibrary(library_name):
def FixLibraryInstallPath(library_path, library):
system_library = FindSystemLibrary(os.path.basename(library_path))
if system_library is None:
if system_library is None or re.match(r'^\s*/usr/local', library_path):
new_path = '@executable_path/../Frameworks/%s' % os.path.basename(library_path)
FixInstallPath(library_path, library, new_path)
else:
@@ -425,21 +438,14 @@ def FixLibraryInstallPath(library_path, library):
def FixFrameworkInstallPath(library_path, library):
parts = library_path.split(os.sep)
full_path = ""
for i, part in enumerate(parts):
if re.match(r'\w+\.framework', part):
full_path = os.path.join(*parts[i:])
break
new_path = '@executable_path/../Frameworks/%s' % full_path
FixInstallPath(library_path, library, new_path)
def FindXinePlugin(name):
for path in XINEPLUGIN_SEARCH_PATH:
if os.path.exists(path):
for dir, dirs, files in os.walk(path):
if name in files:
return os.path.join(dir, name)
raise CouldNotFindXinePluginError(name)
if full_path:
new_path = '@executable_path/../Frameworks/%s' % full_path
FixInstallPath(library_path, library, new_path)
def FindQtPlugin(name):

View File

@@ -38,7 +38,6 @@ tar -cJf $name-$version.tar.xz \
--exclude="$root/debian/changelog" \
--exclude="$root/dist/scripts/maketarball.sh" \
--exclude="$root/dist/unix/PKGBUILD" \
--exclude="$root/dist/macos/create-dmg.sh" \
--exclude="$root/dist/macos/Info.plist" \
--exclude="$root/dist/windows/windres.rc" \
--exclude="$root/src/translations/translations.pot" \

View File

@@ -23,15 +23,11 @@ depends=(
gstreamer
gst-plugins-base
gst-plugins-good
xine-lib
vlc
chromaprint
libgpod
libcdio
libmtp
libusbmuxd
libplist
libimobiledevice
fftw
)
optdepends=(

View File

@@ -12,3 +12,28 @@ Categories=AudioVideo;Player;Qt;Audio;
StartupNotify=false
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
StartupWMClass=strawberry
Actions=Play;Pause;Stop;StopAfterCurrent;Previous;Next;
[Desktop Action Play]
Name=Play
Exec=strawberry --play
[Desktop Action Pause]
Name=Pause
Exec=strawberry --pause
[Desktop Action Stop]
Name=Stop
Exec=strawberry --stop
[Desktop Action StopAfterCurrent]
Name=Stop after this track
Exec=strawberry --stop-after-current
[Desktop Action Previous]
Name=Previous
Exec=strawberry --previous
[Desktop Action Next]
Name=Next
Exec=strawberry --next

View File

@@ -1,7 +1,7 @@
Name: strawberry
Version: @STRAWBERRY_VERSION_RPM_V@
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
Summary: A music player and music collection organiser
Summary: A music player and music collection organizer
Group: Applications/Multimedia
License: GPL-3.0+
URL: https://www.strawberrymusicplayer.org/
@@ -73,7 +73,6 @@ BuildRequires: pkgconfig(libmtp)
BuildRequires: pkgconfig(libnotify)
BuildRequires: pkgconfig(libudf)
%if 0%{?suse_version} || 0%{?fedora_version}
BuildRequires: pkgconfig(libxine)
BuildRequires: pkgconfig(libvlc)
%endif
@@ -107,7 +106,7 @@ Features:
- Support for multiple backends
- Audio analyzer
- Audio equalizer
- Transfer music to iPod, iPhone, MTP or mass-storage USB player
- Transfer music to iPod, MTP or mass-storage USB player
- Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
- Streaming support for Subsonic

View File

@@ -18,6 +18,10 @@
!define debug
!endif
!if "@WITH_QT6@" == "ON"
!define with_qt6
!endif
!ifdef debug
!define PRODUCT_NAME "Strawberry Music Player Debug"
!define PRODUCT_NAME_SHORT "Strawberry"
@@ -91,17 +95,33 @@ SetCompressor /SOLID lzma
Name "${PRODUCT_NAME}"
!ifdef arch_x86
!ifdef debug
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
!ifdef with_qt6
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x86.exe"
!else
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x86.exe"
!endif
!else
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
!ifdef with_qt6
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x86.exe"
!else
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x86.exe"
!endif
!endif
!endif
!ifdef arch_x64
!ifdef debug
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
!ifdef with_qt6
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-Debug-x64.exe"
!else
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Debug-x64.exe"
!endif
!else
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
!ifdef with_qt6
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt6-x64.exe"
!else
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-x64.exe"
!endif
!endif
!endif
@@ -209,6 +229,16 @@ Section "Strawberry" Strawberry
File "libwavpack-1.dll"
File "libwinpthread-1.dll"
File "libxml2-2.dll"
!ifdef with_qt6
File "Qt6Concurrent.dll"
File "Qt6Core.dll"
File "Qt6Gui.dll"
File "Qt6Network.dll"
File "Qt6Sql.dll"
File "Qt6Widgets.dll"
File "Qt6WinExtras.dll"
File "libqtsparkle-qt6.dll"
!else
File "Qt5Concurrent.dll"
File "Qt5Core.dll"
File "Qt5Gui.dll"
@@ -216,6 +246,8 @@ Section "Strawberry" Strawberry
File "Qt5Sql.dll"
File "Qt5Widgets.dll"
File "Qt5WinExtras.dll"
File "libqtsparkle-qt5.dll"
!endif
File "zlib1.dll"
File "libzstd.dll"
File "libtasn1-6.dll"
@@ -445,6 +477,15 @@ Section "Uninstall"
Delete "$INSTDIR\Qt5Sql.dll"
Delete "$INSTDIR\Qt5Widgets.dll"
Delete "$INSTDIR\Qt5WinExtras.dll"
Delete "$INSTDIR\libqtsparkle-qt5.dll"
Delete "$INSTDIR\Qt6Concurrent.dll"
Delete "$INSTDIR\Qt6Core.dll"
Delete "$INSTDIR\Qt6Gui.dll"
Delete "$INSTDIR\Qt6Network.dll"
Delete "$INSTDIR\Qt6Sql.dll"
Delete "$INSTDIR\Qt6Widgets.dll"
Delete "$INSTDIR\Qt6WinExtras.dll"
Delete "$INSTDIR\libqtsparkle-qt6.dll"
Delete "$INSTDIR\zlib1.dll"
Delete "$INSTDIR\libzstd.dll"
Delete "$INSTDIR\libtasn1-6.dll"

View File

@@ -9,7 +9,7 @@ link_directories(
${GSTREAMER_BASE_LIBRARY_DIRS}
${GSTREAMER_AUDIO_LIBRARY_DIRS}
${FFTW3_LIBRARY_DIRS}
${Qt5Core_LIBRARY_DIRS}
${QtCore_LIBRARY_DIRS}
)
add_library(gstmoodbar STATIC ${SOURCES})
@@ -21,7 +21,7 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE
${GSTREAMER_BASE_INCLUDE_DIRS}
${GSTREAMER_AUDIO_INCLUDE_DIRS}
${FFTW3_INCLUDE_DIR}
${Qt5Core_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
)
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
@@ -33,5 +33,5 @@ target_link_libraries(gstmoodbar PRIVATE
${GSTREAMER_BASE_LIBRARIES}
${GSTREAMER_AUDIO_LIBRARIES}
${FFTW3_FFTW_LIBRARY}
${Qt5Core_LIBRARIES}
${QtCore_LIBRARIES}
)

View File

@@ -179,7 +179,7 @@ static void gst_fastspectrum_reset_state (GstFastSpectrum * spectrum) {
static void gst_fastspectrum_finalize (GObject * object) {
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (object);
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(object);
gst_fastspectrum_reset_state (spectrum);
g_mutex_clear (&spectrum->lock);
@@ -190,7 +190,7 @@ static void gst_fastspectrum_finalize (GObject * object) {
static void gst_fastspectrum_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) {
GstFastSpectrum *filter = GST_FASTSPECTRUM (object);
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
switch (prop_id) {
case PROP_INTERVAL:{
@@ -222,7 +222,7 @@ static void gst_fastspectrum_set_property (GObject * object, guint prop_id, cons
static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) {
GstFastSpectrum *filter = GST_FASTSPECTRUM (object);
GstFastSpectrum *filter = reinterpret_cast<GstFastSpectrum*>(object);
switch (prop_id) {
case PROP_INTERVAL:
@@ -240,7 +240,7 @@ static void gst_fastspectrum_get_property (GObject * object, guint prop_id, GVal
static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
gst_fastspectrum_reset_state (spectrum);
@@ -250,7 +250,7 @@ static gboolean gst_fastspectrum_start (GstBaseTransform * trans) {
static gboolean gst_fastspectrum_stop (GstBaseTransform * trans) {
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
gst_fastspectrum_reset_state (spectrum);
@@ -334,7 +334,7 @@ static void input_data_mixed_int16_max (const guint8 * _in, double * out, guint
static gboolean gst_fastspectrum_setup (GstAudioFilter * base, const GstAudioInfo * info) {
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (base);
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(base);
GstFastSpectrumInputData input_data = nullptr;
g_mutex_lock (&spectrum->lock);
@@ -392,7 +392,7 @@ static void gst_fastspectrum_run_fft (GstFastSpectrum * spectrum, guint input_po
static GstFlowReturn gst_fastspectrum_transform_ip (GstBaseTransform *trans, GstBuffer *buffer) {
GstFastSpectrum *spectrum = GST_FASTSPECTRUM (trans);
GstFastSpectrum *spectrum = reinterpret_cast<GstFastSpectrum*>(trans);
guint rate = GST_AUDIO_FILTER_RATE (spectrum);
guint bps = GST_AUDIO_FILTER_BPS (spectrum);
guint bpf = GST_AUDIO_FILTER_BPF (spectrum);

View File

@@ -20,20 +20,24 @@ if(APPLE)
list(APPEND SOURCES core/scoped_nsautorelease_pool.mm)
endif(APPLE)
qt5_wrap_cpp(MOC ${HEADERS})
if(WITH_QT6)
qt6_wrap_cpp(MOC ${HEADERS})
else()
qt5_wrap_cpp(MOC ${HEADERS})
endif()
link_directories(
${GLIB_LIBRARY_DIRS}
${Qt5Core_LIBRARY_DIRS}
${Qt5Network_LIBRARY_DIRS}
${QtCore_LIBRARY_DIRS}
${QtNetwork_LIBRARY_DIRS}
)
add_library(libstrawberry-common STATIC ${SOURCES} ${MOC})
target_include_directories(libstrawberry-common SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${Qt5Core_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-common PRIVATE
@@ -46,8 +50,8 @@ target_include_directories(libstrawberry-common PRIVATE
target_link_libraries(libstrawberry-common PRIVATE
${CMAKE_THREAD_LIBS_INIT}
${GLIB_LIBRARIES}
${Qt5Core_LIBRARIES}
${Qt5Network_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
)
if(Backtrace_FOUND)

View File

@@ -35,7 +35,8 @@
#include <QMap>
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QDateTime>
#include <QIODevice>
#include <QBuffer>
@@ -306,11 +307,12 @@ QString LinuxDemangle(const QString &symbol);
QString LinuxDemangle(const QString &symbol) {
QRegExp regex("\\(([^+]+)");
if (!symbol.contains(regex)) {
QRegularExpression regex("\\(([^+]+)");
QRegularExpressionMatch match = regex.match(symbol);
if (!match.hasMatch()) {
return symbol;
}
QString mangled_function = regex.cap(1);
QString mangled_function = match.captured(1);
return CXXDemangle(mangled_function);
}

View File

@@ -102,10 +102,10 @@ void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
// Sorry.
if (flush_abstract_socket_) {
((static_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
((qobject_cast<QAbstractSocket*>(device_))->*(flush_abstract_socket_))();
}
else if (flush_local_socket_) {
((static_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
}
}

View File

@@ -172,7 +172,7 @@ template <typename HandlerType>
WorkerPool<HandlerType>::~WorkerPool() {
for (const Worker &worker : workers_) {
if (worker.local_socket_ && worker.process_) {
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
// The worker is connected. Close his socket and wait for him to exit.
qLog(Debug) << "Closing worker socket";
@@ -263,7 +263,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
worker->process_ = new QProcess(this);
connect(worker->local_server_, SIGNAL(newConnection()), SLOT(NewConnection()));
connect(worker->process_, SIGNAL(error(QProcess::ProcessError)), SLOT(ProcessError(QProcess::ProcessError)));
connect(worker->process_, SIGNAL(errorOccurred(QProcess::ProcessError)), SLOT(ProcessError(QProcess::ProcessError)));
// Create a server, find an unused name and start listening
forever {

View File

@@ -17,8 +17,8 @@ add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${Qt5Core_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-tagreader PRIVATE
@@ -34,6 +34,11 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
${PROTOBUF_LIBRARY}
${TAGLIB_LIBRARIES}
${Qt5Core_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
libstrawberry-common
)
if(WITH_QT6)
target_link_libraries(libstrawberry-tagreader PRIVATE Qt6::Core5Compat)
endif()

View File

@@ -23,6 +23,8 @@
#include <QVariant>
#include <QString>
#include <QChar>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include "fmpsparser.h"
@@ -89,20 +91,20 @@ bool FMPSParser::Parse(const QString &data) {
int FMPSParser::ParseValueRef(const QStringRef& data, QVariant* ret) const {
// Try to match a float
int pos = float_re_.indexIn(*data.string(), data.position());
if (pos == data.position()) {
*ret = float_re_.cap(1).toDouble();
return float_re_.matchedLength();
QRegularExpressionMatch re_match = float_re_.match(*data.string(), data.position());
if (re_match.capturedStart() == data.position()) {
*ret = re_match.captured(1).toDouble();
return re_match.capturedLength();
}
// Otherwise try to match a string
pos = string_re_.indexIn(*data.string(), data.position());
if (pos == data.position()) {
re_match = string_re_.match(*data.string(), data.position());
if (re_match.capturedStart() == data.position()) {
// Replace escape sequences with their actual characters
QString value = string_re_.cap(1);
QString value = re_match.captured(1);
value.replace(escape_re_, "\\1");
*ret = value;
return string_re_.matchedLength();
return re_match.capturedLength();
}
return -1;

View File

@@ -23,12 +23,12 @@
#include <QList>
#include <QMetaType>
#include <QString>
#include <QRegExp>
#include <QRegularExpression>
class QVariant;
class FMPSParser {
public:
public:
FMPSParser();
// A FMPS result is a list of lists of values (where a value is a string or
@@ -54,10 +54,10 @@ public:
int ParseListList(const QString &data, Result *ret) const;
int ParseListListRef(const QStringRef &data, Result *ret) const;
private:
QRegExp float_re_;
QRegExp string_re_;
QRegExp escape_re_;
private:
QRegularExpression float_re_;
QRegularExpression string_re_;
QRegularExpression escape_re_;
Result result_;
};

View File

@@ -177,11 +177,11 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
song->set_basefilename(DataCommaSizeFromQString(info.fileName()));
song->set_url(url.constData(), url.size());
song->set_filesize(info.size());
song->set_mtime(info.lastModified().toTime_t());
song->set_mtime(info.lastModified().toSecsSinceEpoch());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
song->set_ctime(info.birthTime().isValid() ? info.birthTime().toTime_t() : info.lastModified().toTime_t());
song->set_ctime(info.birthTime().isValid() ? info.birthTime().toSecsSinceEpoch() : info.lastModified().toSecsSinceEpoch());
#else
song->set_ctime(info.created().toTime_t());
song->set_ctime(info.created().toSecsSinceEpoch());
#endif
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
@@ -342,6 +342,9 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
if (mp4_tag->item("\251grp").isValid()) {
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), nullptr, song->mutable_grouping());
}
if (mp4_tag->item("\251lyr").isValid()) {
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), nullptr, song->mutable_lyrics());
}
if (mp4_tag->item(kMP4_OriginalYear_ID).isValid()) {
song->set_originalyear(TStringToQString(mp4_tag->item(kMP4_OriginalYear_ID).toStringList().toString('\n')).left(4).toInt());
@@ -355,6 +358,10 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
song->set_bitdepth(file_asf->audioProperties()->bitsPerSample());
if (file_asf->tag()) {
Decode(file_asf->tag()->comment(), nullptr, song->mutable_comment());
}
const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap();
if (attributes_map.contains(kASF_OriginalDate_ID)) {
@@ -617,6 +624,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
tag->setItem("disk", TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0));
tag->setItem("\251wrt", TagLib::StringList(song.composer().c_str()));
tag->setItem("\251grp", TagLib::StringList(song.grouping().c_str()));
tag->setItem("\251lyr", TagLib::StringList(song.lyrics().c_str()));
tag->setItem("aART", TagLib::StringList(song.albumartist().c_str()));
tag->setItem("cpil", TagLib::StringList(song.compilation() ? "1" : "0"));
}

View File

@@ -37,7 +37,6 @@
class QTextCodec;
#ifndef USE_SYSTEM_TAGLIB
using namespace Strawberry_TagLib;
#endif

View File

@@ -55,8 +55,8 @@ message SongMetadata {
optional string basefilename = 22;
optional FileType filetype = 23;
optional int32 filesize = 24;
optional int32 mtime = 25;
optional int32 ctime = 26;
optional int64 mtime = 25;
optional int64 ctime = 26;
optional int32 playcount = 27;
optional int32 skipcount = 28;

View File

@@ -4,14 +4,19 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(SOURCES main.cpp tagreaderworker.cpp)
qt5_wrap_cpp(MOC ${HEADERS})
qt5_add_resources(QRC data/data.qrc)
if(WITH_QT6)
qt6_wrap_cpp(MOC ${HEADERS})
qt6_add_resources(QRC data/data.qrc)
else()
qt5_wrap_cpp(MOC ${HEADERS})
qt5_add_resources(QRC data/data.qrc)
endif()
link_directories(
${GLIB_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
${Qt5Core_LIBRARY_DIRS}
${Qt5Network_LIBRARY_DIRS}
${QtCore_LIBRARY_DIRS}
${QtNetwork_LIBRARY_DIRS}
)
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
@@ -19,8 +24,8 @@ add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${Qt5Core_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(strawberry-tagreader PRIVATE
@@ -34,8 +39,8 @@ target_include_directories(strawberry-tagreader PRIVATE
target_link_libraries(strawberry-tagreader PRIVATE
${GLIB_LIBRARIES}
${TAGLIB_LIBRARIES}
${Qt5Core_LIBRARIES}
${Qt5Network_LIBRARIES}
${QtCore_LIBRARIES}
${QtNetwork_LIBRARIES}
libstrawberry-common
libstrawberry-tagreader
)

View File

@@ -1,5 +1,5 @@
name: strawberry
version: '0.6.13+git'
version: '0.7.2+git'
summary: music player and collection organizer
description: |
Strawberry is a music player and collection organizer.
@@ -99,14 +99,10 @@ parts:
- libqt5x11extras5-dev
- libgstreamer1.0-dev
- libgstreamer-plugins-base1.0-dev
- libxine2-dev
- libvlc-dev
- libcdio-dev
- libgpod-dev
- libimobiledevice-dev
- libmtp-dev
- libplist-dev
- libusbmuxd-dev
- libchromaprint-dev
- libfftw3-dev
@@ -132,10 +128,6 @@ parts:
- libcdio17
- libgpod4
- libmtp9
- libimobiledevice6
- libplist3
- libusbmuxd4
- libxine2
- libvlc5
- libvlccore9
- libtag1v5
@@ -144,7 +136,6 @@ parts:
- libx11-6
- libxcomposite1
- libxcursor1
- libxinerama1
- libxrandr2
- libdb5.3
- libglu1

View File

@@ -168,6 +168,7 @@ set(SOURCES
dialogs/edittagdialog.cpp
dialogs/trackselectiondialog.cpp
dialogs/addstreamdialog.cpp
dialogs/userpassdialog.cpp
widgets/autoexpandingtreeview.cpp
widgets/busyindicator.cpp
@@ -183,8 +184,6 @@ set(SOURCES
widgets/linetextedit.cpp
widgets/multiloadingindicator.cpp
widgets/playingwidget.cpp
widgets/osd.cpp
widgets/osdpretty.cpp
widgets/renametablineedit.cpp
widgets/volumeslider.cpp
widgets/stickyslider.cpp
@@ -194,6 +193,9 @@ set(SOURCES
widgets/tracksliderslider.cpp
widgets/loginstatewidget.cpp
osd/osdbase.cpp
osd/osdpretty.cpp
musicbrainz/acoustidclient.cpp
musicbrainz/musicbrainzclient.cpp
@@ -221,10 +223,10 @@ set(SOURCES
scrobbler/librefmscrobbler.cpp
scrobbler/listenbrainzscrobbler.cpp
organise/organise.cpp
organise/organiseformat.cpp
organise/organisedialog.cpp
organise/organiseerrordialog.cpp
organize/organize.cpp
organize/organizeformat.cpp
organize/organizedialog.cpp
organize/organizeerrordialog.cpp
)
@@ -364,6 +366,7 @@ set(HEADERS
dialogs/edittagdialog.h
dialogs/trackselectiondialog.h
dialogs/addstreamdialog.h
dialogs/userpassdialog.h
widgets/autoexpandingtreeview.h
widgets/busyindicator.h
@@ -378,8 +381,6 @@ set(HEADERS
widgets/linetextedit.h
widgets/multiloadingindicator.h
widgets/playingwidget.h
widgets/osd.h
widgets/osdpretty.h
widgets/renametablineedit.h
widgets/volumeslider.h
widgets/stickyslider.h
@@ -390,6 +391,9 @@ set(HEADERS
widgets/loginstatewidget.h
widgets/qsearchfield.h
osd/osdbase.h
osd/osdpretty.h
musicbrainz/acoustidclient.h
musicbrainz/musicbrainzclient.h
@@ -415,9 +419,9 @@ set(HEADERS
scrobbler/librefmscrobbler.h
scrobbler/listenbrainzscrobbler.h
organise/organise.h
organise/organisedialog.h
organise/organiseerrordialog.h
organize/organize.h
organize/organizedialog.h
organize/organizeerrordialog.h
)
@@ -465,18 +469,20 @@ set(UI
dialogs/edittagdialog.ui
dialogs/trackselectiondialog.ui
dialogs/addstreamdialog.ui
dialogs/userpassdialog.ui
widgets/trackslider.ui
widgets/osdpretty.ui
widgets/fileview.ui
widgets/loginstatewidget.ui
osd/osdpretty.ui
internet/internettabsview.ui
internet/internetcollectionviewcontainer.ui
internet/internetsearchview.ui
organise/organisedialog.ui
organise/organiseerrordialog.ui
organize/organizedialog.ui
organize/organizeerrordialog.ui
)
@@ -521,10 +527,12 @@ optional_source(HAVE_ALSA
engine/alsadevicefinder.cpp
)
# X11
optional_source(X11_FOUND
# DBUS
optional_source(HAVE_DBUS
SOURCES
widgets/osd_x11.cpp
osd/osddbus.cpp
HEADERS
osd/osddbus.h
)
# GStreamer
@@ -533,15 +541,6 @@ optional_source(HAVE_GSTREAMER
HEADERS engine/gststartup.h engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h
)
# Xine
optional_source(HAVE_XINE
SOURCES engine/xineengine.cpp
HEADERS engine/xineengine.h
)
optional_source(XINE_ANALYZER
SOURCES engine/xinescope.c
)
# VLC
optional_source(HAVE_VLC
SOURCES engine/vlcengine.cpp
@@ -561,31 +560,63 @@ if(UNIX AND HAVE_DBUS)
HEADERS device/udisks2lister.h
)
# MPRIS 2.0 DBUS interfaces
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Player.xml
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.xml
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.TrackList.xml
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
if (WITH_QT6)
# MPRIS 2.1 DBUS interfaces
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# MPRIS 2.0 DBUS interfaces
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Player.xml
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.xml
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.TrackList.xml
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
# org.freedesktop.Notifications DBUS interface
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.Notifications.xml
dbus/notification)
# MPRIS 2.1 DBUS interfaces
qt6_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# org.gnome.SettingsDaemon interface
qt5_add_dbus_interface(SOURCES
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
# org.freedesktop.Notifications DBUS interface
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.Notifications.xml
dbus/notification)
# org.gnome.SettingsDaemon interface
qt6_add_dbus_interface(SOURCES
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
else()
# MPRIS 2.0 DBUS interfaces
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Player.xml
core/mpris2.h mpris::Mpris2 core/mpris2_player Mpris2Player)
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.xml
core/mpris2.h mpris::Mpris2 core/mpris2_root Mpris2Root)
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.TrackList.xml
core/mpris2.h mpris::Mpris2 core/mpris2_tracklist Mpris2TrackList)
# MPRIS 2.1 DBUS interfaces
qt5_add_dbus_adaptor(SOURCES
dbus/org.mpris.MediaPlayer2.Playlists.xml
core/mpris2.h mpris::Mpris2 core/mpris2_playlists Mpris2Playlists)
# org.freedesktop.Notifications DBUS interface
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.Notifications.xml
dbus/notification)
# org.gnome.SettingsDaemon interface
qt5_add_dbus_interface(SOURCES
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
endif()
# org.freedesktop.Avahi.Server interface
add_custom_command(
@@ -626,21 +657,39 @@ if(UNIX AND HAVE_DBUS)
PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Job.xml
PROPERTIES NO_NAMESPACE dbus/udisks2job INCLUDE dbus/metatypes.h)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.DBus.ObjectManager.xml
dbus/objectmanager)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Filesystem.xml
dbus/udisks2filesystem)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Block.xml
dbus/udisks2block)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Drive.xml
dbus/udisks2drive)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Job.xml
dbus/udisks2job)
if(WITH_QT6)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.DBus.ObjectManager.xml
dbus/objectmanager)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Filesystem.xml
dbus/udisks2filesystem)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Block.xml
dbus/udisks2block)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Drive.xml
dbus/udisks2drive)
qt6_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Job.xml
dbus/udisks2job)
else()
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.DBus.ObjectManager.xml
dbus/objectmanager)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Filesystem.xml
dbus/udisks2filesystem)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Block.xml
dbus/udisks2block)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Drive.xml
dbus/udisks2drive)
qt5_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Job.xml
dbus/udisks2job)
endif()
endif(HAVE_UDISKS2)
endif(UNIX AND HAVE_DBUS)
@@ -688,21 +737,6 @@ optional_source(HAVE_GIO
HEADERS device/giolister.h
)
# imobiledevice backend and device
optional_source(HAVE_IMOBILEDEVICE
SOURCES
device/afcdevice.cpp
device/afcfile.cpp
device/afctransfer.cpp
device/ilister.cpp
device/imobiledeviceconnection.cpp
HEADERS
device/afcdevice.h
device/afcfile.h
device/afctransfer.h
device/ilister.h
)
# mtp device
optional_source(HAVE_LIBMTP
SOURCES
@@ -785,7 +819,7 @@ optional_source(APPLE
core/macsystemtrayicon.mm
core/macscreensaver.cpp
core/macfslistener.mm
widgets/osd_mac.mm
osd/osdmac.mm
widgets/qsearchfield_mac.mm
engine/macosdevicefinder.cpp
globalshortcuts/globalshortcutbackend-macos.mm
@@ -793,6 +827,7 @@ optional_source(APPLE
HEADERS
core/macsystemtrayicon.h
core/macfslistener.h
osd/osdmac.h
globalshortcuts/globalshortcutbackend-macos.h
)
@@ -810,7 +845,6 @@ optional_source(WIN32
SOURCES
engine/directsounddevicefinder.cpp
engine/mmdevicefinder.cpp
widgets/osd_win.cpp
core/windows7thumbbar.cpp
HEADERS
core/windows7thumbbar.h
@@ -881,9 +915,15 @@ optional_source(HAVE_MOODBAR
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
qt5_wrap_cpp(MOC ${HEADERS})
qt5_wrap_ui(UIC ${UI})
qt5_add_resources(QRC ${RESOURCES})
if(WITH_QT6)
qt6_wrap_cpp(MOC ${HEADERS})
qt6_wrap_ui(UIC ${UI})
qt6_add_resources(QRC ${RESOURCES})
else()
qt5_wrap_cpp(MOC ${HEADERS})
qt5_wrap_ui(UIC ${UI})
qt5_add_resources(QRC ${RESOURCES})
endif()
if(HAVE_TRANSLATIONS)
@@ -926,6 +966,7 @@ link_directories(
${QT_LIBRARY_DIRS}
${SINGLEAPPLICATION_LIBRARY_DIRS}
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
${QTSPARKLE_LIBRARY_DIRS}
)
if(HAVE_ALSA)
@@ -947,10 +988,6 @@ if(HAVE_GSTREAMER)
)
endif(HAVE_GSTREAMER)
if(HAVE_XINE)
link_directories(${LIBXINE_LIBRARY_DIRS})
endif()
if(HAVE_VLC)
link_directories(${LIBVLC_LIBRARY_DIRS})
endif()
@@ -983,14 +1020,6 @@ if(HAVE_LIBMTP)
link_directories(${LIBMTP_LIBRARY_DIRS})
endif(HAVE_LIBMTP)
if(HAVE_IMOBILEDEVICE)
link_directories(
${LIBUSBMUXD_LIBRARY_DIRS}
${LIBPLIST_LIBRARY_DIRS}
${LIBIMOBILEDEVICE_LIBRARY_DIRS}
)
endif(HAVE_IMOBILEDEVICE)
add_library(strawberry_lib STATIC
${SOURCES}
${MOC}
@@ -1033,6 +1062,7 @@ target_link_libraries(strawberry_lib PUBLIC
${QT_LIBRARIES}
${SINGLEAPPLICATION_LIBRARIES}
${SINGLECOREAPPLICATION_LIBRARIES}
${QTSPARKLE_LIBRARIES}
libstrawberry-common
libstrawberry-tagreader
)
@@ -1070,11 +1100,6 @@ if(HAVE_MOODBAR)
target_link_libraries(strawberry_lib PRIVATE gstmoodbar)
endif()
if(HAVE_XINE)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBXINE_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBXINE_LIBRARIES})
endif()
if(HAVE_VLC)
target_include_directories(strawberry_lib SYSTEM PRIVATE ${LIBVLC_INCLUDE_DIRS})
target_link_libraries(strawberry_lib PRIVATE ${LIBVLC_LIBRARIES})
@@ -1115,19 +1140,6 @@ if(HAVE_LIBMTP)
target_link_libraries(strawberry_lib PRIVATE ${LIBMTP_LIBRARIES})
endif(HAVE_LIBMTP)
if(HAVE_IMOBILEDEVICE)
target_include_directories(strawberry_lib SYSTEM PRIVATE
${LIBUSBMUXD_INCLUDE_DIRS}
${LIBPLIST_INCLUDE_DIRS}
${LIBIMOBILEDEVICE_INCLUDE_DIRS}
)
target_link_libraries(strawberry_lib PRIVATE
${LIBUSBMUXD_LIBRARIES}
${LIBPLIST_LIBRARIES}
${LIBIMOBILEDEVICE_LIBRARIES}
)
endif(HAVE_IMOBILEDEVICE)
if(APPLE)
target_link_libraries(strawberry_lib PRIVATE
@@ -1149,11 +1161,10 @@ if(WIN32)
target_link_libraries(strawberry_lib PRIVATE dsound)
endif(WIN32)
if(UNIX AND NOT APPLE)
if(X11_FOUND)
# Hack: the Gold linker pays attention to the order that libraries are specified on the link line.
# -lX11 and -ldl are provided earlier in the link command but they're actually used by libraries that appear after them, so they end up getting ignored.
# This appends them to the very end of the link line, ensuring they're always used.
find_package(X11)
if(FREEBSD)
target_link_libraries(strawberry_lib PRIVATE ${X11_X11_LIB})
else()
@@ -1196,6 +1207,10 @@ if(NOT APPLE)
install(TARGETS strawberry RUNTIME DESTINATION bin)
endif()
if(HAVE_TRANSLATIONS AND INSTALL_TRANSLATIONS AND INSTALL_TRANSLATIONS_FILES)
install(FILES ${INSTALL_TRANSLATIONS_FILES} DESTINATION share/strawberry/translations)
endif()
if(APPLE)
set_target_properties(strawberry PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/../dist/macos/Info.plist")
endif (APPLE)

View File

@@ -99,10 +99,14 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
if (engine_->type() != Engine::EngineType::GStreamer && engine_->type() != Engine::EngineType::Xine) return;
if (engine_->type() != Engine::EngineType::GStreamer) return;
if (e->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
context_menu_->popup(e->globalPosition().toPoint());
#else
context_menu_->popup(e->globalPos());
#endif
}
}

View File

@@ -28,11 +28,11 @@
#include <QPoint>
#include <QMenu>
#include <QAction>
#include <QActionGroup>
#include "engine/engine_fwd.h"
class QTimer;
class QActionGroup;
class QMouseEvent;
class QWheelEvent;

View File

@@ -325,7 +325,7 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount) {
void BlockAnalyzer::paletteChange(const QPalette&) {
const QColor bg = palette().color(QPalette::Background);
const QColor bg = palette().color(QPalette::Window);
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
topbarpixmap_.fill(fg);
@@ -343,12 +343,12 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
p.fillRect(0, y * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y), b + static_cast<int>(db * y)));
{
const QColor bg2 = palette().color(QPalette::Background).darker(112);
const QColor bg2 = palette().color(QPalette::Window).darker(112);
// make a complimentary fadebar colour
// TODO dark is not always correct, dumbo!
int h, s, v;
palette().color(QPalette::Background).darker(150).getHsv(&h, &s, &v);
palette().color(QPalette::Window).darker(150).getHsv(&h, &s, &v);
const QColor fg2(QColor::fromHsv(h + 120, s, v));
const double dr2 = fg2.red() - bg2.red();
@@ -358,7 +358,7 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
// Precalculate all fade-bar pixmaps
for (uint y = 0; y < kFadeSize; ++y) {
fade_bars_[y].fill(palette().color(QPalette::Background));
fade_bars_[y].fill(palette().color(QPalette::Window));
QPainter f(&fade_bars_[y]);
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
@@ -377,7 +377,7 @@ void BlockAnalyzer::drawBackground() {
return;
}
const QColor bg = palette().color(QPalette::Background);
const QColor bg = palette().color(QPalette::Window);
const QColor bgdark = bg.darker(112);
background_.fill(bg);

View File

@@ -87,7 +87,7 @@ void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
canvas_.fill(palette().color(QPalette::Window));
QPainter p(&barPixmap_);
for (uint y = 0; y < HEIGHT; ++y) {
@@ -120,7 +120,7 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
const uint MAX_HEIGHT = height() - 1;
QPainter canvas_painter(&canvas_);
canvas_.fill(palette().color(QPalette::Background));
canvas_.fill(palette().color(QPalette::Window));
Analyzer::interpolate(scope, scope_);

View File

@@ -131,7 +131,7 @@ void SCollection::Exit() {
void SCollection::ExitReceived() {
QObject *obj = static_cast<QObject*>(sender());
QObject *obj = qobject_cast<QObject*>(sender());
disconnect(obj, nullptr, this, nullptr);
qLog(Debug) << obj << "successfully exited.";
wait_for_exit_.removeAll(obj);

View File

@@ -38,7 +38,7 @@
#include <QUrl>
#include <QFileInfo>
#include <QDateTime>
#include <QRegExp>
#include <QRegularExpression>
#include <QSqlDatabase>
#include <QSqlQuery>
@@ -222,7 +222,7 @@ SubdirectoryList CollectionBackend::SubdirsInDirectory(int id, QSqlDatabase &db)
Subdirectory subdir;
subdir.directory_id = id;
subdir.path = q.value(0).toString();
subdir.mtime = q.value(1).toUInt();
subdir.mtime = q.value(1).toLongLong();
subdirs << subdir;
}
@@ -1031,7 +1031,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
info.first_url = QUrl::fromEncoded(query.Value(7).toByteArray());
QString art_automatic = query.Value(5).toString();
if (art_automatic.contains(QRegExp("..+:.*"))) {
if (art_automatic.contains(QRegularExpression("..+:.*"))) {
info.art_automatic = QUrl::fromEncoded(art_automatic.toUtf8());
}
else {
@@ -1039,7 +1039,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
}
QString art_manual = query.Value(6).toString();
if (art_manual.contains(QRegExp("..+:.*"))) {
if (art_manual.contains(QRegularExpression("..+:.*"))) {
info.art_manual = QUrl::fromEncoded(art_manual.toUtf8());
}
else {
@@ -1228,7 +1228,7 @@ void CollectionBackend::IncrementPlayCount(int id) {
QSqlQuery q(db);
q.prepare(QString("UPDATE %1 SET playcount = playcount + 1, lastplayed = :now WHERE ROWID = :id").arg(songs_table_));
q.bindValue(":now", QDateTime::currentDateTime().toTime_t());
q.bindValue(":now", QDateTime::currentDateTime().toSecsSinceEpoch());
q.bindValue(":id", id);
q.exec();
if (db_->CheckErrors(q)) return;

View File

@@ -31,7 +31,7 @@
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QRegularExpression>
#include <QInputDialog>
#include <QList>
#include <QTimer>
@@ -63,27 +63,26 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
ui_->setupUi(this);
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegExp("\\bfts"), "");
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegularExpression("\\bfts"), "");
ui_->filter->setToolTip(
"<html><head/><body><p>" +
QString("<html><head/><body><p>") +
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
" " +
"<span style=\"font-weight:600;\">" +
QString(" ") +
QString("<span style=\"font-weight:600;\">") +
tr("artist") +
":" +
"</span><span style=\"font-style:italic;\">Strawbs</span>" +
" " +
QString(":") +
QString("</span><span style=\"font-style:italic;\">Strawbs</span>") +
QString(" ") +
tr("searches the collection for all artists that contain the word") +
"Strawbs" +
"." +
"</p><p><span style=\"font-weight:600;\">" +
QString(" Strawbs.") +
QString("</p><p><span style=\"font-weight:600;\">") +
tr("Available fields") +
": " +
QString(": ") +
"</span><span style=\"font-style:italic;\">" +
available_fields +
"</span>." +
"</p></body></html>"
QString("</span>.") +
QString("</p></body></html>")
);
connect(ui_->filter, SIGNAL(returnPressed()), SIGNAL(ReturnPressed()));

View File

@@ -33,7 +33,6 @@ class CollectionItem : public SimpleTreeItem<CollectionItem> {
Type_Divider,
Type_Container,
Type_Song,
Type_PlaylistContainer,
Type_LoadingIndicator,
};

View File

@@ -27,7 +27,7 @@
#include <QObject>
#include <QtGlobal>
#include <QtConcurrentRun>
#include <QtConcurrent>
#include <QThread>
#include <QMutex>
#include <QFuture>
@@ -44,7 +44,7 @@
#include <QUrl>
#include <QImage>
#include <QChar>
#include <QRegExp>
#include <QRegularExpression>
#include <QPixmapCache>
#include <QNetworkDiskCache>
#include <QSettings>
@@ -97,8 +97,6 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
total_album_count_(0),
artist_icon_(IconLoader::Load("folder-sound")),
album_icon_(IconLoader::Load("cdcase")),
playlists_dir_icon_(IconLoader::Load("folder-sound")),
playlist_icon_(IconLoader::Load("albums")),
init_task_id_(-1),
use_pretty_covers_(false),
show_dividers_(true),
@@ -695,8 +693,6 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const
case Qt::DecorationRole:
switch (item->type) {
case CollectionItem::Type_PlaylistContainer:
return playlists_dir_icon_;
case CollectionItem::Type_Container:
switch (container_type) {
case GroupBy_Album:
@@ -859,9 +855,8 @@ void CollectionModel::LazyPopulate(CollectionItem *parent, const bool signal) {
}
void CollectionModel::ResetAsync() {
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(this, &CollectionModel::RunQuery, root_);
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(std::bind(&CollectionModel::RunQuery, this, root_));
NewClosure(future, this, SLOT(ResetAsyncQueryFinished(QFuture<CollectionModel::QueryResult>)), future);
}
void CollectionModel::ResetAsyncQueryFinished(QFuture<CollectionModel::QueryResult> future) {
@@ -1421,7 +1416,7 @@ QString CollectionModel::PrettyYearAlbum(const int year, const QString &album) {
QString CollectionModel::PrettyAlbumDisc(const QString &album, const int disc) {
if (disc <= 0 || album.contains(QRegExp(Song::kAlbumRemoveDisc))) return TextOrUnknown(album);
if (disc <= 0 || album.contains(QRegularExpression(Song::kAlbumRemoveDisc))) return TextOrUnknown(album);
else return TextOrUnknown(album) + " - (Disc " + QString::number(disc) + ")";
}
@@ -1433,7 +1428,7 @@ QString CollectionModel::PrettyYearAlbumDisc(const int year, const QString &albu
if (year <= 0) str = TextOrUnknown(album);
else str = QString::number(year) + " - " + TextOrUnknown(album);
if (!album.contains(QRegExp(Song::kAlbumRemoveDisc)) && disc > 0) str += " - (Disc " + QString::number(disc) + ")";
if (!album.contains(QRegularExpression(Song::kAlbumRemoveDisc)) && disc > 0) str += " - (Disc " + QString::number(disc) + ")";
return str;
@@ -1447,7 +1442,7 @@ QString CollectionModel::SortText(QString text) {
else {
text = text.toLower();
}
text = text.remove(QRegExp("[^\\w ]"));
text = text.remove(QRegularExpression("[^\\w ]", QRegularExpression::UseUnicodePropertiesOption));
return text;

View File

@@ -280,8 +280,6 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
QIcon album_icon_;
// Used as a generic icon to show when no cover art is found, fixed to the same size as the artwork (32x32)
QPixmap no_cover_icon_;
QIcon playlists_dir_icon_;
QIcon playlist_icon_;
static QNetworkDiskCache *sIconCache;

View File

@@ -40,6 +40,7 @@ class CollectionPlaylistItem : public PlaylistItem {
void Reload() override;
Song Metadata() const override;
Song OriginalMetadata() const override { return song_; }
void SetMetadata(const Song &song) { song_ = song; }
QUrl Url() const override;

View File

@@ -26,7 +26,7 @@
#include <QString>
#include <QStringList>
#include <QStringBuilder>
#include <QRegExp>
#include <QRegularExpression>
#include <QSqlDatabase>
#include <QSqlQuery>
@@ -46,9 +46,9 @@ CollectionQuery::CollectionQuery(const QueryOptions &options)
// Split on whitespace
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList tokens(options.filter().split(QRegExp("\\s+"), Qt::SkipEmptyParts));
QStringList tokens(options.filter().split(QRegularExpression("\\s+"), Qt::SkipEmptyParts));
#else
QStringList tokens(options.filter().split(QRegExp("\\s+"), QString::SkipEmptyParts));
QStringList tokens(options.filter().split(QRegularExpression("\\s+"), QString::SkipEmptyParts));
#endif
QString query;
for (QString token : tokens) {
@@ -65,17 +65,21 @@ CollectionQuery::CollectionQuery(const QueryOptions &options)
QString subtoken = token.section(':', 1, -1);
subtoken.replace(":", " ");
subtoken = subtoken.trimmed();
if (!subtoken.isEmpty())
query += "fts" + columntoken + subtoken + "* ";
if (!subtoken.isEmpty()) {
if (!query.isEmpty()) query.append(" ");
query += "fts" + columntoken + "\"" + subtoken + "\"*";
}
}
else {
token.replace(":", " ");
token = token.trimmed();
query += token + "* ";
if (!query.isEmpty()) query.append(" ");
query += "\"" + token + "\"*";
}
}
else {
query += token + "* ";
if (!query.isEmpty()) query.append(" ");
query += "\"" + token + "\"*";
}
}
if (!query.isEmpty()) {
@@ -86,7 +90,7 @@ CollectionQuery::CollectionQuery(const QueryOptions &options)
}
if (options.max_age() != -1) {
int cutoff = QDateTime::currentDateTime().toTime_t() - options.max_age();
int cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - options.max_age();
where_clauses_ << "ctime > ?";
bound_values_ << cutoff;
@@ -202,7 +206,7 @@ QVariant CollectionQuery::Value(int column) const { return query_.value(column);
bool QueryOptions::Matches(const Song &song) const {
if (max_age_ != -1) {
const uint cutoff = QDateTime::currentDateTime().toTime_t() - max_age_;
const uint cutoff = QDateTime::currentDateTime().toSecsSinceEpoch() - max_age_;
if (song.ctime() <= cutoff) return false;
}

View File

@@ -61,7 +61,7 @@
# include "device/devicestatefiltermodel.h"
#endif
#include "dialogs/edittagdialog.h"
#include "organise/organisedialog.h"
#include "organize/organizedialog.h"
#include "settings/collectionsettingspage.h"
CollectionView::CollectionView(QWidget *parent)
@@ -325,7 +325,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
add_to_playlist_enqueue_next_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue to play next"), this, SLOT(AddToPlaylistEnqueueNext()));
context_menu_->addSeparator();
organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise()));
organize_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organize files..."), this, SLOT(Organize()));
#ifndef Q_OS_WIN
copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice()));
#endif
@@ -366,39 +366,40 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
int regular_editable = 0;
for (const QModelIndex &index : selected_indexes) {
regular_elements++;
++regular_elements;
if(app_->collection_model()->data(index, CollectionModel::Role_Editable).toBool()) {
regular_editable++;
++regular_editable;
}
}
// TODO: check if custom plugin actions should be enabled / visible
const int songs_selected = regular_elements;
const bool regular_elements_only = songs_selected == regular_elements && regular_elements > 0;
// in all modes
load_->setEnabled(songs_selected);
add_to_playlist_->setEnabled(songs_selected);
open_in_new_playlist_->setEnabled(songs_selected);
add_to_playlist_enqueue_->setEnabled(songs_selected);
load_->setEnabled(songs_selected > 0);
add_to_playlist_->setEnabled(songs_selected > 0);
open_in_new_playlist_->setEnabled(songs_selected > 0);
add_to_playlist_enqueue_->setEnabled(songs_selected > 0);
// if neither edit_track not edit_tracks are available, we show disabled edit_track element
edit_track_->setVisible(regular_editable <= 1);
edit_track_->setVisible(regular_editable == 1);
edit_track_->setEnabled(regular_editable == 1);
edit_tracks_->setVisible(regular_editable > 1);
edit_tracks_->setEnabled(regular_editable > 1);
rescan_songs_->setVisible(edit_track_->isVisible());
rescan_songs_->setEnabled(true);
rescan_songs_->setVisible(regular_editable > 0);
rescan_songs_->setEnabled(regular_editable > 0);
organise_->setVisible(regular_elements_only);
organize_->setVisible(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setVisible(regular_elements_only);
copy_to_device_->setVisible(regular_elements == regular_editable);
#endif
//delete_->setVisible(regular_elements_only);
show_in_various_->setVisible(regular_elements_only);
no_show_in_various_->setVisible(regular_elements_only);
// only when all selected items are editable
organise_->setEnabled(regular_elements == regular_editable);
organize_->setEnabled(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setEnabled(regular_elements == regular_editable);
#endif
@@ -529,15 +530,15 @@ SongList CollectionView::GetSelectedSongs() const {
}
void CollectionView::Organise() {
void CollectionView::Organize() {
if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager(), app_->collection_backend(), this));
if (!organize_dialog_)
organize_dialog_.reset(new OrganizeDialog(app_->task_manager(), app_->collection_backend(), this));
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organise_dialog_->SetCopy(false);
if (organise_dialog_->SetSongs(GetSelectedSongs()))
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organize_dialog_->SetCopy(false);
if (organize_dialog_->SetSongs(GetSelectedSongs()))
organize_dialog_->show();
else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
}
@@ -568,13 +569,13 @@ void CollectionView::RescanSongs() {
void CollectionView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager(), nullptr, this));
if (!organize_dialog_)
organize_dialog_.reset(new OrganizeDialog(app_->task_manager(), nullptr, this));
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true);
organise_dialog_->SetSongs(GetSelectedSongs());
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organize_dialog_->SetCopy(true);
organize_dialog_->SetSongs(GetSelectedSongs());
organize_dialog_->show();
#endif
}

View File

@@ -45,7 +45,7 @@ class QPaintEvent;
class Application;
class CollectionFilterWidget;
class EditTagDialog;
class OrganiseDialog;
class OrganizeDialog;
class CollectionView : public AutoExpandingTreeView {
Q_OBJECT
@@ -102,7 +102,7 @@ class CollectionView : public AutoExpandingTreeView {
void AddToPlaylistEnqueue();
void AddToPlaylistEnqueueNext();
void OpenInNewPlaylist();
void Organise();
void Organize();
void CopyToDevice();
void EditTracks();
void RescanSongs();
@@ -133,7 +133,7 @@ class CollectionView : public AutoExpandingTreeView {
QAction *add_to_playlist_enqueue_;
QAction *add_to_playlist_enqueue_next_;
QAction *open_in_new_playlist_;
QAction *organise_;
QAction *organize_;
#ifndef Q_OS_WIN
QAction *copy_to_device_;
#endif
@@ -145,7 +145,7 @@ class CollectionView : public AutoExpandingTreeView {
QAction *show_in_various_;
QAction *no_show_in_various_;
std::unique_ptr<OrganiseDialog> organise_dialog_;
std::unique_ptr<OrganizeDialog> organize_dialog_;
std::unique_ptr<EditTagDialog> edit_tag_dialog_;
bool is_in_keyboard_search_;

View File

@@ -328,7 +328,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
return;
}
if (!t->ignores_mtime() && !force_noincremental && t->is_incremental() && subdir.mtime == path_info.lastModified().toTime_t()) {
if (!t->ignores_mtime() && !force_noincremental && t->is_incremental() && subdir.mtime == path_info.lastModified().toSecsSinceEpoch()) {
// The directory hasn't changed since last time
t->AddToProgress(1);
return;
@@ -362,7 +362,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
Subdirectory new_subdir;
new_subdir.directory_id = -1;
new_subdir.path = child;
new_subdir.mtime = child_info.lastModified().toTime_t();
new_subdir.mtime = child_info.lastModified().toSecsSinceEpoch();
my_new_subdirs << new_subdir;
}
}
@@ -393,7 +393,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
Song matching_song(source_);
if (FindSongByPath(songs_in_db, file, &matching_song)) {
uint matching_cue_mtime = GetMtimeForCue(matching_cue);
qint64 matching_cue_mtime = GetMtimeForCue(matching_cue);
// The song is in the database and still on disk.
// Check the mtime to see if it's been changed since it was added.
@@ -407,13 +407,13 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// cue sheet's path from collection (if any)
QString song_cue = matching_song.cue_path();
uint song_cue_mtime = GetMtimeForCue(song_cue);
qint64 song_cue_mtime = GetMtimeForCue(song_cue);
bool cue_deleted = song_cue_mtime == 0 && matching_song.has_cue();
bool cue_added = matching_cue_mtime != 0 && !matching_song.has_cue();
// watch out for cue songs which have their mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toTime_t(), song_cue_mtime)) || cue_deleted || cue_added;
bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toSecsSinceEpoch(), song_cue_mtime)) || cue_deleted || cue_added;
// Also want to look to see whether the album art has changed
QUrl image = ImageForSong(file, album_art);
@@ -470,7 +470,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// Add this subdir to the new or touched list
Subdirectory updated_subdir;
updated_subdir.directory_id = t->dir();
updated_subdir.mtime = path_info.exists() ? path_info.lastModified().toTime_t() : 0;
updated_subdir.mtime = path_info.exists() ? path_info.lastModified().toSecsSinceEpoch() : 0;
updated_subdir.path = path;
if (subdir.directory_id == -1)
@@ -560,7 +560,7 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
SongList song_list;
uint matching_cue_mtime = GetMtimeForCue(matching_cue);
quint64 matching_cue_mtime = GetMtimeForCue(matching_cue);
// If it's a cue - create virtual tracks
if (matching_cue_mtime) {
// don't process the same cue many times
@@ -629,7 +629,7 @@ void CollectionWatcher::PreserveUserSetData(const QString &file, const QUrl &ima
}
uint CollectionWatcher::GetMtimeForCue(const QString &cue_path) {
quint64 CollectionWatcher::GetMtimeForCue(const QString &cue_path) {
// Slight optimisation
if (cue_path.isEmpty()) {
@@ -643,7 +643,7 @@ uint CollectionWatcher::GetMtimeForCue(const QString &cue_path) {
const QDateTime cue_last_modified = file_info.lastModified();
return cue_last_modified.isValid() ? cue_last_modified.toTime_t() : 0;
return cue_last_modified.isValid() ? cue_last_modified.toSecsSinceEpoch() : 0;
}
void CollectionWatcher::AddWatch(const Directory &dir, const QString &path) {

View File

@@ -166,7 +166,7 @@ class CollectionWatcher : public QObject {
QUrl ImageForSong(const QString &path, QMap<QString, QStringList> &album_art);
void AddWatch(const Directory &dir, const QString &path);
void RemoveWatch(const Directory &dir, const Subdirectory &subdir);
uint GetMtimeForCue(const QString &cue_path);
quint64 GetMtimeForCue(const QString &cue_path);
void PerformScan(bool incremental, bool ignore_mtimes);
// Updates the sections of a cue associated and altered (according to mtime) media file during a scan.

View File

@@ -49,7 +49,7 @@ struct Subdirectory {
int directory_id;
QString path;
uint mtime;
qint64 mtime;
};
Q_DECLARE_METATYPE(Subdirectory)

View File

@@ -21,7 +21,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>You can change the way the songs in the collection are organised.</string>
<string>You can change the way the songs in the collection are organized.</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

@@ -31,20 +31,18 @@
#cmakedefine HAVE_X11
#cmakedefine HAVE_UDISKS2
#cmakedefine HAVE_ALSA
#cmakedefine HAVE_IMOBILEDEVICE
#cmakedefine HAVE_AUDIOCD
#cmakedefine HAVE_LIBGPOD
#cmakedefine HAVE_LIBMTP
#cmakedefine HAVE_LIBPULSE
#cmakedefine HAVE_SPARKLE
#cmakedefine HAVE_QTSPARKLE
#cmakedefine HAVE_CHROMAPRINT
#cmakedefine HAVE_GLOBALSHORTCUTS
#cmakedefine USE_INSTALL_PREFIX
#cmakedefine HAVE_GSTREAMER
#cmakedefine HAVE_VLC
#cmakedefine HAVE_XINE
#cmakedefine XINE_ANALYZER
#cmakedefine HAVE_SUBSONIC
#cmakedefine HAVE_TIDAL
@@ -59,9 +57,10 @@
#cmakedefine HAVE_TAGLIB_DSDIFFFILE
#cmakedefine USE_BUNDLE
#define USE_BUNDLE_DIR "${USE_BUNDLE_DIR}"
#cmakedefine HAVE_TRANSLATIONS
#cmakedefine INSTALL_TRANSLATIONS
#define TRANSLATIONS_DIR "${CMAKE_INSTALL_PREFIX}/share/strawberry/translations"
#endif // CONFIG_H_IN

View File

@@ -31,7 +31,7 @@
#include <QVariant>
#include <QList>
#include <QSet>
#include <QRegExp>
#include <QRegularExpression>
#include <QString>
#include <QStringList>
#include <QUrl>
@@ -62,8 +62,7 @@ ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *
SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
backend_(backend),
app_(app),
album_icon_(IconLoader::Load("cdcase")),
playlists_dir_icon_(IconLoader::Load("folder-sound")) {
album_icon_(IconLoader::Load("cdcase")) {
root_->lazy_loaded = true;
@@ -195,8 +194,6 @@ QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
case Qt::DecorationRole:
switch (item->type) {
case CollectionItem::Type_PlaylistContainer:
return playlists_dir_icon_;
case CollectionItem::Type_Container:
if (item->type == CollectionItem::Type_Container && item->container_level == 0) { return album_icon_; }
break;
@@ -370,7 +367,7 @@ QString ContextAlbumsModel::SortText(QString text) {
else {
text = text.toLower();
}
text = text.remove(QRegExp("[^\\w ]"));
text = text.remove(QRegularExpression("[^\\w ]", QRegularExpression::UseUnicodePropertiesOption));
return text;

View File

@@ -120,7 +120,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
QMap<int, CollectionItem*> song_nodes_;
QIcon album_icon_;
QPixmap no_cover_icon_;
QIcon playlists_dir_icon_;
AlbumCoverLoaderOptions cover_loader_options_;
typedef QPair<CollectionItem*, QString> ItemAndCacheKey;
QMap<quint64, ItemAndCacheKey> pending_art_;

View File

@@ -56,7 +56,7 @@
# include "device/devicestatefiltermodel.h"
#endif
#include "dialogs/edittagdialog.h"
#include "organise/organisedialog.h"
#include "organize/organizedialog.h"
#include "contextalbumsmodel.h"
#include "contextalbumsview.h"
@@ -257,7 +257,7 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue track"), this, SLOT(AddToPlaylistEnqueue()));
context_menu_->addSeparator();
organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise()));
organize_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organize files..."), this, SLOT(Organize()));
#ifndef Q_OS_WIN
copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice()));
#endif
@@ -304,13 +304,13 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
edit_track_->setVisible(regular_editable <= 1);
edit_track_->setEnabled(regular_editable == 1);
organise_->setVisible(regular_elements_only);
organize_->setVisible(regular_elements_only);
#ifndef Q_OS_WIN
copy_to_device_->setVisible(regular_elements_only);
#endif
// only when all selected items are editable
organise_->setEnabled(regular_elements == regular_editable);
organize_->setEnabled(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setEnabled(regular_elements == regular_editable);
#endif
@@ -369,15 +369,15 @@ SongList ContextAlbumsView::GetSelectedSongs() const {
return model_->GetChildSongs(selected_indexes);
}
void ContextAlbumsView::Organise() {
void ContextAlbumsView::Organize() {
if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager(), app_->collection_backend(), this));
if (!organize_dialog_)
organize_dialog_.reset(new OrganizeDialog(app_->task_manager(), app_->collection_backend(), this));
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organise_dialog_->SetCopy(false);
if (organise_dialog_->SetSongs(GetSelectedSongs()))
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organize_dialog_->SetCopy(false);
if (organize_dialog_->SetSongs(GetSelectedSongs()))
organize_dialog_->show();
else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
}
@@ -396,13 +396,13 @@ void ContextAlbumsView::EditTracks() {
void ContextAlbumsView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organise_dialog_)
organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
if (!organize_dialog_)
organize_dialog_.reset(new OrganizeDialog(app_->task_manager()));
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true);
organise_dialog_->SetSongs(GetSelectedSongs());
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organize_dialog_->SetCopy(true);
organize_dialog_->SetSongs(GetSelectedSongs());
organize_dialog_->show();
#endif
}

View File

@@ -48,7 +48,7 @@ class QPaintEvent;
class Application;
class ContextAlbumsModel;
class EditTagDialog;
class OrganiseDialog;
class OrganizeDialog;
class ContextItemDelegate : public QStyledItemDelegate {
Q_OBJECT
@@ -93,7 +93,7 @@ class ContextAlbumsView : public AutoExpandingTreeView {
void AddToPlaylist();
void AddToPlaylistEnqueue();
void OpenInNewPlaylist();
void Organise();
void Organize();
void CopyToDevice();
void EditTracks();
void ShowInBrowser();
@@ -112,7 +112,7 @@ class ContextAlbumsView : public AutoExpandingTreeView {
QAction *add_to_playlist_;
QAction *add_to_playlist_enqueue_;
QAction *open_in_new_playlist_;
QAction *organise_;
QAction *organize_;
#ifndef Q_OS_WIN
QAction *copy_to_device_;
#endif
@@ -120,7 +120,7 @@ class ContextAlbumsView : public AutoExpandingTreeView {
QAction *edit_tracks_;
QAction *show_in_browser_;
std::unique_ptr<OrganiseDialog> organise_dialog_;
std::unique_ptr<OrganizeDialog> organize_dialog_;
std::unique_ptr<EditTagDialog> edit_tag_dialog_;
bool is_in_keyboard_search_;

View File

@@ -13,6 +13,10 @@
SBSystemPreferencesWindow, SBSystemPreferencesPane,
SBSystemPreferencesAnchor;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmultichar"
#pragma GCC diagnostic ignored "-Wfour-char-constants"
enum SBSystemPreferencesSaveOptions {
SBSystemPreferencesSaveOptionsYes = 'yes ' /* Save the file. */,
SBSystemPreferencesSaveOptionsNo = 'no ' /* Do not save the file. */,
@@ -27,6 +31,9 @@ enum SBSystemPreferencesPrintingErrorHandling {
SBSystemPreferencesPrintingErrorHandlingDetailed =
'lwdt' /* print a detailed report of PostScript errors */
};
#pragma GCC diagnostic pop
typedef enum SBSystemPreferencesPrintingErrorHandling
SBSystemPreferencesPrintingErrorHandling;

View File

@@ -38,7 +38,7 @@
#include <QString>
#include <QStringBuilder>
#include <QStringList>
#include <QRegExp>
#include <QRegularExpression>
#include <QUrl>
#include <QSqlDriver>
#include <QSqlDatabase>
@@ -392,7 +392,7 @@ void Database::ExecSchemaCommandsFromFile(QSqlDatabase &db, const QString &filen
void Database::ExecSchemaCommands(QSqlDatabase &db, const QString &schema, int schema_version, bool in_transaction) {
// Run each command
const QStringList commands(schema.split(QRegExp("; *\n\n")));
const QStringList commands(schema.split(QRegularExpression("; *\n\n")));
// We don't want this list to reflect possible DB schema changes so we initialize it before executing any statements.
// If no outer transaction is provided the song tables need to be queried before beginning an inner transaction! Otherwise

View File

@@ -69,8 +69,13 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob &job) {
// Copy or move
bool result(true);
if (job.remove_original_) {
result = QFile::rename(src.absoluteFilePath(), dest.absoluteFilePath());
if (!cover_src.filePath().isEmpty() && !cover_dest.filePath().isEmpty()) {
if (dest.exists() && !job.overwrite_) {
result = false;
}
else {
result = QFile::rename(src.absoluteFilePath(), dest.absoluteFilePath());
}
if ((!cover_dest.exists() || job.overwrite_) && !cover_src.filePath().isEmpty() && !cover_dest.filePath().isEmpty()) {
QFile::rename(cover_src.absoluteFilePath(), cover_dest.absoluteFilePath());
}
// Remove empty directories.
@@ -83,10 +88,13 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob &job) {
#endif
}
else {
if (!dest.exists()) {
if (dest.exists() && !job.overwrite_) {
result = false;
}
else {
result = QFile::copy(src.absoluteFilePath(), dest.absoluteFilePath());
}
if (!cover_src.filePath().isEmpty() && !cover_dest.filePath().isEmpty() && !cover_dest.exists()) {
if ((!cover_dest.exists() || job.overwrite_) && !cover_src.filePath().isEmpty() && !cover_dest.filePath().isEmpty()) {
QFile::copy(cover_src.absoluteFilePath(), cover_dest.absoluteFilePath());
}
}

View File

@@ -129,7 +129,6 @@ static const QMap<QString, IconProperties> iconmapper_ = {
{ "view-refresh", { {}} },
{ "library-music", { {"vinyl"}} },
{ "vlc", { {}} },
{ "xine", { {}} },
{ "zoom-in", { {}} },
{ "zoom-out", { {}, 0, 0 } }

View File

@@ -53,7 +53,7 @@
#include "globalshortcuts/globalshortcutbackend-macos.h"
#ifdef HAVE_SPARKLE
# import <Sparkle/SUUpdater.h>
# import <SUUpdater.h>
#endif
#include <QApplication>
@@ -117,6 +117,9 @@ QDebug operator<<(QDebug dbg, NSObject* object) {
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag {
Q_UNUSED(app);
Q_UNUSED(flag);
if (application_handler_) {
application_handler_->Activate();
}
@@ -129,6 +132,7 @@ QDebug operator<<(QDebug dbg, NSObject* object) {
}
- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
Q_UNUSED(sender);
return dock_menu_;
}
@@ -141,11 +145,13 @@ QDebug operator<<(QDebug dbg, NSObject* object) {
}
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
Q_UNUSED(aNotification);
}
- (BOOL)application:(NSApplication*)app openFile:(NSString*)filename {
Q_UNUSED(app);
qLog(Debug) << "Wants to open:" << [filename UTF8String];
if (application_handler_->LoadUrl(QString::fromUtf8([filename UTF8String]))) {
@@ -159,17 +165,20 @@ QDebug operator<<(QDebug dbg, NSObject* object) {
- (void)application:(NSApplication*)app openFiles:(NSArray*)filenames {
qLog(Debug) << "Wants to open:" << filenames;
[filenames enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL* stop) {
[filenames enumerateObjectsUsingBlock:^(id object, NSUInteger, BOOL*) {
[self application:app openFile:(NSString*)object];
}];
}
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*) sender {
Q_UNUSED(sender);
return NSTerminateNow;
}
- (BOOL) userNotificationCenter: (id)center shouldPresentNotification: (id)notification {
Q_UNUSED(center);
Q_UNUSED(notification);
// Always show notifications, even if Strawberry is in the foreground.
return YES;
}

View File

@@ -47,6 +47,10 @@ void MacFSListener::Init() { run_loop_ = CFRunLoopGetCurrent(); }
void MacFSListener::EventStreamCallback(ConstFSEventStreamRef stream, void* user_data, size_t num_events, void* event_paths, const FSEventStreamEventFlags event_flags[], const FSEventStreamEventId event_ids[]) {
Q_UNUSED(stream);
Q_UNUSED(event_flags);
Q_UNUSED(event_ids);
MacFSListener* me = reinterpret_cast<MacFSListener*>(user_data);
char** paths = reinterpret_cast<char**>(event_paths);
for (size_t i = 0; i < num_events; ++i) {

View File

@@ -52,6 +52,7 @@
}
- (BOOL) validateMenuItem: (NSMenuItem*)menuItem {
Q_UNUSED(menuItem);
// This is called when the menu is shown.
return action_->isEnabled();
}
@@ -206,5 +207,6 @@ void MacSystemTrayIcon::ClearNowPlaying() {
}
void MacSystemTrayIcon::SetNowPlaying(const Song& song, const QUrl& cover_url) {
Q_UNUSED(cover_url);
p_->ShowNowPlaying(song.artist(), song.PrettyTitle());
}

View File

@@ -20,6 +20,7 @@
*/
#include "config.h"
#include "version.h"
#include <memory>
#include <functional>
@@ -67,9 +68,11 @@
#include <QStackedWidget>
#include <QTabBar>
#include <QToolButton>
#include <QClipboard>
#include "core/logging.h"
#include "core/closure.h"
#include "core/network.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
@@ -97,14 +100,14 @@
#include "dialogs/trackselectiondialog.h"
#include "dialogs/edittagdialog.h"
#include "dialogs/addstreamdialog.h"
#include "organise/organisedialog.h"
#include "organize/organizedialog.h"
#include "widgets/fancytabwidget.h"
#include "widgets/playingwidget.h"
#include "widgets/volumeslider.h"
#include "widgets/fileview.h"
#include "widgets/multiloadingindicator.h"
#include "widgets/osd.h"
#include "widgets/trackslider.h"
#include "osd/osdbase.h"
#include "context/contextview.h"
#include "context/contextalbumsview.h"
#include "collection/collection.h"
@@ -181,6 +184,14 @@
# include "windows7thumbbar.h"
#endif
#ifdef HAVE_QTSPARKLE
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include <qtsparkle-qt6/Updater>
# else
# include <qtsparkle-qt5/Updater>
# endif
#endif // HAVE_QTSPARKLE
const char *MainWindow::kSettingsGroup = "MainWindow";
const char *MainWindow::kAllFilesFilterSpec = QT_TR_NOOP("All Files (*)");
@@ -189,7 +200,7 @@ const int kTrackSliderUpdateTimeMs = 200;
const int kTrackPositionUpdateTimeMs = 1000;
}
MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, const CommandlineOptions &options, QWidget *parent) :
MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd, const CommandlineOptions &options, QWidget *parent) :
QMainWindow(parent),
ui_(new Ui_MainWindow),
#ifdef Q_OS_WIN
@@ -221,8 +232,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
return cover_manager;
}),
equalizer_(new Equalizer),
organise_dialog_([=]() {
OrganiseDialog *dialog = new OrganiseDialog(app->task_manager(), app->collection_backend(), this);
organize_dialog_([=]() {
OrganizeDialog *dialog = new OrganizeDialog(app->task_manager(), app->collection_backend(), this);
dialog->SetDestinationModel(app->collection()->model()->directory_model());
return dialog;
}),
@@ -243,9 +254,28 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#ifdef HAVE_TIDAL
tidal_view_(new InternetTabsView(app_, app->internet_services()->ServiceBySource(Song::Source_Tidal), TidalSettingsPage::kSettingsGroup, SettingsDialog::Page_Tidal, this)),
#endif
collection_show_all_(nullptr),
collection_show_duplicates_(nullptr),
collection_show_untagged_(nullptr),
playlist_menu_(new QMenu(this)),
playlist_play_pause_(nullptr),
playlist_stop_after_(nullptr),
playlist_undoredo_(nullptr),
playlist_organize_(nullptr),
playlist_show_in_collection_(nullptr),
playlist_copy_to_collection_(nullptr),
playlist_move_to_collection_(nullptr),
#ifndef Q_OS_WIN
playlist_copy_to_device_(nullptr),
#endif
playlist_open_in_browser_(nullptr),
playlist_copy_url_(nullptr),
playlist_queue_(nullptr),
playlist_queue_play_next_(nullptr),
playlist_skip_(nullptr),
playlist_add_to_another_(nullptr),
playlistitem_actions_separator_(nullptr),
playlist_rescan_songs_(nullptr),
collection_sort_model_(new QSortFilterProxyModel(this)),
track_position_timer_(new QTimer(this)),
track_slider_timer_(new QTimer(this)),
@@ -335,7 +365,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#endif
playlist_list_->SetApplication(app_);
organise_dialog_->SetDestinationModel(app_->collection()->model()->directory_model());
organize_dialog_->SetDestinationModel(app_->collection()->model()->directory_model());
// Icons
qLog(Debug) << "Creating UI";
@@ -371,8 +401,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
ui_->action_shuffle->setIcon(IconLoader::Load("media-playlist-shuffle"));
ui_->action_remove_duplicates->setIcon(IconLoader::Load("list-remove"));
ui_->action_remove_unavailable->setIcon(IconLoader::Load("list-remove"));
//ui_->action_remove_from_playlist->setIcon(IconLoader::Load("list-remove"));
ui_->action_remove_from_playlist->setIcon(IconLoader::Load("list-remove"));
// Configure
@@ -416,7 +445,6 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->action_remove_unavailable, SIGNAL(triggered()), app_->playlist_manager(), SLOT(RemoveUnavailableCurrent()));
connect(ui_->action_remove_from_playlist, SIGNAL(triggered()), SLOT(PlaylistRemoveCurrent()));
connect(ui_->action_edit_track, SIGNAL(triggered()), SLOT(EditTracks()));
connect(ui_->action_rescan_songs, SIGNAL(triggered()), SLOT(RescanSongs()));
connect(ui_->action_renumber_tracks, SIGNAL(triggered()), SLOT(RenumberTracks()));
connect(ui_->action_selection_set_value, SIGNAL(triggered()), SLOT(SelectionSetValue()));
connect(ui_->action_edit_value, SIGNAL(triggered()), SLOT(EditValue()));
@@ -446,6 +474,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->action_abort_collection_scan, SIGNAL(triggered()), app_->collection(), SLOT(AbortScan()));
#if defined(HAVE_GSTREAMER)
connect(ui_->action_add_files_to_transcoder, SIGNAL(triggered()), SLOT(AddFilesToTranscoder()));
ui_->action_add_files_to_transcoder->setIcon(IconLoader::Load("tools-wizard"));
#else
ui_->action_add_files_to_transcoder->setDisabled(true);
#endif
@@ -621,8 +650,11 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
playlist_menu_->addAction(ui_->action_edit_value);
playlist_menu_->addAction(ui_->action_renumber_tracks);
playlist_menu_->addAction(ui_->action_selection_set_value);
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
playlist_menu_->addAction(ui_->action_auto_complete_tags);
playlist_menu_->addAction(ui_->action_rescan_songs);
#endif
playlist_rescan_songs_ = playlist_menu_->addAction(IconLoader::Load("view-refresh"), tr("Rescan song(s)..."), this, SLOT(RescanSongs()));
playlist_menu_->addAction(playlist_rescan_songs_);
#ifdef HAVE_GSTREAMER
playlist_menu_->addAction(ui_->action_add_files_to_transcoder);
#endif
@@ -632,10 +664,11 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#endif
playlist_copy_to_collection_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Copy to collection..."), this, SLOT(PlaylistCopyToCollection()));
playlist_move_to_collection_ = playlist_menu_->addAction(IconLoader::Load("go-jump"), tr("Move to collection..."), this, SLOT(PlaylistMoveToCollection()));
playlist_organise_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(PlaylistMoveToCollection()));
playlist_organize_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organize files..."), this, SLOT(PlaylistMoveToCollection()));
playlist_open_in_browser_ = playlist_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, SLOT(PlaylistOpenInBrowser()));
playlist_open_in_browser_->setVisible(false);
playlist_show_in_collection_ = playlist_menu_->addAction(IconLoader::Load("edit-find"), tr("Show in collection..."), this, SLOT(ShowInCollection()));
playlist_copy_url_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Copy URL(s)..."), this, SLOT(PlaylistCopyUrl()));
playlist_menu_->addSeparator();
playlistitem_actions_separator_ = playlist_menu_->addSeparator();
playlist_menu_->addAction(ui_->action_clear_playlist);
@@ -681,8 +714,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
thumbbar_->SetActions(QList<QAction*>() << ui_->action_previous_track << ui_->action_play_pause << ui_->action_stop << ui_->action_next_track << nullptr << ui_->action_love);
#endif
#if (defined(Q_OS_MACOS) && defined(HAVE_SPARKLE))
// Add check for updates item to application menu.
#if defined(HAVE_SPARKLE) || defined(HAVE_QTSPARKLE)
QAction *check_updates = ui_->menu_tools->addAction(tr("Check for updates..."));
check_updates->setMenuRole(QAction::ApplicationSpecificRole);
connect(check_updates, SIGNAL(triggered(bool)), SLOT(CheckForUpdates()));
@@ -822,42 +854,48 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
// Reload playlist settings, for BG and glowing
ui_->playlist->view()->ReloadSettings();
#ifdef Q_OS_MACOS // Always show mainwindow on startup if on macos
#ifdef Q_OS_MACOS // Always show the mainwindow on startup for macOS
show();
#else
QSettings s;
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
BehaviourSettingsPage::StartupBehaviour behaviour = BehaviourSettingsPage::StartupBehaviour(s.value("startupbehaviour", BehaviourSettingsPage::Startup_Remember).toInt());
s.endGroup();
bool hidden = settings_.value("hidden", false).toBool();
if (hidden && (!QSystemTrayIcon::isSystemTrayAvailable() || !tray_icon_ || !tray_icon_->IsVisible())) {
hidden = false;
settings_.setValue("hidden", false);
show();
}
else {
switch (behaviour) {
case BehaviourSettingsPage::Startup_Remember:
was_maximized_ = settings_.value("maximized", true).toBool();
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
was_minimized_ = settings_.value("minimized", false).toBool();
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
setVisible(!hidden);
break;
case BehaviourSettingsPage::Startup_Show:
show();
break;
case BehaviourSettingsPage::Startup_Hide:
switch (behaviour) {
case BehaviourSettingsPage::Startup_Show:
show();
break;
case BehaviourSettingsPage::Startup_ShowMaximized:
setWindowState(windowState() | Qt::WindowMaximized);
show();
break;
case BehaviourSettingsPage::Startup_ShowMinimized:
setWindowState(windowState() | Qt::WindowMinimized);
show();
break;
case BehaviourSettingsPage::Startup_Hide:
if (QSystemTrayIcon::isSystemTrayAvailable() && tray_icon_ && tray_icon_->IsVisible()) {
hide();
break;
case BehaviourSettingsPage::Startup_ShowMaximized:
setWindowState(windowState() | Qt::WindowMaximized);
}
// fallthrough
case BehaviourSettingsPage::Startup_Remember:
default: {
was_maximized_ = settings_.value("maximized", true).toBool();
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
was_minimized_ = settings_.value("minimized", false).toBool();
if (was_minimized_) setWindowState(windowState() | Qt::WindowMinimized);
if (!QSystemTrayIcon::isSystemTrayAvailable() || !tray_icon_ || !tray_icon_->IsVisible()) {
settings_.setValue("hidden", false);
show();
break;
case BehaviourSettingsPage::Startup_ShowMinimized:
setWindowState(windowState() | Qt::WindowMinimized);
show();
break;
}
else {
setVisible(!settings_.value("hidden", false).toBool());
}
break;
}
}
#endif
@@ -881,6 +919,22 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
app_->scrobbler()->Submit();
}
#ifdef HAVE_QTSPARKLE
QUrl sparkle_url;
#if defined(Q_OS_MACOS)
sparkle_url.setUrl("https://www.strawberrymusicplayer.org/sparkle-macos");
#elif defined(Q_OS_WIN)
sparkle_url.setUrl("https://www.strawberrymusicplayer.org/sparkle-windows");
#endif
if (!sparkle_url.isEmpty()) {
qLog(Debug) << "Creating Qt Sparkle updater";
qtsparkle::Updater *updater = new qtsparkle::Updater(sparkle_url, this);
updater->SetNetworkAccessManager(new NetworkAccessManager(this));
updater->SetVersion(STRAWBERRY_VERSION_PACKAGE);
connect(check_updates, SIGNAL(triggered()), updater, SLOT(CheckNow()));
}
#endif
qLog(Debug) << "Started" << QThread::currentThread();
initialized_ = true;
@@ -936,6 +990,8 @@ void MainWindow::ReloadSettings() {
}
}
osd_->ReloadSettings();
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings_.value("search_for_cover_auto", true).toBool());
#ifdef HAVE_SUBSONIC
@@ -970,7 +1026,6 @@ void MainWindow::ReloadAllSettings() {
app_->ReloadSettings();
app_->collection()->ReloadSettings();
app_->player()->ReloadSettings();
osd_->ReloadSettings();
collection_view_->ReloadSettings();
ui_->playlist->view()->ReloadSettings();
app_->playlist_manager()->playlist_container()->ReloadSettings();
@@ -1052,7 +1107,7 @@ void MainWindow::ExitFinished() {
void MainWindow::EngineChanged(Engine::EngineType enginetype) {
ui_->action_equalizer->setEnabled(enginetype == Engine::EngineType::GStreamer || enginetype == Engine::EngineType::Xine);
ui_->action_equalizer->setEnabled(enginetype == Engine::EngineType::GStreamer);
#ifdef Q_OS_WIN
ui_->action_open_cd->setEnabled(false);
ui_->action_open_cd->setVisible(false);
@@ -1191,7 +1246,7 @@ void MainWindow::TrackSkipped(PlaylistItemPtr item) {
void MainWindow::TabSwitched() {
if (playing_widget_ && ui_->action_toggle_show_sidebar->isChecked() && (ui_->tabs->tabBar()->tabData(ui_->tabs->currentIndex()).toString().toLower() != "context" || !context_view_->album_enabled())) {
if (playing_widget_ && ui_->action_toggle_show_sidebar->isChecked() && (ui_->tabs->currentIndex() != ui_->tabs->IndexOfTab(context_view_) || !context_view_->album_enabled())) {
ui_->widget_playing->SetEnabled();
}
else {
@@ -1596,14 +1651,14 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
}
// Are we allowed to pause?
if (index.isValid()) {
if (source_index.isValid()) {
playlist_play_pause_->setEnabled(app_->playlist_manager()->current()->current_row() != source_index.row() || !(app_->playlist_manager()->current()->item_at(source_index.row())->options() & PlaylistItem::PauseDisabled));
}
else {
playlist_play_pause_->setEnabled(false);
}
playlist_stop_after_->setEnabled(index.isValid());
playlist_stop_after_->setEnabled(source_index.isValid());
// Are any of the selected songs editable or queued?
QModelIndexList selection = ui_->playlist->view()->selectionModel()->selectedRows();
@@ -1615,6 +1670,7 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
int in_skipped = 0;
int not_in_skipped = 0;
int local_songs = 0;
int collection_songs = 0;
for (const QModelIndex &idx : selection) {
@@ -1625,6 +1681,7 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
if (!item) continue;
if (item->Metadata().url().isLocalFile()) ++local_songs;
if (item->Metadata().source() == Song::Source_Collection) ++collection_songs;
if (item->Metadata().has_cue()) {
cue_selected = true;
@@ -1642,29 +1699,32 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
}
// this is available when we have one or many files and at least one of those is not CUE related
ui_->action_edit_track->setEnabled(editable);
ui_->action_edit_track->setVisible(editable);
ui_->action_edit_track->setEnabled(editable > 0);
ui_->action_edit_track->setVisible(editable > 0);
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
ui_->action_auto_complete_tags->setEnabled(editable);
ui_->action_auto_complete_tags->setVisible(editable);
#else
ui_->action_auto_complete_tags->setEnabled(false);
ui_->action_auto_complete_tags->setVisible(false);
ui_->action_auto_complete_tags->setEnabled(editable > 0);
ui_->action_auto_complete_tags->setVisible(editable > 0);
#endif
ui_->action_rescan_songs->setEnabled(editable);
ui_->action_rescan_songs->setVisible(editable);
playlist_rescan_songs_->setEnabled(editable > 0);
playlist_rescan_songs_->setVisible(editable > 0);
#ifdef HAVE_GSTREAMER
ui_->action_add_files_to_transcoder->setEnabled(editable);
ui_->action_add_files_to_transcoder->setVisible(editable);
#endif
// the rest of the read / write actions work only when there are no CUEs involved
if (cue_selected) editable = 0;
playlist_open_in_browser_->setVisible(local_songs == selected);
playlist_open_in_browser_->setVisible(selected > 0 && local_songs == selected);
bool track_column = (index.column() == Playlist::Column_Track);
ui_->action_renumber_tracks->setVisible(editable >= 2 && track_column);
ui_->action_selection_set_value->setVisible(editable >= 2 && !track_column);
ui_->action_edit_value->setVisible(editable);
ui_->action_remove_from_playlist->setEnabled(!selection.isEmpty());
ui_->action_edit_value->setVisible(editable > 0);
ui_->action_remove_from_playlist->setEnabled(selected > 0);
ui_->action_remove_from_playlist->setVisible(selected > 0);
playlist_show_in_collection_->setVisible(false);
playlist_copy_to_collection_->setVisible(false);
@@ -1672,7 +1732,9 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
#if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
playlist_copy_to_device_->setVisible(false);
#endif
playlist_organise_->setVisible(false);
playlist_organize_->setVisible(false);
playlist_copy_url_->setVisible(selected > 0);
if (selected < 1) {
playlist_queue_->setVisible(false);
@@ -1715,7 +1777,7 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
else {
Playlist::Column column = static_cast<Playlist::Column>(index.column());
bool column_is_editable = Playlist::column_is_editable(column) && editable;
bool column_is_editable = Playlist::column_is_editable(column) && editable > 0;
ui_->action_selection_set_value->setVisible(ui_->action_selection_set_value->isVisible() && column_is_editable);
ui_->action_edit_value->setVisible(ui_->action_edit_value->isVisible() && column_is_editable);
@@ -1730,17 +1792,17 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
// Is it a collection item?
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (item && item->IsLocalCollectionItem() && item->Metadata().id() != -1) {
playlist_organise_->setVisible(editable);
playlist_show_in_collection_->setVisible(editable);
playlist_organize_->setVisible(editable > 0);
playlist_show_in_collection_->setVisible(editable > 0);
playlist_open_in_browser_->setVisible(true);
}
else {
playlist_copy_to_collection_->setVisible(editable);
playlist_move_to_collection_->setVisible(editable);
playlist_copy_to_collection_->setVisible(editable > 0);
playlist_move_to_collection_->setVisible(editable > 0);
}
#if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
playlist_copy_to_device_->setVisible(editable);
playlist_copy_to_device_->setVisible(editable > 0);
#endif
// Remove old item actions, if any.
@@ -1758,32 +1820,36 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
if (playlist_add_to_another_ != nullptr) {
playlist_menu_->removeAction(playlist_add_to_another_);
delete playlist_add_to_another_;
playlist_add_to_another_ = nullptr;
}
// create the playlist submenu
QMenu *add_to_another_menu = new QMenu(tr("Add to another playlist"), this);
add_to_another_menu->setIcon(IconLoader::Load("list-add"));
// Create the playlist submenu if songs are selected.
if (selected > 0) {
QMenu *add_to_another_menu = new QMenu(tr("Add to another playlist"), this);
add_to_another_menu->setIcon(IconLoader::Load("list-add"));
for (const PlaylistBackend::Playlist &playlist : app_->playlist_backend()->GetAllOpenPlaylists()) {
// don't add the current playlist
if (playlist.id != app_->playlist_manager()->current()->id()) {
QAction *existing_playlist = new QAction(this);
existing_playlist->setText(playlist.name);
existing_playlist->setData(playlist.id);
add_to_another_menu->addAction(existing_playlist);
for (const PlaylistBackend::Playlist &playlist : app_->playlist_backend()->GetAllOpenPlaylists()) {
// don't add the current playlist
if (playlist.id != app_->playlist_manager()->current()->id()) {
QAction *existing_playlist = new QAction(this);
existing_playlist->setText(playlist.name);
existing_playlist->setData(playlist.id);
add_to_another_menu->addAction(existing_playlist);
}
}
add_to_another_menu->addSeparator();
// add to a new playlist
QAction *new_playlist = new QAction(this);
new_playlist->setText(tr("New playlist"));
new_playlist->setData(-1); // fake id
add_to_another_menu->addAction(new_playlist);
playlist_add_to_another_ = playlist_menu_->insertMenu(ui_->action_remove_from_playlist, add_to_another_menu);
connect(add_to_another_menu, SIGNAL(triggered(QAction*)), SLOT(AddToPlaylist(QAction*)));
}
add_to_another_menu->addSeparator();
// add to a new playlist
QAction *new_playlist = new QAction(this);
new_playlist->setText(tr("New playlist"));
new_playlist->setData(-1); // fake id
add_to_another_menu->addAction(new_playlist);
playlist_add_to_another_ = playlist_menu_->insertMenu(ui_->action_remove_from_playlist, add_to_another_menu);
connect(add_to_another_menu, SIGNAL(triggered(QAction*)), SLOT(AddToPlaylist(QAction*)));
playlist_menu_->popup(global_pos);
}
@@ -1811,8 +1877,13 @@ void MainWindow::RescanSongs() {
const QModelIndex source_index = app_->playlist_manager()->current()->proxy()->mapToSource(proxy_index);
if (!source_index.isValid()) continue;
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(source_index.row()));
if (!item || !item->IsLocalCollectionItem()) continue;
songs << item->Metadata();
if (!item) continue;
if (item->IsLocalCollectionItem()) {
songs << item->Metadata();
}
else if (item->Metadata().source() == Song::Source_LocalFile) {
item->Reload();
}
}
if (songs.isEmpty()) return;
@@ -1831,7 +1902,7 @@ void MainWindow::EditTracks() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(source_index.row()));
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (song.IsEditable()) {
songs << song;
items << item;
@@ -1868,7 +1939,7 @@ void MainWindow::RenumberTracks() {
// if first selected song has a track number set, start from that offset
if (!indexes.isEmpty()) {
const Song first_song = app_->playlist_manager()->current()->item_at(indexes[0].row())->Metadata();
const Song first_song = app_->playlist_manager()->current()->item_at(indexes[0].row())->OriginalMetadata();
if (first_song.track() > 0) track = first_song.track();
}
@@ -1877,7 +1948,7 @@ void MainWindow::RenumberTracks() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (song.IsEditable()) {
song.set_track(track);
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
@@ -1907,7 +1978,7 @@ void MainWindow::SelectionSetValue() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (!song.is_valid() || !song.url().isLocalFile()) continue;
if (Playlist::set_column_value(song, column, column_value)) {
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
@@ -2013,13 +2084,13 @@ void MainWindow::ShowInCollection() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (item && item->IsLocalCollectionItem()) {
songs << item->Metadata();
songs << item->OriginalMetadata();
break;
}
}
QString search;
if (!songs.isEmpty()) {
search ="artist:" + songs.first().artist() + " album:" + songs.first().album();
search = "artist:" + songs.first().artist() + " album:" + songs.first().album();
}
collection_view_->filter()->ShowInCollection(search);
@@ -2210,7 +2281,7 @@ void MainWindow::AddFilesToTranscoder() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(source_index.row()));
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (!song.is_valid() || !song.url().isLocalFile()) continue;
filenames << song.url().toLocalFile();
}
@@ -2252,29 +2323,29 @@ void MainWindow::PlayingWidgetPositionChanged(const bool above_status_bar) {
void MainWindow::CopyFilesToCollection(const QList<QUrl> &urls) {
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organise_dialog_->SetUrls(urls);
organise_dialog_->SetCopy(true);
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organize_dialog_->SetUrls(urls);
organize_dialog_->SetCopy(true);
organize_dialog_->show();
}
void MainWindow::MoveFilesToCollection(const QList<QUrl> &urls) {
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organise_dialog_->SetUrls(urls);
organise_dialog_->SetCopy(false);
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organize_dialog_->SetUrls(urls);
organize_dialog_->SetCopy(false);
organize_dialog_->show();
}
void MainWindow::CopyFilesToDevice(const QList<QUrl> &urls) {
#if defined(HAVE_GSTREAMER) && !defined(Q_OS_WIN)
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true);
if (organise_dialog_->SetUrls(urls))
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organize_dialog_->SetCopy(true);
if (organize_dialog_->SetUrls(urls))
organize_dialog_->show();
else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
}
@@ -2301,14 +2372,14 @@ void MainWindow::EditFileTags(const QList<QUrl> &urls) {
}
void MainWindow::PlaylistCopyToCollection() {
PlaylistOrganiseSelected(true);
PlaylistOrganizeSelected(true);
}
void MainWindow::PlaylistMoveToCollection() {
PlaylistOrganiseSelected(false);
PlaylistOrganizeSelected(false);
}
void MainWindow::PlaylistOrganiseSelected(const bool copy) {
void MainWindow::PlaylistOrganizeSelected(const bool copy) {
SongList songs;
for (const QModelIndex &proxy_index : ui_->playlist->view()->selectionModel()->selectedRows()) {
@@ -2316,16 +2387,16 @@ void MainWindow::PlaylistOrganiseSelected(const bool copy) {
if (!source_index.isValid()) continue;
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (!song.is_valid() || !song.url().isLocalFile()) continue;
songs << song;
}
if (songs.isEmpty()) return;
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organise_dialog_->SetSongs(songs);
organise_dialog_->SetCopy(copy);
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organize_dialog_->SetSongs(songs);
organize_dialog_->SetCopy(copy);
organize_dialog_->show();
}
@@ -2342,6 +2413,25 @@ void MainWindow::PlaylistOpenInBrowser() {
}
void MainWindow::PlaylistCopyUrl() {
QList<QUrl> urls;
for (const QModelIndex &proxy_index : ui_->playlist->view()->selectionModel()->selectedRows()) {
const QModelIndex source_index = app_->playlist_manager()->current()->proxy()->mapToSource(proxy_index);
if (!source_index.isValid()) continue;
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (!item) continue;
urls << item->StreamUrl();
}
if (urls.count() > 0) {
QMimeData *mime_data = new QMimeData;
mime_data->setUrls(urls);
QApplication::clipboard()->setMimeData(mime_data);
}
}
void MainWindow::PlaylistQueue() {
QModelIndexList indexes;
@@ -2387,16 +2477,16 @@ void MainWindow::PlaylistCopyToDevice() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item = app_->playlist_manager()->current()->item_at(source_index.row());
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (!song.is_valid() || !song.url().isLocalFile()) continue;
songs << song;
}
if (songs.isEmpty()) return;
organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organise_dialog_->SetCopy(true);
if (organise_dialog_->SetSongs(songs))
organise_dialog_->show();
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organize_dialog_->SetCopy(true);
if (organize_dialog_->SetSongs(songs))
organize_dialog_->show();
else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
}
@@ -2427,7 +2517,7 @@ void MainWindow::ShowCoverManager() {
SettingsDialog *MainWindow::CreateSettingsDialog() {
SettingsDialog *settings_dialog = new SettingsDialog(app_, this);
SettingsDialog *settings_dialog = new SettingsDialog(app_, osd_, this);
#ifdef HAVE_GLOBALSHORTCUTS
settings_dialog->SetGlobalShortcutManager(global_shortcuts_);
#endif
@@ -2436,7 +2526,7 @@ SettingsDialog *MainWindow::CreateSettingsDialog() {
connect(settings_dialog, SIGNAL(ReloadSettings()), SLOT(ReloadAllSettings()));
// Allows custom notification preview
connect(settings_dialog, SIGNAL(NotificationPreview(OSD::Behaviour, QString, QString)), SLOT(HandleNotificationPreview(OSD::Behaviour, QString, QString)));
connect(settings_dialog, SIGNAL(NotificationPreview(OSDBase::Behaviour, QString, QString)), SLOT(HandleNotificationPreview(OSDBase::Behaviour, QString, QString)));
return settings_dialog;
}
@@ -2531,7 +2621,11 @@ void MainWindow::Raise() {
activateWindow();
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) {
#else
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) {
#endif
Q_UNUSED(eventType);
Q_UNUSED(result);
@@ -2573,7 +2667,7 @@ void MainWindow::AutoCompleteTags() {
if (!source_index.isValid()) continue;
PlaylistItemPtr item(app_->playlist_manager()->current()->item_at(source_index.row()));
if (!item) continue;
Song song = item->Metadata();
Song song = item->OriginalMetadata();
if (song.IsEditable()) {
songs << song;
autocomplete_tag_items_ << item;
@@ -2602,7 +2696,7 @@ void MainWindow::AutoCompleteTagsAccepted() {
}
void MainWindow::HandleNotificationPreview(OSD::Behaviour type, QString line1, QString line2) {
void MainWindow::HandleNotificationPreview(OSDBase::Behaviour type, QString line1, QString line2) {
if (!app_->playlist_manager()->current()->GetAllSongs().isEmpty()) {
// Show a preview notification for the first song in the current playlist

View File

@@ -54,7 +54,7 @@
#include "engine/enginetype.h"
#include "engine/engine_fwd.h"
#include "mac_startup.h"
#include "widgets/osd.h"
#include "osd/osdbase.h"
#include "collection/collectionmodel.h"
#include "playlist/playlistitem.h"
#include "settings/settingsdialog.h"
@@ -77,7 +77,7 @@ class ErrorDialog;
class FileView;
class GlobalShortcuts;
class MimeData;
class OrganiseDialog;
class OrganizeDialog;
class PlaylistListContainer;
class QueueView;
class SystemTrayIcon;
@@ -100,7 +100,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
Q_OBJECT
public:
explicit MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, const CommandlineOptions& options, QWidget *parent = nullptr);
explicit MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd, const CommandlineOptions& options, QWidget *parent = nullptr);
~MainWindow() override;
static const char *kSettingsGroup;
@@ -112,7 +112,11 @@ class MainWindow : public QMainWindow, public PlatformInterface {
protected:
void keyPressEvent(QKeyEvent *event) override;
void closeEvent(QCloseEvent *event) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
#else
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif
// PlatformInterface
void Activate() override;
@@ -164,8 +168,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void PlaylistCopyToCollection();
void PlaylistMoveToCollection();
void PlaylistCopyToDevice();
void PlaylistOrganiseSelected(const bool copy);
void PlaylistOrganizeSelected(const bool copy);
void PlaylistOpenInBrowser();
void PlaylistCopyUrl();
void ShowInCollection();
void ChangeCollectionQueryMode(QAction *action);
@@ -238,7 +243,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void Exit();
void DoExit();
void HandleNotificationPreview(const OSD::Behaviour type, QString line1, QString line2);
void HandleNotificationPreview(const OSDBase::Behaviour type, QString line1, QString line2);
void ShowConsole();
@@ -283,7 +288,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
Application *app_;
SystemTrayIcon *tray_icon_;
OSD *osd_;
OSDBase *osd_;
Lazy<About> about_dialog_;
Lazy<EditTagDialog> edit_tag_dialog_;
AlbumCoverChoiceController *album_cover_choice_controller_;
@@ -303,7 +308,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
Lazy<SettingsDialog> settings_dialog_;
Lazy<AlbumCoverManager> cover_manager_;
std::unique_ptr<Equalizer> equalizer_;
Lazy<OrganiseDialog> organise_dialog_;
Lazy<OrganizeDialog> organize_dialog_;
#ifdef HAVE_GSTREAMER
Lazy<TranscodeDialog> transcode_dialog_;
#endif
@@ -326,7 +331,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
QAction *playlist_play_pause_;
QAction *playlist_stop_after_;
QAction *playlist_undoredo_;
QAction *playlist_organise_;
QAction *playlist_organize_;
QAction *playlist_show_in_collection_;
QAction *playlist_copy_to_collection_;
QAction *playlist_move_to_collection_;
@@ -334,14 +339,14 @@ class MainWindow : public QMainWindow, public PlatformInterface {
QAction *playlist_copy_to_device_;
#endif
QAction *playlist_open_in_browser_;
QAction *playlist_copy_url_;
QAction *playlist_queue_;
QAction* playlist_queue_play_next_;
QAction *playlist_skip_;
QAction *playlist_add_to_another_;
QList<QAction*> playlistitem_actions_;
QAction *playlistitem_actions_separator_;
QAction *search_for_artist_;
QAction *search_for_album_;
QAction *playlist_rescan_songs_;
QModelIndex playlist_menu_index_;

View File

@@ -831,11 +831,6 @@
<string notr="true">Ctrl+Shift+T</string>
</property>
</action>
<action name="action_rescan_songs">
<property name="text">
<string>Rescan songs(s)</string>
</property>
</action>
<action name="action_add_stream">
<property name="text">
<string>Add stream...</string>

View File

@@ -193,7 +193,7 @@ void MergedProxyModel::SourceModelReset() {
void MergedProxyModel::SubModelReset() {
QAbstractItemModel *submodel = static_cast<QAbstractItemModel*>(sender());
QAbstractItemModel *submodel = qobject_cast<QAbstractItemModel*>(sender());
// TODO: When we require Qt 4.6, use beginResetModel() and endResetModel() in CollectionModel and catch those here
// that will let us do away with this std::numeric_limits<int>::max() hack.
@@ -241,7 +241,7 @@ QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex &source_pa
}
void MergedProxyModel::RowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end) {
beginInsertRows(mapFromSource(GetActualSourceParent(source_parent, static_cast<QAbstractItemModel*>(sender()))), start, end);
beginInsertRows(mapFromSource(GetActualSourceParent(source_parent, qobject_cast<QAbstractItemModel*>(sender()))), start, end);
}
void MergedProxyModel::RowsInserted(const QModelIndex&, int, int) {
@@ -249,7 +249,7 @@ void MergedProxyModel::RowsInserted(const QModelIndex&, int, int) {
}
void MergedProxyModel::RowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end) {
beginRemoveRows(mapFromSource(GetActualSourceParent(source_parent, static_cast<QAbstractItemModel*>(sender()))), start, end);
beginRemoveRows(mapFromSource(GetActualSourceParent(source_parent, qobject_cast<QAbstractItemModel*>(sender()))), start, end);
}
void MergedProxyModel::RowsRemoved(const QModelIndex&, int, int) {

View File

@@ -32,12 +32,14 @@
#include <QMetaType>
#include <QFileInfo>
#include <QList>
#include <QVector>
#include <QMap>
#include <QByteArray>
#include <QUrl>
#include <QImage>
#include <QNetworkCookie>
#include <QNetworkReply>
#include <QItemSelection>
#ifdef HAVE_DBUS
# include <QDBusMetaType>
#endif
@@ -55,6 +57,7 @@
#include "playlist/playlistsequence.h"
#include "covermanager/albumcoverloaderresult.h"
#include "covermanager/albumcoverfetcher.h"
#include "covermanager/coversearchstatistics.h"
#include "equalizer/equalizer.h"
#ifdef HAVE_DBUS
@@ -69,6 +72,7 @@ void RegisterMetaTypes() {
qRegisterMetaType<const char*>("const char*");
qRegisterMetaType<QList<int>>("QList<int>");
qRegisterMetaType<QList<QUrl>>("QList<QUrl>");
qRegisterMetaType<QVector<int>>("QVector<int>");
qRegisterMetaType<QFileInfo>("QFileInfo");
qRegisterMetaType<QAbstractSocket::SocketState>();
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
@@ -76,7 +80,9 @@ void RegisterMetaTypes() {
qRegisterMetaType<QList<QNetworkCookie> >("QList<QNetworkCookie>");
qRegisterMetaType<QNetworkReply*>("QNetworkReply*");
qRegisterMetaType<QNetworkReply**>("QNetworkReply**");
qRegisterMetaTypeStreamOperators<QMap<int, int> >("ColumnAlignmentMap");
qRegisterMetaType<QItemSelection>("QItemSelection");
qRegisterMetaTypeStreamOperators<QMap<int, Qt::Alignment>>("ColumnAlignmentMap");
qRegisterMetaTypeStreamOperators<QMap<int, int>>("ColumnAlignmentIntMap");
qRegisterMetaType<Directory>("Directory");
qRegisterMetaType<DirectoryList>("DirectoryList");
qRegisterMetaType<Subdirectory>("Subdirectory");
@@ -102,6 +108,7 @@ void RegisterMetaTypes() {
qRegisterMetaType<AlbumCoverLoaderResult>("AlbumCoverLoaderResult");
qRegisterMetaType<AlbumCoverLoaderResult::Type>("AlbumCoverLoaderResult::Type");
qRegisterMetaType<CoverSearchResult>("CoverSearchResult");
qRegisterMetaType<CoverSearchStatistics>("CoverSearchStatistics");
qRegisterMetaType<QList<CoverSearchResult> >("QList<CoverSearchResult>");
qRegisterMetaType<CoverSearchResults>("CoverSearchResults");
qRegisterMetaType<Equalizer::Params>("Equalizer::Params");

View File

@@ -134,10 +134,9 @@ Mpris2::Mpris2(Application *app, QObject *parent)
app_name_[0] = app_name_[0].toUpper();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0))
if (!QGuiApplication::desktopFileName().isEmpty())
desktop_files_ << QGuiApplication::desktopFileName();
#endif
QStringList domain_split = QCoreApplication::organizationDomain().split(".");
std::reverse(domain_split.begin(), domain_split.end());
desktop_files_ << QStringList() << domain_split.join(".") + "." + QCoreApplication::applicationName().toLower();

View File

@@ -56,7 +56,7 @@ inline void AddMetadata(const QString &key, const QDateTime &metadata, QVariantM
}
inline QString AsMPRISDateTimeType(const int time) {
return time != -1 ? QDateTime::fromTime_t(time).toString(Qt::ISODate) : "";
return time != -1 ? QDateTime::fromSecsSinceEpoch(time).toString(Qt::ISODate) : "";
}
} // namespace mpris

View File

@@ -127,7 +127,11 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
}
QNetworkRequest new_request(request);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
new_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
#else
new_request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
#endif
new_request.setRawHeader("User-Agent", user_agent);
if (op == QNetworkAccessManager::PostOperation && !new_request.header(QNetworkRequest::ContentTypeHeader).isValid()) {

View File

@@ -51,9 +51,6 @@
# include "engine/gstengine.h"
# include "engine/gststartup.h"
#endif
#ifdef HAVE_XINE
# include "engine/xineengine.h"
#endif
#ifdef HAVE_VLC
# include "engine/vlcengine.h"
#endif
@@ -125,12 +122,6 @@ Engine::EngineType Player::CreateEngine(Engine::EngineType enginetype) {
break;
}
#endif
#ifdef HAVE_XINE
case Engine::Xine:
use_enginetype=Engine::Xine;
engine_.reset(new XineEngine(app_->task_manager()));
break;
#endif
#ifdef HAVE_VLC
case Engine::VLC:
use_enginetype=Engine::VLC;
@@ -267,8 +258,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
switch (result.type_) {
case UrlHandler::LoadResult::Error:
if (is_current) {
EngineStateChanged(Engine::Error);
FatalError();
InvalidSongRequested(result.original_url_);
}
emit Error(result.error_);
break;

View File

@@ -37,7 +37,7 @@
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QRegularExpression>
#include <QUrl>
#include <QImage>
#include <QIcon>
@@ -148,9 +148,9 @@ const QString Song::kFtsUpdateSpec = Utilities::Updateify(Song::kFtsColumns).joi
const QString Song::kManuallyUnsetCover = "(unset)";
const QString Song::kEmbeddedCover = "(embedded)";
const QRegExp Song::kAlbumRemoveDisc(" ?-? ((\\(|\\[)?)(Disc|CD) ?([0-9]{1,2})((\\)|\\])?)$");
const QRegExp Song::kAlbumRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
const QRegExp Song::kTitleRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|Live|Remastered Version|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
const QRegularExpression Song::kAlbumRemoveDisc(" ?-? ((\\(|\\[)?)(Disc|CD) ?([0-9]{1,2})((\\)|\\])?)$");
const QRegularExpression Song::kAlbumRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
const QRegularExpression Song::kTitleRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|Live|Remastered Version|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
const QString Song::kVariousArtists("various artists");
const QStringList Song::kArticles = QStringList() << "the " << "a " << "an ";
@@ -199,8 +199,8 @@ struct Song::Private : public QSharedData {
QUrl url_;
FileType filetype_;
int filesize_;
int mtime_;
int ctime_;
qint64 mtime_;
qint64 ctime_;
bool unavailable_;
int playcount_;
@@ -322,8 +322,8 @@ const QUrl &Song::url() const { return d->url_; }
const QString &Song::basefilename() const { return d->basefilename_; }
Song::FileType Song::filetype() const { return d->filetype_; }
int Song::filesize() const { return d->filesize_; }
uint Song::mtime() const { return d->mtime_; }
uint Song::ctime() const { return d->ctime_; }
qint64 Song::mtime() const { return d->mtime_; }
qint64 Song::ctime() const { return d->ctime_; }
int Song::playcount() const { return d->playcount_; }
int Song::skipcount() const { return d->skipcount_; }
@@ -428,8 +428,8 @@ void Song::set_url(const QUrl &v) { d->url_ = v; }
void Song::set_basefilename(const QString &v) { d->basefilename_ = v; }
void Song::set_filetype(FileType v) { d->filetype_ = v; }
void Song::set_filesize(int v) { d->filesize_ = v; }
void Song::set_mtime(int v) { d->mtime_ = v; }
void Song::set_ctime(int v) { d->ctime_ = v; }
void Song::set_mtime(qint64 v) { d->mtime_ = v; }
void Song::set_ctime(qint64 v) { d->ctime_ = v; }
void Song::set_unavailable(bool v) { d->unavailable_ = v; }
void Song::set_playcount(int v) { d->playcount_ = v; }
@@ -925,10 +925,10 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
d->filesize_ = toint(x);
}
else if (Song::kColumns.value(i) == "mtime") {
d->mtime_ = toint(x);
d->mtime_ = tolonglong(x);
}
else if (Song::kColumns.value(i) == "ctime") {
d->ctime_ = toint(x);
d->ctime_ = tolonglong(x);
}
else if (Song::kColumns.value(i) == "unavailable") {
d->unavailable_ = q.value(x).toBool();
@@ -958,7 +958,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
else if (Song::kColumns.value(i) == "art_automatic") {
QString art_automatic = tostr(x);
if (art_automatic.contains(QRegExp("..+:.*"))) {
if (art_automatic.contains(QRegularExpression("..+:.*"))) {
set_art_automatic(QUrl::fromEncoded(art_automatic.toUtf8()));
}
else {
@@ -967,7 +967,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
}
else if (Song::kColumns.value(i) == "art_manual") {
QString art_manual = tostr(x);
if (art_manual.contains(QRegExp("..+:.*"))) {
if (art_manual.contains(QRegularExpression("..+:.*"))) {
set_art_manual(QUrl::fromEncoded(art_manual.toUtf8()));
}
else {

View File

@@ -33,7 +33,7 @@
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QRegularExpression>
#include <QUrl>
#include <QImage>
#include <QIcon>
@@ -120,10 +120,9 @@ class Song {
static const QString kManuallyUnsetCover;
static const QString kEmbeddedCover;
static const QRegExp kAlbumRemoveDisc;
static const QRegExp kAlbumRemoveMisc;
static const QRegExp kTitleRemoveMisc;
static const QRegExp kFilenameRemoveNonFatChars;
static const QRegularExpression kAlbumRemoveDisc;
static const QRegularExpression kAlbumRemoveMisc;
static const QRegularExpression kTitleRemoveMisc;
static const QString kVariousArtists;
@@ -228,8 +227,8 @@ class Song {
const QString &basefilename() const;
FileType filetype() const;
int filesize() const;
uint mtime() const;
uint ctime() const;
qint64 mtime() const;
qint64 ctime() const;
int playcount() const;
int skipcount() const;
@@ -329,8 +328,8 @@ class Song {
void set_basefilename(const QString &v);
void set_filetype(FileType v);
void set_filesize(int v);
void set_mtime(int v);
void set_ctime(int v);
void set_mtime(qint64 v);
void set_ctime(qint64 v);
void set_unavailable(bool v);
void set_playcount(int v);

View File

@@ -90,9 +90,8 @@ void StyleSheetLoader::UpdateStyleSheet(QWidget *widget, StyleSheetData styledat
.arg(alt.alpha()));
ReplaceColor(&stylesheet, "Window", p, QPalette::Window);
ReplaceColor(&stylesheet, "Background", p, QPalette::Background);
ReplaceColor(&stylesheet, "Background", p, QPalette::Window);
ReplaceColor(&stylesheet, "WindowText", p, QPalette::WindowText);
ReplaceColor(&stylesheet, "Foreground", p, QPalette::Foreground);
ReplaceColor(&stylesheet, "Base", p, QPalette::Base);
ReplaceColor(&stylesheet, "AlternateBase", p, QPalette::AlternateBase);
ReplaceColor(&stylesheet, "ToolTipBase", p, QPalette::ToolTipBase);

View File

@@ -50,7 +50,8 @@
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QRegExp>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QTcpServer>
#include <QTemporaryFile>
#include <QPoint>
@@ -157,7 +158,7 @@ QString WordyTimeNanosec(qint64 nanoseconds) {
QString Ago(int seconds_since_epoch, const QLocale &locale) {
const QDateTime now = QDateTime::currentDateTime();
const QDateTime then = QDateTime::fromTime_t(seconds_since_epoch);
const QDateTime then = QDateTime::fromSecsSinceEpoch(seconds_since_epoch);
const int days_ago = then.date().daysTo(now.date());
const QString time = then.time().toString(locale.timeFormat(QLocale::ShortFormat));
@@ -336,8 +337,10 @@ QString ColorToRgba(const QColor &c) {
}
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
void OpenInFileManager(const QString &path);
void OpenInFileManager(const QString &path) {
void OpenInFileManager(const QString path, const QUrl &url);
void OpenInFileManager(const QString path, const QUrl &url) {
if (!url.isLocalFile()) return;
QProcess proc;
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
@@ -375,29 +378,22 @@ void OpenInFileManager(const QString &path) {
}
if (command.isEmpty() || command == "exo-open") {
QFileInfo info(path);
if (!info.exists()) return;
QString directory = info.dir().path();
if (directory.isEmpty()) return;
QDesktopServices::openUrl(QUrl::fromLocalFile(directory));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
else if (command.startsWith("nautilus")) {
proc.startDetached(command, QStringList() << command_params << "--select" << path);
proc.startDetached(command, QStringList() << command_params << "--select" << url.toLocalFile());
}
else if (command.startsWith("dolphin") || command.startsWith("konqueror") || command.startsWith("kfmclient")) {
proc.startDetached(command, QStringList() << command_params << "--select" << "--new-window" << path);
proc.startDetached(command, QStringList() << command_params << "--select" << "--new-window" << url.toLocalFile());
}
else if (command.startsWith("caja")) {
QFileInfo info(path);
if (!info.exists()) return;
QString directory = info.dir().path();
proc.startDetached(command, QStringList() << command_params << "--no-desktop" << directory);
proc.startDetached(command, QStringList() << command_params << "--no-desktop" << path);
}
else if (command.startsWith("pcmanfm")) {
proc.startDetached(command, QStringList() << command_params << QFileInfo(path).dir().path());
proc.startDetached(command, QStringList() << command_params << path);
}
else {
proc.startDetached(command, QStringList() << command_params << path);
proc.startDetached(command, QStringList() << command_params << url.toLocalFile());
}
}
@@ -405,28 +401,42 @@ void OpenInFileManager(const QString &path) {
#ifdef Q_OS_MACOS
// Better than openUrl(dirname(path)) - also highlights file at path
void RevealFileInFinder(QString const &path) {
void RevealFileInFinder(const QString &path) {
QProcess::execute("/usr/bin/open", QStringList() << "-R" << path);
}
#endif // Q_OS_MACOS
#ifdef Q_OS_WIN
void ShowFileInExplorer(QString const &path);
void ShowFileInExplorer(QString const &path) {
void ShowFileInExplorer(const QString &path);
void ShowFileInExplorer(const QString &path) {
QProcess::execute("explorer.exe", QStringList() << "/select," << QDir::toNativeSeparators(path));
}
#endif
void OpenInFileBrowser(const QList<QUrl> &urls) {
if (urls.count() > 50) {
QMap<QString, QUrl> dirs;
for (const QUrl &url : urls) {
if (!url.isLocalFile()) {
continue;
}
QString path = url.toLocalFile();
if (!QFile::exists(path)) continue;
const QString directory = QFileInfo(path).dir().path();
if (dirs.contains(directory)) continue;
dirs.insert(directory, url);
}
if (dirs.count() > 50) {
QMessageBox messagebox(QMessageBox::Critical, tr("Show in file browser"), tr("Too many songs selected."));
messagebox.exec();
return;
}
if (urls.count() > 5) {
QMessageBox messagebox(QMessageBox::Information, tr("Show in file browser"), tr("%1 songs selected, are you sure you want to open them all?").arg(urls.count()), QMessageBox::Open|QMessageBox::Cancel);
if (dirs.count() > 5) {
QMessageBox messagebox(QMessageBox::Information, tr("Show in file browser"), tr("%1 songs in %2 different directories selected, are you sure you want to open them all?").arg(urls.count()).arg(dirs.count()), QMessageBox::Open|QMessageBox::Cancel);
messagebox.setTextFormat(Qt::RichText);
int result = messagebox.exec();
switch (result) {
@@ -438,27 +448,15 @@ void OpenInFileBrowser(const QList<QUrl> &urls) {
}
}
QSet<QString> dirs;
for (const QUrl &url : urls) {
if (!url.isLocalFile()) {
continue;
}
QString path = url.toLocalFile();
if (!QFile::exists(path)) continue;
const QString directory = QFileInfo(path).dir().path();
if (dirs.contains(directory)) continue;
dirs.insert(directory);
QMap<QString, QUrl>::iterator i;
for (i = dirs.begin(); i != dirs.end(); ++i) {
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
OpenInFileManager(path);
OpenInFileManager(i.key(), i.value());
#elif defined(Q_OS_MACOS)
// Revealing multiple files in the finder only opens one window, so it also makes sense to reveal at most one per directory
RevealFileInFinder(path);
RevealFileInFinder(i.value().toLocalFile());
#elif defined(Q_OS_WIN32)
ShowFileInExplorer(path);
ShowFileInExplorer(i.value().toLocalFile());
#endif
}
@@ -533,16 +531,6 @@ QString PrettySize(const QSize &size) {
return QString::number(size.width()) + "x" + QString::number(size.height());
}
void ForwardMouseEvent(const QMouseEvent *e, QWidget *target) {
QMouseEvent c(e->type(), target->mapFromGlobal(e->globalPos()), e->globalPos(), e->button(), e->buttons(), e->modifiers());
QApplication::sendEvent(target, &c);
}
bool IsMouseEventInWidget(const QMouseEvent *e, const QWidget *widget) {
return widget->rect().contains(widget->mapFromGlobal(e->globalPos()));
}
quint16 PickUnusedPort() {
forever {
@@ -613,8 +601,9 @@ bool ParseUntilElementCI(QXmlStreamReader *reader, const QString &name) {
QDateTime ParseRFC822DateTime(const QString &text) {
QRegExp regexp("(\\d{1,2}) (\\w{3,12}) (\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
if (regexp.indexIn(text) == -1) {
QRegularExpression regexp("(\\d{1,2}) (\\w{3,12}) (\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
QRegularExpressionMatch re_match = regexp.match(text);
if (!re_match.hasMatch()) {
return QDateTime();
}
@@ -646,9 +635,9 @@ QDateTime ParseRFC822DateTime(const QString &text) {
monthmap["November"] = 11;
monthmap["December"] = 12;
const QDate date(regexp.cap(static_cast<int>(MatchNames::YEARS)).toInt(), monthmap[regexp.cap(static_cast<int>(MatchNames::MONTHS))], regexp.cap(static_cast<int>(MatchNames::DAYS)).toInt());
const QDate date(re_match.captured(static_cast<int>(MatchNames::YEARS)).toInt(), monthmap[re_match.captured(static_cast<int>(MatchNames::MONTHS))], re_match.captured(static_cast<int>(MatchNames::DAYS)).toInt());
const QTime time(regexp.cap(static_cast<int>(MatchNames::HOURS)).toInt(), regexp.cap(static_cast<int>(MatchNames::MINUTES)).toInt(), regexp.cap(static_cast<int>(MatchNames::SECONDS)).toInt());
const QTime time(re_match.captured(static_cast<int>(MatchNames::HOURS)).toInt(), re_match.captured(static_cast<int>(MatchNames::MINUTES)).toInt(), re_match.captured(static_cast<int>(MatchNames::SECONDS)).toInt());
return QDateTime(date, time);
@@ -932,19 +921,20 @@ QString MacAddress() {
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline) {
QRegExp variable_replacer("[%][a-z]+[%]");
QRegularExpression variable_replacer("[%][a-z]+[%]");
QString copy(message);
// Replace the first line
int pos = 0;
variable_replacer.indexIn(message);
while ((pos = variable_replacer.indexIn(message, pos)) != -1) {
QStringList captured = variable_replacer.capturedTexts();
QRegularExpressionMatch match;
for (match = variable_replacer.match(message, pos) ; match.hasMatch() ; match = variable_replacer.match(message, pos)) {
pos = match.capturedStart();
QStringList captured = match.capturedTexts();
copy.replace(captured[0], ReplaceVariable(captured[0], song, newline));
pos += variable_replacer.matchedLength();
pos += match.capturedLength();
}
int index_of = copy.indexOf(QRegExp(" - (>|$)"));
int index_of = copy.indexOf(QRegularExpression(" - (>|$)"));
if (index_of >= 0) copy = copy.remove(index_of, 3);
return copy;

View File

@@ -85,12 +85,6 @@ QByteArray Sha1CoverHash(const QString &artist, const QString &album);
// Picks an unused ephemeral port number. Doesn't hold the port open so there's the obvious race condition
quint16 PickUnusedPort();
// Forwards a mouse event to a different widget, remapping the event's widget coordinates relative to those of the target widget.
void ForwardMouseEvent(const QMouseEvent *e, QWidget *target);
// Checks if the mouse event was inside the widget's rectangle.
bool IsMouseEventInWidget(const QMouseEvent *e, const QWidget *widget);
// Reads all children of the current element,
// and returns with the stream reader either on the EndElement for the current element, or the end of the file - whichever came first.
void ConsumeCurrentElement(QXmlStreamReader *reader);

View File

@@ -35,7 +35,7 @@
#include <QList>
#include <QVariant>
#include <QString>
#include <QRegExp>
#include <QRegularExpression>
#include <QUrl>
#include <QImage>
#include <QImageWriter>
@@ -54,7 +54,7 @@
#include "collection/collectionbackend.h"
#include "settings/collectionsettingspage.h"
#include "organise/organiseformat.h"
#include "organize/organizeformat.h"
#include "internet/internetservices.h"
#include "internet/internetservice.h"
#include "albumcoverchoicecontroller.h"
@@ -163,8 +163,8 @@ void AlbumCoverChoiceController::SaveCoverToFileManual(const Song &song, const Q
}
initial_file_name = initial_file_name + "-" + (song.effective_album().isEmpty() ? tr("unknown") : song.effective_album()) + ".jpg";
initial_file_name = initial_file_name.toLower();
initial_file_name.replace(QRegExp("\\s"), "-");
initial_file_name.remove(OrganiseFormat::kInvalidFatCharacters);
initial_file_name.replace(QRegularExpression("\\s"), "-");
initial_file_name.remove(OrganizeFormat::kInvalidFatCharacters);
QString save_filename = QFileDialog::getSaveFileName(this, tr("Save album cover"), GetInitialPathForFileDialog(song, initial_file_name), tr(kSaveImageFileFilter) + ";;" + tr(kAllFilesFilter));

View File

@@ -61,7 +61,7 @@ struct CoverSearchRequest {
// This structure represents a single result of some album's cover search request.
struct CoverSearchResult {
explicit CoverSearchResult() : score(0.0) {}
explicit CoverSearchResult() : score_provider(0.0), score_match(0.0), score_quality(0.0), number(0) {}
// Used for grouping in the user interface.
QString provider;
@@ -73,8 +73,23 @@ struct CoverSearchResult {
// An URL of a cover image
QUrl image_url;
// Image size
QSize image_size;
// Score for this provider
float score_provider;
// Score for match
float score_match;
// Score for image quality
float score_quality;
// The result number
int number;
// Total score for this result
float score;
float score() const { return score_provider + score_match + score_quality; }
};
Q_DECLARE_METATYPE(CoverSearchResult)

View File

@@ -98,8 +98,8 @@ void AlbumCoverFetcherSearch::Start(CoverProviders *cover_providers) {
continue;
}
// If album is missing, check if we can still use this provider by searching using artist + title.
if (!provider->allow_missing_album() && request_.album.isEmpty()) {
// If artist and album is missing, check if we can still use this provider by searching using title.
if (!provider->allow_missing_album() && request_.album.isEmpty() && !request_.title.isEmpty()) {
continue;
}
@@ -133,17 +133,81 @@ void AlbumCoverFetcherSearch::ProviderSearchResults(CoverProvider *provider, con
CoverSearchResults results_copy(results);
for (int i = 0 ; i < results_copy.count() ; ++i) {
results_copy[i].provider = provider->name();
results_copy[i].score = provider->quality();
if (results_copy[i].artist.toLower() == request_.artist.toLower()) {
results_copy[i].score += 0.5;
results_copy[i].score_provider = provider->quality();
QString request_artist = request_.artist.toLower();
QString request_album = request_.album.toLower();
QString result_artist = results_copy[i].artist.toLower();
QString result_album = results_copy[i].album.toLower();
if (result_artist == request_artist) {
results_copy[i].score_match += 0.5;
}
if (results_copy[i].album.toLower() == request_.album.toLower()) {
results_copy[i].score += 0.5;
if (result_album == request_album) {
results_copy[i].score_match += 0.5;
}
if (results_copy[i].artist.toLower() != request_.artist.toLower() && results_copy[i].album.toLower() != request_.album.toLower()) {
results_copy[i].score -= 1.5;
if (result_artist != request_artist && result_album != request_album) {
results_copy[i].score_match -= 1.5;
}
if (request_album.isEmpty() && result_artist != request_artist) {
results_copy[i].score_match -= 1;
}
// Decrease score if the search was based on artist and song title, and the resulting album is a compilation or live album.
// This is done since we can't match the album titles, and we want to prevent compilation or live albums from being picked before studio albums for streams.
// TODO: Make these regular expressions.
if (request_album.isEmpty() && (
result_album.contains("hits") ||
result_album.contains("greatest") ||
result_album.contains("best") ||
result_album.contains("collection") ||
result_album.contains("classics") ||
result_album.contains("singles") ||
result_album.contains("bootleg") ||
result_album.contains("live") ||
result_album.contains("concert") ||
result_album.contains("essential") ||
result_album.contains("ultimate") ||
result_album.contains("karaoke") ||
result_album.contains("mixtape") ||
result_album.contains("country rock") ||
result_album.contains("indie folk") ||
result_album.contains("soft rock") ||
result_album.contains("folk music") ||
result_album.contains("60's rock") ||
result_album.contains("60's romance") ||
result_album.contains("60s music") ||
result_album.contains("late 60s") ||
result_album.contains("the 60s") ||
result_album.contains("folk and blues") ||
result_album.contains("60 from the 60's") ||
result_album.contains("classic psychedelic") ||
result_album.contains("playlist: acoustic") ||
result_album.contains("90's rnb playlist") ||
result_album.contains("rock 80s") ||
result_album.contains("classic 80s") ||
result_album.contains("rock anthems") ||
result_album.contains("rock songs") ||
result_album.contains("rock 2019") ||
result_album.contains("guitar anthems") ||
result_album.contains("driving anthems") ||
result_album.contains("traffic jam jams") ||
result_album.contains("perfect background music") ||
result_album.contains("70's gold") ||
result_album.contains("rockfluence") ||
result_album.contains("acoustic dinner accompaniment") ||
result_album.contains("complete studio albums") ||
result_album.contains("mellow rock")
)) {
results_copy[i].score_match -= 1;
}
// Set the initial image quality score besed on the size returned by the API, this is recalculated when the image is received.
results_copy[i].score_quality += ScoreImage(results_copy[i].image_size);
}
// Add results from the current provider to our pool
@@ -204,10 +268,14 @@ void AlbumCoverFetcherSearch::FetchMoreImages() {
++i;
CoverSearchResult result = results_.takeFirst();
qLog(Debug) << "Loading" << result.image_url << "from" << result.provider << "with current score" << result.score;
qLog(Debug) << "Loading" << result.artist << result.album << result.image_url << "from" << result.provider << "with current score" << result.score();
QNetworkRequest req(result.image_url);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
#else
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
#endif
QNetworkReply *image_reply = network_->get(req);
connect(image_reply, &QNetworkReply::finished, [=] { ProviderCoverFetchFinished(image_reply); });
pending_image_loads_[image_reply] = result;
@@ -251,9 +319,13 @@ void AlbumCoverFetcherSearch::ProviderCoverFetchFinished(QNetworkReply *reply) {
if (QImageReader::supportedMimeTypes().contains(mimetype.toUtf8())) {
QImage image;
if (image.loadFromData(reply->readAll())) {
result.score += ScoreImage(image);
candidate_images_.insert(result.score, CandidateImage(result, image));
qLog(Debug) << reply->url() << "from" << result.provider << "scored" << result.score;
if (result.image_size != QSize(0,0) && result.image_size != image.size()) {
qLog(Debug) << "API size for image" << result.image_size << "for" << reply->url() << "from" << result.provider << "did not match retrieved size" << image.size();
}
result.image_size = image.size();
result.score_quality = ScoreImage(image.size());
candidate_images_.insert(result.score(), CandidateImage(result, image));
qLog(Debug) << reply->url() << "from" << result.provider << "scored" << result.score();
}
else {
qLog(Error) << "Error decoding image data from" << reply->url();
@@ -283,18 +355,15 @@ void AlbumCoverFetcherSearch::ProviderCoverFetchFinished(QNetworkReply *reply) {
}
float AlbumCoverFetcherSearch::ScoreImage(const QImage &image) const {
float AlbumCoverFetcherSearch::ScoreImage(const QSize size) const {
// Invalid images score nothing
if (image.isNull()) {
return 0.0;
}
if (size.width() == 0 || size.height() == 0) return 0.0;
// A 500x500px image scores 1.0, bigger scores higher
const float size_score = std::sqrt(float(image.width() * image.height())) / kTargetSize;
const float size_score = std::sqrt(float(size.width() * size.height())) / kTargetSize;
// A 1:1 image scores 1.0, anything else scores less
const float aspect_score = 1.0 - float(std::max(image.width(), image.height()) - std::min(image.width(), image.height())) / std::max(image.height(), image.width());
const float aspect_score = 1.0 - float(std::max(size.width(), size.height()) - std::min(size.width(), size.height())) / std::max(size.height(), size.width());
return size_score + aspect_score;
@@ -310,7 +379,7 @@ void AlbumCoverFetcherSearch::SendBestImage() {
cover_url = best_image.first.image_url;
image = best_image.second;
qLog(Info) << "Using" << best_image.first.image_url << "from" << best_image.first.provider << "with score" << best_image.first.score;
qLog(Info) << "Using" << best_image.first.image_url << "from" << best_image.first.provider << "with score" << best_image.first.score();
statistics_.chosen_images_by_provider_[best_image.first.provider]++;
statistics_.chosen_images_++;
@@ -348,5 +417,9 @@ bool AlbumCoverFetcherSearch::ProviderCompareOrder(CoverProvider *a, CoverProvid
}
bool AlbumCoverFetcherSearch::CoverSearchResultCompareScore(const CoverSearchResult &a, const CoverSearchResult &b) {
return a.score > b.score;
return a.score() > b.score();
}
bool AlbumCoverFetcherSearch::CoverSearchResultCompareNumber(const CoverSearchResult &a, const CoverSearchResult &b) {
return a.number < b.number;
}

View File

@@ -59,6 +59,8 @@ class AlbumCoverFetcherSearch : public QObject {
CoverSearchStatistics statistics() const { return statistics_; }
static bool CoverSearchResultCompareNumber(const CoverSearchResult &a, const CoverSearchResult &b);
signals:
// It's the end of search (when there was no fetch-me-a-cover request).
void SearchFinished(const quint64, const CoverSearchResults &results);
@@ -77,7 +79,7 @@ class AlbumCoverFetcherSearch : public QObject {
void AllProvidersFinished();
void FetchMoreImages();
float ScoreImage(const QImage &image) const;
float ScoreImage(const QSize size) const;
void SendBestImage();
static bool ProviderCompareOrder(CoverProvider *a, CoverProvider *b);

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