Compare commits

...

171 Commits
0.8.3 ... 0.8.5

Author SHA1 Message Date
Jonas Kvinge
d3faf18251 Fix typo 2020-12-19 17:42:24 +01:00
Jonas Kvinge
ddcc296af4 Release 0.8.5 2020-12-19 17:38:24 +01:00
Jonas Kvinge
ee765bc210 Update strawberry.spec 2020-12-17 22:49:57 +01:00
Jonas Kvinge
630c67f7d0 Remove -U__STRICT_ANSI__ from compile flags 2020-12-17 19:47:46 +01:00
Jonas Kvinge
e78bb1b29c Use directsoundsink by default because of buggy wasapi plugin 2020-12-13 21:56:25 +01:00
Strawbs Bot
db312a7380 Update translations 2020-12-13 01:02:57 +01:00
Jonas Kvinge
48b4cbe1b9 Update ccpp.yml 2020-12-12 19:05:21 +01:00
Jonas Kvinge
edb1d4c747 Append Qt5 to Windows setups 2020-12-12 12:51:23 +01:00
Jonas Kvinge
128935b55e Use LPCSTR in RegisterWindowMessageA 2020-12-12 12:51:01 +01:00
Jonas Kvinge
d5a0d67c7b Use ScopedWCharArray 2020-12-12 12:50:33 +01:00
Jonas Kvinge
a86640fdee Exclude Windows from find D-Bus in CMake 2020-12-12 12:49:41 +01:00
Jonas Kvinge
03291b27d1 Mark unused 2020-12-12 12:49:35 +01:00
Strawbs Bot
acf81f116f Update translations 2020-12-12 00:51:58 +01:00
Jonas Kvinge
6564c405c1 Update Changelog 2020-12-12 00:50:46 +01:00
Jonas Kvinge
1bd586268c Update some copyrights 2020-12-12 00:33:27 +01:00
Jonas Kvinge
497952611d Fix HTML escaping for custom OSD notifications
Fixes #618
2020-12-11 23:59:38 +01:00
Jonas Kvinge
9149d1baa3 Add back mistakenly removed QT_MIN_VERSION in CMakeLists.txt 2020-12-11 21:01:51 +01:00
Jonas Kvinge
66c60ac5c7 Add back CentOS 8 to CI 2020-12-09 21:44:47 +01:00
Strawbs Bot
971fad4560 Update translations 2020-12-09 19:33:33 +01:00
Jonas Kvinge
8ed1ce4103 Move ThreadSafeNetworkDiskCache to own file 2020-12-09 18:41:07 +01:00
Jonas Kvinge
3112c34d11 Change to NetworkAccessManager 2020-12-09 18:39:37 +01:00
Jonas Kvinge
7ebf3cecc6 Only include Rpm.cmake and Deb.cmake on Linux 2020-12-09 18:29:30 +01:00
Jonas Kvinge
660fa99f9c Simplify Rpm.cmake 2020-12-09 18:09:49 +01:00
Jonas Kvinge
a997e53d8b Update RPM spec for Qt 6 2020-12-09 01:29:38 +01:00
Jonas Kvinge
1d1dd583e2 Remove CentOS from CI 2020-12-08 15:59:53 +01:00
Jonas Kvinge
d490b29265 Use regex 2020-12-07 22:43:00 +01:00
Jonas Kvinge
e8ca64f16b Replace empty quotes with empty strings for values in CUE parser 2020-12-07 22:34:55 +01:00
Jonas Kvinge
380aa7d884 Only set disc and year in CUE parser when valid 2020-12-07 21:57:15 +01:00
Jonas Kvinge
0a31a94ee8 Change double-click in cover manager to open fullsize cover
Fixes #612
2020-12-07 17:23:08 +01:00
Jonas Kvinge
e1d50ce28f Make URL in scrobbler settings clickable 2020-12-07 17:21:34 +01:00
Jonas Kvinge
38cc39f23d Workaround udisks2 mountpoint issue with Qt 6 2020-12-06 18:46:30 +01:00
Jonas Kvinge
8110035d71 Remove unref 2020-12-06 18:45:37 +01:00
Jonas Kvinge
9af409d6d6 Make sure URL is valid in giolister 2020-12-06 18:45:17 +01:00
Jonas Kvinge
8646829853 Update .travis.yml 2020-12-06 11:16:15 +01:00
Jonas Kvinge
b9e87813b1 Add back Travis-CI 2020-12-06 06:45:08 +01:00
Strawbs Bot
138e032746 Update translations 2020-12-05 01:03:29 +01:00
Jonas Kvinge
d94c6b5aba Set active playlist when playlist doubleclick behaviour is enqueue
Fixes #610
2020-12-04 22:11:35 +01:00
Jonas Kvinge
7c03a39316 Update Changelog 2020-12-04 19:38:00 +01:00
Jonas Kvinge
c482e264f4 Rename variable 2020-12-04 19:13:21 +01:00
Nicolas Toussaint
3a9a1f0a94 add CLI play-playlist option, to play given playlist name. (#608)
* add CLI play-playlist option, to play given playlist name.

* Apply MR change requests
2020-12-04 19:08:41 +01:00
Jonas Kvinge
5b5f728f49 Update README.md 2020-12-03 22:24:57 +01:00
Jonas Kvinge
179aac6b35 Update FUNDING.yml 2020-12-03 22:16:22 +01:00
Strawbs Bot
25de9126ca Update translations 2020-11-30 01:19:32 +01:00
Jonas Kvinge
87be5662ad Update .circleci/config.yml 2020-11-29 16:05:02 +01:00
Jonas Kvinge
053a59e3f4 Update .github/workflows/ccpp.yml 2020-11-29 16:03:44 +01:00
Jonas Kvinge
250857b3d4 Update .circleci/config.yml 2020-11-29 07:54:42 +01:00
Jonas Kvinge
dc288c584c Search qpa/qplatformnativeinterface.h for both Qt 5 and 6 2020-11-29 07:50:08 +01:00
Jonas Kvinge
fc02543f15 Make X11Extras and WinExtras optional
The initial release of Qt 6 does not have these.

These modules are not important.
X11Extras is only used for X11 global shortcuts, which in most cases is
overriden by KDE or Gnome shortcuts anyway.
It is also used in OSD Pretty to detect transparency, but this can be
done using private GUI headers instead.

WinExtras is only used in OSD Pretty to make it transparent.
2020-11-29 07:41:30 +01:00
Jonas Kvinge
0808db706f Remove libgstrealmedia.dylib from macdeploy 2020-11-29 06:43:24 +01:00
Strawbs Bot
1e649fc565 Update translations 2020-11-29 01:02:57 +01:00
Strawbs Bot
c523e959d7 Update translations 2020-11-27 01:02:59 +01:00
Strawbs Bot
1422f988c1 Update translations 2020-11-26 01:02:53 +01:00
Strawbs Bot
e86252b934 Update translations 2020-11-25 01:02:17 +01:00
Jonas Kvinge
e9c59b5c31 Add gst-launch-1.0.exe to windows setup 2020-11-24 15:45:24 +01:00
Strawbs Bot
f7bddee0ce Update translations 2020-11-24 01:05:32 +01:00
Jonas Kvinge
d56b76c9e3 Save both ID3v1 and ID3v2
Fixes #598
2020-11-23 22:25:47 +01:00
Jonas Kvinge
a41353ed03 Update progress even if QDateTime::fromString() fails 2020-11-23 21:54:58 +01:00
Strawbs Bot
7ea12cf2f9 Update translations 2020-11-23 01:05:20 +01:00
Jonas Kvinge
5bc5170112 Remove travis 2020-11-22 20:01:45 +01:00
Jonas Kvinge
946ed0c0b5 Check for valid datetime when importing last played 2020-11-22 19:01:53 +01:00
Jonas Kvinge
fcd4e5aca2 Save and restore geometry in edit tag dialog 2020-11-22 03:37:15 +01:00
Jonas Kvinge
6ab6e6d3a8 Format code 2020-11-22 03:36:46 +01:00
Strawbs Bot
0893d01b4a Update translations 2020-11-22 01:02:49 +01:00
Jonas Kvinge
bf04c45ebf Remove unused live scanning option 2020-11-21 09:40:48 +01:00
Jonas Kvinge
2786a1a97e Remove required version from cmake files 2020-11-21 07:41:43 +01:00
Strawbs Bot
cd9a4f1aa9 Update translations 2020-11-21 01:16:20 +01:00
Jonas Kvinge
91e5cafe76 Remove disabling repeat and shuffle buttons
It's more confusing than helpful
2020-11-20 21:48:10 +01:00
Jonas Kvinge
47754951f0 Format code 2020-11-20 21:47:01 +01:00
Strawbs Bot
fbef8ef32e Update translations 2020-11-20 01:08:25 +01:00
Jonas Kvinge
abd5bf3158 Update .github/workflows/ccpp.yml 2020-11-19 21:25:40 +01:00
Jonas Kvinge
fd024368a3 Update .github/workflows/ccpp.yml 2020-11-19 21:24:30 +01:00
Jonas Kvinge
cf4856a0c9 Fix signal not always emitted from messagreply 2020-11-19 18:29:03 +01:00
Jonas Kvinge
1b791c82fc Use qint64 for message request ID 2020-11-19 18:26:50 +01:00
Jonas Kvinge
3a27a3e868 Format code 2020-11-19 18:22:57 +01:00
Jonas Kvinge
7e6c16b287 Change progress and max progress to qint64 in taskmanager 2020-11-19 18:14:42 +01:00
Jonas Kvinge
f877639ed7 Fix repeat/shuffle disabled when a dynamic playlist is open
Fixes #593
2020-11-19 18:12:48 +01:00
Strawbs Bot
846309d4dd Add Swedish 2020-11-19 01:19:58 +01:00
Strawbs Bot
f2239ddfdf Update translations 2020-11-19 01:02:47 +01:00
Jonas Kvinge
742d455b0e Build on Big Sur 2020-11-18 22:48:16 +01:00
Strawbs Bot
20b08a78e7 Update translations 2020-11-18 01:17:34 +01:00
Jonas Kvinge
042da74955 Change return type of qHash with Qt 6 to size_t 2020-11-17 01:22:38 +01:00
Jonas Kvinge
4dcae4ce21 Use QChar::unicode() 2020-11-17 01:22:00 +01:00
Jonas Kvinge
fa0f07f553 Use QString::number() 2020-11-17 01:21:38 +01:00
Jonas Kvinge
39792d76cf Fix PlaylistGenerator::type() 2020-11-17 01:20:45 +01:00
Strawbs Bot
d181ee9101 Update translations 2020-11-17 01:02:12 +01:00
Jonas Kvinge
335ba1a5b0 Disable tagreader checksum tests
These don't match anymore because tags are now removed.
2020-11-15 12:17:35 +01:00
Jonas Kvinge
4934a360f1 Turn back git revision 2020-11-15 12:16:44 +01:00
Strawbs Bot
0559a4c6f2 Update translations 2020-11-15 01:03:31 +01:00
Jonas Kvinge
fc07919a75 Release 0.8.4 2020-11-15 00:31:32 +01:00
Jonas Kvinge
d7661edf67 Update Changelog 2020-11-15 00:20:16 +01:00
Jonas Kvinge
1c91693294 Ignore org.kde.kglobalaccel.NoSuchComponent error 2020-11-15 00:20:08 +01:00
Jonas Kvinge
3fc3cbc6d5 Update protobuf dll in nsi 2020-11-14 19:13:40 +01:00
Jonas Kvinge
11b5895e69 Update ccpp.yml 2020-11-14 18:36:59 +01:00
Jonas Kvinge
b4c614edbf Set volume bit 2020-11-14 04:36:38 +01:00
Jonas Kvinge
deddaed04a Remove use of std::bind where possible 2020-11-14 02:13:22 +01:00
Jonas Kvinge
a155e503f4 Never use reference when iterating QJsonArray 2020-11-13 21:10:50 +01:00
Jonas Kvinge
c0663bc19f Use reference 2020-11-13 20:34:29 +01:00
Strawbs Bot
7ffa51b83d Update translations 2020-11-13 01:02:53 +01:00
Jonas Kvinge
6d397b9988 Fix smart playlist by filename 2020-11-12 20:47:13 +01:00
Jonas Kvinge
571a7fa26b Fix single letter collection nodes showing before dividers 2020-11-12 20:30:58 +01:00
Jonas Kvinge
b3b5a38c3a Minor code style fix 2020-11-11 22:55:56 +01:00
Jonas Kvinge
a4b115f89b Update Changelog 2020-11-11 22:51:55 +01:00
Strawbs Bot
3d672bb145 Update translations 2020-11-11 01:03:35 +01:00
Jonas Kvinge
15b656b753 Merge pull request #587 from fbugno/issue515
Fix HiDPI scaling for glow animation and drag over playlist
2020-11-10 22:52:08 +01:00
Felipe Bugno
f5785db163 Code style changes to match the existing code
This changes the style of the private variable and the call
convention of the inherited functions.
2020-11-10 17:55:00 -03:00
Jonas Kvinge
1ff1bf3292 Only call deleteLater when proxystyle is set 2020-11-10 19:06:26 +01:00
Felipe Bugno
b062febea0 Fix HiDPI scaling for glow animation and drag over playlist
This set the proper scaling and pixel ratio of QPixmap widgets
used as cached objects.

Most of cached objects uses a custom QPaint instead of the default
painter object from the parent widget. The problem is that, unlike
the painter from the parent object, set by the main application,
and that has DPI and scaling settings from the device, these custom
QPainters don't know about the underlying device, thus uses a
scale of 1 to render artifacts.

When a cached object "edited" by a custom QPaint along his pipeline
where used on a paint or drawrow routine, his stored image is distorted
and burred in a effort to resize it to the display configuration.
2020-11-09 21:49:22 -03:00
Strawbs Bot
35301dc79e Update translations 2020-11-10 01:02:36 +01:00
Jonas Kvinge
30c336726b Only backup database if schema version is correct 2020-11-09 23:10:43 +01:00
Jonas Kvinge
3821680817 Increase sqlite busy timeout to 30 seconds
Possible fix for #533
2020-11-09 22:49:33 +01:00
Jonas Kvinge
74242ea24f Fix shortcut settings on macOS and Windows 2020-11-09 20:20:31 +01:00
Jonas Kvinge
73c7024e11 Dont return from SongSaveComplete early, needs to free TagReaderReply 2020-11-09 19:17:31 +01:00
Strawbs Bot
bbdec92dc6 Update translations 2020-11-09 01:01:55 +01:00
Jonas Kvinge
b4c289101c Strip summary but encode message 2020-11-08 21:54:16 +01:00
Jonas Kvinge
722d0797f6 Use QString::toHtmlEscaped() 2020-11-08 17:52:29 +01:00
Jonas Kvinge
deb27d5b55 Replace '&' with '&' in D-BUS OSD messages 2020-11-08 14:24:09 +01:00
Jonas Kvinge
9cc4ffdf6e Only strip '&' from D-Bus OSD messages 2020-11-08 13:46:17 +01:00
Strawbs Bot
c76d63d1d9 Update translations 2020-11-08 13:32:51 +01:00
Jonas Kvinge
21bb4f33ad Update Changelog 2020-11-08 04:05:39 +01:00
Jonas Kvinge
2897b881d6 Only override fancy tabwidget style with adwaita 2020-11-08 04:04:37 +01:00
Jonas Kvinge
e9b89d0929 Simplify FancyTabWidget override 2020-11-08 03:23:18 +01:00
Jonas Kvinge
e801254b2e Log used style 2020-11-08 03:22:38 +01:00
Jonas Kvinge
6f49918ee9 Update Changelog 2020-11-08 02:10:20 +01:00
Jonas Kvinge
0ae7c18f1f Add setting to set style 2020-11-08 02:04:24 +01:00
Jonas Kvinge
c9c3fb396a Move ComboBoxLoadFromSettings function to settingspage 2020-11-08 02:02:48 +01:00
Strawbs Bot
91db4f1934 Update translations 2020-11-08 01:02:31 +01:00
Jonas Kvinge
9bbed6e95c Use QClipboard::setText instead of QClipboard::setMimeData
Fixes #581

Fixes clipboard copying on Windows too
2020-11-08 00:51:24 +01:00
Strawbs Bot
748bc27b25 Update translations 2020-11-06 11:50:48 +01:00
Jonas Kvinge
f42708e8bc Remove qt6-qt5compat-devel from ccpp.yml 2020-11-05 22:30:37 +01:00
Jonas Kvinge
160e4570a2 Use C++17 (#579)
* Use C++17

* Replace std::random_shuffle with std::shuffle

* Add random include
2020-11-05 22:28:49 +01:00
Jonas Kvinge
6272965143 Update Changelog 2020-11-05 00:10:16 +01:00
Jonas Kvinge
1e9613bf7f Update ccpp.yml 2020-11-04 23:45:40 +01:00
Jonas Kvinge
a061dac298 Check network proxy mode 2020-11-04 22:22:26 +01:00
Jonas Kvinge
914dee8571 Pass network proxy settings to gstreamer
Fixes #558
2020-11-04 22:16:20 +01:00
yavuzmert
47e2905edf Re-enable progress on system tray icon (#578)
* Re-enable progress on system tray icon

* fix copy-paste typo in mac sources

* Make tray icon progress optional

* Move tray icon progress setting control to MainWindow

* Move trayicon settings to behavioursettings

Co-authored-by: Yavuz Mert <yavuz.mert@darkbluesystems.net>
2020-11-04 21:01:04 +01:00
Jonas Kvinge
ee6675aee0 No longer need Core5Compat 2020-11-04 18:17:33 +01:00
Jonas Kvinge
62e0d9fe64 Remove all uses of QTextCodec 2020-11-04 18:16:23 +01:00
Jonas Kvinge
a174c142c1 Remove unused linked list includes 2020-11-04 18:06:36 +01:00
Jonas Kvinge
95afc5fdec Keep tabs in the middle on macOS 2020-11-04 18:05:58 +01:00
Strawbs Bot
04d69f66c0 Update translations 2020-11-04 01:02:16 +01:00
Jonas Kvinge
0347141edd Update snap dialog 2020-11-03 19:32:32 +01:00
Jonas Kvinge
1d6baae6e0 Set ssl-strict false in songloader too 2020-11-03 18:48:35 +01:00
yavuzmert
fb0f48f08a Make context view top label selectable (#576)
Co-authored-by: Yavuz Mert <yavuz.mert@darkbluesystems.net>
2020-11-03 01:34:09 +01:00
Jonas Kvinge
76e5e03d31 Change copy command in snap dialog 2020-11-02 21:26:20 +01:00
Jonas Kvinge
4cab743634 Center playlist tabbar star icon
Fixes #574
2020-11-02 17:57:12 +01:00
Jonas Kvinge
7c10ec97b7 Remove unused parameter 2020-11-02 17:47:16 +01:00
Jonas Kvinge
8718a16889 Star/unstar playlist with doubleclick 2020-11-02 17:45:29 +01:00
Strawbs Bot
75a0b924c3 Update translations 2020-11-02 01:02:00 +01:00
Jonas Kvinge
83a90e0c05 Make tabbar style hack less intrusive 2020-11-01 21:54:23 +01:00
Strawbs Bot
e5eadd1315 Update translations 2020-10-31 14:34:41 +01:00
Jonas Kvinge
f0142d90d4 Update comment 2020-10-31 14:27:53 +01:00
Jonas Kvinge
cabd6e6e9d Override QStyle::subElementRect in fancy tabbar to fix style problems
Something is causing the contents of the tabbar to be stretched from top to bottom with space between icons and text.
You can see this on the default Fedora (Gnome) installation.
Also fixes the tabbar on macOS where the content was in the middle instead of the top.
2020-10-31 14:05:06 +01:00
Jonas Kvinge
4804a05736 Remove fixed width in about dialog as it crashes on wayland 2020-10-31 02:41:42 +01:00
Jonas Kvinge
c258e5a3af Add KDE global shortcuts
Fixes #572
2020-10-31 02:08:19 +01:00
Jonas Kvinge
e8492940a5 Add -fPIC compiler flag for Redhat/Fedora/CentOS
Fixes #573
2020-10-30 18:12:42 +01:00
Jonas Kvinge
4bccb1ab47 Only save as ID3v2, and remove empty tags
Fixes #571
2020-10-29 18:47:09 +01:00
Strawbs Bot
b6d219e232 Update translations 2020-10-28 17:50:58 +01:00
Jonas Kvinge
23ee17594d Simplify CMake Qt 2020-10-27 20:54:25 +01:00
Jonas Kvinge
d9d39d8e25 Only override MainWindow::nativeEvent for Windows 2020-10-27 20:17:28 +01:00
Jonas Kvinge
224d5d46c1 Set QApplication::setQuitOnLastWindowClosed to false 2020-10-27 20:01:07 +01:00
Jonas Kvinge
09e0059930 Resize organize window when copying to device
Fixes #566
2020-10-27 17:50:16 +01:00
Jonas Kvinge
0ddff2b087 Fix stupid bug 2020-10-27 17:47:40 +01:00
Jonas Kvinge
2e6a29eacc Remove ifdef HAVE_GSTREAMER
Fixes #568
2020-10-27 17:17:12 +01:00
Jonas Kvinge
ad2fb82aa9 Don't edit playlist name on doubleclick in playlists view
Fixes #567
2020-10-27 17:11:17 +01:00
Strawbs Bot
a3f91c11e8 Update translations 2020-10-26 01:02:24 +01:00
Jonas Kvinge
a50c978ce3 Fix cancelling logout when window is maxmimized
Fixes #565
2020-10-25 19:59:27 +01:00
Strawbs Bot
27d6f881cd Update translations 2020-10-25 01:04:50 +02:00
Jonas Kvinge
944cd020af Only strip problematic characters when saving a playlist 2020-10-25 01:01:43 +02:00
Jonas Kvinge
bbe5d64b99 Add info about backing up configuration to snap dialog 2020-10-25 01:01:16 +02:00
Jonas Kvinge
f7b36ac4c7 Replace use of QVariant::type() with Qt 6 2020-10-24 03:32:40 +02:00
Jonas Kvinge
1d555ca17e Turn back git revision 2020-10-24 03:30:21 +02:00
235 changed files with 15200 additions and 7884 deletions

View File

@@ -155,77 +155,6 @@ commands:
hicolor-icon-theme
install_centos_dependencies:
description: Install CentOS dependencies
steps:
- run:
name: Install epel-release
command: dnf install -y epel-release
- run:
name: Install epel-release-latest-8.noarch.rpm
command: dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
- run:
name: Install config-manager
command: dnf install -y 'dnf-command(config-manager)'
- run:
name: PowerTools
command: dnf config-manager --set-enabled PowerTools
- run:
name: DNF Clean All
command: dnf clean all
- run:
name: Update packages
command: dnf update -y
- run:
name: Install CentOS dependencies
command: >
dnf install -y
glibc
gcc-c++
make
libtool
cmake3
rpmdevtools
redhat-lsb-core
git
man
tar
gettext
boost-devel
fuse-devel
dbus-devel
libnotify-devel
gnutls-devel
sqlite-devel
protobuf-devel
protobuf-compiler
alsa-lib-devel
pulseaudio-libs-devel
qt5-devel
qt5-qtbase-devel
qt5-qtx11extras-devel
qt5-qttools-devel
fftw-devel
libchromaprint-devel
libcdio-devel
libgpod-devel
libmtp-devel
libjpeg-devel
cairo-devel
dbus-x11
xorg-x11-server-Xvfb
xorg-x11-xauth
vim-common
desktop-file-utils
libappstream-glib
appstream-data
hicolor-icon-theme
python3-pip
python3-devel
gstreamer1-devel
gstreamer1-plugins-base-devel
install_debian_dependencies:
description: Install Debian dependencies
steps:
@@ -326,7 +255,7 @@ jobs:
build_source:
docker:
- image: opensuse/leap:15.1
- image: opensuse/leap:15.2
steps:
- install_opensuse_dependencies
- checkout
@@ -359,18 +288,6 @@ jobs:
- build_rpm
build_fedora_31:
docker:
- image: fedora:31
environment:
RPM_BUILD_NCPUS: "2"
steps:
- install_fedora_dependencies
- checkout
- cmake
- build_source
- build_rpm
build_fedora_32:
docker:
- image: fedora:32
@@ -383,14 +300,13 @@ jobs:
- build_source
- build_rpm
build_centos_8:
build_fedora_33:
docker:
- image: centos:8
- image: fedora:33
environment:
RPM_BUILD_NCPUS: "2"
steps:
- install_centos_dependencies
- install_fedora_dependencies
- checkout
- cmake
- build_source
@@ -464,17 +380,11 @@ workflows:
only: /.*/
- build_fedora_31:
filters:
tags:
only: /.*/
- build_fedora_32:
filters:
tags:
only: /.*/
- build_centos_8:
- build_fedora_33:
filters:
tags:
only: /.*/

1
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
github: jonaski
patreon: jonaskvinge

View File

@@ -7,7 +7,7 @@ jobs:
name: Create source tarball
runs-on: ubuntu-latest
container:
image: opensuse/leap:15.1
image: opensuse/leap:15.2
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
@@ -223,8 +223,8 @@ jobs:
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_tumbleweed:
name: Build openSUSE Tumbleweed
build_opensuse_tumbleweed_qt5:
name: Build openSUSE Tumbleweed Qt 5
runs-on: ubuntu-latest
container:
image: opensuse/tumbleweed
@@ -287,7 +287,7 @@ jobs:
- name: Configure CMake
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT5=ON
- name: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
@@ -301,8 +301,8 @@ jobs:
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_opensuse_qt6:
name: Build openSUSE Qt 6
build_opensuse_tumbleweed_qt6:
name: Build openSUSE Tumbleweed Qt 6
runs-on: ubuntu-latest
container:
image: opensuse/tumbleweed
@@ -352,7 +352,7 @@ jobs:
qt6-x11extras-devel
qt6-base-common-devel
qt6-sql-sqlite
qt6-qt5compat-devel
qt6-linguist-devel
libcdio-devel
libgpod-devel
libmtp-devel
@@ -368,9 +368,17 @@ jobs:
shell: bash
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_WITH_QT6=ON
- name: Build
- name: Create source tarball
working-directory: build
run: cmake --build . --config $BUILD_TYPE
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_fedora_32:
@@ -615,15 +623,21 @@ jobs:
steps:
- uses: actions/checkout@v1.2.0
- name: Install dnf-plugins-core
run: dnf install -y dnf-plugins-core
- name: Install epel-release
run: dnf install -y epel-release
- name: Install config-manager
run: dnf install -y 'dnf-command(config-manager)'
- name: Enable PowerTools
run: dnf config-manager --set-enabled PowerTools
- name: DNF Clean All
- name: Enable powertools
run: dnf config-manager --set-enabled powertools
- name: Clean all
run: dnf clean all
- name: DNF Update
- name: Update
run: dnf update -y
- name: Install CentOS dependencies
@@ -649,10 +663,9 @@ jobs:
gnutls-devel
sqlite-devel
protobuf-devel
protobuf-compiler
protobuf-c
alsa-lib-devel
pulseaudio-libs-devel
qt5-devel
qt5-qtbase-devel
qt5-qtx11extras-devel
qt5-qttools-devel
@@ -673,7 +686,89 @@ jobs:
hicolor-icon-theme
gstreamer1-devel
gstreamer1-plugins-base-devel
- 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: Create source tarball
working-directory: build
run: ../dist/scripts/maketarball.sh
- name: Create RPM build sources directories
working-directory: build
run: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
- name: Copy source tarball
working-directory: build
run: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
- name: Build RPM
working-directory: build
run: rpmbuild -ba ../dist/unix/strawberry.spec
build_mageia_7:
name: Build Mageia 7
runs-on: ubuntu-latest
container:
image: mageia:7
steps:
- uses: actions/checkout@v1.2.0
- name: Update packages
run: urpmi.update --auto -a
- name: Configure auto update
run: urpmi --auto --auto-update
- name: Install Mageia dependencies
run: >
urpmi --auto --force
urpmi-debuginfo-install
git
tar
rpmdevtools
make
cmake
glibc
binutils
gcc-c++
man
gettext
notification-daemon
dbus-devel
libgnutls-devel
lib64boost-devel
lib64protobuf-devel
protobuf-compiler
lib64sqlite3-devel
lib64alsa2-devel
lib64pulseaudio-devel
lib64notify-devel
lib64qt5core-devel
lib64qt5gui-devel
lib64qt5widgets-devel
lib64qt5network-devel
lib64qt5concurrent-devel
lib64qt5sql-devel
lib64qt5dbus-devel
lib64qt5x11extras-devel
lib64qt5help-devel
libqt5test-devel
lib64gstreamer1.0-devel
lib64gstreamer-plugins-base1.0-devel
lib64cdio-devel
lib64gpod-devel
lib64mtp-devel
lib64raw1394-devel
lib64chromaprint-devel
libfftw-devel
desktop-file-utils
appstream-util
libappstream-glib8
hicolor-icon-theme
qt5ct
lib64mesaegl1
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
@@ -995,15 +1090,11 @@ jobs:
run: dpkg-buildpackage -b -d -uc -us -nc -j2
build-macos:
name: Build macOS
runs-on: macos-latest
build-macos-catalina:
name: Build macOS Catalina
runs-on: macos-10.15
steps:
- uses: actions/checkout@v1.2.0
#- name: Update
# run: brew update
#- name: Upgrade
# run: brew upgrade
- name: Install packages
run: >
brew install
@@ -1061,8 +1152,73 @@ jobs:
run: make dmg
- uses: actions/upload-artifact@v2
with:
name: upload-macos
path: build/strawberry-*.dmg
name: upload-macos-catalina
path: build/strawberry-*-catalina.dmg
build-macos-bigsur:
name: Build macOS Big Sur
runs-on: macos-11.0
steps:
- uses: actions/checkout@v1.2.0
- name: Install packages
run: >
brew install
glib
pkgconfig
boost
libffi
protobuf
protobuf-c
qt
gettext
gnutls
fftw
sqlite
chromaprint
gstreamer
gst-plugins-base
gst-plugins-good
gst-plugins-bad
gst-plugins-ugly
gst-libav
libcdio
libmtp
create-dmg
taglib
- name: Delete conflicting taglib system headers
shell: bash
run: rm -rf /usr/local/include/taglib
- name: Create Build Environment
shell: bash
run: cmake -E make_directory build
- name: Configure CMake
shell: bash
env:
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
Qt5_DIR: /usr/local/opt/qt5/lib/cmake
Qt5LinguistTools_DIR: /usr/local/opt/qt5/lib/cmake/Qt5LinguistTools
GST_SCANNER_PATH: /usr/local/opt/gstreamer/libexec/gstreamer-1.0/gst-plugin-scanner
GST_PLUGIN_PATH: /usr/local/lib/gstreamer-1.0
working-directory: build
run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLE=ON
- name: Build
working-directory: build
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Install
working-directory: build
shell: bash
run: make install
- name: Create DMG
working-directory: build
shell: bash
run: make dmg
- uses: actions/upload-artifact@v2
with:
name: upload-macos-bigsur
path: build/strawberry-*-bigsur.dmg
build-windows:
@@ -1179,7 +1335,7 @@ jobs:
- name: Copy extra binaries
working-directory: build
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,liborc-0.4-0.dll} .
run: cp /usr/src/strawberry-mxe/usr/x86_64-w64-mingw32.shared/bin/{sqlite3.exe,killproc.exe,gst-launch-1.0.exe} .
- name: Copy dependencies
working-directory: build
@@ -1208,12 +1364,12 @@ jobs:
run: makensis strawberry.nsi
upload-macos:
name: Upload macOS DMG
upload-macos-catalina:
name: Upload macOS Catalina DMG
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
needs:
- build-macos
- build-macos-catalina
steps:
- uses: actions/checkout@v1.2.0
- uses: actions/download-artifact@v2
@@ -1227,6 +1383,29 @@ jobs:
- name: rsync
run: |
set -x
for i in $(find uploads -type f -name '*.dmg'); do
for i in $(find uploads -type f -name 'strawberry-*-catalina.dmg'); do
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/catalina/
done
upload-macos-bigsur:
name: Upload macOS Big Sur DMG
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
needs:
- build-macos-bigsur
steps:
- uses: actions/checkout@v1.2.0
- uses: actions/download-artifact@v2
with:
path: uploads
- name: Install SSH keys
uses: shimataro/ssh-key-action@v2
with:
known_hosts: ${{ secrets.KNOWN_HOSTS2 }}
key: ${{ secrets.SSH_KEY }}
- name: rsync
run: |
set -x
for i in $(find uploads -type f -name 'strawberry-*-bigsur.dmg'); do
rsync -e "ssh -p 50220 -o StrictHostKeyChecking=no" -va $i travis@echoes.jkvinge.net:/home/travis/builds/macos/bigsur/
done

View File

@@ -1,9 +1,8 @@
sudo: required
language: C++
os:
- osx
compiler:
- gcc
os: osx
osx_image: xcode11.3
compiler: clang
before_install:
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
@@ -13,8 +12,8 @@ before_install:
- git fetch --unshallow
- git pull
- brew update
- travis_wait 120 brew upgrade || echo "Failed"
- travis_wait 120 brew upgrade || echo "Failed"
- travis_wait 400 brew upgrade || echo "Failed"
- travis_wait 400 brew upgrade || echo "Failed"
- 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
@@ -32,14 +31,12 @@ before_script:
script:
- make -j8
- make install
- make dmg
- make dmg2
after_success:
- ls -lh strawberry*.dmg
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$CC_FOR_BUILD" == "gcc" ]] && [ -f ~/.ssh/id_rsa ]; then
if [[ "$TRAVIS_BRANCH" == "master" ]]; then
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
elif [[ "$TRAVIS_BRANCH" == "macos" ]]; then
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/highsierra/;
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "travis" ]]; then
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
fi
fi

View File

@@ -16,11 +16,6 @@ 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
${QtCore_INCLUDE_DIRS}
${QtWidgets_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(singleapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
@@ -39,10 +34,6 @@ 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
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(singlecoreapplication PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}

View File

@@ -1,2 +1 @@
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)

View File

@@ -11,13 +11,6 @@ include(cmake/Version.cmake)
include(cmake/Summary.cmake)
include(cmake/OptionalSource.cmake)
include(cmake/ParseArguments.cmake)
include(cmake/Rpm.cmake)
include(cmake/Deb.cmake)
if(APPLE)
include(cmake/Dmg.cmake)
endif()
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(LINUX ON)
@@ -29,13 +22,22 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(OPENBSD ON)
endif()
set(CMAKE_CXX_STANDARD 11)
if(LINUX)
include(cmake/Rpm.cmake)
include(cmake/Deb.cmake)
endif()
if(APPLE)
include(cmake/Dmg.cmake)
endif()
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
list(APPEND COMPILE_OPTIONS
$<$<COMPILE_LANGUAGE:C>:--std=c99>
$<$<COMPILE_LANGUAGE:CXX>:--std=c++11>
-U__STRICT_ANSI__
$<$<COMPILE_LANGUAGE:C>:-std=c99>
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
-Wall
-Wextra
-Wpedantic
@@ -133,6 +135,11 @@ pkg_check_modules(GDK_PIXBUF gdk-pixbuf-2.0)
find_package(Gettext)
find_package(FFTW3)
if(NOT QT_DEFAULT_MAJOR_VERSION)
set(QT_DEFAULT_MAJOR_VERSION 5)
endif()
set(QT_MAJOR_VERSION ${QT_DEFAULT_MAJOR_VERSION} CACHE STRING "Qt version to use (5 or 6), defaults to ${QT_DEFAULT_MAJOR_VERSION}")
option(BUILD_WITH_QT5 "Use Qt 5" OFF)
option(BUILD_WITH_QT6 "Use Qt 6" OFF)
@@ -140,79 +147,66 @@ if(WITH_QT6)
set(BUILD_WITH_QT6 ON)
endif()
if(NOT BUILD_WITH_QT5 AND NOT BUILD_WITH_QT6)
set(BUILD_WITH_QT5 ON)
if(BUILD_WITH_QT5)
set(QT_MAJOR_VERSION 5)
elseif(BUILD_WITH_QT6)
set(QT_MAJOR_VERSION 6)
else()
if(QT_MAJOR_VERSION EQUAL 5)
set(BUILD_WITH_QT5 ON)
elseif(QT_MAJOR_VERSION EQUAL 6)
set(BUILD_WITH_QT6 ON)
else()
set(BUILD_WITH_QT5 ON)
set(QT_MAJOR_VERSION 5)
endif()
endif()
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
if(X11_FOUND)
list(APPEND QT_COMPONENTS X11Extras)
endif()
if(DBUS_FOUND)
list(APPEND QT_COMPONENTS DBus)
endif()
if(WIN32)
list(APPEND QT_COMPONENTS WinExtras)
unset(OPTIONAL_COMPONENTS)
if(QT_MAJOR_VERSION EQUAL 5)
set(QT_MIN_VERSION 5.8)
endif()
if(BUILD_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(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()
elseif(BUILD_WITH_QT5)
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(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()
else()
message(FATAL_ERROR "Set BUILD_WITH_QT5 or BUILD_WITH_QT6")
if(DBUS_FOUND AND NOT WIN32)
list(APPEND QT_COMPONENTS DBus)
endif()
if(X11_FOUND)
list(APPEND OPTIONAL_COMPONENTS X11Extras)
endif()
if(WIN32)
list(APPEND OPTIONAL_COMPONENTS WinExtras)
endif()
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${OPTIONAL_COMPONENTS})
set(QtCore_LIBRARIES Qt${QT_MAJOR_VERSION}::Core)
set(QtConcurrent_LIBRARIES Qt${QT_MAJOR_VERSION}::Concurrent)
set(QtGui_LIBRARIES Qt${QT_MAJOR_VERSION}::Gui)
set(QtWidgets_LIBRARIES Qt${QT_MAJOR_VERSION}::Widgets)
set(QtNetwork_LIBRARIES Qt${QT_MAJOR_VERSION}::Network)
set(QtSql_LIBRARIES Qt${QT_MAJOR_VERSION}::Sql)
set(QT_LIBRARIES Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Concurrent Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::Sql)
if(Qt${QT_MAJOR_VERSION}DBus_FOUND)
set(QtDBus_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::DBus)
get_target_property(QT_DBUSXML2CPP_EXECUTABLE Qt${QT_MAJOR_VERSION}::qdbusxml2cpp LOCATION)
endif()
if(Qt${QT_MAJOR_VERSION}X11Extras_FOUND)
set(QtX11Extras_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::X11Extras)
set(HAVE_X11EXTRAS ON)
endif()
if(Qt${QT_MAJOR_VERSION}WinExtras_FOUND)
set(QtWinExtras_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
list(APPEND QT_LIBRARIES Qt${QT_MAJOR_VERSION}::WinExtras)
set(HAVE_WINEXTRAS ON)
endif()
find_package(Qt${QT_MAJOR_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
if(Qt${QT_MAJOR_VERSION}LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt${QT_MAJOR_VERSION}::lconvert)
endif()
if(X11_FOUND)
@@ -230,6 +224,15 @@ if(X11_FOUND)
endif()
endif(X11_FOUND)
find_path(QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
if(QPA_QPLATFORMNATIVEINTERFACE_H)
set(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H ON)
include_directories(${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
message(STATUS "Have qpa/qplatformnativeinterface.h header.")
else()
message(STATUS "Missing qpa/qplatformnativeinterface.h header.")
endif()
# TAGLIB
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
if(USE_SYSTEM_TAGLIB)
@@ -324,6 +327,10 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
DEPENDS "D-Bus, X11, Windows or macOS" HAVE_GLOBALSHORTCUTS_SUPPORT
)
optional_component(X11_GLOBALSHORTCUTS ON "X11 global shortcuts"
DEPENDS "X11Extras" Qt${QT_MAJOR_VERSION}X11Extras_FOUND
)
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" LIBCDIO_FOUND
)
@@ -392,9 +399,8 @@ endif(USE_BUNDLE AND NOT USE_BUNDLE_DIR)
# Check that we have sqlite3 with FTS5
if(NOT CMAKE_CROSSCOMPILING)
set(CMAKE_REQUIRED_FLAGS "--std=c++11")
set(CMAKE_REQUIRED_FLAGS "-std=c++17")
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>

View File

@@ -2,6 +2,51 @@ Strawberry Music Player
=======================
ChangeLog
0.8.5:
Bugfixes:
* Fix return type of SmartPlaylistQueryWizardPlugin::type().
* Fix comparison between QChar and integer to use QChar::unicode().
* Fix return type of qHash with Qt 6 to use size_t instead of uint.
* Fix tag edit dialog save process sometimes stuck.
* Fix repeat and shuffle buttons greyed out when a dynamic playlist is open.
* Fix CUE parser handling of values with empty quotes.
* Fix broken year and disc collection groupings with CUE songs.
* Fix HTML escaping showing up in OSD notifications when using custom text.
Enhancements:
* Add Swedish translation.
* Made Qt X11Extras and WinExtras modules optional.
* Save and restore geometry in edit tag dialog.
* Add command line option to play a playlist based on name.
* Change double-click behaviour in cover manager to open fullsize cover.
0.8.4:
Bugfixes:
* Fix preventing session logout when window is maxmimized.
* Fix empty space in organize window when copying songs/playlists to devices.
* Fix crash when opening about dialog in a wayland session.
* Fix stretched fancy/side tabbar style issue with adwaita style (Fedora/Gnome).
* Fix centering star icon on playlist tabbar.
* Fix network proxy settings for streaming.
* Fix copy URL to clipboard to handle non-ASCII characters.
* Fix HiDPI scaling for glow animation and drag over playlist.
* Fix smart playlist search by filename.
* Fix single letter collection nodes showing before dividers.
Enhancements:
* Add support for native global shortcuts on KDE.
* Add track progress in system tray icon as an option.
* Only strip problematic characters in suggested filename when saving a playlist to file.
* Change star/unstar playlist to doubleclick instead of singleclick.
* Don't edit playlist name on doubleclick in playlists view.
* Make context view top label text selectable.
* Add setting to change Qt style.
* Clear ID3v3 tags that are empty, and clear ID3v1 tags when setting ID3v3 tags.
* Remove remaining uses of QTextCodec.
* Remove Core5Compat dependency.
0.8.3:
Bugfixes:

View File

@@ -1,5 +1,6 @@
:strawberry: Strawberry Music Player [![Build Status](https://github.com/strawberrymusicplayer/strawberry/workflows/C/C++%20CI/badge.svg)](https://github.com/strawberrymusicplayer/strawberry/actions)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
[![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
[![Patreon](https://img.shields.io/badge/patreon-donate-green.svg)](https://patreon.com/jonaskvinge)
=======================
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors and audiophiles. It's written in C++ using the Qt toolkit.
@@ -84,8 +85,6 @@ Optional dependencies:
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:

View File

@@ -9,8 +9,7 @@ if (LSB_RELEASE_EXEC AND DPKG_BUILDPACKAGE)
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (DEB_CODENAME)
configure_file(${CMAKE_SOURCE_DIR}/debian/changelog.in ${CMAKE_SOURCE_DIR}/debian/changelog)
if (DEB_CODENAME AND DEB_DATE)
add_custom_target(deb
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${DPKG_BUILDPACKAGE} -b -d -uc -us

View File

@@ -6,3 +6,10 @@ add_custom_target(dmg
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}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(dmg2
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND create-dmg --skip-jenkins --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}-${MACOS_VERSION_PACKAGE}.dmg strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

@@ -1,7 +1,5 @@
# From http://www.cmake.org/Wiki/CMakeMacroParseArguments
cmake_minimum_required(VERSION 2.6)
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
SET(DEFAULT_ARGS)
FOREACH(arg_name ${arg_names})

View File

@@ -17,6 +17,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (DIST_NAME)
message(STATUS "Distro Name: ${DIST_NAME}")
if (DIST_RELEASE)
message(STATUS "Distro Release: ${DIST_RELEASE}")
@@ -24,45 +25,40 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
if (DIST_VERSION)
message(STATUS "Distro Version: ${DIST_VERSION}")
endif()
set(RPMBUILD_DIR ~/rpmbuild CACHE STRING "Rpmbuild directory, for the rpm target")
if (${DIST_NAME} STREQUAL "opensuse")
if (DIST_RELEASE)
if (${DIST_RELEASE} STREQUAL "leap")
if (DIST_VERSION)
set(RPM_DISTRO "lp${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
else()
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
endif()
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
set(RPM_DISTRO ${DIST_RELEASE} CACHE STRING "Suffix of the rpm file")
else ()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
if (${DIST_NAME} STREQUAL "opensuse" AND DIST_RELEASE)
if (${DIST_RELEASE} STREQUAL "leap")
if (DIST_VERSION)
set(RPM_DISTRO "lp${DIST_VERSION}")
else()
set(RPM_DISTRO ${DIST_RELEASE})
endif()
else()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
elseif (${DIST_RELEASE} STREQUAL "tumbleweed")
set(RPM_DISTRO ${DIST_RELEASE})
endif()
elseif (${DIST_NAME} STREQUAL "fedora")
if (DIST_VERSION)
set(RPM_DISTRO "fc${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
else ()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()
elseif (${DIST_NAME} STREQUAL "mageia")
if (DIST_RELEASE)
set(RPM_DISTRO "mga${DIST_RELEASE}" CACHE STRING "Suffix of the rpm file")
else ()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()
else()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
elseif (${DIST_NAME} STREQUAL "fedora" AND DIST_VERSION)
set(RPM_DISTRO "fc${DIST_VERSION}")
elseif (${DIST_NAME} STREQUAL "centos" AND DIST_VERSION)
set(RPM_DISTRO "el${DIST_VERSION}")
elseif (${DIST_NAME} STREQUAL "mageia" AND DIST_RELEASE)
set(RPM_DISTRO "mga${DIST_RELEASE}")
endif()
if(NOT RPM_DISTRO)
set(RPM_DISTRO ${DIST_NAME})
endif()
message(STATUS "RPM Suffix: ${RPM_DISTRO}")
configure_file(${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec.in ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec @ONLY)
add_custom_target(rpm
COMMAND ${CMAKE_SOURCE_DIR}/dist/scripts/maketarball.sh
COMMAND ${CMAKE_COMMAND} -E copy strawberry-${STRAWBERRY_VERSION_PACKAGE}.tar.xz ${RPMBUILD_DIR}/SOURCES/
COMMAND ${RPMBUILD_EXEC} -bs ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
COMMAND ${RPMBUILD_EXEC} -bb ${CMAKE_SOURCE_DIR}/dist/unix/strawberry.spec
)
endif()
endif()

View File

@@ -1,5 +1,3 @@
cmake_minimum_required(VERSION 3.0)
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
message(FATAL_ERROR "Could not find xgettext executable")

View File

@@ -1,6 +1,6 @@
set(STRAWBERRY_VERSION_MAJOR 0)
set(STRAWBERRY_VERSION_MINOR 8)
set(STRAWBERRY_VERSION_PATCH 3)
set(STRAWBERRY_VERSION_PATCH 5)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF)

View File

@@ -19,6 +19,7 @@
<file>style/smartplaylistsearchterm.css</file>
<file>html/oauthsuccess.html</file>
<file>pictures/strawberry.png</file>
<file>pictures/strawberry-grey.png</file>
<file>pictures/strawberry-faded.png</file>
<file>pictures/strawbs.png</file>
<file>pictures/nomusic.png</file>

View File

@@ -72,6 +72,7 @@
<file>icons/128x128/star-grey.png</file>
<file>icons/128x128/star.png</file>
<file>icons/128x128/strawberry.png</file>
<file>icons/128x128/strawberry-grey.png</file>
<file>icons/128x128/tools-wizard.png</file>
<file>icons/128x128/view-choose.png</file>
<file>icons/128x128/view-fullscreen.png</file>
@@ -164,6 +165,7 @@
<file>icons/64x64/star-grey.png</file>
<file>icons/64x64/star.png</file>
<file>icons/64x64/strawberry.png</file>
<file>icons/64x64/strawberry-grey.png</file>
<file>icons/64x64/tools-wizard.png</file>
<file>icons/64x64/view-choose.png</file>
<file>icons/64x64/view-fullscreen.png</file>
@@ -260,6 +262,7 @@
<file>icons/48x48/star-grey.png</file>
<file>icons/48x48/star.png</file>
<file>icons/48x48/strawberry.png</file>
<file>icons/48x48/strawberry-grey.png</file>
<file>icons/48x48/tools-wizard.png</file>
<file>icons/48x48/view-choose.png</file>
<file>icons/48x48/view-fullscreen.png</file>
@@ -356,6 +359,7 @@
<file>icons/32x32/star-grey.png</file>
<file>icons/32x32/star.png</file>
<file>icons/32x32/strawberry.png</file>
<file>icons/32x32/strawberry-grey.png</file>
<file>icons/32x32/tools-wizard.png</file>
<file>icons/32x32/view-choose.png</file>
<file>icons/32x32/view-fullscreen.png</file>
@@ -452,6 +456,7 @@
<file>icons/22x22/star-grey.png</file>
<file>icons/22x22/star.png</file>
<file>icons/22x22/strawberry.png</file>
<file>icons/22x22/strawberry-grey.png</file>
<file>icons/22x22/tools-wizard.png</file>
<file>icons/22x22/view-choose.png</file>
<file>icons/22x22/view-fullscreen.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

6
dist/CMakeLists.txt vendored
View File

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

View File

@@ -88,7 +88,6 @@ GSTREAMER_PLUGINS = [
'libgstosxaudio.dylib',
'libgstplayback.dylib',
'libgstrawparse.dylib',
'libgstrealmedia.dylib',
'libgstreplaygain.dylib',
'libgstsoup.dylib',
'libgstspectrum.dylib',

View File

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

View File

@@ -1,12 +1,15 @@
Name: strawberry
Version: @STRAWBERRY_VERSION_RPM_V@
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
Release: @STRAWBERRY_VERSION_RPM_R@%{?dist}
%else
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
%endif
Summary: A music player and music collection organizer
Group: Applications/Multimedia
Group: Productivity/Multimedia/Sound/Players
License: GPL-3.0+
URL: https://www.strawberrymusicplayer.org/
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%if 0%{?suse_version} && 0%{?suse_version} > 1325
BuildRequires: libboost_headers-devel
@@ -26,14 +29,11 @@ BuildRequires: update-desktop-files
%if 0%{?suse_version}
BuildRequires: appstream-glib
%else
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
BuildRequires: libappstream-glib
%else
%else
BuildRequires: appstream-util
%endif
%endif
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
BuildRequires: cmake(Qt5LinguistTools)
%endif
%endif
BuildRequires: pkgconfig
BuildRequires: pkgconfig(glib-2.0)
@@ -49,15 +49,29 @@ BuildRequires: pkgconfig(sqlite3) >= 3.9
BuildRequires: pkgconfig(taglib)
%endif
BuildRequires: pkgconfig(fftw3)
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Gui)
BuildRequires: pkgconfig(Qt5Widgets)
BuildRequires: pkgconfig(Qt5Concurrent)
BuildRequires: pkgconfig(Qt5Network)
BuildRequires: pkgconfig(Qt5Sql)
BuildRequires: pkgconfig(Qt5X11Extras)
BuildRequires: pkgconfig(Qt5DBus)
BuildRequires: pkgconfig(Qt5Test)
%if "@QT_MAJOR_VERSION@" == "5" && ( 0%{?fedora} || 0%{?rhel_version} || 0%{?centos} )
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Core)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Gui)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Widgets)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Concurrent)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Network)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Sql)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@X11Extras)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@DBus)
BuildRequires: pkgconfig(Qt@QT_MAJOR_VERSION@Test)
%else
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Core)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Gui)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Widgets)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Concurrent)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Network)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Sql)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@DBus)
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@Test)
%endif
%if 0%{?suse_version} || 0%{?fedora_version} || 0%{?mageia}
BuildRequires: cmake(Qt@QT_MAJOR_VERSION@LinguistTools)
%endif
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
BuildRequires: pkgconfig(gstreamer-audio-1.0)
@@ -70,14 +84,17 @@ BuildRequires: pkgconfig(libpulse)
BuildRequires: pkgconfig(libcdio)
BuildRequires: pkgconfig(libgpod-1.0)
BuildRequires: pkgconfig(libmtp)
BuildRequires: pkgconfig(libnotify)
BuildRequires: pkgconfig(libudf)
%if 0%{?suse_version} || 0%{?fedora_version}
BuildRequires: pkgconfig(libvlc)
%endif
%if 0%{?suse_version}
Requires: libQt5Sql5-sqlite
%if "@QT_MAJOR_VERSION@" == "6"
Requires: qt6-sql-sqlite
%endif
%if "@QT_MAJOR_VERSION@" == "5"
Requires: libQt5Sql5-sqlite
%endif
%endif
%description
@@ -107,8 +124,11 @@ Features:
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
%build
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release
%if 0%{?centos} || 0%{?mageia}
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos}
export CXXFLAGS="-fPIC $RPM_OPT_FLAGS"
%endif
%{cmake} -DCMAKE_BUILD_TYPE:STRING=Release -DQT_MAJOR_VERSION=@QT_MAJOR_VERSION@
%if 0%{?centos} || (0%{?mageia} && 0%{?mageia} <= 7)
%make_build
%else
%cmake_build

View File

@@ -98,13 +98,13 @@ Name "${PRODUCT_NAME}"
!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"
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-Debug-x86.exe"
!endif
!else
!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"
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-x86.exe"
!endif
!endif
!endif
@@ -114,13 +114,13 @@ Name "${PRODUCT_NAME}"
!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"
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-Debug-x64.exe"
!endif
!else
!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"
OutFile "${PRODUCT_NAME_SHORT}Setup-${PRODUCT_DISPLAY_VERSION}-Qt5-x64.exe"
!endif
!endif
!endif
@@ -175,6 +175,7 @@ Section "Strawberry" Strawberry
File "strawberry-tagreader.exe"
File "strawberry.ico"
File "sqlite3.exe"
File "gst-launch-1.0.exe"
!ifdef arch_x86
File "libgcc_s_sjlj-1.dll"
@@ -238,7 +239,7 @@ Section "Strawberry" Strawberry
File "libpcre-1.dll"
File "libpcre2-16-0.dll"
File "libpng16-16.dll"
File "libprotobuf-24.dll"
File "libprotobuf-25.dll"
File "libpsl-5.dll"
File "libsoup-2.4-1.dll"
File "libspeex-1.dll"
@@ -266,7 +267,7 @@ Section "Strawberry" Strawberry
File "Qt6Network.dll"
File "Qt6Sql.dll"
File "Qt6Widgets.dll"
File "Qt6WinExtras.dll"
;File "Qt6WinExtras.dll"
File "libqtsparkle-qt6.dll"
!else
File "Qt5Concurrent.dll"
@@ -443,6 +444,7 @@ Section "Uninstall"
Delete "$INSTDIR\strawberry.exe"
Delete "$INSTDIR\strawberry-tagreader.exe"
Delete "$INSTDIR\sqlite3.exe"
Delete "$INSTDIR\gst-launch-1.0.exe"
!ifdef arch_x86
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
@@ -506,7 +508,7 @@ Section "Uninstall"
Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-24.dll"
Delete "$INSTDIR\libprotobuf-25.dll"
Delete "$INSTDIR\libpsl-5.dll"
Delete "$INSTDIR\libqtsparkle-qt5.dll"
Delete "$INSTDIR\libqtsparkle-qt6.dll"

View File

@@ -9,7 +9,6 @@ link_directories(
${GSTREAMER_BASE_LIBRARY_DIRS}
${GSTREAMER_AUDIO_LIBRARY_DIRS}
${FFTW3_LIBRARY_DIRS}
${QtCore_LIBRARY_DIRS}
)
add_library(gstmoodbar STATIC ${SOURCES})
@@ -21,7 +20,6 @@ target_include_directories(gstmoodbar SYSTEM PRIVATE
${GSTREAMER_BASE_INCLUDE_DIRS}
${GSTREAMER_AUDIO_INCLUDE_DIRS}
${FFTW3_INCLUDE_DIR}
${QtCore_INCLUDE_DIRS}
)
target_include_directories(gstmoodbar PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -28,16 +28,12 @@ endif()
link_directories(
${GLIB_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}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-common PRIVATE

View File

@@ -107,10 +107,10 @@ void _MessageHandlerBase::WriteMessage(const QByteArray &data) {
else if (flush_local_socket_) {
((qobject_cast<QLocalSocket*>(device_))->*(flush_local_socket_))();
}
}
void _MessageHandlerBase::DeviceClosed() {
is_device_closed_ = true;
AbortAll();
}

View File

@@ -43,7 +43,7 @@ class QIODevice;
class _MessageHandlerBase : public QObject {
Q_OBJECT
public:
public:
// device can be nullptr, in which case you must call SetDevice before writing any messages.
_MessageHandlerBase(QIODevice *device, QObject *parent);
@@ -52,16 +52,16 @@ public:
// After this is true, messages cannot be sent to the handler any more.
bool is_device_closed() const { return is_device_closed_; }
protected slots:
protected slots:
void WriteMessage(const QByteArray &data);
void DeviceReadyRead();
virtual void DeviceClosed();
protected:
protected:
virtual bool RawMessageArrived(const QByteArray &data) = 0;
virtual void AbortAll() = 0;
protected:
protected:
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
typedef bool (QLocalSocket::*FlushLocalSocket)();
@@ -80,7 +80,7 @@ protected:
// You should subclass this and implement the MessageArrived(MessageType) method.
template <typename MT>
class AbstractMessageHandler : public _MessageHandlerBase {
public:
public:
AbstractMessageHandler(QIODevice *device, QObject *parent);
~AbstractMessageHandler() override { AbortAll(); }
@@ -102,7 +102,7 @@ public:
// Sets the "id" field of reply to the same as the request, and sends the reply on the socket. Used on the worker side.
void SendReply(const MessageType &request, MessageType *reply);
protected:
protected:
// Called when a message is received from the socket.
virtual void MessageArrived(const MessageType &message) { Q_UNUSED(message); }
@@ -110,8 +110,8 @@ protected:
bool RawMessageArrived(const QByteArray &data) override;
void AbortAll() override;
private:
QMap<int, ReplyType*> pending_replies_;
private:
QMap<qint64, ReplyType*> pending_replies_;
};
template <typename MT>
@@ -146,6 +146,7 @@ void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageTy
template<typename MT>
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
MessageType message;
if (!message.ParseFromArray(data.constData(), data.size())) {
return false;
@@ -161,14 +162,17 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
}
return true;
}
template<typename MT>
void AbstractMessageHandler<MT>::AbortAll() {
for (ReplyType *reply : pending_replies_) {
reply->Abort();
}
pending_replies_.clear();
}
#endif // MESSAGEHANDLER_H

View File

@@ -20,6 +20,7 @@
#include <QtGlobal>
#include <QObject>
#include <QThread>
#include <QSemaphore>
#include <QString>
@@ -33,7 +34,7 @@ class _MessageReplyBase : public QObject {
public:
explicit _MessageReplyBase(QObject *parent = nullptr);
virtual int id() const = 0;
virtual qint64 id() const = 0;
bool is_finished() const { return finished_; }
bool is_successful() const { return success_; }
@@ -57,29 +58,27 @@ class _MessageReplyBase : public QObject {
template <typename MessageType>
class MessageReply : public _MessageReplyBase {
public:
explicit MessageReply(const MessageType& request_message, QObject *parent = nullptr);
explicit MessageReply(const MessageType &request_message, QObject *parent = nullptr);
int id() const override { return request_message_.id(); }
const MessageType& request_message() const { return request_message_; }
const MessageType& message() const { return reply_message_; }
qint64 id() const override { return request_message_.id(); }
const MessageType &request_message() const { return request_message_; }
const MessageType &message() const { return reply_message_; }
void SetReply(const MessageType& message);
void SetReply(const MessageType &message);
private:
private:
MessageType request_message_;
MessageType reply_message_;
};
template<typename MessageType>
MessageReply<MessageType>::MessageReply(const MessageType& request_message, QObject *parent)
: _MessageReplyBase(parent)
{
MessageReply<MessageType>::MessageReply(const MessageType &request_message, QObject *parent) : _MessageReplyBase(parent) {
request_message_.MergeFrom(request_message);
}
template<typename MessageType>
void MessageReply<MessageType>::SetReply(const MessageType& message) {
void MessageReply<MessageType>::SetReply(const MessageType &message) {
Q_ASSERT(!finished_);
@@ -90,6 +89,9 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
qLog(Debug) << "Releasing ID" << id() << "(finished)";
semaphore_.release();
// The signal is not always emitted without this.
QThread::usleep(10);
emit Finished(success_);
}

View File

@@ -32,7 +32,7 @@
#include <QQueue>
#include <QString>
#include <QStringList>
#include <QAtomicInt>
#include <QAtomicInteger>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QRandomGenerator>
#endif
@@ -48,11 +48,11 @@ class _WorkerPoolBase : public QObject {
public:
explicit _WorkerPoolBase(QObject *parent = nullptr);
signals:
signals:
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
void WorkerFailedToStart();
protected slots:
protected slots:
virtual void DoStart() {}
virtual void NewConnection() {}
virtual void ProcessError(QProcess::ProcessError) {}
@@ -78,7 +78,7 @@ class WorkerPool : public _WorkerPoolBase {
void SetExecutableName(const QString &executable_name);
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
void SetWorkerCount(int count);
void SetWorkerCount(const int count);
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
// Defaults to QApplication::applicationName().
@@ -93,14 +93,14 @@ class WorkerPool : public _WorkerPoolBase {
// Can be called from any thread.
ReplyType *SendMessageWithReply(MessageType *message);
protected:
protected:
// These are all reimplemented slots, they are called on the WorkerPool's thread.
void DoStart() override;
void NewConnection() override;
void ProcessError(QProcess::ProcessError error) override;
void SendQueuedMessages() override;
private:
private:
struct Worker {
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
@@ -138,7 +138,7 @@ private:
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
HandlerType *NextHandler() const;
private:
private:
QString local_server_name_;
QString executable_name_;
QString executable_path_;
@@ -147,7 +147,7 @@ private:
mutable int next_worker_;
QList<Worker> workers_;
QAtomicInt next_id_;
QAtomicInteger<qint64> next_id_;
QMutex message_queue_mutex_;
QQueue<ReplyType*> message_queue_;
@@ -170,6 +170,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
template <typename HandlerType>
WorkerPool<HandlerType>::~WorkerPool() {
for (const Worker &worker : workers_) {
if (worker.local_socket_ && worker.process_) {
disconnect(worker.process_, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
@@ -193,10 +194,11 @@ WorkerPool<HandlerType>::~WorkerPool() {
for (ReplyType *reply : message_queue_) {
reply->Abort();
}
}
template <typename HandlerType>
void WorkerPool<HandlerType>::SetWorkerCount(int count) {
void WorkerPool<HandlerType>::SetWorkerCount(const int count) {
Q_ASSERT(workers_.isEmpty());
worker_count_ = count;
}
@@ -220,6 +222,7 @@ void WorkerPool<HandlerType>::Start() {
template <typename HandlerType>
void WorkerPool<HandlerType>::DoStart() {
Q_ASSERT(workers_.isEmpty());
Q_ASSERT(!executable_name_.isEmpty());
Q_ASSERT(QThread::currentThread() == thread());
@@ -248,10 +251,12 @@ void WorkerPool<HandlerType>::DoStart() {
workers_ << worker;
}
}
template <typename HandlerType>
void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
Q_ASSERT(QThread::currentThread() == thread());
DeleteQObjectPointerLater(&worker->local_server_);
@@ -284,6 +289,7 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker *worker) {
// Start the process
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
worker->process_->start(executable_path_, QStringList() << worker->local_server_->fullServerName());
}
template <typename HandlerType>
@@ -338,20 +344,24 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
StartOneWorker(worker);
break;
}
}
template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::NewReply(MessageType *message) {
const int id = next_id_.fetchAndAddOrdered(1);
const qint64 id = next_id_.fetchAndAddOrdered(1);
message->set_id(id);
return new ReplyType(*message);
}
template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
ReplyType *reply = NewReply(message);
// Add the pending reply to the queue
@@ -364,10 +374,12 @@ WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
metaObject()->invokeMethod(this, "SendQueuedMessages", Qt::QueuedConnection);
return reply;
}
template <typename HandlerType>
void WorkerPool<HandlerType>::SendQueuedMessages() {
QMutexLocker l(&message_queue_mutex_);
while (!message_queue_.isEmpty()) {
@@ -384,10 +396,12 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
handler->SendRequest(reply);
}
}
template <typename HandlerType>
HandlerType *WorkerPool<HandlerType>::NextHandler() const {
for (int i = 0; i < workers_.count(); ++i) {
const int worker_index = (next_worker_ + i) % workers_.count();
@@ -398,6 +412,7 @@ HandlerType *WorkerPool<HandlerType>::NextHandler() const {
}
return nullptr;
}
#endif // WORKERPOOL_H

View File

@@ -9,7 +9,6 @@ link_directories(
${GLIB_LIBRARY_DIRS}
${PROTOBUF_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
${Qt5Core_LIBRARY_DIRS}
)
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
@@ -17,8 +16,6 @@ add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(libstrawberry-tagreader PRIVATE
@@ -38,7 +35,3 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
${QtNetwork_LIBRARIES}
libstrawberry-common
)
if(BUILD_WITH_QT6)
target_link_libraries(libstrawberry-tagreader PRIVATE Qt6::Core5Compat)
endif()

View File

@@ -84,7 +84,6 @@
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QTextCodec>
#include <QVector>
#include <QtDebug>
@@ -138,6 +137,15 @@ TagReader::~TagReader() {
delete factory_;
}
bool TagReader::IsMediaFile(const QString &filename) const {
qLog(Debug) << "Checking for valid file" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
return !fileref->isNull() && fileref->tag();
}
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
@@ -198,10 +206,10 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
TagLib::Tag *tag = fileref->tag();
if (tag) {
Decode(tag->title(), nullptr, song->mutable_title());
Decode(tag->artist(), nullptr, song->mutable_artist()); // TPE1
Decode(tag->album(), nullptr, song->mutable_album());
Decode(tag->genre(), nullptr, song->mutable_genre());
Decode(tag->title(), song->mutable_title());
Decode(tag->artist(), song->mutable_artist()); // TPE1
Decode(tag->album(), song->mutable_album());
Decode(tag->genre(), song->mutable_genre());
song->set_year(tag->year());
song->set_track(tag->track());
song->set_valid(true);
@@ -214,7 +222,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
// apart, so we keep specific behavior for some formats by adding another "else if" block below.
if (TagLib::Ogg::XiphComment *tag_ogg = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
ParseOggTag(tag_ogg->fieldListMap(), nullptr, &disc, &compilation, song);
ParseOggTag(tag_ogg->fieldListMap(), &disc, &compilation, song);
if (!tag_ogg->pictureList().isEmpty()) {
song->set_art_automatic(kEmbeddedCover);
}
@@ -225,28 +233,28 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
song->set_bitdepth(file_flac->audioProperties()->bitsPerSample());
if (file_flac->xiphComment()) {
ParseOggTag(file_flac->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
ParseOggTag(file_flac->xiphComment()->fieldListMap(), &disc, &compilation, song);
if (!file_flac->pictureList().isEmpty()) {
song->set_art_automatic(kEmbeddedCover);
}
}
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
if (tag) Decode(tag->comment(), song->mutable_comment());
}
else if (TagLib::WavPack::File *file_wavpack = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
song->set_bitdepth(file_wavpack->audioProperties()->bitsPerSample());
if (file_wavpack->APETag()) {
ParseAPETag(file_wavpack->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
ParseAPETag(file_wavpack->APETag()->itemListMap(), &disc, &compilation, song);
}
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
if (tag) Decode(tag->comment(), song->mutable_comment());
}
else if (TagLib::APE::File *file_ape = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
if (file_ape->APETag()) {
ParseAPETag(file_ape->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
ParseAPETag(file_ape->APETag()->itemListMap(), &disc, &compilation, song);
}
song->set_bitdepth(file_ape->audioProperties()->bitsPerSample());
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
if (tag) Decode(tag->comment(), song->mutable_comment());
}
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
@@ -255,22 +263,22 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
const TagLib::ID3v2::FrameListMap &map = file_mpeg->ID3v2Tag()->frameListMap();
if (!map["TPOS"].isEmpty()) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), nullptr, song->mutable_composer());
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), song->mutable_composer());
// content group
if (!map["TIT1"].isEmpty()) Decode(map["TIT1"].front()->toString(), nullptr, song->mutable_grouping());
if (!map["TIT1"].isEmpty()) Decode(map["TIT1"].front()->toString(), song->mutable_grouping());
// ID3v2: lead performer/soloist
if (!map["TPE1"].isEmpty()) Decode(map["TPE1"].front()->toString(), nullptr, song->mutable_performer());
if (!map["TPE1"].isEmpty()) Decode(map["TPE1"].front()->toString(), song->mutable_performer());
// original artist/performer
if (!map["TOPE"].isEmpty()) Decode(map["TOPE"].front()->toString(), nullptr, song->mutable_performer());
if (!map["TOPE"].isEmpty()) Decode(map["TOPE"].front()->toString(), song->mutable_performer());
// Skip TPE1 (which is the artist) here because we already fetched it
// non-standard: Apple, Microsoft
if (!map["TPE2"].isEmpty()) Decode(map["TPE2"].front()->toString(), nullptr, song->mutable_albumartist());
if (!map["TPE2"].isEmpty()) Decode(map["TPE2"].front()->toString(), song->mutable_albumartist());
if (!map["TCMP"].isEmpty()) compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
@@ -280,20 +288,20 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
}
if (!map["USLT"].isEmpty()) {
Decode(map["USLT"].front()->toString(), nullptr, song->mutable_lyrics());
Decode(map["USLT"].front()->toString(), song->mutable_lyrics());
}
else if (!map["SYLT"].isEmpty()) {
Decode(map["SYLT"].front()->toString(), nullptr, song->mutable_lyrics());
Decode(map["SYLT"].front()->toString(), song->mutable_lyrics());
}
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
// Find a suitable comment tag. For now we ignore iTunNORM comments.
for (uint i = 0; i < map["COMM"].size(); ++i) {
for (uint i = 0 ; i < map["COMM"].size() ; ++i) {
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
Decode(frame->text(), nullptr, song->mutable_comment());
Decode(frame->text(), song->mutable_comment());
break;
}
}
@@ -312,7 +320,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
if (mp4_tag->item("aART").isValid()) {
TagLib::StringList album_artists = mp4_tag->item("aART").toStringList();
if (!album_artists.isEmpty()) {
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
Decode(album_artists.front(), song->mutable_albumartist());
}
}
@@ -326,20 +334,20 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
}
if (mp4_tag->item("\251wrt").isValid()) {
Decode(mp4_tag->item("\251wrt").toStringList().toString(", "), nullptr, song->mutable_composer());
Decode(mp4_tag->item("\251wrt").toStringList().toString(", "), song->mutable_composer());
}
if (mp4_tag->item("\251grp").isValid()) {
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), nullptr, song->mutable_grouping());
Decode(mp4_tag->item("\251grp").toStringList().toString(" "), song->mutable_grouping());
}
if (mp4_tag->item("\251lyr").isValid()) {
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), nullptr, song->mutable_lyrics());
Decode(mp4_tag->item("\251lyr").toStringList().toString(" "), 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());
}
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
Decode(mp4_tag->comment(), song->mutable_comment());
}
}
@@ -348,7 +356,7 @@ 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());
Decode(file_asf->tag()->comment(), song->mutable_comment());
}
const TagLib::ASF::AttributeListMap &attributes_map = file_asf->tag()->attributeListMap();
@@ -367,15 +375,15 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
}
}
else if (TagLib::MPC::File* file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
else if (TagLib::MPC::File *file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
if (file_mpc->APETag()) {
ParseAPETag(file_mpc->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
ParseAPETag(file_mpc->APETag()->itemListMap(), &disc, &compilation, song);
}
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
if (tag) Decode(tag->comment(), song->mutable_comment());
}
else if (tag) {
Decode(tag->comment(), nullptr, song->mutable_comment());
Decode(tag->comment(), song->mutable_comment());
}
if (!disc.isEmpty()) {
@@ -414,42 +422,27 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
}
void TagReader::Decode(const TagLib::String &tag, const QTextCodec *codec, std::string *output) {
QString tmp;
if (codec && tag.isLatin1()) { // Never override UTF-8.
const std::string fixed = QString::fromUtf8(tag.toCString(true)).toStdString();
tmp = codec->toUnicode(fixed.c_str()).trimmed();
}
else {
tmp = TStringToQString(tag).trimmed();
}
void TagReader::Decode(const TagLib::String &tag, std::string *output) {
QString tmp = TStringToQString(tag).trimmed();
output->assign(DataCommaSizeFromQString(tmp));
}
void TagReader::Decode(const QString &tag, const QTextCodec *codec, std::string *output) {
void TagReader::Decode(const QString &tag, std::string *output) {
if (!codec) {
output->assign(DataCommaSizeFromQString(tag));
}
else {
const QString decoded(codec->toUnicode(tag.toUtf8()));
output->assign(DataCommaSizeFromQString(decoded));
}
output->assign(DataCommaSizeFromQString(tag));
}
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), song->mutable_grouping());
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist());
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), song->mutable_albumartist());
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), song->mutable_albumartist());
if (!map["ORIGINALDATE"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
else if (!map["ORIGINALYEAR"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALYEAR"].front()).toInt());
@@ -461,20 +454,18 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCod
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), codec, song->mutable_lyrics());
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), codec, song->mutable_lyrics());
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), song->mutable_lyrics());
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), song->mutable_lyrics());
}
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
Q_UNUSED(codec);
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
if (it != map.end()) {
TagLib::StringList album_artists = it->second.values();
if (!album_artists.isEmpty()) {
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
Decode(album_artists.front(), song->mutable_albumartist());
}
}
@@ -488,19 +479,19 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCode
}
if (map.contains("PERFORMER")) {
Decode(map["PERFORMER"].values().toString(", "), nullptr, song->mutable_performer());
Decode(map["PERFORMER"].values().toString(", "), song->mutable_performer());
}
if (map.contains("COMPOSER")) {
Decode(map["COMPOSER"].values().toString(", "), nullptr, song->mutable_composer());
Decode(map["COMPOSER"].values().toString(", "), song->mutable_composer());
}
if (map.contains("GROUPING")) {
Decode(map["GROUPING"].values().toString(" "), nullptr, song->mutable_grouping());
Decode(map["GROUPING"].values().toString(" "), song->mutable_grouping());
}
if (map.contains("LYRICS")) {
Decode(map["LYRICS"].toString(), nullptr, song->mutable_lyrics());
Decode(map["LYRICS"].toString(), song->mutable_lyrics());
}
if (map.contains("FMPS_PLAYCOUNT")) {
@@ -517,8 +508,8 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true);
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true);
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
vorbis_comments->addField("COMPILATION", QStringToTaglibString(song.compilation() ? "1" : QString()), true);
// Try to be coherent, the two forms are used but the first one is preferred
@@ -538,13 +529,15 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
if (!fileref || fileref->isNull()) return false;
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
fileref->tag()->setArtist(StdStringToTaglibString(song.artist()));
fileref->tag()->setAlbum(StdStringToTaglibString(song.album()));
fileref->tag()->setGenre(StdStringToTaglibString(song.genre()));
fileref->tag()->setComment(StdStringToTaglibString(song.comment()));
fileref->tag()->setYear(song.year());
fileref->tag()->setTrack(song.track());
fileref->tag()->setTitle(song.title().empty() ? TagLib::String() : StdStringToTaglibString(song.title()));
fileref->tag()->setArtist(song.artist().empty() ? TagLib::String() : StdStringToTaglibString(song.artist()));
fileref->tag()->setAlbum(song.album().empty() ? TagLib::String() : StdStringToTaglibString(song.album()));
fileref->tag()->setGenre(song.genre().empty() ? TagLib::String() : StdStringToTaglibString(song.genre()));
fileref->tag()->setComment(song.comment().empty() ? TagLib::String() : StdStringToTaglibString(song.comment()));
fileref->tag()->setYear(song.year() <= 0 ? 0 : song.year());
fileref->tag()->setTrack(song.track() <= 0 ? 0 : song.track());
bool result = false;
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
TagLib::Ogg::XiphComment *tag = file->xiphComment();
@@ -572,14 +565,14 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
if (!tag) return false;
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
SetTextFrame("TCOM", song.composer(), tag);
SetTextFrame("TIT1", song.grouping(), tag);
SetTextFrame("TOPE", song.performer(), tag);
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
// Skip TPE1 (which is the artist) here because we already set it
SetTextFrame("TPE2", song.albumartist(), tag);
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
SetUnsyncLyricsFrame(song.lyrics(), tag);
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
}
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
@@ -598,53 +591,26 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
SetVorbisComments(tag, song);
}
bool ret = fileref->save();
result = fileref->save();
#ifdef Q_OS_LINUX
if (ret) {
if (result) {
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
}
#endif // Q_OS_LINUX
return ret;
return result;
}
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
tag->setItem("disc", TagLib::APE::Item("disc", TagLib::String::number(song.disc() <= 0 - 1 ? 0 : song.disc())));
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
tag->setItem("compilation", TagLib::APE::Item("compilation", TagLib::StringList(song.compilation() ? "1" : "0")));
}
void TagReader::SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const {
const QByteArray descr_utf8(description.toUtf8());
const QByteArray value_utf8(value.toUtf8());
qLog(Debug) << "Setting FMPSFrame:" << description << ", " << value;
SetUserTextFrame(std::string(descr_utf8.constData(), descr_utf8.length()), std::string(value_utf8.constData(), value_utf8.length()), tag);
}
void TagReader::SetUserTextFrame(const std::string &description, const std::string &value, TagLib::ID3v2::Tag *tag) const {
const TagLib::String t_description = StdStringToTaglibString(description);
// Remove the frame if it already exists
TagLib::ID3v2::UserTextIdentificationFrame *frame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, t_description);
if (frame) {
tag->removeFrame(frame);
}
// Create and add a new frame
frame = new TagLib::ID3v2::UserTextIdentificationFrame(TagLib::String::UTF8);
frame->setDescription(t_description);
frame->setText(StdStringToTaglibString(value));
tag->addFrame(frame);
tag->addValue("compilation", QStringToTaglibString(song.compilation() ? QString::number(1) : QString()), true);
}
@@ -652,6 +618,7 @@ void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2
const QByteArray utf8(value.toUtf8());
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
}
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
@@ -665,6 +632,8 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
tag->removeFrame(tag->frameListMap()[id_vector].front());
}
if (value.empty()) return;
// If no frames stored create empty frame
if (frames_buffer.isEmpty()) {
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
@@ -672,9 +641,9 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
}
// Update and add the frames
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(lyrics_index));
if (lyrics_index == 0) {
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(i));
if (i == 0) {
frame->setText(StdStringToTaglibString(value));
}
// add frame takes ownership and clears the memory
@@ -683,15 +652,6 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
}
bool TagReader::IsMediaFile(const QString &filename) const {
qLog(Debug) << "Checking for valid file" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
return !fileref->isNull() && fileref->tag();
}
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
if (filename.isEmpty()) return QByteArray();
@@ -711,8 +671,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
if (flac_file && flac_file->xiphComment()) {
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
if (!pics.isEmpty()) {
// Use the first picture in the file - this could be made cleverer and
// pick the front cover if it's present.
// Use the first picture in the file - this could be made cleverer and pick the front cover if it's present.
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
TagLib::FLAC::Picture *picture = *it;
@@ -817,7 +776,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
}
void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const {
void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
TagLib::ByteVector id_vector("USLT");
QVector<TagLib::ByteVector> frames_buffer;
@@ -828,6 +787,8 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
tag->removeFrame(tag->frameListMap()[id_vector].front());
}
if (value.empty()) return;
// If no frames stored create empty frame
if (frames_buffer.isEmpty()) {
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
@@ -836,9 +797,9 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
}
// Update and add the frames
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(lyrics_index));
if (lyrics_index == 0) {
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
TagLib::ID3v2::UnsynchronizedLyricsFrame *frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(i));
if (i == 0) {
frame->setText(StdStringToTaglibString(value));
}
// add frame takes ownership and clears the memory

View File

@@ -35,8 +35,6 @@
#include "tagreadermessages.pb.h"
class QTextCodec;
#ifndef USE_SYSTEM_TAGLIB
using namespace Strawberry_TagLib;
#endif
@@ -52,27 +50,24 @@ class TagReader {
explicit TagReader();
~TagReader();
bool IsMediaFile(const QString &filename) const;
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
bool IsMediaFile(const QString &filename) const;
QByteArray LoadEmbeddedArt(const QString &filename) const;
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
static void Decode(const TagLib::String& tag, const QTextCodec *codec, std::string *output);
static void Decode(const QString &tag, const QTextCodec *codec, std::string *output);
static void Decode(const TagLib::String &tag, std::string *output);
static void Decode(const QString &tag, std::string *output);
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
void ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
void ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;

View File

@@ -101,7 +101,7 @@ message LoadEmbeddedArtResponse {
}
message Message {
optional int32 id = 1;
optional int64 id = 1;
optional ReadFileRequest read_file_request = 2;
optional ReadFileResponse read_file_response = 3;

View File

@@ -15,8 +15,6 @@ endif()
link_directories(
${GLIB_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
${QtCore_LIBRARY_DIRS}
${QtNetwork_LIBRARY_DIRS}
)
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
@@ -24,8 +22,6 @@ add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
${GLIB_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
${QtCore_INCLUDE_DIRS}
${QtNetwork_INCLUDE_DIRS}
)
target_include_directories(strawberry-tagreader PRIVATE

View File

@@ -27,11 +27,9 @@
#include "tagreaderworker.h"
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent)
{
}
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent) {}
void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
void TagReaderWorker::MessageArrived(const pb::tagreader::Message &message) {
pb::tagreader::Message reply;
@@ -51,6 +49,7 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
}
SendReply(message, &reply);
}

View File

@@ -29,14 +29,14 @@
class QIODevice;
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
public:
public:
explicit TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
protected:
protected:
void MessageArrived(const pb::tagreader::Message &message) override;
void DeviceClosed() override;
private:
private:
TagReader tag_reader_;
};

View File

@@ -19,6 +19,7 @@ set(SOURCES
core/multisortfilterproxy.cpp
core/musicstorage.cpp
core/network.cpp
core/threadsafenetworkdiskcache.cpp
core/networktimeouts.cpp
core/networkproxyfactory.cpp
core/qtfslistener.cpp
@@ -257,6 +258,7 @@ set(HEADERS
core/filesystemwatcherinterface.h
core/mergedproxymodel.h
core/network.h
core/threadsafenetworkdiskcache.h
core/networktimeouts.h
core/qtfslistener.h
core/songloader.h
@@ -548,19 +550,19 @@ if(HAVE_GLOBALSHORTCUTS)
HEADERS globalshortcuts/globalshortcuts.h globalshortcuts/globalshortcutbackend.h globalshortcuts/globalshortcutgrabber.h settings/shortcutssettingspage.h
UI globalshortcuts/globalshortcutgrabber.ui settings/shortcutssettingspage.ui
)
if (X11_FOUND OR WIN32)
if(HAVE_X11EXTRAS OR WIN32)
set(X11_OR_WIN ON)
endif()
optional_source(X11_OR_WIN
SOURCES globalshortcuts/globalshortcutbackend-system.cpp globalshortcuts/globalshortcut.cpp
HEADERS globalshortcuts/globalshortcutbackend-system.h globalshortcuts/globalshortcut.h
)
optional_source(X11_FOUND
optional_source(HAVE_X11EXTRAS
SOURCES globalshortcuts/globalshortcut-x11.cpp
)
optional_source(HAVE_DBUS
SOURCES globalshortcuts/globalshortcutbackend-gsd.cpp
HEADERS globalshortcuts/globalshortcutbackend-gsd.h
SOURCES globalshortcuts/globalshortcutbackend-gsd.cpp globalshortcuts/globalshortcutbackend-kde.cpp
HEADERS globalshortcuts/globalshortcutbackend-gsd.h globalshortcuts/globalshortcutbackend-kde.h
)
optional_source(WIN32
SOURCES globalshortcuts/globalshortcut-win.cpp
@@ -634,6 +636,14 @@ if(UNIX AND HAVE_DBUS)
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
# org.kde.KGlobalAccel interface
qt6_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.xml
dbus/kglobalaccel)
qt6_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.Component.xml
dbus/kglobalaccelcomponent)
else()
# MPRIS 2.0 DBUS interfaces
@@ -662,6 +672,14 @@ if(UNIX AND HAVE_DBUS)
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
dbus/gnomesettingsdaemon)
# org.kde.KGlobalAccel interface
qt5_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.xml
dbus/kglobalaccel)
qt5_add_dbus_interface(SOURCES
dbus/org.kde.KGlobalAccel.Component.xml
dbus/kglobalaccelcomponent)
endif()
# org.freedesktop.Avahi.Server interface
@@ -1033,7 +1051,6 @@ link_directories(
${GNUTLS_LIBRARY_DIRS}
${SQLITE_LIBRARY_DIRS}
${TAGLIB_LIBRARY_DIRS}
${QT_LIBRARY_DIRS}
${SINGLEAPPLICATION_LIBRARY_DIRS}
${SINGLECOREAPPLICATION_LIBRARY_DIRS}
${QTSPARKLE_LIBRARY_DIRS}
@@ -1108,7 +1125,6 @@ target_include_directories(strawberry_lib SYSTEM PUBLIC
${GOBJECT_INCLUDE_DIRS}
${GNUTLS_INCLUDE_DIRS}
${SQLITE_INCLUDE_DIRS}
${QT_INCLUDE_DIRS}
)
target_include_directories(strawberry_lib PUBLIC

View File

@@ -110,7 +110,7 @@ void SCollection::Init() {
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
connect(app_->player(), SIGNAL(Stopped()), SLOT(Stopped()));
connect(app_->lastfm_import(), SIGNAL(UpdateLastPlayed(QString, QString, QString, int)), backend_, SLOT(UpdateLastPlayed(QString, QString, QString, int)));
connect(app_->lastfm_import(), SIGNAL(UpdateLastPlayed(QString, QString, QString, qint64)), backend_, SLOT(UpdateLastPlayed(QString, QString, QString, qint64)));
connect(app_->lastfm_import(), SIGNAL(UpdatePlayCount(QString, QString, int)), backend_, SLOT(UpdatePlayCount(QString, QString, int)));
// This will start the watcher checking for updates

View File

@@ -1368,7 +1368,7 @@ SongList CollectionBackend::GetSongsBy(const QString &artist, const QString &alb
}
void CollectionBackend::UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const int lastplayed) {
void CollectionBackend::UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed) {
SongList songs = GetSongsBy(artist, album, title);
if (songs.isEmpty()) {

View File

@@ -213,7 +213,7 @@ class CollectionBackend : public CollectionBackendInterface {
void SongPathChanged(const Song &song, const QFileInfo &new_file);
SongList GetSongsBy(const QString &artist, const QString &album, const QString &title);
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const int lastplayed);
void UpdateLastPlayed(const QString &artist, const QString &album, const QString &title, const qint64 lastplayed);
void UpdatePlayCount(const QString &artist, const QString &title, const int playcount);
void UpdateSongRating(const int id, const float rating);

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@
#include <QList>
#include <QSet>
#include <QMap>
#include <QMetaType>
#include <QVariant>
#include <QByteArray>
#include <QString>
@@ -892,7 +893,11 @@ void CollectionModel::LazyPopulate(CollectionItem *parent, const bool signal) {
}
void CollectionModel::ResetAsync() {
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(std::bind(&CollectionModel::RunQuery, this, root_));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(&CollectionModel::RunQuery, this, root_);
#else
QFuture<CollectionModel::QueryResult> future = QtConcurrent::run(this, &CollectionModel::RunQuery, root_);
#endif
NewClosure(future, this, SLOT(ResetAsyncQueryFinished(QFuture<CollectionModel::QueryResult>)), future);
}
@@ -1566,8 +1571,9 @@ void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bo
// Create the divider entry if we're supposed to
if (create_divider && show_dividers_) {
QString divider_key = DividerKey(type, item);
if (!divider_key.isEmpty() && (item->sort_text.isEmpty() || item->sort_text[0].toLower() != divider_key || divider_key[0].isDigit()))
item->sort_text.prepend(divider_key);
if (!divider_key.isEmpty()) {
item->sort_text.prepend(divider_key + " ");
}
if (!divider_key.isEmpty() && !divider_nodes_.contains(divider_key)) {
if (signal)
@@ -1576,7 +1582,7 @@ void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bo
CollectionItem *divider = new CollectionItem(CollectionItem::Type_Divider, root_);
divider->key = divider_key;
divider->display_text = DividerDisplayText(type, divider_key);
divider->sort_text = divider_key + "0000";
divider->sort_text = divider_key + " ";
divider->lazy_loaded = true;
divider_nodes_[divider_key] = divider;
@@ -1730,8 +1736,14 @@ bool CollectionModel::CompareItems(const CollectionItem *a, const CollectionItem
QVariant left(data(a, CollectionModel::Role_SortText));
QVariant right(data(b, CollectionModel::Role_SortText));
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
return left.toString() < right.toString();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (left.metaType().id() == QMetaType::Int)
#else
if (left.type() == QVariant::Int)
#endif
return left.toInt() < right.toInt();
else
return left.toString() < right.toString();
}

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -21,6 +21,7 @@
#include "config.h"
#include <QtGlobal>
#include <QMetaType>
#include <QDateTime>
#include <QVariant>
#include <QString>
@@ -133,10 +134,20 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
}
else {
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (value.metaType().id() == QMetaType::Int) {
#else
if (value.type() == QVariant::Int) {
#endif
where_clauses_ << QString("%1 %2 %3").arg(column, op, value.toString());
}
else if (value.type() == QVariant::String && value.toString().isNull()) {
else if (
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
value.metaType().id() == QMetaType::QString
#else
value.type() == QVariant::String
#endif
&& value.toString().isNull()) {
where_clauses_ << QString("%1 %2 ?").arg(column, op);
bound_values_ << QString("");
}

View File

@@ -76,7 +76,6 @@ CollectionWatcher::CollectionWatcher(Song::Source source, QObject *parent)
scan_on_startup_(true),
monitor_(true),
mark_songs_unavailable_(false),
live_scanning_(false),
stop_requested_(false),
rescan_in_progress_(false),
rescan_timer_(new QTimer(this)),
@@ -480,8 +479,6 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
t->AddToProgress(1);
if (live_scanning_) t->CommitNewOrUpdatedSongs();
// Recurse into the new subdirs that we found
t->AddToProgressMax(my_new_subdirs.count());
for (const Subdirectory &my_new_subdir : my_new_subdirs) {

View File

@@ -195,7 +195,6 @@ class CollectionWatcher : public QObject {
bool scan_on_startup_;
bool monitor_;
bool mark_songs_unavailable_;
bool live_scanning_;
bool stop_requested_;
bool rescan_in_progress_; // True if RescanTracksNow() has been called and is working.

View File

@@ -64,4 +64,8 @@
#cmakedefine INSTALL_TRANSLATIONS
#define TRANSLATIONS_DIR "${CMAKE_INSTALL_PREFIX}/share/strawberry/translations"
#cmakedefine HAVE_X11EXTRAS
#cmakedefine HAVE_WINEXTRAS
#cmakedefine HAVE_QPA_QPLATFORMNATIVEINTERFACE_H
#endif // CONFIG_H_IN

View File

@@ -28,6 +28,7 @@
#include <QtGlobal>
#include <QMutex>
#include <QMimeData>
#include <QMetaType>
#include <QVariant>
#include <QList>
#include <QSet>
@@ -444,8 +445,13 @@ bool ContextAlbumsModel::CompareItems(const CollectionItem *a, const CollectionI
QVariant left(data(a, ContextAlbumsModel::Role_SortText));
QVariant right(data(b, ContextAlbumsModel::Role_SortText));
if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
return left.toString() < right.toString();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (left.metaType().id() == QMetaType::Int)
#else
if (left.type() == QVariant::Int)
#endif
return left.toInt() < right.toInt();
else return left.toString() < right.toString();
}

View File

@@ -397,7 +397,7 @@ void ContextAlbumsView::EditTracks() {
void ContextAlbumsView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organize_dialog_)
organize_dialog_.reset(new OrganizeDialog(app_->task_manager()));
organize_dialog_.reset(new OrganizeDialog(app_->task_manager(), nullptr, this));
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organize_dialog_->SetCopy(true);

View File

@@ -151,6 +151,7 @@ ContextView::ContextView(QWidget *parent) :
label_top_->setWordWrap(true);
label_top_->setMinimumHeight(50);
label_top_->setContentsMargins(0, 0, 32, 0);
label_top_->setTextInteractionFlags(Qt::TextSelectableByMouse);
layout_scrollarea_->setObjectName("context-layout-scrollarea");
layout_scrollarea_->setContentsMargins(15, 15, 15, 15);
@@ -485,7 +486,7 @@ void ContextView::UpdateFonts() {
void ContextView::SetSong() {
label_top_->setStyleSheet(QString("font: %2pt \"%1\"; font-weight: regular;").arg(font_headline_).arg(font_size_headline_));
label_top_->setText(QString("<b>%1</b><br/>%2").arg(Utilities::ReplaceMessage(title_fmt_, song_playing_, "<br/>"), Utilities::ReplaceMessage(summary_fmt_, song_playing_, "<br/>")));
label_top_->setText(QString("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song_playing_, "<br />", true), Utilities::ReplaceMessage(summary_fmt_, song_playing_, "<br />", true)));
label_stop_summary_->clear();
@@ -643,7 +644,7 @@ void ContextView::SetSong() {
void ContextView::UpdateSong(const Song &song) {
label_top_->setText(QString("<b>%1</b><br/>%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br/>"), Utilities::ReplaceMessage(summary_fmt_, song, "<br/>")));
label_top_->setText(QString("<b>%1</b><br />%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br />", true), Utilities::ReplaceMessage(summary_fmt_, song, "<br />", true)));
if (action_show_data_->isChecked()) {
if (song.filetype() != song_playing_.filetype()) label_filetype_->setText(song.TextForFiletype());

View File

@@ -44,36 +44,37 @@ const char *CommandlineOptions::kHelpText =
"%1: strawberry [%2] [%3]\n"
"\n"
"%4:\n"
" -p, --play %5\n"
" -t, --play-pause %6\n"
" -u, --pause %7\n"
" -s, --stop %8\n"
" -q, --stop-after-current %9\n"
" -r, --previous %10\n"
" -f, --next %11\n"
" -v, --volume <value> %12\n"
" --volume-up %13\n"
" --volume-down %14\n"
" --volume-increase-by %15\n"
" --volume-decrease-by %16\n"
" --seek-to <seconds> %17\n"
" --seek-by <seconds> %18\n"
" --restart-or-previous %19\n"
" -p, --play %5\n"
" -t, --play-pause %6\n"
" -u, --pause %7\n"
" -s, --stop %8\n"
" -q, --stop-after-current %9\n"
" -r, --previous %10\n"
" -f, --next %11\n"
" -v, --volume <value> %12\n"
" --volume-up %13\n"
" --volume-down %14\n"
" --volume-increase-by %15\n"
" --volume-decrease-by %16\n"
" --seek-to <seconds> %17\n"
" --seek-by <seconds> %18\n"
" --restart-or-previous %19\n"
"\n"
"%20:\n"
" -c, --create <name> %21\n"
" -a, --append %22\n"
" -l, --load %23\n"
" -k, --play-track <n> %24\n"
" -c, --create <name> %21\n"
" -a, --append %22\n"
" -l, --load %23\n"
" -k, --play-track <n> %24\n"
" -i, --play-playlist <name> %25\n"
"\n"
"%25:\n"
" -o, --show-osd %26\n"
" -y, --toggle-pretty-osd %27\n"
" -g, --language <lang> %28\n"
" --quiet %29\n"
" --verbose %30\n"
" --log-levels <levels> %31\n"
" --version %32\n";
"%26:\n"
" -o, --show-osd %27\n"
" -y, --toggle-pretty-osd %28\n"
" -g, --language <lang> %29\n"
" --quiet %30\n"
" --verbose %31\n"
" --log-levels <levels> %32\n"
" --version %33\n";
const char *CommandlineOptions::kVersionText = "Strawberry %1";
@@ -138,6 +139,7 @@ bool CommandlineOptions::Parse() {
{"append", no_argument, nullptr, 'a'},
{"load", no_argument, nullptr, 'l'},
{"play-track", required_argument, nullptr, 'k'},
{"play-playlist", required_argument, nullptr, 'i'},
{"show-osd", no_argument, nullptr, 'o'},
{"toggle-pretty-osd", no_argument, nullptr, 'y'},
{"language", required_argument, nullptr, 'g'},
@@ -150,7 +152,7 @@ bool CommandlineOptions::Parse() {
// Parse the arguments
bool ok = false;
forever {
int c = getopt_long(argc_, argv_, "hptusqrfv:c:alk:oyg:", kOptions, nullptr);
int c = getopt_long(argc_, argv_, "hptusqrfv:c:alk:i:oyg:", kOptions, nullptr);
// End of the options
if (c == -1) break;
@@ -179,7 +181,8 @@ bool CommandlineOptions::Parse() {
tr("Create a new playlist with files"),
tr("Append files/URLs to the playlist"),
tr("Loads files/URLs, replacing current playlist"),
tr("Play the <n>th track in the playlist"))
tr("Play the <n>th track in the playlist"),
tr("Play given playlist"))
.arg(tr("Other options"), tr("Display the on-screen-display"),
tr("Toggle visibility for the pretty on-screen-display"),
tr("Change the language"),
@@ -213,6 +216,10 @@ bool CommandlineOptions::Parse() {
case 'f':
player_action_ = Player_Next;
break;
case 'i':
player_action_ = Player_PlayPlaylist;
playlist_name_ = QString(optarg);
break;
case 'c':
url_list_action_ = UrlList_CreateNew;
playlist_name_ = QString(optarg);
@@ -362,7 +369,8 @@ QDataStream& operator<<(QDataStream &s, const CommandlineOptions &a) {
<< a.show_osd_
<< a.urls_
<< a.log_levels_
<< a.toggle_pretty_osd_;
<< a.toggle_pretty_osd_
<< a.playlist_name_;
return s;
@@ -382,7 +390,8 @@ QDataStream& operator>>(QDataStream &s, CommandlineOptions &a) {
>> a.show_osd_
>> a.urls_
>> a.log_levels_
>> a.toggle_pretty_osd_;
>> a.toggle_pretty_osd_
>> a.playlist_name_;
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);

View File

@@ -57,6 +57,7 @@ class CommandlineOptions {
Player_Next = 6,
Player_RestartOrPrevious = 7,
Player_StopAfterCurrent = 8,
Player_PlayPlaylist = 9,
};
bool Parse();

View File

@@ -132,6 +132,7 @@ QSqlDatabase Database::Connect() {
if (db.isOpen()) {
return db;
}
db.setConnectOptions("QSQLITE_BUSY_TIMEOUT=30000");
//qLog(Debug) << "Opened database with connection id" << connection_id;
if (!injected_database_name_.isNull())
@@ -518,11 +519,13 @@ void Database::DoBackup() {
QSqlDatabase db(this->Connect());
if (!db.isOpen()) return;
// Before we overwrite anything, make sure the database is not corrupt
QMutexLocker l(&mutex_);
const bool ok = IntegrityCheck(db);
if (ok) {
const bool ok = IntegrityCheck(db);
if (ok && SchemaVersion(&db) == kSchemaVersion) {
BackupFile(db.databaseName());
}

View File

@@ -58,6 +58,7 @@ protected:
private:
QPixmap normal_icon_;
QPixmap grey_icon_;
std::unique_ptr<MacSystemTrayIconPrivate> p_;
Q_DISABLE_COPY(MacSystemTrayIcon);
};

View File

@@ -165,7 +165,8 @@ class MacSystemTrayIconPrivate {
MacSystemTrayIcon::MacSystemTrayIcon(QObject* parent)
: SystemTrayIcon(parent),
normal_icon_(QPixmap(":/pictures/strawberry.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)) {
normal_icon_(QPixmap(":/pictures/strawberry.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)),
grey_icon_(QPixmap(":/pictures/strawberry-grey.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)) {
QApplication::setWindowIcon(normal_icon_);
}

View File

@@ -1023,6 +1023,7 @@ void MainWindow::ReloadSettings() {
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
keep_running_ = s.value("keeprunning", false).toBool();
playing_widget_ = s.value("playing_widget", true).toBool();
bool trayicon_progress = s.value("trayicon_progress", false).toBool();
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
doubleclick_addmode_ = BehaviourSettingsPage::AddBehaviour(s.value("doubleclick_addmode", BehaviourSettingsPage::AddBehaviour_Append).toInt());
doubleclick_playmode_ = BehaviourSettingsPage::PlayBehaviour(s.value("doubleclick_playmode", BehaviourSettingsPage::PlayBehaviour_Never).toInt());
@@ -1034,6 +1035,8 @@ void MainWindow::ReloadSettings() {
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlayControlButtons, 32).toInt();
s.endGroup();
if (tray_icon_) tray_icon_->SetTrayiconProgress(trayicon_progress);
ui_->back_button->setIconSize(QSize(iconsize, iconsize));
ui_->pause_play_button->setIconSize(QSize(iconsize, iconsize));
ui_->stop_button->setIconSize(QSize(iconsize, iconsize));
@@ -1461,21 +1464,22 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &idx) {
if (!idx.isValid()) return;
int row = idx.row();
QModelIndex source_idx = idx;
if (idx.model() == app_->playlist_manager()->current()->proxy()) {
// The index was in the proxy model (might've been filtered), so we need to get the actual row in the source model.
row = app_->playlist_manager()->current()->proxy()->mapToSource(idx).row();
source_idx = app_->playlist_manager()->current()->proxy()->mapToSource(idx);
}
switch (doubleclick_playlist_addmode_) {
case BehaviourSettingsPage::PlaylistAddBehaviour_Play:
app_->playlist_manager()->SetActiveToCurrent();
app_->player()->PlayAt(row, Engine::Manual, Playlist::AutoScroll_Never, true, true);
app_->player()->PlayAt(source_idx.row(), Engine::Manual, Playlist::AutoScroll_Never, true, true);
break;
case BehaviourSettingsPage::PlaylistAddBehaviour_Enqueue:
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << idx);
app_->playlist_manager()->current()->queue()->ToggleTracks(QModelIndexList() << source_idx);
if (app_->player()->GetState() != Engine::Playing) {
app_->playlist_manager()->SetActiveToCurrent();
app_->player()->PlayAt(app_->playlist_manager()->current()->queue()->TakeNext(), Engine::Manual, Playlist::AutoScroll_Never, true);
}
break;
@@ -1530,19 +1534,25 @@ void MainWindow::showEvent(QShowEvent *e) {
void MainWindow::closeEvent(QCloseEvent *e) {
#ifdef Q_OS_MACOS
Exit();
#else
if (!hidden_ && keep_running_ && e->spontaneous() && QSystemTrayIcon::isSystemTrayAvailable()) {
e->ignore();
SetHiddenInTray(true);
}
else {
Exit();
}
#endif
QMainWindow::closeEvent(e);
}
void MainWindow::SetHiddenInTray(const bool hidden) {
hidden_ = hidden;
settings_.setValue("hidden", hidden_);
// Some window managers don't remember maximized state between calls to hide() and show(), so we have to remember it ourself.
if (hidden) {
@@ -2265,6 +2275,14 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
case CommandlineOptions::Player_Next:
app_->player()->Next();
break;
case CommandlineOptions::Player_PlayPlaylist:
if (options.playlist_name().isEmpty()) {
qLog(Error) << "ERROR: playlist name missing";
}
else {
app_->player()->PlayPlaylist(options.playlist_name());
}
break;
case CommandlineOptions::Player_RestartOrPrevious:
app_->player()->RestartOrPrevious();
break;
@@ -2531,9 +2549,9 @@ void MainWindow::PlaylistCopyUrl() {
}
if (urls.count() > 0) {
QMimeData *mime_data = new QMimeData;
mime_data->setUrls(urls);
QApplication::clipboard()->setMimeData(mime_data);
QMimeData mime_data;
mime_data.setUrls(urls);
QApplication::clipboard()->setText(mime_data.text());
}
}
@@ -2743,25 +2761,21 @@ void MainWindow::Raise() {
}
#ifdef Q_OS_WIN
#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);
#ifdef Q_OS_WIN
MSG *msg = static_cast<MSG*>(message);
thumbbar_->HandleWinEvent(msg);
#else
Q_UNUSED(message);
#endif
return false;
if (exit_count_ == 0 && message) {
MSG *msg = static_cast<MSG*>(message);
thumbbar_->HandleWinEvent(msg);
}
return QMainWindow::nativeEvent(eventType, message, result);
}
#endif // Q_OS_WIN
void MainWindow::AutoCompleteTags() {
@@ -2958,7 +2972,7 @@ void MainWindow::SetToggleScrobblingIcon(const bool value) {
if (value) {
if (app_->playlist_manager()->active() && app_->playlist_manager()->active()->scrobbled())
ui_->action_toggle_scrobbling->setIcon(IconLoader::Load("scrobble", 22));
else
else
ui_->action_toggle_scrobbling->setIcon(IconLoader::Load("scrobble", 22)); // TODO: Create a faint version of the icon
}
else {

View File

@@ -117,10 +117,12 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void showEvent(QShowEvent *e) override;
void closeEvent(QCloseEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
#ifdef Q_OS_WIN
#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
#endif
// PlatformInterface

View File

@@ -69,6 +69,20 @@ int MultiSortFilterProxy::Compare(const QVariant &left, const QVariant &right) c
// Copied from the QSortFilterProxyModel::lessThan implementation, but returns -1, 0 or 1 instead of true or false.
switch (left.userType()) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
case QMetaType::UnknownType: return (right.metaType().id() != QMetaType::UnknownType) ? -1 : 0;
case QMetaType::Int: return DoCompare(left.toInt(), right.toInt());
case QMetaType::UInt: return DoCompare(left.toUInt(), right.toUInt());
case QMetaType::LongLong: return DoCompare(left.toLongLong(), right.toLongLong());
case QMetaType::ULongLong: return DoCompare(left.toULongLong(), right.toULongLong());
case QMetaType::Float: return DoCompare(left.toFloat(), right.toFloat());
case QMetaType::Double: return DoCompare(left.toDouble(), right.toDouble());
case QMetaType::Char: return DoCompare(left.toChar(), right.toChar());
case QMetaType::QDate: return DoCompare(left.toDate(), right.toDate());
case QMetaType::QTime: return DoCompare(left.toTime(), right.toTime());
case QMetaType::QDateTime: return DoCompare(left.toDateTime(), right.toDateTime());
case QMetaType::QString:
#else
case QVariant::Invalid: return (right.type() != QVariant::Invalid) ? -1 : 0;
case QVariant::Int: return DoCompare(left.toInt(), right.toInt());
case QVariant::UInt: return DoCompare(left.toUInt(), right.toUInt());
@@ -81,6 +95,7 @@ int MultiSortFilterProxy::Compare(const QVariant &left, const QVariant &right) c
case QVariant::Time: return DoCompare(left.toTime(), right.toTime());
case QVariant::DateTime: return DoCompare(left.toDateTime(), right.toDateTime());
case QVariant::String:
#endif
default:
if (isSortLocaleAware())
return left.toString().localeAwareCompare(right.toString());

View File

@@ -21,89 +21,18 @@
#include "config.h"
#include <type_traits>
#include <QtGlobal>
#include <QObject>
#include <QCoreApplication>
#include <QStandardPaths>
#include <QIODevice>
#include <QMutex>
#include <QVariant>
#include <QByteArray>
#include <QString>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkDiskCache>
#include <QNetworkCacheMetaData>
#include <QAbstractNetworkCache>
#include "network.h"
QMutex ThreadSafeNetworkDiskCache::sMutex;
ThreadSafeNetworkDiskCache *ThreadSafeNetworkDiskCache::sInstance = nullptr;
QNetworkDiskCache *ThreadSafeNetworkDiskCache::sCache = nullptr;
ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject *parent) : QAbstractNetworkCache(parent) {
QMutexLocker l(&sMutex);
if (!sCache) {
sInstance = this;
sCache = new QNetworkDiskCache;
#ifdef Q_OS_WIN32
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/strawberry/networkcache");
#else
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/networkcache");
#endif
}
}
ThreadSafeNetworkDiskCache::~ThreadSafeNetworkDiskCache() {
if (this == sInstance) delete sCache;
}
qint64 ThreadSafeNetworkDiskCache::cacheSize() const {
QMutexLocker l(&sMutex);
return sCache->cacheSize();
}
QIODevice *ThreadSafeNetworkDiskCache::data(const QUrl &url) {
QMutexLocker l(&sMutex);
return sCache->data(url);
}
void ThreadSafeNetworkDiskCache::insert(QIODevice *device) {
QMutexLocker l(&sMutex);
sCache->insert(device);
}
QNetworkCacheMetaData ThreadSafeNetworkDiskCache::metaData(const QUrl &url) {
QMutexLocker l(&sMutex);
return sCache->metaData(url);
}
QIODevice *ThreadSafeNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData) {
QMutexLocker l(&sMutex);
return sCache->prepare(metaData);
}
bool ThreadSafeNetworkDiskCache::remove(const QUrl &url) {
QMutexLocker l(&sMutex);
return sCache->remove(url);
}
void ThreadSafeNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData &metaData) {
QMutexLocker l(&sMutex);
sCache->updateMetaData(metaData);
}
void ThreadSafeNetworkDiskCache::clear() {
QMutexLocker l(&sMutex);
sCache->clear();
}
#include "threadsafenetworkdiskcache.h"
NetworkAccessManager::NetworkAccessManager(QObject *parent)
: QNetworkAccessManager(parent) {
@@ -144,4 +73,5 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
}
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
}

View File

@@ -27,15 +27,10 @@
#include <QtGlobal>
#include <QObject>
#include <QNetworkAccessManager>
#include <QAbstractNetworkCache>
#include <QMutex>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkCacheMetaData>
class QIODevice;
class QNetworkReply;
class QNetworkDiskCache;
class NetworkAccessManager : public QNetworkAccessManager {
Q_OBJECT
@@ -47,25 +42,4 @@ class NetworkAccessManager : public QNetworkAccessManager {
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override;
};
class ThreadSafeNetworkDiskCache : public QAbstractNetworkCache {
public:
explicit ThreadSafeNetworkDiskCache(QObject *parent);
~ThreadSafeNetworkDiskCache() override;
qint64 cacheSize() const override;
QIODevice *data(const QUrl &url) override;
void insert(QIODevice *device) override;
QNetworkCacheMetaData metaData(const QUrl &url) override;
QIODevice *prepare(const QNetworkCacheMetaData &metaData) override;
bool remove(const QUrl &url) override;
void updateMetaData(const QNetworkCacheMetaData &metaData) override;
void clear() override;
private:
static QMutex sMutex;
static ThreadSafeNetworkDiskCache *sInstance;
static QNetworkDiskCache *sCache;
};
#endif // NETWORK_H

View File

@@ -398,6 +398,36 @@ void Player::NextItem(const Engine::TrackChangeFlags change, const Playlist::Aut
}
void Player::PlayPlaylist(const QString &playlist_name) {
PlayPlaylistInternal(Engine::Manual, Playlist::AutoScroll_Always, playlist_name);
}
void Player::PlayPlaylistInternal(Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const QString &playlist_name) {
Playlist *playlist = nullptr;
for (Playlist *p : app_->playlist_manager()->GetAllPlaylists()) {
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) {
playlist = p;
break;
}
}
if (playlist == nullptr) {
qLog(Warning) << "Playlist '" << playlist_name << "' not found.";
return;
}
app_->playlist_manager()->SetActivePlaylist(playlist->id());
app_->playlist_manager()->SetCurrentPlaylist(playlist->id());
if (playlist->rowCount() == 0) return;
int i = app_->playlist_manager()->active()->current_row();
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
if (i == -1) i = 0;
PlayAt(i, change, autoscroll, true);
}
bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
if (app_->playlist_manager()->active()->stop_after_current()) {

View File

@@ -81,8 +81,8 @@ class PlayerInterface : public QObject {
// Skips this track. Might load more of the current radio station.
virtual void Next() = 0;
virtual void Previous() = 0;
virtual void PlayPlaylist(const QString &playlist_name) = 0;
virtual void SetVolume(const int value) = 0;
virtual void VolumeUp() = 0;
virtual void VolumeDown() = 0;
@@ -163,6 +163,7 @@ class Player : public PlayerInterface {
void RestartOrPrevious() override;
void Next() override;
void Previous() override;
void PlayPlaylist(const QString &playlist_name) override;
void SetVolume(const int value) override;
void VolumeUp() override { SetVolume(GetVolume() + 5); }
void VolumeDown() override { SetVolume(GetVolume() - 5); }
@@ -195,6 +196,7 @@ class Player : public PlayerInterface {
void PreviousItem(const Engine::TrackChangeFlags change);
void NextInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll);
void PlayPlaylistInternal(Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
void FatalError();
void ValidSongRequested(const QUrl&);

View File

@@ -54,7 +54,10 @@ QtSystemTrayIcon::QtSystemTrayIcon(QObject *parent)
action_mute_(nullptr) {
app_name_[0] = app_name_[0].toUpper();
QIcon theme_icon_grey = IconLoader::Load("strawberry-grey");
if (!theme_icon_grey.isNull()) {
grey_icon_ = theme_icon_grey.pixmap(48, QIcon::Disabled);
}
tray_->setIcon(normal_icon_);
tray_->installEventFilter(this);
ClearNowPlaying();

View File

@@ -50,7 +50,6 @@
#include <QUrl>
#include <QImage>
#include <QIcon>
#include <QTextCodec>
#include <QSqlQuery>
#include <QStandardPaths>
#include <QtDebug>
@@ -745,14 +744,6 @@ void Song::set_genre_id3(int id) {
set_genre(TStringToQString(TagLib::ID3v1::genre(id)));
}
QString Song::Decode(const QString &tag, const QTextCodec *codec) {
if (!codec) {
return tag;
}
return codec->toUnicode(tag.toUtf8());
}
void Song::InitFromProtobuf(const pb::tagreader::SongMetadata &pb) {
if (d->source_ == Source_Unknown) d->source_ = Source_LocalFile;
@@ -1436,6 +1427,14 @@ QString Song::PrettyYear() const {
}
QString Song::PrettyOriginalYear() const {
if (effective_originalyear() == -1) return QString();
return QString::number(effective_originalyear());
}
QString Song::TitleWithCompilationArtist() const {
QString title(d->title_);
@@ -1509,7 +1508,11 @@ bool Song::operator!=(const Song &other) const {
return source() != other.source() || url() != other.url() || beginning_nanosec() != other.beginning_nanosec();
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
size_t qHash(const Song &song) {
#else
uint qHash(const Song &song) {
#endif
// Should compare the same fields as operator==
return qHash(song.url().toString()) ^ qHash(song.beginning_nanosec());
}

View File

@@ -38,7 +38,6 @@
#include <QImage>
#include <QIcon>
class QTextCodec;
class QSqlQuery;
namespace Engine {
@@ -177,8 +176,6 @@ class Song {
// Useful when you want updated tags from disk but you want to keep user stats.
void MergeUserSetData(const Song &other);
static QString Decode(const QString &tag, const QTextCodec *codec = nullptr);
// Save
void BindToQuery(QSqlQuery *query) const;
void BindToFtsQuery(QSqlQuery *query) const;
@@ -285,6 +282,7 @@ class Song {
QString PrettyTitleWithArtist() const;
QString PrettyLength() const;
QString PrettyYear() const;
QString PrettyOriginalYear() const;
QString TitleWithCompilationArtist() const;
@@ -381,7 +379,11 @@ Q_DECLARE_METATYPE(Song)
typedef QList<Song> SongList;
Q_DECLARE_METATYPE(QList<Song>)
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
size_t qHash(const Song &song);
#else
uint qHash(const Song &song);
#endif
// Hash function using field checked in IsSimilar function
uint HashSimilar(const Song &song);

View File

@@ -439,6 +439,7 @@ SongLoader::Result SongLoader::LoadRemote() {
errors_ << tr("Couldn't create gstreamer source element for %1").arg(url_.toString());
return Error;
}
g_object_set(source, "ssl-strict", FALSE, nullptr);
// Create the other elements and link them up
GstElement *typefind = gst_element_factory_make("typefind", nullptr);

View File

@@ -47,31 +47,31 @@ SystemTrayIcon::SystemTrayIcon(QObject *parent)
QPixmap SystemTrayIcon::CreateIcon(const QPixmap &icon, const QPixmap &grey_icon) {
Q_UNUSED(grey_icon);
QRect rect(icon.rect());
// The angle of the line that's used to cover the icon.
// Centered on rect.topRight()
double angle = double(100 - song_progress()) / 100.0 * M_PI_2 + M_PI;
double length = sqrt(pow(rect.width(), 2.0) + pow(rect.height(), 2.0));
QPolygon mask;
mask << rect.topRight();
mask << rect.topRight() + QPoint(length * sin(angle), -length * cos(angle));
if (song_progress() > 50) mask << rect.bottomLeft();
mask << rect.topLeft();
mask << rect.topRight();
QPixmap ret(icon);
QPainter p(&ret);
// Draw the grey bit
//p.setClipRegion(mask);
//p.drawPixmap(0, 0, grey_icon);
//p.setClipping(false);
if (trayicon_progress_) {
// The angle of the line that's used to cover the icon.
// Centered on rect.topLeft()
double angle = double(100 - song_progress()) / 100.0 * M_PI_2;
double length = sqrt(pow(rect.width(), 2.0) + pow(rect.height(), 2.0));
QPolygon mask;
mask << rect.topLeft();
mask << rect.topLeft() + QPoint(length * sin(angle), length * cos(angle));
if (song_progress() > 50) mask << rect.bottomRight();
mask << rect.topRight();
mask << rect.topLeft();
// Draw the grey bit
p.setClipRegion(mask);
p.drawPixmap(0, 0, grey_icon);
p.setClipping(false);
}
// Draw the playing or paused icon in the top-right
if (!current_state_icon().isNull()) {

View File

@@ -57,6 +57,7 @@ class SystemTrayIcon : public QObject {
public slots:
void SetProgress(int percentage);
void SetTrayiconProgress(bool enabled) { trayicon_progress_ = enabled; }
virtual void SetPaused();
virtual void SetPlaying(bool enable_play_pause = false);
virtual void SetStopped();
@@ -85,6 +86,7 @@ class SystemTrayIcon : public QObject {
QPixmap playing_icon_;
QPixmap paused_icon_;
QPixmap current_state_icon_;
bool trayicon_progress_;
};
#endif // SYSTEMTRAYICON_H

View File

@@ -61,7 +61,7 @@ QList<TaskManager::Task> TaskManager::GetTasks() {
}
void TaskManager::SetTaskBlocksCollectionScans(int id) {
void TaskManager::SetTaskBlocksCollectionScans(const int id) {
{
QMutexLocker l(&mutex_);
@@ -76,7 +76,7 @@ void TaskManager::SetTaskBlocksCollectionScans(int id) {
}
void TaskManager::SetTaskProgress(int id, int progress, int max) {
void TaskManager::SetTaskProgress(const int id, const qint64 progress, const qint64 max) {
{
QMutexLocker l(&mutex_);
@@ -90,7 +90,7 @@ void TaskManager::SetTaskProgress(int id, int progress, int max) {
emit TasksChanged();
}
void TaskManager::IncreaseTaskProgress(int id, int progress, int max) {
void TaskManager::IncreaseTaskProgress(const int id, const qint64 progress, const qint64 max) {
{
QMutexLocker l(&mutex_);
@@ -105,7 +105,7 @@ void TaskManager::IncreaseTaskProgress(int id, int progress, int max) {
}
void TaskManager::SetTaskFinished(int id) {
void TaskManager::SetTaskFinished(const int id) {
bool resume_collection_watchers = false;

View File

@@ -37,10 +37,11 @@ class TaskManager : public QObject {
explicit TaskManager(QObject *parent = nullptr);
struct Task {
Task() : id(0), progress(0), progress_max(0), blocks_collection_scans(false) {}
int id;
QString name;
int progress;
int progress_max;
qint64 progress;
qint64 progress_max;
bool blocks_collection_scans;
};
@@ -61,13 +62,13 @@ class TaskManager : public QObject {
QList<Task> GetTasks();
int StartTask(const QString &name);
void SetTaskBlocksCollectionScans(int id);
void SetTaskProgress(int id, int progress, int max = 0);
void IncreaseTaskProgress(int id, int progress, int max = 0);
void SetTaskFinished(int id);
int GetTaskProgress(int id);
void SetTaskBlocksCollectionScans(const int id);
void SetTaskProgress(const int id, const qint64 progress, const qint64 max = 0);
void IncreaseTaskProgress(const int id, const qint64 progress, const qint64 max = 0);
void SetTaskFinished(const int id);
int GetTaskProgress(const int id);
signals:
signals:
void TasksChanged();
void PauseCollectionWatchers();

View File

@@ -0,0 +1,108 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QCoreApplication>
#include <QStandardPaths>
#include <QIODevice>
#include <QMutex>
#include <QNetworkDiskCache>
#include <QNetworkCacheMetaData>
#include <QAbstractNetworkCache>
#include <QUrl>
#include "core/logging.h"
#include "threadsafenetworkdiskcache.h"
QMutex ThreadSafeNetworkDiskCache::sMutex;
int ThreadSafeNetworkDiskCache::sInstances = 0;
QNetworkDiskCache *ThreadSafeNetworkDiskCache::sCache = nullptr;
ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject *parent) : QAbstractNetworkCache(parent) {
QMutexLocker l(&sMutex);
++sInstances;
if (!sCache) {
sCache = new QNetworkDiskCache;
#ifdef Q_OS_WIN32
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/strawberry/networkcache");
#else
sCache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/networkcache");
#endif
}
}
ThreadSafeNetworkDiskCache::~ThreadSafeNetworkDiskCache() {
QMutexLocker l(&sMutex);
--sInstances;
if (sCache && sInstances == 0) {
sCache->deleteLater();
sCache = nullptr;
}
}
qint64 ThreadSafeNetworkDiskCache::cacheSize() const {
QMutexLocker l(&sMutex);
return sCache->cacheSize();
}
QIODevice *ThreadSafeNetworkDiskCache::data(const QUrl &url) {
QMutexLocker l(&sMutex);
return sCache->data(url);
}
void ThreadSafeNetworkDiskCache::insert(QIODevice *device) {
QMutexLocker l(&sMutex);
sCache->insert(device);
}
QNetworkCacheMetaData ThreadSafeNetworkDiskCache::metaData(const QUrl &url) {
QMutexLocker l(&sMutex);
return sCache->metaData(url);
}
QIODevice *ThreadSafeNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData) {
QMutexLocker l(&sMutex);
return sCache->prepare(metaData);
}
bool ThreadSafeNetworkDiskCache::remove(const QUrl &url) {
QMutexLocker l(&sMutex);
return sCache->remove(url);
}
void ThreadSafeNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData &metaData) {
QMutexLocker l(&sMutex);
sCache->updateMetaData(metaData);
}
void ThreadSafeNetworkDiskCache::clear() {
QMutexLocker l(&sMutex);
sCache->clear();
}

View File

@@ -0,0 +1,61 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef THREADSAFENETWORKDISKCACHE_H
#define THREADSAFENETWORKDISKCACHE_H
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QAbstractNetworkCache>
#include <QMutex>
#include <QUrl>
#include <QNetworkCacheMetaData>
class QIODevice;
class QNetworkDiskCache;
class ThreadSafeNetworkDiskCache : public QAbstractNetworkCache {
Q_OBJECT
public:
explicit ThreadSafeNetworkDiskCache(QObject *parent);
~ThreadSafeNetworkDiskCache() override;
qint64 cacheSize() const override;
QIODevice *data(const QUrl &url) override;
void insert(QIODevice *device) override;
QNetworkCacheMetaData metaData(const QUrl &url) override;
QIODevice *prepare(const QNetworkCacheMetaData &metaData) override;
bool remove(const QUrl &url) override;
void updateMetaData(const QNetworkCacheMetaData &metaData) override;
public slots:
void clear() override;
private:
static QMutex sMutex;
static int sInstances;
static QNetworkDiskCache *sCache;
};
#endif // THREADSAFENETWORKDISKCACHE_H

View File

@@ -215,7 +215,12 @@ quint64 FileSystemCapacity(const QString &path) {
return quint64(fs_info.f_blocks) * quint64(fs_info.f_bsize);
#elif defined(Q_OS_WIN32)
_ULARGE_INTEGER ret;
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), nullptr,&ret, nullptr) != 0)
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
ScopedWCharArray wchar(QDir::toNativeSeparators(path));
if (GetDiskFreeSpaceEx(wchar.get(), nullptr, &ret, nullptr) != 0)
# else
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), nullptr, &ret, nullptr) != 0)
# endif
return ret.QuadPart;
#endif
@@ -231,7 +236,12 @@ quint64 FileSystemFreeSpace(const QString &path) {
return quint64(fs_info.f_bavail) * quint64(fs_info.f_bsize);
#elif defined(Q_OS_WIN32)
_ULARGE_INTEGER ret;
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
ScopedWCharArray wchar(QDir::toNativeSeparators(path));
if (GetDiskFreeSpaceEx(wchar.get(), &ret, nullptr, nullptr) != 0)
# else
if (GetDiskFreeSpaceEx(QDir::toNativeSeparators(path).toLocal8Bit().constData(), &ret, nullptr, nullptr) != 0)
# endif
return ret.QuadPart;
#endif
@@ -924,7 +934,7 @@ QString MacAddress() {
}
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline) {
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline, const bool html_escaped) {
QRegularExpression variable_replacer("[%][a-z]+[%]");
QString copy(message);
@@ -935,7 +945,7 @@ QString ReplaceMessage(const QString &message, const Song &song, const QString &
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));
copy.replace(captured[0], ReplaceVariable(captured[0], song, newline, html_escaped));
pos += match.capturedLength();
}
@@ -943,62 +953,76 @@ QString ReplaceMessage(const QString &message, const Song &song, const QString &
if (index_of >= 0) copy = copy.remove(index_of, 3);
return copy;
}
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline) {
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline, const bool html_escaped) {
QString return_value;
if (variable == "%artist%") {
return song.artist().toHtmlEscaped();
QString value = variable;
if (variable == "%title%") {
value = song.PrettyTitle();
}
else if (variable == "%album%") {
return song.album().toHtmlEscaped();
value = song.album();
}
else if (variable == "%title%") {
return song.PrettyTitle().toHtmlEscaped();
else if (variable == "%artist%") {
value = song.artist();
}
else if (variable == "%albumartist%") {
return song.effective_albumartist().toHtmlEscaped();
}
else if (variable == "%year%") {
return song.PrettyYear().toHtmlEscaped();
}
else if (variable == "%composer%") {
return song.composer().toHtmlEscaped();
}
else if (variable == "%performer%") {
return song.performer().toHtmlEscaped();
}
else if (variable == "%grouping%") {
return song.grouping().toHtmlEscaped();
}
else if (variable == "%length%") {
return song.PrettyLength().toHtmlEscaped();
}
else if (variable == "%disc%") {
return return_value.setNum(song.disc()).toHtmlEscaped();
value = song.effective_albumartist();
}
else if (variable == "%track%") {
return return_value.setNum(song.track()).toHtmlEscaped();
value.setNum(song.track());
}
else if (variable == "%disc%") {
value.setNum(song.disc());
}
else if (variable == "%year%") {
value = song.PrettyYear();
}
else if (variable == "%originalyear%") {
value = song.PrettyOriginalYear();
}
else if (variable == "%genre%") {
return song.genre().toHtmlEscaped();
value = song.genre();
}
else if (variable == "%playcount%") {
return return_value.setNum(song.playcount()).toHtmlEscaped();
else if (variable == "%composer%") {
value = song.composer();
}
else if (variable == "%skipcount%") {
return return_value.setNum(song.skipcount()).toHtmlEscaped();
else if (variable == "%performer%") {
value = song.performer();
}
else if (variable == "%grouping%") {
value = song.grouping();
}
else if (variable == "%length%") {
value = song.PrettyLength();
}
else if (variable == "%filename%") {
return song.basefilename().toHtmlEscaped();
value = song.basefilename();
}
else if (variable == "%url%") {
value = song.url().toString();
}
else if (variable == "%playcount%") {
value.setNum(song.playcount());
}
else if (variable == "%skipcount%") {
value.setNum(song.skipcount());
}
else if (variable == "%rating%") {
value = song.PrettyRating();
}
else if (variable == "%newline%") {
return QString(newline);
return QString(newline); // No HTML escaping, return immediately.
}
//if the variable is not recognized, just return it
return variable;
if (html_escaped) {
value = value.toHtmlEscaped();
}
return value;
}
bool IsColorDark(const QColor &color) {

View File

@@ -147,8 +147,8 @@ QString UnicodeToAscii(const QString &unicode);
QString MacAddress();
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline);
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline);
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline, const bool html_escaped = false);
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline, const bool html_escaped = false);
bool IsColorDark(const QColor &color);

View File

@@ -124,7 +124,7 @@ void Windows7ThumbBar::HandleWinEvent(MSG *msg) {
if (button_created_message_id_ == 0) {
// Compute the value for the TaskbarButtonCreated message
button_created_message_id_ = RegisterWindowMessage("TaskbarButtonCreated");
button_created_message_id_ = RegisterWindowMessageA(LPCSTR("TaskbarButtonCreated"));
qLog(Debug) << "TaskbarButtonCreated message ID registered" << button_created_message_id_;
}

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +23,6 @@
#include <QtGlobal>
#include <QObject>
#include <QNetworkAccessManager>
#include <QTimer>
#include <QString>
#include <QImage>
@@ -34,7 +34,7 @@
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
AlbumCoverFetcher::AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent, QNetworkAccessManager *network)
AlbumCoverFetcher::AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent, NetworkAccessManager *network)
: QObject(parent),
cover_providers_(cover_providers),
network_(network ? network : new NetworkAccessManager(this)),

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,7 +36,7 @@
#include <QImage>
class QTimer;
class QNetworkAccessManager;
class NetworkAccessManager;
class CoverProviders;
class AlbumCoverFetcherSearch;
struct CoverSearchStatistics;
@@ -103,7 +104,7 @@ class AlbumCoverFetcher : public QObject {
Q_OBJECT
public:
explicit AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent = nullptr, QNetworkAccessManager *network = nullptr);
explicit AlbumCoverFetcher(CoverProviders *cover_providers, QObject *parent = nullptr, NetworkAccessManager *network = nullptr);
~AlbumCoverFetcher() override;
static const int kMaxConcurrentRequests;
@@ -126,7 +127,7 @@ class AlbumCoverFetcher : public QObject {
void AddRequest(const CoverSearchRequest &req);
CoverProviders *cover_providers_;
QNetworkAccessManager *network_;
NetworkAccessManager *network_;
quint64 next_id_;
QQueue<CoverSearchRequest> queued_requests_;

View File

@@ -34,13 +34,13 @@
#include <QUrl>
#include <QImage>
#include <QImageReader>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QtDebug>
#include "core/logging.h"
#include "core/utilities.h"
#include "core/network.h"
#include "core/networktimeouts.h"
#include "albumcoverfetcher.h"
#include "albumcoverfetchersearch.h"
@@ -52,8 +52,7 @@ const int AlbumCoverFetcherSearch::kImageLoadTimeoutMs = 6000;
const int AlbumCoverFetcherSearch::kTargetSize = 500;
const float AlbumCoverFetcherSearch::kGoodScore = 4.0;
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(
const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent)
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(const CoverSearchRequest &request, NetworkAccessManager *network, QObject *parent)
: QObject(parent),
request_(request),
image_load_timeout_(new NetworkTimeouts(kImageLoadTimeoutMs, this)),

View File

@@ -36,10 +36,10 @@
#include "albumcoverfetcher.h"
#include "coversearchstatistics.h"
class QNetworkAccessManager;
class QNetworkReply;
class CoverProvider;
class CoverProviders;
class NetworkAccessManager;
class NetworkTimeouts;
// This class encapsulates a single search for covers initiated by an AlbumCoverFetcher.
@@ -49,7 +49,7 @@ class AlbumCoverFetcherSearch : public QObject {
Q_OBJECT
public:
explicit AlbumCoverFetcherSearch(const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent);
explicit AlbumCoverFetcherSearch(const CoverSearchRequest &request, NetworkAccessManager *network, QObject *parent);
~AlbumCoverFetcherSearch() override;
void Start(CoverProviders *cover_providers);
@@ -107,7 +107,7 @@ class AlbumCoverFetcherSearch : public QObject {
typedef QPair<CoverSearchResult, QImage> CandidateImage;
QMultiMap<float, CandidateImage> candidate_images_;
QNetworkAccessManager *network_;
NetworkAccessManager *network_;
bool cancel_requested_;

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -621,6 +622,7 @@ void AlbumCoverManager::ShowCover() {
if (!song.is_valid()) return;
album_cover_choice_controller_->ShowCover(song);
}
void AlbumCoverManager::FetchSingleCover() {
@@ -809,13 +811,11 @@ SongMimeData *AlbumCoverManager::GetMimeDataForAlbums(const QModelIndexList &ind
}
void AlbumCoverManager::AlbumDoubleClicked(const QModelIndex &index) {
void AlbumCoverManager::AlbumDoubleClicked(const QModelIndex &idx) {
SongMimeData *mimedata = GetMimeDataForAlbums(QModelIndexList() << index);
if (mimedata) {
mimedata->from_doubleclick_ = true;
emit AddToPlaylist(mimedata);
}
QListWidgetItem *item = static_cast<QListWidgetItem*>(idx.internalPointer());
if (!item) return;
album_cover_choice_controller_->ShowCover(ItemAsSong(item));
}

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -151,7 +152,7 @@ class AlbumCoverManager : public QMainWindow {
void ShowCover();
// For adding albums to the playlist
void AlbumDoubleClicked(const QModelIndex &index);
void AlbumDoubleClicked(const QModelIndex &idx);
void AddSelectedToPlaylist();
void LoadSelectedToPlaylist();

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -223,7 +223,7 @@ void DeezerCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
QMap<QUrl, CoverSearchResult> results;
int i = 0;
for (const QJsonValue &json_value : array_data) {
for (const QJsonValue json_value : array_data) {
if (!json_value.isObject()) {
Error("Invalid Json reply, data array value is not a object.", json_value);

View File

@@ -32,7 +32,7 @@
#include "jsoncoverprovider.h"
class QNetworkAccessManager;
class NetworkAccessManager;
class QNetworkReply;
class Application;
@@ -58,7 +58,7 @@ class DeezerCoverProvider : public JsonCoverProvider {
static const char *kApiUrl;
static const int kLimit;
QNetworkAccessManager *network_;
NetworkAccessManager *network_;
QList<QNetworkReply*> replies_;
};

View File

@@ -273,7 +273,7 @@ void DiscogsCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id)
array_results = value_results.toArray();
}
for (const QJsonValue &value_result : array_results) {
for (const QJsonValue value_result : array_results) {
if (!value_result.isObject()) {
Error("Invalid Json reply, results value is not a object.", value_result);
@@ -380,7 +380,7 @@ void DiscogsCoverProvider::HandleReleaseReply(QNetworkReply *reply, const int se
QJsonArray array_artists = value_artists.toArray();
int i = 0;
QString artist;
for (const QJsonValue &value_artist : array_artists) {
for (const QJsonValue value_artist : array_artists) {
if (!value_artist.isObject()) {
Error("Invalid Json reply, atists array value is not a object.", value_artist);
continue;
@@ -421,7 +421,7 @@ void DiscogsCoverProvider::HandleReleaseReply(QNetworkReply *reply, const int se
return;
}
for (const QJsonValue &value_image : array_images) {
for (const QJsonValue value_image : array_images) {
if (!value_image.isObject()) {
Error("Invalid Json reply, images array value is not an object.", value_image);

View File

@@ -40,7 +40,7 @@
#include "jsoncoverprovider.h"
#include "albumcoverfetcher.h"
class QNetworkAccessManager;
class NetworkAccessManager;
class QNetworkReply;
class QTimer;
class Application;
@@ -99,7 +99,7 @@ class DiscogsCoverProvider : public JsonCoverProvider {
static const char *kSecretKeyB64;
static const int kRequestsDelay;
QNetworkAccessManager *network_;
NetworkAccessManager *network_;
QTimer *timer_flush_requests_;
QQueue<std::shared_ptr<DiscogsCoverSearchContext>> queue_search_requests_;
QQueue<DiscogsCoverReleaseContext> queue_release_requests_;

View File

@@ -230,7 +230,7 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply *reply, const int id, cons
}
QJsonArray array_type = value_type.toArray();
for (const QJsonValue &value : array_type) {
for (const QJsonValue value : array_type) {
if (!value.isObject()) {
Error("Invalid Json reply, value in albummatches/trackmatches array is not a object.", value);
@@ -255,7 +255,7 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply *reply, const int id, cons
QJsonArray array_image = json_image.toArray();
QUrl url;
LastFmImageSize size(LastFmImageSize::Unknown);
for (const QJsonValue &value_image : array_image) {
for (const QJsonValue value_image : array_image) {
if (!value_image.isObject()) {
Error("Invalid Json reply, album image value is not an object.", value_image);
continue;

View File

@@ -31,7 +31,7 @@
#include "jsoncoverprovider.h"
class QNetworkAccessManager;
class NetworkAccessManager;
class QNetworkReply;
class Application;
@@ -65,7 +65,7 @@ class LastFmCoverProvider : public JsonCoverProvider {
static const char *kApiKey;
static const char *kSecret;
QNetworkAccessManager *network_;
NetworkAccessManager *network_;
QList<QNetworkReply*> replies_;
};

View File

@@ -167,7 +167,7 @@ void MusicbrainzCoverProvider::HandleSearchReply(QNetworkReply *reply, const int
return;
}
for (const QJsonValue &value_release : array_releases) {
for (const QJsonValue value_release : array_releases) {
if (!value_release.isObject()) {
Error("Invalid Json reply, releases array value is not an object.", value_release);
@@ -187,7 +187,7 @@ void MusicbrainzCoverProvider::HandleSearchReply(QNetworkReply *reply, const int
QJsonArray array_artists = json_artists.toArray();
int i = 0;
QString artist;
for (const QJsonValue &value_artist : array_artists) {
for (const QJsonValue value_artist : array_artists) {
if (!value_artist.isObject()) {
Error("Invalid Json reply, artist is not a object.", value_artist);
continue;

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