Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ebf7cd273 | ||
|
|
9986088dc4 | ||
|
|
bfe0b2c634 | ||
|
|
cd2af6974c | ||
|
|
c452486573 | ||
|
|
79406b20f2 | ||
|
|
1226bc214d | ||
|
|
7da79dabdf | ||
|
|
b51026a2ee | ||
|
|
67d01f48a3 | ||
|
|
9fd5c5fc1c | ||
|
|
70bc5b83fa | ||
|
|
b380db51fa | ||
|
|
fab598ebff | ||
|
|
6f6d087fa2 | ||
|
|
6e463d1de3 | ||
|
|
21970f3065 | ||
|
|
9d7e44be2d | ||
|
|
9085fb8285 | ||
|
|
dd79d089f6 | ||
|
|
7aaad124d0 | ||
|
|
0025cb9f53 | ||
|
|
15c8f2a3ee | ||
|
|
fc1a2dac90 | ||
|
|
f698b860f7 | ||
|
|
86b057a301 | ||
|
|
b066158a4b | ||
|
|
046c822604 | ||
|
|
d427733bfc | ||
|
|
04d34a06c7 | ||
|
|
69d86513ae | ||
|
|
019b49a219 | ||
|
|
d9e787784a | ||
|
|
71969b88e3 | ||
|
|
7ae0f5e246 | ||
|
|
1ea7da4bb5 | ||
|
|
8b96bb5f27 | ||
|
|
aefce3ccd0 | ||
|
|
4e599e2aba | ||
|
|
4148c289af | ||
|
|
d575ab0b2b | ||
|
|
d09af19d3f | ||
|
|
a2dff17db9 | ||
|
|
c0fecb935f | ||
|
|
28249b7e99 | ||
|
|
eb63e2257f | ||
|
|
2211716d04 | ||
|
|
242137a50c | ||
|
|
c41311bf76 | ||
|
|
e0d959e3c5 | ||
|
|
f0a5c4b2c2 | ||
|
|
e10a50fdc1 | ||
|
|
380f2509ac | ||
|
|
c0fb35f6b9 | ||
|
|
3e658845d2 | ||
|
|
384209ba70 | ||
|
|
835b589894 | ||
|
|
76e684ee75 | ||
|
|
d0d2c83768 | ||
|
|
b476bc3168 | ||
|
|
5a2ad145e8 | ||
|
|
27233d2549 | ||
|
|
1a0bc75629 | ||
|
|
dc04961699 | ||
|
|
e594cf299e | ||
|
|
096c995a91 | ||
|
|
f64b175cd8 | ||
|
|
954e0e8a59 | ||
|
|
034b032cfa | ||
|
|
e33590bff9 | ||
|
|
55f610f3b2 | ||
|
|
87fd93a1cf | ||
|
|
2db77a248a | ||
|
|
f600274592 | ||
|
|
bc37d00a81 | ||
|
|
1956ea95ee | ||
|
|
a56e3b91e1 | ||
|
|
6bcc9d61e1 | ||
|
|
3ef34191a3 | ||
|
|
60de36aff9 | ||
|
|
b019dd1c51 | ||
|
|
3a083d5527 | ||
|
|
8006241a81 | ||
|
|
b05e3d24ee | ||
|
|
b78c0a4979 | ||
|
|
d3b3c309fa | ||
|
|
65615495d9 | ||
|
|
35f448c34f | ||
|
|
e1abd28a88 | ||
|
|
a109d8be64 | ||
|
|
333a0bc05a | ||
|
|
a831519b54 | ||
|
|
a25052ed96 | ||
|
|
676f8dc8be | ||
|
|
9c3cb82fca | ||
|
|
9b827f58ad | ||
|
|
22e327d391 | ||
|
|
451de4d72d | ||
|
|
0679b78c1d | ||
|
|
ad7084b897 | ||
|
|
3c068eaba9 | ||
|
|
819ae08c6a | ||
|
|
38414ad8de | ||
|
|
f0422f7634 | ||
|
|
2a004dadf3 | ||
|
|
b7ea586e44 | ||
|
|
2a5f286d07 | ||
|
|
0bc14671ec |
155
.gitignore
vendored
@@ -1,80 +1,117 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
# Build
|
||||
build/
|
||||
bin/
|
||||
|
||||
# CMake
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Makefile*
|
||||
Testing
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Dump files
|
||||
*.core
|
||||
*.stackdump
|
||||
|
||||
# Qt
|
||||
*build-*
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.spec
|
||||
*.nsi
|
||||
*.plist
|
||||
maketarball.sh
|
||||
dist/macos/create-dmg.sh
|
||||
dist/debian/changelog
|
||||
dist/pacman/PKGBUILD
|
||||
ui_*.h
|
||||
*.moc
|
||||
*.qm
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
# QtCreator
|
||||
CMakeLists.txt.user*
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*creator.user*
|
||||
target_wrapper.*
|
||||
compile_commands.json
|
||||
|
||||
# xemacs temporary files
|
||||
# Temporary files
|
||||
*~
|
||||
*.autosave
|
||||
*.orig
|
||||
*.rej
|
||||
.*.kate-swp
|
||||
.swp.*
|
||||
.*.swp
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
# Directory files
|
||||
.directory
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
# Package files
|
||||
*.spec
|
||||
*.nsi
|
||||
*.plist
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
# Stuff in dist
|
||||
maketarball.sh
|
||||
create-dmg.sh
|
||||
changelog
|
||||
PKGBUILD
|
||||
|
||||
# Translations
|
||||
translations.pot
|
||||
zanata.xml
|
||||
.zanata-cache/
|
||||
|
||||
# Snap
|
||||
parts/
|
||||
prime/
|
||||
stage/
|
||||
*.snap
|
||||
/snap/.snapcraft/
|
||||
/*_source.tar.bz2
|
||||
|
||||
12
.travis.yml
@@ -2,7 +2,7 @@ sudo: required
|
||||
language: C++
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
#- osx
|
||||
services:
|
||||
- docker
|
||||
compiler:
|
||||
@@ -22,7 +22,7 @@ before_install:
|
||||
git pull;
|
||||
brew update;
|
||||
brew unlink python;
|
||||
brew install glib pkgconfig protobuf protobuf-c qt;
|
||||
brew install glib pkgconfig libffi protobuf protobuf-c qt gettext;
|
||||
brew install sqlite --with-fts;
|
||||
brew install gstreamer gst-plugins-base;
|
||||
brew install gst-plugins-good --with-flac;
|
||||
@@ -30,18 +30,20 @@ before_install:
|
||||
brew install chromaprint;
|
||||
brew install libcdio libmtp libimobiledevice libplist;
|
||||
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
|
||||
export Qt5LinguistTools_DIR=/usr/local/Cellar/qt/$(ls /usr/local/Cellar/qt | head -n1)/lib/cmake/Qt5LinguistTools;
|
||||
export PATH="/usr/local/opt/gettext/bin:$PATH";
|
||||
ls /usr/local/lib/gstreamer-1.0;
|
||||
fi
|
||||
before_script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_STREAM_DEEZER=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON -DENABLE_STREAM_DEEZER=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_TRANSLATIONS=ON ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON -DENABLE_TRANSLATIONS=ON ; fi
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
make -j8;
|
||||
sudo make install;
|
||||
sudo ../dist/macos/macdeploy.py strawberry.app;
|
||||
../dist/macos/create-dmg.sh strawberry.app;
|
||||
../dist/macos/create-dmg.sh strawberry.app $CC_FOR_BUILD;
|
||||
fi
|
||||
after_success:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry.dmg; fi
|
||||
|
||||
38
3rdparty/README.md
vendored
@@ -7,28 +7,11 @@ This is a small static library used by Strawberry to prevent it from starting tw
|
||||
If the user tries to start strawberry twice, the main window will maximize instead of starting another instance.
|
||||
If you dynamically link to your systems version, you'll need two versions, one defined as QApplication and
|
||||
one as a QCoreApplication.
|
||||
It is included here because it is normally not packaged by distros, and is also used on macOS and Windows.
|
||||
It is included here because it is not packed by distros and is also used on macOS and Windows.
|
||||
|
||||
URL: https://github.com/itay-grudev/SingleApplication
|
||||
|
||||
|
||||
qocoa
|
||||
-----
|
||||
This is a small static library currently used for the search fields above the collection, playlist and in
|
||||
the cover manager. It is slightly modified from original version, so it should not be used as a dynamic
|
||||
library.
|
||||
The plan in the long run is to replace it with something else.
|
||||
|
||||
URL: https://github.com/mikemcquaid/Qocoa
|
||||
|
||||
|
||||
SPMediaKeyTap
|
||||
-------------
|
||||
|
||||
This is used for macOS only to enable strawberry to grab global shortcuts and can safely be deleted on other
|
||||
platforms.
|
||||
|
||||
|
||||
taglib
|
||||
------
|
||||
|
||||
@@ -38,9 +21,9 @@ correctly.
|
||||
|
||||
It is kept in 3rdparty because there currently is no offical release of TagLib with the features and bugfixes
|
||||
that are in the official repository. And also because some distros use older, or unpatched versions.
|
||||
This version is a unmodified copy of commit 5cb589a (sha: 5cb589a5b82c13ba8f0542e5e79629da7645cb3c).
|
||||
|
||||
Also, there is a bug in version 1.11.1 corrupting Ogg files, see: https://github.com/taglib/taglib/issues/864
|
||||
There is a bug in the latest version (1.11.1) corrupting Ogg files,
|
||||
see: https://github.com/taglib/taglib/issues/864
|
||||
If you decide to use the systems taglib, make sure it has been patched with the following commit:
|
||||
https://github.com/taglib/taglib/commit/9336c82da3a04552168f208cd7a5fa4646701ea4
|
||||
|
||||
@@ -57,3 +40,18 @@ utf8-cpp
|
||||
This is 2 header files used by taglib, but kept in a seperate directory because it is maintained by others.
|
||||
|
||||
URL: http://utfcpp.sourceforge.net/
|
||||
|
||||
|
||||
qocoa
|
||||
-----
|
||||
This is a small static library currently used for the search fields above the collection, playlist and in
|
||||
the cover manager. It is slightly modified from original version.
|
||||
|
||||
URL: https://github.com/mikemcquaid/Qocoa
|
||||
|
||||
|
||||
SPMediaKeyTap
|
||||
-------------
|
||||
|
||||
This is used for macOS only to enable strawberry to grab global shortcuts and can safely be deleted on other
|
||||
platforms.
|
||||
|
||||
6
3rdparty/taglib/mp4/mp4properties.cpp
vendored
@@ -175,11 +175,11 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
unit = data.toLongLong(28U);
|
||||
length = data.toLongLong(36U);
|
||||
unit = data.toUInt(28U);
|
||||
length = data.toLongLong(32U);
|
||||
}
|
||||
else {
|
||||
if(data.size() < 24 + 4) {
|
||||
if(data.size() < 24 + 8) {
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,8 +112,7 @@ pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
|
||||
pkg_check_modules(LIBUSBMUXD libusbmuxd)
|
||||
pkg_check_modules(LIBPLIST libplist)
|
||||
pkg_check_modules(LIBDEEZER libdeezer)
|
||||
pkg_check_modules(LIBDZMEDIA libdzmedia)
|
||||
find_package(Gettext)
|
||||
|
||||
if(WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -151,6 +150,11 @@ if(WIN32)
|
||||
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::WinExtras)
|
||||
endif()
|
||||
|
||||
find_package(Qt5LinguistTools CONFIG)
|
||||
if (Qt5LinguistTools_FOUND)
|
||||
set(QT_LCONVERT_EXECUTABLE Qt5::lconvert)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
find_path(KEYSYMDEF_H NAMES "keysymdef.h" PATHS "${X11_INCLUDE_DIR}" PATH_SUFFIXES "X11")
|
||||
find_path(XF86KEYSYM_H NAMES "XF86keysym.h" PATHS "${XCB_INCLUDEDIR}" PATH_SUFFIXES "X11")
|
||||
@@ -285,18 +289,6 @@ optional_component(PHONON OFF "Engine: Phonon backend (UNSTABLE)"
|
||||
DEPENDS "phonon4qt5" PHONON_FOUND
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
optional_component(DEEZER ON "Engine: Deezer backend"
|
||||
DEPENDS "libdeezer" LIBDEEZER_FOUND
|
||||
)
|
||||
else ()
|
||||
optional_component(DEEZER ON "Engine: Deezer backend"
|
||||
DEPENDS "Linux" LINUX
|
||||
DEPENDS "libdeezer" LIBDEEZER_FOUND
|
||||
DEPENDS "libpulse" LIBPULSE_FOUND
|
||||
)
|
||||
endif()
|
||||
|
||||
optional_component(CHROMAPRINT ON "Chromaprint (Tag fetching from Musicbrainz)"
|
||||
DEPENDS "chromaprint" CHROMAPRINT_FOUND
|
||||
)
|
||||
@@ -346,22 +338,12 @@ optional_component(SPARKLE ON "Sparkle integration"
|
||||
DEPENDS "Sparkle" SPARKLE
|
||||
)
|
||||
|
||||
optional_component(STREAM_TIDAL ON "Streaming: Tidal support")
|
||||
|
||||
if (LIBDZMEDIA_FOUND OR LIBDEEZER_FOUND)
|
||||
optional_component(STREAM_DEEZER ON "Streaming: Deezer support")
|
||||
else()
|
||||
optional_component(STREAM_DEEZER OFF "Streaming: Deezer support")
|
||||
endif()
|
||||
|
||||
optional_component(DZMEDIA ON "DZMedia"
|
||||
DEPENDS "libdzmedia" LIBDZMEDIA_FOUND
|
||||
DEPENDS "Deezer support" HAVE_STREAM_DEEZER
|
||||
optional_component(TRANSLATIONS ON "Translations"
|
||||
DEPENDS "gettext" GETTEXT_FOUND
|
||||
DEPENDS "Qt5LinguistTools" Qt5LinguistTools_FOUND
|
||||
)
|
||||
|
||||
if (HAVE_STREAM_DEEZER AND NOT HAVE_DZMEDIA AND NOT HAVE_DEEZER)
|
||||
message(STATUS "Deezer is enabled, but not DZMedia or Deezer engine, only preview streams will be available.")
|
||||
endif()
|
||||
optional_component(STREAM_TIDAL ON "Streaming: Tidal support")
|
||||
|
||||
if(APPLE)
|
||||
option(USE_BUNDLE "Bundle macOS dependencies" OFF)
|
||||
@@ -407,8 +389,8 @@ add_custom_target(uninstall
|
||||
|
||||
# Show a summary of what we have enabled
|
||||
summary_show()
|
||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC AND NOT HAVE_PHONON AND NOT HAVE_DEEZER)
|
||||
message(FATAL_ERROR "You need to have either GStreamer, Xine, VLC, Phonon or Deezer to compile!")
|
||||
if(NOT HAVE_GSTREAMER AND NOT HAVE_XINE AND NOT HAVE_VLC AND NOT HAVE_PHONON)
|
||||
message(FATAL_ERROR "You need to have either GStreamer, Xine, VLC or Phonon to compile!")
|
||||
elseif(NOT HAVE_GSTREAMER)
|
||||
message(WARNING "GStreamer is the only engine that is fully implemented. Using other engines is possible but not recommended.")
|
||||
endif()
|
||||
|
||||
40
Changelog
@@ -2,6 +2,46 @@ Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
Version 0.5.3:
|
||||
|
||||
* Changed default tagging to albumartist in organise dialog
|
||||
* Removed support for older taglib in tagreader
|
||||
* Made lyrics selectable in context
|
||||
* Added boom and rainbow analyzers
|
||||
* Made it possible to use enter in shortcuts
|
||||
* Replaced "no album cover" image
|
||||
* Capitalized Strawberry in OSD and tooltip
|
||||
* Added artist search to Tidal
|
||||
* Created systray tooltip workaround for KDE
|
||||
* Changed defaults for backend fade setting
|
||||
* Changed backend settings to allow setting device back to automatic when a custom device is active
|
||||
* Hide ALSA options on non-ALSA systems
|
||||
* Showing errors in dialog when editing tags fails
|
||||
* Update database immediately when saving tags were successful
|
||||
* Show Strawberry icon in OSD when stopping track
|
||||
* Added support for translations
|
||||
* Renamed desktop and appdata files to follow freedesktop specifications
|
||||
* No longer allowing X11 shortcuts on Wayland
|
||||
* Fixed handling of UNC paths in gstreamer engine
|
||||
* Added option to disable volume control
|
||||
* Removed Deezer support (SDK discontinued and streams are encrypted)
|
||||
* Added Norwegian and Spanish translations
|
||||
* Added setting to allow automatically saving album covers directly to album directory
|
||||
* Updated mimetypes
|
||||
* Added basic support for system and custom icons
|
||||
* Moved loading of device icons to device model
|
||||
* Added better support for APE tags
|
||||
* Fixed problems identifying song as collection songs when loading playlist files
|
||||
* Fixed problems loading Tidal URLs from playlist files
|
||||
* Added support for saving and restoring geometry in settings, organise and transcoder dialogs
|
||||
* Improved Tidal error handling and automatic login
|
||||
* Improved Tidal search to handle duplicate albums
|
||||
* Notify collection backend about renamed files when organising files
|
||||
* Added more background image options
|
||||
* Removed API Seeds lyrics provider (require payment)
|
||||
* Added group by format
|
||||
* Fixed gstreamer leaks
|
||||
|
||||
Version 0.5.2:
|
||||
|
||||
* Added error handling and message for URL handler
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
from opensuse:tumbleweed
|
||||
from opensuse/leap
|
||||
|
||||
run zypper --non-interactive --gpg-auto-import-keys ref
|
||||
run zypper --non-interactive --gpg-auto-import-keys dup -l -y
|
||||
|
||||
run zypper --non-interactive --gpg-auto-import-keys install \
|
||||
lsb-release git tar make cmake gcc gcc-c++ pkg-config \
|
||||
lsb-release git tar make cmake gcc gcc-c++ pkg-config gettext-tools \
|
||||
glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel \
|
||||
boost-devel protobuf-devel sqlite3-devel taglib-devel \
|
||||
gstreamer-devel gstreamer-plugins-base-devel libxine-devel vlc-devel \
|
||||
libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5Sql-devel \
|
||||
libQt5DBus-devel libqt5-qtx11extras-devel libqt5-qtbase-common-devel \
|
||||
libQt5DBus-devel libqt5-qtx11extras-devel libqt5-qtbase-common-devel libqt5-linguist-devel \
|
||||
libcdio-devel libgpod-devel libplist-devel libmtp-devel libusbmuxd-devel libchromaprint-devel
|
||||
|
||||
run mkdir -p /usr/src/app
|
||||
|
||||
14
README.md
@@ -2,7 +2,7 @@
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)
|
||||
=======================
|
||||
|
||||
Strawberry is a audio 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.
|
||||
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.
|
||||
|
||||
* Website: https://www.strawbs.org/
|
||||
* Github: https://github.com/jonaski/strawberry
|
||||
@@ -20,12 +20,12 @@ Strawberry is a audio player and music collection organizer. It is a fork of Cle
|
||||
* Edit tags on music files
|
||||
* Fetch tags from MusicBrainz
|
||||
* Album cover art from Last.fm, Musicbrainz and Discogs
|
||||
* Song lyrics from AudD and API Seeds
|
||||
* Song lyrics from AudD
|
||||
* Support for multiple backends
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
* Streaming support for Tidal and Deezer
|
||||
* Streaming support for Tidal
|
||||
* Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
|
||||
It has so far been tested to work on Linux, OpenBSD, macOS and Windows.
|
||||
@@ -48,7 +48,7 @@ To build Strawberry from source you need the following installed on your system
|
||||
* [ALSA library (linux)](https://www.alsa-project.org/)
|
||||
* [DBus (linux)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||
* [PulseAudio (linux optional)](https://www.freedesktop.org/wiki/Software/PulseAudio/?)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org), [VLC](https://www.videolan.org), [Deezer](https://build-repo.deezer.com/native_sdk/deezer-native-sdk-v1.2.10.zip) or [Phonon](https://techbase.kde.org/Phonon)
|
||||
* [GStreamer](https://gstreamer.freedesktop.org/), [Xine](https://www.xine-project.org), [VLC](https://www.videolan.org) or [Phonon](https://techbase.kde.org/Phonon)
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
@@ -57,13 +57,9 @@ Optional dependencies:
|
||||
* iPod Classic devices: [libgpod](http://www.gtkpod.org/libgpod/)
|
||||
* iPhone, iPod Touch, iPad and Apple TV devices: [libimobiledevice, libplist and libusbmuxd](https://www.libimobiledevice.org/)
|
||||
|
||||
Either GStreamer, Xine, VLC, Deezer or Phonon engine is required, but only GStreamer is fully implemented so far.
|
||||
Either GStreamer, Xine, VLC or Phonon engine is required, but only GStreamer is fully implemented so far.
|
||||
You should also install the gstreamer plugins base and good, and optionally bad and ugly.
|
||||
|
||||
Deezer streams with full songs are encrypted and only urls for preview streams (MP3) are exposed by the API.
|
||||
Full length songs requires the use of deezers own engine (Deezer SDK).
|
||||
The Deezer SDK can be found here: https://build-repo.deezer.com/native_sdk/deezer-native-sdk-v1.2.10.zip
|
||||
|
||||
### :wrench: Compiling from source
|
||||
|
||||
### Get the code:
|
||||
|
||||
77
cmake/Translations.cmake
Normal file
@@ -0,0 +1,77 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||
if(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||
message(FATAL_ERROR "Could not find xgettext executable")
|
||||
endif(NOT GETTEXT_XGETTEXT_EXECUTABLE)
|
||||
|
||||
set (XGETTEXT_OPTIONS
|
||||
--qt
|
||||
--keyword=tr:1,2c
|
||||
--keyword=tr --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
|
||||
--keyword=trUtf8 --flag=tr:1:pass-c-format --flag=tr:1:pass-qt-format
|
||||
--keyword=translate:2,3c
|
||||
--keyword=translate:2 --flag=translate:2:pass-c-format --flag=translate:2:pass-qt-format
|
||||
--keyword=QT_TR_NOOP --flag=QT_TR_NOOP:1:pass-c-format --flag=QT_TR_NOOP:1:pass-qt-format
|
||||
--keyword=QT_TRANSLATE_NOOP:2 --flag=QT_TRANSLATE_NOOP:2:pass-c-format --flag=QT_TRANSLATE_NOOP:2:pass-qt-format
|
||||
--keyword=_ --flag=_:1:pass-c-format --flag=_:1:pass-qt-format
|
||||
--keyword=N_ --flag=N_:1:pass-c-format --flag=N_:1:pass-qt-format
|
||||
--from-code=utf-8
|
||||
)
|
||||
|
||||
macro(add_pot outfiles header pot)
|
||||
# Make relative filenames for all source files
|
||||
set(add_pot_sources)
|
||||
foreach(_filename ${ARGN})
|
||||
get_filename_component(_absolute_filename ${_filename} ABSOLUTE)
|
||||
file(RELATIVE_PATH _relative_filename ${CMAKE_CURRENT_SOURCE_DIR} ${_absolute_filename})
|
||||
list(APPEND add_pot_sources ${_relative_filename})
|
||||
endforeach(_filename)
|
||||
|
||||
# Generate the .pot
|
||||
add_custom_command(
|
||||
OUTPUT ${pot}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTIONS} -s -C --omit-header --output=${CMAKE_CURRENT_BINARY_DIR}/pot.temp ${add_pot_sources}
|
||||
COMMAND cat ${header} ${CMAKE_CURRENT_BINARY_DIR}/pot.temp > ${pot}
|
||||
DEPENDS ${add_pot_sources} ${header}
|
||||
)
|
||||
|
||||
list(APPEND ${outfiles} ${pot})
|
||||
endmacro(add_pot)
|
||||
|
||||
# Syntax is:
|
||||
# add_po(sources_var po_prefix LANGUAGES language1 language2 ... DIRECTORY dir)
|
||||
|
||||
macro(add_po outfiles po_prefix)
|
||||
parse_arguments(ADD_PO
|
||||
"LANGUAGES;DIRECTORY"
|
||||
""
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
foreach (_lang ${ADD_PO_LANGUAGES})
|
||||
set(_po_filename "${_lang}.po")
|
||||
set(_po_filepath "${CMAKE_CURRENT_SOURCE_DIR}/${ADD_PO_DIRECTORY}/${_po_filename}")
|
||||
set(_qm_filename "strawberry_${_lang}.qm")
|
||||
set(_qm_filepath "${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/${_qm_filename}")
|
||||
|
||||
# Convert the .po files to .qm files
|
||||
add_custom_command(
|
||||
OUTPUT ${_qm_filepath}
|
||||
COMMAND ${QT_LCONVERT_EXECUTABLE} ARGS ${_po_filepath} -o ${_qm_filepath} -of qm
|
||||
DEPENDS ${_po_filepath} ${_po_filepath}
|
||||
)
|
||||
|
||||
list(APPEND ${outfiles} ${_qm_filepath})
|
||||
endforeach (_lang)
|
||||
|
||||
# Generate a qrc file for the translations
|
||||
set(_qrc ${CMAKE_CURRENT_BINARY_DIR}/${ADD_PO_DIRECTORY}/translations.qrc)
|
||||
file(WRITE ${_qrc} "<RCC><qresource prefix=\"/${ADD_PO_DIRECTORY}\">")
|
||||
foreach(_lang ${ADD_PO_LANGUAGES})
|
||||
file(APPEND ${_qrc} "<file>${po_prefix}${_lang}.qm</file>")
|
||||
endforeach(_lang)
|
||||
file(APPEND ${_qrc} "</qresource></RCC>")
|
||||
qt5_add_resources(${outfiles} ${_qrc})
|
||||
endmacro(add_po)
|
||||
@@ -1,6 +1,6 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 5)
|
||||
set(STRAWBERRY_VERSION_PATCH 2)
|
||||
set(STRAWBERRY_VERSION_PATCH 3)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
@@ -84,8 +84,8 @@ if(GIT_REVISION)
|
||||
set(STRAWBERRY_VERSION_PACKAGE "${GIT_TAGNAME}.${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
||||
set(STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
|
||||
set(STRAWBERRY_VERSION_RPM_R "2.${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
||||
set(STRAWBERRY_VERSION_PAC_V "${GIT_TAGNAME}")
|
||||
set(STRAWBERRY_VERSION_PAC_R "${GIT_COMMITCOUNT}")
|
||||
set(STRAWBERRY_VERSION_PAC_V "${GIT_TAGNAME}.r${GIT_COMMITCOUNT}.${GIT_SHA1}")
|
||||
set(STRAWBERRY_VERSION_PAC_R "1")
|
||||
|
||||
endif()
|
||||
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
<file>schema/schema-3.sql</file>
|
||||
<file>schema/device-schema.sql</file>
|
||||
<file>style/strawberry.css</file>
|
||||
<file>misc/playing_tooltip.txt</file>
|
||||
<file>misc/oauthsuccess.html</file>
|
||||
<file>html/playing-tooltip-plain.html</file>
|
||||
<file>html/playing-tooltip-table.html</file>
|
||||
<file>html/oauthsuccess.html</file>
|
||||
<file>pictures/strawberry.png</file>
|
||||
<file>pictures/strawberry-faded.png</file>
|
||||
<file>pictures/noalbumart.png</file>
|
||||
<file>pictures/nomusic.png</file>
|
||||
<file>pictures/cdcase.png</file>
|
||||
<file>pictures/musicbrainz.png</file>
|
||||
<file>pictures/tiny-play.png</file>
|
||||
<file>pictures/tiny-pause.png</file>
|
||||
@@ -28,7 +29,8 @@
|
||||
<file>pictures/osd_background.png</file>
|
||||
<file>pictures/osd_shadow_corner.png</file>
|
||||
<file>pictures/osd_shadow_edge.png</file>
|
||||
<file>pictures/deezer.png</file>
|
||||
<file>fonts/HumongousofEternitySt.ttf</file>
|
||||
<file>pictures/nyancat.png</file>
|
||||
<file>pictures/rainbowdash.png</file>
|
||||
<file>fonts/HumongousofEternitySt.ttf</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
8
data/html/playing-tooltip-plain.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<h4>%appName</h4>
|
||||
<p>
|
||||
%image<br />
|
||||
%titleKey: %titleValue<br />
|
||||
%artistKey: %artistValue<br />
|
||||
%albumKey: %albumValue<br />
|
||||
%lengthKey: %lengthValue<br />
|
||||
</p>
|
||||
@@ -63,7 +63,6 @@
|
||||
<file>icons/128x128/media-rewind.png</file>
|
||||
<file>icons/128x128/media-stop.png</file>
|
||||
<file>icons/128x128/nvidia.png</file>
|
||||
<file>icons/128x128/play2.png</file>
|
||||
<file>icons/128x128/realtek.png</file>
|
||||
<file>icons/128x128/search.png</file>
|
||||
<file>icons/128x128/soundcard.png</file>
|
||||
@@ -85,7 +84,6 @@
|
||||
<file>icons/128x128/zoom-in.png</file>
|
||||
<file>icons/128x128/zoom-out.png</file>
|
||||
<file>icons/128x128/tidal.png</file>
|
||||
<file>icons/128x128/deezer.png</file>
|
||||
<file>icons/128x128/scrobble.png</file>
|
||||
<file>icons/128x128/scrobble-disabled.png</file>
|
||||
<file>icons/64x64/albums.png</file>
|
||||
@@ -151,7 +149,6 @@
|
||||
<file>icons/64x64/media-rewind.png</file>
|
||||
<file>icons/64x64/media-stop.png</file>
|
||||
<file>icons/64x64/nvidia.png</file>
|
||||
<file>icons/64x64/play2.png</file>
|
||||
<file>icons/64x64/pulseaudio.png</file>
|
||||
<file>icons/64x64/realtek.png</file>
|
||||
<file>icons/64x64/search.png</file>
|
||||
@@ -173,7 +170,6 @@
|
||||
<file>icons/64x64/zoom-in.png</file>
|
||||
<file>icons/64x64/zoom-out.png</file>
|
||||
<file>icons/64x64/tidal.png</file>
|
||||
<file>icons/64x64/deezer.png</file>
|
||||
<file>icons/64x64/scrobble.png</file>
|
||||
<file>icons/64x64/scrobble-disabled.png</file>
|
||||
<file>icons/48x48/albums.png</file>
|
||||
@@ -242,7 +238,6 @@
|
||||
<file>icons/48x48/media-rewind.png</file>
|
||||
<file>icons/48x48/media-stop.png</file>
|
||||
<file>icons/48x48/nvidia.png</file>
|
||||
<file>icons/48x48/play2.png</file>
|
||||
<file>icons/48x48/pulseaudio.png</file>
|
||||
<file>icons/48x48/realtek.png</file>
|
||||
<file>icons/48x48/search.png</file>
|
||||
@@ -332,7 +327,6 @@
|
||||
<file>icons/32x32/media-rewind.png</file>
|
||||
<file>icons/32x32/media-stop.png</file>
|
||||
<file>icons/32x32/nvidia.png</file>
|
||||
<file>icons/32x32/play2.png</file>
|
||||
<file>icons/32x32/pulseaudio.png</file>
|
||||
<file>icons/32x32/realtek.png</file>
|
||||
<file>icons/32x32/search.png</file>
|
||||
@@ -355,7 +349,6 @@
|
||||
<file>icons/32x32/zoom-in.png</file>
|
||||
<file>icons/32x32/zoom-out.png</file>
|
||||
<file>icons/32x32/tidal.png</file>
|
||||
<file>icons/32x32/deezer.png</file>
|
||||
<file>icons/32x32/scrobble.png</file>
|
||||
<file>icons/32x32/scrobble-disabled.png</file>
|
||||
<file>icons/22x22/albums.png</file>
|
||||
@@ -424,7 +417,6 @@
|
||||
<file>icons/22x22/media-rewind.png</file>
|
||||
<file>icons/22x22/media-stop.png</file>
|
||||
<file>icons/22x22/nvidia.png</file>
|
||||
<file>icons/22x22/play2.png</file>
|
||||
<file>icons/22x22/pulseaudio.png</file>
|
||||
<file>icons/22x22/realtek.png</file>
|
||||
<file>icons/22x22/search.png</file>
|
||||
@@ -447,7 +439,6 @@
|
||||
<file>icons/22x22/zoom-in.png</file>
|
||||
<file>icons/22x22/zoom-out.png</file>
|
||||
<file>icons/22x22/tidal.png</file>
|
||||
<file>icons/22x22/deezer.png</file>
|
||||
<file>icons/22x22/scrobble.png</file>
|
||||
<file>icons/22x22/scrobble-disabled.png</file>
|
||||
</qresource>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 23 KiB |
BIN
data/pictures/cdcase.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 12 KiB |
BIN
data/pictures/nyancat.png
Normal file
|
After Width: | Height: | Size: 850 B |
BIN
data/pictures/rainbowdash.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
4
dist/CMakeLists.txt
vendored
@@ -20,8 +20,8 @@ if (UNIX AND NOT APPLE)
|
||||
install(FILES ../data/icons/64x64/strawberry.png DESTINATION share/icons/hicolor/64x64/apps/)
|
||||
install(FILES ../data/icons/128x128/strawberry.png DESTINATION share/icons/hicolor/128x128/apps/)
|
||||
install(FILES ../data/icons/128x128/strawberry.svg DESTINATION share/icons/hicolor/scalable/apps/)
|
||||
install(FILES unix/strawberry.desktop DESTINATION share/applications)
|
||||
install(FILES unix/strawberry.appdata.xml DESTINATION share/metainfo)
|
||||
install(FILES unix/org.strawbs.strawberry.desktop DESTINATION share/applications)
|
||||
install(FILES unix/org.strawbs.strawberry.appdata.xml DESTINATION share/metainfo)
|
||||
install(FILES man/strawberry.1 man/strawberry-tagreader.1 DESTINATION share/man/man1)
|
||||
endif()
|
||||
|
||||
|
||||
6
dist/debian/control
vendored
@@ -19,7 +19,7 @@ Build-Depends: debhelper (>= 7),
|
||||
qtbase5-dev,
|
||||
qtbase5-dev-tools,
|
||||
qtbase5-private-dev,
|
||||
qt5-dev-tools,
|
||||
qttools5-dev,
|
||||
libqt5x11extras5-dev,
|
||||
libgstreamer1.0-dev,
|
||||
libgstreamer-plugins-base1.0-dev,
|
||||
@@ -47,7 +47,7 @@ Depends: ${shlibs:Depends},
|
||||
gstreamer1.0-pulseaudio
|
||||
Homepage: http://www.strawbs.org/
|
||||
Description: Audio player and music collection organizer
|
||||
Strawberry is a audio player aimed at music collectors, audio enthusiasts and audiophiles.
|
||||
Strawberry is a music player aimed at music collectors, audio enthusiasts and audiophiles.
|
||||
.
|
||||
Features:
|
||||
- Play and organize music
|
||||
@@ -59,7 +59,7 @@ Description: Audio player and music collection organizer
|
||||
- Edit tags on music files
|
||||
- Fetch tags from MusicBrainz
|
||||
- Album cover art from Lastfm, Musicbrainz and Discogs
|
||||
- Song lyrics from AudD and API Seeds
|
||||
- Song lyrics from AudD
|
||||
- Support for multiple backends
|
||||
- Audio analyzer
|
||||
- Audio equalizer
|
||||
|
||||
435
dist/debian/copyright
vendored
@@ -8,184 +8,179 @@ Copyright: 2010-2015, David Sansome <me@davidsansome.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/core/timeconstants.h
|
||||
ext/libstrawberry-common/core/logging.cpp
|
||||
ext/libstrawberry-common/core/logging.h
|
||||
ext/libstrawberry-common/core/messagehandler.cpp
|
||||
ext/libstrawberry-common/core/messagehandler.h
|
||||
ext/libstrawberry-common/core/override.h
|
||||
ext/libstrawberry-common/core/logging.cpp
|
||||
ext/libstrawberry-common/core/logging.h
|
||||
ext/libstrawberry-common/core/messagehandler.cpp
|
||||
ext/libstrawberry-common/core/messagehandler.h
|
||||
ext/libstrawberry-common/core/override.h
|
||||
Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
|
||||
License: Apache-2.0
|
||||
|
||||
Files: src/core/main.h
|
||||
src/config.h.in
|
||||
src/version.h.in
|
||||
src/context/contextview.cpp
|
||||
src/context/contextview.h
|
||||
src/engine/enginetype.cpp
|
||||
src/engine/enginetype.h
|
||||
src/engine/alsadevicefinder.cpp
|
||||
src/engine/alsadevicefinder.h
|
||||
src/engine/deezerengine.cpp
|
||||
src/engine/deezerengine.h
|
||||
src/engine/devicefinder.cpp
|
||||
src/engine/devicefinder.h
|
||||
src/engine/enginedevice.cpp
|
||||
src/engine/enginedevice.h
|
||||
src/engine/phononengine.cpp
|
||||
src/engine/phononengine.h
|
||||
src/internet/internetservice.cpp
|
||||
src/internet/internetservice.h
|
||||
src/settings/backendsettingspage.cpp
|
||||
src/settings/backendsettingspage.h
|
||||
src/settings/scrobblersettingspage.cpp
|
||||
src/settings/scrobblersettingspage.h
|
||||
src/settings/deezersettingspage.cpp
|
||||
src/settings/deezersettingspage.h
|
||||
src/settings/tidalsettingspage.cpp
|
||||
src/settings/tidalsettingspage.h
|
||||
src/covermanager/lastfmcoverprovider.cpp
|
||||
src/covermanager/lastfmcoverprovider.h
|
||||
src/covermanager/musicbrainzcoverprovider.cpp
|
||||
src/covermanager/musicbrainzcoverprovider.h
|
||||
src/globalshortcuts/globalshortcutbackend-system.cpp
|
||||
src/globalshortcuts/globalshortcutbackend-system.h
|
||||
src/globalshortcuts/globalshortcut.cpp
|
||||
src/globalshortcuts/globalshortcut.h
|
||||
src/globalshortcuts/globalshortcut-X11.cpp
|
||||
src/globalshortcuts/globalshortcut-win.cpp
|
||||
src/globalshortcuts/keymapper_x11.h
|
||||
src/globalshortcuts/keymapper_win.h
|
||||
src/lyrics/*
|
||||
src/scrobbler/*
|
||||
src/tidal/*
|
||||
src/deezer/*
|
||||
transcoderoptionswavpack.cpp
|
||||
transcoderoptionswavpack.h
|
||||
src/config.h.in
|
||||
src/version.h.in
|
||||
src/context/contextview.cpp
|
||||
src/context/contextview.h
|
||||
src/engine/enginetype.cpp
|
||||
src/engine/enginetype.h
|
||||
src/engine/alsadevicefinder.cpp
|
||||
src/engine/alsadevicefinder.h
|
||||
src/engine/devicefinder.cpp
|
||||
src/engine/devicefinder.h
|
||||
src/engine/enginedevice.cpp
|
||||
src/engine/enginedevice.h
|
||||
src/engine/phononengine.cpp
|
||||
src/engine/phononengine.h
|
||||
src/internet/internetservice.cpp
|
||||
src/internet/internetservice.h
|
||||
src/settings/backendsettingspage.cpp
|
||||
src/settings/backendsettingspage.h
|
||||
src/settings/scrobblersettingspage.cpp
|
||||
src/settings/scrobblersettingspage.h
|
||||
src/settings/tidalsettingspage.cpp
|
||||
src/settings/tidalsettingspage.h
|
||||
src/covermanager/lastfmcoverprovider.cpp
|
||||
src/covermanager/lastfmcoverprovider.h
|
||||
src/covermanager/musicbrainzcoverprovider.cpp
|
||||
src/covermanager/musicbrainzcoverprovider.h
|
||||
src/globalshortcuts/globalshortcutbackend-system.cpp
|
||||
src/globalshortcuts/globalshortcutbackend-system.h
|
||||
src/globalshortcuts/globalshortcut.cpp
|
||||
src/globalshortcuts/globalshortcut.h
|
||||
src/globalshortcuts/globalshortcut-X11.cpp
|
||||
src/globalshortcuts/globalshortcut-win.cpp
|
||||
src/globalshortcuts/keymapper_x11.h
|
||||
src/globalshortcuts/keymapper_win.h
|
||||
src/lyrics/*
|
||||
src/scrobbler/*
|
||||
src/tidal/*
|
||||
src/transcoder/transcoderoptionswavpack.cpp
|
||||
src/transcoder/transcoderoptionswavpack.h
|
||||
Copyright: 2012-2014, 2017-2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/core/main.cpp
|
||||
src/core/mainwindow.cpp
|
||||
src/core/mainwindow.h
|
||||
src/core/player.cpp
|
||||
src/core/player.h
|
||||
src/core/song.cpp
|
||||
src/core/song.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/engine/gstenginepipeline.cpp
|
||||
src/engine/gstenginepipeline.h
|
||||
src/engine/vlcengine.cpp
|
||||
src/engine/vlcengine.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/dialogs/about.cpp
|
||||
src/dialogs/about.h
|
||||
src/playlist/playlistitem.cpp
|
||||
src/playlist/playlistitem.h
|
||||
src/playlist/playlistdelegates.cpp
|
||||
src/playlist/playlistdelegates.h
|
||||
src/internet/internetplaylistitem.cpp
|
||||
src/internet/internetsearch.cpp
|
||||
src/internet/internetsearch.h
|
||||
src/internet/internetsearchview.cpp
|
||||
src/internet/internetsearchview.h
|
||||
src/internet/internetservices.cpp
|
||||
src/internet/internetservices.h
|
||||
ext/libstrawberry-tagreader/tagreader.cpp
|
||||
ext/libstrawberry-tagreader/tagreader.h
|
||||
src/device/devicemanager.cpp
|
||||
src/device/devicemanager.h
|
||||
src/device/deviceinfo.cpp
|
||||
src/device/deviceinfo.h
|
||||
src/device/deviceproperties.cpp
|
||||
src/device/deviceproperties.h
|
||||
src/device/deviceview.cpp
|
||||
src/device/deviceview.h
|
||||
src/device/connecteddevice.cpp
|
||||
src/device/connecteddevice.h
|
||||
src/globalshortcuts/globalshortcuts.cpp
|
||||
src/globalshortcuts/globalshortcuts.h
|
||||
src/settings/shortcutssettingspage.cpp
|
||||
src/settings/shortcutssettingspage.h
|
||||
src/organise/organise.cpp
|
||||
src/organise/organise.h
|
||||
src/organise/organisedialog.cpp
|
||||
src/organise/organisedialog.h
|
||||
src/organise/organiseerrordialog.cpp
|
||||
src/organise/organiseerrordialog.h
|
||||
src/transcoder/transcoder.cpp
|
||||
src/transcoder/transcoder.h
|
||||
src/core/mainwindow.cpp
|
||||
src/core/mainwindow.h
|
||||
src/core/player.cpp
|
||||
src/core/player.h
|
||||
src/core/song.cpp
|
||||
src/core/song.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/engine/gstenginepipeline.cpp
|
||||
src/engine/gstenginepipeline.h
|
||||
src/engine/vlcengine.cpp
|
||||
src/engine/vlcengine.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/dialogs/about.cpp
|
||||
src/dialogs/about.h
|
||||
src/playlist/playlistitem.cpp
|
||||
src/playlist/playlistitem.h
|
||||
src/playlist/playlistdelegates.cpp
|
||||
src/playlist/playlistdelegates.h
|
||||
src/internet/internetplaylistitem.cpp
|
||||
src/internet/internetsearch.cpp
|
||||
src/internet/internetsearch.h
|
||||
src/internet/internetsearchview.cpp
|
||||
src/internet/internetsearchview.h
|
||||
src/internet/internetservices.cpp
|
||||
src/internet/internetservices.h
|
||||
ext/libstrawberry-tagreader/tagreader.cpp
|
||||
ext/libstrawberry-tagreader/tagreader.h
|
||||
src/device/devicemanager.cpp
|
||||
src/device/devicemanager.h
|
||||
src/device/deviceinfo.cpp
|
||||
src/device/deviceinfo.h
|
||||
src/device/deviceproperties.cpp
|
||||
src/device/deviceproperties.h
|
||||
src/device/deviceview.cpp
|
||||
src/device/deviceview.h
|
||||
src/device/connecteddevice.cpp
|
||||
src/device/connecteddevice.h
|
||||
src/globalshortcuts/globalshortcuts.cpp
|
||||
src/globalshortcuts/globalshortcuts.h
|
||||
src/settings/shortcutssettingspage.cpp
|
||||
src/settings/shortcutssettingspage.h
|
||||
src/organise/organise.cpp
|
||||
src/organise/organise.h
|
||||
src/organise/organisedialog.cpp
|
||||
src/organise/organisedialog.h
|
||||
src/organise/organiseerrordialog.cpp
|
||||
src/organise/organiseerrordialog.h
|
||||
src/transcoder/transcoder.cpp
|
||||
src/transcoder/transcoder.h
|
||||
Copyright: 2010, 2012-2014 David Sansome <me@davidsansome.com>
|
||||
2012-2014, 2017-2018 Jonas Kvinge <jonas@jkvinge.net>
|
||||
2012-2014, 2017-2018 Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/engine/enginebase.cpp
|
||||
src/engine/enginebase.h
|
||||
src/engine/enginebase.h
|
||||
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2010, David Sansome <me@davidsansome.com>
|
||||
2004, 2005, Max Howell, <max.howell@methylblue.com>
|
||||
2003, Mark Kretschmann <markey@web.de>
|
||||
2010, David Sansome <me@davidsansome.com>
|
||||
2004, 2005, Max Howell, <max.howell@methylblue.com>
|
||||
2003, Mark Kretschmann <markey@web.de>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/engine/gstengine.cpp
|
||||
src/engine/gstengine.h
|
||||
src/engine/gstengine.h
|
||||
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2006, Paul Cifarelli <paul@cifarelli.net>
|
||||
2005, Jakub Stachowski <qbast@go2.pl>
|
||||
2003-2005, Mark Kretschmann <markey@web.de>
|
||||
2006, Paul Cifarelli <paul@cifarelli.net>
|
||||
2005, Jakub Stachowski <qbast@go2.pl>
|
||||
2003-2005, Mark Kretschmann <markey@web.de>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/engine/xineengine.cpp
|
||||
src/engine/xineengine.h
|
||||
src/engine/xineengine.h
|
||||
Copyright: 2017, 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2005, Ian Monroe <ian@monroe.nu>
|
||||
2005, Christophe Thommeret <hftom@free.fr>
|
||||
2005, 2006, Mark Kretschmann <markey@web.de>
|
||||
2004, 2005, Max Howell <max.howell@methylblue.com>
|
||||
2003, 2004, J. Kofler <kaffeine@gmx.net>
|
||||
2005, Ian Monroe <ian@monroe.nu>
|
||||
2005, Christophe Thommeret <hftom@free.fr>
|
||||
2005, 2006, Mark Kretschmann <markey@web.de>
|
||||
2004, 2005, Max Howell <max.howell@methylblue.com>
|
||||
2003, 2004, J. Kofler <kaffeine@gmx.net>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/engine/xinescope.c
|
||||
src/engine/xinescope.h
|
||||
src/engine/xinescope.h
|
||||
Copyright: 2004, Max Howell <max.howell@methylblue.com>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/widgets/fancytabwidget.cpp
|
||||
src/widgets/fancytabwidget.h
|
||||
src/widgets/fancytabwidget.h
|
||||
Copyright: 2018, Vikram Ambrose <ambroseworks@gmail.com>
|
||||
2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/core/application.cpp
|
||||
src/core/application.h
|
||||
src/core/application.h
|
||||
Copyright: 2012, David Sansome <me@davidsansome.com>
|
||||
2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/core/appearance.cpp
|
||||
src/core/appearance.h
|
||||
src/core/appearance.h
|
||||
Copyright: 2012, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/covermanager/discogscoverprovider.cpp
|
||||
src/covermanager/discogscoverprovider.h
|
||||
src/covermanager/discogscoverprovider.h
|
||||
Copyright: 2012, Martin Björklund <mbj4668@gmail.com>
|
||||
2016, Jonas Kvinge <jonas@jkvinge.net>
|
||||
2016, Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: ext/libstrawberry-common/core/arraysize.h
|
||||
ext/libstrawberry-common/core/scoped_nsautorelease_pool.h
|
||||
ext/libstrawberry-common/core/scoped_nsautorelease_pool.mm
|
||||
ext/libstrawberry-common/core/scoped_nsautorelease_pool.h
|
||||
ext/libstrawberry-common/core/scoped_nsautorelease_pool.mm
|
||||
Copyright: 2010, 2011, 2014, The Chromium Authors.
|
||||
License: BSD-style
|
||||
|
||||
@@ -194,19 +189,47 @@ Copyright: 2016, John Maguire <john.maguire@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/analyzer/fht.cpp
|
||||
src/analyzer/fht.h
|
||||
Copyright: 2004, Melchior FRANZ - mfranz@kde.org
|
||||
src/analyzer/fht.h
|
||||
Copyright: 2004, Melchior FRANZ <mfranz@kde.org>
|
||||
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
2017, Santiago Gil
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/analyzer/analyzerbase.cpp
|
||||
src/analyzer/analyzerbase.h
|
||||
src/analyzer/analyzerbase.h
|
||||
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
2009-2012, David Sansome <me@davidsansome.com>
|
||||
2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2017, Santiago Gil
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/analyzer/blockanalyzer.cpp
|
||||
src/analyzer/blockanalyzer.h
|
||||
src/analyzer/blockanalyzer.h
|
||||
Copyright: 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
2005, Mark Kretschmann <markey@web.de>
|
||||
2005, Mark Kretschmann <markey@web.de>
|
||||
2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/analyzer/boomanalyzer.cpp
|
||||
src/analyzer/boomanalyzer.h
|
||||
Copyright: 2004, Max Howell <max.howell@methylblue.com>
|
||||
2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/analyzer/rainbowanalyzer.cpp
|
||||
src/analyzer/rainbowanalyzer.h
|
||||
Copyright: 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
|
||||
2011-2012, 2014, David Sansome <me@davidsansome.com>
|
||||
2011, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/collection/savedgroupingmanager.h
|
||||
@@ -214,53 +237,53 @@ Copyright: 2015, Nick Lanham <nick@afternight.org>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/core/scoped_cftyperef.h
|
||||
src/core/scoped_nsobject.h
|
||||
src/core/scoped_nsobject.h
|
||||
Copyright: 2010, 2011, 2014, The Chromium Authors.
|
||||
License: BSD-style
|
||||
|
||||
Files: src/core/signalchecker.cpp
|
||||
Copyright: 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2010, David Sansome <me@davidsansome.com>
|
||||
2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2010, David Sansome <me@davidsansome.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/device/udisks2lister.cpp
|
||||
src/device/udisks2lister.h
|
||||
src/device/udisks2lister.h
|
||||
Copyright: 2016, Valeriy Malov <jazzvoid@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/internet/localredirectserver.cpp
|
||||
src/internet/localredirectserver.h
|
||||
src/internet/localredirectserver.h
|
||||
Copyright: 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/transcoder/transcoderoptionsopus.cpp
|
||||
src/transcoder/transcoderoptionsopus.h
|
||||
src/transcoder/transcoderoptionsopus.h
|
||||
Copyright: 2013, Martin Brodbeck <martin@brodbeck-online.de>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/widgets/clickablelabel.cpp
|
||||
src/widgets/clickablelabel.h
|
||||
src/widgets/renametablineedit.cpp
|
||||
src/widgets/renametablineedit.h
|
||||
src/widgets/clickablelabel.h
|
||||
src/widgets/renametablineedit.cpp
|
||||
src/widgets/renametablineedit.h
|
||||
Copyright: 2010, 2011, Andrea Decorte <adecorte@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/widgets/sliderwidget.cpp
|
||||
src/widgets/sliderwidget.h
|
||||
src/widgets/sliderwidget.h
|
||||
Copyright: 2005, Gábor Lehel
|
||||
2003, Mark Kretschmann <markey@web.de>
|
||||
2003, Mark Kretschmann <markey@web.de>
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/widgets/stylehelper.cpp
|
||||
src/widgets/stylehelper.h
|
||||
src/widgets/stylehelper.h
|
||||
Copyright: 2010, Nokia Corporation and/or its subsidiary(-ies).
|
||||
License: LGPL-2.1
|
||||
|
||||
Files: 3rdparty/SPMediaKeyTap/*
|
||||
Copyright: 2010, Spotify AB
|
||||
Copyright: 2011, Joachim Bengtsson
|
||||
2011, Joachim Bengtsson
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: 3rdparty/qocoa/*
|
||||
@@ -280,21 +303,21 @@ Copyright: 2003-2005, Allan Sandfeld Jensen
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/ape/apefile.cpp
|
||||
3rdparty/taglib/ape/apefile.h
|
||||
3rdparty/taglib/ape/apefile.h
|
||||
Copyright: 2010, Alex Novichkov
|
||||
2006, Lukáš Lalinský
|
||||
2004, Allan Sandfeld Jensen
|
||||
2006, Lukáš Lalinský
|
||||
2004, Allan Sandfeld Jensen
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/ape/apefooter.cpp
|
||||
Copyright: 2004, Allan Sandfeld Jensen
|
||||
2002-2008, Scott Wheeler (id3v2header.cpp)
|
||||
2002-2008, Scott Wheeler (id3v2header.cpp)
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/ape/apeproperties.cpp
|
||||
3rdparty/taglib/ape/apeproperties.h
|
||||
3rdparty/taglib/ape/apeproperties.h
|
||||
Copyright: 2010, Alex Novichkov
|
||||
2006, Lukáš Lalinský
|
||||
2006, Lukáš Lalinský
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/asf/*
|
||||
@@ -302,7 +325,7 @@ Copyright: 2005-2007, 2009-2011, Lukáš Lalinský
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/asf/asfpicture.cpp
|
||||
3rdparty/taglib/asf/asfpicture.h
|
||||
3rdparty/taglib/asf/asfpicture.h
|
||||
Copyright: 2010, Anton Sergunov
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
@@ -320,23 +343,23 @@ License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/fileref.cpp
|
||||
Copyright: 2010, Alex Novichkov
|
||||
2002-2008, Scott Wheeler
|
||||
2002-2008, Scott Wheeler
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/flac/*
|
||||
3rdparty/taglib/mp4/*
|
||||
3rdparty/taglib/mp4/*
|
||||
Copyright: 2005-2007, 2009-2011, Lukáš Lalinský
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/flac/flacfile.cpp
|
||||
3rdparty/taglib/flac/flacfile.h
|
||||
3rdparty/taglib/flac/flacproperties.cpp
|
||||
3rdparty/taglib/flac/flacproperties.h
|
||||
3rdparty/taglib/flac/flacfile.h
|
||||
3rdparty/taglib/flac/flacproperties.cpp
|
||||
3rdparty/taglib/flac/flacproperties.h
|
||||
Copyright: 2003-2005, Allan Sandfeld Jensen
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/it/*
|
||||
3rdparty/taglib/mod/*
|
||||
3rdparty/taglib/mod/*
|
||||
Copyright: 2011, Mathias Panzenböck
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
@@ -345,53 +368,53 @@ Copyright: 2003-2005, Allan Sandfeld Jensen
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h
|
||||
Copyright: 2013, Lukas Krejci
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/podcastframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/podcastframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h
|
||||
Copyright: 2014, 2015, Urs Fleisch
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h
|
||||
Copyright: 2006, Aaron VonderHaar
|
||||
2002-2008, Scott Wheeler
|
||||
2002-2008, Scott Wheeler
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h
|
||||
Copyright: 2012, Rupert Daniel
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h
|
||||
Copyright: 2008, 2011, Lukas Lalinsky
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/privateframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/privateframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/privateframe.h
|
||||
Copyright: 2008, Serkan Kalyoncu
|
||||
2008, Scott Wheeler
|
||||
2008, Scott Wheeler
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h
|
||||
3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp
|
||||
3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.h
|
||||
Copyright: 2006, Urs Fleisch
|
||||
2002-2008, Scott Wheeler
|
||||
2002-2008, Scott Wheeler
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/mpeg/xingheader.cpp
|
||||
3rdparty/taglib/mpeg/xingheader.h
|
||||
3rdparty/taglib/mpeg/xingheader.h
|
||||
Copyright: 2003, Ismael Orenstein
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
@@ -400,48 +423,48 @@ Copyright: 2003-2005, Allan Sandfeld Jensen
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/ogg/opus/*
|
||||
3rdparty/taglib/ogg/speex/*
|
||||
3rdparty/taglib/ogg/speex/*
|
||||
Copyright: 2006, 2012, Lukáš Lalinský
|
||||
2002-2008, Scott Wheeler
|
||||
2002-2008, Scott Wheeler
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/riff/riffutils.h
|
||||
3rdparty/taglib/riff/wav/infotag.cpp
|
||||
3rdparty/taglib/riff/wav/infotag.h
|
||||
3rdparty/taglib/tagutils.cpp
|
||||
3rdparty/taglib/tagutils.h
|
||||
3rdparty/taglib/toolkit/tdebuglistener.cpp
|
||||
3rdparty/taglib/toolkit/tdebuglistener.h
|
||||
3rdparty/taglib/toolkit/trefcounter.cpp
|
||||
3rdparty/taglib/toolkit/trefcounter.h
|
||||
3rdparty/taglib/toolkit/tutils.h
|
||||
3rdparty/taglib/toolkit/tzlib.cpp
|
||||
3rdparty/taglib/toolkit/tzlib.h
|
||||
3rdparty/taglib/mpeg/mpegutils.h
|
||||
3rdparty/taglib/riff/wav/infotag.cpp
|
||||
3rdparty/taglib/riff/wav/infotag.h
|
||||
3rdparty/taglib/tagutils.cpp
|
||||
3rdparty/taglib/tagutils.h
|
||||
3rdparty/taglib/toolkit/tdebuglistener.cpp
|
||||
3rdparty/taglib/toolkit/tdebuglistener.h
|
||||
3rdparty/taglib/toolkit/trefcounter.cpp
|
||||
3rdparty/taglib/toolkit/trefcounter.h
|
||||
3rdparty/taglib/toolkit/tutils.h
|
||||
3rdparty/taglib/toolkit/tzlib.cpp
|
||||
3rdparty/taglib/toolkit/tzlib.h
|
||||
3rdparty/taglib/mpeg/mpegutils.h
|
||||
Copyright: 2012, 2013, 2015, 2016, Tsuda Kageyu
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/s3m/*
|
||||
3rdparty/taglib/xm/*
|
||||
3rdparty/taglib/xm/*
|
||||
Copyright: 2011, Mathias Panzenböck
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/toolkit/tbytevectorstream.cpp
|
||||
3rdparty/taglib/toolkit/tbytevectorstream.h
|
||||
3rdparty/taglib/toolkit/tiostream.cpp
|
||||
3rdparty/taglib/toolkit/tiostream.h
|
||||
3rdparty/taglib/toolkit/tbytevectorstream.h
|
||||
3rdparty/taglib/toolkit/tiostream.cpp
|
||||
3rdparty/taglib/toolkit/tiostream.h
|
||||
Copyright: 2008, 2011, Lukas Lalinsky
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/toolkit/tpropertymap.cpp
|
||||
3rdparty/taglib/toolkit/tpropertymap.h
|
||||
3rdparty/taglib/toolkit/tpropertymap.h
|
||||
Copyright: 2012, Michael Helmling
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/taglib/trueaudio/*
|
||||
3rdparty/taglib/wavpack/*
|
||||
3rdparty/taglib/wavpack/*
|
||||
Copyright: 2006, Lukáš Lalinský
|
||||
2004, Allan Sandfeld Jensen
|
||||
2004, Allan Sandfeld Jensen
|
||||
License: LGPL-2.1 and/or MPL-1.1
|
||||
|
||||
Files: 3rdparty/utf8-cpp/*
|
||||
|
||||
3
dist/debian/rules
vendored
@@ -11,7 +11,8 @@ configure: configure-stamp
|
||||
configure-stamp:
|
||||
dh_testdir
|
||||
cmake .. \
|
||||
-DCMAKE_INSTALL_PREFIX=$(CURDIR)/debian/strawberry/usr
|
||||
-DCMAKE_INSTALL_PREFIX=$(CURDIR)/debian/strawberry/usr \
|
||||
-DENABLE_TRANSLATIONS=ON
|
||||
touch configure-stamp
|
||||
|
||||
build: build-stamp
|
||||
|
||||
17
dist/fedora/strawberry.spec.in
vendored
@@ -1,7 +1,7 @@
|
||||
Name: strawberry
|
||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
||||
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||
Summary: A audio player and music collection organiser
|
||||
Summary: A music player and collection organiser
|
||||
Group: Applications/Multimedia
|
||||
License: GPLv3+
|
||||
URL: http://www.strawbs.org/
|
||||
@@ -15,6 +15,8 @@ BuildRequires: gcc-c++
|
||||
BuildRequires: hicolor-icon-theme
|
||||
BuildRequires: make
|
||||
BuildRequires: git
|
||||
BuildRequires: gettext
|
||||
BuildRequires: cmake(Qt5LinguistTools)
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: pkgconfig(glib-2.0)
|
||||
BuildRequires: pkgconfig(gio-2.0)
|
||||
@@ -47,7 +49,7 @@ BuildRequires: pkgconfig(libnotify)
|
||||
BuildRequires: pkgconfig(libudf)
|
||||
|
||||
%description
|
||||
Strawberry is a audio player and music collection organizer.
|
||||
Strawberry is a music player and collection organizer.
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
|
||||
Features:
|
||||
@@ -60,7 +62,7 @@ Features:
|
||||
* Edit tags on music files
|
||||
* Fetch tags from MusicBrainz
|
||||
* Album cover art from Last.fm, Musicbrainz and Discogs
|
||||
* Song lyrics from AudD and API Seeds
|
||||
* Song lyrics from AudD
|
||||
* Support for multiple backends
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
@@ -83,6 +85,7 @@ pushd %{_target_platform}
|
||||
%{cmake} \
|
||||
-DBUILD_WERROR:BOOL=OFF \
|
||||
-DCMAKE_BUILD_TYPE:STRING=Release \
|
||||
-DENABLE_TRANSLATIONS=ON \
|
||||
..
|
||||
popd
|
||||
|
||||
@@ -93,8 +96,8 @@ make install DESTDIR=%{buildroot} -C %{_target_platform}
|
||||
mv %{buildroot}%{_datadir}/metainfo %{buildroot}%{_datadir}/appdata
|
||||
|
||||
%check
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/strawberry.desktop
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/strawberry.appdata.xml
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawbs.strawberry.desktop
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/org.strawbs.strawberry.appdata.xml
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
@@ -102,12 +105,12 @@ appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/strawberry
|
||||
%license COPYING
|
||||
%{_bindir}/strawberry
|
||||
%{_bindir}/strawberry-tagreader
|
||||
%{_datadir}/applications/strawberry.desktop
|
||||
%{_datadir}/applications/*.desktop
|
||||
%{_datadir}/icons/hicolor/48x48/apps/strawberry.png
|
||||
%{_datadir}/icons/hicolor/64x64/apps/strawberry.png
|
||||
%{_datadir}/icons/hicolor/128x128/apps/strawberry.png
|
||||
%{_datadir}/icons/hicolor/scalable/apps/strawberry.svg
|
||||
%{_datadir}/appdata/strawberry.appdata.xml
|
||||
%{_datadir}/appdata/*.appdata.xml
|
||||
%{_mandir}/man1/strawberry.1.*
|
||||
%{_mandir}/man1/strawberry-tagreader.1.*
|
||||
|
||||
|
||||
8
dist/macos/create-dmg.sh.in
vendored
@@ -3,14 +3,18 @@
|
||||
version="@STRAWBERRY_VERSION_PACKAGE@"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <bundle.app>"
|
||||
echo "Usage: $0 <bundle.app> (append)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
name=$(basename "$1" | perl -pe 's/(.*).app/\1/')
|
||||
bundle_dir="$1"
|
||||
temp_dir="dmg/$name"
|
||||
output_file="$name-$version.dmg"
|
||||
if [ -z "$2" ]; then
|
||||
output_file="$name-$version.dmg"
|
||||
else
|
||||
output_file="$name-$2-$version.dmg"
|
||||
fi
|
||||
|
||||
rm -rf "$temp_dir"
|
||||
rm -f "$output_file"
|
||||
|
||||
6
dist/man/strawberry.1
vendored
@@ -1,11 +1,11 @@
|
||||
.TH STRAWBERRY "1" "User Commands"
|
||||
.SH NAME
|
||||
Strawberry \- audio player and music collection organizer
|
||||
Strawberry \- music player and music collection organizer
|
||||
.SH SYNOPSIS
|
||||
.B strawberry
|
||||
[\fI\,options\/\fR] [\fI\,URL(s)\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Strawberry is a audio player especially aimed at audiophiles.
|
||||
Strawberry is a music player especially aimed at audiophiles.
|
||||
.TP
|
||||
Features:
|
||||
.br
|
||||
@@ -27,7 +27,7 @@ Features:
|
||||
.br
|
||||
- Album cover art from Lastfm, Musicbrainz and Discogs
|
||||
.br
|
||||
- Song lyrics from AudD and API Seeds
|
||||
- Song lyrics from AudD
|
||||
.br
|
||||
- Support for multiple backends
|
||||
.br
|
||||
|
||||
25
dist/opensuse/strawberry.spec.in
vendored
@@ -1,7 +1,7 @@
|
||||
Name: strawberry
|
||||
Version: @STRAWBERRY_VERSION_RPM_V@
|
||||
Release: @STRAWBERRY_VERSION_RPM_R@.@RPM_DISTRO@
|
||||
Summary: A audio player and music collection organiser
|
||||
Summary: A music player and collection organiser
|
||||
Group: Applications/Multimedia
|
||||
License: GPL-3.0+
|
||||
URL: http://www.strawbs.org/
|
||||
@@ -21,7 +21,9 @@ BuildRequires: hicolor-icon-theme
|
||||
BuildRequires: make
|
||||
BuildRequires: git
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: gettext-tools
|
||||
BuildRequires: update-desktop-files
|
||||
BuildRequires: cmake(Qt5LinguistTools)
|
||||
BuildRequires: pkgconfig(glib-2.0)
|
||||
BuildRequires: pkgconfig(gio-2.0)
|
||||
BuildRequires: pkgconfig(gio-unix-2.0)
|
||||
@@ -57,7 +59,7 @@ BuildRequires: pkgconfig(libvlc)
|
||||
Requires: libQt5Sql5-sqlite
|
||||
|
||||
%description
|
||||
Strawberry is a audio player and music collection organizer.
|
||||
Strawberry is a music player and collection organizer.
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
|
||||
Features:
|
||||
@@ -70,7 +72,7 @@ Features:
|
||||
* Edit tags on music files
|
||||
* Fetch tags from MusicBrainz
|
||||
* Album cover art from Last.fm, Musicbrainz and Discogs
|
||||
* Song lyrics from AudD and API Seeds
|
||||
* Song lyrics from AudD
|
||||
* Support for multiple backends
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
@@ -82,12 +84,15 @@ Features:
|
||||
%setup -q -n %{name}-@STRAWBERRY_VERSION_PACKAGE@
|
||||
|
||||
%build
|
||||
%{cmake} .. -DUSE_INSTALL_PREFIX=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
%{cmake} .. -DUSE_INSTALL_PREFIX=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DENABLE_TRANSLATIONS=ON
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
cd build
|
||||
make install DESTDIR=$RPM_BUILD_ROOT
|
||||
|
||||
%suse_update_desktop_file org.strawbs.strawberry Qt AudioVideo Audio Player
|
||||
|
||||
%if 0%{?suse_version} < 1500
|
||||
mv %{buildroot}%{_datadir}/metainfo %{buildroot}%{_datadir}/appdata
|
||||
%endif
|
||||
@@ -97,11 +102,11 @@ cd build
|
||||
make clean
|
||||
|
||||
%check
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/strawberry.desktop
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/org.strawbs.strawberry.desktop
|
||||
%if 0%{?suse_version} >= 1500
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/strawberry.appdata.xml
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.strawbs.strawberry.appdata.xml
|
||||
%else
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/strawberry.appdata.xml
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/org.strawbs.strawberry.appdata.xml
|
||||
%endif
|
||||
|
||||
%files
|
||||
@@ -110,15 +115,15 @@ appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/strawberry
|
||||
%license COPYING
|
||||
%{_bindir}/strawberry
|
||||
%{_bindir}/strawberry-tagreader
|
||||
%{_datadir}/applications/strawberry.desktop
|
||||
%{_datadir}/applications/*.desktop
|
||||
%{_datadir}/icons/hicolor/48x48/apps/strawberry.png
|
||||
%{_datadir}/icons/hicolor/64x64/apps/strawberry.png
|
||||
%{_datadir}/icons/hicolor/128x128/apps/strawberry.png
|
||||
%{_datadir}/icons/hicolor/scalable/apps/strawberry.svg
|
||||
%if 0%{?suse_version} >= 1500
|
||||
%{_datadir}/metainfo/strawberry.appdata.xml
|
||||
%{_datadir}/metainfo/*.appdata.xml
|
||||
%else
|
||||
%{_datadir}/appdata/strawberry.appdata.xml
|
||||
%{_datadir}/appdata/*.appdata.xml
|
||||
%endif
|
||||
%{_mandir}/man1/%{name}.1%{?ext_man}
|
||||
%{_mandir}/man1/%{name}-tagreader.1%{?ext_man}
|
||||
|
||||
24
dist/pacman/PKGBUILD.in
vendored
@@ -6,7 +6,7 @@ pkgdesc="A music player aimed at audio enthusiasts and music collectors"
|
||||
arch=(x86_64)
|
||||
url="http://www.strawbs.org/"
|
||||
license=(GPL3)
|
||||
makedepends=(git cmake make gcc boost)
|
||||
makedepends=(git cmake make gcc boost gettext qt5-tools)
|
||||
depends=(
|
||||
desktop-file-utils
|
||||
hicolor-icon-theme
|
||||
@@ -40,30 +40,24 @@ optdepends=(
|
||||
)
|
||||
provides=(strawberry)
|
||||
conflicts=(strawberry)
|
||||
source=("git+https://github.com/jonaski/strawberry.git")
|
||||
source=("strawberry-@STRAWBERRY_VERSION_PACKAGE@.tar.xz")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "strawberry"
|
||||
git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
|
||||
}
|
||||
|
||||
prepare() {
|
||||
cd "${srcdir}/strawberry"
|
||||
install -d strawberry-build
|
||||
mkdir -p build
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/strawberry/strawberry-build"
|
||||
cmake .. \
|
||||
cd build
|
||||
cmake ../${pkgname}-@STRAWBERRY_VERSION_PACKAGE@ \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DUSE_SYSTEM_TAGLIB=ON \
|
||||
-DENABLE_STREAM_DEEZER=ON \
|
||||
-DENABLE_PHONON=ON
|
||||
make
|
||||
-DENABLE_PHONON=ON \
|
||||
-DENABLE_TRANSLATIONS=ON
|
||||
make -j$(nproc)
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/strawberry/strawberry-build"
|
||||
cd build
|
||||
make DESTDIR="${pkgdir}" install
|
||||
}
|
||||
|
||||
4
dist/scripts/maketarball.sh.in
vendored
@@ -26,6 +26,10 @@ tar -cJf $name-$version.tar.xz \
|
||||
--exclude="*.nsi" \
|
||||
--exclude="$root/CMakeLists.txt.user" \
|
||||
--exclude="$root/build" \
|
||||
--exclude="$root/scripts/maketarball.sh" \
|
||||
--exclude="$root/dist/debian/changelog" \
|
||||
--exclude="$root/dist/pacman/PKGBUILD" \
|
||||
--exclude="$root/dist/macos/create-dmg.sh" \
|
||||
--exclude="$root/dist/macos/Info.plist" \
|
||||
--exclude="$root/dist/windows/windres.rc" \
|
||||
"$root"
|
||||
|
||||
260
dist/scripts/upload.sh
vendored
@@ -1,260 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set +x # Do not leak information
|
||||
|
||||
# Exit immediately if one of the files given as arguments is not there
|
||||
# because we don't want to delete the existing release if we don't have
|
||||
# the new files that should be uploaded
|
||||
for file in "$@"
|
||||
do
|
||||
if [ ! -e "$file" ]
|
||||
then echo "$file is missing, giving up." >&2; exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "No artifacts to use for release, giving up."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1 ; then
|
||||
shatool="sha256sum"
|
||||
elif command -v shasum >/dev/null 2>&1 ; then
|
||||
shatool="shasum -a 256" # macOS fallback
|
||||
else
|
||||
echo "Neither sha256sum nor shasum is available, cannot check hashes"
|
||||
fi
|
||||
|
||||
# The calling script (usually .travis.yml) can set a suffix to be used for
|
||||
# the tag and release name. This way it is possible to have a release for
|
||||
# the output of the CI/CD pipeline (marked as 'continuous') and also test
|
||||
# builds for other branches.
|
||||
# If this build was triggered by a tag, call the result a Release
|
||||
if [ ! -z "$UPLOADTOOL_SUFFIX" ] ; then
|
||||
if [ "$UPLOADTOOL_SUFFIX" = "$TRAVIS_TAG" ] ; then
|
||||
RELEASE_NAME="$TRAVIS_TAG"
|
||||
RELEASE_TITLE="Release build ($TRAVIS_TAG)"
|
||||
is_prerelease="false"
|
||||
else
|
||||
RELEASE_NAME="continuous-$UPLOADTOOL_SUFFIX"
|
||||
RELEASE_TITLE="Continuous build ($UPLOADTOOL_SUFFIX)"
|
||||
is_prerelease="true"
|
||||
fi
|
||||
else
|
||||
if [ "$TRAVIS_TAG" != "" ]; then
|
||||
RELEASE_NAME="$TRAVIS_TAG"
|
||||
RELEASE_TITLE="Release build ($TRAVIS_TAG)"
|
||||
is_prerelease="false"
|
||||
else
|
||||
RELEASE_NAME="continuous" # Do not use "latest" as it is reserved by GitHub
|
||||
RELEASE_TITLE="Continuous build"
|
||||
is_prerelease="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$ARTIFACTORY_BASE_URL" != "" ]; then
|
||||
echo "ARTIFACTORY_BASE_URL set, trying to upload to artifactory"
|
||||
|
||||
if [ "$ARTIFACTORY_API_KEY" == "" ]; then
|
||||
echo "Please set ARTIFACTORY_API_KEY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
files="$@"
|
||||
|
||||
# artifactory doesn't support any kind of "artifact description", so we're uploading a text file containing the
|
||||
# relevant details along with the other artifacts
|
||||
tempdir=$(mktemp -d)
|
||||
info_file="$tempdir"/build-info.txt
|
||||
echo "Travis CI build log: https://travis-ci.com/$TRAVIS_REPO_SLUG/builds/$TRAVIS_BUILD_ID/" > "$info_file"
|
||||
files+=("$info_file")
|
||||
|
||||
set +x
|
||||
|
||||
for file in ${files[@]}; do
|
||||
url="${ARTIFACTORY_BASE_URL}/travis-${TRAVIS_BUILD_NUMBER}/"$(basename "$file")
|
||||
md5sum=$(md5sum "$file" | cut -d' ' -f1)
|
||||
sha1sum=$(sha1sum "$file" | cut -d' ' -f1)
|
||||
sha256sum=$(sha256sum "$file" | cut -d' ' -f1)
|
||||
echo "Uploading $file to $url"
|
||||
hashsums=(-H "X-Checksum-Md5:$md5sum")
|
||||
hashsums+=(-H "X-Checksum-Sha1:$sha1sum")
|
||||
hashsums+=(-H "X-Checksum-Sha256:$sha256sum")
|
||||
if ! curl -H 'X-JFrog-Art-Api:'"$ARTIFACTORY_API_KEY" "${hashsums[@]}" -T "$file" "$url"; then
|
||||
echo "Failed to upload file, exiting"
|
||||
rm -r "$tempdir"
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
echo "MD5 checksum: $md5sum"
|
||||
echo "SHA1 checksum: $sha1sum"
|
||||
echo "SHA256 checksum: $sha256sum"
|
||||
done
|
||||
rm -r "$tempdir"
|
||||
fi
|
||||
|
||||
# Do not upload non-master branch builds
|
||||
# if [ "$TRAVIS_TAG" != "$TRAVIS_BRANCH" ] && [ "$TRAVIS_BRANCH" != "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi
|
||||
if [ "$TRAVIS_EVENT_TYPE" == "pull_request" ] ; then
|
||||
echo "Release uploading disabled for pull requests"
|
||||
if [ "$ARTIFACTORY_BASE_URL" != "" ]; then
|
||||
echo "Releases have already been uploaded to Artifactory, exiting"
|
||||
exit 0
|
||||
else
|
||||
echo "Release uploading disabled for pull requests, uploading to transfer.sh instead"
|
||||
rm -f ./uploaded-to
|
||||
for FILE in "$@" ; do
|
||||
BASENAME="$(basename "${FILE}")"
|
||||
curl --upload-file $FILE "https://transfer.sh/$BASENAME" > ./one-upload
|
||||
echo "$(cat ./one-upload)" # this way we get a newline
|
||||
echo -n "$(cat ./one-upload)\\n" >> ./uploaded-to # this way we get a \n but no newline
|
||||
done
|
||||
fi
|
||||
# review_url="https://api.github.com/repos/${TRAVIS_REPO_SLUG}/pulls/${TRAVIS_PULL_REQUEST}/reviews"
|
||||
# if [ -z $UPLOADTOOL_PR_BODY ] ; then
|
||||
# body="Travis CI has created build artifacts for this PR here:"
|
||||
# else
|
||||
# body="$UPLOADTOOL_PR_BODY"
|
||||
# fi
|
||||
# body="$body\n$(cat ./uploaded-to)\nThe link(s) will expire 14 days from now."
|
||||
# review_comment=$(curl -X POST \
|
||||
# --header "Authorization: token ${GITHUB_TOKEN}" \
|
||||
# --data '{"commit_id": "'"$TRAVIS_COMMIT"'","body": "'"$body"'","event": "COMMENT"}' \
|
||||
# $review_url)
|
||||
# if echo $review_comment | grep -q "Bad credentials" 2>/dev/null ; then
|
||||
# echo '"Bad credentials" response for --data {"commit_id": "'"$TRAVIS_COMMIT"'","body": "'"$body"'","event": "COMMENT"}'
|
||||
# fi
|
||||
$shatool "$@"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -z "$TRAVIS_REPO_SLUG" ] ; then
|
||||
# We are running on Travis CI
|
||||
echo "Running on Travis CI"
|
||||
echo "TRAVIS_COMMIT: $TRAVIS_COMMIT"
|
||||
REPO_SLUG="$TRAVIS_REPO_SLUG"
|
||||
if [ -z "$GITHUB_TOKEN" ] ; then
|
||||
echo "\$GITHUB_TOKEN missing, please set it in the Travis CI settings of this project"
|
||||
echo "You can get one from https://github.com/settings/tokens"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# We are not running on Travis CI
|
||||
echo "Not running on Travis CI"
|
||||
if [ -z "$REPO_SLUG" ] ; then
|
||||
read -r -p "Repo Slug (GitHub and Travis CI username/reponame): " REPO_SLUG
|
||||
fi
|
||||
if [ -z "$GITHUB_TOKEN" ] ; then
|
||||
read -r -s -p "Token (https://github.com/settings/tokens): " GITHUB_TOKEN
|
||||
fi
|
||||
fi
|
||||
|
||||
tag_url="https://api.github.com/repos/$REPO_SLUG/git/refs/tags/$RELEASE_NAME"
|
||||
tag_infos=$(curl -XGET --header "Authorization: token ${GITHUB_TOKEN}" "${tag_url}")
|
||||
echo "tag_infos: $tag_infos"
|
||||
tag_sha=$(echo "$tag_infos" | grep '"sha":' | head -n 1 | cut -d '"' -f 4 | cut -d '{' -f 1)
|
||||
echo "tag_sha: $tag_sha"
|
||||
|
||||
release_url="https://api.github.com/repos/$REPO_SLUG/releases/tags/$RELEASE_NAME"
|
||||
echo "Getting the release ID..."
|
||||
echo "release_url: $release_url"
|
||||
release_infos=$(curl -XGET --header "Authorization: token ${GITHUB_TOKEN}" "${release_url}")
|
||||
echo "release_infos: $release_infos"
|
||||
release_id=$(echo "$release_infos" | grep "\"id\":" | head -n 1 | tr -s " " | cut -f 3 -d" " | cut -f 1 -d ",")
|
||||
echo "release ID: $release_id"
|
||||
upload_url=$(echo "$release_infos" | grep '"upload_url":' | head -n 1 | cut -d '"' -f 4 | cut -d '{' -f 1)
|
||||
echo "upload_url: $upload_url"
|
||||
release_url=$(echo "$release_infos" | grep '"url":' | head -n 1 | cut -d '"' -f 4 | cut -d '{' -f 1)
|
||||
echo "release_url: $release_url"
|
||||
target_commit_sha=$(echo "$release_infos" | grep '"target_commitish":' | head -n 1 | cut -d '"' -f 4 | cut -d '{' -f 1)
|
||||
echo "target_commit_sha: $target_commit_sha"
|
||||
|
||||
if [ "$TRAVIS_COMMIT" != "$target_commit_sha" ] ; then
|
||||
|
||||
echo "TRAVIS_COMMIT != target_commit_sha, hence deleting $RELEASE_NAME..."
|
||||
|
||||
if [ ! -z "$release_id" ]; then
|
||||
delete_url="https://api.github.com/repos/$REPO_SLUG/releases/$release_id"
|
||||
echo "Delete the release..."
|
||||
echo "delete_url: $delete_url"
|
||||
curl -XDELETE \
|
||||
--header "Authorization: token ${GITHUB_TOKEN}" \
|
||||
"${delete_url}"
|
||||
fi
|
||||
|
||||
# echo "Checking if release with the same name is still there..."
|
||||
# echo "release_url: $release_url"
|
||||
# curl -XGET --header "Authorization: token ${GITHUB_TOKEN}" \
|
||||
# "$release_url"
|
||||
|
||||
if [ "$is_prerelease" = "true" ] ; then
|
||||
# if this is a continuous build tag, then delete the old tag
|
||||
# in preparation for the new release
|
||||
echo "Delete the tag..."
|
||||
delete_url="https://api.github.com/repos/$REPO_SLUG/git/refs/tags/$RELEASE_NAME"
|
||||
echo "delete_url: $delete_url"
|
||||
curl -XDELETE \
|
||||
--header "Authorization: token ${GITHUB_TOKEN}" \
|
||||
"${delete_url}"
|
||||
fi
|
||||
|
||||
echo "Create release..."
|
||||
|
||||
if [ -z "$TRAVIS_BRANCH" ] ; then
|
||||
TRAVIS_BRANCH="master"
|
||||
fi
|
||||
|
||||
if [ ! -z "$TRAVIS_JOB_ID" ] ; then
|
||||
if [ -z "${UPLOADTOOL_BODY+x}" ] ; then
|
||||
# TODO: The host could be travis-ci.org (legacy open source) or travis-ci.com (subscription or latest open source).
|
||||
BODY="Travis CI build log: https://travis-ci.org/$REPO_SLUG/builds/$TRAVIS_BUILD_ID/"
|
||||
else
|
||||
BODY="$UPLOADTOOL_BODY"
|
||||
fi
|
||||
else
|
||||
BODY="$UPLOADTOOL_BODY"
|
||||
fi
|
||||
|
||||
release_infos=$(curl -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||
--data '{"tag_name": "'"$RELEASE_NAME"'","target_commitish": "'"$TRAVIS_COMMIT"'","name": "'"$RELEASE_TITLE"'","body": "'"$BODY"'","draft": false,"prerelease": '$is_prerelease'}' "https://api.github.com/repos/$REPO_SLUG/releases")
|
||||
|
||||
echo "$release_infos"
|
||||
|
||||
unset upload_url
|
||||
upload_url=$(echo "$release_infos" | grep '"upload_url":' | head -n 1 | cut -d '"' -f 4 | cut -d '{' -f 1)
|
||||
echo "upload_url: $upload_url"
|
||||
|
||||
unset release_url
|
||||
release_url=$(echo "$release_infos" | grep '"url":' | head -n 1 | cut -d '"' -f 4 | cut -d '{' -f 1)
|
||||
echo "release_url: $release_url"
|
||||
|
||||
fi # if [ "$TRAVIS_COMMIT" != "$tag_sha" ]
|
||||
|
||||
if [ -z "$release_url" ] ; then
|
||||
echo "Cannot figure out the release URL for $RELEASE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Upload binaries to the release..."
|
||||
|
||||
for FILE in "$@" ; do
|
||||
FULLNAME="${FILE}"
|
||||
BASENAME="$(basename "${FILE}")"
|
||||
curl -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.manifold-preview" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @$FULLNAME \
|
||||
"$upload_url?name=$BASENAME"
|
||||
echo ""
|
||||
done
|
||||
|
||||
$shatool "$@"
|
||||
|
||||
if [ "$TRAVIS_COMMIT" != "$tag_sha" ] ; then
|
||||
echo "Publish the release..."
|
||||
|
||||
release_infos=$(curl -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||
--data '{"draft": false}' "$release_url")
|
||||
|
||||
echo "$release_infos"
|
||||
fi # if [ "$TRAVIS_COMMIT" != "$tag_sha" ]
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component>
|
||||
<id>org.strawberry.strawberry</id>
|
||||
<id>org.strawbs.strawberry</id>
|
||||
<launchable type="desktop-id">strawberry.desktop</launchable>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
@@ -9,13 +9,13 @@
|
||||
<binary>strawberry-tagreader</binary>
|
||||
</provides>
|
||||
<name>Strawberry Music Player</name>
|
||||
<summary>An audio player and music collection organizer</summary>
|
||||
<summary>A music player and collection organizer</summary>
|
||||
<url type="homepage">https://www.strawbs.org/</url>
|
||||
<url type="bugtracker">https://github.com/jonaski/strawberry/</url>
|
||||
<translation type="qt">strawberry</translation>
|
||||
<description>
|
||||
<p>
|
||||
Strawberry is a audio player and music collection organizer.
|
||||
Strawberry is a music player and music collection organizer.
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
</p>
|
||||
<p>Features:</p>
|
||||
@@ -29,12 +29,12 @@
|
||||
<li>Edit tags on music files</li>
|
||||
<li>Fetch tags from MusicBrainz</li>
|
||||
<li>Album cover art from Last.fm, Musicbrainz and Discogs</li>
|
||||
<li>Song lyrics from AudD and API Seeds</li>
|
||||
<li>Song lyrics from AudD</li>
|
||||
<li>Support for multiple backends</li>
|
||||
<li>Audio analyzer</li>
|
||||
<li>Audio equalizer</li>
|
||||
<li>Transfer music to iPod, iPhone, MTP or mass-storage USB player</li>
|
||||
<li>Streaming support for Tidal and Deezer</li>
|
||||
<li>Streaming support for Tidal</li>
|
||||
<li>Scrobbler with support for Last.fm, Libre.fm and ListenBrainz</li>
|
||||
</ul>
|
||||
</description>
|
||||
13
dist/unix/org.strawbs.strawberry.desktop
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Strawberry
|
||||
GenericName=Strawberry Music Player
|
||||
Comment=Plays music
|
||||
Exec=strawberry %U
|
||||
TryExec=strawberry
|
||||
Icon=strawberry
|
||||
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;
|
||||
13
dist/unix/strawberry.desktop
vendored
@@ -1,13 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Strawberry
|
||||
GenericName=Strawberry Music Player
|
||||
Comment=Plays music
|
||||
Exec=strawberry %U
|
||||
TryExec=strawberry
|
||||
Icon=strawberry
|
||||
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/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/ogg;audio/vorbis;audio/vnd.rn-realaudio;audio/x-wav;audio/x-flac;audio/x-oggflac;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-pn-realaudio;audio/x-scpls;audio/x-speex;video/x-ms-asf;
|
||||
6
dist/windows/strawberry-debug-x64.nsi.in
vendored
@@ -137,7 +137,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-15.dll"
|
||||
File "libprotobuf-18.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
File "libssl-1_1-x64.dll"
|
||||
@@ -162,7 +162,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x64.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@@ -378,7 +377,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-15.dll"
|
||||
Delete "$INSTDIR\libprotobuf-18.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
Delete "$INSTDIR\libssl-1_1-x64.dll"
|
||||
@@ -403,7 +402,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x64.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
||||
6
dist/windows/strawberry-debug-x86.nsi.in
vendored
@@ -137,7 +137,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-15.dll"
|
||||
File "libprotobuf-18.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
File "libssl-1_1.dll"
|
||||
@@ -162,7 +162,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x86.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@@ -378,7 +377,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-15.dll"
|
||||
Delete "$INSTDIR\libprotobuf-18.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
Delete "$INSTDIR\libssl-1_1.dll"
|
||||
@@ -403,7 +402,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x86.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
||||
6
dist/windows/strawberry-x64.nsi.in
vendored
@@ -137,7 +137,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-15.dll"
|
||||
File "libprotobuf-18.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
File "libssl-1_1-x64.dll"
|
||||
@@ -161,7 +161,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x64.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@@ -346,7 +345,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-15.dll"
|
||||
Delete "$INSTDIR\libprotobuf-18.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
Delete "$INSTDIR\libssl-1_1-x64.dll"
|
||||
@@ -370,7 +369,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x64.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
||||
6
dist/windows/strawberry-x86.nsi.in
vendored
@@ -137,7 +137,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-15.dll"
|
||||
File "libprotobuf-18.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
File "libssl-1_1.dll"
|
||||
@@ -161,7 +161,6 @@ Section "Strawberry" Strawberry
|
||||
File "libxml2-2.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "liblzma-5.dll"
|
||||
File "libdeezer.x86.dll"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
@@ -346,7 +345,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-15.dll"
|
||||
Delete "$INSTDIR\libprotobuf-18.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
Delete "$INSTDIR\libssl-1_1.dll"
|
||||
@@ -370,7 +369,6 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\liblzma-5.dll"
|
||||
Delete "$INSTDIR\libdeezer.x86.dll"
|
||||
|
||||
Delete "$INSTDIR\platforms\qwindows.dll"
|
||||
Delete "$INSTDIR\sqldrivers\qsqlite.dll"
|
||||
|
||||
@@ -171,26 +171,14 @@ class CallbackClosure : public ClosureBase {
|
||||
} // namespace _detail
|
||||
|
||||
template <typename... Args>
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
QObject *receiver,
|
||||
const char *slot,
|
||||
const Args&... args) {
|
||||
return new _detail::Closure<Args...>(
|
||||
sender, signal, receiver, slot, args...);
|
||||
_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, QObject *receiver, const char *slot, const Args&... args) {
|
||||
return new _detail::Closure<Args...>(sender, signal, receiver, slot, args...);
|
||||
}
|
||||
|
||||
// QSharedPointer variant
|
||||
template <typename T, typename... Args>
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QSharedPointer<T> sender,
|
||||
const char *signal,
|
||||
QObject *receiver,
|
||||
const char *slot,
|
||||
const Args&... args) {
|
||||
return new _detail::SharedClosure<T, Args...>(
|
||||
sender, signal, receiver, slot, args...);
|
||||
_detail::ClosureBase *NewClosure(QSharedPointer<T> sender, const char *signal, QObject *receiver, const char *slot, const Args&... args) {
|
||||
return new _detail::SharedClosure<T, Args...>(sender, signal, receiver, slot, args...);
|
||||
}
|
||||
|
||||
_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::function<void()> callback);
|
||||
@@ -201,20 +189,12 @@ _detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::funct
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
void (*callback)(Args...),
|
||||
const Args&... args) {
|
||||
_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, void (*callback)(Args...), const Args&... args) {
|
||||
return NewClosure(sender, signal, std::bind(callback, args...));
|
||||
}
|
||||
|
||||
template <typename T, typename Unused, typename... Args>
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
T *receiver, Unused (T::*callback)(Args...),
|
||||
const Args&... args) {
|
||||
_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, T *receiver, Unused (T::*callback)(Args...), const Args&... args) {
|
||||
return NewClosure(sender, signal, std::bind(callback, receiver, args...));
|
||||
}
|
||||
|
||||
@@ -249,4 +229,3 @@ void DoAfter(std::function<void()> callback, std::chrono::duration<R, P> duratio
|
||||
}
|
||||
|
||||
#endif // CLOSURE_H
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/* This file is part of Clementine.
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2016, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LAZY_H
|
||||
|
||||
@@ -43,8 +43,7 @@ class _MessageHandlerBase : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// device can be NULL, in which case you must call SetDevice before writing
|
||||
// any messages.
|
||||
// device can be nullptr, in which case you must call SetDevice before writing any messages.
|
||||
_MessageHandlerBase(QIODevice *device, QObject *parent);
|
||||
|
||||
void SetDevice(QIODevice *device);
|
||||
@@ -176,4 +175,3 @@ void AbstractMessageHandler<MT>::AbortAll() {
|
||||
}
|
||||
|
||||
#endif // MESSAGEHANDLER_H
|
||||
|
||||
|
||||
@@ -43,18 +43,17 @@ class _MessageReplyBase : public QObject {
|
||||
|
||||
void Abort();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void Finished(bool success);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
bool finished_;
|
||||
bool success_;
|
||||
|
||||
QSemaphore semaphore_;
|
||||
};
|
||||
|
||||
// A reply future class that is returned immediately for requests that will
|
||||
// occur in the background. Similar to QNetworkReply.
|
||||
// A reply future class that is returned immediately for requests that will occur in the background. Similar to QNetworkReply.
|
||||
template <typename MessageType>
|
||||
class MessageReply : public _MessageReplyBase {
|
||||
public:
|
||||
|
||||
@@ -101,7 +101,7 @@ protected:
|
||||
|
||||
private:
|
||||
struct Worker {
|
||||
Worker() : local_server_(NULL), local_socket_(NULL), process_(NULL), handler_(NULL) {}
|
||||
Worker() : local_server_(nullptr), local_socket_(nullptr), process_(nullptr), handler_(nullptr) {}
|
||||
|
||||
QLocalServer *local_server_;
|
||||
QLocalSocket *local_socket_;
|
||||
@@ -120,14 +120,14 @@ private:
|
||||
return &(*it);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DeleteQObjectPointerLater(T **p) {
|
||||
if (*p) {
|
||||
(*p)->deleteLater();
|
||||
*p = NULL;
|
||||
*p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ private:
|
||||
// and sets the request's ID to the ID of the reply. Can be called from any thread
|
||||
ReplyType *NewReply(MessageType *message);
|
||||
|
||||
// Returns the next handler, or NULL if there isn't one. Must be called from my thread.
|
||||
// Returns the next handler, or nullptr if there isn't one. Must be called from my thread.
|
||||
HandlerType *NextHandler() const;
|
||||
|
||||
private:
|
||||
@@ -300,7 +300,7 @@ void WorkerPool<HandlerType>::NewConnection() {
|
||||
// We only ever accept one connection per worker, so destroy the server now.
|
||||
worker->local_socket_->setParent(this);
|
||||
worker->local_server_->deleteLater();
|
||||
worker->local_server_ = NULL;
|
||||
worker->local_server_ = nullptr;
|
||||
|
||||
// Create the handler.
|
||||
worker->handler_ = new HandlerType(worker->local_socket_, this);
|
||||
@@ -329,7 +329,7 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
||||
|
||||
default:
|
||||
// On any other error we just restart the process.
|
||||
qLog(Debug) << "Worker" << worker << "failed with error" << error << "- restarting";
|
||||
qLog(Debug) << "Worker" << worker << "failed with error" << error << "- restarting";
|
||||
StartOneWorker(worker);
|
||||
break;
|
||||
}
|
||||
@@ -386,14 +386,13 @@ HandlerType *WorkerPool<HandlerType>::NextHandler() const {
|
||||
for (int i = 0; i < workers_.count(); ++i) {
|
||||
const int worker_index = (next_worker_ + i) % workers_.count();
|
||||
|
||||
if (workers_[worker_index].handler_ &&
|
||||
!workers_[worker_index].handler_->is_device_closed()) {
|
||||
if (workers_[worker_index].handler_ && !workers_[worker_index].handler_->is_device_closed()) {
|
||||
next_worker_ = (worker_index + 1) % workers_.count();
|
||||
return workers_[worker_index].handler_;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // WORKERPOOL_H
|
||||
|
||||
@@ -92,11 +92,6 @@
|
||||
#include "fmpsparser.h"
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
// Taglib added support for FLAC pictures in 1.7.0
|
||||
#if (TAGLIB_MAJOR_VERSION > 1) || (TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7)
|
||||
# define TAGLIB_HAS_FLAC_PICTURELIST
|
||||
#endif
|
||||
|
||||
class FileRefFactory {
|
||||
public:
|
||||
virtual ~FileRefFactory() {}
|
||||
@@ -106,11 +101,11 @@ class FileRefFactory {
|
||||
class TagLibFileRefFactory : public FileRefFactory {
|
||||
public:
|
||||
virtual TagLib::FileRef *GetFileRef(const QString &filename) {
|
||||
#ifdef Q_OS_WIN32
|
||||
#ifdef Q_OS_WIN32
|
||||
return new TagLib::FileRef(filename.toStdWString().c_str());
|
||||
#else
|
||||
#else
|
||||
return new TagLib::FileRef(QFile::encodeName(filename).constData());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@@ -127,18 +122,42 @@ TagLib::String QStringToTaglibString(const QString &s) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Tags containing the year the album was originally released (in contrast to
|
||||
// other tags that contain the release year of the current edition)
|
||||
// Tags containing the year the album was originally released (in contrast to other tags that contain the release year of the current edition)
|
||||
const char *kMP4_OriginalYear_ID = "----:com.apple.iTunes:ORIGINAL YEAR";
|
||||
const char *kASF_OriginalDate_ID = "WM/OriginalReleaseTime";
|
||||
const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear";
|
||||
}
|
||||
|
||||
|
||||
TagReader::TagReader()
|
||||
: factory_(new TagLibFileRefFactory),
|
||||
network_(new QNetworkAccessManager),
|
||||
kEmbeddedCover("(embedded)") {}
|
||||
TagReader::TagReader() :
|
||||
factory_(new TagLibFileRefFactory),
|
||||
network_(new QNetworkAccessManager),
|
||||
kEmbeddedCover("(embedded)") {}
|
||||
|
||||
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||
|
||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
||||
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_FLAC;
|
||||
if (dynamic_cast<TagLib::WavPack::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAVPACK;
|
||||
if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGFLAC;
|
||||
if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGVORBIS;
|
||||
if (dynamic_cast<TagLib::Ogg::Opus::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGOPUS;
|
||||
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGSPEEX;
|
||||
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MPEG;
|
||||
if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MP4;
|
||||
if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_ASF;
|
||||
if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_AIFF;
|
||||
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MPC;
|
||||
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_TRUEAUDIO;
|
||||
if (dynamic_cast<TagLib::APE::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_APE;
|
||||
#ifdef HAVE_TAGLIB_DSFFILE
|
||||
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_DSF;
|
||||
if (dynamic_cast<TagLib::DSDIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_DSDIFF;
|
||||
#endif
|
||||
|
||||
return pb::tagreader::SongMetadata_FileType_UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
@@ -186,29 +205,38 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
// apart, so we keep specific behavior for some formats by adding another "else if" block below.
|
||||
if (TagLib::Ogg::XiphComment *tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
ParseOggTag(tag->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||
#if TAGLIB_MAJOR_VERSION >= 1 && TAGLIB_MINOR_VERSION >= 11
|
||||
if (!tag->pictureList().isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
#endif
|
||||
if (!tag->pictureList().isEmpty()) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
}
|
||||
|
||||
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>(fileref->file())) {
|
||||
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
|
||||
if ( file->xiphComment() ) {
|
||||
if (file->xiphComment()) {
|
||||
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||
if (!file->pictureList().isEmpty()) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::WavPack::File *file = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
//if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
if (file->tag()) {
|
||||
ParseAPETag(file->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||
}
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::APE::File *file = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
if (file->tag()) {
|
||||
ParseAPETag(file->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||
}
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
@@ -217,7 +245,6 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
const TagLib::ID3v2::FrameListMap &map = file->ID3v2Tag()->frameListMap();
|
||||
|
||||
if (!map["TPOS"].isEmpty()) disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
||||
//if (!map["TBPM"].isEmpty()) song->set_bpm(TStringToQString(map["TBPM"].front()->toString()).trimmed().toFloat());
|
||||
if (!map["TCOM"].isEmpty()) Decode(map["TCOM"].front()->toString(), nullptr, song->mutable_composer());
|
||||
|
||||
// content group
|
||||
@@ -330,8 +357,11 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::APE::File *file = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
else if (TagLib::MPC::File* file = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
if (file->tag()) {
|
||||
ParseAPETag(file->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||
}
|
||||
if (tag) Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (tag) {
|
||||
@@ -374,7 +404,6 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
|
||||
}
|
||||
|
||||
|
||||
void TagReader::Decode(const TagLib::String &tag, const QTextCodec *codec, std::string *output) {
|
||||
|
||||
QString tmp;
|
||||
@@ -403,6 +432,74 @@ void TagReader::Decode(const QString &tag, const QTextCodec *codec, std::string
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
|
||||
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
|
||||
|
||||
if (!map["ALBUMARTIST"].isEmpty()) Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist());
|
||||
else if (!map["ALBUM ARTIST"].isEmpty()) Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
|
||||
|
||||
if (!map["ORIGINALDATE"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
|
||||
else if (!map["ORIGINALYEAR"].isEmpty()) song->set_originalyear(TStringToQString(map["ORIGINALYEAR"].front()).toInt());
|
||||
|
||||
if (!map["DISCNUMBER"].isEmpty()) *disc = TStringToQString( map["DISCNUMBER"].front() ).trimmed();
|
||||
if (!map["COMPILATION"].isEmpty()) *compilation = TStringToQString( map["COMPILATION"].front() ).trimmed();
|
||||
if (!map["COVERART"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
if (!map["METADATA_BLOCK_PICTURE"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
|
||||
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
||||
|
||||
if (!map["LYRICS"].isEmpty()) Decode(map["LYRICS"].front(), codec, song->mutable_lyrics());
|
||||
else if (!map["UNSYNCEDLYRICS"].isEmpty()) Decode(map["UNSYNCEDLYRICS"].front(), codec, song->mutable_lyrics());
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
||||
if (it != map.end()) {
|
||||
TagLib::StringList album_artists = it->second.toStringList();
|
||||
if (!album_artists.isEmpty()) {
|
||||
Decode(album_artists.front(), nullptr, song->mutable_albumartist());
|
||||
}
|
||||
}
|
||||
|
||||
if (map.find("COVER ART (FRONT)") != map.end()) song->set_art_automatic(kEmbeddedCover);
|
||||
if (map.contains("COMPILATION")) {
|
||||
*compilation = TStringToQString(TagLib::String::number(map["COMPILATION"].toString().toInt()));
|
||||
}
|
||||
|
||||
if (map.contains("DISC")) {
|
||||
*disc = TStringToQString(TagLib::String::number(map["DISC"].toString().toInt()));
|
||||
}
|
||||
|
||||
if (map.contains("PERFORMER")) {
|
||||
Decode(map["PERFORMER"].toStringList().toString(", "), nullptr, song->mutable_performer());
|
||||
}
|
||||
|
||||
if (map.contains("COMPOSER")) {
|
||||
Decode(map["COMPOSER"].toStringList().toString(", "), nullptr, song->mutable_composer());
|
||||
}
|
||||
|
||||
if (map.contains("GROUPING")) {
|
||||
Decode(map["GROUPING"].toStringList().toString(" "), nullptr, song->mutable_grouping());
|
||||
}
|
||||
|
||||
if (map.contains("LYRICS")) {
|
||||
Decode(map["LYRICS"].toString(), nullptr, song->mutable_lyrics());
|
||||
}
|
||||
|
||||
if (map.contains("FMPS_PLAYCOUNT")) {
|
||||
int playcount = TStringToQString(map["FMPS_PLAYCOUNT"].toString()).toFloat();
|
||||
if (song->playcount() <= 0 && playcount > 0) {
|
||||
song->set_playcount(playcount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
qLog(Debug) << "Parsing FMPSFrame" << name << ", " << value;
|
||||
@@ -430,38 +527,6 @@ void TagReader::ParseFMPSFrame(const QString &name, const QString &value, pb::ta
|
||||
|
||||
}
|
||||
|
||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const {
|
||||
|
||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
|
||||
if (!map["CONTENT GROUP"].isEmpty()) Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
|
||||
|
||||
if (!map["ALBUMARTIST"].isEmpty()) {
|
||||
Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist());
|
||||
}
|
||||
else if (!map["ALBUM ARTIST"].isEmpty()) {
|
||||
Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
|
||||
}
|
||||
|
||||
if (!map["ORIGINALDATE"].isEmpty())
|
||||
song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
|
||||
else if (!map["ORIGINALYEAR"].isEmpty())
|
||||
song->set_originalyear(TStringToQString(map["ORIGINALYEAR"].front()).toInt());
|
||||
|
||||
if (!map["DISCNUMBER"].isEmpty()) *disc = TStringToQString( map["DISCNUMBER"].front() ).trimmed();
|
||||
if (!map["COMPILATION"].isEmpty()) *compilation = TStringToQString( map["COMPILATION"].front() ).trimmed();
|
||||
if (!map["COVERART"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
if (!map["METADATA_BLOCK_PICTURE"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
|
||||
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
||||
|
||||
if (!map["LYRICS"].isEmpty())
|
||||
Decode(map["LYRICS"].front(), codec, song->mutable_lyrics());
|
||||
else if (!map["UNSYNCEDLYRICS"].isEmpty())
|
||||
Decode(map["UNSYNCEDLYRICS"].front(), codec, song->mutable_lyrics());
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||
@@ -480,31 +545,6 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
||||
|
||||
}
|
||||
|
||||
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||
|
||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
||||
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_FLAC;
|
||||
if (dynamic_cast<TagLib::WavPack::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAVPACK;
|
||||
if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGFLAC;
|
||||
if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGVORBIS;
|
||||
if (dynamic_cast<TagLib::Ogg::Opus::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGOPUS;
|
||||
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_OGGSPEEX;
|
||||
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MPEG;
|
||||
if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MP4;
|
||||
if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_ASF;
|
||||
if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_AIFF;
|
||||
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_MPC;
|
||||
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_TRUEAUDIO;
|
||||
if (dynamic_cast<TagLib::APE::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_APE;
|
||||
#ifdef HAVE_TAGLIB_DSFFILE
|
||||
if (dynamic_cast<TagLib::DSF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_DSF;
|
||||
if (dynamic_cast<TagLib::DSDIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_DSDIFF;
|
||||
#endif
|
||||
|
||||
return pb::tagreader::SongMetadata_FileType_UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (filename.isNull() || filename.isEmpty()) return false;
|
||||
@@ -524,17 +564,25 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
||||
SetVorbisComments(tag, song);
|
||||
}
|
||||
|
||||
else if (TagLib::WavPack::File *file = dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = file->APETag(true);
|
||||
if (!tag) return false;
|
||||
tag->setArtist(StdStringToTaglibString(song.artist()));
|
||||
tag->setAlbum(StdStringToTaglibString(song.album()));
|
||||
tag->setTitle(StdStringToTaglibString(song.title()));
|
||||
tag->setGenre(StdStringToTaglibString(song.genre()));
|
||||
tag->setComment(StdStringToTaglibString(song.comment()));
|
||||
tag->setYear(song.year());
|
||||
tag->setTrack(song.track());
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
|
||||
else if (TagLib::APE::File *file = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
|
||||
else if (TagLib::MPC::File *file = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
|
||||
else if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = file->ID3v2Tag(true);
|
||||
if (!tag) return false;
|
||||
@@ -547,6 +595,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
||||
}
|
||||
|
||||
else if (TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
TagLib::MP4::Tag *tag = file->tag();
|
||||
tag->itemListMap()["disk"] = TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0);
|
||||
@@ -563,17 +612,28 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
}
|
||||
|
||||
bool ret = fileref->save();
|
||||
#ifdef Q_OS_LINUX
|
||||
#ifdef Q_OS_LINUX
|
||||
if (ret) {
|
||||
// Linux: inotify doesn't seem to notice the change to the file unless we
|
||||
// change the timestamps as well. (this is what touch does)
|
||||
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
||||
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
||||
tag->setItem("disc", TagLib::APE::Item("disc", TagLib::String::number(song.disc() <= 0 - 1 ? 0 : song.disc())));
|
||||
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
|
||||
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
|
||||
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
||||
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
||||
tag->setItem("compilation", TagLib::APE::Item("compilation", TagLib::StringList(song.compilation() ? "1" : "0")));
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
||||
|
||||
const QByteArray descr_utf8(description.toUtf8());
|
||||
@@ -659,7 +719,6 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
if (ref.isNull() || !ref.file()) return QByteArray();
|
||||
|
||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||
// FLAC
|
||||
TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(ref.file());
|
||||
if (flac_file && flac_file->xiphComment()) {
|
||||
@@ -674,32 +733,33 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
return QByteArray(picture->data().data(), picture->data().size());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// WavPack
|
||||
|
||||
TagLib::WavPack::File *wavpack_file = dynamic_cast<TagLib::WavPack::File*>(ref.file());
|
||||
if (wavpack_file) {
|
||||
return LoadEmbeddedAPEArt(wavpack_file->APETag()->itemListMap());
|
||||
}
|
||||
|
||||
// APE
|
||||
|
||||
TagLib::APE::File *ape_file = dynamic_cast<TagLib::APE::File*>(ref.file());
|
||||
if (ape_file) {
|
||||
return LoadEmbeddedAPEArt(ape_file->APETag()->itemListMap());
|
||||
}
|
||||
|
||||
// MPC
|
||||
|
||||
TagLib::MPC::File *mpc_file = dynamic_cast<TagLib::MPC::File*>(ref.file());
|
||||
if (mpc_file) {
|
||||
return LoadEmbeddedAPEArt(mpc_file->APETag()->itemListMap());
|
||||
}
|
||||
|
||||
// Ogg Vorbis / Speex
|
||||
TagLib::Ogg::XiphComment *xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(ref.file()->tag());
|
||||
if (xiph_comment) {
|
||||
TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();
|
||||
|
||||
#if TAGLIB_MAJOR_VERSION <= 1 && TAGLIB_MINOR_VERSION < 11
|
||||
// Other than the below mentioned non-standard COVERART, METADATA_BLOCK_PICTURE is the proposed tag for cover pictures.
|
||||
// (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
|
||||
if (map.contains("METADATA_BLOCK_PICTURE")) {
|
||||
TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"];
|
||||
for (std::list<TagLib::String>::iterator it = pict_list.begin(); it != pict_list.end(); ++it) {
|
||||
QByteArray data(QByteArray::fromBase64(it->toCString()));
|
||||
TagLib::ByteVector tdata(data.data(), data.size());
|
||||
TagLib::FLAC::Picture p(tdata);
|
||||
if (p.type() == TagLib::FLAC::Picture::FrontCover)
|
||||
return QByteArray(p.data().data(), p.data().size());
|
||||
}
|
||||
// If there was no specific front cover, just take the first picture
|
||||
QByteArray data(QByteArray::fromBase64(map["METADATA_BLOCK_PICTURE"].front().toCString()));
|
||||
TagLib::ByteVector tdata(data.data(), data.size());
|
||||
TagLib::FLAC::Picture p(tdata);
|
||||
return QByteArray(p.data().data(), p.data().size());
|
||||
}
|
||||
#else
|
||||
TagLib::List<TagLib::FLAC::Picture*> pics = xiph_comment->pictureList();
|
||||
if (!pics.isEmpty()) {
|
||||
for (auto p : pics) {
|
||||
@@ -712,7 +772,6 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
return QByteArray(picture->data().data(), picture->data().size());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ogg lacks a definitive standard for embedding cover art, but it seems
|
||||
// b64 encoding a field called COVERART is the general convention
|
||||
@@ -754,6 +813,24 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
|
||||
|
||||
QByteArray ret;
|
||||
|
||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("COVER ART (FRONT)");
|
||||
if (it != map.end()) {
|
||||
TagLib::ByteVector data = it->second.binaryData();
|
||||
|
||||
int pos = data.find('\0') + 1;
|
||||
if ((pos > 0) && (pos < data.size())) {
|
||||
ret = QByteArray(data.data() + pos, data.size() - pos);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const {
|
||||
|
||||
TagLib::ByteVector id_vector("USLT");
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <taglib/xiphcomment.h>
|
||||
#include <taglib/apetag.h>
|
||||
#include <taglib/apefile.h>
|
||||
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
@@ -52,20 +54,24 @@ class TagReader {
|
||||
public:
|
||||
TagReader();
|
||||
|
||||
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||
|
||||
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
||||
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
|
||||
|
||||
bool IsMediaFile(const QString &filename) const;
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||
|
||||
static void Decode(const TagLib::String& tag, const QTextCodec *codec, std::string *output);
|
||||
static void Decode(const QString &tag, const QTextCodec *codec, std::string *output);
|
||||
|
||||
void ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
||||
void ParseAPETag(const TagLib::APE::ItemListMap &map, const QTextCodec *codec, QString *disc, QString *compilation, pb::tagreader::SongMetadata *song) const;
|
||||
void ParseFMPSFrame(const QString &name, const QString &value, pb::tagreader::SongMetadata *song) const;
|
||||
|
||||
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
||||
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
||||
|
||||
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||
void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;
|
||||
@@ -74,7 +80,7 @@ class TagReader {
|
||||
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
FileRefFactory *factory_;
|
||||
QNetworkAccessManager *network_;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
|
||||
public:
|
||||
TagReaderWorker(QIODevice *socket, QObject *parent = NULL);
|
||||
TagReaderWorker(QIODevice *socket, QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void MessageArrived(const pb::tagreader::Message &message);
|
||||
|
||||
117
snap/snapcraft.yaml
Normal file
@@ -0,0 +1,117 @@
|
||||
name: strawberry
|
||||
version: '0.5.2+git'
|
||||
summary: music player and collection organizer
|
||||
description: |
|
||||
Strawberry is a music player and collection organizer.
|
||||
It is a fork of Clementine released in 2018 aimed at music collectors,
|
||||
audio enthusiasts and audiophiles
|
||||
|
||||
grade: stable
|
||||
confinement: strict
|
||||
|
||||
parts:
|
||||
strawberry:
|
||||
plugin: cmake
|
||||
source-type: git
|
||||
source: https://github.com/jonaski/strawberry
|
||||
after: [desktop-qt5]
|
||||
|
||||
build-packages:
|
||||
- cmake
|
||||
- make
|
||||
- gcc
|
||||
- g++
|
||||
- protobuf-compiler
|
||||
- libglib2.0-dev
|
||||
- libdbus-1-dev
|
||||
- libprotobuf-dev
|
||||
- libboost-dev
|
||||
- libsqlite3-dev
|
||||
- libasound2-dev
|
||||
- libpulse-dev
|
||||
- libtag1-dev
|
||||
- qtbase5-dev
|
||||
- qtbase5-dev-tools
|
||||
- qtbase5-private-dev
|
||||
- libqt5x11extras5-dev
|
||||
- libgstreamer1.0-dev
|
||||
- libgstreamer-plugins-base1.0-dev
|
||||
- libxine2-dev
|
||||
- libvlc-dev
|
||||
- libcdio-dev
|
||||
- libgpod-dev
|
||||
- libimobiledevice-dev
|
||||
- libmtp-dev
|
||||
- libplist-dev
|
||||
- libusbmuxd-dev
|
||||
- libchromaprint-dev
|
||||
|
||||
stage-packages:
|
||||
- libstdc++6
|
||||
- libgcc1
|
||||
- libprotobuf9v5
|
||||
- libpcre16-3
|
||||
- libqt5core5a
|
||||
- libqt5gui5
|
||||
- libqt5widgets5
|
||||
- libqt5concurrent5
|
||||
- libqt5network5
|
||||
- libqt5dbus5
|
||||
- libqt5sql5
|
||||
- libqt5x11extras5
|
||||
- libqt5sql5-sqlite
|
||||
- libsqlite3-0
|
||||
- libgpm2
|
||||
- libasound2
|
||||
- libpulse0
|
||||
- libcdio13
|
||||
- libgpod4
|
||||
- libmtp9
|
||||
- libimobiledevice6
|
||||
- libplist3
|
||||
- libusbmuxd4
|
||||
- libxine2
|
||||
- libvlc5
|
||||
- libvlccore8
|
||||
- libtag1v5
|
||||
- libchromaprint0
|
||||
- zlib1g
|
||||
- libx11-6
|
||||
- libdb5.3
|
||||
- libgstreamer1.0-0
|
||||
- libgstreamer-plugins-base1.0-0
|
||||
- gstreamer1.0-alsa
|
||||
- gstreamer1.0-pulseaudio
|
||||
- gstreamer1.0-plugins-base
|
||||
- gstreamer1.0-plugins-good
|
||||
- gstreamer1.0-plugins-bad
|
||||
- gstreamer1.0-plugins-ugly
|
||||
- gstreamer1.0-libav
|
||||
|
||||
apps:
|
||||
strawberry:
|
||||
command: desktop-launch $SNAP/bin/strawberry
|
||||
desktop: share/applications/org.strawbs.strawberry.desktop
|
||||
environment:
|
||||
LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio
|
||||
plugs:
|
||||
- system-observe
|
||||
- system-trace
|
||||
- home
|
||||
- dbus
|
||||
- mpris
|
||||
- udisks2
|
||||
- network
|
||||
- network-bind
|
||||
- desktop
|
||||
- desktop-legacy
|
||||
- x11
|
||||
- wayland
|
||||
- alsa
|
||||
- pulseaudio
|
||||
- removable-media
|
||||
- optical-drive
|
||||
- raw-usb
|
||||
- media-hub
|
||||
- screen-inhibit-control
|
||||
- unity7
|
||||
@@ -26,6 +26,10 @@ if(BUILD_WERROR)
|
||||
endif (LINUX)
|
||||
endif(BUILD_WERROR)
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
include(../cmake/Translations.cmake)
|
||||
endif(HAVE_TRANSLATIONS)
|
||||
|
||||
# Set up definitions and paths
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -60,14 +64,6 @@ if(HAVE_PHONON)
|
||||
include_directories(${PHONON_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBDEEZER)
|
||||
include_directories(${DEEZER_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBDZMEDIA)
|
||||
include_directories(${DZMEDIA_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
include_directories(${TAGLIB_INCLUDE_DIRS})
|
||||
|
||||
@@ -125,6 +121,8 @@ set(SOURCES
|
||||
analyzer/analyzerbase.cpp
|
||||
analyzer/analyzercontainer.cpp
|
||||
analyzer/blockanalyzer.cpp
|
||||
analyzer/boomanalyzer.cpp
|
||||
analyzer/rainbowanalyzer.cpp
|
||||
|
||||
equalizer/equalizer.cpp
|
||||
equalizer/equalizerslider.cpp
|
||||
@@ -206,7 +204,6 @@ set(SOURCES
|
||||
lyrics/lyricsfetcher.cpp
|
||||
lyrics/lyricsfetchersearch.cpp
|
||||
lyrics/auddlyricsprovider.cpp
|
||||
lyrics/apiseedslyricsprovider.cpp
|
||||
|
||||
settings/settingsdialog.cpp
|
||||
settings/settingspage.cpp
|
||||
@@ -242,7 +239,7 @@ set(SOURCES
|
||||
widgets/osd.cpp
|
||||
widgets/osdpretty.cpp
|
||||
widgets/renametablineedit.cpp
|
||||
widgets/sliderwidget.cpp
|
||||
widgets/volumeslider.cpp
|
||||
widgets/stickyslider.cpp
|
||||
widgets/stretchheaderview.cpp
|
||||
widgets/stylehelper.cpp
|
||||
@@ -307,6 +304,8 @@ set(HEADERS
|
||||
analyzer/analyzerbase.h
|
||||
analyzer/analyzercontainer.h
|
||||
analyzer/blockanalyzer.h
|
||||
analyzer/boomanalyzer.h
|
||||
analyzer/rainbowanalyzer.h
|
||||
|
||||
equalizer/equalizer.h
|
||||
equalizer/equalizerslider.h
|
||||
@@ -380,7 +379,6 @@ set(HEADERS
|
||||
lyrics/lyricsfetcher.h
|
||||
lyrics/lyricsfetchersearch.h
|
||||
lyrics/auddlyricsprovider.h
|
||||
lyrics/apiseedslyricsprovider.h
|
||||
|
||||
settings/settingsdialog.h
|
||||
settings/settingspage.h
|
||||
@@ -415,7 +413,7 @@ set(HEADERS
|
||||
widgets/osd.h
|
||||
widgets/osdpretty.h
|
||||
widgets/renametablineedit.h
|
||||
widgets/sliderwidget.h
|
||||
widgets/volumeslider.h
|
||||
widgets/stickyslider.h
|
||||
widgets/stretchheaderview.h
|
||||
widgets/trackslider.h
|
||||
@@ -571,12 +569,6 @@ optional_source(HAVE_PHONON
|
||||
HEADERS engine/phononengine.h
|
||||
)
|
||||
|
||||
# Deezer
|
||||
optional_source(HAVE_DEEZER
|
||||
SOURCES engine/deezerengine.cpp
|
||||
HEADERS engine/deezerengine.h
|
||||
)
|
||||
|
||||
# DBUS and MPRIS - Unix specific
|
||||
if(UNIX AND HAVE_DBUS)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dbus)
|
||||
@@ -880,19 +872,6 @@ optional_source(HAVE_STREAM_TIDAL
|
||||
settings/tidalsettingspage.ui
|
||||
)
|
||||
|
||||
optional_source(HAVE_STREAM_DEEZER
|
||||
SOURCES
|
||||
deezer/deezerservice.cpp
|
||||
deezer/deezerurlhandler.cpp
|
||||
settings/deezersettingspage.cpp
|
||||
HEADERS
|
||||
deezer/deezerservice.h
|
||||
deezer/deezerurlhandler.h
|
||||
settings/deezersettingspage.h
|
||||
UI
|
||||
settings/deezersettingspage.ui
|
||||
)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
||||
|
||||
@@ -900,6 +879,37 @@ qt5_wrap_cpp(MOC ${HEADERS})
|
||||
qt5_wrap_ui(UIC ${UI})
|
||||
qt5_add_resources(QRC ${RESOURCES})
|
||||
|
||||
if(HAVE_TRANSLATIONS)
|
||||
|
||||
set(LINGUAS "All" CACHE STRING "A space-seperated list of translations to compile in to Strawberry, or \"None\".")
|
||||
if (LINGUAS STREQUAL "All")
|
||||
# build LANGUAGES from all existing .po files
|
||||
file(GLOB pofiles translations/*.po)
|
||||
foreach(pofile ${pofiles})
|
||||
get_filename_component(lang ${pofile} NAME_WE)
|
||||
list(APPEND LANGUAGES ${lang})
|
||||
endforeach(pofile)
|
||||
else (LINGUAS STREQUAL "All")
|
||||
if (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
set (LANGUAGES "")
|
||||
else (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
string(REGEX MATCHALL [a-zA-Z_@]+ LANGUAGES ${LINGUAS})
|
||||
endif (NOT LINGUAS OR LINGUAS STREQUAL "None")
|
||||
endif (LINGUAS STREQUAL "All")
|
||||
|
||||
add_pot(POT
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/header
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.pot
|
||||
${SOURCES}
|
||||
${MOC}
|
||||
${UIC}
|
||||
${OTHER_SOURCES}
|
||||
../data/html/oauthsuccess.html
|
||||
)
|
||||
add_po(PO strawberry_ LANGUAGES ${LANGUAGES} DIRECTORY translations)
|
||||
|
||||
endif(HAVE_TRANSLATIONS)
|
||||
|
||||
add_library(strawberry_lib STATIC
|
||||
${SOURCES}
|
||||
${MOC}
|
||||
@@ -955,14 +965,6 @@ if(HAVE_PHONON)
|
||||
target_link_libraries(strawberry_lib ${PHONON_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_DEEZER)
|
||||
target_link_libraries(strawberry_lib ${LIBDEEZER_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_DZMEDIA)
|
||||
target_link_libraries(strawberry_lib ${LIBDZMEDIA_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBGPOD)
|
||||
target_link_libraries(strawberry_lib ${LIBGPOD_LIBRARIES})
|
||||
endif(HAVE_LIBGPOD)
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
/***************************************************************************
|
||||
viswidget.cpp - description
|
||||
-------------------
|
||||
begin : Die Jan 7 2003
|
||||
copyright : (C) 2003 by Max Howell
|
||||
email : markey@web.de
|
||||
***************************************************************************/
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Amarok.
|
||||
Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2012, David Sansome <me@davidsansome.com>
|
||||
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2017, Santiago Gil
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QTimerEvent>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "engine/enginebase.h"
|
||||
|
||||
@@ -36,10 +44,11 @@
|
||||
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it
|
||||
// 4. if you want to manipulate the scope, reimplement transform()
|
||||
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
|
||||
// TODO make an INSTRUCTIONS file
|
||||
//
|
||||
// TODO:
|
||||
// Make an INSTRUCTIONS file
|
||||
// can't mod scope in analyze you have to use transform
|
||||
|
||||
// TODO for 2D use setErasePixmap Qt function insetead of m_background
|
||||
// for 2D use setErasePixmap Qt function insetead of m_background
|
||||
|
||||
// make the linker happy only for gcc < 4.0
|
||||
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
|
||||
@@ -53,7 +62,6 @@ Analyzer::Base::Base(QWidget *parent, uint scopeSize)
|
||||
fht_(new FHT(scopeSize)),
|
||||
engine_(nullptr),
|
||||
lastscope_(512),
|
||||
current_chunk_(0),
|
||||
new_frame_(false),
|
||||
is_playing_(false) {}
|
||||
|
||||
@@ -63,19 +71,18 @@ void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
|
||||
|
||||
void Analyzer::Base::transform(Scope& scope) {
|
||||
|
||||
// This is a standard transformation that should give an FFT scope that has bands for pretty analyzers
|
||||
QVector<float> aux(fht_->size());
|
||||
if (aux.size() >= scope.size()) {
|
||||
std::copy(scope.begin(), scope.end(), aux.begin());
|
||||
}
|
||||
else {
|
||||
std::copy(scope.begin(), scope.begin() + aux.size(), aux.begin());
|
||||
}
|
||||
|
||||
// NOTE: Resizing here is redundant as FHT routines only calculate FHT::size() values scope.resize( fht_->size() );
|
||||
|
||||
float *front = static_cast<float*>(&scope.front());
|
||||
|
||||
float *f = new float[fht_->size()];
|
||||
fht_->copy(&f[0], front);
|
||||
fht_->logSpectrum(front, &f[0]);
|
||||
fht_->scale(front, 1.0 / 20);
|
||||
fht_->logSpectrum(scope.data(), aux.data());
|
||||
fht_->scale(scope.data(), 1.0 / 20);
|
||||
|
||||
scope.resize(fht_->size() / 2); // second half of values are rubbish
|
||||
delete[] f;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
// Maintainer: Max Howell <max.howell@methylblue.com>, (C) 2004
|
||||
// Copyright: See COPYING file that comes with this distribution
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Amarok.
|
||||
Copyright 2003-2004, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2012, David Sansome <me@davidsansome.com>
|
||||
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2017, Santiago Gil
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ANALYZERBASE_H
|
||||
#define ANALYZERBASE_H
|
||||
@@ -7,7 +26,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -23,6 +42,7 @@
|
||||
|
||||
#include "analyzer/fht.h"
|
||||
#include "engine/engine_fwd.h"
|
||||
#include "engine/enginebase.h"
|
||||
|
||||
class QHideEvent;
|
||||
class QShowEvent;
|
||||
@@ -54,7 +74,7 @@ class Base : public QWidget {
|
||||
virtual void framerateChanged() {}
|
||||
|
||||
protected:
|
||||
Base(QWidget*, uint scopeSize = 7);
|
||||
explicit Base(QWidget*, uint scopeSize = 7);
|
||||
|
||||
void hideEvent(QHideEvent*);
|
||||
void showEvent(QShowEvent*);
|
||||
@@ -76,7 +96,7 @@ class Base : public QWidget {
|
||||
FHT *fht_;
|
||||
EngineBase *engine_;
|
||||
Scope lastscope_;
|
||||
int current_chunk_;
|
||||
|
||||
bool new_frame_;
|
||||
bool is_playing_;
|
||||
};
|
||||
@@ -84,6 +104,7 @@ class Base : public QWidget {
|
||||
void interpolate(const Scope&, Scope&);
|
||||
void initSin(Scope&, const uint = 6000);
|
||||
|
||||
} // END namespace Analyzer
|
||||
} // namespace Analyzer
|
||||
|
||||
#endif // ANALYZERBASE_H
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
#include "analyzerbase.h"
|
||||
#include "blockanalyzer.h"
|
||||
#include "boomanalyzer.h"
|
||||
#include "rainbowanalyzer.h"
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
@@ -64,6 +66,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
ignore_next_click_(false),
|
||||
current_analyzer_(nullptr),
|
||||
engine_(nullptr) {
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -79,6 +82,9 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
context_menu_->addSeparator();
|
||||
|
||||
AddAnalyzerType<BlockAnalyzer>();
|
||||
AddAnalyzerType<BoomAnalyzer>();
|
||||
AddAnalyzerType<Rainbow::NyanCatAnalyzer>();
|
||||
AddAnalyzerType<Rainbow::RainbowDashAnalyzer>();
|
||||
|
||||
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
|
||||
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer()));
|
||||
@@ -93,6 +99,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
connect(double_click_timer_, SIGNAL(timeout()), SLOT(ShowPopupMenu()));
|
||||
|
||||
Load();
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SetActions(QAction *visualisation) {
|
||||
@@ -101,6 +108,7 @@ void AnalyzerContainer::SetActions(QAction *visualisation) {
|
||||
}
|
||||
|
||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
if (ignore_next_click_) {
|
||||
ignore_next_click_ = false;
|
||||
@@ -114,6 +122,7 @@ void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
else if (e->button() == Qt::RightButton) {
|
||||
context_menu_->popup(e->globalPos());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::ShowPopupMenu() {
|
||||
@@ -144,6 +153,7 @@ void AnalyzerContainer::DisableAnalyzer() {
|
||||
}
|
||||
|
||||
void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||
|
||||
QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
|
||||
|
||||
if (!instance) {
|
||||
@@ -161,9 +171,11 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||
layout()->addWidget(current_analyzer_);
|
||||
|
||||
Save();
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::ChangeFramerate(int new_framerate) {
|
||||
|
||||
if (current_analyzer_) {
|
||||
// Even if it is not supposed to happen, I don't want to get a dbz error
|
||||
new_framerate = new_framerate == 0 ? kMediumFramerate : new_framerate;
|
||||
@@ -173,14 +185,18 @@ void AnalyzerContainer::ChangeFramerate(int new_framerate) {
|
||||
current_analyzer_->framerateChanged();
|
||||
}
|
||||
SaveFramerate(new_framerate);
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::Load() {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
QString type = s.value("type", "BlockAnalyzer").toString();
|
||||
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
|
||||
s.endGroup();
|
||||
|
||||
// Analyzer
|
||||
QString type = s.value("type", "BlockAnalyzer").toString();
|
||||
if (type.isEmpty()) {
|
||||
DisableAnalyzer();
|
||||
disable_action_->setChecked(true);
|
||||
@@ -196,7 +212,6 @@ void AnalyzerContainer::Load() {
|
||||
}
|
||||
|
||||
// Framerate
|
||||
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
|
||||
for (int i = 0; i < framerate_list_.count(); ++i) {
|
||||
if (current_framerate_ == framerate_list_[i]) {
|
||||
ChangeFramerate(current_framerate_);
|
||||
@@ -204,27 +219,35 @@ void AnalyzerContainer::Load() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SaveFramerate(int framerate) {
|
||||
|
||||
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
|
||||
current_framerate_ = framerate;
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue(kSettingsFramerate, current_framerate_);
|
||||
s.endGroup();
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::Save() {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
s.setValue("type", current_analyzer_ ? current_analyzer_->metaObject()->className() : QVariant());
|
||||
s.endGroup();
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
|
||||
|
||||
QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
|
||||
mapper_framerate_->setMapping(action, framerate);
|
||||
group_framerate_->addAction(action);
|
||||
framerate_list_ << framerate;
|
||||
action->setCheckable(true);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QPoint>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QSignalMapper>
|
||||
#include <QTimer>
|
||||
#include <QtEvents>
|
||||
@@ -120,5 +120,4 @@ void AnalyzerContainer::AddAnalyzerType() {
|
||||
actions_ << action;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ANALYZERCONTAINER_H
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
// Author: Max Howell <max.howell@methylblue.com>, (C) 2003-5
|
||||
// Mark Kretschmann <markey@web.de>, (C) 2005
|
||||
// Copyright: See COPYING file that comes with this distribution
|
||||
//
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Amarok.
|
||||
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2005, Mark Kretschmann <markey@web.de>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "blockanalyzer.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <scoped_allocator>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPixmap>
|
||||
@@ -19,12 +36,12 @@
|
||||
#include "analyzerbase.h"
|
||||
#include "fht.h"
|
||||
|
||||
const uint BlockAnalyzer::HEIGHT = 2;
|
||||
const uint BlockAnalyzer::WIDTH = 4;
|
||||
const uint BlockAnalyzer::MIN_ROWS = 3; // arbituary
|
||||
const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary
|
||||
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
|
||||
const uint BlockAnalyzer::FADE_SIZE = 90;
|
||||
const uint BlockAnalyzer::kHeight = 2;
|
||||
const uint BlockAnalyzer::kWidth = 4;
|
||||
const uint BlockAnalyzer::kMinRows = 3; // arbituary
|
||||
const uint BlockAnalyzer::kMinColumns = 32; // arbituary
|
||||
const uint BlockAnalyzer::kMaxColumns = 256; // must be 2**n
|
||||
const uint BlockAnalyzer::kFadeSize = 90;
|
||||
|
||||
const char *BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
||||
|
||||
@@ -34,18 +51,18 @@ BlockAnalyzer::BlockAnalyzer(QWidget *parent)
|
||||
rows_(0),
|
||||
y_(0),
|
||||
barpixmap_(1, 1),
|
||||
topbarpixmap_(WIDTH, HEIGHT),
|
||||
scope_(MIN_COLUMNS),
|
||||
topbarpixmap_(kWidth, kHeight),
|
||||
scope_(kMinColumns),
|
||||
store_(1 << 8, 0),
|
||||
fade_bars_(FADE_SIZE),
|
||||
fade_bars_(kFadeSize),
|
||||
fade_pos_(1 << 8, 50),
|
||||
fade_intensity_(1 << 8, 32) {
|
||||
|
||||
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, MIN_ROWS * (HEIGHT + 1) - 1); //-1 is padding, no drawing takes place there
|
||||
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
|
||||
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1); //-1 is padding, no drawing takes place there
|
||||
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
|
||||
|
||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||
for (uint i = 0; i < FADE_SIZE; ++i) fade_bars_[i] = QPixmap(1, 1);
|
||||
for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
|
||||
|
||||
}
|
||||
|
||||
@@ -61,20 +78,20 @@ void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||
const uint oldRows = rows_;
|
||||
|
||||
// all is explained in analyze()..
|
||||
//+1 to counter -1 in maxSizes, trust me we need this!
|
||||
columns_ = qMax(uint(double(width() + 1) / (WIDTH + 1)), MAX_COLUMNS);
|
||||
rows_ = uint(double(height() + 1) / (HEIGHT + 1));
|
||||
// +1 to counter -1 in maxSizes, trust me we need this!
|
||||
columns_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1, kMaxColumns);
|
||||
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
|
||||
|
||||
// this is the y-offset for drawing from the top of the widget
|
||||
y_ = (height() - (rows_ * (HEIGHT + 1)) + 2) / 2;
|
||||
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
|
||||
|
||||
scope_.resize(columns_);
|
||||
|
||||
if (rows_ != oldRows) {
|
||||
barpixmap_ = QPixmap(WIDTH, rows_ * (HEIGHT + 1));
|
||||
barpixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||
|
||||
for (uint i = 0; i < FADE_SIZE; ++i)
|
||||
fade_bars_[i] = QPixmap(WIDTH, rows_ * (HEIGHT + 1));
|
||||
for (uint i = 0; i < kFadeSize; ++i)
|
||||
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||
|
||||
yscale_.resize(rows_ + 1);
|
||||
|
||||
@@ -113,13 +130,11 @@ void BlockAnalyzer::transform(Analyzer::Scope &s) {
|
||||
|
||||
for (uint x = 0; x < s.size(); ++x) s[x] *= 2;
|
||||
|
||||
float* front = static_cast<float*>(&s.front());
|
||||
|
||||
fht_->spectrum(front);
|
||||
fht_->scale(front, 1.0 / 20);
|
||||
fht_->spectrum(s.data());
|
||||
fht_->scale(s.data(), 1.0 / 20);
|
||||
|
||||
// the second half is pretty dull, so only show it if the user has a large analyzer by setting to scope_.size() if large we prevent interpolation of large analyzers, this is good!
|
||||
s.resize(scope_.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : scope_.size());
|
||||
s.resize(scope_.size() <= kMaxColumns / 2 ? kMaxColumns / 2 : scope_.size());
|
||||
|
||||
}
|
||||
|
||||
@@ -154,35 +169,33 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
|
||||
// determine y
|
||||
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
|
||||
|
||||
// this is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
||||
if ((float)y > store_[x])
|
||||
y = int(store_[x] += step_);
|
||||
// This is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
||||
if (static_cast<float>(y) > store_[x])
|
||||
y = static_cast<int>(store_[x] += step_);
|
||||
else
|
||||
store_[x] = y;
|
||||
|
||||
// if y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
|
||||
// If y is lower than fade_pos_, then the bar has exceeded the height of the fadeout
|
||||
// if the fadeout is quite faded now, then display the new one
|
||||
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < FADE_SIZE / 3*/) {
|
||||
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < kFadeSize / 3*/) {
|
||||
fade_pos_[x] = y;
|
||||
fade_intensity_[x] = FADE_SIZE;
|
||||
fade_intensity_[x] = kFadeSize;
|
||||
}
|
||||
|
||||
if (fade_intensity_[x] > 0) {
|
||||
const uint offset = --fade_intensity_[x];
|
||||
const uint y = y_ + (fade_pos_[x] * (HEIGHT + 1));
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y, fade_bars_[offset], 0, 0, WIDTH, height() - y);
|
||||
const uint y = y_ + (fade_pos_[x] * (kHeight + 1));
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), y, fade_bars_[offset], 0, 0, kWidth, height() - y);
|
||||
}
|
||||
|
||||
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
|
||||
|
||||
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing, rows_ means none are
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, *bar(),
|
||||
0, y * (HEIGHT + 1), bar()->width(),
|
||||
bar()->height());
|
||||
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)
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1), int(store_[x]) * (HEIGHT + 1) + y_, topbarpixmap_);
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
|
||||
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
|
||||
@@ -223,11 +236,11 @@ static inline void adjustToLimits(int &b, int &f, uint &amount) {
|
||||
* It won't modify the hue of fg unless absolutely necessary
|
||||
* @return the adjusted form of fg
|
||||
*/
|
||||
QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||
QColor ensureContrast(const QColor &bg, const QColor &fg, uint amount = 150) {
|
||||
|
||||
class OutputOnExit {
|
||||
public:
|
||||
OutputOnExit(const QColor &color) : c(color) {}
|
||||
explicit OutputOnExit(const QColor &color) : c(color) {}
|
||||
~OutputOnExit() {
|
||||
int h, s, v;
|
||||
c.getHsv(&h, &s, &v);
|
||||
@@ -237,14 +250,6 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||
const QColor &c;
|
||||
};
|
||||
|
||||
// hack so I don't have to cast everywhere
|
||||
#define amount static_cast<int>(_amount)
|
||||
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
|
||||
// #define STAMP1( string ) debug() << string << ": " <<
|
||||
// (QValueList<int>() << fh << fs << fv) << endl;
|
||||
// #define STAMP2( string, value ) debug() << string << "=" << value << ":
|
||||
// " << (QValueList<int>() << fh << fs << fv) << endl;
|
||||
|
||||
OutputOnExit allocateOnTheStack(fg);
|
||||
|
||||
int bh, bs, bv;
|
||||
@@ -255,23 +260,17 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||
|
||||
int dv = abs(bv - fv);
|
||||
|
||||
// STAMP2( "DV", dv );
|
||||
|
||||
// value is the best measure of contrast
|
||||
// if there is enough difference in value already, return fg unchanged
|
||||
if (dv > amount) return fg;
|
||||
if (dv > static_cast<int>(amount)) return fg;
|
||||
|
||||
int ds = abs(bs - fs);
|
||||
|
||||
// STAMP2( "DS", ds );
|
||||
|
||||
// saturation is good enough too. But not as good. TODO adapt this a little
|
||||
if (ds > amount) return fg;
|
||||
if (ds > static_cast<int>(amount)) return fg;
|
||||
|
||||
int dh = abs(bh - fh);
|
||||
|
||||
// STAMP2( "DH", dh );
|
||||
|
||||
if (dh > 120) {
|
||||
// a third of the colour wheel automatically guarentees contrast
|
||||
// but only if the values are high enough and saturations significant enough
|
||||
@@ -279,79 +278,49 @@ QColor ensureContrast(const QColor &bg, const QColor &fg, uint _amount = 150) {
|
||||
|
||||
// check the saturation for the two colours is sufficient that hue alone can
|
||||
// provide sufficient contrast
|
||||
if (ds > amount / 2 && (bs > 125 && fs > 125))
|
||||
// STAMP1( "Sufficient saturation difference, and hues are
|
||||
// compliemtary" );
|
||||
if (ds > static_cast<int>(amount) / 2 && (bs > 125 && fs > 125))
|
||||
return fg;
|
||||
else if (dv > amount / 2 && (bv > 125 && fv > 125))
|
||||
// STAMP1( "Sufficient value difference, and hues are
|
||||
// compliemtary" );
|
||||
else if (dv > static_cast<int>(amount) / 2 && (bv > 125 && fv > 125))
|
||||
return fg;
|
||||
|
||||
// STAMP1( "Hues are complimentary but we must modify the value or
|
||||
// saturation of the contrasting colour" );
|
||||
|
||||
// but either the colours are two desaturated, or too dark
|
||||
// so we need to adjust the system, although not as much
|
||||
///_amount /= 2;
|
||||
}
|
||||
|
||||
if (fs < 50 && ds < 40) {
|
||||
// low saturation on a low saturation is sad
|
||||
const int tmp = 50 - fs;
|
||||
fs = 50;
|
||||
if (amount > tmp)
|
||||
_amount -= tmp;
|
||||
if (static_cast<int>(amount) > tmp)
|
||||
amount -= tmp;
|
||||
else
|
||||
_amount = 0;
|
||||
amount = 0;
|
||||
}
|
||||
|
||||
// test that there is available value to honor our contrast requirement
|
||||
if (255 - dv < amount) {
|
||||
if (255 - dv < static_cast<int>(amount)) {
|
||||
// we have to modify the value and saturation of fg
|
||||
// adjustToLimits( bv, fv, amount );
|
||||
|
||||
// STAMP
|
||||
|
||||
// see if we need to adjust the saturation
|
||||
if (amount > 0) adjustToLimits(bs, fs, _amount);
|
||||
|
||||
// STAMP
|
||||
if (static_cast<int>(amount) > 0) adjustToLimits(bs, fs, amount);
|
||||
|
||||
// see if we need to adjust the hue
|
||||
if (amount > 0) fh += amount; // cycles around;
|
||||
|
||||
// STAMP
|
||||
if (static_cast<int>(amount) > 0)
|
||||
fh += static_cast<int>(amount); // cycles around;
|
||||
|
||||
return QColor::fromHsv(fh, fs, fv);
|
||||
}
|
||||
|
||||
// STAMP
|
||||
if (fv > bv && bv > static_cast<int>(amount))
|
||||
return QColor::fromHsv(fh, fs, bv - static_cast<int>(amount));
|
||||
|
||||
if (fv > bv && bv > amount) return QColor::fromHsv(fh, fs, bv - amount);
|
||||
if (fv < bv && fv > static_cast<int>(amount))
|
||||
return QColor::fromHsv(fh, fs, fv - static_cast<int>(amount));
|
||||
|
||||
// STAMP
|
||||
if (fv > bv && (255 - fv > static_cast<int>(amount)))
|
||||
return QColor::fromHsv(fh, fs, fv + static_cast<int>(amount));
|
||||
|
||||
if (fv < bv && fv > amount) return QColor::fromHsv(fh, fs, fv - amount);
|
||||
|
||||
// STAMP
|
||||
|
||||
if (fv > bv && (255 - fv > amount))
|
||||
return QColor::fromHsv(fh, fs, fv + amount);
|
||||
|
||||
// STAMP
|
||||
|
||||
if (fv < bv && (255 - bv > amount))
|
||||
return QColor::fromHsv(fh, fs, bv + amount);
|
||||
|
||||
// STAMP
|
||||
// debug() << "Something went wrong!\n";
|
||||
if (fv < bv && (255 - bv > static_cast<int>(amount)))
|
||||
return QColor::fromHsv(fh, fs, bv + static_cast<int>(amount));
|
||||
|
||||
return Qt::blue;
|
||||
|
||||
#undef amount
|
||||
// #undef STAMP
|
||||
|
||||
}
|
||||
|
||||
void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
@@ -361,17 +330,17 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
|
||||
topbarpixmap_.fill(fg);
|
||||
|
||||
const double dr = 15 * double(bg.red() - fg.red()) / (rows_ * 16);
|
||||
const double dg = 15 * double(bg.green() - fg.green()) / (rows_ * 16);
|
||||
const double db = 15 * double(bg.blue() - fg.blue()) / (rows_ * 16);
|
||||
const double dr = 15 * static_cast<double>(bg.red() - fg.red()) / (rows_ * 16);
|
||||
const double dg = 15 * static_cast<double>(bg.green() - fg.green()) / (rows_ * 16);
|
||||
const double db = 15 * static_cast<double>(bg.blue() - fg.blue()) / (rows_ * 16);
|
||||
const int r = fg.red(), g = fg.green(), b = fg.blue();
|
||||
|
||||
bar()->fill(bg);
|
||||
|
||||
QPainter p(bar());
|
||||
for (int y = 0; (uint)y < rows_; ++y)
|
||||
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
|
||||
// graduate the fg color
|
||||
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * y), g + int(dg * y), b + int(db * y)));
|
||||
p.fillRect(0, y * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y), b + static_cast<int>(db * y)));
|
||||
|
||||
{
|
||||
const QColor bg = palette().color(QPalette::Background).dark(112);
|
||||
@@ -388,12 +357,12 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
const int r = bg.red(), g = bg.green(), b = bg.blue();
|
||||
|
||||
// Precalculate all fade-bar pixmaps
|
||||
for (uint y = 0; y < FADE_SIZE; ++y) {
|
||||
for (uint y = 0; y < kFadeSize; ++y) {
|
||||
fade_bars_[y].fill(palette().color(QPalette::Background));
|
||||
QPainter f(&fade_bars_[y]);
|
||||
for (int z = 0; (uint)z < rows_; ++z) {
|
||||
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
|
||||
f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT, QColor(r + int(dr * Y), g + int(dg * Y), b + int(db * Y)));
|
||||
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
|
||||
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
|
||||
f.fillRect(0, z * (kHeight + 1), kWidth, kHeight, QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y), b + static_cast<int>(db * Y)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,14 +373,21 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
|
||||
void BlockAnalyzer::drawBackground() {
|
||||
|
||||
if (background_.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QColor bg = palette().color(QPalette::Background);
|
||||
const QColor bgdark = bg.dark(112);
|
||||
|
||||
background_.fill(bg);
|
||||
|
||||
QPainter p(&background_);
|
||||
|
||||
if (p.paintEngine() == 0) return;
|
||||
|
||||
for (int x = 0; (uint)x < columns_; ++x)
|
||||
for (int y = 0; (uint)y < rows_; ++y)
|
||||
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + y_, WIDTH, HEIGHT, bgdark);
|
||||
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight, bgdark);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
// Maintainer: Max Howell <mac.howell@methylblue.com>, (C) 2003-5
|
||||
// Copyright: See COPYING file that comes with this distribution
|
||||
//
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Amarok.
|
||||
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2005, Mark Kretschmann <markey@web.de>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef BLOCKANALYZER_H
|
||||
#define BLOCKANALYZER_H
|
||||
@@ -11,6 +30,7 @@
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
@@ -21,22 +41,18 @@
|
||||
|
||||
class QResizeEvent;
|
||||
|
||||
/**
|
||||
* @author Max Howell
|
||||
*/
|
||||
|
||||
class BlockAnalyzer : public Analyzer::Base {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_INVOKABLE BlockAnalyzer(QWidget*);
|
||||
~BlockAnalyzer();
|
||||
|
||||
static const uint HEIGHT;
|
||||
static const uint WIDTH;
|
||||
static const uint MIN_ROWS;
|
||||
static const uint MIN_COLUMNS;
|
||||
static const uint MAX_COLUMNS;
|
||||
static const uint FADE_SIZE;
|
||||
static const uint kHeight;
|
||||
static const uint kWidth;
|
||||
static const uint kMinRows;
|
||||
static const uint kMinColumns;
|
||||
static const uint kMaxColumns;
|
||||
static const uint kFadeSize;
|
||||
|
||||
static const char *kName;
|
||||
|
||||
@@ -53,22 +69,21 @@ class BlockAnalyzer : public Analyzer::Base {
|
||||
private:
|
||||
QPixmap *bar() { return &barpixmap_; }
|
||||
|
||||
uint columns_, rows_; // number of rows and columns of blocks
|
||||
uint y_; // y-offset from top of widget
|
||||
uint columns_, rows_; // number of rows and columns of blocks
|
||||
uint y_; // y-offset from top of widget
|
||||
QPixmap barpixmap_;
|
||||
QPixmap topbarpixmap_;
|
||||
QPixmap background_;
|
||||
QPixmap canvas_;
|
||||
Analyzer::Scope scope_; // so we don't create a vector every frame
|
||||
std::vector<float> store_; // current bar heights
|
||||
std::vector<float> yscale_;
|
||||
Analyzer::Scope scope_; // so we don't create a vector every frame
|
||||
QVector<float> store_; // current bar heights
|
||||
QVector<float> yscale_;
|
||||
|
||||
// FIXME why can't I namespace these? c++ issue?
|
||||
std::vector<QPixmap> fade_bars_;
|
||||
std::vector<uint> fade_pos_;
|
||||
std::vector<int> fade_intensity_;
|
||||
QVector<QPixmap> fade_bars_;
|
||||
QVector<uint> fade_pos_;
|
||||
QVector<int> fade_intensity_;
|
||||
|
||||
float step_; // rows to fall per frame
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // BLOCKANALYZER_H
|
||||
|
||||
169
src/analyzer/boomanalyzer.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Clementine.
|
||||
Copyright 2004, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "boomanalyzer.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QColor>
|
||||
|
||||
using Analyzer::Scope;
|
||||
|
||||
const uint BoomAnalyzer::kColumnWidth = 4;
|
||||
const uint BoomAnalyzer::kMaxBandCount = 256;
|
||||
const uint BoomAnalyzer::kMinBandCount = 32;
|
||||
|
||||
const char* BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
|
||||
|
||||
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
bands_(0),
|
||||
scope_(kMinBandCount),
|
||||
fg_(palette().color(QPalette::Highlight)),
|
||||
K_barHeight_(1.271) // 1.471
|
||||
,
|
||||
F_peakSpeed_(1.103) // 1.122
|
||||
,
|
||||
F_(1.0),
|
||||
bar_height_(kMaxBandCount, 0),
|
||||
peak_height_(kMaxBandCount, 0),
|
||||
peak_speed_(kMaxBandCount, 0.01),
|
||||
barPixmap_(kColumnWidth, 50) {
|
||||
|
||||
setMinimumWidth(kMinBandCount * (kColumnWidth + 1) - 1);
|
||||
setMaximumWidth(kMaxBandCount * (kColumnWidth + 1) - 1);
|
||||
|
||||
}
|
||||
|
||||
void BoomAnalyzer::changeK_barHeight(int newValue) {
|
||||
K_barHeight_ = static_cast<double>(newValue) / 1000;
|
||||
}
|
||||
|
||||
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
|
||||
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
|
||||
}
|
||||
|
||||
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
|
||||
QWidget::resizeEvent(e);
|
||||
|
||||
const uint HEIGHT = height() - 2;
|
||||
const double h = 1.2 / HEIGHT;
|
||||
|
||||
bands_ = qMin(static_cast<uint>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) + 1, kMaxBandCount);
|
||||
scope_.resize(bands_);
|
||||
|
||||
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
|
||||
|
||||
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
|
||||
canvas_ = QPixmap(size());
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
QPainter p(&barPixmap_);
|
||||
for (uint y = 0; y < HEIGHT; ++y) {
|
||||
const double F = static_cast<double>(y) * h;
|
||||
|
||||
p.setPen(QColor(qMax(0, 255 - static_cast<int>(229.0 * F)),
|
||||
qMax(0, 255 - static_cast<int>(229.0 * F)),
|
||||
qMax(0, 255 - static_cast<int>(191.0 * F))));
|
||||
p.drawLine(0, y, kColumnWidth - 2, y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BoomAnalyzer::transform(Scope& s) {
|
||||
|
||||
fht_->spectrum(s.data());
|
||||
fht_->scale(s.data(), 1.0 / 50);
|
||||
|
||||
s.resize(scope_.size() <= kMaxBandCount / 2 ? kMaxBandCount / 2 : scope_.size());
|
||||
|
||||
}
|
||||
|
||||
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
|
||||
if (!new_frame || engine_->state() == Engine::Paused) {
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
return;
|
||||
}
|
||||
float h;
|
||||
const uint MAX_HEIGHT = height() - 1;
|
||||
|
||||
QPainter canvas_painter(&canvas_);
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
Analyzer::interpolate(scope, scope_);
|
||||
|
||||
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
|
||||
h = log10(scope_[i] * 256.0) * F_;
|
||||
|
||||
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
||||
|
||||
if (h > bar_height_[i]) {
|
||||
bar_height_[i] = h;
|
||||
|
||||
if (h > peak_height_[i]) {
|
||||
peak_height_[i] = h;
|
||||
peak_speed_[i] = 0.01;
|
||||
}
|
||||
else {
|
||||
goto peak_handling;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bar_height_[i] > 0.0) {
|
||||
bar_height_[i] -= K_barHeight_; // 1.4
|
||||
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
|
||||
}
|
||||
|
||||
peak_handling:
|
||||
|
||||
if (peak_height_[i] > 0.0) {
|
||||
peak_height_[i] -= peak_speed_[i];
|
||||
peak_speed_[i] *= F_peakSpeed_; // 1.12
|
||||
|
||||
if (peak_height_[i] < bar_height_[i]) peak_height_[i] = bar_height_[i];
|
||||
if (peak_height_[i] < 0.0) peak_height_[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
y = height() - uint(bar_height_[i]);
|
||||
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
|
||||
canvas_painter.setPen(fg_);
|
||||
if (bar_height_[i] > 0)
|
||||
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
|
||||
|
||||
y = height() - uint(peak_height_[i]);
|
||||
canvas_painter.setPen(palette().color(QPalette::Midlight));
|
||||
canvas_painter.drawLine(x, y, x + kColumnWidth - 1, y);
|
||||
}
|
||||
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
|
||||
}
|
||||
|
||||
75
src/analyzer/boomanalyzer.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Clementine.
|
||||
Copyright 2004, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef BOOMANALYZER_H
|
||||
#define BOOMANALYZER_H
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QColor>
|
||||
|
||||
class QResizeEvent;
|
||||
|
||||
class BoomAnalyzer : public Analyzer::Base {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE BoomAnalyzer(QWidget*);
|
||||
|
||||
static const char* kName;
|
||||
|
||||
virtual void transform(Analyzer::Scope& s);
|
||||
virtual void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
|
||||
|
||||
public slots:
|
||||
void changeK_barHeight(int);
|
||||
void changeF_peakSpeed(int);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* e);
|
||||
|
||||
static const uint kColumnWidth;
|
||||
static const uint kMaxBandCount;
|
||||
static const uint kMinBandCount;
|
||||
|
||||
uint bands_;
|
||||
Analyzer::Scope scope_;
|
||||
QColor fg_;
|
||||
|
||||
double K_barHeight_, F_peakSpeed_, F_;
|
||||
|
||||
std::vector<float> bar_height_;
|
||||
std::vector<float> peak_height_;
|
||||
std::vector<float> peak_speed_;
|
||||
|
||||
QPixmap barPixmap_;
|
||||
QPixmap canvas_;
|
||||
|
||||
};
|
||||
|
||||
#endif // BOOMANALYZER_H
|
||||
@@ -1,140 +1,140 @@
|
||||
// FHT - Fast Hartley Transform Class
|
||||
//
|
||||
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
//
|
||||
// $Id$
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Clementine.
|
||||
Copyright 2004, Melchior FRANZ <mfranz@kde.org>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2017, Santiago Gil
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fht.h"
|
||||
|
||||
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
|
||||
if (n < 3) {
|
||||
m_num = 0;
|
||||
m_exp2 = -1;
|
||||
return;
|
||||
}
|
||||
m_exp2 = n;
|
||||
m_num = 1 << n;
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <QVector>
|
||||
|
||||
FHT::FHT(int n) : num_((n < 3) ? 0 : 1 << n), exp2_((n < 3) ? -1 : n) {
|
||||
if (n > 3) {
|
||||
m_buf = new float[m_num];
|
||||
m_tab = new float[m_num * 2];
|
||||
buf_vector_.resize(num_);
|
||||
tab_vector_.resize(num_ * 2);
|
||||
makeCasTable();
|
||||
}
|
||||
}
|
||||
|
||||
FHT::~FHT() {
|
||||
delete[] m_buf;
|
||||
delete[] m_tab;
|
||||
delete[] m_log;
|
||||
}
|
||||
FHT::~FHT() {}
|
||||
|
||||
int FHT::sizeExp() const { return exp2_; }
|
||||
int FHT::size() const { return num_; }
|
||||
|
||||
float* FHT::buf_() { return buf_vector_.data(); }
|
||||
float* FHT::tab_() { return tab_vector_.data(); }
|
||||
int* FHT::log_() { return log_vector_.data(); }
|
||||
|
||||
void FHT::makeCasTable(void) {
|
||||
float d, *costab, *sintab;
|
||||
int ul, ndiv2 = m_num / 2;
|
||||
float* costab = tab_();
|
||||
float* sintab = tab_() + num_ / 2 + 1;
|
||||
|
||||
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) {
|
||||
d = M_PI * ul / ndiv2;
|
||||
for (int ul = 0; ul < num_; ul++) {
|
||||
float d = M_PI * ul / (num_ / 2);
|
||||
*costab = *sintab = cos(d);
|
||||
|
||||
costab += 2, sintab += 2;
|
||||
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
|
||||
costab += 2;
|
||||
sintab += 2;
|
||||
if (sintab > tab_() + num_ * 2) sintab = tab_() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
float* FHT::copy(float* d, float* s) {
|
||||
return (float*)memcpy(d, s, m_num * sizeof(float));
|
||||
}
|
||||
|
||||
float* FHT::clear(float* d) {
|
||||
return (float*)memset(d, 0, m_num * sizeof(float));
|
||||
}
|
||||
|
||||
void FHT::scale(float* p, float d) {
|
||||
for (int i = 0; i < (m_num / 2); i++) *p++ *= d;
|
||||
for (int i = 0; i < (num_ / 2); i++) *p++ *= d;
|
||||
}
|
||||
|
||||
void FHT::ewma(float* d, float* s, float w) {
|
||||
for (int i = 0; i < (m_num / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||
for (int i = 0; i < (num_ / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||
}
|
||||
|
||||
void FHT::logSpectrum(float* out, float* p) {
|
||||
int n = m_num / 2, i, j, k, *r;
|
||||
if (!m_log) {
|
||||
m_log = new int[n];
|
||||
float f = n / log10((double)n);
|
||||
for (i = 0, r = m_log; i < n; i++, r++) {
|
||||
j = int(rint(log10(i + 1.0) * f));
|
||||
|
||||
int n = num_ / 2, i, j, k, *r;
|
||||
if (log_vector_.size() < n) {
|
||||
log_vector_.resize(n);
|
||||
float f = n / log10(static_cast<double>(n));
|
||||
for (i = 0, r = log_(); i < n; i++, r++) {
|
||||
j = static_cast<int>(rint(log10(i + 1.0) * f));
|
||||
*r = j >= n ? n - 1 : j;
|
||||
}
|
||||
}
|
||||
semiLogSpectrum(p);
|
||||
*out++ = *p = *p / 100;
|
||||
for (k = i = 1, r = m_log; i < n; i++) {
|
||||
for (k = i = 1, r = log_(); i < n; i++) {
|
||||
j = *r++;
|
||||
if (i == j)
|
||||
if (i == j) {
|
||||
*out++ = p[i];
|
||||
}
|
||||
else {
|
||||
float base = p[k - 1];
|
||||
float step = (p[j] - base) / (j - (k - 1));
|
||||
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FHT::semiLogSpectrum(float* p) {
|
||||
float e;
|
||||
power2(p);
|
||||
for (int i = 0; i < (m_num / 2); i++, p++) {
|
||||
e = 10.0 * log10(sqrt(*p * .5));
|
||||
for (int i = 0; i < (num_ / 2); i++, p++) {
|
||||
float e = 10.0 * log10(sqrt(*p / 2));
|
||||
*p = e < 0 ? 0 : e;
|
||||
}
|
||||
}
|
||||
|
||||
void FHT::spectrum(float* p) {
|
||||
power2(p);
|
||||
for (int i = 0; i < (m_num / 2); i++, p++) *p = (float)sqrt(*p * .5);
|
||||
for (int i = 0; i < (num_ / 2); i++, p++)
|
||||
*p = static_cast<float>(sqrt(*p / 2));
|
||||
}
|
||||
|
||||
void FHT::power(float* p) {
|
||||
power2(p);
|
||||
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5;
|
||||
for (int i = 0; i < (num_ / 2); i++) *p++ /= 2;
|
||||
}
|
||||
|
||||
void FHT::power2(float* p) {
|
||||
int i;
|
||||
float* q;
|
||||
_transform(p, m_num, 0);
|
||||
_transform(p, num_, 0);
|
||||
|
||||
*p = (*p * *p), *p += *p, p++;
|
||||
*p = static_cast<float>(2 * pow(*p, 2));
|
||||
p++;
|
||||
|
||||
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
|
||||
*p = (*p * *p) + (*q * *q), p++;
|
||||
float* q = p + num_ - 2;
|
||||
for (int i = 1; i < (num_ / 2); i++) {
|
||||
*p = static_cast<float>(pow(*p, 2) + pow(*q, 2));
|
||||
p++;
|
||||
q--;
|
||||
}
|
||||
}
|
||||
|
||||
void FHT::transform(float* p) {
|
||||
if (m_num == 8)
|
||||
if (num_ == 8)
|
||||
transform8(p);
|
||||
else
|
||||
_transform(p, m_num, 0);
|
||||
_transform(p, num_, 0);
|
||||
}
|
||||
|
||||
void FHT::transform8(float* p) {
|
||||
|
||||
float a, b, c, d, e, f, g, h, b_f2, d_h2;
|
||||
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
|
||||
|
||||
@@ -159,6 +159,7 @@ void FHT::transform8(float* p) {
|
||||
*--p = a_ce_g + b_df_h;
|
||||
*--p = ac_e_g + b_f2;
|
||||
*--p = aceg + bdfh;
|
||||
|
||||
}
|
||||
|
||||
void FHT::_transform(float* p, int n, int k) {
|
||||
@@ -171,19 +172,19 @@ void FHT::_transform(float* p, int n, int k) {
|
||||
int i, j, ndiv2 = n / 2;
|
||||
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
|
||||
|
||||
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
|
||||
for (i = 0, t1 = buf_(), t2 = buf_() + ndiv2, pp = &p[k]; i < ndiv2; i++)
|
||||
*t1++ = *pp++, *t2++ = *pp++;
|
||||
|
||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
||||
std::copy(buf_(), buf_() + n, p + k);
|
||||
|
||||
_transform(p, ndiv2, k);
|
||||
_transform(p, ndiv2, k + ndiv2);
|
||||
|
||||
j = m_num / ndiv2 - 1;
|
||||
t1 = m_buf;
|
||||
j = num_ / ndiv2 - 1;
|
||||
t1 = buf_();
|
||||
t2 = t1 + ndiv2;
|
||||
t3 = p + k + ndiv2;
|
||||
ptab = m_tab;
|
||||
ptab = tab_();
|
||||
pp = p + k;
|
||||
|
||||
a = *ptab++ * *t3++;
|
||||
@@ -200,5 +201,7 @@ void FHT::_transform(float* p, int n, int k) {
|
||||
*t1++ = *pp + a;
|
||||
*t2++ = *pp++ - a;
|
||||
}
|
||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
||||
|
||||
std::copy(buf_(), buf_() + n, p + k);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
// FHT - Fast Hartley Transform Class
|
||||
//
|
||||
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
//
|
||||
// $Id$
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Clementine.
|
||||
Copyright 2004, Melchior FRANZ <mfranz@kde.org>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2017, Santiago Gil
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FHT_H
|
||||
#define FHT_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
/**
|
||||
* Implementation of the Hartley Transform after Bracewell's discrete
|
||||
* algorithm. The algorithm is subject to US patent No. 4,646,256 (1987)
|
||||
@@ -30,11 +34,16 @@
|
||||
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
|
||||
*/
|
||||
class FHT {
|
||||
int m_exp2;
|
||||
int m_num;
|
||||
float* m_buf;
|
||||
float* m_tab;
|
||||
int* m_log;
|
||||
const int num_;
|
||||
const int exp2_;
|
||||
|
||||
QVector<float> buf_vector_;
|
||||
QVector<float> tab_vector_;
|
||||
QVector<int> log_vector_;
|
||||
|
||||
float* buf_();
|
||||
float* tab_();
|
||||
int* log_();
|
||||
|
||||
/**
|
||||
* Create a table of "cas" (cosine and sine) values.
|
||||
@@ -57,10 +66,8 @@ class FHT {
|
||||
FHT(int);
|
||||
|
||||
~FHT();
|
||||
inline int sizeExp() const { return m_exp2; }
|
||||
inline int size() const { return m_num; }
|
||||
float* copy(float*, float*);
|
||||
float* clear(float*);
|
||||
int sizeExp() const;
|
||||
int size() const;
|
||||
void scale(float*, float);
|
||||
|
||||
/**
|
||||
@@ -115,4 +122,4 @@ class FHT {
|
||||
void transform(float*);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // FHT_H
|
||||
|
||||
207
src/analyzer/rainbowanalyzer.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Clementine.
|
||||
Copyright 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
|
||||
Copyright 2011-2012, 2014, David Sansome <me@davidsansome.com>
|
||||
Copyright 2011, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rainbowanalyzer.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QColor>
|
||||
#include <QBrush>
|
||||
#include <QPen>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include "core/arraysize.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
using Analyzer::Scope;
|
||||
|
||||
const int Rainbow::RainbowAnalyzer::kHeight[] = {21, 33};
|
||||
const int Rainbow::RainbowAnalyzer::kWidth[] = {34, 53};
|
||||
const int Rainbow::RainbowAnalyzer::kFrameCount[] = {6, 16};
|
||||
const int Rainbow::RainbowAnalyzer::kRainbowHeight[] = {21, 16};
|
||||
const int Rainbow::RainbowAnalyzer::kRainbowOverlap[] = {13, 15};
|
||||
const int Rainbow::RainbowAnalyzer::kSleepingHeight[] = {24, 33};
|
||||
|
||||
const char* Rainbow::NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
||||
const char* Rainbow::RainbowDashAnalyzer::kName = "Rainbow Dash";
|
||||
const float Rainbow::RainbowAnalyzer::kPixelScale = 0.02f;
|
||||
|
||||
Rainbow::RainbowAnalyzer::RainbowType Rainbow::RainbowAnalyzer::rainbowtype;
|
||||
|
||||
Rainbow::RainbowAnalyzer::RainbowAnalyzer(const RainbowType& rbtype, QWidget* parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
timer_id_(startTimer(kFrameIntervalMs)),
|
||||
frame_(0),
|
||||
current_buffer_(0),
|
||||
available_rainbow_width_(0),
|
||||
px_per_frame_(0),
|
||||
x_offset_(0),
|
||||
background_brush_(QColor(0x0f, 0x43, 0x73))
|
||||
{
|
||||
|
||||
rainbowtype = rbtype;
|
||||
cat_dash_[0] = QPixmap(":/pictures/nyancat.png");
|
||||
cat_dash_[1] = QPixmap(":/pictures/rainbowdash.png");
|
||||
memset(history_, 0, sizeof(history_));
|
||||
|
||||
for (int i = 0; i < kRainbowBands; ++i) {
|
||||
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255), kRainbowHeight[rainbowtype] / kRainbowBands, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
|
||||
|
||||
// pow constants computed so that
|
||||
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
|
||||
band_scale_[i] = -std::cos(M_PI * i / (kRainbowBands - 1)) * 0.5 * std::pow(2.3, i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Rainbow::RainbowAnalyzer::transform(Scope& s) { fht_->spectrum(s.data()); }
|
||||
|
||||
void Rainbow::RainbowAnalyzer::timerEvent(QTimerEvent* e) {
|
||||
|
||||
if (e->timerId() == timer_id_) {
|
||||
frame_ = (frame_ + 1) % kFrameCount[rainbowtype];
|
||||
}
|
||||
else {
|
||||
Analyzer::Base::timerEvent(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Rainbow::RainbowAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
|
||||
// Invalidate the buffer so it's recreated from scratch in the next paint event.
|
||||
buffer_[0] = QPixmap();
|
||||
buffer_[1] = QPixmap();
|
||||
|
||||
available_rainbow_width_ = width() - kWidth[rainbowtype] + kRainbowOverlap[rainbowtype];
|
||||
px_per_frame_ = static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
|
||||
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
|
||||
|
||||
}
|
||||
|
||||
void Rainbow::RainbowAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_frame) {
|
||||
|
||||
// Discard the second half of the transform
|
||||
const int scope_size = s.size() / 2;
|
||||
|
||||
if ((new_frame && is_playing_) || (buffer_[0].isNull() && buffer_[1].isNull())) {
|
||||
// Transform the music into rainbows!
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
float* band_start = history_ + band * kHistorySize;
|
||||
|
||||
// Move the history of each band across by 1 frame.
|
||||
memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float));
|
||||
}
|
||||
|
||||
// Now accumulate the scope data into each band. Should maybe use a series
|
||||
// of band pass filters for this, so bands can leak into neighbouring bands,
|
||||
// but for now it's a series of separate square filters.
|
||||
const int samples_per_band = scope_size / kRainbowBands;
|
||||
int sample = 0;
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
float accumulator = 0.0;
|
||||
for (int i = 0; i < samples_per_band; ++i) {
|
||||
accumulator += s[sample++];
|
||||
}
|
||||
|
||||
history_[(band + 1) * kHistorySize - 1] = accumulator * band_scale_[band];
|
||||
}
|
||||
|
||||
// Create polylines for the rainbows.
|
||||
QPointF polyline[kRainbowBands * kHistorySize];
|
||||
QPointF* dest = polyline;
|
||||
float* source = history_;
|
||||
|
||||
const float top_of = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight[rainbowtype]) / 2;
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
// Calculate the Y position of this band.
|
||||
const float y = static_cast<float>(kRainbowHeight[rainbowtype]) / (kRainbowBands + 1) * (band + 0.5) + top_of;
|
||||
|
||||
// Add each point in the line.
|
||||
for (int x = 0; x < kHistorySize; ++x) {
|
||||
*dest = QPointF(px_per_frame_ * x, y + *source * kPixelScale);
|
||||
++dest;
|
||||
++source;
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have to draw the whole rainbow into the buffer?
|
||||
if (buffer_[0].isNull()) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
buffer_[i] = QPixmap(QSize(width() + x_offset_, height()));
|
||||
buffer_[i].fill(background_brush_.color());
|
||||
}
|
||||
current_buffer_ = 0;
|
||||
|
||||
QPainter buffer_painter(&buffer_[0]);
|
||||
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
||||
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
||||
buffer_painter.setPen(colors_[band]);
|
||||
buffer_painter.drawPolyline(&polyline[band * kHistorySize], kHistorySize);
|
||||
buffer_painter.drawPolyline(&polyline[band * kHistorySize], kHistorySize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int last_buffer = current_buffer_;
|
||||
current_buffer_ = (current_buffer_ + 1) % 2;
|
||||
|
||||
// We can just shuffle the buffer along a bit and draw the new frame's data.
|
||||
QPainter buffer_painter(&buffer_[current_buffer_]);
|
||||
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
buffer_painter.drawPixmap(0, 0, buffer_[last_buffer], px_per_frame_, 0, x_offset_ + available_rainbow_width_ - px_per_frame_, 0);
|
||||
buffer_painter.fillRect(x_offset_ + available_rainbow_width_ - px_per_frame_, 0, kWidth[rainbowtype] - kRainbowOverlap[rainbowtype] + px_per_frame_, height(), background_brush_);
|
||||
|
||||
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
||||
buffer_painter.setPen(colors_[band]);
|
||||
buffer_painter.drawPolyline(&polyline[(band + 1) * kHistorySize - 3], 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the buffer on to the widget
|
||||
p.drawPixmap(0, 0, buffer_[current_buffer_], x_offset_, 0, 0, 0);
|
||||
|
||||
// Draw rainbow analyzer (nyan cat or rainbowdash)
|
||||
// Nyan nyan nyan nyan dash dash dash dash.
|
||||
if (!is_playing_) {
|
||||
// Ssshhh!
|
||||
p.drawPixmap(SleepingDestRect(rainbowtype), cat_dash_[rainbowtype], SleepingSourceRect(rainbowtype));
|
||||
}
|
||||
else {
|
||||
p.drawPixmap(DestRect(rainbowtype), cat_dash_[rainbowtype], SourceRect(rainbowtype));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rainbow::NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
||||
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Nyancat, parent) {}
|
||||
|
||||
Rainbow::RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
|
||||
: RainbowAnalyzer(Rainbow::RainbowAnalyzer::Dash, parent) {}
|
||||
141
src/analyzer/rainbowanalyzer.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Strawberry Music Player
|
||||
This file was part of Clementine.
|
||||
Copyright 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
|
||||
Copyright 2011-2012, 2014, David Sansome <me@davidsansome.com>
|
||||
Copyright 2011, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2015, Arun Narayanankutty <n.arun.lifescience@gmail.com>
|
||||
|
||||
Strawberry is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Strawberry is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RAINBOWANALYZER_H
|
||||
#define RAINBOWANALYZER_H
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QDateTime>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
|
||||
namespace Rainbow {
|
||||
class RainbowAnalyzer : public Analyzer::Base {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum RainbowType {
|
||||
Nyancat = 0,
|
||||
Dash = 1
|
||||
};
|
||||
|
||||
RainbowAnalyzer(const RainbowType& rbtype, QWidget* parent);
|
||||
|
||||
protected:
|
||||
void transform(Analyzer::Scope&);
|
||||
void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
|
||||
|
||||
void timerEvent(QTimerEvent* e);
|
||||
void resizeEvent(QResizeEvent* e);
|
||||
|
||||
private:
|
||||
static const int kHeight[];
|
||||
static const int kWidth[];
|
||||
static const int kFrameCount[];
|
||||
static const int kRainbowHeight[];
|
||||
static const int kRainbowOverlap[];
|
||||
static const int kSleepingHeight[];
|
||||
|
||||
static const int kHistorySize = 128;
|
||||
static const int kRainbowBands = 6;
|
||||
static const float kPixelScale;
|
||||
|
||||
static const int kFrameIntervalMs = 150;
|
||||
|
||||
static RainbowType rainbowtype;
|
||||
|
||||
inline QRect SourceRect(RainbowType rainbowtype) const {
|
||||
return QRect(0, kHeight[rainbowtype] * frame_, kWidth[rainbowtype], kHeight[rainbowtype]);
|
||||
}
|
||||
|
||||
inline QRect SleepingSourceRect(RainbowType rainbowtype) const {
|
||||
return QRect(0, kHeight[rainbowtype] * kFrameCount[rainbowtype], kWidth[rainbowtype], kSleepingHeight[rainbowtype]);
|
||||
}
|
||||
|
||||
inline QRect DestRect(RainbowType rainbowtype) const {
|
||||
return QRect(width() - kWidth[rainbowtype], (height() - kHeight[rainbowtype]) / 2, kWidth[rainbowtype], kHeight[rainbowtype]);
|
||||
}
|
||||
|
||||
inline QRect SleepingDestRect(RainbowType rainbowtype) const {
|
||||
return QRect(width() - kWidth[rainbowtype], (height() - kSleepingHeight[rainbowtype]) / 2, kWidth[rainbowtype], kSleepingHeight[rainbowtype]);
|
||||
}
|
||||
|
||||
private:
|
||||
// "constants" that get initialised in the constructor
|
||||
float band_scale_[kRainbowBands];
|
||||
QPen colors_[kRainbowBands];
|
||||
|
||||
// Rainbow Nyancat & Dash
|
||||
QPixmap cat_dash_[2];
|
||||
|
||||
// For the cat or dash animation
|
||||
int timer_id_;
|
||||
int frame_;
|
||||
|
||||
// The y positions of each point on the rainbow.
|
||||
float history_[kHistorySize * kRainbowBands];
|
||||
|
||||
// A cache of the last frame's rainbow,
|
||||
// so it can be used in the next frame.
|
||||
QPixmap buffer_[2];
|
||||
int current_buffer_;
|
||||
|
||||
// Geometry information that's updated on resize:
|
||||
// The width of the widget minus the space for the cat
|
||||
int available_rainbow_width_;
|
||||
|
||||
// X spacing between each point in the polyline.
|
||||
int px_per_frame_;
|
||||
|
||||
// Amount the buffer_ is shifted to the left (off the edge of the widget)
|
||||
// to make the rainbow extend from 0 to available_rainbow_width_.
|
||||
int x_offset_;
|
||||
|
||||
QBrush background_brush_;
|
||||
};
|
||||
|
||||
class NyanCatAnalyzer : public RainbowAnalyzer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE NyanCatAnalyzer(QWidget* parent);
|
||||
|
||||
static const char* kName;
|
||||
};
|
||||
|
||||
class RainbowDashAnalyzer : public RainbowAnalyzer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE RainbowDashAnalyzer(QWidget* parent);
|
||||
|
||||
static const char* kName;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // RAINBOWANALYZER_H
|
||||
@@ -63,6 +63,7 @@ SCollection::SCollection(Application *app, QObject *parent)
|
||||
}
|
||||
|
||||
SCollection::~SCollection() {
|
||||
watcher_->Stop();
|
||||
watcher_->deleteLater();
|
||||
watcher_thread_->exit();
|
||||
watcher_thread_->wait(5000 /* five seconds */);
|
||||
|
||||
@@ -323,6 +323,17 @@ SongList CollectionBackend::FindSongsInDirectory(int id) {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::SongPathChanged(const Song &song, const QFileInfo &new_file) {
|
||||
|
||||
// Take a song and update its path
|
||||
Song updated_song = song;
|
||||
updated_song.InitFromFilePartial(new_file.absoluteFilePath());
|
||||
SongList updated_songs;
|
||||
updated_songs << updated_song;
|
||||
AddOrUpdateSongs(updated_songs);
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::AddOrUpdateSubdirs(const SubdirectoryList &subdirs) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
@@ -575,10 +586,8 @@ QStringList CollectionBackend::GetAllArtistsWithAlbums(const QueryOptions &opt)
|
||||
}
|
||||
}
|
||||
|
||||
// QStringList ret;
|
||||
QSet<QString> artists;
|
||||
while (query.Next()) {
|
||||
//ret << query.Value(0).toString();
|
||||
artists << query.Value(0).toString();
|
||||
}
|
||||
|
||||
@@ -586,8 +595,8 @@ QStringList CollectionBackend::GetAllArtistsWithAlbums(const QueryOptions &opt)
|
||||
artists << query2.Value(0).toString();
|
||||
}
|
||||
|
||||
// return ret;
|
||||
return QStringList(artists.toList());
|
||||
|
||||
}
|
||||
|
||||
CollectionBackend::AlbumList CollectionBackend::GetAllAlbums(const QueryOptions &opt) {
|
||||
@@ -659,6 +668,7 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids) {
|
||||
}
|
||||
|
||||
SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QString &table, const QString &column) {
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
@@ -678,6 +688,7 @@ SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QS
|
||||
ret[index].InitFromQuery(q, true);
|
||||
}
|
||||
return ret.toList();
|
||||
|
||||
}
|
||||
|
||||
Song CollectionBackend::GetSongById(int id, QSqlDatabase &db) {
|
||||
@@ -687,6 +698,7 @@ Song CollectionBackend::GetSongById(int id, QSqlDatabase &db) {
|
||||
}
|
||||
|
||||
SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &db) {
|
||||
|
||||
QString in = ids.join(",");
|
||||
|
||||
QSqlQuery q(db);
|
||||
@@ -701,12 +713,14 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &d
|
||||
ret << song;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
Song CollectionBackend::GetSongByUrl(const QUrl &url, qint64 beginning) {
|
||||
|
||||
CollectionQuery query;
|
||||
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
query.AddWhere("filename", url.toEncoded());
|
||||
query.AddWhere("filename", url.toString());
|
||||
query.AddWhere("beginning", beginning);
|
||||
|
||||
Song song;
|
||||
@@ -714,12 +728,14 @@ Song CollectionBackend::GetSongByUrl(const QUrl &url, qint64 beginning) {
|
||||
song.InitFromQuery(query, true);
|
||||
}
|
||||
return song;
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::GetSongsByUrl(const QUrl &url) {
|
||||
|
||||
CollectionQuery query;
|
||||
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
query.AddWhere("filename", url.toEncoded());
|
||||
query.AddWhere("filename", url.toString());
|
||||
|
||||
SongList songlist;
|
||||
if (ExecQuery(&query)) {
|
||||
@@ -730,6 +746,7 @@ SongList CollectionBackend::GetSongsByUrl(const QUrl &url) {
|
||||
}
|
||||
}
|
||||
return songlist;
|
||||
|
||||
}
|
||||
|
||||
CollectionBackend::AlbumList CollectionBackend::GetCompilationAlbums(const QueryOptions &opt) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
@@ -194,6 +195,7 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
void IncrementPlayCount(int id);
|
||||
void IncrementSkipCount(int id, float progress);
|
||||
void ResetStatistics(int id);
|
||||
void SongPathChanged(const Song &song, const QFileInfo &new_file);
|
||||
|
||||
signals:
|
||||
void DirectoryDiscovered(const Directory &dir, const SubdirectoryList &subdirs);
|
||||
|
||||
@@ -149,13 +149,13 @@ void CollectionFilterWidget::UpdateGroupByActions() {
|
||||
QActionGroup *CollectionFilterWidget::CreateGroupByActions(QObject *parent) {
|
||||
|
||||
QActionGroup *ret = new QActionGroup(parent);
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Artist"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Artist)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Artist/Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Artist, CollectionModel::GroupBy_Album)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Album artist/Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_Album)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Artist/Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Artist, CollectionModel::GroupBy_Album)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Genre/Artist/Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Genre, CollectionModel::GroupBy_Artist, CollectionModel::GroupBy_Album)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Artist"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Artist)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Artist/Year - Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Artist, CollectionModel::GroupBy_YearAlbum)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Album)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Genre/Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Genre, CollectionModel::GroupBy_Album)));
|
||||
ret->addAction(CreateGroupByAction(tr("Group by Genre/Artist/Album"), parent, CollectionModel::Grouping(CollectionModel::GroupBy_Genre, CollectionModel::GroupBy_Artist, CollectionModel::GroupBy_Album)));
|
||||
|
||||
QAction *sep1 = new QAction(parent);
|
||||
sep1->setSeparator(true);
|
||||
|
||||
@@ -94,8 +94,7 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
|
||||
playlist_icon_(IconLoader::Load("albums")),
|
||||
init_task_id_(-1),
|
||||
use_pretty_covers_(false),
|
||||
show_dividers_(true)
|
||||
{
|
||||
show_dividers_(true) {
|
||||
|
||||
root_->lazy_loaded = true;
|
||||
|
||||
@@ -212,14 +211,14 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
|
||||
// Otherwise find the proper container at this level based on the item's key
|
||||
QString key;
|
||||
switch (type) {
|
||||
case GroupBy_Album: key = song.album(); break;
|
||||
case GroupBy_AlbumArtist: key = song.effective_albumartist(); break;
|
||||
case GroupBy_Artist: key = song.artist(); break;
|
||||
case GroupBy_Album: key = song.album(); break;
|
||||
case GroupBy_Composer: key = song.composer(); break;
|
||||
case GroupBy_Performer: key = song.performer(); break;
|
||||
case GroupBy_Disc: key = QString::number(song.disc()); break;
|
||||
case GroupBy_Grouping: key = song.grouping(); break;
|
||||
case GroupBy_Disc: key = QString::number(song.disc()); break;
|
||||
case GroupBy_Genre: key = song.genre(); break;
|
||||
case GroupBy_AlbumArtist: key = song.effective_albumartist(); break;
|
||||
case GroupBy_Year:
|
||||
key = QString::number(qMax(0, song.year()));
|
||||
break;
|
||||
@@ -233,16 +232,29 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
|
||||
key = PrettyYearAlbum(qMax(0, song.effective_originalyear()), song.album());
|
||||
break;
|
||||
case GroupBy_FileType:
|
||||
key = song.filetype();
|
||||
break;
|
||||
case GroupBy_Bitrate:
|
||||
key = song.bitrate();
|
||||
key = QString::number(song.filetype());
|
||||
break;
|
||||
case GroupBy_Samplerate:
|
||||
key = song.samplerate();
|
||||
key = QString::number(song.samplerate());
|
||||
break;
|
||||
case GroupBy_Bitdepth:
|
||||
key = song.bitdepth();
|
||||
key = QString::number(song.bitdepth());
|
||||
break;
|
||||
case GroupBy_Bitrate:
|
||||
key = QString::number(song.bitrate());
|
||||
break;
|
||||
case GroupBy_Format:
|
||||
if (song.samplerate() <= 0) {
|
||||
key = QString::number(song.filetype());
|
||||
}
|
||||
else {
|
||||
if (song.bitdepth() <= 0) {
|
||||
key = QString("%1 (%2)").arg(song.filetype()).arg(QString::number(song.samplerate() / 1000.0, 'G', 5));
|
||||
}
|
||||
else {
|
||||
key = QString("%1 (%2/%3)").arg(song.filetype()).arg(QString::number(song.samplerate() / 1000.0, 'G', 5)).arg(song.bitdepth());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GroupBy_None:
|
||||
qLog(Error) << "GroupBy_None";
|
||||
@@ -303,22 +315,23 @@ QString CollectionModel::DividerKey(GroupBy type, CollectionItem *item) const {
|
||||
if (item->sort_text.isEmpty()) return QString();
|
||||
|
||||
switch (type) {
|
||||
case GroupBy_Album:
|
||||
case GroupBy_AlbumArtist:
|
||||
case GroupBy_Artist:
|
||||
case GroupBy_Album:
|
||||
case GroupBy_Composer:
|
||||
case GroupBy_Performer:
|
||||
case GroupBy_Disc:
|
||||
case GroupBy_Grouping:
|
||||
case GroupBy_Disc:
|
||||
case GroupBy_Genre:
|
||||
case GroupBy_AlbumArtist:
|
||||
case GroupBy_Format:
|
||||
case GroupBy_FileType: {
|
||||
QChar c = item->sort_text[0];
|
||||
if (c.isDigit()) return "0";
|
||||
if (c == ' ') return QString();
|
||||
if (c.decompositionTag() != QChar::NoDecomposition)
|
||||
return QChar(c.decomposition()[0]);
|
||||
return c;
|
||||
}
|
||||
if (c.isDigit()) return "0";
|
||||
if (c == ' ') return QString();
|
||||
if (c.decompositionTag() != QChar::NoDecomposition)
|
||||
return QChar(c.decomposition()[0]);
|
||||
return c;
|
||||
}
|
||||
|
||||
case GroupBy_Year:
|
||||
case GroupBy_OriginalYear:
|
||||
@@ -330,15 +343,15 @@ QString CollectionModel::DividerKey(GroupBy type, CollectionItem *item) const {
|
||||
case GroupBy_OriginalYearAlbum:
|
||||
return SortTextForNumber(item->metadata.effective_originalyear());
|
||||
|
||||
case GroupBy_Bitrate:
|
||||
return SortTextForNumber(item->metadata.bitrate());
|
||||
|
||||
case GroupBy_Samplerate:
|
||||
return SortTextForNumber(item->metadata.samplerate());
|
||||
|
||||
case GroupBy_Bitdepth:
|
||||
return SortTextForNumber(item->metadata.bitdepth());
|
||||
|
||||
case GroupBy_Bitrate:
|
||||
return SortTextForNumber(item->metadata.bitrate());
|
||||
|
||||
case GroupBy_None:
|
||||
return QString();
|
||||
}
|
||||
@@ -361,6 +374,7 @@ QString CollectionModel::DividerDisplayText(GroupBy type, const QString &key) co
|
||||
case GroupBy_Genre:
|
||||
case GroupBy_AlbumArtist:
|
||||
case GroupBy_FileType:
|
||||
case GroupBy_Format:
|
||||
if (key == "0") return "0-9";
|
||||
return key.toUpper();
|
||||
|
||||
@@ -374,10 +388,6 @@ QString CollectionModel::DividerDisplayText(GroupBy type, const QString &key) co
|
||||
if (key == "0000") return tr("Unknown");
|
||||
return QString::number(key.toInt()); // To remove leading 0s
|
||||
|
||||
case GroupBy_Bitrate:
|
||||
if (key == "000") return tr("Unknown");
|
||||
return QString::number(key.toInt()); // To remove leading 0s
|
||||
|
||||
case GroupBy_Samplerate:
|
||||
if (key == "000") return tr("Unknown");
|
||||
return QString::number(key.toInt()); // To remove leading 0s
|
||||
@@ -386,6 +396,10 @@ QString CollectionModel::DividerDisplayText(GroupBy type, const QString &key) co
|
||||
if (key == "000") return tr("Unknown");
|
||||
return QString::number(key.toInt()); // To remove leading 0s
|
||||
|
||||
case GroupBy_Bitrate:
|
||||
if (key == "000") return tr("Unknown");
|
||||
return QString::number(key.toInt()); // To remove leading 0s
|
||||
|
||||
case GroupBy_None:
|
||||
// fallthrough
|
||||
;
|
||||
@@ -753,6 +767,7 @@ void CollectionModel::PostQuery(CollectionItem *parent, const CollectionModel::Q
|
||||
}
|
||||
|
||||
void CollectionModel::LazyPopulate(CollectionItem *parent, bool signal) {
|
||||
|
||||
if (parent->lazy_loaded) return;
|
||||
parent->lazy_loaded = true;
|
||||
|
||||
@@ -817,6 +832,9 @@ void CollectionModel::InitQuery(GroupBy type, CollectionQuery *q) {
|
||||
|
||||
// Say what type of thing we want to get back from the database.
|
||||
switch (type) {
|
||||
case GroupBy_AlbumArtist:
|
||||
q->SetColumnSpec("DISTINCT effective_albumartist");
|
||||
break;
|
||||
case GroupBy_Artist:
|
||||
q->SetColumnSpec("DISTINCT artist");
|
||||
break;
|
||||
@@ -850,11 +868,8 @@ void CollectionModel::InitQuery(GroupBy type, CollectionQuery *q) {
|
||||
case GroupBy_Genre:
|
||||
q->SetColumnSpec("DISTINCT genre");
|
||||
break;
|
||||
case GroupBy_AlbumArtist:
|
||||
q->SetColumnSpec("DISTINCT effective_albumartist");
|
||||
break;
|
||||
case GroupBy_Bitrate:
|
||||
q->SetColumnSpec("DISTINCT bitrate");
|
||||
case GroupBy_FileType:
|
||||
q->SetColumnSpec("DISTINCT filetype");
|
||||
break;
|
||||
case GroupBy_Samplerate:
|
||||
q->SetColumnSpec("DISTINCT samplerate");
|
||||
@@ -862,12 +877,15 @@ void CollectionModel::InitQuery(GroupBy type, CollectionQuery *q) {
|
||||
case GroupBy_Bitdepth:
|
||||
q->SetColumnSpec("DISTINCT bitdepth");
|
||||
break;
|
||||
case GroupBy_Bitrate:
|
||||
q->SetColumnSpec("DISTINCT bitrate");
|
||||
break;
|
||||
case GroupBy_Format:
|
||||
q->SetColumnSpec("DISTINCT filetype, samplerate, bitdepth");
|
||||
break;
|
||||
case GroupBy_None:
|
||||
q->SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
break;
|
||||
case GroupBy_FileType:
|
||||
q->SetColumnSpec("DISTINCT filetype");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -877,6 +895,15 @@ void CollectionModel::FilterQuery(GroupBy type, CollectionItem *item, Collection
|
||||
// Say how we want the query to be filtered. This is done once for each parent going up the tree.
|
||||
|
||||
switch (type) {
|
||||
case GroupBy_AlbumArtist:
|
||||
if (IsCompilationArtistNode(item))
|
||||
q->AddCompilationRequirement(true);
|
||||
else {
|
||||
// Don't duplicate compilations outside the Various artists node
|
||||
q->AddCompilationRequirement(false);
|
||||
q->AddWhere("effective_albumartist", item->key);
|
||||
}
|
||||
break;
|
||||
case GroupBy_Artist:
|
||||
if (IsCompilationArtistNode(item))
|
||||
q->AddCompilationRequirement(true);
|
||||
@@ -922,27 +949,23 @@ void CollectionModel::FilterQuery(GroupBy type, CollectionItem *item, Collection
|
||||
case GroupBy_Genre:
|
||||
q->AddWhere("genre", item->key);
|
||||
break;
|
||||
case GroupBy_AlbumArtist:
|
||||
if (IsCompilationArtistNode(item))
|
||||
q->AddCompilationRequirement(true);
|
||||
else {
|
||||
// Don't duplicate compilations outside the Various artists node
|
||||
q->AddCompilationRequirement(false);
|
||||
q->AddWhere("effective_albumartist", item->key);
|
||||
}
|
||||
break;
|
||||
case GroupBy_FileType:
|
||||
q->AddWhere("filetype", item->metadata.filetype());
|
||||
break;
|
||||
case GroupBy_Bitrate:
|
||||
q->AddWhere("bitrate", item->key);
|
||||
break;
|
||||
case GroupBy_Samplerate:
|
||||
q->AddWhere("samplerate", item->key);
|
||||
break;
|
||||
case GroupBy_Bitdepth:
|
||||
q->AddWhere("bitdepth", item->key);
|
||||
break;
|
||||
case GroupBy_Bitrate:
|
||||
q->AddWhere("bitrate", item->key);
|
||||
break;
|
||||
case GroupBy_Format:
|
||||
q->AddWhere("filetype", item->metadata.filetype());
|
||||
q->AddWhere("samplerate", item->metadata.samplerate());
|
||||
q->AddWhere("bitdepth", item->metadata.bitdepth());
|
||||
break;
|
||||
case GroupBy_None:
|
||||
qLog(Error) << "Unknown GroupBy type" << type << "used in filter";
|
||||
break;
|
||||
@@ -968,84 +991,98 @@ CollectionItem *CollectionModel::InitItem(GroupBy type, bool signal, CollectionI
|
||||
CollectionItem *CollectionModel::ItemFromQuery(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, const SqlRow &row, int container_level) {
|
||||
|
||||
CollectionItem *item = InitItem(type, signal, parent, container_level);
|
||||
int year(0), effective_originalyear(0), disc(0), bitrate(0), samplerate(0), bitdepth(0);
|
||||
|
||||
switch (type) {
|
||||
case GroupBy_AlbumArtist:
|
||||
case GroupBy_Artist:
|
||||
case GroupBy_Album:
|
||||
case GroupBy_Composer:
|
||||
case GroupBy_Performer:
|
||||
case GroupBy_Grouping:
|
||||
case GroupBy_Genre:
|
||||
item->key = row.value(0).toString();
|
||||
item->display_text = TextOrUnknown(item->key);
|
||||
item->sort_text = SortTextForArtist(item->key);
|
||||
break;
|
||||
|
||||
case GroupBy_YearAlbum:
|
||||
year = qMax(0, row.value(0).toInt());
|
||||
case GroupBy_OriginalYear:{
|
||||
int year = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(year);
|
||||
item->sort_text = SortTextForNumber(year) + " ";
|
||||
break;
|
||||
}
|
||||
case GroupBy_Year:{
|
||||
int year = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(year);
|
||||
item->sort_text = SortTextForNumber(year) + " ";
|
||||
break;
|
||||
}
|
||||
case GroupBy_OriginalYearAlbum:{
|
||||
item->metadata.set_year(row.value(0).toInt());
|
||||
item->metadata.set_originalyear(row.value(1).toInt());
|
||||
item->metadata.set_album(row.value(2).toString());
|
||||
item->metadata.set_grouping(row.value(3).toString());
|
||||
int effective_originalyear = qMax(0, item->metadata.effective_originalyear());
|
||||
item->key = PrettyYearAlbum(effective_originalyear, item->metadata.album());
|
||||
item->sort_text = SortTextForNumber(effective_originalyear) + item->metadata.grouping() + item->metadata.album();
|
||||
break;
|
||||
}
|
||||
case GroupBy_YearAlbum:{
|
||||
int year = qMax(0, row.value(0).toInt());
|
||||
item->metadata.set_year(row.value(0).toInt());
|
||||
item->metadata.set_album(row.value(1).toString());
|
||||
item->metadata.set_grouping(row.value(2).toString());
|
||||
item->key = PrettyYearAlbum(year, item->metadata.album());
|
||||
item->sort_text = SortTextForNumber(year) + item->metadata.grouping() + item->metadata.album();
|
||||
break;
|
||||
}
|
||||
|
||||
case GroupBy_OriginalYearAlbum:
|
||||
item->metadata.set_year(row.value(0).toInt());
|
||||
item->metadata.set_originalyear(row.value(1).toInt());
|
||||
item->metadata.set_album(row.value(2).toString());
|
||||
item->metadata.set_grouping(row.value(3).toString());
|
||||
effective_originalyear = qMax(0, item->metadata.effective_originalyear());
|
||||
item->key = PrettyYearAlbum(effective_originalyear, item->metadata.album());
|
||||
item->sort_text = SortTextForNumber(effective_originalyear) + item->metadata.grouping() + item->metadata.album();
|
||||
case GroupBy_Format:{
|
||||
item->metadata.set_filetype(Song::FileType(row.value(0).toInt()));
|
||||
item->metadata.set_samplerate(row.value(1).toInt());
|
||||
item->metadata.set_bitdepth(row.value(2).toInt());
|
||||
if (item->metadata.samplerate() <= 0) {
|
||||
item->key = item->metadata.TextForFiletype();
|
||||
}
|
||||
else {
|
||||
if (item->metadata.bitdepth() <= 0) {
|
||||
item->key = QString("%1 (%2)").arg(item->metadata.TextForFiletype()).arg(QString::number(item->metadata.samplerate() / 1000.0, 'G', 5));
|
||||
}
|
||||
else {
|
||||
item->key = QString("%1 (%2/%3)").arg(item->metadata.TextForFiletype()).arg(QString::number(item->metadata.samplerate() / 1000.0, 'G', 5)).arg(QString::number(item->metadata.bitdepth()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GroupBy_Year:
|
||||
year = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(year);
|
||||
item->sort_text = SortTextForNumber(year) + " ";
|
||||
break;
|
||||
case GroupBy_OriginalYear:
|
||||
year = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(year);
|
||||
item->sort_text = SortTextForNumber(year) + " ";
|
||||
break;
|
||||
case GroupBy_Composer:
|
||||
case GroupBy_Performer:
|
||||
case GroupBy_Grouping:
|
||||
case GroupBy_Genre:
|
||||
case GroupBy_Album:
|
||||
case GroupBy_AlbumArtist:
|
||||
item->key = row.value(0).toString();
|
||||
item->display_text = TextOrUnknown(item->key);
|
||||
item->sort_text = SortTextForArtist(item->key);
|
||||
break;
|
||||
|
||||
case GroupBy_Disc:
|
||||
disc = row.value(0).toInt();
|
||||
case GroupBy_Disc:{
|
||||
int disc = row.value(0).toInt();
|
||||
item->key = QString::number(disc);
|
||||
item->sort_text = SortTextForNumber(disc);
|
||||
break;
|
||||
|
||||
}
|
||||
case GroupBy_FileType:
|
||||
item->metadata.set_filetype(Song::FileType(row.value(0).toInt()));
|
||||
item->key = item->metadata.TextForFiletype();
|
||||
break;
|
||||
|
||||
case GroupBy_Bitrate:
|
||||
bitrate = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(bitrate);
|
||||
item->sort_text = SortTextForNumber(bitrate) + " ";
|
||||
break;
|
||||
|
||||
case GroupBy_Samplerate:
|
||||
samplerate = qMax(0, row.value(0).toInt());
|
||||
case GroupBy_Samplerate:{
|
||||
int samplerate = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(samplerate);
|
||||
item->sort_text = SortTextForNumber(samplerate) + " ";
|
||||
break;
|
||||
|
||||
case GroupBy_Bitdepth:
|
||||
bitdepth = qMax(0, row.value(0).toInt());
|
||||
}
|
||||
case GroupBy_Bitdepth:{
|
||||
int bitdepth = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(bitdepth);
|
||||
item->sort_text = SortTextForNumber(bitdepth) + " ";
|
||||
break;
|
||||
|
||||
}
|
||||
case GroupBy_Bitrate:{
|
||||
int bitrate = qMax(0, row.value(0).toInt());
|
||||
item->key = QString::number(bitrate);
|
||||
item->sort_text = SortTextForNumber(bitrate) + " ";
|
||||
break;
|
||||
}
|
||||
case GroupBy_None:
|
||||
item->metadata.InitFromQuery(row, true);
|
||||
item->key = item->metadata.title();
|
||||
@@ -1063,7 +1100,6 @@ CollectionItem *CollectionModel::ItemFromQuery(GroupBy type, bool signal, bool c
|
||||
CollectionItem *CollectionModel::ItemFromSong(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, const Song &s, int container_level) {
|
||||
|
||||
CollectionItem *item = InitItem(type, signal, parent, container_level);
|
||||
int year(0), originalyear(0), effective_originalyear(0), bitrate(0), samplerate(0), bitdepth(0);
|
||||
|
||||
switch (type) {
|
||||
case GroupBy_Artist:
|
||||
@@ -1072,37 +1108,37 @@ CollectionItem *CollectionModel::ItemFromSong(GroupBy type, bool signal, bool cr
|
||||
item->sort_text = SortTextForArtist(item->key);
|
||||
break;
|
||||
|
||||
case GroupBy_YearAlbum:
|
||||
year = qMax(0, s.year());
|
||||
case GroupBy_YearAlbum:{
|
||||
int year = qMax(0, s.year());
|
||||
item->metadata.set_year(year);
|
||||
item->metadata.set_album(s.album());
|
||||
item->key = PrettyYearAlbum(year, s.album());
|
||||
item->sort_text = SortTextForNumber(year) + s.grouping() + s.album();
|
||||
break;
|
||||
|
||||
case GroupBy_OriginalYearAlbum:
|
||||
year = qMax(0, s.year());
|
||||
originalyear = qMax(0, s.originalyear());
|
||||
effective_originalyear = qMax(0, s.effective_originalyear());
|
||||
}
|
||||
case GroupBy_OriginalYearAlbum:{
|
||||
int year = qMax(0, s.year());
|
||||
int originalyear = qMax(0, s.originalyear());
|
||||
int effective_originalyear = qMax(0, s.effective_originalyear());
|
||||
item->metadata.set_year(year);
|
||||
item->metadata.set_originalyear(originalyear);
|
||||
item->metadata.set_album(s.album());
|
||||
item->key = PrettyYearAlbum(effective_originalyear, s.album());
|
||||
item->sort_text = SortTextForNumber(effective_originalyear) + s.grouping() + s.album();
|
||||
break;
|
||||
|
||||
case GroupBy_Year:
|
||||
year = qMax(0, s.year());
|
||||
}
|
||||
case GroupBy_Year:{
|
||||
int year = qMax(0, s.year());
|
||||
item->key = QString::number(year);
|
||||
item->sort_text = SortTextForNumber(year) + " ";
|
||||
break;
|
||||
|
||||
case GroupBy_OriginalYear:
|
||||
year = qMax(0, s.effective_originalyear());
|
||||
}
|
||||
case GroupBy_OriginalYear:{
|
||||
int year = qMax(0, s.effective_originalyear());
|
||||
item->key = QString::number(year);
|
||||
item->sort_text = SortTextForNumber(year) + " ";
|
||||
break;
|
||||
|
||||
}
|
||||
case GroupBy_Composer: item->key = s.composer();
|
||||
case GroupBy_Performer: item->key = s.performer();
|
||||
case GroupBy_Grouping: item->key = s.grouping();
|
||||
@@ -1123,24 +1159,41 @@ CollectionItem *CollectionModel::ItemFromSong(GroupBy type, bool signal, bool cr
|
||||
item->key = s.TextForFiletype();
|
||||
break;
|
||||
|
||||
case GroupBy_Bitrate:
|
||||
bitrate = qMax(0, s.bitrate());
|
||||
case GroupBy_Bitrate:{
|
||||
int bitrate = qMax(0, s.bitrate());
|
||||
item->key = QString::number(bitrate);
|
||||
item->sort_text = SortTextForNumber(bitrate) + " ";
|
||||
break;
|
||||
|
||||
case GroupBy_Samplerate:
|
||||
samplerate = qMax(0, s.samplerate());
|
||||
}
|
||||
case GroupBy_Samplerate:{
|
||||
int samplerate = qMax(0, s.samplerate());
|
||||
item->key = QString::number(samplerate);
|
||||
item->sort_text = SortTextForNumber(samplerate) + " ";
|
||||
break;
|
||||
|
||||
case GroupBy_Bitdepth:
|
||||
bitdepth = qMax(0, s.bitdepth());
|
||||
}
|
||||
case GroupBy_Bitdepth:{
|
||||
int bitdepth = qMax(0, s.bitdepth());
|
||||
item->key = QString::number(bitdepth);
|
||||
item->sort_text = SortTextForNumber(bitdepth) + " ";
|
||||
break;
|
||||
|
||||
}
|
||||
case GroupBy_Format:{
|
||||
item->metadata.set_filetype(s.filetype());
|
||||
item->metadata.set_samplerate(s.samplerate());
|
||||
item->metadata.set_bitdepth(s.bitdepth());
|
||||
if (s.samplerate() <= 0) {
|
||||
item->key = s.TextForFiletype();
|
||||
}
|
||||
else {
|
||||
if (s.bitdepth() <= 0) {
|
||||
item->key = QString("%1 (%2)").arg(s.TextForFiletype()).arg(QString::number(s.samplerate() / 1000.0, 'G', 5));
|
||||
}
|
||||
else {
|
||||
item->key = QString("%1 (%2/%3)").arg(s.TextForFiletype()).arg(QString::number(s.samplerate() / 1000.0, 'G', 5)).arg(QString::number(s.bitdepth()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GroupBy_None:
|
||||
item->metadata = s;
|
||||
item->key = s.title();
|
||||
|
||||
@@ -100,7 +100,8 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
GroupBy_OriginalYearAlbum = 13,
|
||||
GroupBy_OriginalYear = 14,
|
||||
GroupBy_Samplerate = 15,
|
||||
GroupBy_Bitdepth = 16
|
||||
GroupBy_Bitdepth = 16,
|
||||
GroupBy_Format = 17
|
||||
};
|
||||
|
||||
struct Grouping {
|
||||
|
||||
@@ -655,7 +655,7 @@ SongList CollectionView::GetSelectedSongs() const {
|
||||
void CollectionView::Organise() {
|
||||
|
||||
if (!organise_dialog_)
|
||||
organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
|
||||
organise_dialog_.reset(new OrganiseDialog(app_->task_manager(), app_->collection_backend()));
|
||||
|
||||
organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
|
||||
organise_dialog_->SetCopy(false);
|
||||
@@ -664,18 +664,24 @@ void CollectionView::Organise() {
|
||||
else {
|
||||
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CollectionView::EditTracks() {
|
||||
|
||||
if (!edit_tag_dialog_) {
|
||||
edit_tag_dialog_.reset(new EditTagDialog(app_, this));
|
||||
connect(edit_tag_dialog_.get(), SIGNAL(Error(QString)), SLOT(EditTagError(QString)));
|
||||
}
|
||||
edit_tag_dialog_->SetSongs(GetSelectedSongs());
|
||||
edit_tag_dialog_->show();
|
||||
|
||||
}
|
||||
|
||||
void CollectionView::EditTagError(const QString &message) {
|
||||
emit Error(message);
|
||||
}
|
||||
|
||||
void CollectionView::CopyToDevice() {
|
||||
#ifndef Q_OS_WIN
|
||||
if (!organise_dialog_)
|
||||
|
||||
@@ -98,12 +98,15 @@ class CollectionView : public AutoExpandingTreeView {
|
||||
void SaveFocus();
|
||||
void RestoreFocus();
|
||||
|
||||
signals:
|
||||
void EditTagError(const QString &message);
|
||||
|
||||
signals:
|
||||
void ShowConfigDialog();
|
||||
|
||||
void TotalSongCountUpdated_();
|
||||
void TotalArtistCountUpdated_();
|
||||
void TotalAlbumCountUpdated_();
|
||||
void Error(const QString &message);
|
||||
|
||||
protected:
|
||||
// QWidget
|
||||
|
||||
@@ -684,6 +684,8 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
|
||||
QString biggest_path;
|
||||
|
||||
for (const QString &path : filtered) {
|
||||
if (stop_requested_) return QString();
|
||||
|
||||
QImage image(path);
|
||||
if (image.isNull()) continue;
|
||||
|
||||
|
||||
@@ -69,11 +69,8 @@ class GroupByDialogPrivate {
|
||||
typedef multi_index_container<
|
||||
Mapping,
|
||||
indexed_by<
|
||||
ordered_unique<tag<tag_index>,
|
||||
member<Mapping, int, &Mapping::combo_box_index> >,
|
||||
ordered_unique<tag<tag_group_by>,
|
||||
member<Mapping, CollectionModel::GroupBy,
|
||||
&Mapping::group_by> > > > MappingContainer;
|
||||
ordered_unique<tag<tag_index>, member<Mapping, int, &Mapping::combo_box_index> >,
|
||||
ordered_unique<tag<tag_group_by>, member<Mapping, CollectionModel::GroupBy, &Mapping::group_by> > > > MappingContainer;
|
||||
|
||||
public:
|
||||
MappingContainer mapping_;
|
||||
@@ -85,33 +82,35 @@ GroupByDialog::GroupByDialog(QWidget *parent) : QDialog(parent), ui_(new Ui_Grou
|
||||
Reset();
|
||||
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_None, 0));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Album, 1));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Artist, 2));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_AlbumArtist, 3));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Composer, 4));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_FileType, 5));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Artist, 1));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_AlbumArtist, 2));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Album, 3));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Disc, 4));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Format, 5));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Genre, 6));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Year, 7));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_OriginalYear, 8));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_YearAlbum, 9));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_OriginalYearAlbum, 10));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Bitrate, 11));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Samplerate, 12));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Bitdepth, 13));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Disc, 14));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Performer, 15));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Grouping, 16));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_OriginalYear, 7));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Year, 8));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_OriginalYearAlbum, 9));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_YearAlbum, 10));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Composer, 11));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Performer, 12));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Grouping, 13));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_FileType, 14));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Samplerate, 15));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Bitdepth, 16));
|
||||
p_->mapping_.insert(Mapping(CollectionModel::GroupBy_Bitrate, 17));
|
||||
|
||||
connect(ui_->buttonbox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), SLOT(Reset()));
|
||||
|
||||
resize(sizeHint());
|
||||
|
||||
}
|
||||
|
||||
GroupByDialog::~GroupByDialog() {}
|
||||
|
||||
void GroupByDialog::Reset() {
|
||||
ui_->combobox_first->setCurrentIndex(2); // Artist
|
||||
ui_->combobox_second->setCurrentIndex(1); // Album
|
||||
ui_->combobox_first->setCurrentIndex(2); // Album Artist
|
||||
ui_->combobox_second->setCurrentIndex(3); // Album
|
||||
ui_->combobox_third->setCurrentIndex(0); // None
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<iconset resource="../../data/icons.qrc">
|
||||
<normaloff>:/icons/64x64/strawberry.png</normaloff>:/icons/64x64/strawberry.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="layout_groupbydialog">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
@@ -48,11 +48,6 @@
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Artist</string>
|
||||
@@ -65,12 +60,17 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -78,11 +78,6 @@
|
||||
<string>Genre</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year</string>
|
||||
@@ -90,7 +85,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year - Album</string>
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -100,7 +95,27 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
<string>Year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -115,17 +130,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
<string>Bitrate</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
@@ -144,11 +149,6 @@
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Artist</string>
|
||||
@@ -161,12 +161,17 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -174,11 +179,6 @@
|
||||
<string>Genre</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year</string>
|
||||
@@ -186,7 +186,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year - Album</string>
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -196,7 +196,27 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
<string>Year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -211,17 +231,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
<string>Bitrate</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
@@ -240,11 +250,6 @@
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Artist</string>
|
||||
@@ -257,12 +262,17 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -270,11 +280,6 @@
|
||||
<string>Genre</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year</string>
|
||||
@@ -282,7 +287,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year - Album</string>
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -292,7 +297,27 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
<string>Year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -307,17 +332,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
<string>Bitrate</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
@@ -326,7 +341,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<spacer name="spacer_bottom">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
|
||||
@@ -77,6 +77,9 @@ QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g)
|
||||
case CollectionModel::GroupBy_None: {
|
||||
return tr("None");
|
||||
}
|
||||
case CollectionModel::GroupBy_AlbumArtist: {
|
||||
return tr("Album artist");
|
||||
}
|
||||
case CollectionModel::GroupBy_Artist: {
|
||||
return tr("Artist");
|
||||
}
|
||||
@@ -95,9 +98,6 @@ QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g)
|
||||
case CollectionModel::GroupBy_Genre: {
|
||||
return tr("Genre");
|
||||
}
|
||||
case CollectionModel::GroupBy_AlbumArtist: {
|
||||
return tr("Album artist");
|
||||
}
|
||||
case CollectionModel::GroupBy_FileType: {
|
||||
return tr("File type");
|
||||
}
|
||||
@@ -107,15 +107,15 @@ QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g)
|
||||
case CollectionModel::GroupBy_Grouping: {
|
||||
return tr("Grouping");
|
||||
}
|
||||
case CollectionModel::GroupBy_Bitrate: {
|
||||
return tr("Bitrate");
|
||||
}
|
||||
case CollectionModel::GroupBy_Samplerate: {
|
||||
return tr("Sample rate");
|
||||
}
|
||||
case CollectionModel::GroupBy_Bitdepth: {
|
||||
return tr("Bit depth");
|
||||
}
|
||||
case CollectionModel::GroupBy_Bitrate: {
|
||||
return tr("Bitrate");
|
||||
}
|
||||
case CollectionModel::GroupBy_Disc: {
|
||||
return tr("Disc");
|
||||
}
|
||||
@@ -125,6 +125,9 @@ QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g)
|
||||
case CollectionModel::GroupBy_OriginalYear: {
|
||||
return tr("Original year");
|
||||
}
|
||||
case CollectionModel::GroupBy_Format: {
|
||||
return tr("Format");
|
||||
}
|
||||
default: { return tr("Unknown"); }
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#cmakedefine HAVE_SPARKLE
|
||||
#cmakedefine HAVE_CHROMAPRINT
|
||||
#cmakedefine HAVE_TAGLIB_DSFFILE
|
||||
#cmakedefine HAVE_DZMEDIA
|
||||
#cmakedefine HAVE_GLOBALSHORTCUTS
|
||||
#cmakedefine IMOBILEDEVICE_USES_UDIDS
|
||||
#cmakedefine USE_INSTALL_PREFIX
|
||||
@@ -48,10 +47,8 @@
|
||||
#cmakedefine HAVE_VLC
|
||||
#cmakedefine HAVE_XINE
|
||||
#cmakedefine HAVE_PHONON
|
||||
#cmakedefine HAVE_DEEZER
|
||||
|
||||
#cmakedefine HAVE_STREAM_TIDAL
|
||||
#cmakedefine HAVE_STREAM_DEEZER
|
||||
|
||||
#cmakedefine HAVE_KEYSYMDEF_H
|
||||
#cmakedefine HAVE_XF86KEYSYM_H
|
||||
@@ -60,5 +57,6 @@
|
||||
|
||||
#define USE_BUNDLE_DIR "${USE_BUNDLE_DIR}"
|
||||
|
||||
#endif // CONFIG_H_IN
|
||||
#cmakedefine HAVE_TRANSLATIONS
|
||||
|
||||
#endif // CONFIG_H_IN
|
||||
|
||||
@@ -232,9 +232,9 @@ void ContextView::NoSong() {
|
||||
"font-weight: Regular;"
|
||||
);
|
||||
|
||||
ui_->label_stop_top->setText("No song playing");
|
||||
ui_->label_stop_top->setText(tr("No song playing"));
|
||||
|
||||
QString html = QString(
|
||||
QString html = tr(
|
||||
"%1 songs<br />\n"
|
||||
"%2 artists<br />\n"
|
||||
"%3 albums<br />\n"
|
||||
@@ -404,7 +404,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(QString("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
|
||||
ui_->label_play_albums->setText(tr("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
|
||||
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);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>927</height>
|
||||
<height>900</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="layout_container">
|
||||
@@ -57,8 +57,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>923</height>
|
||||
<width>394</width>
|
||||
<height>894</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -175,8 +175,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>923</height>
|
||||
<width>394</width>
|
||||
<height>894</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
@@ -526,6 +526,12 @@
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -32,19 +32,17 @@
|
||||
#include "appearance.h"
|
||||
#include "settings/appearancesettingspage.h"
|
||||
|
||||
const char *Appearance::kUseCustomColorSet = "use-custom-set";
|
||||
const char *Appearance::kForegroundColor = "foreground-color";
|
||||
const char *Appearance::kBackgroundColor = "background-color";
|
||||
|
||||
const QPalette Appearance::kDefaultPalette = QPalette();
|
||||
|
||||
Appearance::Appearance(QObject *parent) : QObject(parent) {
|
||||
|
||||
QPalette p = QApplication::palette();
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
|
||||
QPalette p = QApplication::palette();
|
||||
background_color_ = s.value(kBackgroundColor, p.color(QPalette::WindowText)).value<QColor>();
|
||||
foreground_color_ = s.value(kForegroundColor, p.color(QPalette::Window)).value<QColor>();
|
||||
background_color_ = s.value(AppearanceSettingsPage::kBackgroundColor, p.color(QPalette::WindowText)).value<QColor>();
|
||||
foreground_color_ = s.value(AppearanceSettingsPage::kForegroundColor, p.color(QPalette::Window)).value<QColor>();
|
||||
s.endGroup();
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +50,9 @@ void Appearance::LoadUserTheme() {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
|
||||
bool use_a_custom_color_set = s.value(kUseCustomColorSet).toBool();
|
||||
bool use_a_custom_color_set = s.value(AppearanceSettingsPage::kUseCustomColorSet).toBool();
|
||||
s.endGroup();
|
||||
|
||||
if (!use_a_custom_color_set) return;
|
||||
|
||||
ChangeForegroundColor(foreground_color_);
|
||||
|
||||
@@ -30,18 +30,14 @@
|
||||
class Appearance : public QObject {
|
||||
public:
|
||||
explicit Appearance(QObject *parent = nullptr);
|
||||
// Load the user preferred theme, which could the default system theme or a custom set of colors that user has chosen
|
||||
|
||||
static const QPalette kDefaultPalette;
|
||||
|
||||
void LoadUserTheme();
|
||||
void ResetToSystemDefaultTheme();
|
||||
void ChangeForegroundColor(const QColor &color);
|
||||
void ChangeBackgroundColor(const QColor &color);
|
||||
|
||||
static const char *kSettingsGroup;
|
||||
static const char *kUseCustomColorSet;
|
||||
static const char *kForegroundColor;
|
||||
static const char *kBackgroundColor;
|
||||
static const QPalette kDefaultPalette;
|
||||
|
||||
private:
|
||||
QColor foreground_color_;
|
||||
QColor background_color_;
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
#include "lyrics/lyricsproviders.h"
|
||||
#include "lyrics/lyricsprovider.h"
|
||||
#include "lyrics/auddlyricsprovider.h"
|
||||
#include "lyrics/apiseedslyricsprovider.h"
|
||||
|
||||
#include "internet/internetservices.h"
|
||||
#include "internet/internetsearch.h"
|
||||
@@ -66,9 +65,6 @@
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
# include "tidal/tidalservice.h"
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
# include "deezer/deezerservice.h"
|
||||
#endif
|
||||
|
||||
#include "scrobbler/audioscrobbler.h"
|
||||
|
||||
@@ -120,24 +116,17 @@ class ApplicationImpl {
|
||||
lyrics_providers_([=]() {
|
||||
LyricsProviders *lyrics_providers = new LyricsProviders(app);
|
||||
lyrics_providers->AddProvider(new AuddLyricsProvider(app));
|
||||
lyrics_providers->AddProvider(new APISeedsLyricsProvider(app));
|
||||
return lyrics_providers;
|
||||
}),
|
||||
internet_services_([=]() {
|
||||
InternetServices *internet_services = new InternetServices(app);
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
internet_services->AddService(new TidalService(app, internet_services));
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
internet_services->AddService(new DeezerService(app, internet_services));
|
||||
#endif
|
||||
return internet_services;
|
||||
}),
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
tidal_search_([=]() { return new InternetSearch(app, Song::Source_Tidal, app); }),
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
deezer_search_([=]() { return new InternetSearch(app, Song::Source_Deezer, app); }),
|
||||
#endif
|
||||
scrobbler_([=]() { return new AudioScrobbler(app, app); })
|
||||
{}
|
||||
@@ -161,9 +150,6 @@ class ApplicationImpl {
|
||||
Lazy<InternetServices> internet_services_;
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
Lazy<InternetSearch> tidal_search_;
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
Lazy<InternetSearch> deezer_search_;
|
||||
#endif
|
||||
Lazy<AudioScrobbler> scrobbler_;
|
||||
|
||||
@@ -236,7 +222,4 @@ InternetServices *Application::internet_services() const { return p_->internet_s
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
InternetSearch *Application::tidal_search() const { return p_->tidal_search_.get(); }
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
InternetSearch *Application::deezer_search() const { return p_->deezer_search_.get(); }
|
||||
#endif
|
||||
AudioScrobbler *Application::scrobbler() const { return p_->scrobbler_.get(); }
|
||||
|
||||
@@ -96,9 +96,6 @@ class Application : public QObject {
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
InternetSearch *tidal_search() const;
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
InternetSearch *deezer_search() const;
|
||||
#endif
|
||||
|
||||
AudioScrobbler *scrobbler() const;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2013, 2017-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
|
||||
@@ -21,28 +21,64 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
#include <QSize>
|
||||
#include <QtDebug>
|
||||
#include <QStandardPaths>
|
||||
#include <QSettings>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "settings/appearancesettingspage.h"
|
||||
#include "iconloader.h"
|
||||
|
||||
bool IconLoader::system_icons_ = false;
|
||||
bool IconLoader::custom_icons_ = false;
|
||||
|
||||
void IconLoader::Init() {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
|
||||
system_icons_ = s.value("system_icons", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
QDir dir;
|
||||
if (dir.exists(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/icons")) {
|
||||
custom_icons_ = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QIcon IconLoader::Load(const QString &name, const int size) {
|
||||
|
||||
QIcon ret;
|
||||
|
||||
if (name.isEmpty()) {
|
||||
qLog(Error) << "Icon name is empty!";
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<int> sizes;
|
||||
sizes.clear();
|
||||
if (size == 0) { sizes << 22 << 32 << 48 << 64; }
|
||||
else sizes << size;
|
||||
|
||||
if (name.isEmpty()) {
|
||||
qLog(Error) << "Icon name is empty!";
|
||||
return ret;
|
||||
if (system_icons_) {
|
||||
ret = QIcon::fromTheme(name);
|
||||
if (!ret.isNull()) return ret;
|
||||
qLog(Warning) << "Couldn't load icon" << name << "from system theme icons.";
|
||||
}
|
||||
|
||||
if (custom_icons_) {
|
||||
QString custom_icon_path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/icons/%1x%2/%3.png";
|
||||
for (int s : sizes) {
|
||||
QString filename(custom_icon_path.arg(s).arg(s).arg(name));
|
||||
if (QFile::exists(filename)) ret.addFile(filename, QSize(s, s));
|
||||
}
|
||||
if (!ret.isNull()) return ret;
|
||||
qLog(Warning) << "Couldn't load icon" << name << "from custom icons.";
|
||||
}
|
||||
|
||||
const QString path(":/icons/%1x%2/%3.png");
|
||||
@@ -51,13 +87,6 @@ QIcon IconLoader::Load(const QString &name, const int size) {
|
||||
if (QFile::exists(filename)) ret.addFile(filename, QSize(s, s));
|
||||
}
|
||||
|
||||
// Load icon from system theme only if it hasn't been found
|
||||
if (ret.isNull()) {
|
||||
ret = QIcon::fromTheme(name);
|
||||
if (!ret.isNull()) return ret;
|
||||
qLog(Warning) << "Couldn't load icon" << name;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2013, 2017-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
|
||||
@@ -27,10 +28,12 @@
|
||||
|
||||
class IconLoader {
|
||||
public:
|
||||
static void Init();
|
||||
static QIcon Load(const QString &name, const int size = 0);
|
||||
private:
|
||||
private:
|
||||
IconLoader() {}
|
||||
static bool system_icons_;
|
||||
static bool custom_icons_;
|
||||
};
|
||||
|
||||
#endif // ICONLOADER_H
|
||||
|
||||
#endif // ICONLOADER_H
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
#include "organise/organisedialog.h"
|
||||
#include "widgets/fancytabwidget.h"
|
||||
#include "widgets/playingwidget.h"
|
||||
#include "widgets/sliderwidget.h"
|
||||
#include "widgets/volumeslider.h"
|
||||
#include "widgets/fileview.h"
|
||||
#include "widgets/multiloadingindicator.h"
|
||||
#include "widgets/osd.h"
|
||||
@@ -131,13 +131,11 @@
|
||||
#include "transcoder/transcodedialog.h"
|
||||
#include "settings/settingsdialog.h"
|
||||
#include "settings/behavioursettingspage.h"
|
||||
#include "settings/backendsettingspage.h"
|
||||
#include "settings/playlistsettingspage.h"
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
# include "settings/tidalsettingspage.h"
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
# include "settings/deezersettingspage.h"
|
||||
#endif
|
||||
|
||||
#include "internet/internetservices.h"
|
||||
#include "internet/internetservice.h"
|
||||
@@ -199,15 +197,12 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
//organise_dialog_(new OrganiseDialog(app_->task_manager())),
|
||||
equalizer_(new Equalizer),
|
||||
organise_dialog_([=]() {
|
||||
OrganiseDialog *dialog = new OrganiseDialog(app->task_manager());
|
||||
OrganiseDialog *dialog = new OrganiseDialog(app->task_manager(), app->collection_backend());
|
||||
dialog->SetDestinationModel(app->collection()->model()->directory_model());
|
||||
return dialog;
|
||||
}),
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
tidal_search_view_(new InternetSearchView(app_, app_->tidal_search(), TidalSettingsPage::kSettingsGroup, SettingsDialog::Page_Tidal, this)),
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
deezer_search_view_(new InternetSearchView(app_, app_->deezer_search(), DeezerSettingsPage::kSettingsGroup, SettingsDialog::Page_Deezer, this)),
|
||||
#endif
|
||||
playlist_menu_(new QMenu(this)),
|
||||
playlist_add_to_another_(nullptr),
|
||||
@@ -219,9 +214,11 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
was_maximized_(true),
|
||||
saved_playback_position_(0),
|
||||
saved_playback_state_(Engine::Empty),
|
||||
doubleclick_addmode_(AddBehaviour_Append),
|
||||
doubleclick_playmode_(PlayBehaviour_Never),
|
||||
menu_playmode_(PlayBehaviour_Never) {
|
||||
playing_widget_(true),
|
||||
doubleclick_addmode_(BehaviourSettingsPage::AddBehaviour_Append),
|
||||
doubleclick_playmode_(BehaviourSettingsPage::PlayBehaviour_Never),
|
||||
menu_playmode_(BehaviourSettingsPage::PlayBehaviour_Never)
|
||||
{
|
||||
|
||||
qLog(Debug) << "Starting";
|
||||
|
||||
@@ -248,27 +245,20 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
context_view_->SetApplication(app_, collection_view_->view(), album_cover_choice_controller_);
|
||||
ui_->widget_playing->SetApplication(app_, album_cover_choice_controller_);
|
||||
|
||||
int volume = app_->player()->GetVolume();
|
||||
ui_->volume->setValue(volume);
|
||||
VolumeChanged(volume);
|
||||
|
||||
// Initialise the search widget
|
||||
StyleHelper::setBaseColor(palette().color(QPalette::Highlight).darker());
|
||||
|
||||
// Add tabs to the fancy tab widget
|
||||
ui_->tabs->addTab(context_view_, IconLoader::Load("strawberry"), "Context");
|
||||
ui_->tabs->addTab(collection_view_, IconLoader::Load("vinyl"), "Collection");
|
||||
ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), "Files");
|
||||
ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist"), "Playlists");
|
||||
ui_->tabs->addTab(queue_view_, IconLoader::Load("footsteps"), "Queue");
|
||||
ui_->tabs->addTab(context_view_, IconLoader::Load("strawberry"), tr("Context"));
|
||||
ui_->tabs->addTab(collection_view_, IconLoader::Load("vinyl"), tr("Collection"));
|
||||
ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), tr("Files"));
|
||||
ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist"), tr("Playlists"));
|
||||
ui_->tabs->addTab(queue_view_, IconLoader::Load("footsteps"), tr("Queue"));
|
||||
#ifndef Q_OS_WIN
|
||||
ui_->tabs->addTab(device_view_, IconLoader::Load("device"), "Devices");
|
||||
ui_->tabs->addTab(device_view_, IconLoader::Load("device"), tr("Devices"));
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), "Tidal");
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
ui_->tabs->addTab(deezer_search_view_, IconLoader::Load("deezer"), "Deezer");
|
||||
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal"));
|
||||
#endif
|
||||
|
||||
// Add the playing widget to the fancy tab widget
|
||||
@@ -286,6 +276,9 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
app_->player()->SetAnalyzer(ui_->analyzer);
|
||||
app_->player()->SetEqualizer(equalizer_.get());
|
||||
app_->player()->Init();
|
||||
int volume = app_->player()->GetVolume();
|
||||
ui_->volume->setValue(volume);
|
||||
VolumeChanged(volume);
|
||||
|
||||
// Models
|
||||
qLog(Debug) << "Creating models";
|
||||
@@ -499,6 +492,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
// Collection connections
|
||||
connect(collection_view_->view(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
|
||||
connect(collection_view_->view(), SIGNAL(ShowConfigDialog()), SLOT(ShowCollectionConfig()));
|
||||
connect(collection_view_->view(), SIGNAL(Error(QString)), SLOT(ShowErrorDialog(QString)));
|
||||
connect(app_->collection_model(), SIGNAL(TotalSongCountUpdated(int)), collection_view_->view(), SLOT(TotalSongCountUpdated(int)));
|
||||
connect(app_->collection_model(), SIGNAL(TotalArtistCountUpdated(int)), collection_view_->view(), SLOT(TotalArtistCountUpdated(int)));
|
||||
connect(app_->collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), collection_view_->view(), SLOT(TotalAlbumCountUpdated(int)));
|
||||
@@ -544,9 +538,6 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
connect(tidal_search_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
connect(deezer_search_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
|
||||
#endif
|
||||
|
||||
// Playlist menu
|
||||
playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, SLOT(PlaylistPlay()));
|
||||
@@ -819,36 +810,43 @@ void MainWindow::ReloadSettings() {
|
||||
#endif
|
||||
|
||||
settings.beginGroup(BehaviourSettingsPage::kSettingsGroup);
|
||||
doubleclick_addmode_ = AddBehaviour(settings.value("doubleclick_addmode", AddBehaviour_Append).toInt());
|
||||
doubleclick_playmode_ = PlayBehaviour(settings.value("doubleclick_playmode", PlayBehaviour_IfStopped).toInt());
|
||||
doubleclick_playlist_addmode_ = PlaylistAddBehaviour(settings.value("doubleclick_playlist_addmode", PlaylistAddBehaviour_Play).toInt());
|
||||
menu_playmode_ = PlayBehaviour(settings.value("menu_playmode", PlayBehaviour_IfStopped).toInt());
|
||||
playing_widget_ = settings.value("playing_widget", true).toBool();
|
||||
if (playing_widget_ != ui_->widget_playing->IsEnabled()) TabSwitched();
|
||||
doubleclick_addmode_ = BehaviourSettingsPage::AddBehaviour(settings.value("doubleclick_addmode", BehaviourSettingsPage::AddBehaviour_Append).toInt());
|
||||
doubleclick_playmode_ = BehaviourSettingsPage::PlayBehaviour(settings.value("doubleclick_playmode", BehaviourSettingsPage::PlayBehaviour_IfStopped).toInt());
|
||||
doubleclick_playlist_addmode_ = BehaviourSettingsPage::PlaylistAddBehaviour(settings.value("doubleclick_playlist_addmode", BehaviourSettingsPage::PlaylistAddBehaviour_Play).toInt());
|
||||
menu_playmode_ = BehaviourSettingsPage::PlayBehaviour(settings.value("menu_playmode", BehaviourSettingsPage::PlayBehaviour_IfStopped).toInt());
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(kSettingsGroup);
|
||||
album_cover_choice_controller_->search_cover_auto_action()->setChecked(settings.value("search_for_cover_auto", true).toBool());
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(BackendSettingsPage::kSettingsGroup);
|
||||
bool volume_control = settings.value("volume_control", true).toBool();
|
||||
settings.endGroup();
|
||||
if (volume_control != ui_->volume->isEnabled()) {
|
||||
ui_->volume->SetEnabled(volume_control);
|
||||
if (volume_control) {
|
||||
if (!ui_->action_mute->isVisible()) ui_->action_mute->setVisible(true);
|
||||
if (tray_icon_ && !tray_icon_->MuteEnabled()) tray_icon_->SetMuteEnabled(true);
|
||||
}
|
||||
else {
|
||||
if (ui_->action_mute->isVisible()) ui_->action_mute->setVisible(false);
|
||||
if (tray_icon_ && tray_icon_->MuteEnabled()) tray_icon_->SetMuteEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
settings.beginGroup(TidalSettingsPage::kSettingsGroup);
|
||||
bool enable_tidal = settings.value("enabled", false).toBool();
|
||||
settings.endGroup();
|
||||
if (enable_tidal)
|
||||
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), "Tidal");
|
||||
ui_->tabs->addTab(tidal_search_view_, IconLoader::Load("tidal"), tr("Tidal"));
|
||||
else
|
||||
ui_->tabs->delTab("Tidal");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
settings.beginGroup(DeezerSettingsPage::kSettingsGroup);
|
||||
bool enable_deezer = settings.value("enabled", false).toBool();
|
||||
settings.endGroup();
|
||||
if (enable_deezer)
|
||||
ui_->tabs->addTab(deezer_search_view_, IconLoader::Load("deezer"), "Deezer");
|
||||
else
|
||||
ui_->tabs->delTab("Deezer");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::ReloadAllSettings() {
|
||||
@@ -862,12 +860,11 @@ void MainWindow::ReloadAllSettings() {
|
||||
osd_->ReloadSettings();
|
||||
collection_view_->ReloadSettings();
|
||||
ui_->playlist->view()->ReloadSettings();
|
||||
album_cover_choice_controller_->ReloadSettings();
|
||||
if (cover_manager_.get()) cover_manager_->ReloadSettings();
|
||||
#ifdef HAVE_STREAM_TIDAL
|
||||
tidal_search_view_->ReloadSettings();
|
||||
#endif
|
||||
#ifdef HAVE_STREAM_DEEZER
|
||||
deezer_search_view_->ReloadSettings();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -997,10 +994,12 @@ void MainWindow::resizeEvent(QResizeEvent *event) {
|
||||
|
||||
void MainWindow::TabSwitched() {
|
||||
|
||||
if (ui_->tabs->tabBar()->tabData(ui_->tabs->currentIndex()).toString().toLower() == "context")
|
||||
ui_->widget_playing->SetDisabled();
|
||||
else
|
||||
if (playing_widget_ && ui_->tabs->tabBar()->tabData(ui_->tabs->currentIndex()).toString().toLower() != "context") {
|
||||
ui_->widget_playing->SetEnabled();
|
||||
}
|
||||
else {
|
||||
ui_->widget_playing->SetDisabled();
|
||||
}
|
||||
|
||||
if (!initialised_) return;
|
||||
|
||||
@@ -1109,12 +1108,12 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &index) {
|
||||
QModelIndexList dummyIndexList;
|
||||
|
||||
switch (doubleclick_playlist_addmode_) {
|
||||
case PlaylistAddBehaviour_Play:
|
||||
case BehaviourSettingsPage::PlaylistAddBehaviour_Play:
|
||||
app_->playlist_manager()->SetActiveToCurrent();
|
||||
app_->player()->PlayAt(row, Engine::Manual, true);
|
||||
break;
|
||||
|
||||
case PlaylistAddBehaviour_Enqueue:
|
||||
case BehaviourSettingsPage::PlaylistAddBehaviour_Enqueue:
|
||||
dummyIndexList.append(index);
|
||||
app_->playlist_manager()->current()->queue()->ToggleTracks(dummyIndexList);
|
||||
if (app_->player()->GetState() != Engine::Playing) {
|
||||
@@ -1242,42 +1241,42 @@ void MainWindow::UpdateTrackSliderPosition() {
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::ApplyAddBehaviour(MainWindow::AddBehaviour b, MimeData *data) const {
|
||||
void MainWindow::ApplyAddBehaviour(BehaviourSettingsPage::AddBehaviour b, MimeData *data) const {
|
||||
|
||||
switch (b) {
|
||||
case AddBehaviour_Append:
|
||||
case BehaviourSettingsPage::AddBehaviour_Append:
|
||||
data->clear_first_ = false;
|
||||
data->enqueue_now_ = false;
|
||||
break;
|
||||
|
||||
case AddBehaviour_Enqueue:
|
||||
case BehaviourSettingsPage::AddBehaviour_Enqueue:
|
||||
data->clear_first_ = false;
|
||||
data->enqueue_now_ = true;
|
||||
break;
|
||||
|
||||
case AddBehaviour_Load:
|
||||
case BehaviourSettingsPage::AddBehaviour_Load:
|
||||
data->clear_first_ = true;
|
||||
data->enqueue_now_ = false;
|
||||
break;
|
||||
|
||||
case AddBehaviour_OpenInNew:
|
||||
case BehaviourSettingsPage::AddBehaviour_OpenInNew:
|
||||
data->open_in_new_playlist_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::ApplyPlayBehaviour(MainWindow::PlayBehaviour b, MimeData *data) const {
|
||||
void MainWindow::ApplyPlayBehaviour(BehaviourSettingsPage::PlayBehaviour b, MimeData *data) const {
|
||||
|
||||
switch (b) {
|
||||
case PlayBehaviour_Always:
|
||||
case BehaviourSettingsPage::PlayBehaviour_Always:
|
||||
data->play_now_ = true;
|
||||
break;
|
||||
|
||||
case PlayBehaviour_Never:
|
||||
case BehaviourSettingsPage::PlayBehaviour_Never:
|
||||
data->play_now_ = false;
|
||||
break;
|
||||
|
||||
case PlayBehaviour_IfStopped:
|
||||
case BehaviourSettingsPage::PlayBehaviour_IfStopped:
|
||||
data->play_now_ = !(app_->player()->GetState() == Engine::Playing);
|
||||
break;
|
||||
}
|
||||
@@ -1581,7 +1580,6 @@ void MainWindow::EditTracks() {
|
||||
}
|
||||
}
|
||||
|
||||
//EnsureEditTagDialogCreated();
|
||||
edit_tag_dialog_->SetSongs(songs, items);
|
||||
edit_tag_dialog_->show();
|
||||
|
||||
@@ -1625,7 +1623,7 @@ void MainWindow::RenumberTracks() {
|
||||
if (song.IsEditable()) {
|
||||
song.set_track(track);
|
||||
|
||||
TagReaderReply *reply =TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
|
||||
NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(SongSaveComplete(TagReaderReply*, QPersistentModelIndex)),reply, QPersistentModelIndex(source_index));
|
||||
}
|
||||
@@ -1634,7 +1632,7 @@ void MainWindow::RenumberTracks() {
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::SongSaveComplete(TagReaderReply *reply,const QPersistentModelIndex &index) {
|
||||
void MainWindow::SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &index) {
|
||||
if (reply->is_successful() && index.isValid()) {
|
||||
app_->playlist_manager()->current()->ReloadItems(QList<int>()<< index.row());
|
||||
}
|
||||
@@ -1644,7 +1642,7 @@ void MainWindow::SongSaveComplete(TagReaderReply *reply,const QPersistentModelIn
|
||||
void MainWindow::SelectionSetValue() {
|
||||
|
||||
Playlist::Column column = (Playlist::Column)playlist_menu_index_.column();
|
||||
QVariant column_value =app_->playlist_manager()->current()->data(playlist_menu_index_);
|
||||
QVariant column_value = app_->playlist_manager()->current()->data(playlist_menu_index_);
|
||||
|
||||
QModelIndexList indexes =ui_->playlist->view()->selectionModel()->selection().indexes();
|
||||
|
||||
@@ -1656,9 +1654,9 @@ void MainWindow::SelectionSetValue() {
|
||||
Song song = app_->playlist_manager()->current()->item_at(row)->Metadata();
|
||||
|
||||
if (Playlist::set_column_value(song, column, column_value)) {
|
||||
TagReaderReply *reply =TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
|
||||
NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(SongSaveComplete(TagReaderReply*, QPersistentModelIndex)),reply, QPersistentModelIndex(source_index));
|
||||
NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(SongSaveComplete(TagReaderReply*, QPersistentModelIndex)), reply, QPersistentModelIndex(source_index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1836,7 +1834,7 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
|
||||
break;
|
||||
case CommandlineOptions::UrlList_CreateNew:
|
||||
data->name_for_new_playlist_ = options.playlist_name();
|
||||
ApplyAddBehaviour(AddBehaviour_OpenInNew, data);
|
||||
ApplyAddBehaviour(BehaviourSettingsPage::AddBehaviour_OpenInNew, data);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2136,7 +2134,6 @@ void MainWindow::ShowTranscodeDialog() {
|
||||
#endif
|
||||
|
||||
void MainWindow::ShowErrorDialog(const QString &message) {
|
||||
|
||||
error_dialog_->ShowMessage(message);
|
||||
}
|
||||
|
||||
@@ -2331,7 +2328,7 @@ void MainWindow::SearchForCover() {
|
||||
}
|
||||
|
||||
void MainWindow::SaveCoverToFile() {
|
||||
album_cover_choice_controller_->SaveCoverToFile(song_, image_original_);
|
||||
album_cover_choice_controller_->SaveCoverToFileManual(song_, image_original_);
|
||||
}
|
||||
|
||||
void MainWindow::UnsetCover() {
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QModelIndex>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
@@ -56,6 +55,7 @@
|
||||
#include "collection/collectionmodel.h"
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "settings/settingsdialog.h"
|
||||
#include "settings/behavioursettingspage.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
@@ -109,27 +109,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
Startup_AlwaysHide = 3,
|
||||
};
|
||||
|
||||
// Don't change the values
|
||||
enum AddBehaviour {
|
||||
AddBehaviour_Append = 1,
|
||||
AddBehaviour_Enqueue = 2,
|
||||
AddBehaviour_Load = 3,
|
||||
AddBehaviour_OpenInNew = 4
|
||||
};
|
||||
|
||||
// Don't change the values
|
||||
enum PlayBehaviour {
|
||||
PlayBehaviour_Never = 1,
|
||||
PlayBehaviour_IfStopped = 2,
|
||||
PlayBehaviour_Always = 3,
|
||||
};
|
||||
|
||||
// Don't change the values
|
||||
enum PlaylistAddBehaviour {
|
||||
PlaylistAddBehaviour_Play = 1,
|
||||
PlaylistAddBehaviour_Enqueue = 2,
|
||||
};
|
||||
|
||||
void SetHiddenInTray(bool hidden);
|
||||
void CommandlineOptionsReceived(const CommandlineOptions& options);
|
||||
|
||||
@@ -279,8 +258,8 @@ signals:
|
||||
|
||||
private:
|
||||
|
||||
void ApplyAddBehaviour(AddBehaviour b, MimeData *data) const;
|
||||
void ApplyPlayBehaviour(PlayBehaviour b, MimeData *data) const;
|
||||
void ApplyAddBehaviour(BehaviourSettingsPage::AddBehaviour b, MimeData *data) const;
|
||||
void ApplyPlayBehaviour(BehaviourSettingsPage::PlayBehaviour b, MimeData *data) const;
|
||||
|
||||
void CheckFullRescanRevisions();
|
||||
|
||||
@@ -298,8 +277,8 @@ signals:
|
||||
Application *app_;
|
||||
SystemTrayIcon *tray_icon_;
|
||||
OSD *osd_;
|
||||
Lazy<EditTagDialog> edit_tag_dialog_;
|
||||
Lazy<About> about_dialog_;
|
||||
Lazy<EditTagDialog> edit_tag_dialog_;
|
||||
AlbumCoverChoiceController *album_cover_choice_controller_;
|
||||
|
||||
GlobalShortcuts *global_shortcuts_;
|
||||
@@ -332,7 +311,6 @@ signals:
|
||||
#endif
|
||||
|
||||
InternetSearchView *tidal_search_view_;
|
||||
InternetSearchView *deezer_search_view_;
|
||||
|
||||
QAction *collection_show_all_;
|
||||
QAction *collection_show_duplicates_;
|
||||
@@ -371,10 +349,11 @@ signals:
|
||||
bool was_maximized_;
|
||||
int saved_playback_position_;
|
||||
Engine::State saved_playback_state_;
|
||||
AddBehaviour doubleclick_addmode_;
|
||||
PlayBehaviour doubleclick_playmode_;
|
||||
PlaylistAddBehaviour doubleclick_playlist_addmode_;
|
||||
PlayBehaviour menu_playmode_;
|
||||
bool playing_widget_;
|
||||
BehaviourSettingsPage::AddBehaviour doubleclick_addmode_;
|
||||
BehaviourSettingsPage::PlayBehaviour doubleclick_playmode_;
|
||||
BehaviourSettingsPage::PlaylistAddBehaviour doubleclick_playlist_addmode_;
|
||||
BehaviourSettingsPage::PlayBehaviour menu_playmode_;
|
||||
|
||||
Song song_;
|
||||
Song song_playing_;
|
||||
|
||||
@@ -225,7 +225,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Amarok::VolumeSlider" name="volume">
|
||||
<widget class="VolumeSlider" name="volume">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -773,9 +773,9 @@
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Amarok::VolumeSlider</class>
|
||||
<class>VolumeSlider</class>
|
||||
<extends>QSlider</extends>
|
||||
<header>widgets/sliderwidget.h</header>
|
||||
<header>widgets/volumeslider.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AnalyzerContainer</class>
|
||||
|
||||