Compare commits

...

113 Commits
0.6.6 ... 0.6.8

Author SHA1 Message Date
Jonas Kvinge
be7cc55488 Release 0.6.8 2020-01-05 23:27:31 +01:00
Jonas Kvinge
c8f3379a48 Fix crash when deleting playlist folder. 2020-01-05 23:26:07 +01:00
Jonas Kvinge
b5a7945e49 Use QModelIndex::model() 2020-01-05 23:25:23 +01:00
Strawbs Bot
0bdac2e97d Update translations 2020-01-05 22:52:27 +01:00
Jonas Kvinge
1468a821fb Fix restoring to correct screen when maximized 2020-01-05 22:21:55 +01:00
Jonas Kvinge
3cdc8dc4b6 Use QWidget::screen() with Qt 5.14 2020-01-05 19:15:28 +01:00
Jonas Kvinge
aa255aa7e6 Use current screen, not primary screen 2020-01-05 19:14:25 +01:00
Strawbs Bot
66e5ccb9cc Update translations 2020-01-05 01:02:24 +01:00
Jonas Kvinge
2215f300bf Added option to disable playlist clear button
Fixes #339
2020-01-04 06:38:25 +01:00
Jonas Kvinge
eec767406b Add confirmation before clearing playlists with more than 500 songs 2020-01-04 06:11:21 +01:00
Jonas Kvinge
31aa42c2fa Fix compile with translations on Windows 2020-01-03 02:07:37 +01:00
Strawbs Bot
e912c59402 Add Italian 2020-01-03 01:46:38 +01:00
Strawbs Bot
c9988976f3 Update translations 2020-01-03 01:02:34 +01:00
Jonas Kvinge
443be1c2c8 Increase lyrics score if lyrics text > 60 2020-01-02 19:21:27 +01:00
Jonas Kvinge
7f442cff3b Fix QProxyStyle 2020-01-02 18:57:53 +01:00
Strawbs Bot
3696ae44ad Update translations 2019-12-31 01:09:52 +01:00
Jonas Kvinge
fc2d601424 Remove useless stdbool.h include 2019-12-30 23:14:40 +01:00
Jonas Kvinge
8818f24114 Fix compile with Qt 5.14 and above 2019-12-30 02:28:54 +01:00
Jonas Kvinge
0e12c8249e Fix actions builds 2019-12-30 00:49:39 +01:00
Strawbs Bot
06d62a70a9 Update translations 2019-12-30 00:35:45 +01:00
Jonas Kvinge
76d8018ca2 Remove HTML from translations 2019-12-29 23:53:54 +01:00
Jonas Kvinge
4123b41a5e Merge branch 'master' of github.com:jonaski/strawberry 2019-12-29 23:47:07 +01:00
Jonas Kvinge
5930257fed Remove HTML from translations 2019-12-29 23:46:49 +01:00
Jonas Kvinge
d3d60327ab Remove the answer to life the universe and everything 2019-12-29 23:46:35 +01:00
Strawbs Bot
5080ffb9fc Update translations 2019-12-29 23:41:39 +01:00
Jonas Kvinge
9b688a5179 Remove HTML from translations
Fixes #260
2019-12-29 23:37:48 +01:00
Strawbs Bot
a603dc5227 Update translations 2019-12-29 01:02:01 +01:00
Jonas Kvinge
c25f682caf Emit ExitFinished if no internet services are enabled 2019-12-28 03:35:07 +01:00
Jonas Kvinge
27a2fd298d Add the device view container widget to the tabbar
Fixes bugs related to the tabbar and the widgets being unresponsive

Fixes #279
Fixes #321
2019-12-28 03:13:41 +01:00
Strawbs Bot
bb38053cb3 Update translations 2019-12-24 01:01:42 +01:00
Strawbs Bot
5e82ee8695 Add German 2019-12-22 14:56:55 +01:00
Strawbs Bot
6c691ff9a8 Update translations 2019-12-22 14:55:33 +01:00
Jonas Kvinge
ac5a14fe4a Add StartupWMClass to desktop file
Fixes #305
2019-12-22 14:16:24 +01:00
Jonas Kvinge
00402d13ef Fix collection watcher on macOS
Fixes #324
2019-12-22 14:15:25 +01:00
Gavin D. Howard
079a559247 Make context title and summary changeable (#329)
* Make context title and summary changeable

Closes #30

* Fix checkboxes on context settings page

So...I am new to Qt, and I forgot that checkboxes can have a label.
Duh. Fixed.

* Put context settings in a different place

* Put ReplaceMessage and ReplaceVariable in Utilities
2019-12-22 12:09:05 +01:00
Strawbs Bot
a19ea8fdba Update translations 2019-12-22 01:04:20 +01:00
Jonas Kvinge
c6e172f942 Remove unused afc_port_ variable 2019-12-22 00:54:56 +01:00
Jonas Kvinge
bdc9f3e8bf Use AFC_E_SUCCESS 2019-12-22 00:45:01 +01:00
Jonas Kvinge
8ec5a587fc Fix compile 2019-12-21 22:34:42 +01:00
Jonas Kvinge
9caf46f2fb Use QString::asprintf 2019-12-21 22:07:04 +01:00
Jonas Kvinge
13fdbfc5e8 Use QString::asprintf 2019-12-21 21:56:48 +01:00
Jonas Kvinge
882c94110e Update temporary metadata when tags are changed 2019-12-21 21:55:24 +01:00
Jonas Kvinge
97bc980611 Upgrade packages 2019-12-21 18:26:51 +01:00
Jonas Kvinge
be9bf5c173 Replace use of QSet::fromList with Qt 5.14 and above 2019-12-21 18:22:18 +01:00
Jonas Kvinge
6df38c389c Replace use of QSet::toList() with QSet::values() 2019-12-21 18:19:09 +01:00
Jonas Kvinge
6c6bceb8cc Replace use of QString::sprintf 2019-12-21 18:17:58 +01:00
Jonas Kvinge
b5a897bb4d Replace use of QString::sprintf 2019-12-21 18:15:45 +01:00
Jonas Kvinge
ab85b716bb Replace use of QTime::start() with QElapsedTimer 2019-12-21 18:11:54 +01:00
Jonas Kvinge
8e256e6d5c Fix indent 2019-12-21 17:55:24 +01:00
Jonas Kvinge
288036a63b Fix spelling 2019-12-21 17:54:43 +01:00
Jonas Kvinge
f2005c3343 Update README.md 2019-12-11 20:46:15 +01:00
Jonas Kvinge
79d516b899 Update README.md 2019-12-11 20:45:27 +01:00
Jonas Kvinge
28b2f6eae3 Update README.md 2019-12-11 20:21:03 +01:00
Strawbs Bot
09ed2b945c Update translations 2019-12-07 12:45:08 +01:00
Jonas Kvinge
74aec89ef0 Add French 2019-12-05 19:58:07 +01:00
Jonas Kvinge
4faa23d099 Add Indonesian 2019-12-05 19:57:14 +01:00
Jonas Kvinge
5c4997ab20 Turn back git revision 2019-12-05 19:53:41 +01:00
Jonas Kvinge
cd490ca946 Release 0.6.7 2019-11-27 23:09:49 +01:00
Jonas Kvinge
793f6b00e6 Allow 4 tagreader workers 2019-11-27 22:57:51 +01:00
Strawbs Bot
c1ec568fda Update translations 2019-11-27 01:03:22 +01:00
Jonas Kvinge
4e2b52975e Remove require for sqlite on centos since we use customized sqlite 2019-11-26 23:43:34 +01:00
Jonas Kvinge
c141df6b86 Remove low-latency setting for wasapisink 2019-11-26 22:30:56 +01:00
Jonas Kvinge
63b781765a Update copyrights 2019-11-26 22:30:14 +01:00
Jonas Kvinge
c121e141e7 Remove duplicate entries in Changelog 2019-11-26 22:29:13 +01:00
Jonas Kvinge
7544c9a9f5 Update nsi with libprotobuf-22.dll 2019-11-26 19:42:38 +01:00
Jonas Kvinge
722a088515 Only remove pixmap cache when removing parents in collection model 2019-11-26 19:42:05 +01:00
Jonas Kvinge
0ce75bff58 Remove unused code in context albums 2019-11-26 19:41:32 +01:00
Strawbs Bot
3744b6d3de Update translations 2019-11-26 01:02:03 +01:00
Jonas Kvinge
a4ebd91e8d Make sure QSqlQuery::exec() was successful 2019-11-25 22:43:09 +01:00
Jonas Kvinge
3bb0ee916d Update Changelog 2019-11-25 22:31:42 +01:00
Jonas Kvinge
fd6afdf5f3 Extend url test 2019-11-25 22:30:33 +01:00
Jonas Kvinge
47c13a840e Handle different urls in collection backend for backward compatibility 2019-11-25 22:29:12 +01:00
Jonas Kvinge
5b61992b3c Save cover urls encoded 2019-11-25 22:28:48 +01:00
Jonas Kvinge
36331dc253 Fix removing nodes from pending art 2019-11-25 22:25:29 +01:00
Strawbs Bot
4265cf31b2 Update translations 2019-11-25 01:05:00 +01:00
Jonas Kvinge
337e47269f Remove portable, we dont use it 2019-11-25 00:35:48 +01:00
Jonas Kvinge
7039234471 Fix compile collection model test 2019-11-25 00:35:16 +01:00
Jonas Kvinge
f7f9333d91 Add collection backend url tests 2019-11-25 00:34:59 +01:00
Jonas Kvinge
bf35665932 Update all songs for the same directory+album when updating compilations
- Fixes a bug where the songs are stuck in various artists, because the
album has child songs, it will be stuck with various artists as the
parent node.
2019-11-25 00:28:49 +01:00
Jonas Kvinge
089a2271c2 Add GitHub Actions 2019-11-24 20:04:05 +01:00
Jonas Kvinge
f8e83e3631 Fix loading replay gain setting
Fixes #311
2019-11-24 19:34:05 +01:00
Strawbs Bot
46fd329913 Update translations 2019-11-23 01:07:52 +01:00
Jonas Kvinge
6b9ba96e77 Revert "Tidal: Add explicit to album titles"
This reverts commit b7d360d850.
2019-11-21 22:55:39 +01:00
Jonas Kvinge
b7d360d850 Tidal: Add explicit to album titles 2019-11-20 22:13:28 +01:00
Jonas Kvinge
8e226302ab Allow scrobbling songs without album
Fixes #309
2019-11-20 21:30:41 +01:00
Jonas Kvinge
7795b9edaf Dont replace metadata when loading playlists 2019-11-20 19:34:57 +01:00
Jonas Kvinge
9375d9699a No 2019-11-19 21:51:15 +01:00
Jonas Kvinge
cf0442d5b8 Fix setting pixmap cache limit 2019-11-19 21:49:46 +01:00
Jonas Kvinge
b386ca14df Show fullsize cover on doubleclick 2019-11-19 21:20:36 +01:00
Jonas Kvinge
ea47fae31e Add seperator between "unset cover" and "show fullsize" 2019-11-19 21:19:44 +01:00
Jonas Kvinge
e0fed07b10 Change pixmap cache limit 2019-11-19 21:03:06 +01:00
Jonas Kvinge
779d5ff7b6 Dont reset pixmap cache on model reset 2019-11-19 20:56:03 +01:00
Jonas Kvinge
eb6fbd03ec Update Changelog 2019-11-19 20:49:54 +01:00
Jonas Kvinge
95409d1b0e Remove unused variables 2019-11-19 20:47:06 +01:00
Jonas Kvinge
49599c8731 Add back ChartLyrics
This reverts commit c992768efe.
2019-11-19 20:45:22 +01:00
Jonas Kvinge
572f94e000 Update Changelog 2019-11-18 17:25:57 +01:00
Jonas Kvinge
c0a2ad5f50 Change comment 2019-11-18 17:16:58 +01:00
Jonas Kvinge
71fa5acc74 Fix previous player and doubleclick playlist song behaviour settings 2019-11-17 23:46:10 +01:00
Jonas Kvinge
bac5b7679d Use killproc.exe instead in nsi 2019-11-17 16:34:30 +01:00
Strawbs Bot
93ade821a5 Update translations 2019-11-17 01:10:32 +01:00
Jonas Kvinge
a718e19979 Use KillProc in nsi 2019-11-15 23:25:12 +01:00
Jonas Kvinge
8ac83a46f5 Remove clang compiler flag 2019-11-15 23:24:37 +01:00
Jonas Kvinge
1b65dcd7df Fix comparison between signed/unsigned 2019-11-15 00:23:06 +01:00
Jonas Kvinge
bbad45f1e7 Minor cmake fixes 2019-11-15 00:22:41 +01:00
Jonas Kvinge
331b9cca18 Remove sudo 2019-11-14 21:23:50 +01:00
Jonas Kvinge
a9accb7d85 Refactor scrobbler authentication code
Fix a crash when authentication is cancelled
2019-11-14 21:07:30 +01:00
Jonas Kvinge
1862e70628 Declare song using source 2019-11-14 00:09:35 +01:00
Jonas Kvinge
c4f7054ca6 Use QUrl::FullyEncoded in update compilations 2019-11-13 23:51:04 +01:00
Jonas Kvinge
175e568a28 Minor improvements to update compilations 2019-11-13 21:27:04 +01:00
Jonas Kvinge
c8d580e7de No need to delete pixmap cache when deleting empty parents 2019-11-13 21:16:48 +01:00
Jonas Kvinge
bc0c50ee65 Remove commented code 2019-11-13 21:12:50 +01:00
Jonas Kvinge
45e9dd96d1 Remove left click on analyzer to popup menu
Fixes #294
2019-11-11 00:01:39 +01:00
Jonas Kvinge
1cc73562a3 Turn back git revision 2019-11-10 15:35:36 +01:00
314 changed files with 22336 additions and 2603 deletions

52
.github/workflows/ccpp.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: C/C++ CI
on: [push, pull_request]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Update Packages
run: sudo apt-get update -y
- name: Install Packages
run:
sudo apt-get install -y git make cmake g++ gettext libglib2.0-dev libdbus-1-dev libboost-dev libprotobuf-dev protobuf-compiler libsqlite3-dev sqlite3 libgnutls28-dev libasound2-dev libpulse-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev libqt5core5a libqt5gui5 libqt5widgets5 libqt5concurrent5 libqt5network5 libqt5sql5 libqt5x11extras5-dev libqt5dbus5 qttools5-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev gstreamer1.0-alsa gstreamer1.0-pulseaudio libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 libgstreamer-plugins-good1.0-0 libgstreamer-plugins-bad1.0-0 libchromaprint-dev libfftw3-dev libcdio-dev libmtp-dev libgpod-dev libimobiledevice-dev libplist-dev libusbmuxd-dev libxine2-dev libvlc-dev
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure CMake
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Update HomeBrew
run: brew update
- name: Unlink python
run: brew unlink python@2
- 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 libimobiledevice libplist create-dmg
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure CMake
env:
Qt5_DIR: /usr/local/opt/qt5/lib/cmake
Qt5LinguistTools_DIR: /usr/local/opt/qt5/lib/cmake/Qt5LinguistTools
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLE=ON
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Install
working-directory: ${{runner.workspace}}/build
shell: bash
run: make install

View File

@@ -41,7 +41,7 @@ script:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
make -j8 || travis_terminate 1;
make install || travis_terminate 1;
sudo make dmg;
make dmg;
fi
after_success:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry*.dmg; fi

View File

@@ -5,10 +5,10 @@ include(CheckFunctionExists)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
if(CMAKE_VERSION VERSION_GREATER 3.0)

View File

@@ -37,7 +37,7 @@
#include <QSharedMemory>
#include <QByteArray>
#include <QDateTime>
#include <QTime>
#include <QElapsedTimer>
#include "singleapplication.h"
#include "singleapplication_p.h"
@@ -88,7 +88,7 @@ SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondar
}
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
QTime time;
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state

View File

@@ -114,12 +114,9 @@ void SingleApplicationPrivate::genBlockServerName() {
#ifdef Q_OS_UNIX
QByteArray username;
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
uid_t uid = geteuid();
if (uid != -1) {
struct passwd *pw = getpwuid(uid);
if (pw) {
username = pw->pw_name;
}
struct passwd *pw = getpwuid(geteuid());
if (pw) {
username = pw->pw_name;
}
#endif
if (username.isEmpty()) username = qgetenv("USER");

View File

@@ -37,7 +37,7 @@
#include <QSharedMemory>
#include <QByteArray>
#include <QDateTime>
#include <QTime>
#include <QElapsedTimer>
#include "singlecoreapplication.h"
#include "singlecoreapplication_p.h"
@@ -89,7 +89,7 @@ SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allow
}
InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data());
QTime time;
QElapsedTimer time;
time.start();
// Make sure the shared memory block is initialised and in consistent state

View File

@@ -114,12 +114,9 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
#ifdef Q_OS_UNIX
QByteArray username;
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
uid_t uid = geteuid();
if (uid != -1) {
struct passwd *pw = getpwuid(uid);
if (pw) {
username = pw->pw_name;
}
struct passwd *pw = getpwuid(geteuid());
if (pw) {
username = pw->pw_name;
}
#endif
if (username.isEmpty()) username = qgetenv("USER");

View File

@@ -56,32 +56,17 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(DEBUG ON)
endif()
if (CMAKE_CXX_COMPILER MATCHES ".*clang")
set(CMAKE_COMPILER_IS_CLANGXX 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized")
endif()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
endif(APPLE)
find_program(CCACHE_EXECUTABLE NAMES ccache)
if (CCACHE_EXECUTABLE)
message(STATUS "ccache found: will be used for compilation and linkage")
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_EXECUTABLE})
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
endif ()
find_program(QT_LCONVERT_EXECUTABLE NAMES lconvert lconvert-qt5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH)
find_program(QT_LCONVERT_EXECUTABLE NAMES lconvert lconvert-qt5)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GIO REQUIRED gio-2.0)
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
pkg_check_modules(CDIO libcdio)
find_package(Boost REQUIRED)
find_package(Threads)
find_package(GnuTLS)
find_package(Boost REQUIRED)
find_package(Protobuf REQUIRED)
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
if(LINUX)
@@ -98,6 +83,10 @@ endif()
if(X11_FOUND)
set(HAVE_X11 ON)
endif()
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GIO REQUIRED gio-2.0)
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
pkg_check_modules(LIBCDIO libcdio)
pkg_check_modules(GSTREAMER gstreamer-1.0)
pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0)
pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
@@ -118,10 +107,6 @@ pkg_check_modules(LIBPLIST libplist)
find_package(Gettext)
find_package(FFTW3)
if(WIN32)
find_package(ZLIB REQUIRED)
endif(WIN32)
# QT
set(QT_MIN_VERSION 5.5)
set(QT_COMPONENTS Core Concurrent Widgets Network Sql)
@@ -139,7 +124,7 @@ if(WIN32)
list(APPEND QT_COMPONENTS WinExtras)
endif()
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED ${QT_COMPONENTS})
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS})
set(QT_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Sql_LIBRARIES})
@@ -157,7 +142,7 @@ if(Qt5WinExtras_FOUND)
list(APPEND QT_LIBRARIES ${Qt5WinExtras_LIBRARIES})
endif()
find_package(Qt5LinguistTools CONFIG)
find_package(Qt5 ${QT_MIN_VERSION} QUIET COMPONENTS LinguistTools CONFIG)
if (Qt5LinguistTools_FOUND)
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
endif()
@@ -303,7 +288,7 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
)
optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" CDIO_FOUND
DEPENDS "libcdio" LIBCDIO_FOUND
)
optional_component(UDISKS2 ON "Devices: UDisks2 backend"

View File

@@ -2,6 +2,39 @@ Strawberry Music Player
=======================
ChangeLog
Version 0.6.8:
* Fixed stuck tabbar and collection GUI with some themes.
* Fixed possible crashes related to QProxyStyle.
* Fixed a bug where metadata in the playlist was not updated when editing metadata for the current playing track.
* Fixed crash when deleting a folder with playlists.
* Increased lyrics score if lyrics text is larger than 60 characters to avoid using "no lyrics available" text.
* Made context title and summary changeable.
* Added option to disable playlist clear button.
* Added confirmation dialog before clearing playlists with more than 500 songs.
* Added German, French and Indonesian translations.
* Added StartupWMClass to desktop file.
* Replaced use of Qt deprecated functionality as of 5.14.
* (macOS) Fixed filesystem watcher to correctly pick up changed collection directories.
* (Windows) Fixed translations not being included.
Version 0.6.7:
* Fixed crash when cancelling scrobbler authentication
* Fixed "Double clicking a song in the playlist" behaviour setting
* Fixed "Pressing Previous in player" behaviour setting
* Fixed updating compilations where there are spaces or special characters in filenames
* Fixed cases where songs were stuck in "Various Artists" because not all songs in
the same compilation was removed from the model before readded with actual artist.
* Fixed a bug when importing playlists where metadata was reset
* Fixed scrobbler to also scrobble songs without album title
* Fixed text for replay gain setting not loading in backend setting
* Added back lyrics from Chartlyrics
* Added ability to show fullsize cover on double-click in playing widget
* Added separator between "unset cover" and "show fullsize" in popup menu
* Removed left click on analyzer to popup menu
* (Windows) Added killproc executable to terminate running process before uninstalling
Version 0.6.6:
* Fixed lowercased album artist in playlist column

View File

@@ -1,5 +1,5 @@
:strawberry: Strawberry Music Player [![Build Status](https://travis-ci.org/jonaski/strawberry.svg?branch=master)](https://travis-ci.org/jonaski/strawberry)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/jonaskvinge)
=======================
Strawberry is a music player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles. The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.
@@ -19,15 +19,15 @@ Strawberry is a music player and music collection organizer. It is a fork of Cle
* Advanced audio output and device configuration for bit-perfect playback on Linux
* Edit tags on music files
* Fetch tags from MusicBrainz
* Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal
* Song lyrics from AudD, lyrics.ovh and lololyrics.com
* Album cover art from [Last.fm](https://www.last.fm/), [Musicbrainz](https://musicbrainz.org/), [Discogs](https://www.discogs.com/), [Deezer](https://www.deezer.com/) and [Tidal](https://tidal.com/)
* Song lyrics from [AudD](https://audd.io/), [ChartLyrics](http://www.chartlyrics.com/), [lyrics.ovh](https://lyrics.ovh/) and [lololyrics.com](https://www.lololyrics.com/)
* Support for multiple backends
* Audio analyzer
* Audio equalizer
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
* Subsonic streaming support
* Unofficial streaming support for Tidal and Qobuz
* Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
* Unofficial streaming support for [Tidal](https://tidal.com/) and [Qobuz](https://www.qobuz.com/)
* Scrobbler with support for [Last.fm](https://www.last.fm/), [Libre.fm](https://libre.fm/) and [ListenBrainz](https://listenbrainz.org/)
**Tidal and Qobuz streaming in Strawberry is unofficial. You need an official API token (or App ID/Secret) to use it, we can not provide API tokens, or help getting them. Tidal will not work with Tidal Masters (MQA), because MQA is a proprietary format in lossy quality without an open source decoder, we can't support it.**
@@ -91,4 +91,4 @@ You should also install the gstreamer plugins base and good, and optionally bad
### :moneybag: Donate
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/jonaskvinge)

View File

@@ -1,6 +1,6 @@
add_custom_target(dmg
COMMAND sudo /usr/local/opt/qt5/bin/macdeployqt strawberry.app
COMMAND sudo ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND sudo ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

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

2
debian/control vendored
View File

@@ -58,7 +58,7 @@ Description: Audio player and music collection organizer
- Edit tags on music files
- Fetch tags from MusicBrainz
- Album cover art from Lastfm, Musicbrainz, Discogs, Deezer and Tidal
- Song lyrics from AudD, lyrics.ovh and lololyrics.com
- Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
- Support for multiple backends
- Audio analyzer
- Audio equalizer

48
debian/copyright vendored
View File

@@ -17,6 +17,9 @@ Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
License: Apache-2.0
Files: src/core/main.h
src/core/iconloader.cpp
src/core/iconloader.h
src/core/iconmapper.h
src/config.h.in
src/version.h.in
src/context/contextview.cpp
@@ -25,6 +28,8 @@ Files: src/core/main.h
src/engine/enginetype.h
src/engine/alsadevicefinder.cpp
src/engine/alsadevicefinder.h
src/engine/mmdevicefinder.cpp
src/engine/mmdevicefinder.h
src/engine/devicefinder.cpp
src/engine/devicefinder.h
src/engine/enginedevice.cpp
@@ -33,6 +38,8 @@ Files: src/core/main.h
src/engine/phononengine.h
src/internet/internetservice.cpp
src/internet/internetservice.h
src/internet/internettabsview.cpp
src/internet/internettabsview.h
src/settings/backendsettingspage.cpp
src/settings/backendsettingspage.h
src/settings/scrobblersettingspage.cpp
@@ -72,28 +79,50 @@ Files: src/core/main.cpp
src/core/player.h
src/core/song.cpp
src/core/song.h
src/core/songloader.cpp
src/core/songloader.h
src/core/urlhandler.cpp
src/core/urlhandler.h
src/core/utilities.cpp
src/core/utilities.h
src/core/iconloader.cpp
src/core/iconloader.h
src/core/network.cpp
src/core/network.h
src/core/filesystemmusicstorage.cpp
src/core/filesystemmusicstorage.h
src/core/stylesheetloader.cpp
src/core/stylesheetloader.h
src/engine/gstenginepipeline.cpp
src/engine/gstenginepipeline.h
src/engine/vlcengine.cpp
src/engine/vlcengine.h
src/collection/collectionwatcher.cpp
src/collection/collectionwatcher.h
src/collection/collectionbackend.cpp
src/collection/collectionbackend.h
src/collection/collectionmodel.cpp
src/collection/collectionmodel.h
src/context/contextalbumsmodel.cpp
src/context/contextalbumsview.cpp
src/context/contextalbumsmodel.h
src/context/contextalbumsview.h
src/widgets/playingwidget.cpp
src/widgets/playingwidget.h
src/widgets/osdpretty.cpp
src/widgets/osdpretty.h
src/dialogs/about.cpp
src/dialogs/about.h
src/playlist/playlist.cpp
src/playlist/playlist.h
src/playlist/playlistitem.cpp
src/playlist/playlistitem.h
src/playlist/playlistdelegates.cpp
src/playlist/playlistdelegates.h
src/playlist/playlistbackend.cpp
src/playlist/playlistbackend.h
src/playlist/playlistview.cpp
src/playlist/playlistview.h
src/playlist/songplaylistitem.cpp
src/playlist/songplaylistitem.h
src/internet/internetplaylistitem.cpp
src/internet/internetsearch.cpp
src/internet/internetsearch.h
@@ -101,6 +130,10 @@ Files: src/core/main.cpp
src/internet/internetsearchview.h
src/internet/internetservices.cpp
src/internet/internetservices.h
src/internet/internetsongsview.cpp
src/internet/internetsongsview.h
src/internet/internetcollectionview.cpp
src/internet/internetcollectionview.h
ext/libstrawberry-tagreader/tagreader.cpp
ext/libstrawberry-tagreader/tagreader.h
src/device/devicemanager.cpp
@@ -113,10 +146,16 @@ Files: src/core/main.cpp
src/device/deviceview.h
src/device/connecteddevice.cpp
src/device/connecteddevice.h
src/device/mtpconnection.cpp
src/device/mtpconnection.h
src/device/mtpdevice.cpp
src/device/mtpdevice.h
src/globalshortcuts/globalshortcuts.cpp
src/globalshortcuts/globalshortcuts.h
src/settings/shortcutssettingspage.cpp
src/settings/shortcutssettingspage.h
src/settings/appearancesettingspage.cpp
src/settings/appearancesettingspage.h
src/organise/organise.cpp
src/organise/organise.h
src/organise/organisedialog.cpp
@@ -125,8 +164,10 @@ Files: src/core/main.cpp
src/organise/organiseerrordialog.h
src/transcoder/transcoder.cpp
src/transcoder/transcoder.h
src/musicbrainz/musicbrainzclient.cpp
src/musicbrainz/musicbrainzclient.h
Copyright: 2010, 2012-2014 David Sansome <me@davidsansome.com>
2012-2014, 2017-2018 Jonas Kvinge <jonas@jkvinge.net>
2012-2014, 2017-2019 Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/engine/enginebase.cpp
@@ -262,6 +303,7 @@ Files: src/internet/localredirectserver.cpp
src/internet/localredirectserver.h
Copyright: 2012, 2014, John Maguire <john.maguire@gmail.com>
2014, Krzysztof Sobiecki <sobkas@gmail.com>
2018-2019, Jonas Kvinge <jonas@jkvinge.net>
License: GPL-3+
Files: src/transcoder/transcoderoptionsopus.cpp

View File

@@ -304,10 +304,9 @@ def CopyLibrary(path):
new_path = os.path.join(frameworks_dir, os.path.basename(path))
#args = ['cp', path, new_path]
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
commands.append(args)
commands.append(['chmod', '+w', new_path])
LOGGER.info("Copying library '%s'", path)
commands.append(args)
args = ['chmod', 'u+w', new_path]
commands.append(args)
return new_path
@@ -318,9 +317,8 @@ def CopyPlugin(path, subdir):
#args = ['cp', path, new_path]
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
commands.append(args)
commands.append(['chmod', '+w', new_path])
LOGGER.info("Copying plugin '%s'", path)
args = ['chmod', 'u+w', new_path]
commands.append(args)
return new_path
def CopyFramework(path):
@@ -351,6 +349,7 @@ def CopyFramework(src_binary):
commands.append(['mkdir', '-p', dest_dir])
commands.append(['cp', src_binary, dest_binary])
commands.append(['chmod', '+w', dest_binary])
# Copy special files from various places:
# QtCore has Resources/qt_menu.nib (copy to app's Resources)

View File

@@ -27,7 +27,7 @@ Features:
.br
- Album cover art from Lastfm, Musicbrainz, Discogs, Deezer and Tidal
.br
- Song lyrics from AudD, lyrics.ovh and lololyrics.com
- Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
.br
- Support for multiple backends
.br

View File

@@ -44,7 +44,9 @@ BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(gnutls)
BuildRequires: pkgconfig(alsa)
BuildRequires: pkgconfig(protobuf)
%if ! 0%{?centos}
BuildRequires: pkgconfig(sqlite3) >= 3.9
%endif
%if ! 0%{?centos} && ! 0%{?mageia}
BuildRequires: pkgconfig(taglib)
%endif
@@ -103,7 +105,7 @@ Features:
- Edit tags on music files
- Fetch tags from MusicBrainz
- Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal
- Song lyrics from AudD, lyrics.ovh and lololyrics.com
- Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
- Support for multiple backends
- Audio analyzer
- Audio equalizer

View File

@@ -11,3 +11,4 @@ Terminal=false
Categories=AudioVideo;Player;Qt;Audio;
StartupNotify=false
MimeType=x-content/audio-player;application/ogg;application/x-ogg;application/x-ogm-audio;audio/flac;audio/ogg;audio/vorbis;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-speex;audio/x-wav;audio/x-wavpack;audio/x-ape;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;video/x-ms-asf;x-scheme-handler/tidal;
StartupWMClass=strawberry

View File

@@ -68,13 +68,12 @@ SetCompressor /SOLID lzma
!include "MUI2.nsh"
!include "FileAssociation.nsh"
!include "Capabilities.nsh"
!include LogicLib.nsh
!include x64.nsh
!define MUI_ICON "strawberry.ico"
!define MUI_COMPONENTSPAGE_SMALLDESC
;!define MUI_FINISHPAGE_RUN
;!define MUI_FINISHPAGE_RUN_TEXT "Run Strawberry"
;!define MUI_FINISHPAGE_RUN_FUNCTION "RunStrawberry"
; Installer pages
!insertmacro MUI_PAGE_WELCOME
@@ -144,16 +143,14 @@ Function .onInit
FunctionEnd
;Function RunStrawberry
;ShellExecAsUser::ShellExecAsUser "" "$INSTDIR/strawberry.exe" ""
;FunctionEnd
Section "Delete old files" oldfiles
SectionEnd
Section "Strawberry" Strawberry
SetOutPath "$INSTDIR"
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
File "strawberry.exe"
File "strawberry-tagreader.exe"
File "strawberry.ico"
@@ -199,7 +196,7 @@ Section "Strawberry" Strawberry
File "libpcre-1.dll"
File "libpcre2-16-0.dll"
File "libpng16-16.dll"
File "libprotobuf-21.dll"
File "libprotobuf-22.dll"
File "libsoup-2.4-1.dll"
File "libspeex-1.dll"
File "libsqlite3-0.dll"
@@ -243,6 +240,8 @@ Section "Strawberry" Strawberry
File "libxine-2.dll"
!endif
File "killproc.exe"
; Register Strawberry with Default Programs
Var /GLOBAL AppIcon
Var /GLOBAL AppExe
@@ -406,28 +405,8 @@ Section "Uninstaller"
SectionEnd
Section "Uninstall"
; Kill strawberry.exe if it's running
; This calling convention is retarded...
;StrCpy $0 "strawberry.exe"
;KillProc::FindProcesses
;StrCmp $1 "-1" wooops
;StrCmp $0 "0" completed
;DetailPrint "Killing running strawberry.exe..."
;StrCpy $0 "strawberry.exe"
;KillProc::KillProcesses
;StrCmp $1 "-1" wooops
;Sleep 2000
;Goto completed
;wooops:
;DetailPrint "-> Error: Something went wrong while killing running strawberry.exe"
;Abort
;completed:
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
; Delete all the files
@@ -476,7 +455,7 @@ Section "Uninstall"
Delete "$INSTDIR\libpcre-1.dll"
Delete "$INSTDIR\libpcre2-16-0.dll"
Delete "$INSTDIR\libpng16-16.dll"
Delete "$INSTDIR\libprotobuf-21.dll"
Delete "$INSTDIR\libprotobuf-22.dll"
Delete "$INSTDIR\libsoup-2.4-1.dll"
Delete "$INSTDIR\libspeex-1.dll"
Delete "$INSTDIR\libsqlite3-0.dll"
@@ -602,12 +581,14 @@ Section "Uninstall"
Delete "$INSTDIR\xine-plugins\xineplug_post_visualizations.dll"
!endif
Delete "$INSTDIR\killproc.exe"
Delete "$INSTDIR\Uninstall.exe"
; Remove the installation folders.
RMDir "$INSTDIR\platforms"
RMDir "$INSTDIR\sqldrivers"
RMDir "$INSTDIR\imageformats"
RMDir "$INSTDIR\gio-modules"
RMDir "$INSTDIR\gstreamer-plugins"
RMDir "$INSTDIR\xine-plugins"
RMDir "$INSTDIR"

View File

@@ -2,10 +2,10 @@ cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -5,10 +5,10 @@ include_directories(${CMAKE_SOURCE_DIR}/src)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
set(SOURCES

View File

@@ -159,7 +159,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
next_worker_(0),
next_id_(0) {
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2);
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4);
local_server_name_ = qApp->applicationName().toLower();
if (local_server_name_.isEmpty())

View File

@@ -7,10 +7,10 @@ include_directories(${CMAKE_BINARY_DIR}/src)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
set(MESSAGES

View File

@@ -290,7 +290,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
// Find a suitable comment tag. For now we ignore iTunNORM comments.
for (int 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") {
@@ -300,7 +300,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
}
// Parse FMPS frames
for (int i = 0; i < map["TXXX"].size(); ++i) {
for (uint i = 0; i < map["TXXX"].size(); ++i) {
const TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i]);
if (frame && frame->description().startsWith("FMPS_")) {
@@ -836,7 +836,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
TagLib::ByteVector data = it->second.binaryData();
int pos = data.find('\0') + 1;
if ((pos > 0) && (pos < data.size())) {
if ((pos > 0) && ((uint)pos < data.size())) {
ret = QByteArray(data.data() + pos, data.size() - pos);
}
}

View File

@@ -9,10 +9,10 @@ include_directories(${CMAKE_BINARY_DIR}/src)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})

View File

@@ -1,5 +1,5 @@
name: strawberry
version: '0.6.6+git'
version: '0.6.8+git'
summary: music player and collection organizer
description: |
Strawberry is a music player and collection organizer.

View File

@@ -16,10 +16,10 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
option(BUILD_WERROR "Build with -Werror" OFF)
@@ -44,12 +44,11 @@ add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
include_directories(${CMAKE_BINARY_DIR})
include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${GOBJECT_INCLUDE_DIRS})
include_directories(${GNUTLS_INCLUDE_DIR})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
if (X11_FOUND)
include_directories(${X11_INCLUDE_DIR})
@@ -58,16 +57,31 @@ endif(X11_FOUND)
if(HAVE_GSTREAMER)
link_directories(${GSTREAMER_LIBRARY_DIRS})
include_directories(${GSTREAMER_INCLUDE_DIRS})
include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
link_directories(${GSTREAMER_BASE_LIBRARY_DIRS})
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
link_directories(${GSTREAMER_APP_LIBRARY_DIRS})
include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
link_directories(${GSTREAMER_AUDIO_LIBRARY_DIRS})
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
link_directories(${GSTREAMER_TAG_LIBRARY_DIRS})
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
link_directories(${GSTREAMER_PBUTILS_LIBRARY_DIRS})
include_directories(${GSTREAMER_PBUTILS_INCLUDE_DIRS})
endif()
endif(HAVE_GSTREAMER)
if(HAVE_PHONON)
include_directories(${PHONON_INCLUDE_DIRS})
endif()
endif(HAVE_PHONON)
if(HAVE_CHROMAPRINT)
link_directories(${CHROMAPRINT_LIBRARY_DIRS})
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
endif(HAVE_CHROMAPRINT)
if(HAVE_AUDIOCD)
link_directories(${LIBCDIO_LIBRARY_DIRS})
include_directories(${LIBCDIO_INCLUDE_DIRS})
endif(HAVE_AUDIOCD)
link_directories(${TAGLIB_LIBRARY_DIRS})
include_directories(${TAGLIB_INCLUDE_DIRS})
@@ -77,6 +91,11 @@ link_directories(${SINGLECOREAPPLICATION_LIBRARY_DIRS})
include_directories(${SINGLEAPPLICATION_INCLUDE_DIRS})
include_directories(${SINGLECOREAPPLICATION_INCLUDE_DIRS})
if(HAVE_LIBMTP)
link_directories(${LIBMTP_LIBRARY_DIRS})
include_directories(${LIBMTP_INCLUDE_DIRS})
endif(HAVE_LIBMTP)
include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-common)
include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader)
include_directories(${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader)
@@ -216,12 +235,14 @@ set(SOURCES
lyrics/auddlyricsprovider.cpp
lyrics/ovhlyricsprovider.cpp
lyrics/lololyricsprovider.cpp
lyrics/chartlyricsprovider.cpp
settings/settingsdialog.cpp
settings/settingspage.cpp
settings/behavioursettingspage.cpp
settings/collectionsettingspage.cpp
settings/backendsettingspage.cpp
settings/contextsettingspage.cpp
settings/playlistsettingspage.cpp
settings/networkproxysettingspage.cpp
settings/appearancesettingspage.cpp
@@ -400,12 +421,14 @@ set(HEADERS
lyrics/auddlyricsprovider.h
lyrics/ovhlyricsprovider.h
lyrics/lololyricsprovider.h
lyrics/chartlyricsprovider.h
settings/settingsdialog.h
settings/settingspage.h
settings/behavioursettingspage.h
settings/collectionsettingspage.h
settings/backendsettingspage.h
settings/contextsettingspage.h
settings/playlistsettingspage.h
settings/networkproxysettingspage.h
settings/appearancesettingspage.h
@@ -503,6 +526,7 @@ set(UI
settings/behavioursettingspage.ui
settings/collectionsettingspage.ui
settings/backendsettingspage.ui
settings/contextsettingspage.ui
settings/playlistsettingspage.ui
settings/networkproxysettingspage.ui
settings/appearancesettingspage.ui
@@ -1069,7 +1093,7 @@ if(HAVE_GIO)
endif(HAVE_GIO)
if(HAVE_AUDIOCD)
target_link_libraries(strawberry_lib ${CDIO_LIBRARIES})
target_link_libraries(strawberry_lib ${LIBCDIO_LIBRARIES})
endif(HAVE_AUDIOCD)
if(HAVE_IMOBILEDEVICE)
@@ -1113,10 +1137,7 @@ if (APPLE)
endif (APPLE)
if (WIN32)
target_link_libraries(strawberry_lib
${ZLIB_LIBRARIES}
dsound
)
target_link_libraries(strawberry_lib dsound)
endif (WIN32)
if (UNIX AND NOT APPLE)

View File

@@ -72,7 +72,7 @@ void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) {
QVector<float> aux(fht_->size());
if (aux.size() >= scope.size()) {
if ((long unsigned int)aux.size() >= scope.size()) {
std::copy(scope.begin(), scope.end(), aux.begin());
}
else {

View File

@@ -29,7 +29,6 @@
# include <sys/types.h>
#endif
#include <stdbool.h>
#include <vector>
#include <QtGlobal>

View File

@@ -59,7 +59,6 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
context_menu_framerate_(new QMenu(tr("Framerate"), this)),
group_(new QActionGroup(this)),
group_framerate_(new QActionGroup(this)),
visualisation_action_(nullptr),
double_click_timer_(new QTimer(this)),
ignore_next_click_(false),
current_analyzer_(nullptr),
@@ -88,7 +87,6 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
group_->addAction(disable_action_);
context_menu_->addSeparator();
// Visualisation action gets added in SetActions
double_click_timer_->setSingleShot(true);
double_click_timer_->setInterval(250);
@@ -98,26 +96,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
}
void AnalyzerContainer::SetActions(QAction *visualisation) {
visualisation_action_ = visualisation;
context_menu_->addAction(visualisation_action_);
}
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
if (engine_->type() != Engine::EngineType::GStreamer && engine_->type() != Engine::EngineType::Xine) return;
if (e->button() == Qt::LeftButton) {
if (ignore_next_click_) {
ignore_next_click_ = false;
}
else {
// Might be the first click in a double click, so wait a while before actually doing anything
double_click_timer_->start();
last_click_pos_ = e->globalPos();
}
}
else if (e->button() == Qt::RightButton) {
if (e->button() == Qt::RightButton) {
context_menu_->popup(e->globalPos());
}
@@ -127,16 +110,6 @@ void AnalyzerContainer::ShowPopupMenu() {
context_menu_->popup(last_click_pos_);
}
void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent*) {
if (engine_->type() != Engine::EngineType::GStreamer && engine_->type() != Engine::EngineType::Xine) return;
double_click_timer_->stop();
ignore_next_click_ = true;
if (visualisation_action_) visualisation_action_->trigger();
}
void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
emit WheelEvent(e->delta());
}

View File

@@ -22,7 +22,6 @@
#include "config.h"
#include <stdbool.h>
#include <QWidget>
#include <QList>
@@ -59,7 +58,6 @@ signals:
protected:
void mouseReleaseEvent(QMouseEvent*);
void mouseDoubleClickEvent(QMouseEvent*);
void wheelEvent(QWheelEvent *e);
private slots:
@@ -93,7 +91,6 @@ signals:
QList<QAction*> actions_;
QAction *disable_action_;
QAction *visualisation_action_;
QTimer *double_click_timer_;
QPoint last_click_pos_;
bool ignore_next_click_;

View File

@@ -196,7 +196,7 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
}
for (uint x = 0; x < store_.size(); ++x)
for (int x = 0; x < store_.size(); ++x)
canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
p.drawPixmap(0, 0, canvas_);

View File

@@ -24,7 +24,6 @@
#ifndef BLOCKANALYZER_H
#define BLOCKANALYZER_H
#include <stdbool.h>
#include <vector>
#include <QtGlobal>

View File

@@ -21,7 +21,6 @@
#include "config.h"
#include <stdbool.h>
#include <unistd.h>
#include <QObject>

View File

@@ -2,6 +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>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -284,12 +285,6 @@ void CollectionBackend::AddDirectory(const QString &path) {
QString canonical_path = QFileInfo(path).canonicalFilePath();
QString db_path = canonical_path;
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(canonical_path))) {
db_path = Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(db_path)).toLocalFile();
qLog(Debug) << "db_path" << db_path;
}
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@@ -350,7 +345,7 @@ SongList CollectionBackend::FindSongsInDirectory(int id) {
SongList ret;
while (q.next()) {
Song song;
Song song(source_);
song.InitFromQuery(q, true);
ret << song;
}
@@ -663,7 +658,7 @@ QStringList CollectionBackend::GetAllArtistsWithAlbums(const QueryOptions &opt)
artists << query2.Value(0).toString();
}
return QStringList(artists.toList());
return QStringList(artists.values());
}
@@ -698,7 +693,7 @@ SongList CollectionBackend::ExecCollectionQuery(CollectionQuery *query) {
SongList ret;
while (query->Next()) {
Song song;
Song song(source_);
song.InitFromQuery(*query, true);
ret << song;
}
@@ -772,7 +767,7 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &d
SongList ret;
while (q.next()) {
Song song;
Song song(source_);
song.InitFromQuery(q, true);
ret << song;
}
@@ -780,36 +775,52 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &d
}
Song CollectionBackend::GetSongByUrl(const QUrl &url, qint64 beginning) {
Song CollectionBackend::GetSongByUrl(const QUrl &url, const qint64 beginning) {
CollectionQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("url", url.toString());
query.AddWhere("beginning", beginning);
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
Song song;
if (ExecQuery(&query) && query.Next()) {
song.InitFromQuery(query, true);
QSqlQuery q(db);
q.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND beginning = :beginning AND unavailable = 0").arg(songs_table_));
q.bindValue(":url1", url);
q.bindValue(":url2", url.toString());
q.bindValue(":url3", url.toString(QUrl::FullyEncoded));
q.bindValue(":url4", url.toEncoded());
q.bindValue(":beginning", beginning);
Song song(source_);
if (q.exec() && q.next()) {
song.InitFromQuery(q, true);
}
return song;
}
SongList CollectionBackend::GetSongsByUrl(const QUrl &url) {
CollectionQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("url", url.toString());
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
SongList songlist;
if (ExecQuery(&query)) {
while (query.Next()) {
Song song;
song.InitFromQuery(query, true);
songlist << song;
QSqlQuery q(db);
q.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND unavailable = 0").arg(songs_table_));
q.bindValue(":url1", url);
q.bindValue(":url2", url.toString());
q.bindValue(":url3", url.toString(QUrl::FullyEncoded));
q.bindValue(":url4", url.toEncoded());
SongList songs;
if (q.exec()) {
while (q.next()) {
Song song(source_);
song.InitFromQuery(q, true);
songs << song;
}
}
return songlist;
return songs;
}
@@ -856,7 +867,7 @@ SongList CollectionBackend::GetSongsBySongId(const QStringList &song_ids, QSqlDa
SongList ret;
while (q.next()) {
Song song;
Song song(source_);
song.InitFromQuery(q, true);
ret << song;
}
@@ -880,7 +891,7 @@ SongList CollectionBackend::GetCompilationSongs(const QString &album, const Quer
SongList ret;
while (query.Next()) {
Song song;
Song song(source_);
song.InitFromQuery(query, true);
ret << song;
}
@@ -911,23 +922,22 @@ void CollectionBackend::UpdateCompilations() {
if (album.isEmpty()) continue;
// Find the directory the song is in
QString directory = url.toString(QUrl::PreferLocalFile|QUrl::RemoveFilename|QUrl::StripTrailingSlash);
QString directory = url.toString(QUrl::PreferLocalFile|QUrl::RemoveFilename);
CompilationInfo &info = compilation_info[directory + album];
info.urls << url;
info.directory = directory;
info.album = album;
info.artists.insert(artist);
if (!info.artists.contains(artist))
info.artists << artist;
if (compilation_detected) info.has_compilation_detected++;
else info.has_not_compilation_detected++;
}
// Now mark the songs that we think are in compilations
QSqlQuery find_songs(db);
find_songs.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE url = :url AND compilation_detected = :compilation_detected AND unavailable = 0").arg(songs_table_));
QSqlQuery find_song(db);
find_song.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND unavailable = 0").arg(songs_table_));
QSqlQuery update_songs(db);
update_songs.prepare(QString("UPDATE %1 SET compilation_detected = :compilation_detected, compilation_effective = ((compilation OR :compilation_detected OR compilation_on) AND NOT compilation_off) + 0 WHERE url = :url AND unavailable = 0").arg(songs_table_));
QSqlQuery update_song(db);
update_song.prepare(QString("UPDATE %1 SET compilation_detected = :compilation_detected, compilation_effective = ((compilation OR :compilation_detected OR compilation_on) AND NOT compilation_off) + 0 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND unavailable = 0").arg(songs_table_));
SongList deleted_songs;
SongList added_songs;
@@ -938,16 +948,16 @@ void CollectionBackend::UpdateCompilations() {
for (; it != compilation_info.constEnd(); ++it) {
const CompilationInfo &info = it.value();
// If there were more 'effective album artists' than there were directories for this album then it's a compilation.
// If there were more than one 'effective album artist' for this album directory, then it's a compilation.
for (const QUrl &url : info.urls) {
if (info.artists.count() > 1) { // This directory+album is a compilation.
if (info.has_not_compilation_detected > 0) // Run updates if any of the songs is not marked as compilations.
UpdateCompilations(find_songs, update_songs, deleted_songs, added_songs, url, true);
UpdateCompilations(find_song, update_song, deleted_songs, added_songs, url, true);
}
else {
if (info.has_compilation_detected > 0)
UpdateCompilations(find_songs, update_songs, deleted_songs, added_songs, url, false);
UpdateCompilations(find_song, update_song, deleted_songs, added_songs, url, false);
}
}
}
@@ -961,25 +971,32 @@ void CollectionBackend::UpdateCompilations() {
}
void CollectionBackend::UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected) {
void CollectionBackend::UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected) {
// Get song, so we can tell the model its updated
find_songs.bindValue(":url", url.toString());
find_songs.bindValue(":compilation_detected", int(!compilation_detected));
find_songs.exec();
while (find_songs.next()) {
Song song;
song.InitFromQuery(find_songs, true);
deleted_songs << song;
song.set_compilation_detected(compilation_detected);
added_songs << song;
find_song.bindValue(":url1", url);
find_song.bindValue(":url2", url.toString());
find_song.bindValue(":url3", url.toString(QUrl::FullyEncoded));
find_song.bindValue(":url4", url.toEncoded());
if (find_song.exec()) {
while (find_song.next()) {
Song song(source_);
song.InitFromQuery(find_song, true);
deleted_songs << song;
song.set_compilation_detected(compilation_detected);
added_songs << song;
}
}
// Update the song
update_songs.bindValue(":compilation_detected", int(compilation_detected));
update_songs.bindValue(":url", url);
update_songs.exec();
db_->CheckErrors(update_songs);
update_song.bindValue(":compilation_detected", int(compilation_detected));
update_song.bindValue(":url1", url);
update_song.bindValue(":url2", url.toString());
update_song.bindValue(":url3", url.toString(QUrl::FullyEncoded));
update_song.bindValue(":url4", url.toEncoded());
update_song.exec();
db_->CheckErrors(update_song);
}
@@ -1021,7 +1038,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
info.art_automatic = QUrl::fromEncoded(art_automatic.toUtf8());
}
else {
info.art_automatic = QUrl::fromLocalFile(art_automatic.toUtf8());
info.art_automatic = QUrl::fromLocalFile(art_automatic);
}
QString art_manual = query.Value(6).toString();
@@ -1029,7 +1046,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
info.art_manual = QUrl::fromEncoded(art_manual.toUtf8());
}
else {
info.art_manual = QUrl::fromLocalFile(art_manual.toUtf8());
info.art_manual = QUrl::fromLocalFile(art_manual);
}
if ((info.artist == last_artist || info.album_artist == last_album_artist) && info.album_name == last_album)
@@ -1067,8 +1084,8 @@ CollectionBackend::Album CollectionBackend::GetAlbumArt(const QString &artist, c
if (!ExecQuery(&query)) return ret;
if (query.Next()) {
ret.art_automatic = query.Value(0).toUrl();
ret.art_manual = query.Value(1).toUrl();
ret.art_automatic = QUrl::fromEncoded(query.Value(0).toByteArray());
ret.art_manual = QUrl::fromEncoded(query.Value(1).toByteArray());
ret.first_url = QUrl::fromEncoded(query.Value(2).toByteArray());
}
@@ -1103,7 +1120,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
SongList deleted_songs;
while (query.Next()) {
Song song;
Song song(source_);
song.InitFromQuery(query, true);
deleted_songs << song;
}
@@ -1111,7 +1128,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
// Update the songs
QString sql(QString("UPDATE %1 SET art_manual = :cover WHERE album = :album AND unavailable = 0").arg(songs_table_));
if (!albumartist.isNull() && !albumartist.isEmpty()) {
if (!albumartist.isEmpty()) {
sql += " AND albumartist = :albumartist";
}
else if (!artist.isNull()) {
@@ -1120,7 +1137,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
QSqlQuery q(db);
q.prepare(sql);
q.bindValue(":cover", cover_url);
q.bindValue(":cover", cover_url.toString(QUrl::FullyEncoded));
q.bindValue(":album", album);
if (!albumartist.isEmpty()) {
q.bindValue(":albumartist", albumartist);
@@ -1137,7 +1154,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
SongList added_songs;
while (query.Next()) {
Song song;
Song song(source_);
song.InitFromQuery(query, true);
added_songs << song;
}
@@ -1165,7 +1182,7 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
if (!ExecQuery(&query)) return;
while (query.Next()) {
Song song;
Song song(source_);
song.InitFromQuery(query, true);
deleted_songs << song;
}
@@ -1188,7 +1205,7 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
if (!ExecQuery(&query)) return;
while (query.Next()) {
Song song;
Song song(source_);
song.InitFromQuery(query, true);
added_songs << song;
}

View File

@@ -2,6 +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>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
@@ -109,7 +109,7 @@ class CollectionBackendInterface : public QObject {
virtual SongList GetSongsByUrl(const QUrl &url) = 0;
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) = 0;
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
virtual void AddDirectory(const QString &path) = 0;
virtual void RemoveDirectory(const Directory &dir) = 0;
@@ -227,16 +227,14 @@ class CollectionBackend : public CollectionBackendInterface {
struct CompilationInfo {
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
QString directory;
QString album;
QList<QUrl> urls;
QSet<QString> artists;
QStringList artists;
int has_compilation_detected;
int has_not_compilation_detected;
};
void UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
void UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
AlbumList GetAlbums(const QString &artist, const QString &album_artist, bool compilation = false, const QueryOptions &opt = QueryOptions());
AlbumList GetAlbums(const QString &artist, bool compilation, const QueryOptions &opt = QueryOptions());
SubdirectoryList SubdirsInDirectory(int id, QSqlDatabase &db);

View File

@@ -53,13 +53,7 @@ CollectionDirectoryModel::~CollectionDirectoryModel() {}
void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
QStandardItem *item;
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(dir.path))) {
item = new QStandardItem(Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(dir.path)).toLocalFile());
}
else {
item = new QStandardItem(dir.path);
}
QStandardItem *item = new QStandardItem(dir.path);
item->setData(dir.id, kIdRole);
item->setIcon(dir_icon_);
storage_ << std::shared_ptr<MusicStorage>(new FilesystemMusicStorage(dir.path));

View File

@@ -61,9 +61,28 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
ui_->setupUi(this);
// Add the available fields to the tooltip here instead of the ui file to prevent that they get translated by mistake.
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegExp("\\bfts"), "");
ui_->filter->setToolTip(ui_->filter->toolTip().arg(available_fields));
ui_->filter->setToolTip(
"<html><head/><body><p>" +
tr("Prefix a word with a field name to limit the search to that field, e.g.:") +
" " +
"<span style=\"font-weight:600;\">" +
tr("artist") +
":" +
"</span><span style=\"font-style:italic;\">Strawbs</span>" +
" " +
tr("searches the collection for all artists that contain the word") +
"Strawbs" +
"." +
"</p><p><span style=\"font-weight:600;\">" +
tr("Available fields") +
": " +
"</span><span style=\"font-style:italic;\">" +
available_fields +
"</span>." +
"</p></body></html>"
);
connect(ui_->filter, SIGNAL(returnPressed()), SIGNAL(ReturnPressed()));
connect(filter_delay_, SIGNAL(timeout()), SLOT(FilterDelayTimeout()));

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QWidget>
#include <QObject>

View File

@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
<string>Collection Filter</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
@@ -31,9 +31,6 @@
</property>
<item>
<widget class="QSearchField" name="filter" native="true">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Prefix a word with a field name to limit the search to that field, e.g. &lt;span style=&quot; font-weight:600;&quot;&gt;artist:&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Bode&lt;/span&gt; searches the collection for all artists that contain the word Bode.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Available fields: &lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;%1&lt;/span&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="placeholderText" stdset="0">
<string>Enter search terms here</string>
</property>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QStyledItemDelegate>
#include <QAbstractItemView>

View File

@@ -37,6 +37,7 @@
#include <QVariant>
#include <QList>
#include <QSet>
#include <QMap>
#include <QChar>
#include <QRegExp>
#include <QString>
@@ -70,7 +71,7 @@ using std::placeholders::_2;
const char *CollectionModel::kSavedGroupingsSettingsGroup = "SavedGroupings";
const int CollectionModel::kPrettyCoverSize = 32;
const qint64 CollectionModel::kIconCacheSize = 100000000; //~100MB
const int CollectionModel::kPixmapCacheLimit = QPixmapCache::cacheLimit() * 8;
static bool IsArtistGroupBy(const CollectionModel::GroupBy by) {
return by == CollectionModel::GroupBy_Artist || by == CollectionModel::GroupBy_AlbumArtist;
@@ -126,7 +127,7 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
backend_->UpdateTotalArtistCountAsync();
backend_->UpdateTotalAlbumCountAsync();
QPixmapCache::setCacheLimit(61440);
QPixmapCache::setCacheLimit(kPixmapCacheLimit);
}
@@ -434,25 +435,11 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
if (node->parent != root_) parents << node->parent;
// Remove from pixmap cache
QModelIndex idx = ItemToIndex(node->parent);
const QString cache_key = AlbumIconPixmapCacheKey(idx);
QPixmapCache::remove(cache_key);
if (pending_cache_keys_.contains(cache_key)) pending_cache_keys_.remove(cache_key);
// Remove from pending art loading
QMapIterator<quint64, ItemAndCacheKey> i(pending_art_);
while (i.hasNext()) {
i.next();
if (i.value().first == node) {
pending_art_.remove(i.key());
break;
}
}
beginRemoveRows(idx, node->row, node->row);
beginRemoveRows(ItemToIndex(node->parent), node->row, node->row);
node->parent->Delete(node->row);
song_nodes_.remove(song.id());
endRemoveRows();
}
else {
// If we get here it means some of the songs we want to delete haven't been lazy-loaded yet.
@@ -487,18 +474,20 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
container_nodes_[node->container_level].remove(node->key);
// Remove from pixmap cache
QModelIndex idx = ItemToIndex(node->parent);
const QString cache_key = AlbumIconPixmapCacheKey(idx);
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(node));
QPixmapCache::remove(cache_key);
if (pending_cache_keys_.contains(cache_key)) pending_cache_keys_.remove(cache_key);
if (pending_cache_keys_.contains(cache_key)) {
pending_cache_keys_.remove(cache_key);
}
// Remove from pending art loading
QMapIterator<quint64, ItemAndCacheKey> i(pending_art_);
while (i.hasNext()) {
i.next();
QMap<quint64, ItemAndCacheKey>::iterator i = pending_art_.begin();
while (i != pending_art_.end()) {
if (i.value().first == node) {
pending_art_.remove(i.key());
break;
i = pending_art_.erase(i);
}
else {
++i;
}
}
@@ -539,15 +528,7 @@ QString CollectionModel::AlbumIconPixmapCacheKey(const QModelIndex &idx) const {
QStringList path;
QModelIndex idx_copy(idx);
while (idx_copy.isValid()) {
//const CollectionItem *item = IndexToItem(idx_copy);
//if (item && group_by_[item->container_level] == GroupBy_Album) {
// QString album = idx_copy.data().toString();
// album.remove(Song::kAlbumRemoveDisc);
// path.prepend(album);
//}
//else {
path.prepend(idx_copy.data().toString());
//}
path.prepend(idx_copy.data().toString());
idx_copy = idx_copy.parent();
}
@@ -859,7 +840,6 @@ void CollectionModel::BeginReset() {
divider_nodes_.clear();
pending_art_.clear();
pending_cache_keys_.clear();
QPixmapCache::clear();
root_ = new CollectionItem(this);
root_->compilation_artist_node_ = nullptr;

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
@@ -70,7 +69,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
static const char *kSavedGroupingsSettingsGroup;
static const int kPrettyCoverSize;
static const qint64 kIconCacheSize;
static const int kPixmapCacheLimit;
enum Role {
Role_Type = Qt::UserRole + 1,

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QVariant>
#include <QString>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QMetaType>
#include <QVariant>

View File

@@ -463,7 +463,11 @@ void CollectionView::ShowInVarious(bool on) {
}
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
for (const QString &album : QSet<QString>(albums.keyBegin(), albums.keyEnd())) {
#else
for (const QString &album : QSet<QString>::fromList(albums.keys())) {
#endif
app_->collection_backend()->ForceCompilation(album, albums.values(album), on);
}

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QObject>
#include <QWidget>

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -18,7 +18,6 @@
*
*/
#include <stdbool.h>
#include <QDialog>
#include <QWidget>

View File

@@ -62,18 +62,13 @@ using std::placeholders::_1;
using std::placeholders::_2;
const int ContextAlbumsModel::kPrettyCoverSize = 32;
const qint64 ContextAlbumsModel::kIconCacheSize = 100000000; //~100MB
ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent) :
SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
backend_(backend),
app_(app),
artist_icon_(IconLoader::Load("folder-sound")),
album_icon_(IconLoader::Load("cdcase")),
playlists_dir_icon_(IconLoader::Load("folder-sound")),
playlist_icon_(IconLoader::Load("albums")),
use_pretty_covers_(true)
{
playlists_dir_icon_(IconLoader::Load("folder-sound")) {
root_->lazy_loaded = true;
@@ -90,15 +85,6 @@ ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *
ContextAlbumsModel::~ContextAlbumsModel() { delete root_; }
void ContextAlbumsModel::set_pretty_covers(bool use_pretty_covers) {
if (use_pretty_covers != use_pretty_covers_) {
use_pretty_covers_ = use_pretty_covers;
Reset();
}
}
void ContextAlbumsModel::AddSongs(const SongList &songs) {
for (const Song &song : songs) {
@@ -199,18 +185,8 @@ QVariant ContextAlbumsModel::data(const QModelIndex &index, int role) const {
const CollectionItem *item = IndexToItem(index);
// Handle a special case for returning album artwork instead of a generic CD icon.
// This is here instead of in the other data() function to let us use the QModelIndex& version of GetChildSongs,
// which satisfies const-ness, instead of the CollectionItem *version, which doesn't.
if (use_pretty_covers_) {
bool is_album_node = false;
if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container) {
is_album_node = (item->container_level == 0);
}
if (is_album_node) {
// It has const behaviour some of the time - that's ok right?
return const_cast<ContextAlbumsModel*>(this)->AlbumIcon(index);
}
if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container && item->container_level == 0) {
return const_cast<ContextAlbumsModel*>(this)->AlbumIcon(index);
}
return data(item, role);
@@ -239,9 +215,6 @@ QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
case Role_Type:
return item->type;
case Role_IsDivider:
return item->type == CollectionItem::Type_Divider;
case Role_ContainerType:
return item->type;
@@ -265,7 +238,8 @@ QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
}
}
return true;
} else {
}
else {
return false;
}
}
@@ -349,11 +323,19 @@ void ContextAlbumsModel::LazyPopulate(CollectionItem *parent, bool signal) {
void ContextAlbumsModel::Reset() {
QMap<QString, CollectionItem*>::iterator i = container_nodes_.begin();
while (i != container_nodes_.end()) {
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(i.value()));
QPixmapCache::remove(cache_key);
++i;
}
beginResetModel();
delete root_;
song_nodes_.clear();
container_nodes_.clear();
pending_art_.clear();
pending_cache_keys_.clear();
root_ = new CollectionItem(this);
root_->lazy_loaded = false;
@@ -434,7 +416,6 @@ Qt::ItemFlags ContextAlbumsModel::flags(const QModelIndex &index) const {
case CollectionItem::Type_Song:
case CollectionItem::Type_Container:
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
case CollectionItem::Type_Divider:
case CollectionItem::Type_Root:
case CollectionItem::Type_LoadingIndicator:
default:

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
@@ -37,11 +36,9 @@
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QMimeData>
#include <QImage>
#include <QIcon>
#include <QPixmap>
#include <QNetworkDiskCache>
#include <QSettings>
#include "core/simpletreemodel.h"
@@ -51,6 +48,8 @@
#include "collection/sqlrow.h"
#include "covermanager/albumcoverloaderoptions.h"
class QMimeData;
class Application;
class CollectionBackend;
class CollectionItem;
@@ -63,7 +62,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
~ContextAlbumsModel();
static const int kPrettyCoverSize;
static const qint64 kIconCacheSize;
enum Role {
Role_Type = Qt::UserRole + 1,
@@ -71,7 +69,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
Role_SortText,
Role_Key,
Role_Artist,
Role_IsDivider,
Role_Editable,
LastRole
};
@@ -81,8 +78,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
SqlRowList rows;
};
CollectionBackend *backend() const { return backend_; }
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
SongList GetChildSongs(const QModelIndex &index) const;
SongList GetChildSongs(const QModelIndexList &indexes) const;
@@ -93,9 +88,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
QMimeData *mimeData(const QModelIndexList &indexes) const;
bool canFetchMore(const QModelIndex &parent) const;
void set_pretty_covers(bool use_pretty_covers);
bool use_pretty_covers() const { return use_pretty_covers_; }
static QString TextOrUnknown(const QString &text);
static QString SortText(QString text);
static QString SortTextForArtist(QString artist);
@@ -125,15 +117,11 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
CollectionBackend *backend_;
Application *app_;
QueryOptions query_options_;
QMap<int, CollectionItem*> song_nodes_;
QMap<QString, CollectionItem*> container_nodes_;
QIcon artist_icon_;
QMap<int, CollectionItem*> song_nodes_;
QIcon album_icon_;
QPixmap no_cover_icon_;
QIcon playlists_dir_icon_;
QIcon playlist_icon_;
QNetworkDiskCache *icon_cache_;
bool use_pretty_covers_;
AlbumCoverLoaderOptions cover_loader_options_;
typedef QPair<CollectionItem*, QString> ItemAndCacheKey;
QMap<quint64, ItemAndCacheKey> pending_art_;

View File

@@ -82,76 +82,6 @@
ContextItemDelegate::ContextItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
void ContextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const {
const bool is_divider = index.data(ContextAlbumsModel::Role_IsDivider).toBool();
if (is_divider) {
QString text(index.data().toString());
painter->save();
QRect text_rect(opt.rect);
// Does this item have an icon?
QPixmap pixmap;
QVariant decoration = index.data(Qt::DecorationRole);
if (!decoration.isNull()) {
if (decoration.canConvert<QPixmap>()) {
pixmap = decoration.value<QPixmap>();
}
else if (decoration.canConvert<QIcon>()) {
pixmap = decoration.value<QIcon>().pixmap(opt.decorationSize);
}
}
// Draw the icon at the left of the text rectangle
if (!pixmap.isNull()) {
QRect icon_rect(text_rect.topLeft(), opt.decorationSize);
const int padding = (text_rect.height() - icon_rect.height()) / 2;
icon_rect.adjust(padding, padding, padding, padding);
text_rect.moveLeft(icon_rect.right() + padding + 6);
if (pixmap.size() != opt.decorationSize) {
pixmap = pixmap.scaled(opt.decorationSize, Qt::KeepAspectRatio);
}
painter->drawPixmap(icon_rect, pixmap);
}
else {
text_rect.setLeft(text_rect.left() + 30);
}
// Draw the text
QFont bold_font(opt.font);
bold_font.setBold(true);
painter->setPen(opt.palette.color(QPalette::Text));
painter->setFont(bold_font);
painter->drawText(text_rect, text);
// Draw the line under the item
QColor line_color = opt.palette.color(QPalette::Text);
QLinearGradient grad_color(opt.rect.bottomLeft(), opt.rect.bottomRight());
const double fade_start_end = (opt.rect.width()/3.0)/opt.rect.width();
line_color.setAlphaF(0.0);
grad_color.setColorAt(0, line_color);
line_color.setAlphaF(0.5);
grad_color.setColorAt(fade_start_end, line_color);
grad_color.setColorAt(1.0 - fade_start_end, line_color);
line_color.setAlphaF(0.0);
grad_color.setColorAt(1, line_color);
painter->setPen(QPen(grad_color, 1));
painter->drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight());
painter->restore();
}
else {
if (!is_divider) QStyledItemDelegate::paint(painter, opt, index);
}
}
bool ContextItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) {
return true;
@@ -311,22 +241,7 @@ bool ContextAlbumsView::RestoreLevelFocus(const QModelIndex &parent) {
}
void ContextAlbumsView::ReloadSettings() {
QSettings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
SetAutoOpen(settings.value("auto_open", true).toBool());
if (app_ && model_) {
model_->set_pretty_covers(settings.value("pretty_covers", true).toBool());
}
settings.endGroup();
}
void ContextAlbumsView::SetApplication(Application *app) {
void ContextAlbumsView::Init(Application *app) {
app_ = app;
@@ -338,8 +253,6 @@ void ContextAlbumsView::SetApplication(Application *app) {
connect(model_, SIGNAL(modelAboutToBeReset()), this, SLOT(SaveFocus()));
connect(model_, SIGNAL(modelReset()), this, SLOT(RestoreFocus()));
ReloadSettings();
}
void ContextAlbumsView::paintEvent(QPaintEvent *event) {

View File

@@ -25,7 +25,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QObject>
#include <QWidget>
@@ -55,7 +54,6 @@ class ContextItemDelegate : public QStyledItemDelegate {
public:
ContextItemDelegate(QObject *parent);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
public slots:
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index);
@@ -72,7 +70,7 @@ class ContextAlbumsView : public AutoExpandingTreeView {
// Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs.
SongList GetSelectedSongs() const;
void SetApplication(Application *app);
void Init(Application *app);
// QTreeView
void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
@@ -80,14 +78,9 @@ class ContextAlbumsView : public AutoExpandingTreeView {
ContextAlbumsModel *albums_model() { return model_; }
public slots:
void ReloadSettings();
void SaveFocus();
void RestoreFocus();
signals:
void ShowConfigDialog();
protected:
// QWidget
void paintEvent(QPaintEvent *event);
@@ -122,7 +115,6 @@ signals:
#ifndef Q_OS_WIN
QAction *copy_to_device_;
#endif
QAction *delete_;
QAction *edit_track_;
QAction *edit_tracks_;
QAction *show_in_browser_;

View File

@@ -62,6 +62,8 @@
#include "covermanager/albumcoverloader.h"
#include "covermanager/currentalbumcoverloader.h"
#include "lyrics/lyricsfetcher.h"
#include "settings/contextsettingspage.h"
#include "widgets/osd.h"
#include "contextview.h"
#include "contextalbumsmodel.h"
@@ -69,8 +71,6 @@
using std::unique_ptr;
const char *ContextView::kSettingsGroup = "ContextView";
ContextView::ContextView(QWidget *parent) :
QWidget(parent),
ui_(new Ui_ContextViewContainer),
@@ -110,7 +110,7 @@ void ContextView::Init(Application *app, CollectionView *collectionview, AlbumCo
collectionview_ = collectionview;
album_cover_choice_controller_ = album_cover_choice_controller;
ui_->widget_play_albums->SetApplication(app_);
ui_->widget_play_albums->Init(app_);
lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this);
connect(collectionview_, SIGNAL(TotalSongCountUpdated_()), this, SLOT(UpdateNoSong()));
@@ -152,13 +152,7 @@ void ContextView::AddActions() {
menu_->addActions(cover_actions);
menu_->addSeparator();
QSettings s;
s.beginGroup(kSettingsGroup);
action_show_data_->setChecked(s.value("show_data", true).toBool());
action_show_output_->setChecked(s.value("show_output", true).toBool());
action_show_albums_->setChecked(s.value("show_albums", false).toBool());
action_show_lyrics_->setChecked(s.value("show_lyrics", true).toBool());
s.endGroup();
ReloadSettings();
connect(action_show_data_, SIGNAL(triggered()), this, SLOT(ActionShowData()));
connect(action_show_output_, SIGNAL(triggered()), this, SLOT(ActionShowOutput()));
@@ -207,6 +201,26 @@ void ContextView::SongChanged(const Song &song) {
}
void ContextView::ReloadSettings() {
QSettings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup);
title_fmt_ = s.value(ContextSettingsPage::kSettingsTitleFmt, "%title% - %artist%").toString();
summary_fmt_ = s.value(ContextSettingsPage::kSettingsSummaryFmt, "%album%").toString();
action_show_data_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA], true).toBool());
action_show_output_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ENGINE_AND_DEVICE], true).toBool());
action_show_albums_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ALBUMS_BY_ARTIST], false).toBool());
action_show_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS], true).toBool());
s.endGroup();
if (song_.is_valid()) {
SetSong(song_);
}
else {
UpdateNoSong();
}
}
void ContextView::SetLabelEnabled(QLabel *label) {
label->setEnabled(true);
label->setVisible(true);
@@ -232,15 +246,18 @@ void ContextView::NoSong() {
ui_->label_stop_top->setText(tr("No song playing"));
QString html = tr(
"%1 songs<br />\n"
"%2 artists<br />\n"
"%3 albums<br />\n"
)
.arg(collectionview_->TotalSongs())
.arg(collectionview_->TotalArtists())
.arg(collectionview_->TotalAlbums())
;
QString html;
if (collectionview_->TotalSongs() == 1) html += tr("%1 song").arg(collectionview_->TotalSongs());
else html += tr("%1 songs").arg(collectionview_->TotalSongs());
html += "<br />";
if (collectionview_->TotalArtists() == 1) html += tr("%1 artist").arg(collectionview_->TotalArtists());
else html += tr("%1 artists").arg(collectionview_->TotalArtists());
html += "<br />";
if (collectionview_->TotalAlbums() == 1) html += tr("%1 album").arg(collectionview_->TotalAlbums());
else html += tr("%1 albums").arg(collectionview_->TotalAlbums());
html += "<br />";
ui_->label_stop_summary->setStyleSheet(
"font: 12pt;"
@@ -269,7 +286,7 @@ void ContextView::SetSong(const Song &song) {
"font: 11pt;"
"font-weight: regular;"
);
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
ui_->label_play_top->setText(QString("<b>%1</b><br/>%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br/>"), Utilities::ReplaceMessage(summary_fmt_, song, "<br/>")));
if (action_show_data_->isChecked()) {
ui_->layout_play_data->setEnabled(true);
@@ -402,7 +419,7 @@ void ContextView::SetSong(const Song &song) {
if (albumlist.count() > 1) {
ui_->label_play_albums->setVisible(true);
ui_->label_play_albums->setMinimumSize(0, 20);
ui_->label_play_albums->setText(tr("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
ui_->label_play_albums->setText("<b>" + tr("Albums by %1").arg( song.artist().toHtmlEscaped()) + "</b>");
ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
for (CollectionBackend::Album album : albumlist) {
SongList songs = app_->collection_backend()->GetSongs(song.artist(), album.album_name, opt);
@@ -447,9 +464,7 @@ void ContextView::SetSong(const Song &song) {
void ContextView::UpdateSong(const Song &song) {
if (song.artist() != song_playing_.artist() || song.album() != song_playing_.album() || song.title() != song_playing_.title()) {
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
}
ui_->label_play_top->setText(QString("<b>%1</b><br/>%2").arg(Utilities::ReplaceMessage(title_fmt_, song, "<br/>"), Utilities::ReplaceMessage(summary_fmt_, song, "<br/>")));
if (action_show_data_->isChecked()) {
if (song.filetype() != song_playing_.filetype()) ui_->filetype->setText(song.TextForFiletype());
@@ -498,7 +513,7 @@ void ContextView::UpdateSong(const Song &song) {
void ContextView::UpdateLyrics(const quint64 id, const QString &provider, const QString &lyrics) {
if (id != lyrics_id_) return;
if ((qint64) id != lyrics_id_) return;
lyrics_ = lyrics + "\n\n(Lyrics from " + provider + ")\n";
lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) {
@@ -657,24 +672,24 @@ void ContextView::AutomaticCoverSearchDone() {
void ContextView::ActionShowData() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("show_data", action_show_data_->isChecked());
s.beginGroup(ContextSettingsPage::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA], action_show_data_->isChecked());
s.endGroup();
SetSong(song_);
}
void ContextView::ActionShowOutput() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("show_output", action_show_output_->isChecked());
s.beginGroup(ContextSettingsPage::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ENGINE_AND_DEVICE], action_show_output_->isChecked());
s.endGroup();
SetSong(song_);
}
void ContextView::ActionShowAlbums() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("show_albums", action_show_albums_->isChecked());
s.beginGroup(ContextSettingsPage::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ALBUMS_BY_ARTIST], action_show_albums_->isChecked());
s.endGroup();
song_prev_ = Song();
SetSong(song_);
@@ -682,8 +697,8 @@ void ContextView::ActionShowAlbums() {
void ContextView::ActionShowLyrics() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("show_lyrics", action_show_lyrics_->isChecked());
s.beginGroup(ContextSettingsPage::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS], action_show_lyrics_->isChecked());
s.endGroup();
SetSong(song_);
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked() && !song_.artist().isEmpty() && !song_.title().isEmpty()) {

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
@@ -71,9 +70,9 @@ class ContextView : public QWidget {
void Stopped();
void Error();
void SongChanged(const Song &song);
void ReloadSettings();
private:
static const char *kSettingsGroup;
Ui_ContextViewContainer *ui_;
Application *app_;
@@ -103,6 +102,8 @@ class ContextView : public QWidget {
std::unique_ptr<QMovie> spinner_animation_;
qint64 lyrics_id_;
QString lyrics_;
QString title_fmt_;
QString summary_fmt_;
void AddActions();
void SetLabelEnabled(QLabel *label);

View File

@@ -191,7 +191,7 @@
<property name="maximumSize">
<size>
<width>16777215</width>
<height>70</height>
<height>700</height>
</size>
</property>
<property name="text">

View File

@@ -20,7 +20,6 @@
#include "config.h"
#include <stdbool.h>
#include <QApplication>
#include <QObject>

View File

@@ -61,6 +61,7 @@
#include "lyrics/auddlyricsprovider.h"
#include "lyrics/ovhlyricsprovider.h"
#include "lyrics/lololyricsprovider.h"
#include "lyrics/chartlyricsprovider.h"
#include "scrobbler/audioscrobbler.h"
@@ -85,8 +86,6 @@
# include "moodbar/moodbarloader.h"
#endif
bool Application::kIsPortable = false;
class ApplicationImpl {
public:
explicit ApplicationImpl(Application *app) :
@@ -139,6 +138,7 @@ class ApplicationImpl {
lyrics_providers->AddProvider(new AuddLyricsProvider(app));
lyrics_providers->AddProvider(new OVHLyricsProvider(app));
lyrics_providers->AddProvider(new LoloLyricsProvider(app));
lyrics_providers->AddProvider(new ChartLyricsProvider(app));
return lyrics_providers;
}),
internet_services_([=]() {

View File

@@ -26,7 +26,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QObject>
#include <QThread>
@@ -68,8 +67,6 @@ class Application : public QObject {
Q_OBJECT
public:
static bool kIsPortable;
explicit Application(QObject *parent = nullptr);
~Application();

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QDataStream>
#include <QByteArray>

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <sqlite3.h>
#include <QtGlobal>

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QObject>
#include <QThread>

View File

@@ -21,7 +21,6 @@
#include "config.h"
#include <stdbool.h>
#include <QDir>
#include <QFile>

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QString>
#include "musicstorage.h"

View File

@@ -26,12 +26,13 @@
#include <CoreServices/CoreServices.h>
#include <QObject>
#include <QString>
#include <QSet>
#include <QTimer>
#include <QString>
#include "filesystemwatcherinterface.h"
class QTimer;
class MacFSListener : public FileSystemWatcherInterface {
Q_OBJECT
@@ -57,7 +58,7 @@ signals:
FSEventStreamRef stream_;
QSet<QString> paths_;
QTimer update_timer_;
QTimer *update_timer_;
};
#endif

View File

@@ -22,6 +22,8 @@
#include "config.h"
#include <QTimer>
#include <CoreFoundation/CFArray.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSString.h>
@@ -30,21 +32,21 @@
#include "scoped_nsobject.h"
MacFSListener::MacFSListener(QObject* parent)
: FileSystemWatcherInterface(parent), run_loop_(nullptr), stream_(nullptr) {
update_timer_.setSingleShot(true);
update_timer_.setInterval(2000);
connect(&update_timer_, SIGNAL(timeout()), SLOT(UpdateStream()));
: FileSystemWatcherInterface(parent),
run_loop_(nullptr),
stream_(nullptr),
update_timer_(new QTimer(this)) {
update_timer_->setSingleShot(true);
update_timer_->setInterval(2000);
connect(update_timer_, SIGNAL(timeout()), SLOT(UpdateStream()));
}
void MacFSListener::Init() { run_loop_ = CFRunLoopGetCurrent(); }
void MacFSListener::EventStreamCallback(
ConstFSEventStreamRef stream,
void* user_data,
size_t num_events,
void* event_paths,
const FSEventStreamEventFlags event_flags[],
const FSEventStreamEventId event_ids[]) {
void MacFSListener::EventStreamCallback(ConstFSEventStreamRef stream, void* user_data, size_t num_events, void* event_paths, const FSEventStreamEventFlags event_flags[], const FSEventStreamEventId event_ids[]) {
MacFSListener* me = reinterpret_cast<MacFSListener*>(user_data);
char** paths = reinterpret_cast<char**>(event_paths);
for (int i = 0; i < num_events; ++i) {
@@ -55,26 +57,35 @@ void MacFSListener::EventStreamCallback(
}
emit me->PathChanged(path);
}
}
void MacFSListener::AddPath(const QString& path) {
Q_ASSERT(run_loop_);
paths_.insert(path);
UpdateStreamAsync();
}
void MacFSListener::RemovePath(const QString& path) {
Q_ASSERT(run_loop_);
paths_.remove(path);
UpdateStreamAsync();
}
void MacFSListener::Clear() {
paths_.clear();
UpdateStreamAsync();
}
void MacFSListener::UpdateStreamAsync() { update_timer_.start(); }
void MacFSListener::UpdateStreamAsync() {
update_timer_->start();
}
void MacFSListener::UpdateStream() {
if (stream_) {
@@ -88,11 +99,10 @@ void MacFSListener::UpdateStream() {
return;
}
scoped_nsobject<NSMutableArray> array([[NSMutableArray alloc] init]);
scoped_nsobject<NSMutableArray> array([ [NSMutableArray alloc] init]);
for (const QString& path : paths_) {
scoped_nsobject<NSString> string(
[[NSString alloc] initWithUTF8String:path.toUtf8().constData()]);
scoped_nsobject<NSString> string([ [NSString alloc] initWithUTF8String:path.toUtf8().constData()]);
[array addObject:string.get()];
}

View File

@@ -200,8 +200,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
collection_view_(new CollectionViewContainer(this)),
file_view_(new FileView(this)),
#ifndef Q_OS_WIN
device_view_container_(new DeviceViewContainer(this)),
device_view_(device_view_container_->view()),
device_view_(new DeviceViewContainer(this)),
#endif
playlist_list_(new PlaylistListContainer(this)),
queue_view_(new QueueView(this)),
@@ -214,8 +213,6 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(cover_manager, SIGNAL(AddToPlaylist(QMimeData*)), this, SLOT(AddToPlaylist(QMimeData*)));
return cover_manager;
}),
//organise_dialog_(new OrganiseDialog(app_->task_manager())),
equalizer_(new Equalizer),
organise_dialog_([=]() {
OrganiseDialog *dialog = new OrganiseDialog(app->task_manager(), app->collection_backend());
@@ -329,7 +326,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
collection_view_->view()->setModel(collection_sort_model_);
collection_view_->view()->SetApplication(app_);
#ifndef Q_OS_WIN
device_view_->SetApplication(app_);
device_view_->view()->SetApplication(app_);
#endif
playlist_list_->SetApplication(app_);
@@ -408,7 +405,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->action_stop_after_this_track, SIGNAL(triggered()), SLOT(StopAfterCurrent()));
connect(ui_->action_mute, SIGNAL(triggered()), app_->player(), SLOT(Mute()));
connect(ui_->action_clear_playlist, SIGNAL(triggered()), app_->playlist_manager(), SLOT(ClearCurrent()));
connect(ui_->action_clear_playlist, SIGNAL(triggered()), SLOT(PlaylistClearCurrent()));
connect(ui_->action_remove_duplicates, SIGNAL(triggered()), app_->playlist_manager(), SLOT(RemoveDuplicatesCurrent()));
connect(ui_->action_remove_unavailable, SIGNAL(triggered()), app_->playlist_manager(), SLOT(RemoveUnavailableCurrent()));
connect(ui_->action_remove_from_playlist, SIGNAL(triggered()), SLOT(PlaylistRemoveCurrent()));
@@ -539,7 +536,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#ifndef Q_OS_WIN
// Devices connections
connect(device_view_, SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
connect(device_view_->view(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
#endif
// Collection filter widget
@@ -782,10 +779,11 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
// Set last used geometry to position window on the correct monitor
// Set window state only if the window was last maximized
if (settings_.contains("geometry")) {
restoreGeometry(settings_.value("geometry").toByteArray());
}
was_maximized_ = settings_.value("maximized", true).toBool();
if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
else restoreGeometry(settings_.value("geometry").toByteArray());
if (!ui_->splitter->restoreState(settings_.value("splitter_state").toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 250 << width() - 250);
@@ -952,9 +950,11 @@ void MainWindow::ReloadAllSettings() {
osd_->ReloadSettings();
collection_view_->ReloadSettings();
ui_->playlist->view()->ReloadSettings();
app_->playlist_manager()->playlist_container()->ReloadSettings();
app_->album_cover_loader()->ReloadSettings();
album_cover_choice_controller_->ReloadSettings();
if (cover_manager_.get()) cover_manager_->ReloadSettings();
context_view_->ReloadSettings();
#ifdef HAVE_TIDAL
tidal_view_->ReloadSettings();
#endif
@@ -1168,10 +1168,8 @@ void MainWindow::SaveGeometry() {
if (!initialised_) return;
was_maximized_ = isMaximized();
settings_.setValue("maximized", was_maximized_);
if (was_maximized_) settings_.remove("geometry");
else settings_.setValue("geometry", saveGeometry());
settings_.setValue("maximized", isMaximized());
settings_.setValue("geometry", saveGeometry());
settings_.setValue("splitter_state", ui_->splitter->saveState());
}
@@ -1962,6 +1960,25 @@ void MainWindow::PlaylistRemoveCurrent() {
ui_->playlist->view()->RemoveSelected(false);
}
void MainWindow::PlaylistClearCurrent() {
if (app_->playlist_manager()->current()->rowCount() > Playlist::kUndoItemLimit) {
QMessageBox messagebox(QMessageBox::Warning, tr("Clear playlist"), tr("Playlist has %1 songs, too large to undo, are you sure you want to clear the playlist?").arg(app_->playlist_manager()->current()->rowCount()), QMessageBox::Ok|QMessageBox::Cancel);
messagebox.setTextFormat(Qt::RichText);
int result = messagebox.exec();
switch (result) {
case QMessageBox::Ok:
break;
case QMessageBox::Cancel:
default:
return;
}
}
app_->playlist_manager()->ClearCurrent();
}
void MainWindow::PlaylistEditFinished(const QModelIndex &index) {
if (index == playlist_menu_index_) SelectionSetValue();
}

View File

@@ -25,7 +25,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
@@ -156,6 +155,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void PlaylistSkip();
void PlaylistRemoveCurrent();
void PlaylistEditFinished(const QModelIndex& index);
void PlaylistClearCurrent();
void RescanSongs();
void EditTracks();
void EditTagDialogAccepted();
@@ -282,7 +282,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
#endif
Application *app_;
SystemTrayIcon *tray_icon_;
SystemTrayIcon *tray_icon_;
OSD *osd_;
Lazy<About> about_dialog_;
Lazy<EditTagDialog> edit_tag_dialog_;
@@ -294,8 +294,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
CollectionViewContainer *collection_view_;
FileView *file_view_;
#ifndef Q_OS_WIN
DeviceViewContainer *device_view_container_;
DeviceView *device_view_;
DeviceViewContainer *device_view_;
#endif
PlaylistListContainer *playlist_list_;
QueueView *queue_view_;

View File

@@ -25,7 +25,6 @@
#include <memory>
#include <cstddef>
#include <stdbool.h>
#include <QObject>
#include <QMimeData>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -55,7 +55,7 @@ inline void AddMetadata(const QString &key, const QDateTime &metadata, QVariantM
if (metadata.isValid()) (*map)[key] = metadata;
}
inline QString AsMPRISDateTimeType(uint time) {
inline QString AsMPRISDateTimeType(const int time) {
return time != -1 ? QDateTime::fromTime_t(time).toString(Qt::ISODate) : "";
}

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QSortFilterProxyModel>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <functional>
#include <memory>

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QMutex>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -96,7 +96,7 @@ Player::Player(Application *app, QObject *parent)
last_pressed_previous_(QDateTime::currentDateTime()),
continue_on_error_(false),
greyout_(true),
menu_previousmode_(PreviousBehaviour_DontRestart),
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour_DontRestart),
seek_step_sec_(10),
volume_control_(true)
{
@@ -230,10 +230,10 @@ void Player::ReloadSettings() {
s.beginGroup(PlaylistSettingsPage::kSettingsGroup);
continue_on_error_ = s.value("continue_on_error", false).toBool();
greyout_ = s.value("greyout_songs_play", true).toBool();
menu_previousmode_ = PreviousBehaviour(s.value("menu_previousmode", PreviousBehaviour_DontRestart).toInt());
s.endGroup();
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
menu_previousmode_ = BehaviourSettingsPage::PreviousBehaviour(s.value("menu_previousmode", BehaviourSettingsPage::PreviousBehaviour_DontRestart).toInt());
seek_step_sec_ = s.value("seek_step_sec", 10).toInt();
s.endGroup();
@@ -503,7 +503,7 @@ void Player::StopAfterCurrent() {
bool Player::PreviousWouldRestartTrack() const {
// Check if it has been over two seconds since previous button was pressed
return menu_previousmode_ == PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
return menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
}
void Player::Previous() { PreviousItem(Engine::Manual); }
@@ -512,7 +512,7 @@ void Player::PreviousItem(Engine::TrackChangeFlags change) {
const bool ignore_repeat_track = change & Engine::Manual;
if (menu_previousmode_ == PreviousBehaviour_Restart) {
if (menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart) {
// Check if it has been over two seconds since previous button was pressed
QDateTime now = QDateTime::currentDateTime();
if (last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(now) >= 2) {

View File

@@ -25,7 +25,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>
@@ -42,6 +41,7 @@
#include "engine/gststartup.h"
#endif
#include "playlist/playlistitem.h"
#include "settings/behavioursettingspage.h"
class Application;
class Song;
@@ -136,12 +136,6 @@ class Player : public PlayerInterface {
static const char *kSettingsGroup;
// Don't change the values: they are saved in preferences
enum PreviousBehaviour {
PreviousBehaviour_DontRestart = 1,
PreviousBehaviour_Restart = 2
};
Engine::EngineType CreateEngine(Engine::EngineType enginetype);
void Init();
@@ -240,7 +234,7 @@ class Player : public PlayerInterface {
bool continue_on_error_;
bool greyout_;
PreviousBehaviour menu_previousmode_;
BehaviourSettingsPage::PreviousBehaviour menu_previousmode_;
int seek_step_sec_;
bool volume_control_;

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QSystemTrayIcon>

View File

@@ -20,7 +20,6 @@
#include "config.h"
#include <stdbool.h>
#include <QSqlDatabase>
#include "core/logging.h"

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <boost/noncopyable.hpp>
#include <QSqlDatabase>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <glib-object.h>
#include <stdbool.h>
#include <QtGlobal>

View File

@@ -152,6 +152,7 @@ const QString Song::kEmbeddedCover = "(embedded)";
const QRegExp Song::kAlbumRemoveDisc(" ?-? ((\\(|\\[)?)(Disc|CD) ?([0-9]{1,2})((\\)|\\])?)$");
const QRegExp Song::kAlbumRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
const QRegExp Song::kTitleRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|Live|Remastered Version|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
const QString Song::kVariousArtists("various artists");
const QStringList Song::kArticles = QStringList() << "the " << "a " << "an ";
@@ -351,7 +352,7 @@ const QString &Song::cue_path() const { return d->cue_path_; }
bool Song::has_cue() const { return !d->cue_path_.isEmpty(); }
bool Song::is_collection_song() const { return d->source_ == Source_Collection; }
bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->album_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; }
bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; }
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Subsonic || d->source_ == Source_Qobuz; }
bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; }
@@ -426,15 +427,7 @@ void Song::set_bitdepth(int v) { d->bitdepth_ = v; }
void Song::set_source(Source v) { d->source_ = v; }
void Song::set_directory_id(int v) { d->directory_id_ = v; }
void Song::set_url(const QUrl &v) {
if (Application::kIsPortable) {
QUrl base = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/");
d->url_ = base.resolved(v);
}
else {
d->url_ = v;
}
}
void Song::set_url(const QUrl &v) { d->url_ = v; }
void Song::set_basefilename(const QString &v) { d->basefilename_ = v; }
void Song::set_filetype(FileType v) { d->filetype_ = v; }
void Song::set_filesize(int v) { d->filesize_ = v; }
@@ -949,7 +942,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
set_art_automatic(QUrl::fromEncoded(art_automatic.toUtf8()));
}
else {
set_art_automatic(QUrl::fromLocalFile(art_automatic.toUtf8()));
set_art_automatic(QUrl::fromLocalFile(art_automatic));
}
}
else if (Song::kColumns.value(i) == "art_manual") {
@@ -958,7 +951,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
set_art_manual(QUrl::fromEncoded(art_manual.toUtf8()));
}
else {
set_art_manual(QUrl::fromLocalFile(art_manual.toUtf8()));
set_art_manual(QUrl::fromLocalFile(art_manual));
}
}
@@ -1287,18 +1280,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
query->bindValue(":source", d->source_);
query->bindValue(":directory_id", notnullintval(d->directory_id_));
QString url;
if (d->url_.isValid()) {
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(d->url_)) {
url = Utilities::GetRelativePathToStrawberryBin(d->url_).toEncoded();
}
else {
url = d->url_.toEncoded();
}
}
query->bindValue(":url", url);
query->bindValue(":url", d->url_.toString(QUrl::FullyEncoded));
query->bindValue(":filetype", d->filetype_);
query->bindValue(":filesize", notnullintval(d->filesize_));
query->bindValue(":mtime", notnullintval(d->mtime_));
@@ -1314,8 +1296,8 @@ void Song::BindToQuery(QSqlQuery *query) const {
query->bindValue(":compilation_off", d->compilation_off_ ? 1 : 0);
query->bindValue(":compilation_effective", is_compilation() ? 1 : 0);
query->bindValue(":art_automatic", d->art_automatic_);
query->bindValue(":art_manual", d->art_manual_);
query->bindValue(":art_automatic", d->art_automatic_.toString(QUrl::FullyEncoded));
query->bindValue(":art_manual", d->art_manual_.toString(QUrl::FullyEncoded));
query->bindValue(":effective_albumartist", this->effective_albumartist());
query->bindValue(":effective_originalyear", intval(this->effective_originalyear()));

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QSharedData>
@@ -126,6 +125,8 @@ class Song {
static const QRegExp kTitleRemoveMisc;
static const QRegExp kFilenameRemoveNonFatChars;
static const QString kVariousArtists;
static const QStringList kArticles;
static QString JoinSpec(const QString &table);

View File

@@ -2,6 +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>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -167,7 +168,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
LoadLocalDirectory(filename);
return Success;
}
Song song;
Song song(Song::Source_LocalFile);
song.InitFromFilePartial(filename);
if (song.is_valid()) {
songs_ << song;
@@ -228,7 +229,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
if (collection_->ExecQuery(&query) && query.Next()) {
// We may have many results when the file has many sections
do {
Song song;
Song song(Song::Source_Collection);
song.InitFromQuery(query, true);
if (song.is_valid()) {
@@ -289,7 +290,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
}
// Assume it's just a normal file
Song song;
Song song(Song::Source_LocalFile);
song.InitFromFilePartial(filename);
if (song.is_valid()) {
songs_ << song;

View File

@@ -2,6 +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>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,7 +27,6 @@
#include <memory>
#include <functional>
#include <glib.h>
#include <stdbool.h>
#ifdef HAVE_GSTREAMER
# include <gst/gst.h>

View File

@@ -87,7 +87,11 @@ void StandardItemIconLoader::RowsAboutToBeRemoved(const QModelIndex &parent, int
void StandardItemIconLoader::ModelReset() {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
cover_loader_->CancelTasks(QSet<quint64>(pending_covers_.keyBegin(), pending_covers_.keyEnd()));
#else
cover_loader_->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
#endif
pending_covers_.clear();
}

View File

@@ -196,9 +196,8 @@ static void verticalGradientHelper(QPainter *p, const QRect &spanRect, const QRe
void StyleHelper::verticalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored) {
if (StyleHelper::usePixmapCache()) {
QString key;
QColor keyColor = baseColor(lightColored);
key.sprintf("mh_vertical %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb());
QString key = QString::asprintf("mh_vertical %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb());
QPixmap pixmap;
if (!QPixmapCache::find(key, &pixmap)) {
@@ -253,9 +252,8 @@ static void horizontalGradientHelper(QPainter *p, const QRect &spanRect, const Q
void StyleHelper::horizontalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored) {
if (StyleHelper::usePixmapCache()) {
QString key;
QColor keyColor = baseColor(lightColored);
key.sprintf("mh_horizontal %d %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb(), spanRect.x());
QString key = QString::asprintf("mh_horizontal %d %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb(), spanRect.x());
QPixmap pixmap;
if (!QPixmapCache::find(key, &pixmap)) {
@@ -294,8 +292,7 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
QRect r = option->rect;
int size = qMin(r.height(), r.width());
QPixmap pixmap;
QString pixmapName;
pixmapName.sprintf("StyleHelper::drawArrow-%d-%d-%d-%f", element, size, enabled, devicePixelRatio);
QString pixmapName = QString::asprintf("StyleHelper::drawArrow-%d-%d-%d-%f", element, size, enabled, devicePixelRatio);
if (!QPixmapCache::find(pixmapName, &pixmap)) {
QImage image(size * devicePixelRatio, size * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
@@ -336,8 +333,7 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
void StyleHelper::menuGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect) {
if (StyleHelper::usePixmapCache()) {
QString key;
key.sprintf("mh_menu %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), StyleHelper::baseColor().rgb());
QString key = QString::asprintf("mh_menu %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), StyleHelper::baseColor().rgb());
QPixmap pixmap;
if (!QPixmapCache::find(key, &pixmap)) {

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QString>

View File

@@ -46,7 +46,7 @@ TagReaderClient::TagReaderClient(QObject *parent) : QObject(parent), worker_pool
original_thread_ = thread();
worker_pool_->SetExecutableName(kWorkerExecutableName);
worker_pool_->SetWorkerCount(qBound(1, QThread::idealThreadCount() / 2, 2));
worker_pool_->SetWorkerCount(qBound(1, QThread::idealThreadCount() / 2, 4));
connect(worker_pool_, SIGNAL(WorkerFailedToStart()), SLOT(WorkerFailedToStart()));
}

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QList>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -88,6 +88,7 @@
#endif
#include "core/logging.h"
#include "core/song.h"
#include "utilities.h"
#include "timeconstants.h"
@@ -119,8 +120,8 @@ QString PrettyTime(int seconds) {
seconds %= 60;
QString ret;
if (hours) ret.sprintf("%d:%02d:%02d", hours, minutes, seconds);
else ret.sprintf("%d:%02d", minutes, seconds);
if (hours) ret = QString::asprintf("%d:%02d:%02d", hours, minutes, seconds);
else ret = QString::asprintf("%d:%02d", minutes, seconds);
return ret;
@@ -186,11 +187,11 @@ QString PrettySize(quint64 bytes) {
if (bytes <= 1000)
ret = QString::number(bytes) + " bytes";
else if (bytes <= 1000 * 1000)
ret.sprintf("%.1f KB", float(bytes) / 1000);
ret = QString::asprintf("%.1f KB", float(bytes) / 1000);
else if (bytes <= 1000 * 1000 * 1000)
ret.sprintf("%.1f MB", float(bytes) / (1000 * 1000));
ret = QString::asprintf("%.1f MB", float(bytes) / (1000 * 1000));
else
ret.sprintf("%.1f GB", float(bytes) / (1000 * 1000 * 1000));
ret = QString::asprintf("%.1f GB", float(bytes) / (1000 * 1000 * 1000));
}
return ret;
@@ -514,6 +515,26 @@ bool ParseUntilElement(QXmlStreamReader *reader, const QString &name) {
}
bool ParseUntilElementCI(QXmlStreamReader *reader, const QString &name) {
while (!reader->atEnd()) {
QXmlStreamReader::TokenType type = reader->readNext();
switch (type) {
case QXmlStreamReader::StartElement:{
QString element = reader->name().toString().toLower();
if (element == name) {
return true;
}
break;
}
default:
break;
}
}
return false;
}
QDateTime ParseRFC822DateTime(const QString &text) {
QRegExp regexp("(\\d{1,2}) (\\w{3,12}) (\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
@@ -716,19 +737,6 @@ void IncreaseFDLimit() {
}
void CheckPortable() {
QFile f(QApplication::applicationDirPath() + QDir::separator() + "data");
if (f.exists()) {
// We are portable. Set the bool and change the qsettings path
Application::kIsPortable = true;
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, f.fileName());
}
}
QString GetRandomStringWithChars(const int len) {
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
return GetRandomString(len, UseCharacters);
@@ -842,6 +850,79 @@ QString MacAddress() {
}
QString ReplaceMessage(const QString &message, const Song &song, const QString &newline) {
QRegExp variable_replacer("[%][a-z]+[%]");
QString copy(message);
// Replace the first line
int pos = 0;
variable_replacer.indexIn(message);
while ((pos = variable_replacer.indexIn(message, pos)) != -1) {
QStringList captured = variable_replacer.capturedTexts();
copy.replace(captured[0], ReplaceVariable(captured[0], song, newline));
pos += variable_replacer.matchedLength();
}
return copy;
}
QString ReplaceVariable(const QString &variable, const Song &song, const QString &newline) {
QString return_value;
if (variable == "%artist%") {
return song.artist().toHtmlEscaped();
}
else if (variable == "%album%") {
return song.album().toHtmlEscaped();
}
else if (variable == "%title%") {
return song.PrettyTitle().toHtmlEscaped();
}
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();
}
else if (variable == "%track%") {
return return_value.setNum(song.track()).toHtmlEscaped();
}
else if (variable == "%genre%") {
return song.genre().toHtmlEscaped();
}
else if (variable == "%playcount%") {
return return_value.setNum(song.playcount()).toHtmlEscaped();
}
else if (variable == "%skipcount%") {
return return_value.setNum(song.skipcount()).toHtmlEscaped();
}
else if (variable == "%filename%") {
return song.basefilename().toHtmlEscaped();
}
else if (variable == "%newline%") {
return QString(newline);
}
//if the variable is not recognized, just return it
return variable;
}
} // namespace Utilities
ScopedWCharArray::ScopedWCharArray(const QString &str)

View File

@@ -25,7 +25,6 @@
#include "config.h"
#include <memory>
#include <stdbool.h>
#include <QtGlobal>
#include <QWidget>
@@ -44,6 +43,8 @@
#include <QXmlStreamReader>
#include <QtEvents>
#include "core/song.h"
namespace Utilities {
QString PrettyTime(int seconds);
QString PrettyTimeDelta(int seconds);
@@ -96,6 +97,7 @@ void ConsumeCurrentElement(QXmlStreamReader *reader);
// Advances the stream reader until it finds an element with the given name.
// Returns false if the end of the document was reached before finding a matching element.
bool ParseUntilElement(QXmlStreamReader *reader, const QString &name);
bool ParseUntilElementCI(QXmlStreamReader *reader, const QString &name);
// Parses a string containing an RFC822 time and date.
QDateTime ParseRFC822DateTime(const QString &text);
@@ -123,7 +125,6 @@ QString FiddleFileExtension(const QString &filename, const QString &new_extensio
QString GetEnv(const QString &key);
void SetEnv(const char *key, const QString &value);
void IncreaseFDLimit();
void CheckPortable();
// Borrowed from schedutils
enum IoPriority {
@@ -156,6 +157,9 @@ 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);
} // namespace
class ScopedWCharArray {

View File

@@ -24,6 +24,7 @@
#include <QtGlobal>
#include <QGuiApplication>
#include <QScreen>
#include <QWindow>
#include <QWidget>
#include <QDialog>
#include <QDir>
@@ -131,7 +132,7 @@ void AlbumCoverChoiceController::ReloadSettings() {
}
QList<QAction*> AlbumCoverChoiceController::GetAllActions() {
return QList<QAction*>() << cover_from_file_ << cover_to_file_ << separator_ << cover_from_url_ << search_for_cover_ << unset_cover_ << show_cover_;
return QList<QAction*>() << cover_from_file_ << cover_to_file_ << separator_ << cover_from_url_ << search_for_cover_ << unset_cover_ << separator_ << show_cover_;
}
QUrl AlbumCoverChoiceController::LoadCoverFromFile(Song *song) {
@@ -283,8 +284,12 @@ void AlbumCoverChoiceController::ShowCover(const Song &song, const QPixmap &pixm
title_text += " (" + QString::number(label->pixmap()->width()) + "x" + QString::number(label->pixmap()->height()) + "px)";
// If the cover is larger than the screen, resize the window 85% seems to be enough to account for title bar and taskbar etc.
QScreen *screen = QGuiApplication::primaryScreen();
QRect screenGeometry = screen->geometry();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QScreen *screen = QWidget::screen();
#else
QScreen *screen = (window() && window()->windowHandle() ? window()->windowHandle()->screen() : QGuiApplication::primaryScreen());
#endif
QRect screenGeometry = screen->availableGeometry();
int desktop_height = screenGeometry.height();
int desktop_width = screenGeometry.width();

View File

@@ -24,7 +24,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QWidget>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

View File

@@ -23,7 +23,6 @@
#include "config.h"
#include <stdbool.h>
#include <QtGlobal>
#include <QObject>

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