Compare commits

..

56 Commits
0.6.6 ... 0.6.7

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -56,32 +56,17 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(DEBUG ON) set(DEBUG ON)
endif() endif()
if (CMAKE_CXX_COMPILER MATCHES ".*clang")
set(CMAKE_COMPILER_IS_CLANGXX 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized")
endif()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
endif(APPLE)
find_program(CCACHE_EXECUTABLE NAMES ccache) find_program(CCACHE_EXECUTABLE NAMES ccache)
if (CCACHE_EXECUTABLE) if (CCACHE_EXECUTABLE)
message(STATUS "ccache found: will be used for compilation and linkage") message(STATUS "ccache found: will be used for compilation and linkage")
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_EXECUTABLE}) SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_EXECUTABLE})
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE}) SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
endif () endif ()
find_program(QT_LCONVERT_EXECUTABLE NAMES lconvert lconvert-qt5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH)
find_program(QT_LCONVERT_EXECUTABLE NAMES lconvert lconvert-qt5)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0) find_package(Boost REQUIRED)
pkg_check_modules(GIO REQUIRED gio-2.0)
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
pkg_check_modules(CDIO libcdio)
find_package(Threads) find_package(Threads)
find_package(GnuTLS) find_package(GnuTLS)
find_package(Boost REQUIRED)
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf) find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
if(LINUX) if(LINUX)
@@ -98,6 +83,10 @@ endif()
if(X11_FOUND) if(X11_FOUND)
set(HAVE_X11 ON) set(HAVE_X11 ON)
endif() endif()
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GIO REQUIRED gio-2.0)
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
pkg_check_modules(LIBCDIO libcdio)
pkg_check_modules(GSTREAMER gstreamer-1.0) pkg_check_modules(GSTREAMER gstreamer-1.0)
pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0) pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0)
pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0) pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
@@ -303,7 +292,7 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
) )
optional_component(AUDIOCD ON "Devices: Audio CD support" optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" CDIO_FOUND DEPENDS "libcdio" LIBCDIO_FOUND
) )
optional_component(UDISKS2 ON "Devices: UDisks2 backend" optional_component(UDISKS2 ON "Devices: UDisks2 backend"

View File

@@ -2,6 +2,23 @@ Strawberry Music Player
======================= =======================
ChangeLog ChangeLog
Version 0.6.7:
* Fixed crash when cancelling scrobbler authentication
* Fixed "Double clicking a song in the playlist" behaviour setting
* Fixed "Pressing Previous in player" behaviour setting
* Fixed updating compilations where there are spaces or special characters in filenames
* Fixed cases where songs were stuck in "Various Artists" because not all songs in
the same compilation was removed from the model before readded with actual artist.
* Fixed a bug when importing playlists where metadata was reset
* Fixed scrobbler to also scrobble songs without album title
* Fixed text for replay gain setting not loading in backend setting
* Added back lyrics from Chartlyrics
* Added ability to show fullsize cover on doubleclick in playing widget
* Added seperator between "unset cover" and "show fullsize" in popup menu
* Removed left click on analyzer to popup menu
* (Windows) Added killproc executable to terminate running process before uninstalling
Version 0.6.6: Version 0.6.6:
* Fixed lowercased album artist in playlist column * Fixed lowercased album artist in playlist column

View File

@@ -20,7 +20,7 @@ Strawberry is a music player and music collection organizer. It is a fork of Cle
* Edit tags on music files * Edit tags on music files
* Fetch tags from MusicBrainz * Fetch tags from MusicBrainz
* Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal * Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal
* Song lyrics from AudD, lyrics.ovh and lololyrics.com * Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
* Support for multiple backends * Support for multiple backends
* Audio analyzer * Audio analyzer
* Audio equalizer * Audio equalizer

View File

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

View File

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

2
debian/control vendored
View File

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

48
debian/copyright vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,7 +59,6 @@ signals:
protected: protected:
void mouseReleaseEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*);
void mouseDoubleClickEvent(QMouseEvent*);
void wheelEvent(QWheelEvent *e); void wheelEvent(QWheelEvent *e);
private slots: private slots:
@@ -93,7 +92,6 @@ signals:
QList<QAction*> actions_; QList<QAction*> actions_;
QAction *disable_action_; QAction *disable_action_;
QAction *visualisation_action_;
QTimer *double_click_timer_; QTimer *double_click_timer_;
QPoint last_click_pos_; QPoint last_click_pos_;
bool ignore_next_click_; bool ignore_next_click_;

View File

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

View File

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

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -109,7 +110,7 @@ class CollectionBackendInterface : public QObject {
virtual SongList GetSongsByUrl(const QUrl &url) = 0; virtual SongList GetSongsByUrl(const QUrl &url) = 0;
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song. // Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs. // Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) = 0; virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
virtual void AddDirectory(const QString &path) = 0; virtual void AddDirectory(const QString &path) = 0;
virtual void RemoveDirectory(const Directory &dir) = 0; virtual void RemoveDirectory(const Directory &dir) = 0;
@@ -227,16 +228,14 @@ class CollectionBackend : public CollectionBackendInterface {
struct CompilationInfo { struct CompilationInfo {
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {} CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
QString directory;
QString album;
QList<QUrl> urls; QList<QUrl> urls;
QSet<QString> artists; QStringList artists;
int has_compilation_detected; int has_compilation_detected;
int has_not_compilation_detected; int has_not_compilation_detected;
}; };
void UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected); void UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
AlbumList GetAlbums(const QString &artist, const QString &album_artist, bool compilation = false, const QueryOptions &opt = QueryOptions()); AlbumList GetAlbums(const QString &artist, const QString &album_artist, bool compilation = false, const QueryOptions &opt = QueryOptions());
AlbumList GetAlbums(const QString &artist, bool compilation, const QueryOptions &opt = QueryOptions()); AlbumList GetAlbums(const QString &artist, bool compilation, const QueryOptions &opt = QueryOptions());
SubdirectoryList SubdirsInDirectory(int id, QSqlDatabase &db); SubdirectoryList SubdirsInDirectory(int id, QSqlDatabase &db);

View File

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

View File

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

View File

@@ -70,7 +70,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
static const char *kSavedGroupingsSettingsGroup; static const char *kSavedGroupingsSettingsGroup;
static const int kPrettyCoverSize; static const int kPrettyCoverSize;
static const qint64 kIconCacheSize; static const int kPixmapCacheLimit;
enum Role { enum Role {
Role_Type = Qt::UserRole + 1, Role_Type = Qt::UserRole + 1,

View File

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

View File

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

View File

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

View File

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

View File

@@ -110,7 +110,7 @@ void ContextView::Init(Application *app, CollectionView *collectionview, AlbumCo
collectionview_ = collectionview; collectionview_ = collectionview;
album_cover_choice_controller_ = album_cover_choice_controller; album_cover_choice_controller_ = album_cover_choice_controller;
ui_->widget_play_albums->SetApplication(app_); ui_->widget_play_albums->Init(app_);
lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this); lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this);
connect(collectionview_, SIGNAL(TotalSongCountUpdated_()), this, SLOT(UpdateNoSong())); connect(collectionview_, SIGNAL(TotalSongCountUpdated_()), this, SLOT(UpdateNoSong()));
@@ -498,7 +498,7 @@ void ContextView::UpdateSong(const Song &song) {
void ContextView::UpdateLyrics(const quint64 id, const QString &provider, const QString &lyrics) { void ContextView::UpdateLyrics(const quint64 id, const QString &provider, const QString &lyrics) {
if (id != lyrics_id_) return; if ((qint64) id != lyrics_id_) return;
lyrics_ = lyrics + "\n\n(Lyrics from " + provider + ")\n"; lyrics_ = lyrics + "\n\n(Lyrics from " + provider + ")\n";
lyrics_id_ = -1; lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) { if (action_show_lyrics_->isChecked()) {

View File

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

View File

@@ -68,8 +68,6 @@ class Application : public QObject {
Q_OBJECT Q_OBJECT
public: public:
static bool kIsPortable;
explicit Application(QObject *parent = nullptr); explicit Application(QObject *parent = nullptr);
~Application(); ~Application();

View File

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

View File

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

View File

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

View File

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

View File

@@ -126,6 +126,8 @@ class Song {
static const QRegExp kTitleRemoveMisc; static const QRegExp kTitleRemoveMisc;
static const QRegExp kFilenameRemoveNonFatChars; static const QRegExp kFilenameRemoveNonFatChars;
static const QString kVariousArtists;
static const QStringList kArticles; static const QStringList kArticles;
static QString JoinSpec(const QString &table); static QString JoinSpec(const QString &table);

View File

@@ -2,6 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This file was part of Clementine. * This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com> * Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -167,7 +168,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
LoadLocalDirectory(filename); LoadLocalDirectory(filename);
return Success; return Success;
} }
Song song; Song song(Song::Source_LocalFile);
song.InitFromFilePartial(filename); song.InitFromFilePartial(filename);
if (song.is_valid()) { if (song.is_valid()) {
songs_ << song; songs_ << song;
@@ -228,7 +229,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
if (collection_->ExecQuery(&query) && query.Next()) { if (collection_->ExecQuery(&query) && query.Next()) {
// We may have many results when the file has many sections // We may have many results when the file has many sections
do { do {
Song song; Song song(Song::Source_Collection);
song.InitFromQuery(query, true); song.InitFromQuery(query, true);
if (song.is_valid()) { if (song.is_valid()) {
@@ -289,7 +290,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
} }
// Assume it's just a normal file // Assume it's just a normal file
Song song; Song song(Song::Source_LocalFile);
song.InitFromFilePartial(filename); song.InitFromFilePartial(filename);
if (song.is_valid()) { if (song.is_valid()) {
songs_ << song; songs_ << song;

View File

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

View File

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

View File

@@ -514,6 +514,26 @@ bool ParseUntilElement(QXmlStreamReader *reader, const QString &name) {
} }
bool ParseUntilElementCI(QXmlStreamReader *reader, const QString &name) {
while (!reader->atEnd()) {
QXmlStreamReader::TokenType type = reader->readNext();
switch (type) {
case QXmlStreamReader::StartElement:{
QString element = reader->name().toString().toLower();
if (element == name) {
return true;
}
break;
}
default:
break;
}
}
return false;
}
QDateTime ParseRFC822DateTime(const QString &text) { QDateTime ParseRFC822DateTime(const QString &text) {
QRegExp regexp("(\\d{1,2}) (\\w{3,12}) (\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2})"); QRegExp regexp("(\\d{1,2}) (\\w{3,12}) (\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
@@ -716,19 +736,6 @@ void IncreaseFDLimit() {
} }
void CheckPortable() {
QFile f(QApplication::applicationDirPath() + QDir::separator() + "data");
if (f.exists()) {
// We are portable. Set the bool and change the qsettings path
Application::kIsPortable = true;
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, f.fileName());
}
}
QString GetRandomStringWithChars(const int len) { QString GetRandomStringWithChars(const int len) {
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
return GetRandomString(len, UseCharacters); return GetRandomString(len, UseCharacters);

View File

@@ -96,6 +96,7 @@ void ConsumeCurrentElement(QXmlStreamReader *reader);
// Advances the stream reader until it finds an element with the given name. // Advances the stream reader until it finds an element with the given name.
// Returns false if the end of the document was reached before finding a matching element. // Returns false if the end of the document was reached before finding a matching element.
bool ParseUntilElement(QXmlStreamReader *reader, const QString &name); bool ParseUntilElement(QXmlStreamReader *reader, const QString &name);
bool ParseUntilElementCI(QXmlStreamReader *reader, const QString &name);
// Parses a string containing an RFC822 time and date. // Parses a string containing an RFC822 time and date.
QDateTime ParseRFC822DateTime(const QString &text); QDateTime ParseRFC822DateTime(const QString &text);
@@ -123,7 +124,6 @@ QString FiddleFileExtension(const QString &filename, const QString &new_extensio
QString GetEnv(const QString &key); QString GetEnv(const QString &key);
void SetEnv(const char *key, const QString &value); void SetEnv(const char *key, const QString &value);
void IncreaseFDLimit(); void IncreaseFDLimit();
void CheckPortable();
// Borrowed from schedutils // Borrowed from schedutils
enum IoPriority { enum IoPriority {

View File

@@ -131,7 +131,7 @@ void AlbumCoverChoiceController::ReloadSettings() {
} }
QList<QAction*> AlbumCoverChoiceController::GetAllActions() { QList<QAction*> AlbumCoverChoiceController::GetAllActions() {
return QList<QAction*>() << cover_from_file_ << cover_to_file_ << separator_ << cover_from_url_ << search_for_cover_ << unset_cover_ << show_cover_; return QList<QAction*>() << cover_from_file_ << cover_to_file_ << separator_ << cover_from_url_ << search_for_cover_ << unset_cover_ << separator_ << show_cover_;
} }
QUrl AlbumCoverChoiceController::LoadCoverFromFile(Song *song) { QUrl AlbumCoverChoiceController::LoadCoverFromFile(Song *song) {

View File

@@ -107,10 +107,9 @@ void CddaSongLoader::LoadSongs() {
SongList songs; SongList songs;
for (int track_number = 1; track_number <= num_tracks; track_number++) { for (int track_number = 1; track_number <= num_tracks; track_number++) {
// Init song // Init song
Song song; Song song(Song::Source_CDDA);
song.set_id(track_number); song.set_id(track_number);
song.set_valid(true); song.set_valid(true);
song.set_source(Song::Source_CDDA);
song.set_filetype(Song::FileType_CDDA); song.set_filetype(Song::FileType_CDDA);
song.set_url(GetUrlFromTrack(track_number)); song.set_url(GetUrlFromTrack(track_number));
song.set_title(QString("Track %1").arg(track_number)); song.set_title(QString("Track %1").arg(track_number));
@@ -150,7 +149,7 @@ void CddaSongLoader::LoadSongs() {
gst_message_parse_toc (msg_toc, &toc, nullptr); gst_message_parse_toc (msg_toc, &toc, nullptr);
if (toc) { if (toc) {
GList *entries = gst_toc_get_entries(toc); GList *entries = gst_toc_get_entries(toc);
if (entries && songs.size() <= g_list_length (entries)) { if (entries && (guint)songs.size() <= g_list_length(entries)) {
int i = 0; int i = 0;
for (GList *node = entries; node != nullptr; node = node->next) { for (GList *node = entries; node != nullptr; node = node->next) {
GstTocEntry *entry = static_cast<GstTocEntry*>(node->data); GstTocEntry *entry = static_cast<GstTocEntry*>(node->data);
@@ -200,7 +199,7 @@ void CddaSongLoader::AudioCDTagsLoaded(const QString &artist, const QString &alb
if (results.empty()) return; if (results.empty()) return;
int track_number = 1; int track_number = 1;
for (const MusicBrainzClient::Result &ret : results) { for (const MusicBrainzClient::Result &ret : results) {
Song song; Song song(Song::Source_CDDA);
song.set_artist(artist); song.set_artist(artist);
song.set_album(album); song.set_album(album);
song.set_title(ret.title_); song.set_title(ret.title_);
@@ -208,7 +207,6 @@ void CddaSongLoader::AudioCDTagsLoaded(const QString &artist, const QString &alb
song.set_track(track_number); song.set_track(track_number);
song.set_year(ret.year_); song.set_year(ret.year_);
song.set_id(track_number); song.set_id(track_number);
song.set_source(Song::Source_CDDA);
song.set_filetype(Song::FileType_CDDA); song.set_filetype(Song::FileType_CDDA);
song.set_valid(true); song.set_valid(true);
// We need to set url: that's how playlist will find the correct item to update // We need to set url: that's how playlist will find the correct item to update

View File

@@ -511,7 +511,7 @@ void GstEngine::EndOfStreamReached(const int pipeline_id, const bool has_next_tr
} }
void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int domain, int error_code) { void GstEngine::HandlePipelineError(const int pipeline_id, const QString &message, const int domain, const int error_code) {
if (!current_pipeline_.get() || current_pipeline_->id() != pipeline_id) return; if (!current_pipeline_.get() || current_pipeline_->id() != pipeline_id) return;
@@ -521,7 +521,7 @@ void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int
BufferingFinished(); BufferingFinished();
emit StateChanged(Engine::Error); emit StateChanged(Engine::Error);
if (domain == GST_RESOURCE_ERROR && (error_code == GST_RESOURCE_ERROR_NOT_FOUND || error_code == GST_RESOURCE_ERROR_NOT_AUTHORIZED)) { if (domain == (int)GST_RESOURCE_ERROR && (error_code == (int)GST_RESOURCE_ERROR_NOT_FOUND || error_code == (int)GST_RESOURCE_ERROR_NOT_AUTHORIZED)) {
emit InvalidSongRequested(stream_url_); emit InvalidSongRequested(stream_url_);
} }
else { else {

View File

@@ -270,12 +270,6 @@ bool GstEnginePipeline::InitAudioBin() {
} }
} }
if (output_ == "wasapisink") {
// Dont set exclusive, there is a bug in gstreamer causing freeze/crash:
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/868
g_object_set(G_OBJECT(audiosink), "low-latency", true, nullptr);
}
// Create all the other elements // Create all the other elements
audioqueue_ = engine_->CreateElement("queue2", audiobin_); audioqueue_ = engine_->CreateElement("queue2", audiobin_);
@@ -395,7 +389,7 @@ bool GstEnginePipeline::InitAudioBin() {
next = equalizer_; next = equalizer_;
} }
// Link equalizer elements if enabled. // Link stereo balancer elements if enabled.
if (stereo_balancer_enabled_ && audiopanorama_) { if (stereo_balancer_enabled_ && audiopanorama_) {
gst_element_link(next, audiopanorama_); gst_element_link(next, audiopanorama_);
next = audiopanorama_; next = audiopanorama_;
@@ -617,7 +611,7 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad *pad, GstPadProbeInf
if (instance->end_offset_nanosec_ > 0) { if (instance->end_offset_nanosec_ > 0) {
quint64 start_time = GST_BUFFER_TIMESTAMP(buf) - instance->segment_start_; quint64 start_time = GST_BUFFER_TIMESTAMP(buf) - instance->segment_start_;
quint64 duration = GST_BUFFER_DURATION(buf); quint64 duration = GST_BUFFER_DURATION(buf);
quint64 end_time = start_time + duration; qint64 end_time = start_time + duration;
if (end_time > instance->end_offset_nanosec_) { if (end_time > instance->end_offset_nanosec_) {
if (instance->has_next_valid_url() && instance->next_stream_url_ == instance->stream_url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) { if (instance->has_next_valid_url() && instance->next_stream_url_ == instance->stream_url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) {
@@ -804,7 +798,7 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) {
g_error_free(error); g_error_free(error);
free(debugs); free(debugs);
if (state() == GST_STATE_PLAYING && pipeline_is_initialised_ && next_uri_set_ && (domain == GST_RESOURCE_ERROR || domain == GST_STREAM_ERROR)) { if (state() == GST_STATE_PLAYING && pipeline_is_initialised_ && next_uri_set_ && (domain == (int)GST_RESOURCE_ERROR || domain == (int)GST_STREAM_ERROR)) {
// A track is still playing and the next uri is not playable. We ignore the error here so it can play until the end. // A track is still playing and the next uri is not playable. We ignore the error here so it can play until the end.
// But there is no message send to the bus when the current track finishes, we have to add an EOS ourself. // But there is no message send to the bus when the current track finishes, we have to add an EOS ourself.
qLog(Debug) << "Ignoring error when loading next track"; qLog(Debug) << "Ignoring error when loading next track";

View File

@@ -52,7 +52,9 @@ LocalRedirectServer::LocalRedirectServer(const bool https, QObject *parent)
socket_(nullptr) socket_(nullptr)
{} {}
LocalRedirectServer::~LocalRedirectServer() {} LocalRedirectServer::~LocalRedirectServer() {
if (isListening()) close();
}
bool LocalRedirectServer::GenerateCertificate() { bool LocalRedirectServer::GenerateCertificate() {

View File

@@ -0,0 +1,135 @@
/*
* Strawberry Music Player
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QObject>
#include <QByteArray>
#include <QList>
#include <QPair>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QUrlQuery>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QXmlStreamReader>
#include "core/closure.h"
#include "core/logging.h"
#include "core/network.h"
#include "core/utilities.h"
#include "lyricsprovider.h"
#include "lyricsfetcher.h"
#include "chartlyricsprovider.h"
const char *ChartLyricsProvider::kUrlSearch = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect";
const int ChartLyricsProvider::kMaxLength = 6000;
ChartLyricsProvider::ChartLyricsProvider(QObject *parent) : LyricsProvider("ChartLyrics", parent), network_(new NetworkAccessManager(this)) {}
bool ChartLyricsProvider::StartSearch(const QString &artist, const QString&, const QString &title, const quint64 id) {
const ParamList params = ParamList() << Param("artist", artist)
<< Param("song", title);
QUrlQuery url_query;
for (const Param &param : params) {
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
}
QUrl url(kUrlSearch);
url.setQuery(url_query);
QNetworkReply *reply = network_->get(QNetworkRequest(url));
NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleSearchReply(QNetworkReply*, const quint64, const QString&, const QString&)), reply, id, artist, title);
//qLog(Debug) << "ChartLyrics: Sending request for" << url;
return true;
}
void ChartLyricsProvider::CancelSearch(const quint64) {
}
void ChartLyricsProvider::HandleSearchReply(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title) {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
Error(id, failure_reason);
return;
}
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
QString failure_reason = QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
Error(id, failure_reason);
return;
}
QXmlStreamReader reader(reply);
LyricsSearchResults results;
LyricsSearchResult result;
while (!reader.atEnd()) {
QXmlStreamReader::TokenType type = reader.readNext();
QStringRef name = reader.name();
if (type == QXmlStreamReader::StartElement) {
if (name == "GetLyricResult") {
result = LyricsSearchResult();
}
if (name == "LyricArtist") {
result.artist = reader.readElementText();
}
else if (name == "LyricSong") {
result.title = reader.readElementText();
}
else if (name == "Lyric") {
result.lyrics = reader.readElementText();
}
}
else if (type == QXmlStreamReader::EndElement) {
if (name == "GetLyricResult") {
if (!result.artist.isEmpty() && !result.title.isEmpty() && !result.lyrics.isEmpty()) {
result.score = 0.0;
if (result.artist.toLower() == artist.toLower()) result.score += 1.0;
if (result.title.toLower() == title.toLower()) result.score += 1.0;
if (result.artist.toLower() == artist.toLower() || result.title.toLower() == title.toLower()) {
results << result;
}
}
result = LyricsSearchResult();
}
}
}
if (results.isEmpty()) qLog(Debug) << "ChartLyrics: No lyrics for" << artist << title;
else qLog(Debug) << "ChartLyrics: Got lyrics for" << artist << title;
emit SearchFinished(id, results);
}
void ChartLyricsProvider::Error(const quint64 id, const QString &error, QVariant debug) {
qLog(Error) << "ChartLyrics:" << error;
if (debug.isValid()) qLog(Debug) << debug;
emit SearchFinished(id, LyricsSearchResults());
}

View File

@@ -0,0 +1,56 @@
/*
* Strawberry Music Player
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CHARTLYRICSPROVIDER_H
#define CHARTLYRICSPROVIDER_H
#include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QVariant>
#include <QString>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "lyricsprovider.h"
#include "lyricsfetcher.h"
class ChartLyricsProvider : public LyricsProvider {
Q_OBJECT
public:
explicit ChartLyricsProvider(QObject *parent = nullptr);
bool StartSearch(const QString &artist, const QString &album, const QString &title, const quint64 id);
void CancelSearch(quint64 id);
private slots:
void HandleSearchReply(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title);
private:
static const char *kUrlSearch;
static const int kMaxLength;
QNetworkAccessManager *network_;
void Error(const quint64 id, const QString &error, QVariant debug = QVariant());
};
#endif // CHARTLYRICSPROVIDER_H

View File

@@ -134,7 +134,6 @@ int main(int argc, char* argv[]) {
// Only start a core application now so we can check if there's another instance without requiring an X server. // Only start a core application now so we can check if there's another instance without requiring an X server.
// This MUST be done before parsing the commandline options so QTextCodec gets the right system locale for filenames. // This MUST be done before parsing the commandline options so QTextCodec gets the right system locale for filenames.
SingleCoreApplication core_app(argc, argv, true, SingleCoreApplication::Mode::User | SingleCoreApplication::Mode::ExcludeAppVersion | SingleCoreApplication::Mode::ExcludeAppPath); SingleCoreApplication core_app(argc, argv, true, SingleCoreApplication::Mode::User | SingleCoreApplication::Mode::ExcludeAppVersion | SingleCoreApplication::Mode::ExcludeAppPath);
Utilities::CheckPortable();
// Parse commandline options - need to do this before starting the full QApplication so it works without an X server // Parse commandline options - need to do this before starting the full QApplication so it works without an X server
if (!options.Parse()) return 1; if (!options.Parse()) return 1;
logging::SetLevels(options.log_levels()); logging::SetLevels(options.log_levels());

View File

@@ -46,21 +46,9 @@ SongList ASXParser::Load(QIODevice *device, const QString &playlist_path, const
// We have to load everything first so we can munge the "XML". // We have to load everything first so we can munge the "XML".
QByteArray data = device->readAll(); QByteArray data = device->readAll();
// (thanks Amarok...)
// ASX looks a lot like xml, but doesn't require tags to be case sensitive,
// meaning we have to accept things like: <Abstract>...</abstract>
// We use a dirty way to achieve this: we make all tags lower case
QRegExp ex("(<[/]?[^>]*[A-Z]+[^>]*>)");
ex.setCaseSensitivity(Qt::CaseInsensitive);
int index = 0;
while ((index = ex.indexIn(data, index)) != -1) {
data.replace(ex.cap(1).toLocal8Bit(), ex.cap(1).toLower().toLocal8Bit());
index += ex.matchedLength();
}
// Some playlists have unescaped & characters in URLs :( // Some playlists have unescaped & characters in URLs :(
ex.setPattern("(href\\s*=\\s*\")([^\"]+)\""); QRegExp ex("(href\\s*=\\s*\")([^\"]+)\"");
index = 0; int index = 0;
while ((index = ex.indexIn(data, index)) != -1) { while ((index = ex.indexIn(data, index)) != -1) {
QString url = ex.cap(2); QString url = ex.cap(2);
url.replace(QRegExp("&(?!amp;|quot;|apos;|lt;|gt;)"), "&amp;"); url.replace(QRegExp("&(?!amp;|quot;|apos;|lt;|gt;)"), "&amp;");
@@ -76,16 +64,17 @@ SongList ASXParser::Load(QIODevice *device, const QString &playlist_path, const
SongList ret; SongList ret;
QXmlStreamReader reader(&buffer); QXmlStreamReader reader(&buffer);
if (!Utilities::ParseUntilElement(&reader, "asx")) { if (!Utilities::ParseUntilElementCI(&reader, "asx")) {
return ret; return ret;
} }
while (!reader.atEnd() && Utilities::ParseUntilElement(&reader, "entry")) { while (!reader.atEnd() && Utilities::ParseUntilElementCI(&reader, "entry")) {
Song song = ParseTrack(&reader, dir); Song song = ParseTrack(&reader, dir);
if (song.is_valid()) { if (song.is_valid()) {
ret << song; ret << song;
} }
} }
return ret; return ret;
} }
@@ -99,18 +88,21 @@ Song ASXParser::ParseTrack(QXmlStreamReader *reader, const QDir &dir) const {
switch (type) { switch (type) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
QStringRef name = reader->name(); QString name = reader->name().toString().toLower();
if (name == "ref") { if (name == "ref") {
ref = reader->attributes().value("href").toString(); ref = reader->attributes().value("href").toString();
} else if (name == "title") { }
else if (name == "title") {
title = reader->readElementText(); title = reader->readElementText();
} else if (name == "author") { }
else if (name == "author") {
artist = reader->readElementText(); artist = reader->readElementText();
} }
break; break;
} }
case QXmlStreamReader::EndElement: { case QXmlStreamReader::EndElement: {
if (reader->name() == "entry") { QString name = reader->name().toString().toLower();
if (name == "entry") {
goto return_song; goto return_song;
} }
break; break;
@@ -124,9 +116,12 @@ return_song:
Song song = LoadSong(ref, 0, dir); Song song = LoadSong(ref, 0, dir);
// Override metadata with what was in the playlist // Override metadata with what was in the playlist
song.set_title(title); if (song.source() != Song::Source_Collection) {
song.set_artist(artist); if (!title.isEmpty()) song.set_title(title);
song.set_album(album); if (!artist.isEmpty()) song.set_artist(artist);
if (!album.isEmpty()) song.set_album(album);
}
return song; return song;
} }

View File

@@ -121,6 +121,7 @@ bool M3UParser::ParseMetadata(const QString &line, M3UParser::Metadata *metadata
metadata->artist = list[0].trimmed(); metadata->artist = list[0].trimmed();
metadata->title = list[1].trimmed(); metadata->title = list[1].trimmed();
return true; return true;
} }
void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir, Playlist::Path path_type) const { void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir, Playlist::Path path_type) const {
@@ -146,6 +147,7 @@ void M3UParser::Save(const SongList &songs, QIODevice *device, const QDir &dir,
device->write(URLOrFilename(song.url(), dir, path_type).toUtf8()); device->write(URLOrFilename(song.url(), dir, path_type).toUtf8());
device->write("\n"); device->write("\n");
} }
} }
bool M3UParser::TryMagic(const QByteArray &data) const { bool M3UParser::TryMagic(const QByteArray &data) const {

View File

@@ -93,7 +93,7 @@ void ParserBase::LoadSong(const QString &filename_or_url, qint64 beginning, cons
Song ParserBase::LoadSong(const QString &filename_or_url, qint64 beginning, const QDir &dir) const { Song ParserBase::LoadSong(const QString &filename_or_url, qint64 beginning, const QDir &dir) const {
Song song; Song song(Song::Source_LocalFile);
LoadSong(filename_or_url, beginning, dir, &song); LoadSong(filename_or_url, beginning, dir, &song);
return song; return song;

View File

@@ -27,4 +27,3 @@ class CollectionBackendInterface;
XMLParser::XMLParser(CollectionBackendInterface *collection, QObject *parent) XMLParser::XMLParser(CollectionBackendInterface *collection, QObject *parent)
: ParserBase(collection, parent) {} : ParserBase(collection, parent) {}

View File

@@ -124,11 +124,14 @@ return_song:
Song song = LoadSong(location, 0, dir); Song song = LoadSong(location, 0, dir);
// Override metadata with what was in the playlist // Override metadata with what was in the playlist
song.set_title(title); if (song.source() != Song::Source_Collection) {
song.set_artist(artist); if (!title.isEmpty()) song.set_title(title);
song.set_album(album); if (!artist.isEmpty()) song.set_artist(artist);
song.set_length_nanosec(nanosec); if (!album.isEmpty()) song.set_album(album);
song.set_track(track_num); if (nanosec > 0) song.set_length_nanosec(nanosec);
if (track_num > 0) song.set_track(track_num);
}
return song; return song;
} }

View File

@@ -876,7 +876,7 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id_re
QJsonObject json_obj = value.toObject(); QJsonObject json_obj = value.toObject();
++songs_received; ++songs_received;
Song song; Song song(Song::Source_Qobuz);
ParseSong(song, json_obj, artist_id, album_id, album_artist, album, cover_url); ParseSong(song, json_obj, artist_id, album_id, album_artist, album, cover_url);
if (!song.is_valid()) continue; if (!song.is_valid()) continue;
if (song.disc() >= 2) multidisc = true; if (song.disc() >= 2) multidisc = true;

View File

@@ -71,9 +71,11 @@ ListenBrainzScrobbler::ListenBrainzScrobbler(Application *app, QObject *parent)
app_(app), app_(app),
network_(new NetworkAccessManager(this)), network_(new NetworkAccessManager(this)),
cache_(new ScrobblerCache(kCacheFile, this)), cache_(new ScrobblerCache(kCacheFile, this)),
server_(nullptr),
enabled_(false), enabled_(false),
expires_in_(-1), expires_in_(-1),
submitted_(false) { submitted_(false),
timestamp_(0) {
ReloadSettings(); ReloadSettings();
LoadSession(); LoadSession();
@@ -123,16 +125,19 @@ void ListenBrainzScrobbler::Logout() {
void ListenBrainzScrobbler::Authenticate(const bool https) { void ListenBrainzScrobbler::Authenticate(const bool https) {
LocalRedirectServer *server = new LocalRedirectServer(https, this); if (!server_) {
if (!server->Listen()) { server_ = new LocalRedirectServer(https, this);
AuthError(server->error()); if (!server_->Listen()) {
delete server; AuthError(server_->error());
delete server_;
server_ = nullptr;
return; return;
} }
NewClosure(server, SIGNAL(Finished()), this, &ListenBrainzScrobbler::RedirectArrived, server); connect(server_, SIGNAL(Finished()), this, SLOT(RedirectArrived()));
}
QUrl redirect_url(kRedirectUrl); QUrl redirect_url(kRedirectUrl);
redirect_url.setPort(server->url().port()); redirect_url.setPort(server_->url().port());
QUrlQuery url_query; QUrlQuery url_query;
url_query.addQueryItem("response_type", "code"); url_query.addQueryItem("response_type", "code");
@@ -151,25 +156,39 @@ void ListenBrainzScrobbler::Authenticate(const bool https) {
} }
void ListenBrainzScrobbler::RedirectArrived(LocalRedirectServer *server) { void ListenBrainzScrobbler::RedirectArrived() {
server->deleteLater(); if (!server_) return;
QUrl url = server->request_url(); if (server_->error().isEmpty()) {
if (!QUrlQuery(url).queryItemValue("error").isEmpty()) { QUrl url = server_->request_url();
if (url.isValid()) {
QUrlQuery url_query(url);
if (url_query.hasQueryItem("error")) {
AuthError(QUrlQuery(url).queryItemValue("error")); AuthError(QUrlQuery(url).queryItemValue("error"));
return;
} }
if (QUrlQuery(url).queryItemValue("code").isEmpty()) { else if (url_query.hasQueryItem("code")) {
AuthError("Redirect missing token code!"); RequestSession(url, url_query.queryItemValue("code"));
return; }
else {
AuthError(tr("Redirect missing token code!"));
}
}
else {
AuthError(tr("Received invalid reply from web browser."));
}
}
else {
AuthError(server_->error());
} }
RequestSession(url, QUrlQuery(url).queryItemValue("code").toUtf8()); server_->close();
server_->deleteLater();
server_ = nullptr;
} }
void ListenBrainzScrobbler::RequestSession(QUrl url, QString token) { void ListenBrainzScrobbler::RequestSession(const QUrl &url, const QString &token) {
QUrl session_url(kAuthTokenUrl); QUrl session_url(kAuthTokenUrl);
QUrlQuery url_query; QUrlQuery url_query;
@@ -345,13 +364,16 @@ void ListenBrainzScrobbler::UpdateNowPlaying(const Song &song) {
album = album.remove(Song::kAlbumRemoveMisc); album = album.remove(Song::kAlbumRemoveMisc);
QJsonObject object_track_metadata; QJsonObject object_track_metadata;
if (song.albumartist().isEmpty() || song.albumartist().toLower() == "various artists") { if (song.albumartist().isEmpty() || song.albumartist().toLower() == Song::kVariousArtists) {
object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.artist())); object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.artist()));
} }
else { else {
object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.albumartist())); object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.albumartist()));
} }
if (!album.isEmpty())
object_track_metadata.insert("release_name", QJsonValue::fromVariant(album)); object_track_metadata.insert("release_name", QJsonValue::fromVariant(album));
object_track_metadata.insert("track_name", QJsonValue::fromVariant(song.title())); object_track_metadata.insert("track_name", QJsonValue::fromVariant(song.title()));
QJsonObject object_listen; QJsonObject object_listen;
@@ -459,16 +481,19 @@ void ListenBrainzScrobbler::Submit() {
for (ScrobblerCacheItem *item : cache_->List()) { for (ScrobblerCacheItem *item : cache_->List()) {
if (item->sent_) continue; if (item->sent_) continue;
item->sent_ = true; item->sent_ = true;
i++; ++i;
list << item->timestamp_; list << item->timestamp_;
QJsonObject object_listen; QJsonObject object_listen;
object_listen.insert("listened_at", QJsonValue::fromVariant(item->timestamp_)); object_listen.insert("listened_at", QJsonValue::fromVariant(item->timestamp_));
QJsonObject object_track_metadata; QJsonObject object_track_metadata;
if (item->albumartist_.isEmpty() || item->albumartist_.toLower() == "various artists") if (item->albumartist_.isEmpty() || item->albumartist_.toLower() == Song::kVariousArtists)
object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->artist_)); object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->artist_));
else else
object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->albumartist_)); object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->albumartist_));
if (!item->album_.isEmpty())
object_track_metadata.insert("release_name", QJsonValue::fromVariant(item->album_)); object_track_metadata.insert("release_name", QJsonValue::fromVariant(item->album_));
object_track_metadata.insert("track_name", QJsonValue::fromVariant(item->song_)); object_track_metadata.insert("track_name", QJsonValue::fromVariant(item->song_));
object_listen.insert("track_metadata", object_track_metadata); object_listen.insert("track_metadata", object_track_metadata);
array.append(QJsonValue::fromVariant(object_listen)); array.append(QJsonValue::fromVariant(object_listen));
@@ -525,11 +550,11 @@ void ListenBrainzScrobbler::ScrobbleRequestFinished(QNetworkReply *reply, QList<
} }
void ListenBrainzScrobbler::AuthError(QString error) { void ListenBrainzScrobbler::AuthError(const QString &error) {
emit AuthenticationComplete(false, error); emit AuthenticationComplete(false, error);
} }
void ListenBrainzScrobbler::Error(QString error, QVariant debug) { void ListenBrainzScrobbler::Error(const QString &error, const QVariant &debug) {
qLog(Error) << "ListenBrainz:" << error; qLog(Error) << "ListenBrainz:" << error;
if (debug.isValid()) qLog(Debug) << debug; if (debug.isValid()) qLog(Debug) << debug;

View File

@@ -26,7 +26,6 @@
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QNetworkReply>
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
#include <QByteArray> #include <QByteArray>
@@ -37,6 +36,8 @@
#include "scrobblerservice.h" #include "scrobblerservice.h"
#include "scrobblercache.h" #include "scrobblercache.h"
class QNetworkReply;
class Application; class Application;
class NetworkAccessManager; class NetworkAccessManager;
class LocalRedirectServer; class LocalRedirectServer;
@@ -75,7 +76,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
void WriteCache() { cache_->WriteCache(); } void WriteCache() { cache_->WriteCache(); }
private slots: private slots:
void RedirectArrived(LocalRedirectServer *server); void RedirectArrived();
void AuthenticateReplyFinished(QNetworkReply *reply); void AuthenticateReplyFinished(QNetworkReply *reply);
void UpdateNowPlayingRequestFinished(QNetworkReply *reply); void UpdateNowPlayingRequestFinished(QNetworkReply *reply);
void ScrobbleRequestFinished(QNetworkReply *reply, QList<quint64>); void ScrobbleRequestFinished(QNetworkReply *reply, QList<quint64>);
@@ -84,9 +85,9 @@ class ListenBrainzScrobbler : public ScrobblerService {
QNetworkReply *CreateRequest(const QUrl &url, const QJsonDocument &json_doc); QNetworkReply *CreateRequest(const QUrl &url, const QJsonDocument &json_doc);
QByteArray GetReplyData(QNetworkReply *reply); QByteArray GetReplyData(QNetworkReply *reply);
void RequestSession(QUrl url, QString token); void RequestSession(const QUrl &url, const QString &token);
void AuthError(QString error); void AuthError(const QString &error);
void Error(QString error, QVariant debug = QVariant()); void Error(const QString &error, const QVariant &debug = QVariant());
void DoSubmit(); void DoSubmit();
static const char *kAuthUrl; static const char *kAuthUrl;
@@ -101,6 +102,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
Application *app_; Application *app_;
NetworkAccessManager *network_; NetworkAccessManager *network_;
ScrobblerCache *cache_; ScrobblerCache *cache_;
LocalRedirectServer *server_;
bool enabled_; bool enabled_;
QString user_token_; QString user_token_;
QString access_token_; QString access_token_;

View File

@@ -28,6 +28,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include "core/song.h"
class ScrobblerCacheItem : public QObject { class ScrobblerCacheItem : public QObject {
Q_OBJECT Q_OBJECT
@@ -35,7 +37,7 @@ class ScrobblerCacheItem : public QObject {
explicit ScrobblerCacheItem(const QString &artist, const QString &album, const QString &song, const QString &albumartist, const int track, const qint64 duration, const quint64 &timestamp); explicit ScrobblerCacheItem(const QString &artist, const QString &album, const QString &song, const QString &albumartist, const int track, const qint64 duration, const quint64 &timestamp);
~ScrobblerCacheItem(); ~ScrobblerCacheItem();
QString effective_albumartist() const { return albumartist_.isEmpty() ? artist_ : albumartist_; } QString effective_albumartist() const { return albumartist_.isEmpty() || albumartist_.toLower() == Song::kVariousArtists ? artist_ : albumartist_; }
public: public:
QString artist_; QString artist_;

View File

@@ -52,7 +52,7 @@ class ScrobblerService : public QObject {
virtual void ClearPlaying() = 0; virtual void ClearPlaying() = 0;
virtual void Scrobble(const Song &song) = 0; virtual void Scrobble(const Song &song) = 0;
virtual void Love() {} virtual void Love() {}
virtual void Error(QString error, QVariant debug = QVariant()) = 0; virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
virtual void DoSubmit() = 0; virtual void DoSubmit() = 0;
virtual void Submitted() = 0; virtual void Submitted() = 0;

View File

@@ -72,9 +72,11 @@ ScrobblingAPI20::ScrobblingAPI20(const QString &name, const QString &settings_gr
api_url_(api_url), api_url_(api_url),
batch_(batch), batch_(batch),
app_(app), app_(app),
server_(nullptr),
enabled_(false), enabled_(false),
subscriber_(false), subscriber_(false),
submitted_(false) {} submitted_(false),
timestamp_(0) {}
ScrobblingAPI20::~ScrobblingAPI20() {} ScrobblingAPI20::~ScrobblingAPI20() {}
@@ -121,16 +123,19 @@ void ScrobblingAPI20::Logout() {
void ScrobblingAPI20::Authenticate(const bool https) { void ScrobblingAPI20::Authenticate(const bool https) {
LocalRedirectServer *server = new LocalRedirectServer(https, this); if (!server_) {
if (!server->Listen()) { server_ = new LocalRedirectServer(https, this);
AuthError(server->error()); if (!server_->Listen()) {
delete server; AuthError(server_->error());
delete server_;
server_ = nullptr;
return; return;
} }
NewClosure(server, SIGNAL(Finished()), this, &ScrobblingAPI20::RedirectArrived, server); connect(server_, SIGNAL(Finished()), this, SLOT(RedirectArrived()));
}
QUrlQuery redirect_url_query; QUrlQuery redirect_url_query;
const QString port = QString::number(server->url().port()); const QString port = QString::number(server_->url().port());
redirect_url_query.addQueryItem("port", port); redirect_url_query.addQueryItem("port", port);
if (https) redirect_url_query.addQueryItem("https", QString("1")); if (https) redirect_url_query.addQueryItem("https", QString("1"));
QUrl redirect_url(kRedirectUrl); QUrl redirect_url(kRedirectUrl);
@@ -160,8 +165,11 @@ void ScrobblingAPI20::Authenticate(const bool https) {
QApplication::clipboard()->setText(url.toString()); QApplication::clipboard()->setText(url.toString());
break; break;
case QMessageBox::Cancel: case QMessageBox::Cancel:
server->close(); if (server_) {
server->deleteLater(); server_->close();
server_->deleteLater();
server_ = nullptr;
}
emit AuthenticationComplete(false); emit AuthenticationComplete(false);
break; break;
default: default:
@@ -170,31 +178,37 @@ void ScrobblingAPI20::Authenticate(const bool https) {
} }
void ScrobblingAPI20::RedirectArrived(LocalRedirectServer *server) { void ScrobblingAPI20::RedirectArrived() {
server->deleteLater(); if (!server_) return;
if (!server->error().isEmpty()) { if (server_->error().isEmpty()) {
AuthError(server->error()); QUrl url = server_->request_url();
return; if (url.isValid()) {
}
QUrl url = server->request_url();
if (!url.isValid()) {
AuthError(tr("Received invalid reply from web browser. Try the HTTPS option, or use another browser like Chromium or Chrome."));
return;
}
QUrlQuery url_query(url); QUrlQuery url_query(url);
if (url_query.hasQueryItem("token")) {
QString token = url_query.queryItemValue("token").toUtf8(); QString token = url_query.queryItemValue("token").toUtf8();
if (token.isEmpty()) {
AuthError(tr("Invalid reply from web browser. Missing token."));
return;
}
RequestSession(token); RequestSession(token);
}
else {
AuthError(tr("Invalid reply from web browser. Missing token."));
}
}
else {
AuthError(tr("Received invalid reply from web browser. Try the HTTPS option, or use another browser like Chromium or Chrome."));
}
}
else {
AuthError(server_->error());
}
server_->close();
server_->deleteLater();
server_ = nullptr;
} }
void ScrobblingAPI20::RequestSession(QString token) { void ScrobblingAPI20::RequestSession(const QString &token) {
QUrl session_url(api_url_); QUrl session_url(api_url_);
QUrlQuery session_url_query; QUrlQuery session_url_query;
@@ -422,11 +436,14 @@ void ScrobblingAPI20::UpdateNowPlaying(const Song &song) {
ParamList params = ParamList() ParamList params = ParamList()
<< Param("method", "track.updateNowPlaying") << Param("method", "track.updateNowPlaying")
<< Param("artist", prefer_albumartist_ ? song.effective_albumartist() : song.artist()) << Param("artist", prefer_albumartist_ && song.effective_albumartist() != Song::kVariousArtists ? song.effective_albumartist() : song.artist())
<< Param("track", song.title()) << Param("track", song.title());
<< Param("album", album);
if (!prefer_albumartist_ && !song.albumartist().isEmpty()) params << Param("albumArtist", song.albumartist()); if (!album.isEmpty())
params << Param("album", album);
if (!prefer_albumartist_ && !song.albumartist().isEmpty() && song.albumartist().toLower() != Song::kVariousArtists)
params << Param("albumArtist", song.albumartist());
QNetworkReply *reply = CreateRequest(params); QNetworkReply *reply = CreateRequest(params);
NewClosure(reply, SIGNAL(finished()), this, SLOT(UpdateNowPlayingRequestFinished(QNetworkReply*)), reply); NewClosure(reply, SIGNAL(finished()), this, SLOT(UpdateNowPlayingRequestFinished(QNetworkReply*)), reply);
@@ -518,16 +535,22 @@ void ScrobblingAPI20::Submit() {
for (ScrobblerCacheItem *item : cache()->List()) { for (ScrobblerCacheItem *item : cache()->List()) {
if (item->sent_) continue; if (item->sent_) continue;
item->sent_ = true; item->sent_ = true;
if (!batch_) { SendSingleScrobble(item); continue; } if (!batch_) {
SendSingleScrobble(item);
continue;
}
i++; i++;
list << item->timestamp_; list << item->timestamp_;
params << Param(QString("%1[%2]").arg("artist").arg(i), prefer_albumartist_ ? item->effective_albumartist() : item->artist_); params << Param(QString("%1[%2]").arg("artist").arg(i), prefer_albumartist_ ? item->effective_albumartist() : item->artist_);
params << Param(QString("%1[%2]").arg("album").arg(i), item->album_);
params << Param(QString("%1[%2]").arg("track").arg(i), item->song_); params << Param(QString("%1[%2]").arg("track").arg(i), item->song_);
params << Param(QString("%1[%2]").arg("timestamp").arg(i), QString::number(item->timestamp_)); params << Param(QString("%1[%2]").arg("timestamp").arg(i), QString::number(item->timestamp_));
params << Param(QString("%1[%2]").arg("duration").arg(i), QString::number(item->duration_ / kNsecPerSec)); params << Param(QString("%1[%2]").arg("duration").arg(i), QString::number(item->duration_ / kNsecPerSec));
if (!prefer_albumartist_ && !item->albumartist_.isEmpty()) params << Param(QString("%1[%2]").arg("albumArtist").arg(i), item->albumartist_); if (!item->album_.isEmpty())
if (item->track_ > 0) params << Param(QString("%1[%2]").arg(i).arg("trackNumber"), QString::number(item->track_)); params << Param(QString("%1[%2]").arg("album").arg(i), item->album_);
if (!prefer_albumartist_ && !item->albumartist_.isEmpty() && item->albumartist_.toLower() != Song::kVariousArtists)
params << Param(QString("%1[%2]").arg("albumArtist").arg(i), item->albumartist_);
if (item->track_ > 0)
params << Param(QString("%1[%2]").arg(i).arg("trackNumber"), QString::number(item->track_));
if (i >= kScrobblesPerRequest) break; if (i >= kScrobblesPerRequest) break;
} }
@@ -699,13 +722,16 @@ void ScrobblingAPI20::SendSingleScrobble(ScrobblerCacheItem *item) {
ParamList params = ParamList() ParamList params = ParamList()
<< Param("method", "track.scrobble") << Param("method", "track.scrobble")
<< Param("artist", prefer_albumartist_ ? item->effective_albumartist() : item->artist_) << Param("artist", prefer_albumartist_ ? item->effective_albumartist() : item->artist_)
<< Param("album", item->album_)
<< Param("track", item->song_) << Param("track", item->song_)
<< Param("timestamp", QString::number(item->timestamp_)) << Param("timestamp", QString::number(item->timestamp_))
<< Param("duration", QString::number(item->duration_ / kNsecPerSec)); << Param("duration", QString::number(item->duration_ / kNsecPerSec));
if (!prefer_albumartist_ && !item->albumartist_.isEmpty()) params << Param("albumArtist", item->albumartist_); if (!item->album_.isEmpty())
if (item->track_ > 0) params << Param("trackNumber", QString::number(item->track_)); params << Param("album", item->album_);
if (!prefer_albumartist_ && !item->albumartist_.isEmpty() && item->albumartist_.toLower() != Song::kVariousArtists)
params << Param("albumArtist", item->albumartist_);
if (item->track_ > 0)
params << Param("trackNumber", QString::number(item->track_));
QNetworkReply *reply = CreateRequest(params); QNetworkReply *reply = CreateRequest(params);
NewClosure(reply, SIGNAL(finished()), this, SLOT(SingleScrobbleRequestFinished(QNetworkReply*, quint64)), reply, item->timestamp_); NewClosure(reply, SIGNAL(finished()), this, SLOT(SingleScrobbleRequestFinished(QNetworkReply*, quint64)), reply, item->timestamp_);
@@ -846,10 +872,14 @@ void ScrobblingAPI20::Love() {
ParamList params = ParamList() ParamList params = ParamList()
<< Param("method", "track.love") << Param("method", "track.love")
<< Param("artist", song_playing_.artist()) << Param("artist", prefer_albumartist_ && song_playing_.effective_albumartist() != Song::kVariousArtists ? song_playing_.effective_albumartist() : song_playing_.artist())
<< Param("track", song_playing_.title()) << Param("track", song_playing_.title());
<< Param("album", song_playing_.album());
if (!song_playing_.albumartist().isEmpty()) params << Param("albumArtist", song_playing_.albumartist()); if (!song_playing_.album().isEmpty())
params << Param("album", song_playing_.album());
if (!prefer_albumartist_ && !song_playing_.albumartist().isEmpty() && song_playing_.albumartist().toLower() != Song::kVariousArtists)
params << Param("albumArtist", song_playing_.albumartist());
QNetworkReply *reply = CreateRequest(params); QNetworkReply *reply = CreateRequest(params);
NewClosure(reply, SIGNAL(finished()), this, SLOT(LoveRequestFinished(QNetworkReply*)), reply); NewClosure(reply, SIGNAL(finished()), this, SLOT(LoveRequestFinished(QNetworkReply*)), reply);
@@ -906,18 +936,18 @@ void ScrobblingAPI20::LoveRequestFinished(QNetworkReply *reply) {
} }
void ScrobblingAPI20::AuthError(QString error) { void ScrobblingAPI20::AuthError(const QString &error) {
emit AuthenticationComplete(false, error); emit AuthenticationComplete(false, error);
} }
void ScrobblingAPI20::Error(QString error, QVariant debug) { void ScrobblingAPI20::Error(const QString &error, const QVariant &debug) {
qLog(Error) << name_ << error; qLog(Error) << name_ << error;
if (debug.isValid()) qLog(Debug) << debug; if (debug.isValid()) qLog(Debug) << debug;
} }
QString ScrobblingAPI20::ErrorString(ScrobbleErrorCode error) const { QString ScrobblingAPI20::ErrorString(const ScrobbleErrorCode error) const {
switch (error) { switch (error) {
case ScrobbleErrorCode::InvalidService: case ScrobbleErrorCode::InvalidService:

View File

@@ -26,7 +26,6 @@
#include <QtGlobal> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QNetworkReply>
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
#include <QByteArray> #include <QByteArray>
@@ -36,6 +35,8 @@
#include "scrobblerservice.h" #include "scrobblerservice.h"
#include "scrobblercache.h" #include "scrobblercache.h"
class QNetworkReply;
class Application; class Application;
class NetworkAccessManager; class NetworkAccessManager;
class LocalRedirectServer; class LocalRedirectServer;
@@ -81,7 +82,7 @@ class ScrobblingAPI20 : public ScrobblerService {
void WriteCache() { cache()->WriteCache(); } void WriteCache() { cache()->WriteCache(); }
private slots: private slots:
void RedirectArrived(LocalRedirectServer *server); void RedirectArrived();
void AuthenticateReplyFinished(QNetworkReply *reply); void AuthenticateReplyFinished(QNetworkReply *reply);
void UpdateNowPlayingRequestFinished(QNetworkReply *reply); void UpdateNowPlayingRequestFinished(QNetworkReply *reply);
void ScrobbleRequestFinished(QNetworkReply *reply, QList<quint64>); void ScrobbleRequestFinished(QNetworkReply *reply, QList<quint64>);
@@ -130,11 +131,11 @@ class ScrobblingAPI20 : public ScrobblerService {
QNetworkReply *CreateRequest(const ParamList &request_params); QNetworkReply *CreateRequest(const ParamList &request_params);
QByteArray GetReplyData(QNetworkReply *reply); QByteArray GetReplyData(QNetworkReply *reply);
void RequestSession(QString token); void RequestSession(const QString &token);
void AuthError(QString error); void AuthError(const QString &error);
void SendSingleScrobble(ScrobblerCacheItem *item); void SendSingleScrobble(ScrobblerCacheItem *item);
void Error(QString error, QVariant debug = QVariant()); void Error(const QString &error, const QVariant &debug = QVariant());
QString ErrorString(ScrobbleErrorCode error) const; QString ErrorString(const ScrobbleErrorCode error) const;
void DoSubmit(); void DoSubmit();
QString name_; QString name_;
@@ -144,6 +145,7 @@ class ScrobblingAPI20 : public ScrobblerService {
bool batch_; bool batch_;
Application *app_; Application *app_;
LocalRedirectServer *server_;
bool enabled_; bool enabled_;
bool https_; bool https_;

View File

@@ -65,7 +65,6 @@ BackendSettingsPage::BackendSettingsPage(SettingsDialog *dialog) : SettingsPage(
ui_->label_bufferminfillvalue->setMinimumWidth(QFontMetrics(ui_->label_bufferminfillvalue->font()).width("WW%")); ui_->label_bufferminfillvalue->setMinimumWidth(QFontMetrics(ui_->label_bufferminfillvalue->font()).width("WW%"));
ui_->label_replaygainpreamp->setMinimumWidth(QFontMetrics(ui_->label_replaygainpreamp->font()).width("-WW.W dB")); ui_->label_replaygainpreamp->setMinimumWidth(QFontMetrics(ui_->label_replaygainpreamp->font()).width("-WW.W dB"));
#endif #endif
RgPreampChanged(ui_->stickslider_replaygainpreamp->value());
s_.beginGroup(BackendSettingsPage::kSettingsGroup); s_.beginGroup(BackendSettingsPage::kSettingsGroup);
@@ -179,6 +178,7 @@ void BackendSettingsPage::Load() {
connect(ui_->checkbox_volume_control, SIGNAL(toggled(bool)), SLOT(FadingOptionsChanged())); connect(ui_->checkbox_volume_control, SIGNAL(toggled(bool)), SLOT(FadingOptionsChanged()));
FadingOptionsChanged(); FadingOptionsChanged();
RgPreampChanged(ui_->stickslider_replaygainpreamp->value());
} }

View File

@@ -71,19 +71,6 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog *dialog) : SettingsP
} }
#endif #endif
ui_->combobox_doubleclickaddmode->setItemData(0, AddBehaviour_Append);
ui_->combobox_doubleclickaddmode->setItemData(1, AddBehaviour_Load);
ui_->combobox_doubleclickaddmode->setItemData(2, AddBehaviour_OpenInNew);
ui_->combobox_doubleclickaddmode->setItemData(3, AddBehaviour_Enqueue);
ui_->combobox_doubleclickplaymode->setItemData(0, PlayBehaviour_Never);
ui_->combobox_doubleclickplaymode->setItemData(1, PlayBehaviour_IfStopped);
ui_->combobox_doubleclickplaymode->setItemData(2, PlayBehaviour_Always);
ui_->combobox_menuplaymode->setItemData(0, PlayBehaviour_Never);
ui_->combobox_menuplaymode->setItemData(1, PlayBehaviour_IfStopped);
ui_->combobox_menuplaymode->setItemData(2, PlayBehaviour_Always);
#ifdef HAVE_TRANSLATIONS #ifdef HAVE_TRANSLATIONS
// Populate the language combo box. We do this by looking at all the compiled in translations. // Populate the language combo box. We do this by looking at all the compiled in translations.
QDir dir(":/translations/"); QDir dir(":/translations/");
@@ -121,6 +108,25 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog *dialog) : SettingsP
ui_->groupbox_language->setVisible(false); ui_->groupbox_language->setVisible(false);
#endif #endif
ui_->combobox_menuplaymode->setItemData(0, PlayBehaviour_Never);
ui_->combobox_menuplaymode->setItemData(1, PlayBehaviour_IfStopped);
ui_->combobox_menuplaymode->setItemData(2, PlayBehaviour_Always);
ui_->combobox_previousmode->setItemData(0, PreviousBehaviour_DontRestart);
ui_->combobox_previousmode->setItemData(1, PreviousBehaviour_Restart);
ui_->combobox_doubleclickaddmode->setItemData(0, AddBehaviour_Append);
ui_->combobox_doubleclickaddmode->setItemData(1, AddBehaviour_Load);
ui_->combobox_doubleclickaddmode->setItemData(2, AddBehaviour_OpenInNew);
ui_->combobox_doubleclickaddmode->setItemData(3, AddBehaviour_Enqueue);
ui_->combobox_doubleclickplaymode->setItemData(0, PlayBehaviour_Never);
ui_->combobox_doubleclickplaymode->setItemData(1, PlayBehaviour_IfStopped);
ui_->combobox_doubleclickplaymode->setItemData(2, PlayBehaviour_Always);
ui_->combobox_doubleclickplaylistaddmode->setItemData(0, PlaylistAddBehaviour_Play);
ui_->combobox_doubleclickplaylistaddmode->setItemData(1, PlaylistAddBehaviour_Enqueue);
#ifdef HAVE_X11 #ifdef HAVE_X11
QString de = Utilities::DesktopEnvironment(); QString de = Utilities::DesktopEnvironment();
if (de.toLower() == "kde") if (de.toLower() == "kde")
@@ -154,7 +160,7 @@ void BehaviourSettingsPage::Load() {
ui_->checkbox_keeprunning->setChecked(false); ui_->checkbox_keeprunning->setChecked(false);
} }
#endif #endif
ui_->checkbox_resumeplayback->setChecked(s.value("resumeplayback", false).toBool());
ui_->checkbox_playingwidget->setChecked(s.value("playing_widget", true).toBool()); ui_->checkbox_playingwidget->setChecked(s.value("playing_widget", true).toBool());
MainWindow::StartupBehaviour behaviour = MainWindow::StartupBehaviour(s.value("startupbehaviour", MainWindow::Startup_Remember).toInt()); MainWindow::StartupBehaviour behaviour = MainWindow::StartupBehaviour(s.value("startupbehaviour", MainWindow::Startup_Remember).toInt());
@@ -164,20 +170,24 @@ void BehaviourSettingsPage::Load() {
case MainWindow::Startup_Remember: ui_->radiobutton_remember->setChecked(true); break; case MainWindow::Startup_Remember: ui_->radiobutton_remember->setChecked(true); break;
} }
ui_->checkbox_resumeplayback->setChecked(s.value("resumeplayback", false).toBool());
ui_->combobox_doubleclickaddmode->setCurrentIndex(ui_->combobox_doubleclickaddmode->findData(s.value("doubleclick_addmode", AddBehaviour_Append).toInt()));
ui_->combobox_doubleclickplaymode->setCurrentIndex(ui_->combobox_doubleclickplaymode->findData(s.value("doubleclick_playmode", PlayBehaviour_Never).toInt()));
ui_->combobox_menuplaymode->setCurrentIndex(ui_->combobox_menuplaymode->findData(s.value("menu_playmode", PlayBehaviour_Never).toInt()));
ui_->spinbox_seekstepsec->setValue(s.value("seek_step_sec", 10).toInt());
QString name = language_map_.key(s.value("language").toString()); QString name = language_map_.key(s.value("language").toString());
if (name.isEmpty()) if (name.isEmpty())
ui_->combobox_language->setCurrentIndex(0); ui_->combobox_language->setCurrentIndex(0);
else else
ui_->combobox_language->setCurrentIndex(ui_->combobox_language->findText(name)); ui_->combobox_language->setCurrentIndex(ui_->combobox_language->findText(name));
ui_->combobox_menuplaymode->setCurrentIndex(ui_->combobox_menuplaymode->findData(s.value("menu_playmode", PlayBehaviour_Never).toInt()));
ui_->combobox_previousmode->setCurrentIndex(ui_->combobox_previousmode->findData(s.value("menu_previousmode", PreviousBehaviour_DontRestart).toInt()));
ui_->combobox_doubleclickaddmode->setCurrentIndex(ui_->combobox_doubleclickaddmode->findData(s.value("doubleclick_addmode", AddBehaviour_Append).toInt()));
ui_->combobox_doubleclickplaymode->setCurrentIndex(ui_->combobox_doubleclickplaymode->findData(s.value("doubleclick_playmode", PlayBehaviour_Never).toInt()));
ui_->combobox_doubleclickplaylistaddmode->setCurrentIndex(ui_->combobox_doubleclickplaylistaddmode->findData(s.value("doubleclick_playlist_addmode", PlaylistAddBehaviour_Play).toInt()));
ui_->spinbox_seekstepsec->setValue(s.value("seek_step_sec", 10).toInt());
s.endGroup(); s.endGroup();
} }
@@ -187,28 +197,37 @@ void BehaviourSettingsPage::Save() {
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("showtrayicon", ui_->checkbox_showtrayicon->isChecked());
s.setValue("keeprunning", ui_->checkbox_keeprunning->isChecked());
s.setValue("resumeplayback", ui_->checkbox_resumeplayback->isChecked());
s.setValue("playing_widget", ui_->checkbox_playingwidget->isChecked());
s.setValue("scrolltrayicon", ui_->checkbox_scrolltrayicon->isChecked());
MainWindow::StartupBehaviour behaviour = MainWindow::Startup_Remember; MainWindow::StartupBehaviour behaviour = MainWindow::Startup_Remember;
if (ui_->radiobutton_alwayshide->isChecked()) behaviour = MainWindow::Startup_AlwaysHide; if (ui_->radiobutton_alwayshide->isChecked()) behaviour = MainWindow::Startup_AlwaysHide;
if (ui_->radiobutton_alwaysshow->isChecked()) behaviour = MainWindow::Startup_AlwaysShow; if (ui_->radiobutton_alwaysshow->isChecked()) behaviour = MainWindow::Startup_AlwaysShow;
if (ui_->radiobutton_remember->isChecked()) behaviour = MainWindow::Startup_Remember; if (ui_->radiobutton_remember->isChecked()) behaviour = MainWindow::Startup_Remember;
AddBehaviour doubleclick_addmode = AddBehaviour(ui_->combobox_doubleclickaddmode->itemData(ui_->combobox_doubleclickaddmode->currentIndex()).toInt());
PlayBehaviour doubleclick_playmode = PlayBehaviour(ui_->combobox_doubleclickplaymode->itemData(ui_->combobox_doubleclickplaymode->currentIndex()).toInt());
PlayBehaviour menu_playmode = PlayBehaviour(ui_->combobox_menuplaymode->itemData(ui_->combobox_menuplaymode->currentIndex()).toInt());
s.setValue("showtrayicon", ui_->checkbox_showtrayicon->isChecked());
s.setValue("scrolltrayicon", ui_->checkbox_scrolltrayicon->isChecked());
s.setValue("keeprunning", ui_->checkbox_keeprunning->isChecked());
s.setValue("resumeplayback", ui_->checkbox_resumeplayback->isChecked());
s.setValue("playing_widget", ui_->checkbox_playingwidget->isChecked());
s.setValue("startupbehaviour", int(behaviour)); s.setValue("startupbehaviour", int(behaviour));
s.setValue("doubleclick_addmode", doubleclick_addmode);
s.setValue("doubleclick_playmode", doubleclick_playmode);
s.setValue("menu_playmode", menu_playmode);
s.setValue("seek_step_sec", ui_->spinbox_seekstepsec->value());
s.setValue("language", language_map_.contains(ui_->combobox_language->currentText()) ? language_map_[ui_->combobox_language->currentText()] : QString()); s.setValue("language", language_map_.contains(ui_->combobox_language->currentText()) ? language_map_[ui_->combobox_language->currentText()] : QString());
PlayBehaviour menu_playmode = PlayBehaviour(ui_->combobox_menuplaymode->itemData(ui_->combobox_menuplaymode->currentIndex()).toInt());
PreviousBehaviour menu_previousmode = PreviousBehaviour(ui_->combobox_previousmode->itemData(ui_->combobox_previousmode->currentIndex()).toInt());
AddBehaviour doubleclick_addmode = AddBehaviour(ui_->combobox_doubleclickaddmode->itemData(ui_->combobox_doubleclickaddmode->currentIndex()).toInt());
PlayBehaviour doubleclick_playmode = PlayBehaviour(ui_->combobox_doubleclickplaymode->itemData(ui_->combobox_doubleclickplaymode->currentIndex()).toInt());
PlaylistAddBehaviour doubleclick_playlist_addmode = PlaylistAddBehaviour(ui_->combobox_doubleclickplaylistaddmode->itemData(ui_->combobox_doubleclickplaylistaddmode->currentIndex()).toInt());
s.setValue("menu_playmode", menu_playmode);
s.setValue("menu_previousmode", menu_previousmode);
s.setValue("doubleclick_addmode", doubleclick_addmode);
s.setValue("doubleclick_playmode", doubleclick_playmode);
s.setValue("doubleclick_playlist_addmode", doubleclick_playlist_addmode);
s.setValue("seek_step_sec", ui_->spinbox_seekstepsec->value());
s.endGroup(); s.endGroup();
} }

View File

@@ -43,7 +43,17 @@ public:
static const char *kSettingsGroup; static const char *kSettingsGroup;
// Don't change the values enum PlayBehaviour {
PlayBehaviour_Never = 1,
PlayBehaviour_IfStopped = 2,
PlayBehaviour_Always = 3,
};
enum PreviousBehaviour {
PreviousBehaviour_DontRestart = 1,
PreviousBehaviour_Restart = 2
};
enum AddBehaviour { enum AddBehaviour {
AddBehaviour_Append = 1, AddBehaviour_Append = 1,
AddBehaviour_Enqueue = 2, AddBehaviour_Enqueue = 2,
@@ -51,14 +61,6 @@ public:
AddBehaviour_OpenInNew = 4 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 { enum PlaylistAddBehaviour {
PlaylistAddBehaviour_Play = 1, PlaylistAddBehaviour_Play = 1,
PlaylistAddBehaviour_Enqueue = 2, PlaylistAddBehaviour_Enqueue = 2,

View File

@@ -435,7 +435,7 @@ void SubsonicRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const qint64
QJsonObject json_obj = value.toObject(); QJsonObject json_obj = value.toObject();
++songs_received; ++songs_received;
Song song; Song song(Song::Source_Subsonic);
ParseSong(song, json_obj, artist_id, album_id, album_artist); ParseSong(song, json_obj, artist_id, album_id, album_artist);
if (!song.is_valid()) continue; if (!song.is_valid()) continue;
if (song.disc() >= 2) multidisc = true; if (song.disc() >= 2) multidisc = true;

View File

@@ -834,7 +834,7 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id, c
} }
++songs_received; ++songs_received;
Song song; Song song(Song::Source_Tidal);
ParseSong(song, json_obj, artist_id, album_id, album_artist); ParseSong(song, json_obj, artist_id, album_id, album_artist);
if (!song.is_valid()) continue; if (!song.is_valid()) continue;
if (song.disc() >= 2) multidisc = true; if (song.disc() >= 2) multidisc = true;

View File

@@ -575,7 +575,7 @@ void Transcoder::SetElementProperties(const QString &name, GObject *object) {
guint properties_count = 0; guint properties_count = 0;
GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), &properties_count); GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), &properties_count);
for (int i = 0; i < properties_count; ++i) { for (uint i = 0; i < properties_count; ++i) {
GParamSpec *property = properties[i]; GParamSpec *property = properties[i];
const QVariant value = s.value(property->name); const QVariant value = s.value(property->name);

View File

@@ -120,7 +120,7 @@ msgstr " s"
msgid " seconds" msgid " seconds"
msgstr " segundos" msgstr " segundos"
#: scrobbler/scrobblingapi20.cpp:145 scrobbler/scrobblingapi20.cpp:154 #: scrobbler/scrobblingapi20.cpp:150 scrobbler/scrobblingapi20.cpp:159
#, qt-format #, qt-format
msgid "%1 Scrobbler Authentication" msgid "%1 Scrobbler Authentication"
msgstr "Inicio de sesión en el servicio de registro de reproducción %1" msgstr "Inicio de sesión en el servicio de registro de reproducción %1"
@@ -828,7 +828,7 @@ msgstr "Apariencia"
msgid "Append files/URLs to the playlist" msgid "Append files/URLs to the playlist"
msgstr "Añadir archivos/URL a la lista de reproducción" msgstr "Añadir archivos/URL a la lista de reproducción"
#: context/contextalbumsview.cpp:358 collection/collectionview.cpp:333 #: context/contextalbumsview.cpp:271 collection/collectionview.cpp:333
#: widgets/fileviewlist.cpp:43 internet/internetsearchview.cpp:433 #: widgets/fileviewlist.cpp:43 internet/internetsearchview.cpp:433
#: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228 #: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228
msgid "Append to current playlist" msgid "Append to current playlist"
@@ -842,7 +842,7 @@ msgstr "Añadir a la lista de reproducción"
msgid "Apply compression to prevent clipping" msgid "Apply compression to prevent clipping"
msgstr "Aplicar compresión para evitar saturación" msgstr "Aplicar compresión para evitar saturación"
#: equalizer/equalizer.cpp:229 #: equalizer/equalizer.cpp:227
#, qt-format #, qt-format
msgid "Are you sure you want to delete the \"%1\" preset?" msgid "Are you sure you want to delete the \"%1\" preset?"
msgstr "¿Seguro que desea eliminar el ajuste predefinido «%1»?" msgstr "¿Seguro que desea eliminar el ajuste predefinido «%1»?"
@@ -1044,7 +1044,7 @@ msgstr "Búfer"
msgid "Buffer duration" msgid "Buffer duration"
msgstr "Duración del búfer" msgstr "Duración del búfer"
#: engine/gstengine.cpp:622 #: engine/gstengine.cpp:636
msgid "Buffering" msgid "Buffering"
msgstr "Guardando en búfer" msgstr "Guardando en búfer"
@@ -1052,11 +1052,11 @@ msgstr "Guardando en búfer"
msgid "C&onsole" msgid "C&onsole"
msgstr "C&onsola" msgstr "C&onsola"
#: core/song.cpp:481 #: core/song.cpp:474
msgid "CD" msgid "CD"
msgstr "CD" msgstr "CD"
#: core/songloader.cpp:195 #: core/songloader.cpp:196
msgid "CD playback is only available with the GStreamer engine." msgid "CD playback is only available with the GStreamer engine."
msgstr "La reproducción de CD solo es posible con GStreamer." msgstr "La reproducción de CD solo es posible con GStreamer."
@@ -1092,7 +1092,7 @@ msgstr "Elegir color…"
msgid "Choose font..." msgid "Choose font..."
msgstr "Elegir tipo de letra…" msgstr "Elegir tipo de letra…"
#: equalizer/equalizer.cpp:140 #: equalizer/equalizer.cpp:138
msgid "Classical" msgid "Classical"
msgstr "Clásica" msgstr "Clásica"
@@ -1143,11 +1143,11 @@ msgid "Closing this window will stop searching for album covers."
msgstr "" msgstr ""
"Si cierra esta ventana se detendrá la búsqueda de portadas para los álbumes." "Si cierra esta ventana se detendrá la búsqueda de portadas para los álbumes."
#: equalizer/equalizer.cpp:141 #: equalizer/equalizer.cpp:139
msgid "Club" msgid "Club"
msgstr "Club" msgstr "Club"
#: core/mainwindow.cpp:276 core/song.cpp:480 #: core/mainwindow.cpp:276 core/song.cpp:473
#: ../build/src/ui_collectionsettingspage.h:244 #: ../build/src/ui_collectionsettingspage.h:244
msgid "Collection" msgid "Collection"
msgstr "Colección" msgstr "Colección"
@@ -1249,7 +1249,7 @@ msgstr "Copiar la portada del álbum"
msgid "Copy to collection..." msgid "Copy to collection..."
msgstr "Copiar en la colección…" msgstr "Copiar en la colección…"
#: core/mainwindow.cpp:621 context/contextalbumsview.cpp:368 #: core/mainwindow.cpp:621 context/contextalbumsview.cpp:281
#: collection/collectionview.cpp:344 playlist/playlistlistcontainer.cpp:101 #: collection/collectionview.cpp:344 playlist/playlistlistcontainer.cpp:101
#: widgets/fileviewlist.cpp:49 #: widgets/fileviewlist.cpp:49
msgid "Copy to device..." msgid "Copy to device..."
@@ -1268,7 +1268,7 @@ msgstr ""
"No se pudo crear el elemento «%1» de GStreamer. Asegúrese de tener " "No se pudo crear el elemento «%1» de GStreamer. Asegúrese de tener "
"instalados todos los complementos necesarios de GStreamer." "instalados todos los complementos necesarios de GStreamer."
#: scrobbler/scrobblingapi20.cpp:154 #: scrobbler/scrobblingapi20.cpp:159
#, qt-format #, qt-format
msgid "" msgid ""
"Could not open URL. Please open this URL in your browser:<br /><a href=" "Could not open URL. Please open this URL in your browser:<br /><a href="
@@ -1277,12 +1277,12 @@ msgstr ""
"No se ha podido abrir la URL. Por favor, abra esta URL en su navegador:<br /" "No se ha podido abrir la URL. Por favor, abra esta URL en su navegador:<br /"
"><a href=\"%1\">%1</a>" "><a href=\"%1\">%1</a>"
#: core/songloader.cpp:260 #: core/songloader.cpp:261
#, qt-format #, qt-format
msgid "Could not open file %1" msgid "Could not open file %1"
msgstr "No se ha podido abrir el archivo %1" msgstr "No se ha podido abrir el archivo %1"
#: core/songloader.cpp:436 #: core/songloader.cpp:437
#, qt-format #, qt-format
msgid "Couldn't create gstreamer source element for %1" msgid "Couldn't create gstreamer source element for %1"
msgstr "No se ha podido crear el elemento de origen gstreamer para %1" msgstr "No se ha podido crear el elemento de origen gstreamer para %1"
@@ -1414,7 +1414,7 @@ msgstr "Ctrl+T"
msgid "Ctrl+Up" msgid "Ctrl+Up"
msgstr "Ctrl+Arriba" msgstr "Ctrl+Arriba"
#: equalizer/equalizer.cpp:139 #: equalizer/equalizer.cpp:137
msgid "Custom" msgid "Custom"
msgstr "Personalizado" msgstr "Personalizado"
@@ -1434,7 +1434,7 @@ msgstr "Personalizado…"
msgid "D-Bus path" msgid "D-Bus path"
msgstr "Ruta de D-Bus" msgstr "Ruta de D-Bus"
#: equalizer/equalizer.cpp:142 #: equalizer/equalizer.cpp:140
msgid "Dance" msgid "Dance"
msgstr "Dance" msgstr "Dance"
@@ -1479,7 +1479,7 @@ msgstr "Eliminar del dispositivo…"
msgid "Delete from disk..." msgid "Delete from disk..."
msgstr "Eliminar del disco…" msgstr "Eliminar del disco…"
#: equalizer/equalizer.cpp:228 ../build/src/ui_equalizer.h:174 #: equalizer/equalizer.cpp:226 ../build/src/ui_equalizer.h:174
msgid "Delete preset" msgid "Delete preset"
msgstr "Eliminar ajuste predefinido" msgstr "Eliminar ajuste predefinido"
@@ -1507,7 +1507,7 @@ msgstr "Destino"
msgid "Details..." msgid "Details..."
msgstr "Detalles…" msgstr "Detalles…"
#: core/song.cpp:482 device/giolister.cpp:188 #: core/song.cpp:475 device/giolister.cpp:188
#: ../build/src/ui_contextviewcontainer.h:369 #: ../build/src/ui_contextviewcontainer.h:369
#: ../build/src/ui_backendsettingspage.h:428 #: ../build/src/ui_backendsettingspage.h:428
msgid "Device" msgid "Device"
@@ -1633,12 +1633,12 @@ msgstr "Editar etiquetas"
msgid "Edit track information" msgid "Edit track information"
msgstr "Editar información de la pista" msgstr "Editar información de la pista"
#: context/contextalbumsview.cpp:372 collection/collectionview.cpp:349 #: context/contextalbumsview.cpp:285 collection/collectionview.cpp:349
#: widgets/fileviewlist.cpp:53 ../build/src/ui_mainwindow.h:628 #: widgets/fileviewlist.cpp:53 ../build/src/ui_mainwindow.h:628
msgid "Edit track information..." msgid "Edit track information..."
msgstr "Editar información de la pista…" msgstr "Editar información de la pista…"
#: context/contextalbumsview.cpp:373 collection/collectionview.cpp:350 #: context/contextalbumsview.cpp:286 collection/collectionview.cpp:350
msgid "Edit tracks information..." msgid "Edit tracks information..."
msgstr "Editar información de las pistas…" msgstr "Editar información de las pistas…"
@@ -1733,7 +1733,7 @@ msgid "Equivalent to --log-levels *:3"
msgstr "Equivalente a --log-levels*:3" msgstr "Equivalente a --log-levels*:3"
#: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287 #: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287
#: context/contextalbumsview.cpp:488 collection/collectionview.cpp:552 #: context/contextalbumsview.cpp:401 collection/collectionview.cpp:552
msgid "Error" msgid "Error"
msgstr "Error" msgstr "Error"
@@ -1869,11 +1869,11 @@ msgstr "Devolver el álbum completo al buscar pistas"
msgid "Fetching cover error" msgid "Fetching cover error"
msgstr "Error al obtener la portada" msgstr "Error al obtener la portada"
#: core/song.cpp:479 #: core/song.cpp:472
msgid "File" msgid "File"
msgstr "Archivo" msgstr "Archivo"
#: core/song.cpp:1006 #: core/song.cpp:999
#, qt-format #, qt-format
msgid "File %1 is not recognized as a valid audio file." msgid "File %1 is not recognized as a valid audio file."
msgstr "El archivo de audio %1 no parece válido." msgstr "El archivo de audio %1 no parece válido."
@@ -1992,15 +1992,15 @@ msgstr "Tramas por búfer"
msgid "Frozen" msgid "Frozen"
msgstr "Congelado" msgstr "Congelado"
#: equalizer/equalizer.cpp:143 #: equalizer/equalizer.cpp:141
msgid "Full Bass" msgid "Full Bass"
msgstr "Graves completos" msgstr "Graves completos"
#: equalizer/equalizer.cpp:145 #: equalizer/equalizer.cpp:143
msgid "Full Bass + Treble" msgid "Full Bass + Treble"
msgstr "Graves y agudos completos" msgstr "Graves y agudos completos"
#: equalizer/equalizer.cpp:144 #: equalizer/equalizer.cpp:142
msgid "Full Treble" msgid "Full Treble"
msgstr "Agudos completos" msgstr "Agudos completos"
@@ -2155,7 +2155,7 @@ msgstr "Ayuda"
msgid "High" msgid "High"
msgstr "Alto" msgstr "Alto"
#: analyzer/analyzercontainer.cpp:75 #: analyzer/analyzercontainer.cpp:74
#, qt-format #, qt-format
msgid "High (%1 fps)" msgid "High (%1 fps)"
msgstr "Alta (%1 fps)" msgstr "Alta (%1 fps)"
@@ -2239,7 +2239,7 @@ msgstr "Comprobación de integridad"
msgid "Intro tracks" msgid "Intro tracks"
msgstr "Pistas de introducción" msgstr "Pistas de introducción"
#: scrobbler/scrobblingapi20.cpp:190 #: scrobbler/scrobblingapi20.cpp:194
msgid "Invalid reply from web browser. Missing token." msgid "Invalid reply from web browser. Missing token."
msgstr "Respuesta inválida del servidor web. Falta token." msgstr "Respuesta inválida del servidor web. Falta token."
@@ -2271,11 +2271,11 @@ msgstr "Mantener los archivos originales"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: equalizer/equalizer.cpp:146 #: equalizer/equalizer.cpp:144
msgid "Laptop/Headphones" msgid "Laptop/Headphones"
msgstr "Portátil/auriculares" msgstr "Portátil/auriculares"
#: equalizer/equalizer.cpp:147 #: equalizer/equalizer.cpp:145
msgid "Large Hall" msgid "Large Hall"
msgstr "Salón grande" msgstr "Salón grande"
@@ -2314,7 +2314,7 @@ msgstr "Duración"
msgid "Libre.fm" msgid "Libre.fm"
msgstr "Libre.fm" msgstr "Libre.fm"
#: scrobbler/listenbrainzscrobbler.cpp:147 #: scrobbler/listenbrainzscrobbler.cpp:152
msgid "ListenBrainz Authentication" msgid "ListenBrainz Authentication"
msgstr "Inicio de sesión en ListenBrainz" msgstr "Inicio de sesión en ListenBrainz"
@@ -2322,7 +2322,7 @@ msgstr "Inicio de sesión en ListenBrainz"
msgid "Listenbrainz" msgid "Listenbrainz"
msgstr "Listenbrainz" msgstr "Listenbrainz"
#: equalizer/equalizer.cpp:148 #: equalizer/equalizer.cpp:146
msgid "Live" msgid "Live"
msgstr "En directo" msgstr "En directo"
@@ -2358,7 +2358,7 @@ msgstr "Cargando el dispositivo MTP"
msgid "Loading iPod database" msgid "Loading iPod database"
msgstr "Cargando la base de datos del iPod" msgstr "Cargando la base de datos del iPod"
#: collection/collectionmodel.cpp:179 #: collection/collectionmodel.cpp:180
msgid "Loading songs" msgid "Loading songs"
msgstr "Cargando pistas" msgstr "Cargando pistas"
@@ -2370,7 +2370,7 @@ msgstr "Cargando pistas"
msgid "Loading tracks info" msgid "Loading tracks info"
msgstr "Cargando información de pistas" msgstr "Cargando información de pistas"
#: collection/collectionmodel.cpp:172 ../build/src/ui_organisedialog.h:271 #: collection/collectionmodel.cpp:173 ../build/src/ui_organisedialog.h:271
msgid "Loading..." msgid "Loading..."
msgstr "Cargando…" msgstr "Cargando…"
@@ -2394,7 +2394,7 @@ msgstr "Perfil de predicción a largo plazo (LTP)"
msgid "Love" msgid "Love"
msgstr "Amor" msgstr "Amor"
#: analyzer/analyzercontainer.cpp:73 #: analyzer/analyzercontainer.cpp:72
#, qt-format #, qt-format
msgid "Low (%1 fps)" msgid "Low (%1 fps)"
msgstr "Baja (%1 fps)" msgstr "Baja (%1 fps)"
@@ -2452,7 +2452,7 @@ msgstr "Tasa de bits máxima"
msgid "Maximum number of login attempts reached." msgid "Maximum number of login attempts reached."
msgstr "Se ha alcanzado el número máximo de intentos de inicio de sesión." msgstr "Se ha alcanzado el número máximo de intentos de inicio de sesión."
#: analyzer/analyzercontainer.cpp:74 #: analyzer/analyzercontainer.cpp:73
#, qt-format #, qt-format
msgid "Medium (%1 fps)" msgid "Medium (%1 fps)"
msgstr "Media (%1 fps)" msgstr "Media (%1 fps)"
@@ -2578,7 +2578,7 @@ msgstr "Música"
msgid "Mute" msgid "Mute"
msgstr "Silenciar" msgstr "Silenciar"
#: equalizer/equalizer.cpp:211 collection/savedgroupingmanager.cpp:55 #: equalizer/equalizer.cpp:209 collection/savedgroupingmanager.cpp:55
#: ../build/src/ui_deviceproperties.h:368 #: ../build/src/ui_deviceproperties.h:368
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
@@ -2625,7 +2625,7 @@ msgstr "Siguiente"
msgid "Next week" msgid "Next week"
msgstr "Próxima semana" msgstr "Próxima semana"
#: analyzer/analyzercontainer.cpp:86 #: analyzer/analyzercontainer.cpp:85
msgid "No analyzer" msgid "No analyzer"
msgstr "Sin analizador" msgstr "Sin analizador"
@@ -2662,7 +2662,7 @@ msgid "None"
msgstr "Ninguno" msgstr "Ninguno"
#: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287 #: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287
#: context/contextalbumsview.cpp:488 collection/collectionview.cpp:552 #: context/contextalbumsview.cpp:401 collection/collectionview.cpp:552
msgid "None of the selected songs were suitable for copying to a device" msgid "None of the selected songs were suitable for copying to a device"
msgstr "" msgstr ""
"Ninguna de las pistas seleccionadas era apta para copiarse en un dispositivo" "Ninguna de las pistas seleccionadas era apta para copiarse en un dispositivo"
@@ -2737,7 +2737,7 @@ msgstr "Al iniciar"
msgid "Opacity" msgid "Opacity"
msgstr "Opacidad" msgstr "Opacidad"
#: scrobbler/scrobblingapi20.cpp:145 #: scrobbler/scrobblingapi20.cpp:150
#, qt-format #, qt-format
msgid "" msgid ""
"Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to " "Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to "
@@ -2759,7 +2759,7 @@ msgstr "Abrir &CD de audio..."
msgid "Open device" msgid "Open device"
msgstr "Abrir dispositivo" msgstr "Abrir dispositivo"
#: context/contextalbumsview.cpp:360 collection/collectionview.cpp:335 #: context/contextalbumsview.cpp:273 collection/collectionview.cpp:335
#: widgets/fileviewlist.cpp:45 internet/internetsearchview.cpp:435 #: widgets/fileviewlist.cpp:45 internet/internetsearchview.cpp:435
#: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230 #: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230
#: ../build/src/ui_behavioursettingspage.h:274 #: ../build/src/ui_behavioursettingspage.h:274
@@ -2791,7 +2791,7 @@ msgstr "Opus"
msgid "Organise Files" msgid "Organise Files"
msgstr "Organizar archivos" msgstr "Organizar archivos"
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:366 #: core/mainwindow.cpp:625 context/contextalbumsview.cpp:279
#: collection/collectionview.cpp:342 #: collection/collectionview.cpp:342
msgid "Organise files..." msgid "Organise files..."
msgstr "Organizar archivos…" msgstr "Organizar archivos…"
@@ -2848,7 +2848,7 @@ msgstr "Lista de re&producción"
msgid "Partition label" msgid "Partition label"
msgstr "Etiqueta de partición" msgstr "Etiqueta de partición"
#: equalizer/equalizer.cpp:149 #: equalizer/equalizer.cpp:147
msgid "Party" msgid "Party"
msgstr "Fiesta" msgstr "Fiesta"
@@ -2942,12 +2942,12 @@ msgstr "Listas"
msgid "Please close your browser and return to Strawberry." msgid "Please close your browser and return to Strawberry."
msgstr "Por favor, cierre el navegador y regrese a Strawberry." msgstr "Por favor, cierre el navegador y regrese a Strawberry."
#: scrobbler/listenbrainzscrobbler.cpp:147 #: scrobbler/listenbrainzscrobbler.cpp:152
#, qt-format #, qt-format
msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>" msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>"
msgstr "Por favor, abra esta URL en su navegador: <br /><a href=\"%1\">%1</a>" msgstr "Por favor, abra esta URL en su navegador: <br /><a href=\"%1\">%1</a>"
#: equalizer/equalizer.cpp:150 #: equalizer/equalizer.cpp:148
msgid "Pop" msgid "Pop"
msgstr "Pop" msgstr "Pop"
@@ -2985,7 +2985,7 @@ msgstr "Nombres de archivo preferidos para las portadas (separados por comas)"
msgid "Preferred format" msgid "Preferred format"
msgstr "Formato preferido" msgstr "Formato preferido"
#: core/songloader.cpp:156 #: core/songloader.cpp:157
msgid "Preload function was not set for blocking operation." msgid "Preload function was not set for blocking operation."
msgstr "" msgstr ""
"La función de precarga no se ajustó para un funcionamiento con bloqueo." "La función de precarga no se ajustó para un funcionamiento con bloqueo."
@@ -3070,7 +3070,7 @@ msgstr "Encolar las pistas seleccionadas para reproducir a continuación"
msgid "Queue to play next" msgid "Queue to play next"
msgstr "Encolar para reproducir a continuación" msgstr "Encolar para reproducir a continuación"
#: core/mainwindow.cpp:1631 context/contextalbumsview.cpp:363 #: core/mainwindow.cpp:1631 context/contextalbumsview.cpp:276
#: collection/collectionview.cpp:338 internet/internetsearchview.cpp:438 #: collection/collectionview.cpp:338 internet/internetsearchview.cpp:438
#: internet/internetcollectionview.cpp:307 #: internet/internetcollectionview.cpp:307
msgid "Queue track" msgid "Queue track"
@@ -3092,7 +3092,11 @@ msgstr "Re&lativo"
msgid "Really cancel?" msgid "Really cancel?"
msgstr "¿Seguro que quiere cancelar?" msgstr "¿Seguro que quiere cancelar?"
#: scrobbler/scrobblingapi20.cpp:184 #: scrobbler/listenbrainzscrobbler.cpp:178
msgid "Received invalid reply from web browser."
msgstr ""
#: scrobbler/scrobblingapi20.cpp:198
msgid "" msgid ""
"Received invalid reply from web browser. Try the HTTPS option, or use " "Received invalid reply from web browser. Try the HTTPS option, or use "
"another browser like Chromium or Chrome." "another browser like Chromium or Chrome."
@@ -3100,11 +3104,15 @@ msgstr ""
"Se ha recibido una respuesta inválida del navegador. Prueba usando la opción " "Se ha recibido una respuesta inválida del navegador. Prueba usando la opción "
"de HTTPS o utiliza otro navegador como Chromium o Chrome." "de HTTPS o utiliza otro navegador como Chromium o Chrome."
#: scrobbler/listenbrainzscrobbler.cpp:174
msgid "Redirect missing token code!"
msgstr ""
#: ../build/src/ui_internetcollectionviewcontainer.h:142 #: ../build/src/ui_internetcollectionviewcontainer.h:142
msgid "Refresh catalogue" msgid "Refresh catalogue"
msgstr "Actualiza el catálogo" msgstr "Actualiza el catálogo"
#: equalizer/equalizer.cpp:151 #: equalizer/equalizer.cpp:149
msgid "Reggae" msgid "Reggae"
msgstr "Reggae" msgstr "Reggae"
@@ -3181,7 +3189,7 @@ msgstr "Repetir lista de reproducción"
msgid "Repeat track" msgid "Repeat track"
msgstr "Repetir pista" msgstr "Repetir pista"
#: context/contextalbumsview.cpp:359 collection/collectionview.cpp:334 #: context/contextalbumsview.cpp:272 collection/collectionview.cpp:334
#: widgets/fileviewlist.cpp:44 internet/internetsearchview.cpp:434 #: widgets/fileviewlist.cpp:44 internet/internetsearchview.cpp:434
#: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229 #: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229
msgid "Replace current playlist" msgid "Replace current playlist"
@@ -3305,7 +3313,7 @@ msgstr "Volver a Strawberry"
msgid "Right" msgid "Right"
msgstr "Derecha" msgstr "Derecha"
#: equalizer/equalizer.cpp:152 #: equalizer/equalizer.cpp:150
msgid "Rock" msgid "Rock"
msgstr "Rock" msgstr "Rock"
@@ -3374,7 +3382,7 @@ msgstr "Guardar lista de reproducción"
msgid "Save playlist..." msgid "Save playlist..."
msgstr "Guardar lista de reproducción…" msgstr "Guardar lista de reproducción…"
#: equalizer/equalizer.cpp:211 ../build/src/ui_equalizer.h:171 #: equalizer/equalizer.cpp:209 ../build/src/ui_equalizer.h:171
msgid "Save preset" msgid "Save preset"
msgstr "Guardar ajuste predefinido" msgstr "Guardar ajuste predefinido"
@@ -3406,7 +3414,7 @@ msgstr "Tamaño de escala"
msgid "Scrobbler" msgid "Scrobbler"
msgstr "Registro de reproducción" msgstr "Registro de reproducción"
#: scrobbler/scrobblingapi20.cpp:478 #: scrobbler/scrobblingapi20.cpp:495
#, qt-format #, qt-format
msgid "Scrobbler %1 is not authenticated!" msgid "Scrobbler %1 is not authenticated!"
msgstr "" msgstr ""
@@ -3617,7 +3625,7 @@ msgstr "Mostrar en la colección…"
msgid "Show in file browser" msgid "Show in file browser"
msgstr "Mostrar en el navegador de archivos" msgstr "Mostrar en el navegador de archivos"
#: core/mainwindow.cpp:626 context/contextalbumsview.cpp:374 #: core/mainwindow.cpp:626 context/contextalbumsview.cpp:287
#: collection/collectionview.cpp:351 widgets/fileviewlist.cpp:54 #: collection/collectionview.cpp:351 widgets/fileviewlist.cpp:54
msgid "Show in file browser..." msgid "Show in file browser..."
msgstr "Mostrar en el gestor de archivos…" msgstr "Mostrar en el gestor de archivos…"
@@ -3694,7 +3702,7 @@ msgstr "Tamaño"
msgid "Size:" msgid "Size:"
msgstr "Tamaño:" msgstr "Tamaño:"
#: equalizer/equalizer.cpp:154 #: equalizer/equalizer.cpp:152
msgid "Ska" msgid "Ska"
msgstr "Ska" msgstr "Ska"
@@ -3726,11 +3734,11 @@ msgstr "Portada de álbum pequeña"
msgid "Small sidebar" msgid "Small sidebar"
msgstr "Barra lateral pequeña" msgstr "Barra lateral pequeña"
#: equalizer/equalizer.cpp:153 #: equalizer/equalizer.cpp:151
msgid "Soft" msgid "Soft"
msgstr "Soft" msgstr "Soft"
#: equalizer/equalizer.cpp:155 #: equalizer/equalizer.cpp:153
msgid "Soft Rock" msgid "Soft Rock"
msgstr "Soft rock" msgstr "Soft rock"
@@ -3854,7 +3862,7 @@ msgstr ""
msgid "Strawberry was unable to find results for this file" msgid "Strawberry was unable to find results for this file"
msgstr "Strawberry no encontró resultados para este archivo" msgstr "Strawberry no encontró resultados para este archivo"
#: core/song.cpp:483 #: core/song.cpp:476
msgid "Stream" msgid "Stream"
msgstr "Transmisión" msgstr "Transmisión"
@@ -3900,7 +3908,7 @@ msgstr "Etiquetas sugeridas"
msgid "Summary" msgid "Summary"
msgstr "Resumen" msgstr "Resumen"
#: analyzer/analyzercontainer.cpp:76 #: analyzer/analyzercontainer.cpp:75
#, qt-format #, qt-format
msgid "Super high (%1 fps)" msgid "Super high (%1 fps)"
msgstr "Muy alta (%1 fps)" msgstr "Muy alta (%1 fps)"
@@ -3929,7 +3937,7 @@ msgstr "Obtener etiquetas"
msgid "Target bitrate" msgid "Target bitrate"
msgstr "Tasa de bits objetivo" msgstr "Tasa de bits objetivo"
#: equalizer/equalizer.cpp:156 #: equalizer/equalizer.cpp:154
msgid "Techno" msgid "Techno"
msgstr "Techno" msgstr "Techno"
@@ -4093,7 +4101,7 @@ msgstr ""
msgid "This type of device is not supported: %1" msgid "This type of device is not supported: %1"
msgstr "No se admite este tipo de dispositivo: %1" msgstr "No se admite este tipo de dispositivo: %1"
#: core/mainwindow.cpp:284 core/song.cpp:484 #: core/mainwindow.cpp:284 core/song.cpp:477
#: ../build/src/ui_tidalsettingspage.h:293 #: ../build/src/ui_tidalsettingspage.h:293
msgid "Tidal" msgid "Tidal"
msgstr "Tidal" msgstr "Tidal"
@@ -4195,11 +4203,11 @@ msgstr "UUID"
msgid "Ultra wide band (UWB)" msgid "Ultra wide band (UWB)"
msgstr "Banda ultraancha (UWB)" msgstr "Banda ultraancha (UWB)"
#: core/song.cpp:487 core/song.cpp:489 core/song.cpp:533 #: core/song.cpp:480 core/song.cpp:482 core/song.cpp:526
#: context/contextalbumsmodel.cpp:385 collection/collectionmodel.cpp:398 #: context/contextalbumsmodel.cpp:367 collection/collectionmodel.cpp:399
#: collection/collectionmodel.cpp:403 collection/collectionmodel.cpp:407 #: collection/collectionmodel.cpp:404 collection/collectionmodel.cpp:408
#: collection/collectionmodel.cpp:411 collection/collectionmodel.cpp:415 #: collection/collectionmodel.cpp:412 collection/collectionmodel.cpp:416
#: collection/collectionmodel.cpp:1362 collection/savedgroupingmanager.cpp:137 #: collection/collectionmodel.cpp:1342 collection/savedgroupingmanager.cpp:137
#: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542 #: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542
#: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490 #: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490
#: dialogs/edittagdialog.cpp:538 #: dialogs/edittagdialog.cpp:538
@@ -4360,7 +4368,7 @@ msgstr "Al usar el menú para añadir una canción…"
msgid "Variable bit rate" msgid "Variable bit rate"
msgstr "Tasa de bits variable" msgstr "Tasa de bits variable"
#: collection/collectionmodel.cpp:312 playlist/playlistmanager.cpp:554 #: collection/collectionmodel.cpp:313 playlist/playlistmanager.cpp:554
#: covermanager/albumcovermanager.cpp:282 internet/internetsearchmodel.cpp:94 #: covermanager/albumcovermanager.cpp:282 internet/internetsearchmodel.cpp:94
#: internet/internetsearchmodel.cpp:106 #: internet/internetsearchmodel.cpp:106
msgid "Various artists" msgid "Various artists"
@@ -4502,7 +4510,7 @@ msgid "You can change the way the songs in the collection are organised."
msgstr "" msgstr ""
"Puede modificar el modo en que se organizan las canciones en la colección." "Puede modificar el modo en que se organizan las canciones en la colección."
#: core/songloader.cpp:137 core/songloader.cpp:142 #: core/songloader.cpp:138 core/songloader.cpp:143
msgid "You need GStreamer for this URL." msgid "You need GStreamer for this URL."
msgstr "Necesitas GStreamer para esta URL" msgstr "Necesitas GStreamer para esta URL"
@@ -4540,7 +4548,7 @@ msgstr "Necesitará reiniciar Strawberry si cambia el idioma."
msgid "Your collection is empty!" msgid "Your collection is empty!"
msgstr "La colección está vacía." msgstr "La colección está vacía."
#: equalizer/equalizer.cpp:157 #: equalizer/equalizer.cpp:155
msgid "Zero" msgid "Zero"
msgstr "Cero" msgstr "Cero"
@@ -4620,7 +4628,7 @@ msgstr "opciones"
msgid "p&lughw" msgid "p&lughw"
msgstr "p&lughw" msgstr "p&lughw"
#: core/song.cpp:486 #: core/song.cpp:479
msgid "qobuz" msgid "qobuz"
msgstr "qobuz" msgstr "qobuz"
@@ -4646,7 +4654,7 @@ msgstr "ordenar canciones"
msgid "stop" msgid "stop"
msgstr "detener" msgstr "detener"
#: core/song.cpp:485 #: core/song.cpp:478
msgid "subsonic" msgid "subsonic"
msgstr "subsonic" msgstr "subsonic"

View File

@@ -97,7 +97,7 @@ msgstr " s"
msgid " seconds" msgid " seconds"
msgstr " sekunder" msgstr " sekunder"
#: scrobbler/scrobblingapi20.cpp:145 scrobbler/scrobblingapi20.cpp:154 #: scrobbler/scrobblingapi20.cpp:150 scrobbler/scrobblingapi20.cpp:159
#, qt-format #, qt-format
msgid "%1 Scrobbler Authentication" msgid "%1 Scrobbler Authentication"
msgstr "%1 Scrobbler bruker autentisering" msgstr "%1 Scrobbler bruker autentisering"
@@ -803,7 +803,7 @@ msgstr "Utseende"
msgid "Append files/URLs to the playlist" msgid "Append files/URLs to the playlist"
msgstr "Tilføy filer/URLer til spillelista" msgstr "Tilføy filer/URLer til spillelista"
#: context/contextalbumsview.cpp:358 collection/collectionview.cpp:333 #: context/contextalbumsview.cpp:271 collection/collectionview.cpp:333
#: widgets/fileviewlist.cpp:43 internet/internetsearchview.cpp:433 #: widgets/fileviewlist.cpp:43 internet/internetsearchview.cpp:433
#: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228 #: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228
msgid "Append to current playlist" msgid "Append to current playlist"
@@ -817,7 +817,7 @@ msgstr "Legg til i spillelista"
msgid "Apply compression to prevent clipping" msgid "Apply compression to prevent clipping"
msgstr "Legg til kompressor, for å unngå klipping" msgstr "Legg til kompressor, for å unngå klipping"
#: equalizer/equalizer.cpp:229 #: equalizer/equalizer.cpp:227
#, qt-format #, qt-format
msgid "Are you sure you want to delete the \"%1\" preset?" msgid "Are you sure you want to delete the \"%1\" preset?"
msgstr "Er du sikker på at du vil slette \"%1\"-forhåndsinnstillingen?" msgstr "Er du sikker på at du vil slette \"%1\"-forhåndsinnstillingen?"
@@ -1020,7 +1020,7 @@ msgstr "Buffer"
msgid "Buffer duration" msgid "Buffer duration"
msgstr "Mellomlagringslengde" msgstr "Mellomlagringslengde"
#: engine/gstengine.cpp:622 #: engine/gstengine.cpp:636
msgid "Buffering" msgid "Buffering"
msgstr "Mellomlagring" msgstr "Mellomlagring"
@@ -1028,11 +1028,11 @@ msgstr "Mellomlagring"
msgid "C&onsole" msgid "C&onsole"
msgstr "K&onsoll" msgstr "K&onsoll"
#: core/song.cpp:481 #: core/song.cpp:474
msgid "CD" msgid "CD"
msgstr "CD" msgstr "CD"
#: core/songloader.cpp:195 #: core/songloader.cpp:196
msgid "CD playback is only available with the GStreamer engine." msgid "CD playback is only available with the GStreamer engine."
msgstr "CD avspilling er kun mulig med gstreamer" msgstr "CD avspilling er kun mulig med gstreamer"
@@ -1068,7 +1068,7 @@ msgstr "Velg farge…"
msgid "Choose font..." msgid "Choose font..."
msgstr "Velg skrifttype…" msgstr "Velg skrifttype…"
#: equalizer/equalizer.cpp:140 #: equalizer/equalizer.cpp:138
msgid "Classical" msgid "Classical"
msgstr "Klassisk" msgstr "Klassisk"
@@ -1117,11 +1117,11 @@ msgstr "Lukk spillelista"
msgid "Closing this window will stop searching for album covers." msgid "Closing this window will stop searching for album covers."
msgstr "Lukking av dette vinduet vil medføre stopp i søk etter albumomslag." msgstr "Lukking av dette vinduet vil medføre stopp i søk etter albumomslag."
#: equalizer/equalizer.cpp:141 #: equalizer/equalizer.cpp:139
msgid "Club" msgid "Club"
msgstr "Klubbmusikk" msgstr "Klubbmusikk"
#: core/mainwindow.cpp:276 core/song.cpp:480 #: core/mainwindow.cpp:276 core/song.cpp:473
#: ../build/src/ui_collectionsettingspage.h:244 #: ../build/src/ui_collectionsettingspage.h:244
msgid "Collection" msgid "Collection"
msgstr "Samling" msgstr "Samling"
@@ -1222,7 +1222,7 @@ msgstr "Kopier album omslaggrafikk"
msgid "Copy to collection..." msgid "Copy to collection..."
msgstr "Kopier til samling…" msgstr "Kopier til samling…"
#: core/mainwindow.cpp:621 context/contextalbumsview.cpp:368 #: core/mainwindow.cpp:621 context/contextalbumsview.cpp:281
#: collection/collectionview.cpp:344 playlist/playlistlistcontainer.cpp:101 #: collection/collectionview.cpp:344 playlist/playlistlistcontainer.cpp:101
#: widgets/fileviewlist.cpp:49 #: widgets/fileviewlist.cpp:49
msgid "Copy to device..." msgid "Copy to device..."
@@ -1241,7 +1241,7 @@ msgstr ""
"Kunne ikke opprette GStreamer-elementet \"%1\" - sørg for at du har alle " "Kunne ikke opprette GStreamer-elementet \"%1\" - sørg for at du har alle "
"nødvendige GStreamer-programutvidelser installert" "nødvendige GStreamer-programutvidelser installert"
#: scrobbler/scrobblingapi20.cpp:154 #: scrobbler/scrobblingapi20.cpp:159
#, qt-format #, qt-format
msgid "" msgid ""
"Could not open URL. Please open this URL in your browser:<br /><a href=" "Could not open URL. Please open this URL in your browser:<br /><a href="
@@ -1250,12 +1250,12 @@ msgstr ""
"Kunne ikke åpne adressen. Åpne denne nettadressen i nettleseren din:<br /><a " "Kunne ikke åpne adressen. Åpne denne nettadressen i nettleseren din:<br /><a "
"href=\"%1\">%1</a>" "href=\"%1\">%1</a>"
#: core/songloader.cpp:260 #: core/songloader.cpp:261
#, qt-format #, qt-format
msgid "Could not open file %1" msgid "Could not open file %1"
msgstr "Kan ikke åpne fil %1" msgstr "Kan ikke åpne fil %1"
#: core/songloader.cpp:436 #: core/songloader.cpp:437
#, qt-format #, qt-format
msgid "Couldn't create gstreamer source element for %1" msgid "Couldn't create gstreamer source element for %1"
msgstr "Kunne ikke opprette gstreamer kilde for %1" msgstr "Kunne ikke opprette gstreamer kilde for %1"
@@ -1387,7 +1387,7 @@ msgstr "Ctrl+T"
msgid "Ctrl+Up" msgid "Ctrl+Up"
msgstr "Ctrl+Opp" msgstr "Ctrl+Opp"
#: equalizer/equalizer.cpp:139 #: equalizer/equalizer.cpp:137
msgid "Custom" msgid "Custom"
msgstr "Egendefinert" msgstr "Egendefinert"
@@ -1407,7 +1407,7 @@ msgstr "Egendefinert…"
msgid "D-Bus path" msgid "D-Bus path"
msgstr "D-Bus sti" msgstr "D-Bus sti"
#: equalizer/equalizer.cpp:142 #: equalizer/equalizer.cpp:140
msgid "Dance" msgid "Dance"
msgstr "Dansemusikk" msgstr "Dansemusikk"
@@ -1452,7 +1452,7 @@ msgstr "Slett fra enhet…"
msgid "Delete from disk..." msgid "Delete from disk..."
msgstr "Slett fra disk…" msgstr "Slett fra disk…"
#: equalizer/equalizer.cpp:228 ../build/src/ui_equalizer.h:174 #: equalizer/equalizer.cpp:226 ../build/src/ui_equalizer.h:174
msgid "Delete preset" msgid "Delete preset"
msgstr "Slett forhåndsinnstilling" msgstr "Slett forhåndsinnstilling"
@@ -1480,7 +1480,7 @@ msgstr "Mål"
msgid "Details..." msgid "Details..."
msgstr "Detaljer…" msgstr "Detaljer…"
#: core/song.cpp:482 device/giolister.cpp:188 #: core/song.cpp:475 device/giolister.cpp:188
#: ../build/src/ui_contextviewcontainer.h:369 #: ../build/src/ui_contextviewcontainer.h:369
#: ../build/src/ui_backendsettingspage.h:428 #: ../build/src/ui_backendsettingspage.h:428
msgid "Device" msgid "Device"
@@ -1606,12 +1606,12 @@ msgstr "Rediger tagger"
msgid "Edit track information" msgid "Edit track information"
msgstr "Rediger spor informasjon" msgstr "Rediger spor informasjon"
#: context/contextalbumsview.cpp:372 collection/collectionview.cpp:349 #: context/contextalbumsview.cpp:285 collection/collectionview.cpp:349
#: widgets/fileviewlist.cpp:53 ../build/src/ui_mainwindow.h:628 #: widgets/fileviewlist.cpp:53 ../build/src/ui_mainwindow.h:628
msgid "Edit track information..." msgid "Edit track information..."
msgstr "Rediger spor informasjon…" msgstr "Rediger spor informasjon…"
#: context/contextalbumsview.cpp:373 collection/collectionview.cpp:350 #: context/contextalbumsview.cpp:286 collection/collectionview.cpp:350
msgid "Edit tracks information..." msgid "Edit tracks information..."
msgstr "Rediger spor informasjon…" msgstr "Rediger spor informasjon…"
@@ -1704,7 +1704,7 @@ msgid "Equivalent to --log-levels *:3"
msgstr "Tilsvarer --log-levels *:3" msgstr "Tilsvarer --log-levels *:3"
#: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287 #: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287
#: context/contextalbumsview.cpp:488 collection/collectionview.cpp:552 #: context/contextalbumsview.cpp:401 collection/collectionview.cpp:552
msgid "Error" msgid "Error"
msgstr "Feil" msgstr "Feil"
@@ -1840,11 +1840,11 @@ msgstr "Hent hele album når en søker etter sanger"
msgid "Fetching cover error" msgid "Fetching cover error"
msgstr "Kunne ikke hente albumgrafikk" msgstr "Kunne ikke hente albumgrafikk"
#: core/song.cpp:479 #: core/song.cpp:472
msgid "File" msgid "File"
msgstr "Fil" msgstr "Fil"
#: core/song.cpp:1006 #: core/song.cpp:999
#, qt-format #, qt-format
msgid "File %1 is not recognized as a valid audio file." msgid "File %1 is not recognized as a valid audio file."
msgstr "Fil %1 er ikke gjenkjent som en lydfil" msgstr "Fil %1 er ikke gjenkjent som en lydfil"
@@ -1963,15 +1963,15 @@ msgstr "Bilder per buffer"
msgid "Frozen" msgid "Frozen"
msgstr "Frossen" msgstr "Frossen"
#: equalizer/equalizer.cpp:143 #: equalizer/equalizer.cpp:141
msgid "Full Bass" msgid "Full Bass"
msgstr "Full bass" msgstr "Full bass"
#: equalizer/equalizer.cpp:145 #: equalizer/equalizer.cpp:143
msgid "Full Bass + Treble" msgid "Full Bass + Treble"
msgstr "Full bass + diskant" msgstr "Full bass + diskant"
#: equalizer/equalizer.cpp:144 #: equalizer/equalizer.cpp:142
msgid "Full Treble" msgid "Full Treble"
msgstr "Full diskant" msgstr "Full diskant"
@@ -2121,7 +2121,7 @@ msgstr "Hjelp"
msgid "High" msgid "High"
msgstr "Høy" msgstr "Høy"
#: analyzer/analyzercontainer.cpp:75 #: analyzer/analyzercontainer.cpp:74
#, qt-format #, qt-format
msgid "High (%1 fps)" msgid "High (%1 fps)"
msgstr "Høy (%1 bilder/sekund)" msgstr "Høy (%1 bilder/sekund)"
@@ -2202,7 +2202,7 @@ msgstr "Integritetskontrol"
msgid "Intro tracks" msgid "Intro tracks"
msgstr "Introspor" msgstr "Introspor"
#: scrobbler/scrobblingapi20.cpp:190 #: scrobbler/scrobblingapi20.cpp:194
msgid "Invalid reply from web browser. Missing token." msgid "Invalid reply from web browser. Missing token."
msgstr "Ugyldig svar fra nettleseren. Mangler token." msgstr "Ugyldig svar fra nettleseren. Mangler token."
@@ -2234,11 +2234,11 @@ msgstr "Behold originalfilene"
msgid "Language" msgid "Language"
msgstr "Språk" msgstr "Språk"
#: equalizer/equalizer.cpp:146 #: equalizer/equalizer.cpp:144
msgid "Laptop/Headphones" msgid "Laptop/Headphones"
msgstr "Laptop/hodetelefoner" msgstr "Laptop/hodetelefoner"
#: equalizer/equalizer.cpp:147 #: equalizer/equalizer.cpp:145
msgid "Large Hall" msgid "Large Hall"
msgstr "Storsal" msgstr "Storsal"
@@ -2277,7 +2277,7 @@ msgstr "Lengde"
msgid "Libre.fm" msgid "Libre.fm"
msgstr "Libre.fm" msgstr "Libre.fm"
#: scrobbler/listenbrainzscrobbler.cpp:147 #: scrobbler/listenbrainzscrobbler.cpp:152
msgid "ListenBrainz Authentication" msgid "ListenBrainz Authentication"
msgstr "ListenBrainz autentisering" msgstr "ListenBrainz autentisering"
@@ -2285,7 +2285,7 @@ msgstr "ListenBrainz autentisering"
msgid "Listenbrainz" msgid "Listenbrainz"
msgstr "Listenbrainz" msgstr "Listenbrainz"
#: equalizer/equalizer.cpp:148 #: equalizer/equalizer.cpp:146
msgid "Live" msgid "Live"
msgstr "Live" msgstr "Live"
@@ -2321,7 +2321,7 @@ msgstr "Åpner MTP-enhet"
msgid "Loading iPod database" msgid "Loading iPod database"
msgstr "Åpner iPod-database" msgstr "Åpner iPod-database"
#: collection/collectionmodel.cpp:179 #: collection/collectionmodel.cpp:180
msgid "Loading songs" msgid "Loading songs"
msgstr "Åpner sanger" msgstr "Åpner sanger"
@@ -2333,7 +2333,7 @@ msgstr "Åpner spor"
msgid "Loading tracks info" msgid "Loading tracks info"
msgstr "Henter informasjon om spor" msgstr "Henter informasjon om spor"
#: collection/collectionmodel.cpp:172 ../build/src/ui_organisedialog.h:271 #: collection/collectionmodel.cpp:173 ../build/src/ui_organisedialog.h:271
msgid "Loading..." msgid "Loading..."
msgstr "Åpner…" msgstr "Åpner…"
@@ -2357,7 +2357,7 @@ msgstr "Profil for langtidspredikie (LTP)"
msgid "Love" msgid "Love"
msgstr "" msgstr ""
#: analyzer/analyzercontainer.cpp:73 #: analyzer/analyzercontainer.cpp:72
#, qt-format #, qt-format
msgid "Low (%1 fps)" msgid "Low (%1 fps)"
msgstr "Lav (%1 bilder/sekund)" msgstr "Lav (%1 bilder/sekund)"
@@ -2414,7 +2414,7 @@ msgstr "Høyeste bitrate"
msgid "Maximum number of login attempts reached." msgid "Maximum number of login attempts reached."
msgstr "" msgstr ""
#: analyzer/analyzercontainer.cpp:74 #: analyzer/analyzercontainer.cpp:73
#, qt-format #, qt-format
msgid "Medium (%1 fps)" msgid "Medium (%1 fps)"
msgstr "Medium (%1 bilder/sekund)" msgstr "Medium (%1 bilder/sekund)"
@@ -2540,7 +2540,7 @@ msgstr "Musikk"
msgid "Mute" msgid "Mute"
msgstr "Demp" msgstr "Demp"
#: equalizer/equalizer.cpp:211 collection/savedgroupingmanager.cpp:55 #: equalizer/equalizer.cpp:209 collection/savedgroupingmanager.cpp:55
#: ../build/src/ui_deviceproperties.h:368 #: ../build/src/ui_deviceproperties.h:368
msgid "Name" msgid "Name"
msgstr "Navn" msgstr "Navn"
@@ -2587,7 +2587,7 @@ msgstr "Neste"
msgid "Next week" msgid "Next week"
msgstr "Neste uke" msgstr "Neste uke"
#: analyzer/analyzercontainer.cpp:86 #: analyzer/analyzercontainer.cpp:85
msgid "No analyzer" msgid "No analyzer"
msgstr "Ingen analyse" msgstr "Ingen analyse"
@@ -2622,7 +2622,7 @@ msgid "None"
msgstr "Ingen" msgstr "Ingen"
#: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287 #: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287
#: context/contextalbumsview.cpp:488 collection/collectionview.cpp:552 #: context/contextalbumsview.cpp:401 collection/collectionview.cpp:552
msgid "None of the selected songs were suitable for copying to a device" msgid "None of the selected songs were suitable for copying to a device"
msgstr "Kunne ikke kopiere noen av de valgte sangene til en enhet" msgstr "Kunne ikke kopiere noen av de valgte sangene til en enhet"
@@ -2694,7 +2694,7 @@ msgstr "Ved oppstart"
msgid "Opacity" msgid "Opacity"
msgstr "Dekkevne" msgstr "Dekkevne"
#: scrobbler/scrobblingapi20.cpp:145 #: scrobbler/scrobblingapi20.cpp:150
#, qt-format #, qt-format
msgid "" msgid ""
"Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to " "Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to "
@@ -2715,7 +2715,7 @@ msgstr "Åpne lyd &CD"
msgid "Open device" msgid "Open device"
msgstr "Åpne enhet" msgstr "Åpne enhet"
#: context/contextalbumsview.cpp:360 collection/collectionview.cpp:335 #: context/contextalbumsview.cpp:273 collection/collectionview.cpp:335
#: widgets/fileviewlist.cpp:45 internet/internetsearchview.cpp:435 #: widgets/fileviewlist.cpp:45 internet/internetsearchview.cpp:435
#: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230 #: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230
#: ../build/src/ui_behavioursettingspage.h:274 #: ../build/src/ui_behavioursettingspage.h:274
@@ -2747,7 +2747,7 @@ msgstr "Opus"
msgid "Organise Files" msgid "Organise Files"
msgstr "Organiser filer" msgstr "Organiser filer"
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:366 #: core/mainwindow.cpp:625 context/contextalbumsview.cpp:279
#: collection/collectionview.cpp:342 #: collection/collectionview.cpp:342
msgid "Organise files..." msgid "Organise files..."
msgstr "Organiser filer…" msgstr "Organiser filer…"
@@ -2804,7 +2804,7 @@ msgstr "Spilleliste"
msgid "Partition label" msgid "Partition label"
msgstr "Partisjonsnavn" msgstr "Partisjonsnavn"
#: equalizer/equalizer.cpp:149 #: equalizer/equalizer.cpp:147
msgid "Party" msgid "Party"
msgstr "Fest" msgstr "Fest"
@@ -2898,12 +2898,12 @@ msgstr "Spillelister"
msgid "Please close your browser and return to Strawberry." msgid "Please close your browser and return to Strawberry."
msgstr "Lukk nettleseren og gå tilbake til Strawberry." msgstr "Lukk nettleseren og gå tilbake til Strawberry."
#: scrobbler/listenbrainzscrobbler.cpp:147 #: scrobbler/listenbrainzscrobbler.cpp:152
#, qt-format #, qt-format
msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>" msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>"
msgstr "Vennligst åpne lenke i din nettleser:<br /><a href=\"%1\">%1</a>" msgstr "Vennligst åpne lenke i din nettleser:<br /><a href=\"%1\">%1</a>"
#: equalizer/equalizer.cpp:150 #: equalizer/equalizer.cpp:148
msgid "Pop" msgid "Pop"
msgstr "Pop" msgstr "Pop"
@@ -2941,7 +2941,7 @@ msgstr "Foretrukne filnavn for omslag (inndelt med komma)"
msgid "Preferred format" msgid "Preferred format"
msgstr "Foretrukket format" msgstr "Foretrukket format"
#: core/songloader.cpp:156 #: core/songloader.cpp:157
msgid "Preload function was not set for blocking operation." msgid "Preload function was not set for blocking operation."
msgstr "Preload function was not set for blocking operation." msgstr "Preload function was not set for blocking operation."
@@ -3025,7 +3025,7 @@ msgstr "Legg valgte spor i kø for å spille som neste"
msgid "Queue to play next" msgid "Queue to play next"
msgstr "Legg i kø for å spille som neste" msgstr "Legg i kø for å spille som neste"
#: core/mainwindow.cpp:1631 context/contextalbumsview.cpp:363 #: core/mainwindow.cpp:1631 context/contextalbumsview.cpp:276
#: collection/collectionview.cpp:338 internet/internetsearchview.cpp:438 #: collection/collectionview.cpp:338 internet/internetsearchview.cpp:438
#: internet/internetcollectionview.cpp:307 #: internet/internetcollectionview.cpp:307
msgid "Queue track" msgid "Queue track"
@@ -3047,7 +3047,11 @@ msgstr "Relativ"
msgid "Really cancel?" msgid "Really cancel?"
msgstr "Vil du virkelig avbryte?" msgstr "Vil du virkelig avbryte?"
#: scrobbler/scrobblingapi20.cpp:184 #: scrobbler/listenbrainzscrobbler.cpp:178
msgid "Received invalid reply from web browser."
msgstr ""
#: scrobbler/scrobblingapi20.cpp:198
msgid "" msgid ""
"Received invalid reply from web browser. Try the HTTPS option, or use " "Received invalid reply from web browser. Try the HTTPS option, or use "
"another browser like Chromium or Chrome." "another browser like Chromium or Chrome."
@@ -3055,11 +3059,15 @@ msgstr ""
"Mottok ugyldig svar fra nettleseren. Prøv HTTPS valget, eller bruk en annen " "Mottok ugyldig svar fra nettleseren. Prøv HTTPS valget, eller bruk en annen "
"nettleser som Chromium eller Chrome." "nettleser som Chromium eller Chrome."
#: scrobbler/listenbrainzscrobbler.cpp:174
msgid "Redirect missing token code!"
msgstr ""
#: ../build/src/ui_internetcollectionviewcontainer.h:142 #: ../build/src/ui_internetcollectionviewcontainer.h:142
msgid "Refresh catalogue" msgid "Refresh catalogue"
msgstr "" msgstr ""
#: equalizer/equalizer.cpp:151 #: equalizer/equalizer.cpp:149
msgid "Reggae" msgid "Reggae"
msgstr "Reggae" msgstr "Reggae"
@@ -3136,7 +3144,7 @@ msgstr "Gjenta spilleliste"
msgid "Repeat track" msgid "Repeat track"
msgstr "Gjenta spor" msgstr "Gjenta spor"
#: context/contextalbumsview.cpp:359 collection/collectionview.cpp:334 #: context/contextalbumsview.cpp:272 collection/collectionview.cpp:334
#: widgets/fileviewlist.cpp:44 internet/internetsearchview.cpp:434 #: widgets/fileviewlist.cpp:44 internet/internetsearchview.cpp:434
#: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229 #: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229
msgid "Replace current playlist" msgid "Replace current playlist"
@@ -3260,7 +3268,7 @@ msgstr "Gå tilbake til Strawberry"
msgid "Right" msgid "Right"
msgstr "Høyre" msgstr "Høyre"
#: equalizer/equalizer.cpp:152 #: equalizer/equalizer.cpp:150
msgid "Rock" msgid "Rock"
msgstr "Rock" msgstr "Rock"
@@ -3329,7 +3337,7 @@ msgstr "Lagre spilleliste"
msgid "Save playlist..." msgid "Save playlist..."
msgstr "Lagre spilleliste…" msgstr "Lagre spilleliste…"
#: equalizer/equalizer.cpp:211 ../build/src/ui_equalizer.h:171 #: equalizer/equalizer.cpp:209 ../build/src/ui_equalizer.h:171
msgid "Save preset" msgid "Save preset"
msgstr "Lagre forhåndsinnstilling" msgstr "Lagre forhåndsinnstilling"
@@ -3361,7 +3369,7 @@ msgstr "Skaler størrelse"
msgid "Scrobbler" msgid "Scrobbler"
msgstr "Scrobbler" msgstr "Scrobbler"
#: scrobbler/scrobblingapi20.cpp:478 #: scrobbler/scrobblingapi20.cpp:495
#, qt-format #, qt-format
msgid "Scrobbler %1 is not authenticated!" msgid "Scrobbler %1 is not authenticated!"
msgstr "Scrobbler %1 er ikke autentisert!" msgstr "Scrobbler %1 er ikke autentisert!"
@@ -3571,7 +3579,7 @@ msgstr "Vis i samling…"
msgid "Show in file browser" msgid "Show in file browser"
msgstr "" msgstr ""
#: core/mainwindow.cpp:626 context/contextalbumsview.cpp:374 #: core/mainwindow.cpp:626 context/contextalbumsview.cpp:287
#: collection/collectionview.cpp:351 widgets/fileviewlist.cpp:54 #: collection/collectionview.cpp:351 widgets/fileviewlist.cpp:54
msgid "Show in file browser..." msgid "Show in file browser..."
msgstr "Vis i filbehandler…" msgstr "Vis i filbehandler…"
@@ -3648,7 +3656,7 @@ msgstr "Størrelse"
msgid "Size:" msgid "Size:"
msgstr "Størrelse:" msgstr "Størrelse:"
#: equalizer/equalizer.cpp:154 #: equalizer/equalizer.cpp:152
msgid "Ska" msgid "Ska"
msgstr "Ska" msgstr "Ska"
@@ -3680,11 +3688,11 @@ msgstr "Lite albumomslag"
msgid "Small sidebar" msgid "Small sidebar"
msgstr "Lite sidefelt" msgstr "Lite sidefelt"
#: equalizer/equalizer.cpp:153 #: equalizer/equalizer.cpp:151
msgid "Soft" msgid "Soft"
msgstr "Myk" msgstr "Myk"
#: equalizer/equalizer.cpp:155 #: equalizer/equalizer.cpp:153
msgid "Soft Rock" msgid "Soft Rock"
msgstr "Soft rock" msgstr "Soft rock"
@@ -3807,7 +3815,7 @@ msgstr ""
msgid "Strawberry was unable to find results for this file" msgid "Strawberry was unable to find results for this file"
msgstr "Strawberry klarte ikke å finne resultater for denne filen" msgstr "Strawberry klarte ikke å finne resultater for denne filen"
#: core/song.cpp:483 #: core/song.cpp:476
msgid "Stream" msgid "Stream"
msgstr "Strøm" msgstr "Strøm"
@@ -3853,7 +3861,7 @@ msgstr "Foreslåtte etiketter"
msgid "Summary" msgid "Summary"
msgstr "Sammendrag" msgstr "Sammendrag"
#: analyzer/analyzercontainer.cpp:76 #: analyzer/analyzercontainer.cpp:75
#, qt-format #, qt-format
msgid "Super high (%1 fps)" msgid "Super high (%1 fps)"
msgstr "Superhøy (%1 bilder/sek)" msgstr "Superhøy (%1 bilder/sek)"
@@ -3882,7 +3890,7 @@ msgstr "Etikett-henter"
msgid "Target bitrate" msgid "Target bitrate"
msgstr "Ønsket bitrate" msgstr "Ønsket bitrate"
#: equalizer/equalizer.cpp:156 #: equalizer/equalizer.cpp:154
msgid "Techno" msgid "Techno"
msgstr "Tekno" msgstr "Tekno"
@@ -4039,7 +4047,7 @@ msgstr "Dette valget kan endres under innstillinger for \"Oppførsel\""
msgid "This type of device is not supported: %1" msgid "This type of device is not supported: %1"
msgstr "Denne enhetstypen (%1) støttes ikke." msgstr "Denne enhetstypen (%1) støttes ikke."
#: core/mainwindow.cpp:284 core/song.cpp:484 #: core/mainwindow.cpp:284 core/song.cpp:477
#: ../build/src/ui_tidalsettingspage.h:293 #: ../build/src/ui_tidalsettingspage.h:293
msgid "Tidal" msgid "Tidal"
msgstr "Tidal" msgstr "Tidal"
@@ -4141,11 +4149,11 @@ msgstr "UUID"
msgid "Ultra wide band (UWB)" msgid "Ultra wide band (UWB)"
msgstr "Ultrabredt bånd (UWB)" msgstr "Ultrabredt bånd (UWB)"
#: core/song.cpp:487 core/song.cpp:489 core/song.cpp:533 #: core/song.cpp:480 core/song.cpp:482 core/song.cpp:526
#: context/contextalbumsmodel.cpp:385 collection/collectionmodel.cpp:398 #: context/contextalbumsmodel.cpp:367 collection/collectionmodel.cpp:399
#: collection/collectionmodel.cpp:403 collection/collectionmodel.cpp:407 #: collection/collectionmodel.cpp:404 collection/collectionmodel.cpp:408
#: collection/collectionmodel.cpp:411 collection/collectionmodel.cpp:415 #: collection/collectionmodel.cpp:412 collection/collectionmodel.cpp:416
#: collection/collectionmodel.cpp:1362 collection/savedgroupingmanager.cpp:137 #: collection/collectionmodel.cpp:1342 collection/savedgroupingmanager.cpp:137
#: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542 #: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542
#: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490 #: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490
#: dialogs/edittagdialog.cpp:538 #: dialogs/edittagdialog.cpp:538
@@ -4303,7 +4311,7 @@ msgstr "Bruk av menyen for å legge til et spor vil…"
msgid "Variable bit rate" msgid "Variable bit rate"
msgstr "Variabel bitrate" msgstr "Variabel bitrate"
#: collection/collectionmodel.cpp:312 playlist/playlistmanager.cpp:554 #: collection/collectionmodel.cpp:313 playlist/playlistmanager.cpp:554
#: covermanager/albumcovermanager.cpp:282 internet/internetsearchmodel.cpp:94 #: covermanager/albumcovermanager.cpp:282 internet/internetsearchmodel.cpp:94
#: internet/internetsearchmodel.cpp:106 #: internet/internetsearchmodel.cpp:106
msgid "Various artists" msgid "Various artists"
@@ -4441,7 +4449,7 @@ msgstr "Du er innlogget"
msgid "You can change the way the songs in the collection are organised." msgid "You can change the way the songs in the collection are organised."
msgstr "Du kan velge hvordan sangene i biblioteket er organisert." msgstr "Du kan velge hvordan sangene i biblioteket er organisert."
#: core/songloader.cpp:137 core/songloader.cpp:142 #: core/songloader.cpp:138 core/songloader.cpp:143
msgid "You need GStreamer for this URL." msgid "You need GStreamer for this URL."
msgstr "Du trenge gstreamer for denne URLen" msgstr "Du trenge gstreamer for denne URLen"
@@ -4479,7 +4487,7 @@ msgstr "Du må starte Strawberry på nytt for å bytte språk."
msgid "Your collection is empty!" msgid "Your collection is empty!"
msgstr "Samlingen din er tom!" msgstr "Samlingen din er tom!"
#: equalizer/equalizer.cpp:157 #: equalizer/equalizer.cpp:155
msgid "Zero" msgid "Zero"
msgstr "Null" msgstr "Null"
@@ -4558,7 +4566,7 @@ msgstr "innstillinger"
msgid "p&lughw" msgid "p&lughw"
msgstr "p&lughw" msgstr "p&lughw"
#: core/song.cpp:486 #: core/song.cpp:479
msgid "qobuz" msgid "qobuz"
msgstr "" msgstr ""
@@ -4584,7 +4592,7 @@ msgstr "sorter sanger"
msgid "stop" msgid "stop"
msgstr "stopp" msgstr "stopp"
#: core/song.cpp:485 #: core/song.cpp:478
msgid "subsonic" msgid "subsonic"
msgstr "" msgstr ""

View File

@@ -120,7 +120,7 @@ msgstr " с"
msgid " seconds" msgid " seconds"
msgstr " секунд" msgstr " секунд"
#: scrobbler/scrobblingapi20.cpp:145 scrobbler/scrobblingapi20.cpp:154 #: scrobbler/scrobblingapi20.cpp:150 scrobbler/scrobblingapi20.cpp:159
#, qt-format #, qt-format
msgid "%1 Scrobbler Authentication" msgid "%1 Scrobbler Authentication"
msgstr "Аутентификация скроблерра %1" msgstr "Аутентификация скроблерра %1"
@@ -827,7 +827,7 @@ msgstr "Внешний вид"
msgid "Append files/URLs to the playlist" msgid "Append files/URLs to the playlist"
msgstr "Добавить файлы/ссылки в плейлист" msgstr "Добавить файлы/ссылки в плейлист"
#: context/contextalbumsview.cpp:358 collection/collectionview.cpp:333 #: context/contextalbumsview.cpp:271 collection/collectionview.cpp:333
#: widgets/fileviewlist.cpp:43 internet/internetsearchview.cpp:433 #: widgets/fileviewlist.cpp:43 internet/internetsearchview.cpp:433
#: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228 #: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228
msgid "Append to current playlist" msgid "Append to current playlist"
@@ -841,7 +841,7 @@ msgstr "Добавить в плейлист"
msgid "Apply compression to prevent clipping" msgid "Apply compression to prevent clipping"
msgstr "Применить сжатие для предотвращения искажений" msgstr "Применить сжатие для предотвращения искажений"
#: equalizer/equalizer.cpp:229 #: equalizer/equalizer.cpp:227
#, qt-format #, qt-format
msgid "Are you sure you want to delete the \"%1\" preset?" msgid "Are you sure you want to delete the \"%1\" preset?"
msgstr "Вы действительно хотите удалить профиль \"%1\"?" msgstr "Вы действительно хотите удалить профиль \"%1\"?"
@@ -1043,7 +1043,7 @@ msgstr "Буфер"
msgid "Buffer duration" msgid "Buffer duration"
msgstr "Размер буфера" msgstr "Размер буфера"
#: engine/gstengine.cpp:622 #: engine/gstengine.cpp:636
msgid "Buffering" msgid "Buffering"
msgstr "Буферизация" msgstr "Буферизация"
@@ -1051,11 +1051,11 @@ msgstr "Буферизация"
msgid "C&onsole" msgid "C&onsole"
msgstr "&Консоль" msgstr "&Консоль"
#: core/song.cpp:481 #: core/song.cpp:474
msgid "CD" msgid "CD"
msgstr "CD" msgstr "CD"
#: core/songloader.cpp:195 #: core/songloader.cpp:196
msgid "CD playback is only available with the GStreamer engine." msgid "CD playback is only available with the GStreamer engine."
msgstr "Воспроизведение CD доступно только с движком GStreamer." msgstr "Воспроизведение CD доступно только с движком GStreamer."
@@ -1091,7 +1091,7 @@ msgstr "Выбрать цвет…"
msgid "Choose font..." msgid "Choose font..."
msgstr "Выбрать шрифт…" msgstr "Выбрать шрифт…"
#: equalizer/equalizer.cpp:140 #: equalizer/equalizer.cpp:138
msgid "Classical" msgid "Classical"
msgstr "Классический" msgstr "Классический"
@@ -1141,11 +1141,11 @@ msgstr "Закрыть плейлист"
msgid "Closing this window will stop searching for album covers." msgid "Closing this window will stop searching for album covers."
msgstr "Закрытие этого окна остановит поиск обложек для альбомов." msgstr "Закрытие этого окна остановит поиск обложек для альбомов."
#: equalizer/equalizer.cpp:141 #: equalizer/equalizer.cpp:139
msgid "Club" msgid "Club"
msgstr "Клубный" msgstr "Клубный"
#: core/mainwindow.cpp:276 core/song.cpp:480 #: core/mainwindow.cpp:276 core/song.cpp:473
#: ../build/src/ui_collectionsettingspage.h:244 #: ../build/src/ui_collectionsettingspage.h:244
msgid "Collection" msgid "Collection"
msgstr "Фонотека" msgstr "Фонотека"
@@ -1247,7 +1247,7 @@ msgstr "Копировать обложку альбома"
msgid "Copy to collection..." msgid "Copy to collection..."
msgstr "Копировать в фонотеку…" msgstr "Копировать в фонотеку…"
#: core/mainwindow.cpp:621 context/contextalbumsview.cpp:368 #: core/mainwindow.cpp:621 context/contextalbumsview.cpp:281
#: collection/collectionview.cpp:344 playlist/playlistlistcontainer.cpp:101 #: collection/collectionview.cpp:344 playlist/playlistlistcontainer.cpp:101
#: widgets/fileviewlist.cpp:49 #: widgets/fileviewlist.cpp:49
msgid "Copy to device..." msgid "Copy to device..."
@@ -1266,7 +1266,7 @@ msgstr ""
"Невозможно создать элемент GStreamer «%1» - убедитесь, что у вас установлены " "Невозможно создать элемент GStreamer «%1» - убедитесь, что у вас установлены "
"все необходимые дополнения GStreamer" "все необходимые дополнения GStreamer"
#: scrobbler/scrobblingapi20.cpp:154 #: scrobbler/scrobblingapi20.cpp:159
#, qt-format #, qt-format
msgid "" msgid ""
"Could not open URL. Please open this URL in your browser:<br /><a href=" "Could not open URL. Please open this URL in your browser:<br /><a href="
@@ -1275,12 +1275,12 @@ msgstr ""
"Не удалось открыть URL. Откройте этот URL в своем браузере:<br /><a href=" "Не удалось открыть URL. Откройте этот URL в своем браузере:<br /><a href="
"\"%1\">%1</a>" "\"%1\">%1</a>"
#: core/songloader.cpp:260 #: core/songloader.cpp:261
#, qt-format #, qt-format
msgid "Could not open file %1" msgid "Could not open file %1"
msgstr "Не удалось открыть файл %1" msgstr "Не удалось открыть файл %1"
#: core/songloader.cpp:436 #: core/songloader.cpp:437
#, qt-format #, qt-format
msgid "Couldn't create gstreamer source element for %1" msgid "Couldn't create gstreamer source element for %1"
msgstr "Не удалось создать исходный элемент gstreamer для %1" msgstr "Не удалось создать исходный элемент gstreamer для %1"
@@ -1412,7 +1412,7 @@ msgstr "Ctrl+T"
msgid "Ctrl+Up" msgid "Ctrl+Up"
msgstr "Ctrl+Up" msgstr "Ctrl+Up"
#: equalizer/equalizer.cpp:139 #: equalizer/equalizer.cpp:137
msgid "Custom" msgid "Custom"
msgstr "Пользовательский" msgstr "Пользовательский"
@@ -1432,7 +1432,7 @@ msgstr "Пользовательский…"
msgid "D-Bus path" msgid "D-Bus path"
msgstr "Путь D-Bus" msgstr "Путь D-Bus"
#: equalizer/equalizer.cpp:142 #: equalizer/equalizer.cpp:140
msgid "Dance" msgid "Dance"
msgstr "Танцевальный" msgstr "Танцевальный"
@@ -1477,7 +1477,7 @@ msgstr "Удалить с устройства..."
msgid "Delete from disk..." msgid "Delete from disk..."
msgstr "Удалить с диска…" msgstr "Удалить с диска…"
#: equalizer/equalizer.cpp:228 ../build/src/ui_equalizer.h:174 #: equalizer/equalizer.cpp:226 ../build/src/ui_equalizer.h:174
msgid "Delete preset" msgid "Delete preset"
msgstr "Удалить профиль" msgstr "Удалить профиль"
@@ -1505,7 +1505,7 @@ msgstr "Назначение"
msgid "Details..." msgid "Details..."
msgstr "Подробнее…" msgstr "Подробнее…"
#: core/song.cpp:482 device/giolister.cpp:188 #: core/song.cpp:475 device/giolister.cpp:188
#: ../build/src/ui_contextviewcontainer.h:369 #: ../build/src/ui_contextviewcontainer.h:369
#: ../build/src/ui_backendsettingspage.h:428 #: ../build/src/ui_backendsettingspage.h:428
msgid "Device" msgid "Device"
@@ -1631,12 +1631,12 @@ msgstr "Изменить теги"
msgid "Edit track information" msgid "Edit track information"
msgstr "Изменить информацию о треке" msgstr "Изменить информацию о треке"
#: context/contextalbumsview.cpp:372 collection/collectionview.cpp:349 #: context/contextalbumsview.cpp:285 collection/collectionview.cpp:349
#: widgets/fileviewlist.cpp:53 ../build/src/ui_mainwindow.h:628 #: widgets/fileviewlist.cpp:53 ../build/src/ui_mainwindow.h:628
msgid "Edit track information..." msgid "Edit track information..."
msgstr "Изменить информацию о треке" msgstr "Изменить информацию о треке"
#: context/contextalbumsview.cpp:373 collection/collectionview.cpp:350 #: context/contextalbumsview.cpp:286 collection/collectionview.cpp:350
msgid "Edit tracks information..." msgid "Edit tracks information..."
msgstr "Изменить информацию о треках" msgstr "Изменить информацию о треках"
@@ -1729,7 +1729,7 @@ msgid "Equivalent to --log-levels *:3"
msgstr "Аналогично --log-levels *:3" msgstr "Аналогично --log-levels *:3"
#: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287 #: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287
#: context/contextalbumsview.cpp:488 collection/collectionview.cpp:552 #: context/contextalbumsview.cpp:401 collection/collectionview.cpp:552
msgid "Error" msgid "Error"
msgstr "Ошибка" msgstr "Ошибка"
@@ -1865,11 +1865,11 @@ msgstr "Получать весь альбом при поиске песен"
msgid "Fetching cover error" msgid "Fetching cover error"
msgstr "Ошибка получения обложки" msgstr "Ошибка получения обложки"
#: core/song.cpp:479 #: core/song.cpp:472
msgid "File" msgid "File"
msgstr "Файл" msgstr "Файл"
#: core/song.cpp:1006 #: core/song.cpp:999
#, qt-format #, qt-format
msgid "File %1 is not recognized as a valid audio file." msgid "File %1 is not recognized as a valid audio file."
msgstr "Файл %1 не распознается как допустимый аудиофайл." msgstr "Файл %1 не распознается как допустимый аудиофайл."
@@ -1988,15 +1988,15 @@ msgstr "Фреймов на буфер"
msgid "Frozen" msgid "Frozen"
msgstr "Замороженный" msgstr "Замороженный"
#: equalizer/equalizer.cpp:143 #: equalizer/equalizer.cpp:141
msgid "Full Bass" msgid "Full Bass"
msgstr "Бас" msgstr "Бас"
#: equalizer/equalizer.cpp:145 #: equalizer/equalizer.cpp:143
msgid "Full Bass + Treble" msgid "Full Bass + Treble"
msgstr "Бас + высокие частоты" msgstr "Бас + высокие частоты"
#: equalizer/equalizer.cpp:144 #: equalizer/equalizer.cpp:142
msgid "Full Treble" msgid "Full Treble"
msgstr "Высокие частоты" msgstr "Высокие частоты"
@@ -2148,7 +2148,7 @@ msgstr "Помощь"
msgid "High" msgid "High"
msgstr "Высокое" msgstr "Высокое"
#: analyzer/analyzercontainer.cpp:75 #: analyzer/analyzercontainer.cpp:74
#, qt-format #, qt-format
msgid "High (%1 fps)" msgid "High (%1 fps)"
msgstr "Высокая (%1 fps)" msgstr "Высокая (%1 fps)"
@@ -2232,7 +2232,7 @@ msgstr "Проверка целостности"
msgid "Intro tracks" msgid "Intro tracks"
msgstr "Вступительные треки" msgstr "Вступительные треки"
#: scrobbler/scrobblingapi20.cpp:190 #: scrobbler/scrobblingapi20.cpp:194
msgid "Invalid reply from web browser. Missing token." msgid "Invalid reply from web browser. Missing token."
msgstr "Неверный ответ от веб-браузера. Отсутствует токен." msgstr "Неверный ответ от веб-браузера. Отсутствует токен."
@@ -2264,11 +2264,11 @@ msgstr "Сохранять оригинальные файлы"
msgid "Language" msgid "Language"
msgstr "Язык" msgstr "Язык"
#: equalizer/equalizer.cpp:146 #: equalizer/equalizer.cpp:144
msgid "Laptop/Headphones" msgid "Laptop/Headphones"
msgstr "Наушники/ноутбук" msgstr "Наушники/ноутбук"
#: equalizer/equalizer.cpp:147 #: equalizer/equalizer.cpp:145
msgid "Large Hall" msgid "Large Hall"
msgstr "Большой зал" msgstr "Большой зал"
@@ -2307,7 +2307,7 @@ msgstr "Длина"
msgid "Libre.fm" msgid "Libre.fm"
msgstr "Libre.fm" msgstr "Libre.fm"
#: scrobbler/listenbrainzscrobbler.cpp:147 #: scrobbler/listenbrainzscrobbler.cpp:152
msgid "ListenBrainz Authentication" msgid "ListenBrainz Authentication"
msgstr "Аутентификация ListenBrainz" msgstr "Аутентификация ListenBrainz"
@@ -2315,7 +2315,7 @@ msgstr "Аутентификация ListenBrainz"
msgid "Listenbrainz" msgid "Listenbrainz"
msgstr "Listenbrainz" msgstr "Listenbrainz"
#: equalizer/equalizer.cpp:148 #: equalizer/equalizer.cpp:146
msgid "Live" msgid "Live"
msgstr "Лайв" msgstr "Лайв"
@@ -2351,7 +2351,7 @@ msgstr "Загрузка устройства MTP"
msgid "Loading iPod database" msgid "Loading iPod database"
msgstr "Загрузка база данных iPod" msgstr "Загрузка база данных iPod"
#: collection/collectionmodel.cpp:179 #: collection/collectionmodel.cpp:180
msgid "Loading songs" msgid "Loading songs"
msgstr "Загрузка песен" msgstr "Загрузка песен"
@@ -2363,7 +2363,7 @@ msgstr "Загрузка композиций"
msgid "Loading tracks info" msgid "Loading tracks info"
msgstr "Загрузка информации о треках" msgstr "Загрузка информации о треках"
#: collection/collectionmodel.cpp:172 ../build/src/ui_organisedialog.h:271 #: collection/collectionmodel.cpp:173 ../build/src/ui_organisedialog.h:271
msgid "Loading..." msgid "Loading..."
msgstr "Загрузка…" msgstr "Загрузка…"
@@ -2387,7 +2387,7 @@ msgstr "Профиль Long term prediction (LTP)"
msgid "Love" msgid "Love"
msgstr "Нравится" msgstr "Нравится"
#: analyzer/analyzercontainer.cpp:73 #: analyzer/analyzercontainer.cpp:72
#, qt-format #, qt-format
msgid "Low (%1 fps)" msgid "Low (%1 fps)"
msgstr "Низкая (%1 fps)" msgstr "Низкая (%1 fps)"
@@ -2444,7 +2444,7 @@ msgstr "Максимальный битрейт"
msgid "Maximum number of login attempts reached." msgid "Maximum number of login attempts reached."
msgstr "Достигнуто максимальное количество попыток входа в систему." msgstr "Достигнуто максимальное количество попыток входа в систему."
#: analyzer/analyzercontainer.cpp:74 #: analyzer/analyzercontainer.cpp:73
#, qt-format #, qt-format
msgid "Medium (%1 fps)" msgid "Medium (%1 fps)"
msgstr "Средняя (%1 fps)" msgstr "Средняя (%1 fps)"
@@ -2570,7 +2570,7 @@ msgstr "Музыка"
msgid "Mute" msgid "Mute"
msgstr "Приглушить звук" msgstr "Приглушить звук"
#: equalizer/equalizer.cpp:211 collection/savedgroupingmanager.cpp:55 #: equalizer/equalizer.cpp:209 collection/savedgroupingmanager.cpp:55
#: ../build/src/ui_deviceproperties.h:368 #: ../build/src/ui_deviceproperties.h:368
msgid "Name" msgid "Name"
msgstr "Имя" msgstr "Имя"
@@ -2617,7 +2617,7 @@ msgstr "Дальше"
msgid "Next week" msgid "Next week"
msgstr "На следующей неделе" msgstr "На следующей неделе"
#: analyzer/analyzercontainer.cpp:86 #: analyzer/analyzercontainer.cpp:85
msgid "No analyzer" msgid "No analyzer"
msgstr "Без анализатора" msgstr "Без анализатора"
@@ -2653,7 +2653,7 @@ msgid "None"
msgstr "Ничего" msgstr "Ничего"
#: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287 #: core/mainwindow.cpp:2181 core/mainwindow.cpp:2287
#: context/contextalbumsview.cpp:488 collection/collectionview.cpp:552 #: context/contextalbumsview.cpp:401 collection/collectionview.cpp:552
msgid "None of the selected songs were suitable for copying to a device" msgid "None of the selected songs were suitable for copying to a device"
msgstr "Ни одна из выбранных песен не была скопирована на устройство" msgstr "Ни одна из выбранных песен не была скопирована на устройство"
@@ -2727,7 +2727,7 @@ msgstr "При запуске"
msgid "Opacity" msgid "Opacity"
msgstr "Непрозрачность" msgstr "Непрозрачность"
#: scrobbler/scrobblingapi20.cpp:145 #: scrobbler/scrobblingapi20.cpp:150
#, qt-format #, qt-format
msgid "" msgid ""
"Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to " "Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to "
@@ -2749,7 +2749,7 @@ msgstr "Открыть аудио &CD..."
msgid "Open device" msgid "Open device"
msgstr "Открыть устройство" msgstr "Открыть устройство"
#: context/contextalbumsview.cpp:360 collection/collectionview.cpp:335 #: context/contextalbumsview.cpp:273 collection/collectionview.cpp:335
#: widgets/fileviewlist.cpp:45 internet/internetsearchview.cpp:435 #: widgets/fileviewlist.cpp:45 internet/internetsearchview.cpp:435
#: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230 #: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230
#: ../build/src/ui_behavioursettingspage.h:274 #: ../build/src/ui_behavioursettingspage.h:274
@@ -2781,7 +2781,7 @@ msgstr "Opus"
msgid "Organise Files" msgid "Organise Files"
msgstr "Упорядочить файлы" msgstr "Упорядочить файлы"
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:366 #: core/mainwindow.cpp:625 context/contextalbumsview.cpp:279
#: collection/collectionview.cpp:342 #: collection/collectionview.cpp:342
msgid "Organise files..." msgid "Organise files..."
msgstr "Упорядочить файлы…" msgstr "Упорядочить файлы…"
@@ -2838,7 +2838,7 @@ msgstr "&Плейлист"
msgid "Partition label" msgid "Partition label"
msgstr "Метка раздела" msgstr "Метка раздела"
#: equalizer/equalizer.cpp:149 #: equalizer/equalizer.cpp:147
msgid "Party" msgid "Party"
msgstr "Вечеринка" msgstr "Вечеринка"
@@ -2932,12 +2932,12 @@ msgstr "Плейлисты"
msgid "Please close your browser and return to Strawberry." msgid "Please close your browser and return to Strawberry."
msgstr "Пожалуйста, закройте браузер и вернитесь в Strawberry." msgstr "Пожалуйста, закройте браузер и вернитесь в Strawberry."
#: scrobbler/listenbrainzscrobbler.cpp:147 #: scrobbler/listenbrainzscrobbler.cpp:152
#, qt-format #, qt-format
msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>" msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>"
msgstr "Откройте этот URL в своем браузере:<br /><a href=\"%1\">%1</a>" msgstr "Откройте этот URL в своем браузере:<br /><a href=\"%1\">%1</a>"
#: equalizer/equalizer.cpp:150 #: equalizer/equalizer.cpp:148
msgid "Pop" msgid "Pop"
msgstr "Поп" msgstr "Поп"
@@ -2975,7 +2975,7 @@ msgstr "Приоритетные имена файлов обложек (чер
msgid "Preferred format" msgid "Preferred format"
msgstr "Предпочитаемый формат" msgstr "Предпочитаемый формат"
#: core/songloader.cpp:156 #: core/songloader.cpp:157
msgid "Preload function was not set for blocking operation." msgid "Preload function was not set for blocking operation."
msgstr "" msgstr ""
"Функция предварительной нагрузки не была установлена ​​для операции " "Функция предварительной нагрузки не была установлена ​​для операции "
@@ -3061,7 +3061,7 @@ msgstr "Очередь выбранных треков для последующ
msgid "Queue to play next" msgid "Queue to play next"
msgstr "Очередь воспроизведения" msgstr "Очередь воспроизведения"
#: core/mainwindow.cpp:1631 context/contextalbumsview.cpp:363 #: core/mainwindow.cpp:1631 context/contextalbumsview.cpp:276
#: collection/collectionview.cpp:338 internet/internetsearchview.cpp:438 #: collection/collectionview.cpp:338 internet/internetsearchview.cpp:438
#: internet/internetcollectionview.cpp:307 #: internet/internetcollectionview.cpp:307
msgid "Queue track" msgid "Queue track"
@@ -3083,7 +3083,11 @@ msgstr "&Относительно"
msgid "Really cancel?" msgid "Really cancel?"
msgstr "Действительно отменить?" msgstr "Действительно отменить?"
#: scrobbler/scrobblingapi20.cpp:184 #: scrobbler/listenbrainzscrobbler.cpp:178
msgid "Received invalid reply from web browser."
msgstr ""
#: scrobbler/scrobblingapi20.cpp:198
msgid "" msgid ""
"Received invalid reply from web browser. Try the HTTPS option, or use " "Received invalid reply from web browser. Try the HTTPS option, or use "
"another browser like Chromium or Chrome." "another browser like Chromium or Chrome."
@@ -3091,11 +3095,15 @@ msgstr ""
"Получен неверный ответ от веб-браузера. Попробуйте опцию HTTPS или " "Получен неверный ответ от веб-браузера. Попробуйте опцию HTTPS или "
"используйте другой браузер, такой как Chromium или Chrome." "используйте другой браузер, такой как Chromium или Chrome."
#: scrobbler/listenbrainzscrobbler.cpp:174
msgid "Redirect missing token code!"
msgstr ""
#: ../build/src/ui_internetcollectionviewcontainer.h:142 #: ../build/src/ui_internetcollectionviewcontainer.h:142
msgid "Refresh catalogue" msgid "Refresh catalogue"
msgstr "Обновить каталог" msgstr "Обновить каталог"
#: equalizer/equalizer.cpp:151 #: equalizer/equalizer.cpp:149
msgid "Reggae" msgid "Reggae"
msgstr "Регги" msgstr "Регги"
@@ -3172,7 +3180,7 @@ msgstr "Повторять плейлист"
msgid "Repeat track" msgid "Repeat track"
msgstr "Повторять трек" msgstr "Повторять трек"
#: context/contextalbumsview.cpp:359 collection/collectionview.cpp:334 #: context/contextalbumsview.cpp:272 collection/collectionview.cpp:334
#: widgets/fileviewlist.cpp:44 internet/internetsearchview.cpp:434 #: widgets/fileviewlist.cpp:44 internet/internetsearchview.cpp:434
#: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229 #: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229
msgid "Replace current playlist" msgid "Replace current playlist"
@@ -3296,7 +3304,7 @@ msgstr "Вернуться в Strawberry"
msgid "Right" msgid "Right"
msgstr "Правый канал" msgstr "Правый канал"
#: equalizer/equalizer.cpp:152 #: equalizer/equalizer.cpp:150
msgid "Rock" msgid "Rock"
msgstr "Рок" msgstr "Рок"
@@ -3365,7 +3373,7 @@ msgstr "Сохранить плейлист"
msgid "Save playlist..." msgid "Save playlist..."
msgstr "Сохранить плейлист…" msgstr "Сохранить плейлист…"
#: equalizer/equalizer.cpp:211 ../build/src/ui_equalizer.h:171 #: equalizer/equalizer.cpp:209 ../build/src/ui_equalizer.h:171
msgid "Save preset" msgid "Save preset"
msgstr "Сохранить профиль" msgstr "Сохранить профиль"
@@ -3397,7 +3405,7 @@ msgstr "Размер масштабирования"
msgid "Scrobbler" msgid "Scrobbler"
msgstr "Скробблер" msgstr "Скробблер"
#: scrobbler/scrobblingapi20.cpp:478 #: scrobbler/scrobblingapi20.cpp:495
#, qt-format #, qt-format
msgid "Scrobbler %1 is not authenticated!" msgid "Scrobbler %1 is not authenticated!"
msgstr "Скробблер %1 не аутентифицирован!" msgstr "Скробблер %1 не аутентифицирован!"
@@ -3607,7 +3615,7 @@ msgstr "Показать в фонотеке…"
msgid "Show in file browser" msgid "Show in file browser"
msgstr "Показать в файловом браузере" msgstr "Показать в файловом браузере"
#: core/mainwindow.cpp:626 context/contextalbumsview.cpp:374 #: core/mainwindow.cpp:626 context/contextalbumsview.cpp:287
#: collection/collectionview.cpp:351 widgets/fileviewlist.cpp:54 #: collection/collectionview.cpp:351 widgets/fileviewlist.cpp:54
msgid "Show in file browser..." msgid "Show in file browser..."
msgstr "Открыть в диспетчере файлов" msgstr "Открыть в диспетчере файлов"
@@ -3684,7 +3692,7 @@ msgstr "Размер"
msgid "Size:" msgid "Size:"
msgstr "Размер:" msgstr "Размер:"
#: equalizer/equalizer.cpp:154 #: equalizer/equalizer.cpp:152
msgid "Ska" msgid "Ska"
msgstr "Ска" msgstr "Ска"
@@ -3716,11 +3724,11 @@ msgstr "Маленькая обложка альбома"
msgid "Small sidebar" msgid "Small sidebar"
msgstr "Узкая боковая панель" msgstr "Узкая боковая панель"
#: equalizer/equalizer.cpp:153 #: equalizer/equalizer.cpp:151
msgid "Soft" msgid "Soft"
msgstr "Мягкий" msgstr "Мягкий"
#: equalizer/equalizer.cpp:155 #: equalizer/equalizer.cpp:153
msgid "Soft Rock" msgid "Soft Rock"
msgstr "Софт-рок" msgstr "Софт-рок"
@@ -3847,7 +3855,7 @@ msgstr ""
msgid "Strawberry was unable to find results for this file" msgid "Strawberry was unable to find results for this file"
msgstr "Strawberry не смог найти результаты по запросу для этого файла" msgstr "Strawberry не смог найти результаты по запросу для этого файла"
#: core/song.cpp:483 #: core/song.cpp:476
msgid "Stream" msgid "Stream"
msgstr "Поток" msgstr "Поток"
@@ -3893,7 +3901,7 @@ msgstr "Предлагаемые теги"
msgid "Summary" msgid "Summary"
msgstr "Сводка" msgstr "Сводка"
#: analyzer/analyzercontainer.cpp:76 #: analyzer/analyzercontainer.cpp:75
#, qt-format #, qt-format
msgid "Super high (%1 fps)" msgid "Super high (%1 fps)"
msgstr "Очень высокая (%1 fps)" msgstr "Очень высокая (%1 fps)"
@@ -3922,7 +3930,7 @@ msgstr "Сборщик тегов"
msgid "Target bitrate" msgid "Target bitrate"
msgstr "Целевой битрейт" msgstr "Целевой битрейт"
#: equalizer/equalizer.cpp:156 #: equalizer/equalizer.cpp:154
msgid "Techno" msgid "Techno"
msgstr "Техно" msgstr "Техно"
@@ -4080,7 +4088,7 @@ msgstr "Эта опция может быть изменена в настрой
msgid "This type of device is not supported: %1" msgid "This type of device is not supported: %1"
msgstr "Не поддерживаемый тип устройства: %1" msgstr "Не поддерживаемый тип устройства: %1"
#: core/mainwindow.cpp:284 core/song.cpp:484 #: core/mainwindow.cpp:284 core/song.cpp:477
#: ../build/src/ui_tidalsettingspage.h:293 #: ../build/src/ui_tidalsettingspage.h:293
msgid "Tidal" msgid "Tidal"
msgstr "Tidal" msgstr "Tidal"
@@ -4182,11 +4190,11 @@ msgstr "UUID"
msgid "Ultra wide band (UWB)" msgid "Ultra wide band (UWB)"
msgstr "Ультраширокая полоса пропускания (UWB)" msgstr "Ультраширокая полоса пропускания (UWB)"
#: core/song.cpp:487 core/song.cpp:489 core/song.cpp:533 #: core/song.cpp:480 core/song.cpp:482 core/song.cpp:526
#: context/contextalbumsmodel.cpp:385 collection/collectionmodel.cpp:398 #: context/contextalbumsmodel.cpp:367 collection/collectionmodel.cpp:399
#: collection/collectionmodel.cpp:403 collection/collectionmodel.cpp:407 #: collection/collectionmodel.cpp:404 collection/collectionmodel.cpp:408
#: collection/collectionmodel.cpp:411 collection/collectionmodel.cpp:415 #: collection/collectionmodel.cpp:412 collection/collectionmodel.cpp:416
#: collection/collectionmodel.cpp:1362 collection/savedgroupingmanager.cpp:137 #: collection/collectionmodel.cpp:1342 collection/savedgroupingmanager.cpp:137
#: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542 #: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542
#: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490 #: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490
#: dialogs/edittagdialog.cpp:538 #: dialogs/edittagdialog.cpp:538
@@ -4346,7 +4354,7 @@ msgstr "После добавления песни через меню…"
msgid "Variable bit rate" msgid "Variable bit rate"
msgstr "Переменный битрейт" msgstr "Переменный битрейт"
#: collection/collectionmodel.cpp:312 playlist/playlistmanager.cpp:554 #: collection/collectionmodel.cpp:313 playlist/playlistmanager.cpp:554
#: covermanager/albumcovermanager.cpp:282 internet/internetsearchmodel.cpp:94 #: covermanager/albumcovermanager.cpp:282 internet/internetsearchmodel.cpp:94
#: internet/internetsearchmodel.cpp:106 #: internet/internetsearchmodel.cpp:106
msgid "Various artists" msgid "Various artists"
@@ -4487,7 +4495,7 @@ msgstr "Вы вошли в систему."
msgid "You can change the way the songs in the collection are organised." msgid "You can change the way the songs in the collection are organised."
msgstr "Можно изменить способ организации композиций в фонотеке." msgstr "Можно изменить способ организации композиций в фонотеке."
#: core/songloader.cpp:137 core/songloader.cpp:142 #: core/songloader.cpp:138 core/songloader.cpp:143
msgid "You need GStreamer for this URL." msgid "You need GStreamer for this URL."
msgstr "Вам нужен GStreamer для этого URL." msgstr "Вам нужен GStreamer для этого URL."
@@ -4524,7 +4532,7 @@ msgstr "Для применения языка потребуется перез
msgid "Your collection is empty!" msgid "Your collection is empty!"
msgstr "Ваша фонотека пуста!" msgstr "Ваша фонотека пуста!"
#: equalizer/equalizer.cpp:157 #: equalizer/equalizer.cpp:155
msgid "Zero" msgid "Zero"
msgstr "По-умолчанию" msgstr "По-умолчанию"
@@ -4603,7 +4611,7 @@ msgstr "настройки"
msgid "p&lughw" msgid "p&lughw"
msgstr "p&lughw" msgstr "p&lughw"
#: core/song.cpp:486 #: core/song.cpp:479
msgid "qobuz" msgid "qobuz"
msgstr "qobuz" msgstr "qobuz"
@@ -4629,7 +4637,7 @@ msgstr "сортировать песни"
msgid "stop" msgid "stop"
msgstr "стоп" msgstr "стоп"
#: core/song.cpp:485 #: core/song.cpp:478
msgid "subsonic" msgid "subsonic"
msgstr "subsonic" msgstr "subsonic"

View File

@@ -474,8 +474,12 @@ void PlayingWidget::contextMenuEvent(QContextMenuEvent* e) {
menu_->popup(mapToGlobal(e->pos())); menu_->popup(mapToGlobal(e->pos()));
} }
void PlayingWidget::mouseReleaseEvent(QMouseEvent*) { void PlayingWidget::mouseDoubleClickEvent(QMouseEvent* e) {
// Same behaviour as right-click > Show Fullsize // Same behaviour as right-click > Show Fullsize
if (e->button() == Qt::LeftButton && song_.is_valid()) {
album_cover_choice_controller_->ShowCover(song_, image_original_);
}
} }

View File

@@ -89,7 +89,7 @@ class PlayingWidget : public QWidget {
void paintEvent(QPaintEvent *e); void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent*); void resizeEvent(QResizeEvent*);
void contextMenuEvent(QContextMenuEvent *e); void contextMenuEvent(QContextMenuEvent *e);
void mouseReleaseEvent(QMouseEvent*); void mouseDoubleClickEvent(QMouseEvent*);
void dragEnterEvent(QDragEnterEvent *e); void dragEnterEvent(QDragEnterEvent *e);
void dropEvent(QDropEvent *e); void dropEvent(QDropEvent *e);

View File

@@ -30,6 +30,7 @@
#include "test_utils.h" #include "test_utils.h"
#include "core/timeconstants.h"
#include "core/song.h" #include "core/song.h"
#include "core/database.h" #include "core/database.h"
#include "core/logging.h" #include "core/logging.h"
@@ -43,7 +44,7 @@ class CollectionBackendTest : public ::testing::Test {
virtual void SetUp() { virtual void SetUp() {
database_.reset(new MemoryDatabase(nullptr)); database_.reset(new MemoryDatabase(nullptr));
backend_.reset(new CollectionBackend); backend_.reset(new CollectionBackend);
backend_->Init(database_.get(), SCollection::kSongsTable, SCollection::kDirsTable, SCollection::kSubdirsTable, SCollection::kFtsTable); backend_->Init(database_.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kDirsTable, SCollection::kSubdirsTable, SCollection::kFtsTable);
} }
Song MakeDummySong(int directory_id) { Song MakeDummySong(int directory_id) {
@@ -367,4 +368,65 @@ TEST_F(SingleSong, MarkSongsUnavailable) {
} }
TEST_F(SingleSong, TestUrls) {
QStringList strings = QStringList() << "file:///mnt/music/01 - Pink Floyd - Echoes.flac"
<< "file:///mnt/music/02 - Björn Afzelius - Det räcker nu.flac"
<< "file:///mnt/music/03 - Vazelina Bilopphøggers - Bomull i øra.flac"
<< "file:///mnt/music/Test !#$%&'()-@^_`{}~..flac";
QList<QUrl> urls = QUrl::fromStringList(strings);
for (const QUrl &url : urls) {
QString str = url.toString(QUrl::FullyEncoded);
QUrl test_url = QUrl::fromEncoded(str.toUtf8());
EXPECT_EQ(url, test_url);
Song song(Song::Source_Collection);
song.set_directory_id(1);
song.set_title("Test Title");
song.set_album("Test Album");
song.set_artist("Test Artist");
song.set_url(url);
song.set_length_nanosec(kNsecPerSec);
song.set_mtime(1);
song.set_ctime(1);
song.set_filesize(1);
song.set_valid(true);
backend_->AddOrUpdateSongs(SongList() << song);
if (HasFatalFailure()) continue;
SongList songs = backend_->GetSongsByUrl(url);
EXPECT_EQ(1, songs.count());
if (songs.count() < 1) continue;
Song new_song = songs.first();
EXPECT_TRUE(new_song.is_valid());
EXPECT_EQ(new_song.url(), url);
new_song = backend_->GetSongByUrl(url);
EXPECT_EQ(1, songs.count());
if (songs.count() < 1) continue;
EXPECT_TRUE(new_song.is_valid());
EXPECT_EQ(new_song.url(), url);
QSqlDatabase db(database_.get()->Connect());
QSqlQuery q(db);
q.prepare(QString("SELECT url FROM %1 WHERE url = :url").arg(SCollection::kSongsTable));
q.bindValue(":url", url.toString(QUrl::FullyEncoded));
q.exec();
while (q.next()) {
EXPECT_EQ(url, q.value(0).toUrl());
EXPECT_EQ(url, QUrl::fromEncoded(q.value(0).toByteArray()));
}
}
}
} // namespace } // namespace

View File

@@ -44,7 +44,7 @@ class CollectionModelTest : public ::testing::Test {
void SetUp() { void SetUp() {
database_.reset(new MemoryDatabase(nullptr)); database_.reset(new MemoryDatabase(nullptr));
backend_.reset(new CollectionBackend); backend_.reset(new CollectionBackend);
backend_->Init(database_.get(), SCollection::kSongsTable, SCollection::kDirsTable, SCollection::kSubdirsTable, SCollection::kFtsTable); backend_->Init(database_.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kDirsTable, SCollection::kSubdirsTable, SCollection::kFtsTable);
model_.reset(new CollectionModel(backend_.get(), nullptr)); model_.reset(new CollectionModel(backend_.get(), nullptr));
added_dir_ = false; added_dir_ = false;