Compare commits

...

108 Commits
0.5.2 ... 0.5.3

Author SHA1 Message Date
Jonas Kvinge
5ebf7cd273 Release 0.5.3 2019-04-02 00:52:28 +02:00
Jonas Kvinge
9986088dc4 Update protobuf in nsi 2019-04-02 00:01:06 +02:00
Jonas Kvinge
bfe0b2c634 Add missing error check on login error 2019-04-01 23:03:36 +02:00
Jonas Kvinge
cd2af6974c Remove ignore for content not found 2019-04-01 22:54:10 +02:00
Jonas Kvinge
c452486573 Update libprotobuf in nsi 2019-03-31 03:43:37 +02:00
Jonas Kvinge
79406b20f2 Fix Tidal login handling 2019-03-31 03:41:25 +02:00
Jonas Kvinge
1226bc214d Update Changelog 2019-03-31 01:16:11 +01:00
Jonas Kvinge
7da79dabdf Add group by format 2019-03-30 22:03:33 +01:00
Jonas Kvinge
b51026a2ee Remove dot from filenames 2019-03-28 21:19:50 +01:00
Jonas Kvinge
67d01f48a3 Remove api seeds lyrics (requires payment) 2019-03-28 02:24:05 +01:00
Jonas Kvinge
9fd5c5fc1c Add Spanish 2019-03-28 01:45:04 +01:00
Jonas Kvinge
70bc5b83fa Replace swedish characters 2019-03-28 01:40:59 +01:00
Jonas Kvinge
b380db51fa Add lines to settings 2019-03-27 01:23:15 +01:00
Jonas Kvinge
fab598ebff Add some lines to scrobbler settings 2019-03-27 00:48:38 +01:00
Jonas Kvinge
6f6d087fa2 Remove mono playback setting and fix the layout abit 2019-03-27 00:44:03 +01:00
Jonas Kvinge
6e463d1de3 Remove broken mono playback setting 2019-03-27 00:31:47 +01:00
Jonas Kvinge
21970f3065 Fix gst leaks 2019-03-27 00:27:49 +01:00
Jonas Kvinge
9d7e44be2d Update Changelog 2019-03-25 23:35:33 +01:00
Jonas Kvinge
9085fb8285 Disable aspect ratio checkbox when stretch is not checked 2019-03-25 23:34:36 +01:00
Jonas Kvinge
dd79d089f6 Tweak the size a bit 2019-03-25 23:33:58 +01:00
Jonas Kvinge
7aaad124d0 Add more background image options 2019-03-25 22:00:40 +01:00
Jonas Kvinge
0025cb9f53 Add missing include 2019-03-25 19:52:46 +01:00
Jonas Kvinge
15c8f2a3ee Notify collection backend about renamed files when organising files 2019-03-25 00:53:12 +01:00
Jonas Kvinge
fc1a2dac90 Update 3rdparty/README.md 2019-03-24 19:28:55 +01:00
Jonas Kvinge
f698b860f7 Tidal: Handle login better and allow duplicate albums 2019-03-23 21:14:46 +01:00
Jonas Kvinge
86b057a301 Add Song::ExtensionForFiletype 2019-03-23 02:24:09 +01:00
Jonas Kvinge
b066158a4b Remove redundant includes 2019-03-22 23:23:50 +01:00
Jonas Kvinge
046c822604 Fix loading playlists correctly 2019-03-22 23:23:22 +01:00
Jonas Kvinge
d427733bfc Save and restore geometry in transcoder dialog 2019-03-22 23:22:23 +01:00
Jonas Kvinge
04d34a06c7 Improve Tidal error handling 2019-03-22 23:20:43 +01:00
Jonas Kvinge
69d86513ae Save/restore geometry in settings dialog 2019-03-22 23:19:24 +01:00
Jonas Kvinge
019b49a219 Add option to allow extended ascii characters, save/restore geometry 2019-03-22 23:18:14 +01:00
Jonas Kvinge
d9e787784a Fix Song::InitFromProtobuf not setting source, add function Song::SourceFromURL 2019-03-22 23:16:18 +01:00
Jonas Kvinge
71969b88e3 Fix GetSongByUrl incorrectly using bytearray instead of string 2019-03-22 23:14:25 +01:00
Jonas Kvinge
7ae0f5e246 Add better support for APE tags 2019-03-22 23:13:49 +01:00
Jonas Kvinge
1ea7da4bb5 Update taglib 2019-03-22 23:12:59 +01:00
Jonas Kvinge
8b96bb5f27 Fix formatting 2019-03-22 23:12:41 +01:00
Jonas Kvinge
aefce3ccd0 Remove unused mpris1 files 2019-03-22 23:12:24 +01:00
Jonas Kvinge
4e599e2aba Move icon loading to device model 2019-03-22 23:10:42 +01:00
Jonas Kvinge
4148c289af Fix copyright 2019-03-13 00:51:51 +01:00
Jonas Kvinge
d575ab0b2b Add basic support for system icons and custom icons 2019-03-13 00:43:46 +01:00
Jonas Kvinge
d09af19d3f Update mimetypes 2019-03-12 23:26:40 +01:00
Jonas Kvinge
a2dff17db9 Add CanPlay and CanPause 2019-03-12 23:10:31 +01:00
Jonas Kvinge
c0fecb935f Change mpris path 2019-03-12 01:00:30 +01:00
Jonas Kvinge
28249b7e99 Remove unused variable 2019-03-12 00:59:35 +01:00
Jonas Kvinge
eb63e2257f Fix load settings in albumcoverchoicecontroller 2019-03-12 00:01:52 +01:00
Jonas Kvinge
2211716d04 Add option to save album cover in album directory 2019-03-11 23:07:11 +01:00
Jonas Kvinge
242137a50c Only do QUrl toEncoded() if url is valid 2019-03-10 21:09:05 +01:00
Jonas Kvinge
c41311bf76 Disable macos build 2019-03-09 18:19:15 +01:00
Jonas Kvinge
e0d959e3c5 Add norwegian translations 2019-03-09 18:02:47 +01:00
Jonas Kvinge
f0a5c4b2c2 Enable translations 2019-03-09 18:02:17 +01:00
Jonas Kvinge
e10a50fdc1 Remove unused roles 2019-03-09 17:49:12 +01:00
Jonas Kvinge
380f2509ac Remove deezer 2019-03-09 17:43:20 +01:00
Jonas Kvinge
c0fb35f6b9 Add option to disable playing widget 2019-03-09 17:20:07 +01:00
Jonas Kvinge
3e658845d2 Add option to disable volume control 2019-03-09 16:48:45 +01:00
Jonas Kvinge
384209ba70 Fix UNC path in gst engine 2019-02-25 16:35:29 +01:00
Jonas Kvinge
835b589894 Fix untranslated text 2019-02-23 18:54:00 +01:00
Jonas Kvinge
76e684ee75 Add translations to macos builds (#84) 2019-02-23 04:20:28 +01:00
Jonas Kvinge
d0d2c83768 Ops 2019-02-23 04:18:45 +01:00
Jonas Kvinge
b476bc3168 Add translations to debian 2019-02-23 04:14:49 +01:00
Jonas Kvinge
5a2ad145e8 Add gettext and Qt5LinguistTools 2019-02-23 01:14:36 +01:00
Jonas Kvinge
27233d2549 Dont allow X11 shortcuts on wayland 2019-02-23 01:10:45 +01:00
Jonas Kvinge
1a0bc75629 Change pacman to use current git revision 2019-02-22 22:57:53 +01:00
Jonas Kvinge
dc04961699 Add update-desktop-files 2019-02-22 21:17:10 +01:00
Jonas Kvinge
e594cf299e Rename desktop and appdata files 2019-02-22 20:41:06 +01:00
Jonas Kvinge
096c995a91 Add src/core/potranslator.h 2019-02-22 20:38:23 +01:00
Jonas Kvinge
f64b175cd8 Fix .gitignore 2019-02-22 20:38:08 +01:00
Jonas Kvinge
954e0e8a59 Add translations support (#82)
* Add translations support
* Update .gitignore
2019-02-22 20:24:38 +01:00
Jonas Kvinge
034b032cfa Update .gitignore 2019-02-22 20:08:43 +01:00
Jonas Kvinge
e33590bff9 Show strawberry icon in OSD 2019-02-21 18:12:40 +01:00
Jonas Kvinge
55f610f3b2 Fix formatting 2019-02-20 21:29:14 +01:00
Jonas Kvinge
87fd93a1cf Show error when editing tags fails, update DB immediately when successful 2019-02-20 21:27:53 +01:00
Jonas Kvinge
2db77a248a Fix formatting 2019-02-20 21:27:04 +01:00
Jonas Kvinge
f600274592 Replace NULL with nullptr 2019-02-20 21:26:14 +01:00
Jonas Kvinge
bc37d00a81 Stop watcher when unmounting 2019-02-20 21:24:10 +01:00
Jonas Kvinge
1956ea95ee Use leap instead 2019-02-20 21:22:59 +01:00
Jonas Kvinge
a56e3b91e1 Change fade default and allow setting device back if custom device is
selected
2019-02-20 21:21:33 +01:00
Jonas Kvinge
6bcc9d61e1 Dont set keep running 2019-02-20 21:21:09 +01:00
Jonas Kvinge
3ef34191a3 Fix icon 2019-02-20 21:20:38 +01:00
Jonas Kvinge
60de36aff9 Remove whitespace 2019-02-20 21:15:58 +01:00
Jonas Kvinge
b019dd1c51 Update Dockerfile 2019-02-19 00:37:37 +01:00
Jonas Kvinge
3a083d5527 Update Dockerfile 2019-02-19 00:26:43 +01:00
Jonas Kvinge
8006241a81 Change Dockerfile 2019-02-19 00:14:20 +01:00
Jonas Kvinge
b05e3d24ee Change id 2019-02-18 22:58:10 +01:00
Jonas Kvinge
b78c0a4979 Remove unused includes 2019-02-13 20:12:20 +01:00
Jonas Kvinge
d3b3c309fa Create systray tooltip workaround for KDE 2019-02-13 20:04:05 +01:00
Jonas Kvinge
65615495d9 Remove debugging 2019-02-12 22:08:26 +01:00
Jonas Kvinge
35f448c34f Add artist search in internet view, and use album artist 2019-02-12 21:58:03 +01:00
Jonas Kvinge
e1abd28a88 Remove upload script 2019-02-10 23:14:49 +01:00
Jonas Kvinge
a109d8be64 Revert change to tooltip 2019-02-10 21:28:09 +01:00
Jonas Kvinge
333a0bc05a Capitalize strawberry in osd and tooltip, change cdcase and remove some
unused code
2019-02-10 21:25:36 +01:00
Jonas Kvinge
a831519b54 Remove unused file 2019-02-10 18:37:06 +01:00
Jonas Kvinge
a25052ed96 Replace no cover image 2019-02-09 14:51:12 +01:00
Jonas Kvinge
676f8dc8be Make it possible to use enter in shortcuts 2019-02-09 12:47:40 +01:00
Jonas Kvinge
9c3cb82fca Add compiler to macos img file (#73) 2019-02-04 23:42:07 +01:00
Jonas Kvinge
9b827f58ad Add files to exculde in maketarball 2019-02-04 23:30:08 +01:00
Jonas Kvinge
22e327d391 Remove TextSelectableByKeyboard 2019-02-04 22:34:22 +01:00
Jonas Kvinge
451de4d72d Add spaces to debian copyright 2019-02-04 22:32:53 +01:00
Jonas Kvinge
0679b78c1d Add boom and rainbow analyzers 2019-02-04 21:34:12 +01:00
Jonas Kvinge
ad7084b897 Make lyrics selectable 2019-02-04 19:07:05 +01:00
Jonas Kvinge
3c068eaba9 Remove support for older taglib in tagreader 2019-02-02 12:39:11 +01:00
Jonas Kvinge
819ae08c6a Change to default to albumartist in organise dialog 2019-02-02 00:31:26 +01:00
Jonas Kvinge
38414ad8de Add unity7 to snap 2019-01-31 20:43:26 +01:00
Jonas Kvinge
f0422f7634 Fix snap 2019-01-31 00:08:52 +01:00
Jonas Kvinge
2a004dadf3 Remove system-files from snap 2019-01-30 00:00:35 +01:00
Jonas Kvinge
b7ea586e44 Add snap 2019-01-29 22:44:07 +01:00
Jonas Kvinge
2a5f286d07 Update README.md 2019-01-28 23:46:33 +01:00
Jonas Kvinge
0bc14671ec Turn back git revision 2019-01-26 23:53:35 +01:00
242 changed files with 13750 additions and 5770 deletions

155
.gitignore vendored
View File

@@ -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

View File

@@ -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
View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -2,7 +2,7 @@
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](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
View 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)

View File

@@ -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()

View File

@@ -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>

View File

@@ -0,0 +1,8 @@
<h4>%appName</h4>
<p>
%image<br />
%titleKey: %titleValue<br />
%artistKey: %artistValue<br />
%albumKey: %albumValue<br />
%lengthKey: %lengthValue<br />
</p>

View File

@@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

BIN
data/pictures/cdcase.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
data/pictures/nyancat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

4
dist/CMakeLists.txt vendored
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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

View File

@@ -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.*

View File

@@ -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"

View File

@@ -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

View File

@@ -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}

View File

@@ -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
}

View File

@@ -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
View File

@@ -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" ]

View File

@@ -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
View 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;

View File

@@ -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;

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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");

View File

@@ -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_;

View File

@@ -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
View 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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View 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_);
}

View 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

View File

@@ -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);
}

View File

@@ -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

View 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) {}

View 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

View File

@@ -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 */);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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_)

View File

@@ -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

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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"); }
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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>

View File

@@ -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_);

View File

@@ -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_;

View File

@@ -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(); }

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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() {

View File

@@ -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_;

View File

@@ -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>

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