Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd490ca946 | ||
|
|
793f6b00e6 | ||
|
|
c1ec568fda | ||
|
|
4e2b52975e | ||
|
|
c141df6b86 | ||
|
|
63b781765a | ||
|
|
c121e141e7 | ||
|
|
7544c9a9f5 | ||
|
|
722a088515 | ||
|
|
0ce75bff58 | ||
|
|
3744b6d3de | ||
|
|
a4ebd91e8d | ||
|
|
3bb0ee916d | ||
|
|
fd6afdf5f3 | ||
|
|
47c13a840e | ||
|
|
5b61992b3c | ||
|
|
36331dc253 | ||
|
|
4265cf31b2 | ||
|
|
337e47269f | ||
|
|
7039234471 | ||
|
|
f7f9333d91 | ||
|
|
bf35665932 | ||
|
|
089a2271c2 | ||
|
|
f8e83e3631 | ||
|
|
46fd329913 | ||
|
|
6b9ba96e77 | ||
|
|
b7d360d850 | ||
|
|
8e226302ab | ||
|
|
7795b9edaf | ||
|
|
9375d9699a | ||
|
|
cf0442d5b8 | ||
|
|
b386ca14df | ||
|
|
ea47fae31e | ||
|
|
e0fed07b10 | ||
|
|
779d5ff7b6 | ||
|
|
eb6fbd03ec | ||
|
|
95409d1b0e | ||
|
|
49599c8731 | ||
|
|
572f94e000 | ||
|
|
c0a2ad5f50 | ||
|
|
71fa5acc74 | ||
|
|
bac5b7679d | ||
|
|
93ade821a5 | ||
|
|
a718e19979 | ||
|
|
8ac83a46f5 | ||
|
|
1b65dcd7df | ||
|
|
bbad45f1e7 | ||
|
|
331b9cca18 | ||
|
|
a9accb7d85 | ||
|
|
1862e70628 | ||
|
|
c4f7054ca6 | ||
|
|
175e568a28 | ||
|
|
c8d580e7de | ||
|
|
bc0c50ee65 | ||
|
|
45e9dd96d1 | ||
|
|
1cc73562a3 |
48
.github/workflows/ccpp.yml
vendored
Normal file
48
.github/workflows/ccpp.yml
vendored
Normal 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
|
||||
@@ -41,7 +41,7 @@ script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
make -j8 || travis_terminate 1;
|
||||
make install || travis_terminate 1;
|
||||
sudo make dmg;
|
||||
make dmg;
|
||||
fi
|
||||
after_success:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry*.dmg; fi
|
||||
|
||||
6
3rdparty/singleapplication/CMakeLists.txt
vendored
6
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -5,10 +5,10 @@ include(CheckFunctionExists)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
|
||||
endif()
|
||||
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.0)
|
||||
|
||||
@@ -114,12 +114,9 @@ void SingleApplicationPrivate::genBlockServerName() {
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
uid_t uid = geteuid();
|
||||
if (uid != -1) {
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
struct passwd *pw = getpwuid(geteuid());
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) username = qgetenv("USER");
|
||||
|
||||
@@ -114,12 +114,9 @@ void SingleCoreApplicationPrivate::genBlockServerName() {
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||
uid_t uid = geteuid();
|
||||
if (uid != -1) {
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
struct passwd *pw = getpwuid(geteuid());
|
||||
if (pw) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
#endif
|
||||
if (username.isEmpty()) username = qgetenv("USER");
|
||||
|
||||
@@ -56,32 +56,17 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
|
||||
set(DEBUG ON)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER MATCHES ".*clang")
|
||||
set(CMAKE_COMPILER_IS_CLANGXX 1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
endif(APPLE)
|
||||
|
||||
find_program(CCACHE_EXECUTABLE NAMES ccache)
|
||||
if (CCACHE_EXECUTABLE)
|
||||
message(STATUS "ccache found: will be used for compilation and linkage")
|
||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_EXECUTABLE})
|
||||
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_EXECUTABLE})
|
||||
endif ()
|
||||
find_program(QT_LCONVERT_EXECUTABLE NAMES lconvert lconvert-qt5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH)
|
||||
find_program(QT_LCONVERT_EXECUTABLE NAMES lconvert lconvert-qt5)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||
pkg_check_modules(GIO REQUIRED gio-2.0)
|
||||
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
|
||||
pkg_check_modules(CDIO libcdio)
|
||||
find_package(Boost REQUIRED)
|
||||
find_package(Threads)
|
||||
find_package(GnuTLS)
|
||||
find_package(Boost REQUIRED)
|
||||
find_package(Protobuf REQUIRED)
|
||||
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
|
||||
if(LINUX)
|
||||
@@ -98,6 +83,10 @@ endif()
|
||||
if(X11_FOUND)
|
||||
set(HAVE_X11 ON)
|
||||
endif()
|
||||
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||
pkg_check_modules(GIO REQUIRED gio-2.0)
|
||||
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
|
||||
pkg_check_modules(LIBCDIO libcdio)
|
||||
pkg_check_modules(GSTREAMER gstreamer-1.0)
|
||||
pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0)
|
||||
pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
|
||||
@@ -303,7 +292,7 @@ optional_component(GLOBALSHORTCUTS ON "Global shortcuts"
|
||||
)
|
||||
|
||||
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||
DEPENDS "libcdio" CDIO_FOUND
|
||||
DEPENDS "libcdio" LIBCDIO_FOUND
|
||||
)
|
||||
|
||||
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
|
||||
|
||||
17
Changelog
17
Changelog
@@ -2,6 +2,23 @@ Strawberry Music Player
|
||||
=======================
|
||||
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:
|
||||
|
||||
* Fixed lowercased album artist in playlist column
|
||||
|
||||
@@ -20,7 +20,7 @@ Strawberry is a music player and music collection organizer. It is a fork of Cle
|
||||
* Edit tags on music files
|
||||
* Fetch tags from MusicBrainz
|
||||
* Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal
|
||||
* Song lyrics from AudD, lyrics.ovh and lololyrics.com
|
||||
* Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
|
||||
* Support for multiple backends
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
add_custom_target(dmg
|
||||
COMMAND sudo /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
||||
COMMAND sudo ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
||||
COMMAND sudo ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
|
||||
COMMAND /usr/local/opt/qt5/bin/macdeployqt strawberry.app
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/macdeploy.py strawberry.app
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/dist/macos/create-dmg.sh strawberry.app
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 6)
|
||||
set(STRAWBERRY_VERSION_PATCH 6)
|
||||
set(STRAWBERRY_VERSION_PATCH 7)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -58,7 +58,7 @@ Description: Audio player and music collection organizer
|
||||
- Edit tags on music files
|
||||
- Fetch tags from MusicBrainz
|
||||
- Album cover art from Lastfm, Musicbrainz, Discogs, Deezer and Tidal
|
||||
- Song lyrics from AudD, lyrics.ovh and lololyrics.com
|
||||
- Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
|
||||
- Support for multiple backends
|
||||
- Audio analyzer
|
||||
- Audio equalizer
|
||||
|
||||
48
debian/copyright
vendored
48
debian/copyright
vendored
@@ -17,6 +17,9 @@ Copyright: 2011, 2012, David Sansome <me@davidsansome.com>
|
||||
License: Apache-2.0
|
||||
|
||||
Files: src/core/main.h
|
||||
src/core/iconloader.cpp
|
||||
src/core/iconloader.h
|
||||
src/core/iconmapper.h
|
||||
src/config.h.in
|
||||
src/version.h.in
|
||||
src/context/contextview.cpp
|
||||
@@ -25,6 +28,8 @@ Files: src/core/main.h
|
||||
src/engine/enginetype.h
|
||||
src/engine/alsadevicefinder.cpp
|
||||
src/engine/alsadevicefinder.h
|
||||
src/engine/mmdevicefinder.cpp
|
||||
src/engine/mmdevicefinder.h
|
||||
src/engine/devicefinder.cpp
|
||||
src/engine/devicefinder.h
|
||||
src/engine/enginedevice.cpp
|
||||
@@ -33,6 +38,8 @@ Files: src/core/main.h
|
||||
src/engine/phononengine.h
|
||||
src/internet/internetservice.cpp
|
||||
src/internet/internetservice.h
|
||||
src/internet/internettabsview.cpp
|
||||
src/internet/internettabsview.h
|
||||
src/settings/backendsettingspage.cpp
|
||||
src/settings/backendsettingspage.h
|
||||
src/settings/scrobblersettingspage.cpp
|
||||
@@ -72,28 +79,50 @@ Files: src/core/main.cpp
|
||||
src/core/player.h
|
||||
src/core/song.cpp
|
||||
src/core/song.h
|
||||
src/core/songloader.cpp
|
||||
src/core/songloader.h
|
||||
src/core/urlhandler.cpp
|
||||
src/core/urlhandler.h
|
||||
src/core/utilities.cpp
|
||||
src/core/utilities.h
|
||||
src/core/iconloader.cpp
|
||||
src/core/iconloader.h
|
||||
src/core/network.cpp
|
||||
src/core/network.h
|
||||
src/core/filesystemmusicstorage.cpp
|
||||
src/core/filesystemmusicstorage.h
|
||||
src/core/stylesheetloader.cpp
|
||||
src/core/stylesheetloader.h
|
||||
src/engine/gstenginepipeline.cpp
|
||||
src/engine/gstenginepipeline.h
|
||||
src/engine/vlcengine.cpp
|
||||
src/engine/vlcengine.h
|
||||
src/collection/collectionwatcher.cpp
|
||||
src/collection/collectionwatcher.h
|
||||
src/collection/collectionbackend.cpp
|
||||
src/collection/collectionbackend.h
|
||||
src/collection/collectionmodel.cpp
|
||||
src/collection/collectionmodel.h
|
||||
src/context/contextalbumsmodel.cpp
|
||||
src/context/contextalbumsview.cpp
|
||||
src/context/contextalbumsmodel.h
|
||||
src/context/contextalbumsview.h
|
||||
src/widgets/playingwidget.cpp
|
||||
src/widgets/playingwidget.h
|
||||
src/widgets/osdpretty.cpp
|
||||
src/widgets/osdpretty.h
|
||||
src/dialogs/about.cpp
|
||||
src/dialogs/about.h
|
||||
src/playlist/playlist.cpp
|
||||
src/playlist/playlist.h
|
||||
src/playlist/playlistitem.cpp
|
||||
src/playlist/playlistitem.h
|
||||
src/playlist/playlistdelegates.cpp
|
||||
src/playlist/playlistdelegates.h
|
||||
src/playlist/playlistbackend.cpp
|
||||
src/playlist/playlistbackend.h
|
||||
src/playlist/playlistview.cpp
|
||||
src/playlist/playlistview.h
|
||||
src/playlist/songplaylistitem.cpp
|
||||
src/playlist/songplaylistitem.h
|
||||
src/internet/internetplaylistitem.cpp
|
||||
src/internet/internetsearch.cpp
|
||||
src/internet/internetsearch.h
|
||||
@@ -101,6 +130,10 @@ Files: src/core/main.cpp
|
||||
src/internet/internetsearchview.h
|
||||
src/internet/internetservices.cpp
|
||||
src/internet/internetservices.h
|
||||
src/internet/internetsongsview.cpp
|
||||
src/internet/internetsongsview.h
|
||||
src/internet/internetcollectionview.cpp
|
||||
src/internet/internetcollectionview.h
|
||||
ext/libstrawberry-tagreader/tagreader.cpp
|
||||
ext/libstrawberry-tagreader/tagreader.h
|
||||
src/device/devicemanager.cpp
|
||||
@@ -113,10 +146,16 @@ Files: src/core/main.cpp
|
||||
src/device/deviceview.h
|
||||
src/device/connecteddevice.cpp
|
||||
src/device/connecteddevice.h
|
||||
src/device/mtpconnection.cpp
|
||||
src/device/mtpconnection.h
|
||||
src/device/mtpdevice.cpp
|
||||
src/device/mtpdevice.h
|
||||
src/globalshortcuts/globalshortcuts.cpp
|
||||
src/globalshortcuts/globalshortcuts.h
|
||||
src/settings/shortcutssettingspage.cpp
|
||||
src/settings/shortcutssettingspage.h
|
||||
src/settings/appearancesettingspage.cpp
|
||||
src/settings/appearancesettingspage.h
|
||||
src/organise/organise.cpp
|
||||
src/organise/organise.h
|
||||
src/organise/organisedialog.cpp
|
||||
@@ -125,8 +164,10 @@ Files: src/core/main.cpp
|
||||
src/organise/organiseerrordialog.h
|
||||
src/transcoder/transcoder.cpp
|
||||
src/transcoder/transcoder.h
|
||||
src/musicbrainz/musicbrainzclient.cpp
|
||||
src/musicbrainz/musicbrainzclient.h
|
||||
Copyright: 2010, 2012-2014 David Sansome <me@davidsansome.com>
|
||||
2012-2014, 2017-2018 Jonas Kvinge <jonas@jkvinge.net>
|
||||
2012-2014, 2017-2019 Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/engine/enginebase.cpp
|
||||
@@ -262,6 +303,7 @@ Files: src/internet/localredirectserver.cpp
|
||||
src/internet/localredirectserver.h
|
||||
Copyright: 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
License: GPL-3+
|
||||
|
||||
Files: src/transcoder/transcoderoptionsopus.cpp
|
||||
|
||||
9
dist/macos/macdeploy.py
vendored
9
dist/macos/macdeploy.py
vendored
@@ -304,10 +304,9 @@ def CopyLibrary(path):
|
||||
new_path = os.path.join(frameworks_dir, os.path.basename(path))
|
||||
#args = ['cp', path, new_path]
|
||||
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
|
||||
commands.append(args)
|
||||
commands.append(['chmod', '+w', new_path])
|
||||
LOGGER.info("Copying library '%s'", path)
|
||||
commands.append(args)
|
||||
args = ['chmod', 'u+w', new_path]
|
||||
commands.append(args)
|
||||
return new_path
|
||||
|
||||
|
||||
@@ -318,9 +317,8 @@ def CopyPlugin(path, subdir):
|
||||
#args = ['cp', path, new_path]
|
||||
args = ['ditto', '--arch=i386', '--arch=x86_64', path, new_path]
|
||||
commands.append(args)
|
||||
commands.append(['chmod', '+w', new_path])
|
||||
LOGGER.info("Copying plugin '%s'", path)
|
||||
args = ['chmod', 'u+w', new_path]
|
||||
commands.append(args)
|
||||
return new_path
|
||||
|
||||
def CopyFramework(path):
|
||||
@@ -351,6 +349,7 @@ def CopyFramework(src_binary):
|
||||
|
||||
commands.append(['mkdir', '-p', dest_dir])
|
||||
commands.append(['cp', src_binary, dest_binary])
|
||||
commands.append(['chmod', '+w', dest_binary])
|
||||
|
||||
# Copy special files from various places:
|
||||
# QtCore has Resources/qt_menu.nib (copy to app's Resources)
|
||||
|
||||
2
dist/man/strawberry.1
vendored
2
dist/man/strawberry.1
vendored
@@ -27,7 +27,7 @@ Features:
|
||||
.br
|
||||
- Album cover art from Lastfm, Musicbrainz, Discogs, Deezer and Tidal
|
||||
.br
|
||||
- Song lyrics from AudD, lyrics.ovh and lololyrics.com
|
||||
- Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
|
||||
.br
|
||||
- Support for multiple backends
|
||||
.br
|
||||
|
||||
4
dist/rpm/strawberry.spec.in
vendored
4
dist/rpm/strawberry.spec.in
vendored
@@ -44,7 +44,9 @@ BuildRequires: pkgconfig(dbus-1)
|
||||
BuildRequires: pkgconfig(gnutls)
|
||||
BuildRequires: pkgconfig(alsa)
|
||||
BuildRequires: pkgconfig(protobuf)
|
||||
%if ! 0%{?centos}
|
||||
BuildRequires: pkgconfig(sqlite3) >= 3.9
|
||||
%endif
|
||||
%if ! 0%{?centos} && ! 0%{?mageia}
|
||||
BuildRequires: pkgconfig(taglib)
|
||||
%endif
|
||||
@@ -103,7 +105,7 @@ Features:
|
||||
- Edit tags on music files
|
||||
- Fetch tags from MusicBrainz
|
||||
- Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal
|
||||
- Song lyrics from AudD, lyrics.ovh and lololyrics.com
|
||||
- Song lyrics from AudD, ChartLyrics, lyrics.ovh and lololyrics.com
|
||||
- Support for multiple backends
|
||||
- Audio analyzer
|
||||
- Audio equalizer
|
||||
|
||||
41
dist/windows/strawberry.nsi.in
vendored
41
dist/windows/strawberry.nsi.in
vendored
@@ -68,13 +68,12 @@ SetCompressor /SOLID lzma
|
||||
!include "MUI2.nsh"
|
||||
!include "FileAssociation.nsh"
|
||||
!include "Capabilities.nsh"
|
||||
!include LogicLib.nsh
|
||||
!include x64.nsh
|
||||
|
||||
!define MUI_ICON "strawberry.ico"
|
||||
|
||||
!define MUI_COMPONENTSPAGE_SMALLDESC
|
||||
;!define MUI_FINISHPAGE_RUN
|
||||
;!define MUI_FINISHPAGE_RUN_TEXT "Run Strawberry"
|
||||
;!define MUI_FINISHPAGE_RUN_FUNCTION "RunStrawberry"
|
||||
|
||||
; Installer pages
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
@@ -144,16 +143,14 @@ Function .onInit
|
||||
|
||||
FunctionEnd
|
||||
|
||||
;Function RunStrawberry
|
||||
;ShellExecAsUser::ShellExecAsUser "" "$INSTDIR/strawberry.exe" ""
|
||||
;FunctionEnd
|
||||
|
||||
Section "Delete old files" oldfiles
|
||||
SectionEnd
|
||||
|
||||
Section "Strawberry" Strawberry
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
|
||||
|
||||
File "strawberry.exe"
|
||||
File "strawberry-tagreader.exe"
|
||||
File "strawberry.ico"
|
||||
@@ -199,7 +196,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-21.dll"
|
||||
File "libprotobuf-22.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
@@ -243,6 +240,8 @@ Section "Strawberry" Strawberry
|
||||
File "libxine-2.dll"
|
||||
!endif
|
||||
|
||||
File "killproc.exe"
|
||||
|
||||
; Register Strawberry with Default Programs
|
||||
Var /GLOBAL AppIcon
|
||||
Var /GLOBAL AppExe
|
||||
@@ -406,28 +405,8 @@ Section "Uninstaller"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
; Kill strawberry.exe if it's running
|
||||
; This calling convention is retarded...
|
||||
;StrCpy $0 "strawberry.exe"
|
||||
;KillProc::FindProcesses
|
||||
;StrCmp $1 "-1" wooops
|
||||
|
||||
;StrCmp $0 "0" completed
|
||||
|
||||
;DetailPrint "Killing running strawberry.exe..."
|
||||
|
||||
;StrCpy $0 "strawberry.exe"
|
||||
;KillProc::KillProcesses
|
||||
;StrCmp $1 "-1" wooops
|
||||
|
||||
;Sleep 2000
|
||||
;Goto completed
|
||||
|
||||
;wooops:
|
||||
;DetailPrint "-> Error: Something went wrong while killing running strawberry.exe"
|
||||
;Abort
|
||||
|
||||
;completed:
|
||||
nsExec::Exec '"$INSTDIR\killproc.exe" strawberry.exe'
|
||||
|
||||
; Delete all the files
|
||||
|
||||
@@ -476,7 +455,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-21.dll"
|
||||
Delete "$INSTDIR\libprotobuf-22.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
@@ -602,12 +581,14 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\xine-plugins\xineplug_post_visualizations.dll"
|
||||
!endif
|
||||
|
||||
Delete "$INSTDIR\killproc.exe"
|
||||
Delete "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Remove the installation folders.
|
||||
RMDir "$INSTDIR\platforms"
|
||||
RMDir "$INSTDIR\sqldrivers"
|
||||
RMDir "$INSTDIR\imageformats"
|
||||
RMDir "$INSTDIR\gio-modules"
|
||||
RMDir "$INSTDIR\gstreamer-plugins"
|
||||
RMDir "$INSTDIR\xine-plugins"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
@@ -2,10 +2,10 @@ cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
@@ -5,10 +5,10 @@ include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
|
||||
endif()
|
||||
|
||||
set(SOURCES
|
||||
|
||||
@@ -159,7 +159,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
||||
next_worker_(0),
|
||||
next_id_(0) {
|
||||
|
||||
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2);
|
||||
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 4);
|
||||
local_server_name_ = qApp->applicationName().toLower();
|
||||
|
||||
if (local_server_name_.isEmpty())
|
||||
|
||||
@@ -7,10 +7,10 @@ include_directories(${CMAKE_BINARY_DIR}/src)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
|
||||
endif()
|
||||
|
||||
set(MESSAGES
|
||||
|
||||
@@ -290,7 +290,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
|
||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
||||
for (int i = 0; i < map["COMM"].size(); ++i) {
|
||||
for (uint i = 0; i < map["COMM"].size(); ++i) {
|
||||
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
||||
|
||||
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
||||
@@ -300,7 +300,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
||||
}
|
||||
|
||||
// Parse FMPS frames
|
||||
for (int i = 0; i < map["TXXX"].size(); ++i) {
|
||||
for (uint i = 0; i < map["TXXX"].size(); ++i) {
|
||||
const TagLib::ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i]);
|
||||
|
||||
if (frame && frame->description().startsWith("FMPS_")) {
|
||||
@@ -836,7 +836,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
|
||||
TagLib::ByteVector data = it->second.binaryData();
|
||||
|
||||
int pos = data.find('\0') + 1;
|
||||
if ((pos > 0) && (pos < data.size())) {
|
||||
if ((pos > 0) && ((uint)pos < data.size())) {
|
||||
ret = QByteArray(data.data() + pos, data.size() - pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ include_directories(${CMAKE_BINARY_DIR}/src)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
|
||||
endif()
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: strawberry
|
||||
version: '0.6.6+git'
|
||||
version: '0.6.7+git'
|
||||
summary: music player and collection organizer
|
||||
description: |
|
||||
Strawberry is a music player and collection organizer.
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Woverloaded-virtual -Wno-sign-compare -fpermissive")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11 -U__STRICT_ANSI__ -Wall -Wextra -Wpedantic -Woverloaded-virtual -fpermissive")
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wpedantic")
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
|
||||
endif()
|
||||
|
||||
option(BUILD_WERROR "Build with -Werror" OFF)
|
||||
@@ -44,12 +44,11 @@ add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
|
||||
add_definitions(-DBOOST_BIND_NO_PLACEHOLDERS)
|
||||
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
include_directories(${GLIB_INCLUDE_DIRS})
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
|
||||
include_directories(${GLIB_INCLUDE_DIRS})
|
||||
include_directories(${GOBJECT_INCLUDE_DIRS})
|
||||
include_directories(${GNUTLS_INCLUDE_DIR})
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
|
||||
|
||||
if (X11_FOUND)
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
@@ -58,16 +57,31 @@ endif(X11_FOUND)
|
||||
if(HAVE_GSTREAMER)
|
||||
link_directories(${GSTREAMER_LIBRARY_DIRS})
|
||||
include_directories(${GSTREAMER_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
|
||||
link_directories(${GSTREAMER_BASE_LIBRARY_DIRS})
|
||||
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
|
||||
link_directories(${GSTREAMER_APP_LIBRARY_DIRS})
|
||||
include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
|
||||
link_directories(${GSTREAMER_AUDIO_LIBRARY_DIRS})
|
||||
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
|
||||
link_directories(${GSTREAMER_TAG_LIBRARY_DIRS})
|
||||
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
|
||||
link_directories(${GSTREAMER_PBUTILS_LIBRARY_DIRS})
|
||||
include_directories(${GSTREAMER_PBUTILS_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif(HAVE_GSTREAMER)
|
||||
|
||||
if(HAVE_PHONON)
|
||||
include_directories(${PHONON_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif(HAVE_PHONON)
|
||||
|
||||
if(HAVE_CHROMAPRINT)
|
||||
link_directories(${CHROMAPRINT_LIBRARY_DIRS})
|
||||
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
|
||||
endif(HAVE_CHROMAPRINT)
|
||||
|
||||
if(HAVE_AUDIOCD)
|
||||
link_directories(${LIBCDIO_LIBRARY_DIRS})
|
||||
include_directories(${LIBCDIO_INCLUDE_DIRS})
|
||||
endif(HAVE_AUDIOCD)
|
||||
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
include_directories(${TAGLIB_INCLUDE_DIRS})
|
||||
@@ -77,6 +91,11 @@ link_directories(${SINGLECOREAPPLICATION_LIBRARY_DIRS})
|
||||
include_directories(${SINGLEAPPLICATION_INCLUDE_DIRS})
|
||||
include_directories(${SINGLECOREAPPLICATION_INCLUDE_DIRS})
|
||||
|
||||
if(HAVE_LIBMTP)
|
||||
link_directories(${LIBMTP_LIBRARY_DIRS})
|
||||
include_directories(${LIBMTP_INCLUDE_DIRS})
|
||||
endif(HAVE_LIBMTP)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-common)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader)
|
||||
include_directories(${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader)
|
||||
@@ -216,6 +235,7 @@ set(SOURCES
|
||||
lyrics/auddlyricsprovider.cpp
|
||||
lyrics/ovhlyricsprovider.cpp
|
||||
lyrics/lololyricsprovider.cpp
|
||||
lyrics/chartlyricsprovider.cpp
|
||||
|
||||
settings/settingsdialog.cpp
|
||||
settings/settingspage.cpp
|
||||
@@ -400,6 +420,7 @@ set(HEADERS
|
||||
lyrics/auddlyricsprovider.h
|
||||
lyrics/ovhlyricsprovider.h
|
||||
lyrics/lololyricsprovider.h
|
||||
lyrics/chartlyricsprovider.h
|
||||
|
||||
settings/settingsdialog.h
|
||||
settings/settingspage.h
|
||||
@@ -1069,7 +1090,7 @@ if(HAVE_GIO)
|
||||
endif(HAVE_GIO)
|
||||
|
||||
if(HAVE_AUDIOCD)
|
||||
target_link_libraries(strawberry_lib ${CDIO_LIBRARIES})
|
||||
target_link_libraries(strawberry_lib ${LIBCDIO_LIBRARIES})
|
||||
endif(HAVE_AUDIOCD)
|
||||
|
||||
if(HAVE_IMOBILEDEVICE)
|
||||
|
||||
@@ -72,7 +72,7 @@ void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
|
||||
void Analyzer::Base::transform(Scope& scope) {
|
||||
|
||||
QVector<float> aux(fht_->size());
|
||||
if (aux.size() >= scope.size()) {
|
||||
if ((long unsigned int)aux.size() >= scope.size()) {
|
||||
std::copy(scope.begin(), scope.end(), aux.begin());
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -59,7 +59,6 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
context_menu_framerate_(new QMenu(tr("Framerate"), this)),
|
||||
group_(new QActionGroup(this)),
|
||||
group_framerate_(new QActionGroup(this)),
|
||||
visualisation_action_(nullptr),
|
||||
double_click_timer_(new QTimer(this)),
|
||||
ignore_next_click_(false),
|
||||
current_analyzer_(nullptr),
|
||||
@@ -88,7 +87,6 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
group_->addAction(disable_action_);
|
||||
|
||||
context_menu_->addSeparator();
|
||||
// Visualisation action gets added in SetActions
|
||||
|
||||
double_click_timer_->setSingleShot(true);
|
||||
double_click_timer_->setInterval(250);
|
||||
@@ -98,26 +96,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SetActions(QAction *visualisation) {
|
||||
visualisation_action_ = visualisation;
|
||||
context_menu_->addAction(visualisation_action_);
|
||||
}
|
||||
|
||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
if (engine_->type() != Engine::EngineType::GStreamer && engine_->type() != Engine::EngineType::Xine) return;
|
||||
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
if (ignore_next_click_) {
|
||||
ignore_next_click_ = false;
|
||||
}
|
||||
else {
|
||||
// Might be the first click in a double click, so wait a while before actually doing anything
|
||||
double_click_timer_->start();
|
||||
last_click_pos_ = e->globalPos();
|
||||
}
|
||||
}
|
||||
else if (e->button() == Qt::RightButton) {
|
||||
if (e->button() == Qt::RightButton) {
|
||||
context_menu_->popup(e->globalPos());
|
||||
}
|
||||
|
||||
@@ -127,16 +110,6 @@ void AnalyzerContainer::ShowPopupMenu() {
|
||||
context_menu_->popup(last_click_pos_);
|
||||
}
|
||||
|
||||
void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent*) {
|
||||
|
||||
if (engine_->type() != Engine::EngineType::GStreamer && engine_->type() != Engine::EngineType::Xine) return;
|
||||
|
||||
double_click_timer_->stop();
|
||||
ignore_next_click_ = true;
|
||||
|
||||
if (visualisation_action_) visualisation_action_->trigger();
|
||||
}
|
||||
|
||||
void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
|
||||
emit WheelEvent(e->delta());
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ signals:
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QMouseEvent*);
|
||||
void mouseDoubleClickEvent(QMouseEvent*);
|
||||
void wheelEvent(QWheelEvent *e);
|
||||
|
||||
private slots:
|
||||
@@ -93,7 +92,6 @@ signals:
|
||||
QList<QAction*> actions_;
|
||||
QAction *disable_action_;
|
||||
|
||||
QAction *visualisation_action_;
|
||||
QTimer *double_click_timer_;
|
||||
QPoint last_click_pos_;
|
||||
bool ignore_next_click_;
|
||||
|
||||
@@ -196,7 +196,7 @@ void BlockAnalyzer::analyze(QPainter &p, const Analyzer::Scope &s, bool new_fram
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(), 0, y * (kHeight + 1), bar()->width(), bar()->height());
|
||||
}
|
||||
|
||||
for (uint x = 0; x < store_.size(); ++x)
|
||||
for (int x = 0; x < store_.size(); ++x)
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), static_cast<int>(store_[x]) * (kHeight + 1) + y_, topbarpixmap_);
|
||||
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -284,12 +285,6 @@ void CollectionBackend::AddDirectory(const QString &path) {
|
||||
QString canonical_path = QFileInfo(path).canonicalFilePath();
|
||||
QString db_path = canonical_path;
|
||||
|
||||
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(canonical_path))) {
|
||||
|
||||
db_path = Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(db_path)).toLocalFile();
|
||||
qLog(Debug) << "db_path" << db_path;
|
||||
}
|
||||
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
@@ -350,7 +345,7 @@ SongList CollectionBackend::FindSongsInDirectory(int id) {
|
||||
|
||||
SongList ret;
|
||||
while (q.next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(q, true);
|
||||
ret << song;
|
||||
}
|
||||
@@ -698,7 +693,7 @@ SongList CollectionBackend::ExecCollectionQuery(CollectionQuery *query) {
|
||||
|
||||
SongList ret;
|
||||
while (query->Next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(*query, true);
|
||||
ret << song;
|
||||
}
|
||||
@@ -772,7 +767,7 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &d
|
||||
|
||||
SongList ret;
|
||||
while (q.next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(q, true);
|
||||
ret << song;
|
||||
}
|
||||
@@ -780,36 +775,52 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &d
|
||||
|
||||
}
|
||||
|
||||
Song CollectionBackend::GetSongByUrl(const QUrl &url, qint64 beginning) {
|
||||
Song CollectionBackend::GetSongByUrl(const QUrl &url, const qint64 beginning) {
|
||||
|
||||
CollectionQuery query;
|
||||
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
query.AddWhere("url", url.toString());
|
||||
query.AddWhere("beginning", beginning);
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
Song song;
|
||||
if (ExecQuery(&query) && query.Next()) {
|
||||
song.InitFromQuery(query, true);
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND beginning = :beginning AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
q.bindValue(":url1", url);
|
||||
q.bindValue(":url2", url.toString());
|
||||
q.bindValue(":url3", url.toString(QUrl::FullyEncoded));
|
||||
q.bindValue(":url4", url.toEncoded());
|
||||
q.bindValue(":beginning", beginning);
|
||||
|
||||
Song song(source_);
|
||||
if (q.exec() && q.next()) {
|
||||
song.InitFromQuery(q, true);
|
||||
}
|
||||
|
||||
return song;
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::GetSongsByUrl(const QUrl &url) {
|
||||
|
||||
CollectionQuery query;
|
||||
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
||||
query.AddWhere("url", url.toString());
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
SongList songlist;
|
||||
if (ExecQuery(&query)) {
|
||||
while (query.Next()) {
|
||||
Song song;
|
||||
song.InitFromQuery(query, true);
|
||||
songlist << song;
|
||||
QSqlQuery q(db);
|
||||
q.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
q.bindValue(":url1", url);
|
||||
q.bindValue(":url2", url.toString());
|
||||
q.bindValue(":url3", url.toString(QUrl::FullyEncoded));
|
||||
q.bindValue(":url4", url.toEncoded());
|
||||
|
||||
SongList songs;
|
||||
if (q.exec()) {
|
||||
while (q.next()) {
|
||||
Song song(source_);
|
||||
song.InitFromQuery(q, true);
|
||||
songs << song;
|
||||
}
|
||||
}
|
||||
return songlist;
|
||||
|
||||
return songs;
|
||||
|
||||
}
|
||||
|
||||
@@ -856,7 +867,7 @@ SongList CollectionBackend::GetSongsBySongId(const QStringList &song_ids, QSqlDa
|
||||
|
||||
SongList ret;
|
||||
while (q.next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(q, true);
|
||||
ret << song;
|
||||
}
|
||||
@@ -880,7 +891,7 @@ SongList CollectionBackend::GetCompilationSongs(const QString &album, const Quer
|
||||
|
||||
SongList ret;
|
||||
while (query.Next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(query, true);
|
||||
ret << song;
|
||||
}
|
||||
@@ -911,23 +922,22 @@ void CollectionBackend::UpdateCompilations() {
|
||||
if (album.isEmpty()) continue;
|
||||
|
||||
// Find the directory the song is in
|
||||
QString directory = url.toString(QUrl::PreferLocalFile|QUrl::RemoveFilename|QUrl::StripTrailingSlash);
|
||||
QString directory = url.toString(QUrl::PreferLocalFile|QUrl::RemoveFilename);
|
||||
|
||||
CompilationInfo &info = compilation_info[directory + album];
|
||||
info.urls << url;
|
||||
info.directory = directory;
|
||||
info.album = album;
|
||||
info.artists.insert(artist);
|
||||
if (!info.artists.contains(artist))
|
||||
info.artists << artist;
|
||||
if (compilation_detected) info.has_compilation_detected++;
|
||||
else info.has_not_compilation_detected++;
|
||||
}
|
||||
|
||||
// Now mark the songs that we think are in compilations
|
||||
QSqlQuery find_songs(db);
|
||||
find_songs.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE url = :url AND compilation_detected = :compilation_detected AND unavailable = 0").arg(songs_table_));
|
||||
QSqlQuery find_song(db);
|
||||
find_song.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
QSqlQuery update_songs(db);
|
||||
update_songs.prepare(QString("UPDATE %1 SET compilation_detected = :compilation_detected, compilation_effective = ((compilation OR :compilation_detected OR compilation_on) AND NOT compilation_off) + 0 WHERE url = :url AND unavailable = 0").arg(songs_table_));
|
||||
QSqlQuery update_song(db);
|
||||
update_song.prepare(QString("UPDATE %1 SET compilation_detected = :compilation_detected, compilation_effective = ((compilation OR :compilation_detected OR compilation_on) AND NOT compilation_off) + 0 WHERE (url = :url1 OR url = :url2 OR url = :url3 OR url = :url4) AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
SongList deleted_songs;
|
||||
SongList added_songs;
|
||||
@@ -938,16 +948,16 @@ void CollectionBackend::UpdateCompilations() {
|
||||
for (; it != compilation_info.constEnd(); ++it) {
|
||||
const CompilationInfo &info = it.value();
|
||||
|
||||
// If there were more 'effective album artists' than there were directories for this album then it's a compilation.
|
||||
// If there were more than one 'effective album artist' for this album directory, then it's a compilation.
|
||||
|
||||
for (const QUrl &url : info.urls) {
|
||||
if (info.artists.count() > 1) { // This directory+album is a compilation.
|
||||
if (info.has_not_compilation_detected > 0) // Run updates if any of the songs is not marked as compilations.
|
||||
UpdateCompilations(find_songs, update_songs, deleted_songs, added_songs, url, true);
|
||||
UpdateCompilations(find_song, update_song, deleted_songs, added_songs, url, true);
|
||||
}
|
||||
else {
|
||||
if (info.has_compilation_detected > 0)
|
||||
UpdateCompilations(find_songs, update_songs, deleted_songs, added_songs, url, false);
|
||||
UpdateCompilations(find_song, update_song, deleted_songs, added_songs, url, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -961,25 +971,32 @@ void CollectionBackend::UpdateCompilations() {
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected) {
|
||||
void CollectionBackend::UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected) {
|
||||
|
||||
// Get song, so we can tell the model its updated
|
||||
find_songs.bindValue(":url", url.toString());
|
||||
find_songs.bindValue(":compilation_detected", int(!compilation_detected));
|
||||
find_songs.exec();
|
||||
while (find_songs.next()) {
|
||||
Song song;
|
||||
song.InitFromQuery(find_songs, true);
|
||||
deleted_songs << song;
|
||||
song.set_compilation_detected(compilation_detected);
|
||||
added_songs << song;
|
||||
find_song.bindValue(":url1", url);
|
||||
find_song.bindValue(":url2", url.toString());
|
||||
find_song.bindValue(":url3", url.toString(QUrl::FullyEncoded));
|
||||
find_song.bindValue(":url4", url.toEncoded());
|
||||
|
||||
if (find_song.exec()) {
|
||||
while (find_song.next()) {
|
||||
Song song(source_);
|
||||
song.InitFromQuery(find_song, true);
|
||||
deleted_songs << song;
|
||||
song.set_compilation_detected(compilation_detected);
|
||||
added_songs << song;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the song
|
||||
update_songs.bindValue(":compilation_detected", int(compilation_detected));
|
||||
update_songs.bindValue(":url", url);
|
||||
update_songs.exec();
|
||||
db_->CheckErrors(update_songs);
|
||||
update_song.bindValue(":compilation_detected", int(compilation_detected));
|
||||
update_song.bindValue(":url1", url);
|
||||
update_song.bindValue(":url2", url.toString());
|
||||
update_song.bindValue(":url3", url.toString(QUrl::FullyEncoded));
|
||||
update_song.bindValue(":url4", url.toEncoded());
|
||||
update_song.exec();
|
||||
db_->CheckErrors(update_song);
|
||||
|
||||
}
|
||||
|
||||
@@ -1021,7 +1038,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
|
||||
info.art_automatic = QUrl::fromEncoded(art_automatic.toUtf8());
|
||||
}
|
||||
else {
|
||||
info.art_automatic = QUrl::fromLocalFile(art_automatic.toUtf8());
|
||||
info.art_automatic = QUrl::fromLocalFile(art_automatic);
|
||||
}
|
||||
|
||||
QString art_manual = query.Value(6).toString();
|
||||
@@ -1029,7 +1046,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbums(const QString &artist,
|
||||
info.art_manual = QUrl::fromEncoded(art_manual.toUtf8());
|
||||
}
|
||||
else {
|
||||
info.art_manual = QUrl::fromLocalFile(art_manual.toUtf8());
|
||||
info.art_manual = QUrl::fromLocalFile(art_manual);
|
||||
}
|
||||
|
||||
if ((info.artist == last_artist || info.album_artist == last_album_artist) && info.album_name == last_album)
|
||||
@@ -1067,8 +1084,8 @@ CollectionBackend::Album CollectionBackend::GetAlbumArt(const QString &artist, c
|
||||
if (!ExecQuery(&query)) return ret;
|
||||
|
||||
if (query.Next()) {
|
||||
ret.art_automatic = query.Value(0).toUrl();
|
||||
ret.art_manual = query.Value(1).toUrl();
|
||||
ret.art_automatic = QUrl::fromEncoded(query.Value(0).toByteArray());
|
||||
ret.art_manual = QUrl::fromEncoded(query.Value(1).toByteArray());
|
||||
ret.first_url = QUrl::fromEncoded(query.Value(2).toByteArray());
|
||||
}
|
||||
|
||||
@@ -1103,7 +1120,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
|
||||
|
||||
SongList deleted_songs;
|
||||
while (query.Next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(query, true);
|
||||
deleted_songs << song;
|
||||
}
|
||||
@@ -1111,7 +1128,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
|
||||
// Update the songs
|
||||
QString sql(QString("UPDATE %1 SET art_manual = :cover WHERE album = :album AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
if (!albumartist.isNull() && !albumartist.isEmpty()) {
|
||||
if (!albumartist.isEmpty()) {
|
||||
sql += " AND albumartist = :albumartist";
|
||||
}
|
||||
else if (!artist.isNull()) {
|
||||
@@ -1120,7 +1137,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
|
||||
|
||||
QSqlQuery q(db);
|
||||
q.prepare(sql);
|
||||
q.bindValue(":cover", cover_url);
|
||||
q.bindValue(":cover", cover_url.toString(QUrl::FullyEncoded));
|
||||
q.bindValue(":album", album);
|
||||
if (!albumartist.isEmpty()) {
|
||||
q.bindValue(":albumartist", albumartist);
|
||||
@@ -1137,7 +1154,7 @@ void CollectionBackend::UpdateManualAlbumArt(const QString &artist, const QStrin
|
||||
|
||||
SongList added_songs;
|
||||
while (query.Next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(query, true);
|
||||
added_songs << song;
|
||||
}
|
||||
@@ -1165,7 +1182,7 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
|
||||
if (!ExecQuery(&query)) return;
|
||||
|
||||
while (query.Next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(query, true);
|
||||
deleted_songs << song;
|
||||
}
|
||||
@@ -1188,7 +1205,7 @@ void CollectionBackend::ForceCompilation(const QString &album, const QList<QStri
|
||||
if (!ExecQuery(&query)) return;
|
||||
|
||||
while (query.Next()) {
|
||||
Song song;
|
||||
Song song(source_);
|
||||
song.InitFromQuery(query, true);
|
||||
added_songs << song;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -109,7 +110,7 @@ class CollectionBackendInterface : public QObject {
|
||||
virtual SongList GetSongsByUrl(const QUrl &url) = 0;
|
||||
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
|
||||
// Using default beginning value is suitable when searching for single-section songs.
|
||||
virtual Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) = 0;
|
||||
virtual Song GetSongByUrl(const QUrl &url, const qint64 beginning = 0) = 0;
|
||||
|
||||
virtual void AddDirectory(const QString &path) = 0;
|
||||
virtual void RemoveDirectory(const Directory &dir) = 0;
|
||||
@@ -227,16 +228,14 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
struct CompilationInfo {
|
||||
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
|
||||
|
||||
QString directory;
|
||||
QString album;
|
||||
QList<QUrl> urls;
|
||||
QSet<QString> artists;
|
||||
QStringList artists;
|
||||
|
||||
int has_compilation_detected;
|
||||
int has_not_compilation_detected;
|
||||
};
|
||||
|
||||
void UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
||||
void UpdateCompilations(QSqlQuery &find_song, QSqlQuery &update_song, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
||||
AlbumList GetAlbums(const QString &artist, const QString &album_artist, bool compilation = false, const QueryOptions &opt = QueryOptions());
|
||||
AlbumList GetAlbums(const QString &artist, bool compilation, const QueryOptions &opt = QueryOptions());
|
||||
SubdirectoryList SubdirsInDirectory(int id, QSqlDatabase &db);
|
||||
|
||||
@@ -53,13 +53,7 @@ CollectionDirectoryModel::~CollectionDirectoryModel() {}
|
||||
|
||||
void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
|
||||
|
||||
QStandardItem *item;
|
||||
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(dir.path))) {
|
||||
item = new QStandardItem(Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(dir.path)).toLocalFile());
|
||||
}
|
||||
else {
|
||||
item = new QStandardItem(dir.path);
|
||||
}
|
||||
QStandardItem *item = new QStandardItem(dir.path);
|
||||
item->setData(dir.id, kIdRole);
|
||||
item->setIcon(dir_icon_);
|
||||
storage_ << std::shared_ptr<MusicStorage>(new FilesystemMusicStorage(dir.path));
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QChar>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
@@ -70,7 +71,7 @@ using std::placeholders::_2;
|
||||
|
||||
const char *CollectionModel::kSavedGroupingsSettingsGroup = "SavedGroupings";
|
||||
const int CollectionModel::kPrettyCoverSize = 32;
|
||||
const qint64 CollectionModel::kIconCacheSize = 100000000; //~100MB
|
||||
const int CollectionModel::kPixmapCacheLimit = QPixmapCache::cacheLimit() * 8;
|
||||
|
||||
static bool IsArtistGroupBy(const CollectionModel::GroupBy by) {
|
||||
return by == CollectionModel::GroupBy_Artist || by == CollectionModel::GroupBy_AlbumArtist;
|
||||
@@ -126,7 +127,7 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
|
||||
backend_->UpdateTotalArtistCountAsync();
|
||||
backend_->UpdateTotalAlbumCountAsync();
|
||||
|
||||
QPixmapCache::setCacheLimit(61440);
|
||||
QPixmapCache::setCacheLimit(kPixmapCacheLimit);
|
||||
|
||||
}
|
||||
|
||||
@@ -434,25 +435,11 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
|
||||
|
||||
if (node->parent != root_) parents << node->parent;
|
||||
|
||||
// Remove from pixmap cache
|
||||
QModelIndex idx = ItemToIndex(node->parent);
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(idx);
|
||||
QPixmapCache::remove(cache_key);
|
||||
if (pending_cache_keys_.contains(cache_key)) pending_cache_keys_.remove(cache_key);
|
||||
|
||||
// Remove from pending art loading
|
||||
QMapIterator<quint64, ItemAndCacheKey> i(pending_art_);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value().first == node) {
|
||||
pending_art_.remove(i.key());
|
||||
break;
|
||||
}
|
||||
}
|
||||
beginRemoveRows(idx, node->row, node->row);
|
||||
beginRemoveRows(ItemToIndex(node->parent), node->row, node->row);
|
||||
node->parent->Delete(node->row);
|
||||
song_nodes_.remove(song.id());
|
||||
endRemoveRows();
|
||||
|
||||
}
|
||||
else {
|
||||
// If we get here it means some of the songs we want to delete haven't been lazy-loaded yet.
|
||||
@@ -487,18 +474,20 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
|
||||
container_nodes_[node->container_level].remove(node->key);
|
||||
|
||||
// Remove from pixmap cache
|
||||
QModelIndex idx = ItemToIndex(node->parent);
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(idx);
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(node));
|
||||
QPixmapCache::remove(cache_key);
|
||||
if (pending_cache_keys_.contains(cache_key)) pending_cache_keys_.remove(cache_key);
|
||||
if (pending_cache_keys_.contains(cache_key)) {
|
||||
pending_cache_keys_.remove(cache_key);
|
||||
}
|
||||
|
||||
// Remove from pending art loading
|
||||
QMapIterator<quint64, ItemAndCacheKey> i(pending_art_);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
QMap<quint64, ItemAndCacheKey>::iterator i = pending_art_.begin();
|
||||
while (i != pending_art_.end()) {
|
||||
if (i.value().first == node) {
|
||||
pending_art_.remove(i.key());
|
||||
break;
|
||||
i = pending_art_.erase(i);
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,15 +528,7 @@ QString CollectionModel::AlbumIconPixmapCacheKey(const QModelIndex &idx) const {
|
||||
QStringList path;
|
||||
QModelIndex idx_copy(idx);
|
||||
while (idx_copy.isValid()) {
|
||||
//const CollectionItem *item = IndexToItem(idx_copy);
|
||||
//if (item && group_by_[item->container_level] == GroupBy_Album) {
|
||||
// QString album = idx_copy.data().toString();
|
||||
// album.remove(Song::kAlbumRemoveDisc);
|
||||
// path.prepend(album);
|
||||
//}
|
||||
//else {
|
||||
path.prepend(idx_copy.data().toString());
|
||||
//}
|
||||
path.prepend(idx_copy.data().toString());
|
||||
idx_copy = idx_copy.parent();
|
||||
}
|
||||
|
||||
@@ -859,7 +840,6 @@ void CollectionModel::BeginReset() {
|
||||
divider_nodes_.clear();
|
||||
pending_art_.clear();
|
||||
pending_cache_keys_.clear();
|
||||
QPixmapCache::clear();
|
||||
|
||||
root_ = new CollectionItem(this);
|
||||
root_->compilation_artist_node_ = nullptr;
|
||||
|
||||
@@ -70,7 +70,7 @@ class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
static const char *kSavedGroupingsSettingsGroup;
|
||||
|
||||
static const int kPrettyCoverSize;
|
||||
static const qint64 kIconCacheSize;
|
||||
static const int kPixmapCacheLimit;
|
||||
|
||||
enum Role {
|
||||
Role_Type = Qt::UserRole + 1,
|
||||
|
||||
@@ -62,18 +62,13 @@ using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
const int ContextAlbumsModel::kPrettyCoverSize = 32;
|
||||
const qint64 ContextAlbumsModel::kIconCacheSize = 100000000; //~100MB
|
||||
|
||||
ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent) :
|
||||
SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
|
||||
backend_(backend),
|
||||
app_(app),
|
||||
artist_icon_(IconLoader::Load("folder-sound")),
|
||||
album_icon_(IconLoader::Load("cdcase")),
|
||||
playlists_dir_icon_(IconLoader::Load("folder-sound")),
|
||||
playlist_icon_(IconLoader::Load("albums")),
|
||||
use_pretty_covers_(true)
|
||||
{
|
||||
playlists_dir_icon_(IconLoader::Load("folder-sound")) {
|
||||
|
||||
root_->lazy_loaded = true;
|
||||
|
||||
@@ -90,15 +85,6 @@ ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *
|
||||
|
||||
ContextAlbumsModel::~ContextAlbumsModel() { delete root_; }
|
||||
|
||||
void ContextAlbumsModel::set_pretty_covers(bool use_pretty_covers) {
|
||||
|
||||
if (use_pretty_covers != use_pretty_covers_) {
|
||||
use_pretty_covers_ = use_pretty_covers;
|
||||
Reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsModel::AddSongs(const SongList &songs) {
|
||||
|
||||
for (const Song &song : songs) {
|
||||
@@ -199,18 +185,8 @@ QVariant ContextAlbumsModel::data(const QModelIndex &index, int role) const {
|
||||
|
||||
const CollectionItem *item = IndexToItem(index);
|
||||
|
||||
// Handle a special case for returning album artwork instead of a generic CD icon.
|
||||
// This is here instead of in the other data() function to let us use the QModelIndex& version of GetChildSongs,
|
||||
// which satisfies const-ness, instead of the CollectionItem *version, which doesn't.
|
||||
if (use_pretty_covers_) {
|
||||
bool is_album_node = false;
|
||||
if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container) {
|
||||
is_album_node = (item->container_level == 0);
|
||||
}
|
||||
if (is_album_node) {
|
||||
// It has const behaviour some of the time - that's ok right?
|
||||
return const_cast<ContextAlbumsModel*>(this)->AlbumIcon(index);
|
||||
}
|
||||
if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container && item->container_level == 0) {
|
||||
return const_cast<ContextAlbumsModel*>(this)->AlbumIcon(index);
|
||||
}
|
||||
|
||||
return data(item, role);
|
||||
@@ -239,9 +215,6 @@ QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
|
||||
case Role_Type:
|
||||
return item->type;
|
||||
|
||||
case Role_IsDivider:
|
||||
return item->type == CollectionItem::Type_Divider;
|
||||
|
||||
case Role_ContainerType:
|
||||
return item->type;
|
||||
|
||||
@@ -265,7 +238,8 @@ QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -349,11 +323,19 @@ void ContextAlbumsModel::LazyPopulate(CollectionItem *parent, bool signal) {
|
||||
|
||||
void ContextAlbumsModel::Reset() {
|
||||
|
||||
QMap<QString, CollectionItem*>::iterator i = container_nodes_.begin();
|
||||
while (i != container_nodes_.end()) {
|
||||
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(i.value()));
|
||||
QPixmapCache::remove(cache_key);
|
||||
++i;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
delete root_;
|
||||
song_nodes_.clear();
|
||||
container_nodes_.clear();
|
||||
pending_art_.clear();
|
||||
pending_cache_keys_.clear();
|
||||
|
||||
root_ = new CollectionItem(this);
|
||||
root_->lazy_loaded = false;
|
||||
@@ -434,7 +416,6 @@ Qt::ItemFlags ContextAlbumsModel::flags(const QModelIndex &index) const {
|
||||
case CollectionItem::Type_Song:
|
||||
case CollectionItem::Type_Container:
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
|
||||
case CollectionItem::Type_Divider:
|
||||
case CollectionItem::Type_Root:
|
||||
case CollectionItem::Type_LoadingIndicator:
|
||||
default:
|
||||
|
||||
@@ -37,11 +37,9 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QMimeData>
|
||||
#include <QImage>
|
||||
#include <QIcon>
|
||||
#include <QPixmap>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QSettings>
|
||||
|
||||
#include "core/simpletreemodel.h"
|
||||
@@ -51,6 +49,8 @@
|
||||
#include "collection/sqlrow.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
|
||||
class QMimeData;
|
||||
|
||||
class Application;
|
||||
class CollectionBackend;
|
||||
class CollectionItem;
|
||||
@@ -63,7 +63,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
|
||||
~ContextAlbumsModel();
|
||||
|
||||
static const int kPrettyCoverSize;
|
||||
static const qint64 kIconCacheSize;
|
||||
|
||||
enum Role {
|
||||
Role_Type = Qt::UserRole + 1,
|
||||
@@ -71,7 +70,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
|
||||
Role_SortText,
|
||||
Role_Key,
|
||||
Role_Artist,
|
||||
Role_IsDivider,
|
||||
Role_Editable,
|
||||
LastRole
|
||||
};
|
||||
@@ -81,8 +79,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
|
||||
SqlRowList rows;
|
||||
};
|
||||
|
||||
CollectionBackend *backend() const { return backend_; }
|
||||
|
||||
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
|
||||
SongList GetChildSongs(const QModelIndex &index) const;
|
||||
SongList GetChildSongs(const QModelIndexList &indexes) const;
|
||||
@@ -93,9 +89,6 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const;
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
|
||||
void set_pretty_covers(bool use_pretty_covers);
|
||||
bool use_pretty_covers() const { return use_pretty_covers_; }
|
||||
|
||||
static QString TextOrUnknown(const QString &text);
|
||||
static QString SortText(QString text);
|
||||
static QString SortTextForArtist(QString artist);
|
||||
@@ -125,15 +118,11 @@ class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
|
||||
CollectionBackend *backend_;
|
||||
Application *app_;
|
||||
QueryOptions query_options_;
|
||||
QMap<int, CollectionItem*> song_nodes_;
|
||||
QMap<QString, CollectionItem*> container_nodes_;
|
||||
QIcon artist_icon_;
|
||||
QMap<int, CollectionItem*> song_nodes_;
|
||||
QIcon album_icon_;
|
||||
QPixmap no_cover_icon_;
|
||||
QIcon playlists_dir_icon_;
|
||||
QIcon playlist_icon_;
|
||||
QNetworkDiskCache *icon_cache_;
|
||||
bool use_pretty_covers_;
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
typedef QPair<CollectionItem*, QString> ItemAndCacheKey;
|
||||
QMap<quint64, ItemAndCacheKey> pending_art_;
|
||||
|
||||
@@ -82,76 +82,6 @@
|
||||
|
||||
ContextItemDelegate::ContextItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
|
||||
|
||||
void ContextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const {
|
||||
|
||||
const bool is_divider = index.data(ContextAlbumsModel::Role_IsDivider).toBool();
|
||||
|
||||
if (is_divider) {
|
||||
QString text(index.data().toString());
|
||||
|
||||
painter->save();
|
||||
|
||||
QRect text_rect(opt.rect);
|
||||
|
||||
// Does this item have an icon?
|
||||
QPixmap pixmap;
|
||||
QVariant decoration = index.data(Qt::DecorationRole);
|
||||
if (!decoration.isNull()) {
|
||||
if (decoration.canConvert<QPixmap>()) {
|
||||
pixmap = decoration.value<QPixmap>();
|
||||
}
|
||||
else if (decoration.canConvert<QIcon>()) {
|
||||
pixmap = decoration.value<QIcon>().pixmap(opt.decorationSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the icon at the left of the text rectangle
|
||||
if (!pixmap.isNull()) {
|
||||
QRect icon_rect(text_rect.topLeft(), opt.decorationSize);
|
||||
const int padding = (text_rect.height() - icon_rect.height()) / 2;
|
||||
icon_rect.adjust(padding, padding, padding, padding);
|
||||
text_rect.moveLeft(icon_rect.right() + padding + 6);
|
||||
|
||||
if (pixmap.size() != opt.decorationSize) {
|
||||
pixmap = pixmap.scaled(opt.decorationSize, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
painter->drawPixmap(icon_rect, pixmap);
|
||||
}
|
||||
else {
|
||||
text_rect.setLeft(text_rect.left() + 30);
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
QFont bold_font(opt.font);
|
||||
bold_font.setBold(true);
|
||||
|
||||
painter->setPen(opt.palette.color(QPalette::Text));
|
||||
painter->setFont(bold_font);
|
||||
painter->drawText(text_rect, text);
|
||||
|
||||
// Draw the line under the item
|
||||
QColor line_color = opt.palette.color(QPalette::Text);
|
||||
QLinearGradient grad_color(opt.rect.bottomLeft(), opt.rect.bottomRight());
|
||||
const double fade_start_end = (opt.rect.width()/3.0)/opt.rect.width();
|
||||
line_color.setAlphaF(0.0);
|
||||
grad_color.setColorAt(0, line_color);
|
||||
line_color.setAlphaF(0.5);
|
||||
grad_color.setColorAt(fade_start_end, line_color);
|
||||
grad_color.setColorAt(1.0 - fade_start_end, line_color);
|
||||
line_color.setAlphaF(0.0);
|
||||
grad_color.setColorAt(1, line_color);
|
||||
painter->setPen(QPen(grad_color, 1));
|
||||
painter->drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight());
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
else {
|
||||
if (!is_divider) QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ContextItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) {
|
||||
|
||||
return true;
|
||||
@@ -311,22 +241,7 @@ bool ContextAlbumsView::RestoreLevelFocus(const QModelIndex &parent) {
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::ReloadSettings() {
|
||||
|
||||
QSettings settings;
|
||||
|
||||
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
||||
SetAutoOpen(settings.value("auto_open", true).toBool());
|
||||
|
||||
if (app_ && model_) {
|
||||
model_->set_pretty_covers(settings.value("pretty_covers", true).toBool());
|
||||
}
|
||||
|
||||
settings.endGroup();
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::SetApplication(Application *app) {
|
||||
void ContextAlbumsView::Init(Application *app) {
|
||||
|
||||
app_ = app;
|
||||
|
||||
@@ -338,8 +253,6 @@ void ContextAlbumsView::SetApplication(Application *app) {
|
||||
connect(model_, SIGNAL(modelAboutToBeReset()), this, SLOT(SaveFocus()));
|
||||
connect(model_, SIGNAL(modelReset()), this, SLOT(RestoreFocus()));
|
||||
|
||||
ReloadSettings();
|
||||
|
||||
}
|
||||
|
||||
void ContextAlbumsView::paintEvent(QPaintEvent *event) {
|
||||
|
||||
@@ -55,7 +55,6 @@ class ContextItemDelegate : public QStyledItemDelegate {
|
||||
|
||||
public:
|
||||
ContextItemDelegate(QObject *parent);
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
public slots:
|
||||
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index);
|
||||
@@ -72,7 +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.
|
||||
SongList GetSelectedSongs() const;
|
||||
|
||||
void SetApplication(Application *app);
|
||||
void Init(Application *app);
|
||||
|
||||
// QTreeView
|
||||
void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
|
||||
@@ -80,14 +79,9 @@ class ContextAlbumsView : public AutoExpandingTreeView {
|
||||
ContextAlbumsModel *albums_model() { return model_; }
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
|
||||
void SaveFocus();
|
||||
void RestoreFocus();
|
||||
|
||||
signals:
|
||||
void ShowConfigDialog();
|
||||
|
||||
protected:
|
||||
// QWidget
|
||||
void paintEvent(QPaintEvent *event);
|
||||
@@ -122,7 +116,6 @@ signals:
|
||||
#ifndef Q_OS_WIN
|
||||
QAction *copy_to_device_;
|
||||
#endif
|
||||
QAction *delete_;
|
||||
QAction *edit_track_;
|
||||
QAction *edit_tracks_;
|
||||
QAction *show_in_browser_;
|
||||
|
||||
@@ -110,7 +110,7 @@ void ContextView::Init(Application *app, CollectionView *collectionview, AlbumCo
|
||||
collectionview_ = collectionview;
|
||||
album_cover_choice_controller_ = album_cover_choice_controller;
|
||||
|
||||
ui_->widget_play_albums->SetApplication(app_);
|
||||
ui_->widget_play_albums->Init(app_);
|
||||
lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this);
|
||||
|
||||
connect(collectionview_, SIGNAL(TotalSongCountUpdated_()), this, SLOT(UpdateNoSong()));
|
||||
@@ -498,7 +498,7 @@ void ContextView::UpdateSong(const Song &song) {
|
||||
|
||||
void ContextView::UpdateLyrics(const quint64 id, const QString &provider, const QString &lyrics) {
|
||||
|
||||
if (id != lyrics_id_) return;
|
||||
if ((qint64) id != lyrics_id_) return;
|
||||
lyrics_ = lyrics + "\n\n(Lyrics from " + provider + ")\n";
|
||||
lyrics_id_ = -1;
|
||||
if (action_show_lyrics_->isChecked()) {
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "lyrics/auddlyricsprovider.h"
|
||||
#include "lyrics/ovhlyricsprovider.h"
|
||||
#include "lyrics/lololyricsprovider.h"
|
||||
#include "lyrics/chartlyricsprovider.h"
|
||||
|
||||
#include "scrobbler/audioscrobbler.h"
|
||||
|
||||
@@ -85,8 +86,6 @@
|
||||
# include "moodbar/moodbarloader.h"
|
||||
#endif
|
||||
|
||||
bool Application::kIsPortable = false;
|
||||
|
||||
class ApplicationImpl {
|
||||
public:
|
||||
explicit ApplicationImpl(Application *app) :
|
||||
@@ -139,6 +138,7 @@ class ApplicationImpl {
|
||||
lyrics_providers->AddProvider(new AuddLyricsProvider(app));
|
||||
lyrics_providers->AddProvider(new OVHLyricsProvider(app));
|
||||
lyrics_providers->AddProvider(new LoloLyricsProvider(app));
|
||||
lyrics_providers->AddProvider(new ChartLyricsProvider(app));
|
||||
return lyrics_providers;
|
||||
}),
|
||||
internet_services_([=]() {
|
||||
|
||||
@@ -68,8 +68,6 @@ class Application : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static bool kIsPortable;
|
||||
|
||||
explicit Application(QObject *parent = nullptr);
|
||||
~Application();
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ inline void AddMetadata(const QString &key, const QDateTime &metadata, QVariantM
|
||||
if (metadata.isValid()) (*map)[key] = metadata;
|
||||
}
|
||||
|
||||
inline QString AsMPRISDateTimeType(uint time) {
|
||||
inline QString AsMPRISDateTimeType(const int time) {
|
||||
return time != -1 ? QDateTime::fromTime_t(time).toString(Qt::ISODate) : "";
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ Player::Player(Application *app, QObject *parent)
|
||||
last_pressed_previous_(QDateTime::currentDateTime()),
|
||||
continue_on_error_(false),
|
||||
greyout_(true),
|
||||
menu_previousmode_(PreviousBehaviour_DontRestart),
|
||||
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour_DontRestart),
|
||||
seek_step_sec_(10),
|
||||
volume_control_(true)
|
||||
{
|
||||
@@ -230,10 +230,10 @@ void Player::ReloadSettings() {
|
||||
s.beginGroup(PlaylistSettingsPage::kSettingsGroup);
|
||||
continue_on_error_ = s.value("continue_on_error", false).toBool();
|
||||
greyout_ = s.value("greyout_songs_play", true).toBool();
|
||||
menu_previousmode_ = PreviousBehaviour(s.value("menu_previousmode", PreviousBehaviour_DontRestart).toInt());
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup(BehaviourSettingsPage::kSettingsGroup);
|
||||
menu_previousmode_ = BehaviourSettingsPage::PreviousBehaviour(s.value("menu_previousmode", BehaviourSettingsPage::PreviousBehaviour_DontRestart).toInt());
|
||||
seek_step_sec_ = s.value("seek_step_sec", 10).toInt();
|
||||
s.endGroup();
|
||||
|
||||
@@ -503,7 +503,7 @@ void Player::StopAfterCurrent() {
|
||||
bool Player::PreviousWouldRestartTrack() const {
|
||||
|
||||
// Check if it has been over two seconds since previous button was pressed
|
||||
return menu_previousmode_ == PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
|
||||
return menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
|
||||
}
|
||||
|
||||
void Player::Previous() { PreviousItem(Engine::Manual); }
|
||||
@@ -512,7 +512,7 @@ void Player::PreviousItem(Engine::TrackChangeFlags change) {
|
||||
|
||||
const bool ignore_repeat_track = change & Engine::Manual;
|
||||
|
||||
if (menu_previousmode_ == PreviousBehaviour_Restart) {
|
||||
if (menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart) {
|
||||
// Check if it has been over two seconds since previous button was pressed
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
if (last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(now) >= 2) {
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "engine/gststartup.h"
|
||||
#endif
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "settings/behavioursettingspage.h"
|
||||
|
||||
class Application;
|
||||
class Song;
|
||||
@@ -136,12 +137,6 @@ class Player : public PlayerInterface {
|
||||
|
||||
static const char *kSettingsGroup;
|
||||
|
||||
// Don't change the values: they are saved in preferences
|
||||
enum PreviousBehaviour {
|
||||
PreviousBehaviour_DontRestart = 1,
|
||||
PreviousBehaviour_Restart = 2
|
||||
};
|
||||
|
||||
Engine::EngineType CreateEngine(Engine::EngineType enginetype);
|
||||
void Init();
|
||||
|
||||
@@ -240,7 +235,7 @@ class Player : public PlayerInterface {
|
||||
|
||||
bool continue_on_error_;
|
||||
bool greyout_;
|
||||
PreviousBehaviour menu_previousmode_;
|
||||
BehaviourSettingsPage::PreviousBehaviour menu_previousmode_;
|
||||
int seek_step_sec_;
|
||||
|
||||
bool volume_control_;
|
||||
|
||||
@@ -152,6 +152,7 @@ const QString Song::kEmbeddedCover = "(embedded)";
|
||||
const QRegExp Song::kAlbumRemoveDisc(" ?-? ((\\(|\\[)?)(Disc|CD) ?([0-9]{1,2})((\\)|\\])?)$");
|
||||
const QRegExp Song::kAlbumRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
|
||||
const QRegExp Song::kTitleRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|Live|Remastered Version|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$");
|
||||
const QString Song::kVariousArtists("various artists");
|
||||
|
||||
const QStringList Song::kArticles = QStringList() << "the " << "a " << "an ";
|
||||
|
||||
@@ -351,7 +352,7 @@ const QString &Song::cue_path() const { return d->cue_path_; }
|
||||
bool Song::has_cue() const { return !d->cue_path_.isEmpty(); }
|
||||
|
||||
bool Song::is_collection_song() const { return d->source_ == Source_Collection; }
|
||||
bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->album_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; }
|
||||
bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; }
|
||||
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Subsonic || d->source_ == Source_Qobuz; }
|
||||
bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
|
||||
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; }
|
||||
@@ -426,15 +427,7 @@ void Song::set_bitdepth(int v) { d->bitdepth_ = v; }
|
||||
|
||||
void Song::set_source(Source v) { d->source_ = v; }
|
||||
void Song::set_directory_id(int v) { d->directory_id_ = v; }
|
||||
void Song::set_url(const QUrl &v) {
|
||||
if (Application::kIsPortable) {
|
||||
QUrl base = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/");
|
||||
d->url_ = base.resolved(v);
|
||||
}
|
||||
else {
|
||||
d->url_ = v;
|
||||
}
|
||||
}
|
||||
void Song::set_url(const QUrl &v) { d->url_ = v; }
|
||||
void Song::set_basefilename(const QString &v) { d->basefilename_ = v; }
|
||||
void Song::set_filetype(FileType v) { d->filetype_ = v; }
|
||||
void Song::set_filesize(int v) { d->filesize_ = v; }
|
||||
@@ -949,7 +942,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
|
||||
set_art_automatic(QUrl::fromEncoded(art_automatic.toUtf8()));
|
||||
}
|
||||
else {
|
||||
set_art_automatic(QUrl::fromLocalFile(art_automatic.toUtf8()));
|
||||
set_art_automatic(QUrl::fromLocalFile(art_automatic));
|
||||
}
|
||||
}
|
||||
else if (Song::kColumns.value(i) == "art_manual") {
|
||||
@@ -958,7 +951,7 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
|
||||
set_art_manual(QUrl::fromEncoded(art_manual.toUtf8()));
|
||||
}
|
||||
else {
|
||||
set_art_manual(QUrl::fromLocalFile(art_manual.toUtf8()));
|
||||
set_art_manual(QUrl::fromLocalFile(art_manual));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1287,18 +1280,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
|
||||
|
||||
query->bindValue(":source", d->source_);
|
||||
query->bindValue(":directory_id", notnullintval(d->directory_id_));
|
||||
|
||||
QString url;
|
||||
if (d->url_.isValid()) {
|
||||
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(d->url_)) {
|
||||
url = Utilities::GetRelativePathToStrawberryBin(d->url_).toEncoded();
|
||||
}
|
||||
else {
|
||||
url = d->url_.toEncoded();
|
||||
}
|
||||
}
|
||||
query->bindValue(":url", url);
|
||||
|
||||
query->bindValue(":url", d->url_.toString(QUrl::FullyEncoded));
|
||||
query->bindValue(":filetype", d->filetype_);
|
||||
query->bindValue(":filesize", notnullintval(d->filesize_));
|
||||
query->bindValue(":mtime", notnullintval(d->mtime_));
|
||||
@@ -1314,8 +1296,8 @@ void Song::BindToQuery(QSqlQuery *query) const {
|
||||
query->bindValue(":compilation_off", d->compilation_off_ ? 1 : 0);
|
||||
query->bindValue(":compilation_effective", is_compilation() ? 1 : 0);
|
||||
|
||||
query->bindValue(":art_automatic", d->art_automatic_);
|
||||
query->bindValue(":art_manual", d->art_manual_);
|
||||
query->bindValue(":art_automatic", d->art_automatic_.toString(QUrl::FullyEncoded));
|
||||
query->bindValue(":art_manual", d->art_manual_.toString(QUrl::FullyEncoded));
|
||||
|
||||
query->bindValue(":effective_albumartist", this->effective_albumartist());
|
||||
query->bindValue(":effective_originalyear", intval(this->effective_originalyear()));
|
||||
|
||||
@@ -126,6 +126,8 @@ class Song {
|
||||
static const QRegExp kTitleRemoveMisc;
|
||||
static const QRegExp kFilenameRemoveNonFatChars;
|
||||
|
||||
static const QString kVariousArtists;
|
||||
|
||||
static const QStringList kArticles;
|
||||
|
||||
static QString JoinSpec(const QString &table);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -167,7 +168,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString &filename) {
|
||||
LoadLocalDirectory(filename);
|
||||
return Success;
|
||||
}
|
||||
Song song;
|
||||
Song song(Song::Source_LocalFile);
|
||||
song.InitFromFilePartial(filename);
|
||||
if (song.is_valid()) {
|
||||
songs_ << song;
|
||||
@@ -228,7 +229,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
|
||||
if (collection_->ExecQuery(&query) && query.Next()) {
|
||||
// We may have many results when the file has many sections
|
||||
do {
|
||||
Song song;
|
||||
Song song(Song::Source_Collection);
|
||||
song.InitFromQuery(query, true);
|
||||
|
||||
if (song.is_valid()) {
|
||||
@@ -289,7 +290,7 @@ SongLoader::Result SongLoader::LoadLocalAsync(const QString &filename) {
|
||||
}
|
||||
|
||||
// Assume it's just a normal file
|
||||
Song song;
|
||||
Song song(Song::Source_LocalFile);
|
||||
song.InitFromFilePartial(filename);
|
||||
if (song.is_valid()) {
|
||||
songs_ << song;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018-2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -46,7 +46,7 @@ TagReaderClient::TagReaderClient(QObject *parent) : QObject(parent), worker_pool
|
||||
original_thread_ = thread();
|
||||
|
||||
worker_pool_->SetExecutableName(kWorkerExecutableName);
|
||||
worker_pool_->SetWorkerCount(qBound(1, QThread::idealThreadCount() / 2, 2));
|
||||
worker_pool_->SetWorkerCount(qBound(1, QThread::idealThreadCount() / 2, 4));
|
||||
connect(worker_pool_, SIGNAL(WorkerFailedToStart()), SLOT(WorkerFailedToStart()));
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
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) {
|
||||
const QString UseCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
||||
return GetRandomString(len, UseCharacters);
|
||||
|
||||
@@ -96,6 +96,7 @@ void ConsumeCurrentElement(QXmlStreamReader *reader);
|
||||
// Advances the stream reader until it finds an element with the given name.
|
||||
// Returns false if the end of the document was reached before finding a matching element.
|
||||
bool ParseUntilElement(QXmlStreamReader *reader, const QString &name);
|
||||
bool ParseUntilElementCI(QXmlStreamReader *reader, const QString &name);
|
||||
|
||||
// Parses a string containing an RFC822 time and date.
|
||||
QDateTime ParseRFC822DateTime(const QString &text);
|
||||
@@ -123,7 +124,6 @@ QString FiddleFileExtension(const QString &filename, const QString &new_extensio
|
||||
QString GetEnv(const QString &key);
|
||||
void SetEnv(const char *key, const QString &value);
|
||||
void IncreaseFDLimit();
|
||||
void CheckPortable();
|
||||
|
||||
// Borrowed from schedutils
|
||||
enum IoPriority {
|
||||
|
||||
@@ -131,7 +131,7 @@ void AlbumCoverChoiceController::ReloadSettings() {
|
||||
}
|
||||
|
||||
QList<QAction*> AlbumCoverChoiceController::GetAllActions() {
|
||||
return QList<QAction*>() << cover_from_file_ << cover_to_file_ << separator_ << cover_from_url_ << search_for_cover_ << unset_cover_ << show_cover_;
|
||||
return QList<QAction*>() << cover_from_file_ << cover_to_file_ << separator_ << cover_from_url_ << search_for_cover_ << unset_cover_ << separator_ << show_cover_;
|
||||
}
|
||||
|
||||
QUrl AlbumCoverChoiceController::LoadCoverFromFile(Song *song) {
|
||||
|
||||
@@ -107,10 +107,9 @@ void CddaSongLoader::LoadSongs() {
|
||||
SongList songs;
|
||||
for (int track_number = 1; track_number <= num_tracks; track_number++) {
|
||||
// Init song
|
||||
Song song;
|
||||
Song song(Song::Source_CDDA);
|
||||
song.set_id(track_number);
|
||||
song.set_valid(true);
|
||||
song.set_source(Song::Source_CDDA);
|
||||
song.set_filetype(Song::FileType_CDDA);
|
||||
song.set_url(GetUrlFromTrack(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);
|
||||
if (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;
|
||||
for (GList *node = entries; node != nullptr; node = node->next) {
|
||||
GstTocEntry *entry = static_cast<GstTocEntry*>(node->data);
|
||||
@@ -200,7 +199,7 @@ void CddaSongLoader::AudioCDTagsLoaded(const QString &artist, const QString &alb
|
||||
if (results.empty()) return;
|
||||
int track_number = 1;
|
||||
for (const MusicBrainzClient::Result &ret : results) {
|
||||
Song song;
|
||||
Song song(Song::Source_CDDA);
|
||||
song.set_artist(artist);
|
||||
song.set_album(album);
|
||||
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_year(ret.year_);
|
||||
song.set_id(track_number);
|
||||
song.set_source(Song::Source_CDDA);
|
||||
song.set_filetype(Song::FileType_CDDA);
|
||||
song.set_valid(true);
|
||||
// We need to set url: that's how playlist will find the correct item to update
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -521,7 +521,7 @@ void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int
|
||||
BufferingFinished();
|
||||
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_);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -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
|
||||
|
||||
audioqueue_ = engine_->CreateElement("queue2", audiobin_);
|
||||
@@ -395,7 +389,7 @@ bool GstEnginePipeline::InitAudioBin() {
|
||||
next = equalizer_;
|
||||
}
|
||||
|
||||
// Link equalizer elements if enabled.
|
||||
// Link stereo balancer elements if enabled.
|
||||
if (stereo_balancer_enabled_ && audiopanorama_) {
|
||||
gst_element_link(next, audiopanorama_);
|
||||
next = audiopanorama_;
|
||||
@@ -617,7 +611,7 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad *pad, GstPadProbeInf
|
||||
if (instance->end_offset_nanosec_ > 0) {
|
||||
quint64 start_time = GST_BUFFER_TIMESTAMP(buf) - instance->segment_start_;
|
||||
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 (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);
|
||||
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.
|
||||
// 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";
|
||||
|
||||
@@ -52,7 +52,9 @@ LocalRedirectServer::LocalRedirectServer(const bool https, QObject *parent)
|
||||
socket_(nullptr)
|
||||
{}
|
||||
|
||||
LocalRedirectServer::~LocalRedirectServer() {}
|
||||
LocalRedirectServer::~LocalRedirectServer() {
|
||||
if (isListening()) close();
|
||||
}
|
||||
|
||||
bool LocalRedirectServer::GenerateCertificate() {
|
||||
|
||||
|
||||
135
src/lyrics/chartlyricsprovider.cpp
Normal file
135
src/lyrics/chartlyricsprovider.cpp
Normal 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 ¶m : 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());
|
||||
}
|
||||
56
src/lyrics/chartlyricsprovider.h
Normal file
56
src/lyrics/chartlyricsprovider.h
Normal 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
|
||||
@@ -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.
|
||||
// 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);
|
||||
Utilities::CheckPortable();
|
||||
// Parse commandline options - need to do this before starting the full QApplication so it works without an X server
|
||||
if (!options.Parse()) return 1;
|
||||
logging::SetLevels(options.log_levels());
|
||||
|
||||
@@ -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".
|
||||
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 :(
|
||||
ex.setPattern("(href\\s*=\\s*\")([^\"]+)\"");
|
||||
index = 0;
|
||||
QRegExp ex("(href\\s*=\\s*\")([^\"]+)\"");
|
||||
int index = 0;
|
||||
while ((index = ex.indexIn(data, index)) != -1) {
|
||||
QString url = ex.cap(2);
|
||||
url.replace(QRegExp("&(?!amp;|quot;|apos;|lt;|gt;)"), "&");
|
||||
@@ -76,16 +64,17 @@ SongList ASXParser::Load(QIODevice *device, const QString &playlist_path, const
|
||||
SongList ret;
|
||||
|
||||
QXmlStreamReader reader(&buffer);
|
||||
if (!Utilities::ParseUntilElement(&reader, "asx")) {
|
||||
if (!Utilities::ParseUntilElementCI(&reader, "asx")) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (!reader.atEnd() && Utilities::ParseUntilElement(&reader, "entry")) {
|
||||
while (!reader.atEnd() && Utilities::ParseUntilElementCI(&reader, "entry")) {
|
||||
Song song = ParseTrack(&reader, dir);
|
||||
if (song.is_valid()) {
|
||||
ret << song;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
@@ -99,18 +88,21 @@ Song ASXParser::ParseTrack(QXmlStreamReader *reader, const QDir &dir) const {
|
||||
|
||||
switch (type) {
|
||||
case QXmlStreamReader::StartElement: {
|
||||
QStringRef name = reader->name();
|
||||
QString name = reader->name().toString().toLower();
|
||||
if (name == "ref") {
|
||||
ref = reader->attributes().value("href").toString();
|
||||
} else if (name == "title") {
|
||||
}
|
||||
else if (name == "title") {
|
||||
title = reader->readElementText();
|
||||
} else if (name == "author") {
|
||||
}
|
||||
else if (name == "author") {
|
||||
artist = reader->readElementText();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QXmlStreamReader::EndElement: {
|
||||
if (reader->name() == "entry") {
|
||||
QString name = reader->name().toString().toLower();
|
||||
if (name == "entry") {
|
||||
goto return_song;
|
||||
}
|
||||
break;
|
||||
@@ -124,9 +116,12 @@ return_song:
|
||||
Song song = LoadSong(ref, 0, dir);
|
||||
|
||||
// Override metadata with what was in the playlist
|
||||
song.set_title(title);
|
||||
song.set_artist(artist);
|
||||
song.set_album(album);
|
||||
if (song.source() != Song::Source_Collection) {
|
||||
if (!title.isEmpty()) song.set_title(title);
|
||||
if (!artist.isEmpty()) song.set_artist(artist);
|
||||
if (!album.isEmpty()) song.set_album(album);
|
||||
}
|
||||
|
||||
return song;
|
||||
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ bool M3UParser::ParseMetadata(const QString &line, M3UParser::Metadata *metadata
|
||||
metadata->artist = list[0].trimmed();
|
||||
metadata->title = list[1].trimmed();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
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("\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool M3UParser::TryMagic(const QByteArray &data) const {
|
||||
|
||||
@@ -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 song;
|
||||
Song song(Song::Source_LocalFile);
|
||||
LoadSong(filename_or_url, beginning, dir, &song);
|
||||
return song;
|
||||
|
||||
|
||||
@@ -27,4 +27,3 @@ class CollectionBackendInterface;
|
||||
|
||||
XMLParser::XMLParser(CollectionBackendInterface *collection, QObject *parent)
|
||||
: ParserBase(collection, parent) {}
|
||||
|
||||
|
||||
@@ -124,11 +124,14 @@ return_song:
|
||||
Song song = LoadSong(location, 0, dir);
|
||||
|
||||
// Override metadata with what was in the playlist
|
||||
song.set_title(title);
|
||||
song.set_artist(artist);
|
||||
song.set_album(album);
|
||||
song.set_length_nanosec(nanosec);
|
||||
song.set_track(track_num);
|
||||
if (song.source() != Song::Source_Collection) {
|
||||
if (!title.isEmpty()) song.set_title(title);
|
||||
if (!artist.isEmpty()) song.set_artist(artist);
|
||||
if (!album.isEmpty()) song.set_album(album);
|
||||
if (nanosec > 0) song.set_length_nanosec(nanosec);
|
||||
if (track_num > 0) song.set_track(track_num);
|
||||
}
|
||||
|
||||
return song;
|
||||
|
||||
}
|
||||
|
||||
@@ -876,7 +876,7 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id_re
|
||||
QJsonObject json_obj = value.toObject();
|
||||
|
||||
++songs_received;
|
||||
Song song;
|
||||
Song song(Song::Source_Qobuz);
|
||||
ParseSong(song, json_obj, artist_id, album_id, album_artist, album, cover_url);
|
||||
if (!song.is_valid()) continue;
|
||||
if (song.disc() >= 2) multidisc = true;
|
||||
|
||||
@@ -71,9 +71,11 @@ ListenBrainzScrobbler::ListenBrainzScrobbler(Application *app, QObject *parent)
|
||||
app_(app),
|
||||
network_(new NetworkAccessManager(this)),
|
||||
cache_(new ScrobblerCache(kCacheFile, this)),
|
||||
server_(nullptr),
|
||||
enabled_(false),
|
||||
expires_in_(-1),
|
||||
submitted_(false) {
|
||||
submitted_(false),
|
||||
timestamp_(0) {
|
||||
|
||||
ReloadSettings();
|
||||
LoadSession();
|
||||
@@ -123,16 +125,19 @@ void ListenBrainzScrobbler::Logout() {
|
||||
|
||||
void ListenBrainzScrobbler::Authenticate(const bool https) {
|
||||
|
||||
LocalRedirectServer *server = new LocalRedirectServer(https, this);
|
||||
if (!server->Listen()) {
|
||||
AuthError(server->error());
|
||||
delete server;
|
||||
return;
|
||||
if (!server_) {
|
||||
server_ = new LocalRedirectServer(https, this);
|
||||
if (!server_->Listen()) {
|
||||
AuthError(server_->error());
|
||||
delete server_;
|
||||
server_ = nullptr;
|
||||
return;
|
||||
}
|
||||
connect(server_, SIGNAL(Finished()), this, SLOT(RedirectArrived()));
|
||||
}
|
||||
NewClosure(server, SIGNAL(Finished()), this, &ListenBrainzScrobbler::RedirectArrived, server);
|
||||
|
||||
QUrl redirect_url(kRedirectUrl);
|
||||
redirect_url.setPort(server->url().port());
|
||||
redirect_url.setPort(server_->url().port());
|
||||
|
||||
QUrlQuery url_query;
|
||||
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 (!QUrlQuery(url).queryItemValue("error").isEmpty()) {
|
||||
AuthError(QUrlQuery(url).queryItemValue("error"));
|
||||
return;
|
||||
if (server_->error().isEmpty()) {
|
||||
QUrl url = server_->request_url();
|
||||
if (url.isValid()) {
|
||||
QUrlQuery url_query(url);
|
||||
if (url_query.hasQueryItem("error")) {
|
||||
AuthError(QUrlQuery(url).queryItemValue("error"));
|
||||
}
|
||||
else if (url_query.hasQueryItem("code")) {
|
||||
RequestSession(url, url_query.queryItemValue("code"));
|
||||
}
|
||||
else {
|
||||
AuthError(tr("Redirect missing token code!"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
AuthError(tr("Received invalid reply from web browser."));
|
||||
}
|
||||
}
|
||||
if (QUrlQuery(url).queryItemValue("code").isEmpty()) {
|
||||
AuthError("Redirect missing token code!");
|
||||
return;
|
||||
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);
|
||||
QUrlQuery url_query;
|
||||
@@ -345,13 +364,16 @@ void ListenBrainzScrobbler::UpdateNowPlaying(const Song &song) {
|
||||
album = album.remove(Song::kAlbumRemoveMisc);
|
||||
|
||||
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()));
|
||||
}
|
||||
else {
|
||||
object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.albumartist()));
|
||||
}
|
||||
object_track_metadata.insert("release_name", QJsonValue::fromVariant(album));
|
||||
|
||||
if (!album.isEmpty())
|
||||
object_track_metadata.insert("release_name", QJsonValue::fromVariant(album));
|
||||
|
||||
object_track_metadata.insert("track_name", QJsonValue::fromVariant(song.title()));
|
||||
|
||||
QJsonObject object_listen;
|
||||
@@ -459,16 +481,19 @@ void ListenBrainzScrobbler::Submit() {
|
||||
for (ScrobblerCacheItem *item : cache_->List()) {
|
||||
if (item->sent_) continue;
|
||||
item->sent_ = true;
|
||||
i++;
|
||||
++i;
|
||||
list << item->timestamp_;
|
||||
QJsonObject object_listen;
|
||||
object_listen.insert("listened_at", QJsonValue::fromVariant(item->timestamp_));
|
||||
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_));
|
||||
else
|
||||
object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->albumartist_));
|
||||
object_track_metadata.insert("release_name", QJsonValue::fromVariant(item->album_));
|
||||
|
||||
if (!item->album_.isEmpty())
|
||||
object_track_metadata.insert("release_name", QJsonValue::fromVariant(item->album_));
|
||||
|
||||
object_track_metadata.insert("track_name", QJsonValue::fromVariant(item->song_));
|
||||
object_listen.insert("track_metadata", object_track_metadata);
|
||||
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);
|
||||
}
|
||||
|
||||
void ListenBrainzScrobbler::Error(QString error, QVariant debug) {
|
||||
void ListenBrainzScrobbler::Error(const QString &error, const QVariant &debug) {
|
||||
|
||||
qLog(Error) << "ListenBrainz:" << error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QByteArray>
|
||||
@@ -37,6 +36,8 @@
|
||||
#include "scrobblerservice.h"
|
||||
#include "scrobblercache.h"
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class Application;
|
||||
class NetworkAccessManager;
|
||||
class LocalRedirectServer;
|
||||
@@ -75,7 +76,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
||||
void WriteCache() { cache_->WriteCache(); }
|
||||
|
||||
private slots:
|
||||
void RedirectArrived(LocalRedirectServer *server);
|
||||
void RedirectArrived();
|
||||
void AuthenticateReplyFinished(QNetworkReply *reply);
|
||||
void UpdateNowPlayingRequestFinished(QNetworkReply *reply);
|
||||
void ScrobbleRequestFinished(QNetworkReply *reply, QList<quint64>);
|
||||
@@ -84,9 +85,9 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
||||
QNetworkReply *CreateRequest(const QUrl &url, const QJsonDocument &json_doc);
|
||||
QByteArray GetReplyData(QNetworkReply *reply);
|
||||
|
||||
void RequestSession(QUrl url, QString token);
|
||||
void AuthError(QString error);
|
||||
void Error(QString error, QVariant debug = QVariant());
|
||||
void RequestSession(const QUrl &url, const QString &token);
|
||||
void AuthError(const QString &error);
|
||||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
void DoSubmit();
|
||||
|
||||
static const char *kAuthUrl;
|
||||
@@ -101,6 +102,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
||||
Application *app_;
|
||||
NetworkAccessManager *network_;
|
||||
ScrobblerCache *cache_;
|
||||
LocalRedirectServer *server_;
|
||||
bool enabled_;
|
||||
QString user_token_;
|
||||
QString access_token_;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
class ScrobblerCacheItem : public QObject {
|
||||
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 ×tamp);
|
||||
~ScrobblerCacheItem();
|
||||
|
||||
QString effective_albumartist() const { return albumartist_.isEmpty() ? artist_ : albumartist_; }
|
||||
QString effective_albumartist() const { return albumartist_.isEmpty() || albumartist_.toLower() == Song::kVariousArtists ? artist_ : albumartist_; }
|
||||
|
||||
public:
|
||||
QString artist_;
|
||||
|
||||
@@ -52,7 +52,7 @@ class ScrobblerService : public QObject {
|
||||
virtual void ClearPlaying() = 0;
|
||||
virtual void Scrobble(const Song &song) = 0;
|
||||
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 Submitted() = 0;
|
||||
|
||||
@@ -72,9 +72,11 @@ ScrobblingAPI20::ScrobblingAPI20(const QString &name, const QString &settings_gr
|
||||
api_url_(api_url),
|
||||
batch_(batch),
|
||||
app_(app),
|
||||
server_(nullptr),
|
||||
enabled_(false),
|
||||
subscriber_(false),
|
||||
submitted_(false) {}
|
||||
submitted_(false),
|
||||
timestamp_(0) {}
|
||||
|
||||
ScrobblingAPI20::~ScrobblingAPI20() {}
|
||||
|
||||
@@ -121,16 +123,19 @@ void ScrobblingAPI20::Logout() {
|
||||
|
||||
void ScrobblingAPI20::Authenticate(const bool https) {
|
||||
|
||||
LocalRedirectServer *server = new LocalRedirectServer(https, this);
|
||||
if (!server->Listen()) {
|
||||
AuthError(server->error());
|
||||
delete server;
|
||||
return;
|
||||
if (!server_) {
|
||||
server_ = new LocalRedirectServer(https, this);
|
||||
if (!server_->Listen()) {
|
||||
AuthError(server_->error());
|
||||
delete server_;
|
||||
server_ = nullptr;
|
||||
return;
|
||||
}
|
||||
connect(server_, SIGNAL(Finished()), this, SLOT(RedirectArrived()));
|
||||
}
|
||||
NewClosure(server, SIGNAL(Finished()), this, &ScrobblingAPI20::RedirectArrived, server);
|
||||
|
||||
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);
|
||||
if (https) redirect_url_query.addQueryItem("https", QString("1"));
|
||||
QUrl redirect_url(kRedirectUrl);
|
||||
@@ -160,8 +165,11 @@ void ScrobblingAPI20::Authenticate(const bool https) {
|
||||
QApplication::clipboard()->setText(url.toString());
|
||||
break;
|
||||
case QMessageBox::Cancel:
|
||||
server->close();
|
||||
server->deleteLater();
|
||||
if (server_) {
|
||||
server_->close();
|
||||
server_->deleteLater();
|
||||
server_ = nullptr;
|
||||
}
|
||||
emit AuthenticationComplete(false);
|
||||
break;
|
||||
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()) {
|
||||
AuthError(server->error());
|
||||
return;
|
||||
if (server_->error().isEmpty()) {
|
||||
QUrl url = server_->request_url();
|
||||
if (url.isValid()) {
|
||||
QUrlQuery url_query(url);
|
||||
if (url_query.hasQueryItem("token")) {
|
||||
QString token = url_query.queryItemValue("token").toUtf8();
|
||||
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());
|
||||
}
|
||||
|
||||
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);
|
||||
QString token = url_query.queryItemValue("token").toUtf8();
|
||||
if (token.isEmpty()) {
|
||||
AuthError(tr("Invalid reply from web browser. Missing token."));
|
||||
return;
|
||||
}
|
||||
RequestSession(token);
|
||||
server_->close();
|
||||
server_->deleteLater();
|
||||
server_ = nullptr;
|
||||
|
||||
}
|
||||
|
||||
void ScrobblingAPI20::RequestSession(QString token) {
|
||||
void ScrobblingAPI20::RequestSession(const QString &token) {
|
||||
|
||||
QUrl session_url(api_url_);
|
||||
QUrlQuery session_url_query;
|
||||
@@ -422,11 +436,14 @@ void ScrobblingAPI20::UpdateNowPlaying(const Song &song) {
|
||||
|
||||
ParamList params = ParamList()
|
||||
<< Param("method", "track.updateNowPlaying")
|
||||
<< Param("artist", prefer_albumartist_ ? song.effective_albumartist() : song.artist())
|
||||
<< Param("track", song.title())
|
||||
<< Param("album", album);
|
||||
<< Param("artist", prefer_albumartist_ && song.effective_albumartist() != Song::kVariousArtists ? song.effective_albumartist() : song.artist())
|
||||
<< Param("track", song.title());
|
||||
|
||||
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);
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(UpdateNowPlayingRequestFinished(QNetworkReply*)), reply);
|
||||
@@ -518,16 +535,22 @@ void ScrobblingAPI20::Submit() {
|
||||
for (ScrobblerCacheItem *item : cache()->List()) {
|
||||
if (item->sent_) continue;
|
||||
item->sent_ = true;
|
||||
if (!batch_) { SendSingleScrobble(item); continue; }
|
||||
if (!batch_) {
|
||||
SendSingleScrobble(item);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
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("album").arg(i), item->album_);
|
||||
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("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->track_ > 0) params << Param(QString("%1[%2]").arg(i).arg("trackNumber"), QString::number(item->track_));
|
||||
if (!item->album_.isEmpty())
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -699,13 +722,16 @@ void ScrobblingAPI20::SendSingleScrobble(ScrobblerCacheItem *item) {
|
||||
ParamList params = ParamList()
|
||||
<< Param("method", "track.scrobble")
|
||||
<< Param("artist", prefer_albumartist_ ? item->effective_albumartist() : item->artist_)
|
||||
<< Param("album", item->album_)
|
||||
<< Param("track", item->song_)
|
||||
<< Param("timestamp", QString::number(item->timestamp_))
|
||||
<< Param("duration", QString::number(item->duration_ / kNsecPerSec));
|
||||
|
||||
if (!prefer_albumartist_ && !item->albumartist_.isEmpty()) params << Param("albumArtist", item->albumartist_);
|
||||
if (item->track_ > 0) params << Param("trackNumber", QString::number(item->track_));
|
||||
if (!item->album_.isEmpty())
|
||||
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);
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(SingleScrobbleRequestFinished(QNetworkReply*, quint64)), reply, item->timestamp_);
|
||||
@@ -846,10 +872,14 @@ void ScrobblingAPI20::Love() {
|
||||
|
||||
ParamList params = ParamList()
|
||||
<< Param("method", "track.love")
|
||||
<< Param("artist", song_playing_.artist())
|
||||
<< Param("track", song_playing_.title())
|
||||
<< Param("album", song_playing_.album());
|
||||
if (!song_playing_.albumartist().isEmpty()) params << Param("albumArtist", song_playing_.albumartist());
|
||||
<< Param("artist", prefer_albumartist_ && song_playing_.effective_albumartist() != Song::kVariousArtists ? song_playing_.effective_albumartist() : song_playing_.artist())
|
||||
<< Param("track", song_playing_.title());
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
void ScrobblingAPI20::Error(QString error, QVariant debug) {
|
||||
void ScrobblingAPI20::Error(const QString &error, const QVariant &debug) {
|
||||
|
||||
qLog(Error) << name_ << error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
}
|
||||
|
||||
QString ScrobblingAPI20::ErrorString(ScrobbleErrorCode error) const {
|
||||
QString ScrobblingAPI20::ErrorString(const ScrobbleErrorCode error) const {
|
||||
|
||||
switch (error) {
|
||||
case ScrobbleErrorCode::InvalidService:
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QByteArray>
|
||||
@@ -36,6 +35,8 @@
|
||||
#include "scrobblerservice.h"
|
||||
#include "scrobblercache.h"
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class Application;
|
||||
class NetworkAccessManager;
|
||||
class LocalRedirectServer;
|
||||
@@ -81,7 +82,7 @@ class ScrobblingAPI20 : public ScrobblerService {
|
||||
void WriteCache() { cache()->WriteCache(); }
|
||||
|
||||
private slots:
|
||||
void RedirectArrived(LocalRedirectServer *server);
|
||||
void RedirectArrived();
|
||||
void AuthenticateReplyFinished(QNetworkReply *reply);
|
||||
void UpdateNowPlayingRequestFinished(QNetworkReply *reply);
|
||||
void ScrobbleRequestFinished(QNetworkReply *reply, QList<quint64>);
|
||||
@@ -130,11 +131,11 @@ class ScrobblingAPI20 : public ScrobblerService {
|
||||
QNetworkReply *CreateRequest(const ParamList &request_params);
|
||||
QByteArray GetReplyData(QNetworkReply *reply);
|
||||
|
||||
void RequestSession(QString token);
|
||||
void AuthError(QString error);
|
||||
void RequestSession(const QString &token);
|
||||
void AuthError(const QString &error);
|
||||
void SendSingleScrobble(ScrobblerCacheItem *item);
|
||||
void Error(QString error, QVariant debug = QVariant());
|
||||
QString ErrorString(ScrobbleErrorCode error) const;
|
||||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
QString ErrorString(const ScrobbleErrorCode error) const;
|
||||
void DoSubmit();
|
||||
|
||||
QString name_;
|
||||
@@ -144,6 +145,7 @@ class ScrobblingAPI20 : public ScrobblerService {
|
||||
bool batch_;
|
||||
|
||||
Application *app_;
|
||||
LocalRedirectServer *server_;
|
||||
|
||||
bool enabled_;
|
||||
bool https_;
|
||||
|
||||
@@ -65,7 +65,6 @@ BackendSettingsPage::BackendSettingsPage(SettingsDialog *dialog) : SettingsPage(
|
||||
ui_->label_bufferminfillvalue->setMinimumWidth(QFontMetrics(ui_->label_bufferminfillvalue->font()).width("WW%"));
|
||||
ui_->label_replaygainpreamp->setMinimumWidth(QFontMetrics(ui_->label_replaygainpreamp->font()).width("-WW.W dB"));
|
||||
#endif
|
||||
RgPreampChanged(ui_->stickslider_replaygainpreamp->value());
|
||||
|
||||
s_.beginGroup(BackendSettingsPage::kSettingsGroup);
|
||||
|
||||
@@ -179,6 +178,7 @@ void BackendSettingsPage::Load() {
|
||||
connect(ui_->checkbox_volume_control, SIGNAL(toggled(bool)), SLOT(FadingOptionsChanged()));
|
||||
|
||||
FadingOptionsChanged();
|
||||
RgPreampChanged(ui_->stickslider_replaygainpreamp->value());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -71,19 +71,6 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog *dialog) : SettingsP
|
||||
}
|
||||
#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
|
||||
// Populate the language combo box. We do this by looking at all the compiled in translations.
|
||||
QDir dir(":/translations/");
|
||||
@@ -121,6 +108,25 @@ BehaviourSettingsPage::BehaviourSettingsPage(SettingsDialog *dialog) : SettingsP
|
||||
ui_->groupbox_language->setVisible(false);
|
||||
#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
|
||||
QString de = Utilities::DesktopEnvironment();
|
||||
if (de.toLower() == "kde")
|
||||
@@ -154,7 +160,7 @@ void BehaviourSettingsPage::Load() {
|
||||
ui_->checkbox_keeprunning->setChecked(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
ui_->checkbox_resumeplayback->setChecked(s.value("resumeplayback", false).toBool());
|
||||
ui_->checkbox_playingwidget->setChecked(s.value("playing_widget", true).toBool());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
if (name.isEmpty())
|
||||
ui_->combobox_language->setCurrentIndex(0);
|
||||
else
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -187,28 +197,37 @@ void BehaviourSettingsPage::Save() {
|
||||
QSettings s;
|
||||
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;
|
||||
if (ui_->radiobutton_alwayshide->isChecked()) behaviour = MainWindow::Startup_AlwaysHide;
|
||||
if (ui_->radiobutton_alwaysshow->isChecked()) behaviour = MainWindow::Startup_AlwaysShow;
|
||||
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("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());
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,17 @@ public:
|
||||
|
||||
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 {
|
||||
AddBehaviour_Append = 1,
|
||||
AddBehaviour_Enqueue = 2,
|
||||
@@ -51,14 +61,6 @@ public:
|
||||
AddBehaviour_OpenInNew = 4
|
||||
};
|
||||
|
||||
// Don't change the values
|
||||
enum PlayBehaviour {
|
||||
PlayBehaviour_Never = 1,
|
||||
PlayBehaviour_IfStopped = 2,
|
||||
PlayBehaviour_Always = 3,
|
||||
};
|
||||
|
||||
// Don't change the values
|
||||
enum PlaylistAddBehaviour {
|
||||
PlaylistAddBehaviour_Play = 1,
|
||||
PlaylistAddBehaviour_Enqueue = 2,
|
||||
|
||||
@@ -435,7 +435,7 @@ void SubsonicRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const qint64
|
||||
QJsonObject json_obj = value.toObject();
|
||||
|
||||
++songs_received;
|
||||
Song song;
|
||||
Song song(Song::Source_Subsonic);
|
||||
ParseSong(song, json_obj, artist_id, album_id, album_artist);
|
||||
if (!song.is_valid()) continue;
|
||||
if (song.disc() >= 2) multidisc = true;
|
||||
|
||||
@@ -834,7 +834,7 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id, c
|
||||
}
|
||||
|
||||
++songs_received;
|
||||
Song song;
|
||||
Song song(Song::Source_Tidal);
|
||||
ParseSong(song, json_obj, artist_id, album_id, album_artist);
|
||||
if (!song.is_valid()) continue;
|
||||
if (song.disc() >= 2) multidisc = true;
|
||||
|
||||
@@ -575,7 +575,7 @@ void Transcoder::SetElementProperties(const QString &name, GObject *object) {
|
||||
guint properties_count = 0;
|
||||
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];
|
||||
|
||||
const QVariant value = s.value(property->name);
|
||||
|
||||
@@ -120,7 +120,7 @@ msgstr " s"
|
||||
msgid " seconds"
|
||||
msgstr " segundos"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:145 scrobbler/scrobblingapi20.cpp:154
|
||||
#: scrobbler/scrobblingapi20.cpp:150 scrobbler/scrobblingapi20.cpp:159
|
||||
#, qt-format
|
||||
msgid "%1 Scrobbler Authentication"
|
||||
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"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228
|
||||
msgid "Append to current playlist"
|
||||
@@ -842,7 +842,7 @@ msgstr "Añadir a la lista de reproducción"
|
||||
msgid "Apply compression to prevent clipping"
|
||||
msgstr "Aplicar compresión para evitar saturación"
|
||||
|
||||
#: equalizer/equalizer.cpp:229
|
||||
#: equalizer/equalizer.cpp:227
|
||||
#, qt-format
|
||||
msgid "Are you sure you want to delete the \"%1\" preset?"
|
||||
msgstr "¿Seguro que desea eliminar el ajuste predefinido «%1»?"
|
||||
@@ -1044,7 +1044,7 @@ msgstr "Búfer"
|
||||
msgid "Buffer duration"
|
||||
msgstr "Duración del búfer"
|
||||
|
||||
#: engine/gstengine.cpp:622
|
||||
#: engine/gstengine.cpp:636
|
||||
msgid "Buffering"
|
||||
msgstr "Guardando en búfer"
|
||||
|
||||
@@ -1052,11 +1052,11 @@ msgstr "Guardando en búfer"
|
||||
msgid "C&onsole"
|
||||
msgstr "C&onsola"
|
||||
|
||||
#: core/song.cpp:481
|
||||
#: core/song.cpp:474
|
||||
msgid "CD"
|
||||
msgstr "CD"
|
||||
|
||||
#: core/songloader.cpp:195
|
||||
#: core/songloader.cpp:196
|
||||
msgid "CD playback is only available with the GStreamer engine."
|
||||
msgstr "La reproducción de CD solo es posible con GStreamer."
|
||||
|
||||
@@ -1092,7 +1092,7 @@ msgstr "Elegir color…"
|
||||
msgid "Choose font..."
|
||||
msgstr "Elegir tipo de letra…"
|
||||
|
||||
#: equalizer/equalizer.cpp:140
|
||||
#: equalizer/equalizer.cpp:138
|
||||
msgid "Classical"
|
||||
msgstr "Clásica"
|
||||
|
||||
@@ -1143,11 +1143,11 @@ msgid "Closing this window will stop searching for album covers."
|
||||
msgstr ""
|
||||
"Si cierra esta ventana se detendrá la búsqueda de portadas para los álbumes."
|
||||
|
||||
#: equalizer/equalizer.cpp:141
|
||||
#: equalizer/equalizer.cpp:139
|
||||
msgid "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
|
||||
msgid "Collection"
|
||||
msgstr "Colección"
|
||||
@@ -1249,7 +1249,7 @@ msgstr "Copiar la portada del álbum"
|
||||
msgid "Copy to collection..."
|
||||
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
|
||||
#: widgets/fileviewlist.cpp:49
|
||||
msgid "Copy to device..."
|
||||
@@ -1268,7 +1268,7 @@ msgstr ""
|
||||
"No se pudo crear el elemento «%1» de GStreamer. Asegúrese de tener "
|
||||
"instalados todos los complementos necesarios de GStreamer."
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:154
|
||||
#: scrobbler/scrobblingapi20.cpp:159
|
||||
#, qt-format
|
||||
msgid ""
|
||||
"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 /"
|
||||
"><a href=\"%1\">%1</a>"
|
||||
|
||||
#: core/songloader.cpp:260
|
||||
#: core/songloader.cpp:261
|
||||
#, qt-format
|
||||
msgid "Could not open file %1"
|
||||
msgstr "No se ha podido abrir el archivo %1"
|
||||
|
||||
#: core/songloader.cpp:436
|
||||
#: core/songloader.cpp:437
|
||||
#, qt-format
|
||||
msgid "Couldn't create gstreamer source element for %1"
|
||||
msgstr "No se ha podido crear el elemento de origen gstreamer para %1"
|
||||
@@ -1414,7 +1414,7 @@ msgstr "Ctrl+T"
|
||||
msgid "Ctrl+Up"
|
||||
msgstr "Ctrl+Arriba"
|
||||
|
||||
#: equalizer/equalizer.cpp:139
|
||||
#: equalizer/equalizer.cpp:137
|
||||
msgid "Custom"
|
||||
msgstr "Personalizado"
|
||||
|
||||
@@ -1434,7 +1434,7 @@ msgstr "Personalizado…"
|
||||
msgid "D-Bus path"
|
||||
msgstr "Ruta de D-Bus"
|
||||
|
||||
#: equalizer/equalizer.cpp:142
|
||||
#: equalizer/equalizer.cpp:140
|
||||
msgid "Dance"
|
||||
msgstr "Dance"
|
||||
|
||||
@@ -1479,7 +1479,7 @@ msgstr "Eliminar del dispositivo…"
|
||||
msgid "Delete from disk..."
|
||||
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"
|
||||
msgstr "Eliminar ajuste predefinido"
|
||||
|
||||
@@ -1507,7 +1507,7 @@ msgstr "Destino"
|
||||
msgid "Details..."
|
||||
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_backendsettingspage.h:428
|
||||
msgid "Device"
|
||||
@@ -1633,12 +1633,12 @@ msgstr "Editar etiquetas"
|
||||
msgid "Edit track information"
|
||||
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
|
||||
msgid "Edit track information..."
|
||||
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..."
|
||||
msgstr "Editar información de las pistas…"
|
||||
|
||||
@@ -1733,7 +1733,7 @@ msgid "Equivalent to --log-levels *:3"
|
||||
msgstr "Equivalente a --log-levels*:3"
|
||||
|
||||
#: 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"
|
||||
msgstr "Error"
|
||||
|
||||
@@ -1869,11 +1869,11 @@ msgstr "Devolver el álbum completo al buscar pistas"
|
||||
msgid "Fetching cover error"
|
||||
msgstr "Error al obtener la portada"
|
||||
|
||||
#: core/song.cpp:479
|
||||
#: core/song.cpp:472
|
||||
msgid "File"
|
||||
msgstr "Archivo"
|
||||
|
||||
#: core/song.cpp:1006
|
||||
#: core/song.cpp:999
|
||||
#, qt-format
|
||||
msgid "File %1 is not recognized as a valid audio file."
|
||||
msgstr "El archivo de audio %1 no parece válido."
|
||||
@@ -1992,15 +1992,15 @@ msgstr "Tramas por búfer"
|
||||
msgid "Frozen"
|
||||
msgstr "Congelado"
|
||||
|
||||
#: equalizer/equalizer.cpp:143
|
||||
#: equalizer/equalizer.cpp:141
|
||||
msgid "Full Bass"
|
||||
msgstr "Graves completos"
|
||||
|
||||
#: equalizer/equalizer.cpp:145
|
||||
#: equalizer/equalizer.cpp:143
|
||||
msgid "Full Bass + Treble"
|
||||
msgstr "Graves y agudos completos"
|
||||
|
||||
#: equalizer/equalizer.cpp:144
|
||||
#: equalizer/equalizer.cpp:142
|
||||
msgid "Full Treble"
|
||||
msgstr "Agudos completos"
|
||||
|
||||
@@ -2155,7 +2155,7 @@ msgstr "Ayuda"
|
||||
msgid "High"
|
||||
msgstr "Alto"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:75
|
||||
#: analyzer/analyzercontainer.cpp:74
|
||||
#, qt-format
|
||||
msgid "High (%1 fps)"
|
||||
msgstr "Alta (%1 fps)"
|
||||
@@ -2239,7 +2239,7 @@ msgstr "Comprobación de integridad"
|
||||
msgid "Intro tracks"
|
||||
msgstr "Pistas de introducción"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:190
|
||||
#: scrobbler/scrobblingapi20.cpp:194
|
||||
msgid "Invalid reply from web browser. Missing token."
|
||||
msgstr "Respuesta inválida del servidor web. Falta token."
|
||||
|
||||
@@ -2271,11 +2271,11 @@ msgstr "Mantener los archivos originales"
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
|
||||
#: equalizer/equalizer.cpp:146
|
||||
#: equalizer/equalizer.cpp:144
|
||||
msgid "Laptop/Headphones"
|
||||
msgstr "Portátil/auriculares"
|
||||
|
||||
#: equalizer/equalizer.cpp:147
|
||||
#: equalizer/equalizer.cpp:145
|
||||
msgid "Large Hall"
|
||||
msgstr "Salón grande"
|
||||
|
||||
@@ -2314,7 +2314,7 @@ msgstr "Duración"
|
||||
msgid "Libre.fm"
|
||||
msgstr "Libre.fm"
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:147
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:152
|
||||
msgid "ListenBrainz Authentication"
|
||||
msgstr "Inicio de sesión en ListenBrainz"
|
||||
|
||||
@@ -2322,7 +2322,7 @@ msgstr "Inicio de sesión en ListenBrainz"
|
||||
msgid "Listenbrainz"
|
||||
msgstr "Listenbrainz"
|
||||
|
||||
#: equalizer/equalizer.cpp:148
|
||||
#: equalizer/equalizer.cpp:146
|
||||
msgid "Live"
|
||||
msgstr "En directo"
|
||||
|
||||
@@ -2358,7 +2358,7 @@ msgstr "Cargando el dispositivo MTP"
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Cargando la base de datos del iPod"
|
||||
|
||||
#: collection/collectionmodel.cpp:179
|
||||
#: collection/collectionmodel.cpp:180
|
||||
msgid "Loading songs"
|
||||
msgstr "Cargando pistas"
|
||||
|
||||
@@ -2370,7 +2370,7 @@ msgstr "Cargando pistas"
|
||||
msgid "Loading tracks info"
|
||||
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..."
|
||||
msgstr "Cargando…"
|
||||
|
||||
@@ -2394,7 +2394,7 @@ msgstr "Perfil de predicción a largo plazo (LTP)"
|
||||
msgid "Love"
|
||||
msgstr "Amor"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:73
|
||||
#: analyzer/analyzercontainer.cpp:72
|
||||
#, qt-format
|
||||
msgid "Low (%1 fps)"
|
||||
msgstr "Baja (%1 fps)"
|
||||
@@ -2452,7 +2452,7 @@ msgstr "Tasa de bits máxima"
|
||||
msgid "Maximum number of login attempts reached."
|
||||
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
|
||||
msgid "Medium (%1 fps)"
|
||||
msgstr "Media (%1 fps)"
|
||||
@@ -2578,7 +2578,7 @@ msgstr "Música"
|
||||
msgid "Mute"
|
||||
msgstr "Silenciar"
|
||||
|
||||
#: equalizer/equalizer.cpp:211 collection/savedgroupingmanager.cpp:55
|
||||
#: equalizer/equalizer.cpp:209 collection/savedgroupingmanager.cpp:55
|
||||
#: ../build/src/ui_deviceproperties.h:368
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
@@ -2625,7 +2625,7 @@ msgstr "Siguiente"
|
||||
msgid "Next week"
|
||||
msgstr "Próxima semana"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:86
|
||||
#: analyzer/analyzercontainer.cpp:85
|
||||
msgid "No analyzer"
|
||||
msgstr "Sin analizador"
|
||||
|
||||
@@ -2662,7 +2662,7 @@ msgid "None"
|
||||
msgstr "Ninguno"
|
||||
|
||||
#: 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"
|
||||
msgstr ""
|
||||
"Ninguna de las pistas seleccionadas era apta para copiarse en un dispositivo"
|
||||
@@ -2737,7 +2737,7 @@ msgstr "Al iniciar"
|
||||
msgid "Opacity"
|
||||
msgstr "Opacidad"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:145
|
||||
#: scrobbler/scrobblingapi20.cpp:150
|
||||
#, qt-format
|
||||
msgid ""
|
||||
"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"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230
|
||||
#: ../build/src/ui_behavioursettingspage.h:274
|
||||
@@ -2791,7 +2791,7 @@ msgstr "Opus"
|
||||
msgid "Organise Files"
|
||||
msgstr "Organizar archivos"
|
||||
|
||||
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:366
|
||||
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:279
|
||||
#: collection/collectionview.cpp:342
|
||||
msgid "Organise files..."
|
||||
msgstr "Organizar archivos…"
|
||||
@@ -2848,7 +2848,7 @@ msgstr "Lista de re&producción"
|
||||
msgid "Partition label"
|
||||
msgstr "Etiqueta de partición"
|
||||
|
||||
#: equalizer/equalizer.cpp:149
|
||||
#: equalizer/equalizer.cpp:147
|
||||
msgid "Party"
|
||||
msgstr "Fiesta"
|
||||
|
||||
@@ -2942,12 +2942,12 @@ msgstr "Listas"
|
||||
msgid "Please close your browser and return to Strawberry."
|
||||
msgstr "Por favor, cierre el navegador y regrese a Strawberry."
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:147
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:152
|
||||
#, qt-format
|
||||
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>"
|
||||
|
||||
#: equalizer/equalizer.cpp:150
|
||||
#: equalizer/equalizer.cpp:148
|
||||
msgid "Pop"
|
||||
msgstr "Pop"
|
||||
|
||||
@@ -2985,7 +2985,7 @@ msgstr "Nombres de archivo preferidos para las portadas (separados por comas)"
|
||||
msgid "Preferred format"
|
||||
msgstr "Formato preferido"
|
||||
|
||||
#: core/songloader.cpp:156
|
||||
#: core/songloader.cpp:157
|
||||
msgid "Preload function was not set for blocking operation."
|
||||
msgstr ""
|
||||
"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"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:307
|
||||
msgid "Queue track"
|
||||
@@ -3092,7 +3092,11 @@ msgstr "Re&lativo"
|
||||
msgid "Really cancel?"
|
||||
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 ""
|
||||
"Received invalid reply from web browser. Try the HTTPS option, or use "
|
||||
"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 "
|
||||
"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
|
||||
msgid "Refresh catalogue"
|
||||
msgstr "Actualiza el catálogo"
|
||||
|
||||
#: equalizer/equalizer.cpp:151
|
||||
#: equalizer/equalizer.cpp:149
|
||||
msgid "Reggae"
|
||||
msgstr "Reggae"
|
||||
|
||||
@@ -3181,7 +3189,7 @@ msgstr "Repetir lista de reproducción"
|
||||
msgid "Repeat track"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229
|
||||
msgid "Replace current playlist"
|
||||
@@ -3305,7 +3313,7 @@ msgstr "Volver a Strawberry"
|
||||
msgid "Right"
|
||||
msgstr "Derecha"
|
||||
|
||||
#: equalizer/equalizer.cpp:152
|
||||
#: equalizer/equalizer.cpp:150
|
||||
msgid "Rock"
|
||||
msgstr "Rock"
|
||||
|
||||
@@ -3374,7 +3382,7 @@ msgstr "Guardar lista de reproducción"
|
||||
msgid "Save playlist..."
|
||||
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"
|
||||
msgstr "Guardar ajuste predefinido"
|
||||
|
||||
@@ -3406,7 +3414,7 @@ msgstr "Tamaño de escala"
|
||||
msgid "Scrobbler"
|
||||
msgstr "Registro de reproducción"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:478
|
||||
#: scrobbler/scrobblingapi20.cpp:495
|
||||
#, qt-format
|
||||
msgid "Scrobbler %1 is not authenticated!"
|
||||
msgstr ""
|
||||
@@ -3617,7 +3625,7 @@ msgstr "Mostrar en la colección…"
|
||||
msgid "Show in file browser"
|
||||
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
|
||||
msgid "Show in file browser..."
|
||||
msgstr "Mostrar en el gestor de archivos…"
|
||||
@@ -3694,7 +3702,7 @@ msgstr "Tamaño"
|
||||
msgid "Size:"
|
||||
msgstr "Tamaño:"
|
||||
|
||||
#: equalizer/equalizer.cpp:154
|
||||
#: equalizer/equalizer.cpp:152
|
||||
msgid "Ska"
|
||||
msgstr "Ska"
|
||||
|
||||
@@ -3726,11 +3734,11 @@ msgstr "Portada de álbum pequeña"
|
||||
msgid "Small sidebar"
|
||||
msgstr "Barra lateral pequeña"
|
||||
|
||||
#: equalizer/equalizer.cpp:153
|
||||
#: equalizer/equalizer.cpp:151
|
||||
msgid "Soft"
|
||||
msgstr "Soft"
|
||||
|
||||
#: equalizer/equalizer.cpp:155
|
||||
#: equalizer/equalizer.cpp:153
|
||||
msgid "Soft Rock"
|
||||
msgstr "Soft rock"
|
||||
|
||||
@@ -3854,7 +3862,7 @@ msgstr ""
|
||||
msgid "Strawberry was unable to find results for this file"
|
||||
msgstr "Strawberry no encontró resultados para este archivo"
|
||||
|
||||
#: core/song.cpp:483
|
||||
#: core/song.cpp:476
|
||||
msgid "Stream"
|
||||
msgstr "Transmisión"
|
||||
|
||||
@@ -3900,7 +3908,7 @@ msgstr "Etiquetas sugeridas"
|
||||
msgid "Summary"
|
||||
msgstr "Resumen"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:76
|
||||
#: analyzer/analyzercontainer.cpp:75
|
||||
#, qt-format
|
||||
msgid "Super high (%1 fps)"
|
||||
msgstr "Muy alta (%1 fps)"
|
||||
@@ -3929,7 +3937,7 @@ msgstr "Obtener etiquetas"
|
||||
msgid "Target bitrate"
|
||||
msgstr "Tasa de bits objetivo"
|
||||
|
||||
#: equalizer/equalizer.cpp:156
|
||||
#: equalizer/equalizer.cpp:154
|
||||
msgid "Techno"
|
||||
msgstr "Techno"
|
||||
|
||||
@@ -4093,7 +4101,7 @@ msgstr ""
|
||||
msgid "This type of device is not supported: %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
|
||||
msgid "Tidal"
|
||||
msgstr "Tidal"
|
||||
@@ -4195,11 +4203,11 @@ msgstr "UUID"
|
||||
msgid "Ultra wide band (UWB)"
|
||||
msgstr "Banda ultraancha (UWB)"
|
||||
|
||||
#: core/song.cpp:487 core/song.cpp:489 core/song.cpp:533
|
||||
#: context/contextalbumsmodel.cpp:385 collection/collectionmodel.cpp:398
|
||||
#: collection/collectionmodel.cpp:403 collection/collectionmodel.cpp:407
|
||||
#: collection/collectionmodel.cpp:411 collection/collectionmodel.cpp:415
|
||||
#: collection/collectionmodel.cpp:1362 collection/savedgroupingmanager.cpp:137
|
||||
#: core/song.cpp:480 core/song.cpp:482 core/song.cpp:526
|
||||
#: context/contextalbumsmodel.cpp:367 collection/collectionmodel.cpp:399
|
||||
#: collection/collectionmodel.cpp:404 collection/collectionmodel.cpp:408
|
||||
#: collection/collectionmodel.cpp:412 collection/collectionmodel.cpp:416
|
||||
#: collection/collectionmodel.cpp:1342 collection/savedgroupingmanager.cpp:137
|
||||
#: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542
|
||||
#: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490
|
||||
#: dialogs/edittagdialog.cpp:538
|
||||
@@ -4360,7 +4368,7 @@ msgstr "Al usar el menú para añadir una canción…"
|
||||
msgid "Variable bit rate"
|
||||
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
|
||||
#: internet/internetsearchmodel.cpp:106
|
||||
msgid "Various artists"
|
||||
@@ -4502,7 +4510,7 @@ msgid "You can change the way the songs in the collection are organised."
|
||||
msgstr ""
|
||||
"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."
|
||||
msgstr "Necesitas GStreamer para esta URL"
|
||||
|
||||
@@ -4540,7 +4548,7 @@ msgstr "Necesitará reiniciar Strawberry si cambia el idioma."
|
||||
msgid "Your collection is empty!"
|
||||
msgstr "La colección está vacía."
|
||||
|
||||
#: equalizer/equalizer.cpp:157
|
||||
#: equalizer/equalizer.cpp:155
|
||||
msgid "Zero"
|
||||
msgstr "Cero"
|
||||
|
||||
@@ -4620,7 +4628,7 @@ msgstr "opciones"
|
||||
msgid "p&lughw"
|
||||
msgstr "p&lughw"
|
||||
|
||||
#: core/song.cpp:486
|
||||
#: core/song.cpp:479
|
||||
msgid "qobuz"
|
||||
msgstr "qobuz"
|
||||
|
||||
@@ -4646,7 +4654,7 @@ msgstr "ordenar canciones"
|
||||
msgid "stop"
|
||||
msgstr "detener"
|
||||
|
||||
#: core/song.cpp:485
|
||||
#: core/song.cpp:478
|
||||
msgid "subsonic"
|
||||
msgstr "subsonic"
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ msgstr " s"
|
||||
msgid " seconds"
|
||||
msgstr " sekunder"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:145 scrobbler/scrobblingapi20.cpp:154
|
||||
#: scrobbler/scrobblingapi20.cpp:150 scrobbler/scrobblingapi20.cpp:159
|
||||
#, qt-format
|
||||
msgid "%1 Scrobbler Authentication"
|
||||
msgstr "%1 Scrobbler bruker autentisering"
|
||||
@@ -803,7 +803,7 @@ msgstr "Utseende"
|
||||
msgid "Append files/URLs to the playlist"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228
|
||||
msgid "Append to current playlist"
|
||||
@@ -817,7 +817,7 @@ msgstr "Legg til i spillelista"
|
||||
msgid "Apply compression to prevent clipping"
|
||||
msgstr "Legg til kompressor, for å unngå klipping"
|
||||
|
||||
#: equalizer/equalizer.cpp:229
|
||||
#: equalizer/equalizer.cpp:227
|
||||
#, qt-format
|
||||
msgid "Are you sure you want to delete the \"%1\" preset?"
|
||||
msgstr "Er du sikker på at du vil slette \"%1\"-forhåndsinnstillingen?"
|
||||
@@ -1020,7 +1020,7 @@ msgstr "Buffer"
|
||||
msgid "Buffer duration"
|
||||
msgstr "Mellomlagringslengde"
|
||||
|
||||
#: engine/gstengine.cpp:622
|
||||
#: engine/gstengine.cpp:636
|
||||
msgid "Buffering"
|
||||
msgstr "Mellomlagring"
|
||||
|
||||
@@ -1028,11 +1028,11 @@ msgstr "Mellomlagring"
|
||||
msgid "C&onsole"
|
||||
msgstr "K&onsoll"
|
||||
|
||||
#: core/song.cpp:481
|
||||
#: core/song.cpp:474
|
||||
msgid "CD"
|
||||
msgstr "CD"
|
||||
|
||||
#: core/songloader.cpp:195
|
||||
#: core/songloader.cpp:196
|
||||
msgid "CD playback is only available with the GStreamer engine."
|
||||
msgstr "CD avspilling er kun mulig med gstreamer"
|
||||
|
||||
@@ -1068,7 +1068,7 @@ msgstr "Velg farge…"
|
||||
msgid "Choose font..."
|
||||
msgstr "Velg skrifttype…"
|
||||
|
||||
#: equalizer/equalizer.cpp:140
|
||||
#: equalizer/equalizer.cpp:138
|
||||
msgid "Classical"
|
||||
msgstr "Klassisk"
|
||||
|
||||
@@ -1117,11 +1117,11 @@ msgstr "Lukk spillelista"
|
||||
msgid "Closing this window will stop searching for album covers."
|
||||
msgstr "Lukking av dette vinduet vil medføre stopp i søk etter albumomslag."
|
||||
|
||||
#: equalizer/equalizer.cpp:141
|
||||
#: equalizer/equalizer.cpp:139
|
||||
msgid "Club"
|
||||
msgstr "Klubbmusikk"
|
||||
|
||||
#: core/mainwindow.cpp:276 core/song.cpp:480
|
||||
#: core/mainwindow.cpp:276 core/song.cpp:473
|
||||
#: ../build/src/ui_collectionsettingspage.h:244
|
||||
msgid "Collection"
|
||||
msgstr "Samling"
|
||||
@@ -1222,7 +1222,7 @@ msgstr "Kopier album omslaggrafikk"
|
||||
msgid "Copy to collection..."
|
||||
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
|
||||
#: widgets/fileviewlist.cpp:49
|
||||
msgid "Copy to device..."
|
||||
@@ -1241,7 +1241,7 @@ msgstr ""
|
||||
"Kunne ikke opprette GStreamer-elementet \"%1\" - sørg for at du har alle "
|
||||
"nødvendige GStreamer-programutvidelser installert"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:154
|
||||
#: scrobbler/scrobblingapi20.cpp:159
|
||||
#, qt-format
|
||||
msgid ""
|
||||
"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 "
|
||||
"href=\"%1\">%1</a>"
|
||||
|
||||
#: core/songloader.cpp:260
|
||||
#: core/songloader.cpp:261
|
||||
#, qt-format
|
||||
msgid "Could not open file %1"
|
||||
msgstr "Kan ikke åpne fil %1"
|
||||
|
||||
#: core/songloader.cpp:436
|
||||
#: core/songloader.cpp:437
|
||||
#, qt-format
|
||||
msgid "Couldn't create gstreamer source element for %1"
|
||||
msgstr "Kunne ikke opprette gstreamer kilde for %1"
|
||||
@@ -1387,7 +1387,7 @@ msgstr "Ctrl+T"
|
||||
msgid "Ctrl+Up"
|
||||
msgstr "Ctrl+Opp"
|
||||
|
||||
#: equalizer/equalizer.cpp:139
|
||||
#: equalizer/equalizer.cpp:137
|
||||
msgid "Custom"
|
||||
msgstr "Egendefinert"
|
||||
|
||||
@@ -1407,7 +1407,7 @@ msgstr "Egendefinert…"
|
||||
msgid "D-Bus path"
|
||||
msgstr "D-Bus sti"
|
||||
|
||||
#: equalizer/equalizer.cpp:142
|
||||
#: equalizer/equalizer.cpp:140
|
||||
msgid "Dance"
|
||||
msgstr "Dansemusikk"
|
||||
|
||||
@@ -1452,7 +1452,7 @@ msgstr "Slett fra enhet…"
|
||||
msgid "Delete from 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"
|
||||
msgstr "Slett forhåndsinnstilling"
|
||||
|
||||
@@ -1480,7 +1480,7 @@ msgstr "Mål"
|
||||
msgid "Details..."
|
||||
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_backendsettingspage.h:428
|
||||
msgid "Device"
|
||||
@@ -1606,12 +1606,12 @@ msgstr "Rediger tagger"
|
||||
msgid "Edit track information"
|
||||
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
|
||||
msgid "Edit track information..."
|
||||
msgstr "Rediger spor informasjon…"
|
||||
|
||||
#: context/contextalbumsview.cpp:373 collection/collectionview.cpp:350
|
||||
#: context/contextalbumsview.cpp:286 collection/collectionview.cpp:350
|
||||
msgid "Edit tracks information..."
|
||||
msgstr "Rediger spor informasjon…"
|
||||
|
||||
@@ -1704,7 +1704,7 @@ msgid "Equivalent to --log-levels *:3"
|
||||
msgstr "Tilsvarer --log-levels *:3"
|
||||
|
||||
#: 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"
|
||||
msgstr "Feil"
|
||||
|
||||
@@ -1840,11 +1840,11 @@ msgstr "Hent hele album når en søker etter sanger"
|
||||
msgid "Fetching cover error"
|
||||
msgstr "Kunne ikke hente albumgrafikk"
|
||||
|
||||
#: core/song.cpp:479
|
||||
#: core/song.cpp:472
|
||||
msgid "File"
|
||||
msgstr "Fil"
|
||||
|
||||
#: core/song.cpp:1006
|
||||
#: core/song.cpp:999
|
||||
#, qt-format
|
||||
msgid "File %1 is not recognized as a valid audio file."
|
||||
msgstr "Fil %1 er ikke gjenkjent som en lydfil"
|
||||
@@ -1963,15 +1963,15 @@ msgstr "Bilder per buffer"
|
||||
msgid "Frozen"
|
||||
msgstr "Frossen"
|
||||
|
||||
#: equalizer/equalizer.cpp:143
|
||||
#: equalizer/equalizer.cpp:141
|
||||
msgid "Full Bass"
|
||||
msgstr "Full bass"
|
||||
|
||||
#: equalizer/equalizer.cpp:145
|
||||
#: equalizer/equalizer.cpp:143
|
||||
msgid "Full Bass + Treble"
|
||||
msgstr "Full bass + diskant"
|
||||
|
||||
#: equalizer/equalizer.cpp:144
|
||||
#: equalizer/equalizer.cpp:142
|
||||
msgid "Full Treble"
|
||||
msgstr "Full diskant"
|
||||
|
||||
@@ -2121,7 +2121,7 @@ msgstr "Hjelp"
|
||||
msgid "High"
|
||||
msgstr "Høy"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:75
|
||||
#: analyzer/analyzercontainer.cpp:74
|
||||
#, qt-format
|
||||
msgid "High (%1 fps)"
|
||||
msgstr "Høy (%1 bilder/sekund)"
|
||||
@@ -2202,7 +2202,7 @@ msgstr "Integritetskontrol"
|
||||
msgid "Intro tracks"
|
||||
msgstr "Introspor"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:190
|
||||
#: scrobbler/scrobblingapi20.cpp:194
|
||||
msgid "Invalid reply from web browser. Missing token."
|
||||
msgstr "Ugyldig svar fra nettleseren. Mangler token."
|
||||
|
||||
@@ -2234,11 +2234,11 @@ msgstr "Behold originalfilene"
|
||||
msgid "Language"
|
||||
msgstr "Språk"
|
||||
|
||||
#: equalizer/equalizer.cpp:146
|
||||
#: equalizer/equalizer.cpp:144
|
||||
msgid "Laptop/Headphones"
|
||||
msgstr "Laptop/hodetelefoner"
|
||||
|
||||
#: equalizer/equalizer.cpp:147
|
||||
#: equalizer/equalizer.cpp:145
|
||||
msgid "Large Hall"
|
||||
msgstr "Storsal"
|
||||
|
||||
@@ -2277,7 +2277,7 @@ msgstr "Lengde"
|
||||
msgid "Libre.fm"
|
||||
msgstr "Libre.fm"
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:147
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:152
|
||||
msgid "ListenBrainz Authentication"
|
||||
msgstr "ListenBrainz autentisering"
|
||||
|
||||
@@ -2285,7 +2285,7 @@ msgstr "ListenBrainz autentisering"
|
||||
msgid "Listenbrainz"
|
||||
msgstr "Listenbrainz"
|
||||
|
||||
#: equalizer/equalizer.cpp:148
|
||||
#: equalizer/equalizer.cpp:146
|
||||
msgid "Live"
|
||||
msgstr "Live"
|
||||
|
||||
@@ -2321,7 +2321,7 @@ msgstr "Åpner MTP-enhet"
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Åpner iPod-database"
|
||||
|
||||
#: collection/collectionmodel.cpp:179
|
||||
#: collection/collectionmodel.cpp:180
|
||||
msgid "Loading songs"
|
||||
msgstr "Åpner sanger"
|
||||
|
||||
@@ -2333,7 +2333,7 @@ msgstr "Åpner spor"
|
||||
msgid "Loading tracks info"
|
||||
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..."
|
||||
msgstr "Åpner…"
|
||||
|
||||
@@ -2357,7 +2357,7 @@ msgstr "Profil for langtidspredikie (LTP)"
|
||||
msgid "Love"
|
||||
msgstr ""
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:73
|
||||
#: analyzer/analyzercontainer.cpp:72
|
||||
#, qt-format
|
||||
msgid "Low (%1 fps)"
|
||||
msgstr "Lav (%1 bilder/sekund)"
|
||||
@@ -2414,7 +2414,7 @@ msgstr "Høyeste bitrate"
|
||||
msgid "Maximum number of login attempts reached."
|
||||
msgstr ""
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:74
|
||||
#: analyzer/analyzercontainer.cpp:73
|
||||
#, qt-format
|
||||
msgid "Medium (%1 fps)"
|
||||
msgstr "Medium (%1 bilder/sekund)"
|
||||
@@ -2540,7 +2540,7 @@ msgstr "Musikk"
|
||||
msgid "Mute"
|
||||
msgstr "Demp"
|
||||
|
||||
#: equalizer/equalizer.cpp:211 collection/savedgroupingmanager.cpp:55
|
||||
#: equalizer/equalizer.cpp:209 collection/savedgroupingmanager.cpp:55
|
||||
#: ../build/src/ui_deviceproperties.h:368
|
||||
msgid "Name"
|
||||
msgstr "Navn"
|
||||
@@ -2587,7 +2587,7 @@ msgstr "Neste"
|
||||
msgid "Next week"
|
||||
msgstr "Neste uke"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:86
|
||||
#: analyzer/analyzercontainer.cpp:85
|
||||
msgid "No analyzer"
|
||||
msgstr "Ingen analyse"
|
||||
|
||||
@@ -2622,7 +2622,7 @@ msgid "None"
|
||||
msgstr "Ingen"
|
||||
|
||||
#: 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"
|
||||
msgstr "Kunne ikke kopiere noen av de valgte sangene til en enhet"
|
||||
|
||||
@@ -2694,7 +2694,7 @@ msgstr "Ved oppstart"
|
||||
msgid "Opacity"
|
||||
msgstr "Dekkevne"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:145
|
||||
#: scrobbler/scrobblingapi20.cpp:150
|
||||
#, qt-format
|
||||
msgid ""
|
||||
"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"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230
|
||||
#: ../build/src/ui_behavioursettingspage.h:274
|
||||
@@ -2747,7 +2747,7 @@ msgstr "Opus"
|
||||
msgid "Organise Files"
|
||||
msgstr "Organiser filer"
|
||||
|
||||
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:366
|
||||
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:279
|
||||
#: collection/collectionview.cpp:342
|
||||
msgid "Organise files..."
|
||||
msgstr "Organiser filer…"
|
||||
@@ -2804,7 +2804,7 @@ msgstr "Spilleliste"
|
||||
msgid "Partition label"
|
||||
msgstr "Partisjonsnavn"
|
||||
|
||||
#: equalizer/equalizer.cpp:149
|
||||
#: equalizer/equalizer.cpp:147
|
||||
msgid "Party"
|
||||
msgstr "Fest"
|
||||
|
||||
@@ -2898,12 +2898,12 @@ msgstr "Spillelister"
|
||||
msgid "Please close your browser and return to Strawberry."
|
||||
msgstr "Lukk nettleseren og gå tilbake til Strawberry."
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:147
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:152
|
||||
#, qt-format
|
||||
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>"
|
||||
|
||||
#: equalizer/equalizer.cpp:150
|
||||
#: equalizer/equalizer.cpp:148
|
||||
msgid "Pop"
|
||||
msgstr "Pop"
|
||||
|
||||
@@ -2941,7 +2941,7 @@ msgstr "Foretrukne filnavn for omslag (inndelt med komma)"
|
||||
msgid "Preferred format"
|
||||
msgstr "Foretrukket format"
|
||||
|
||||
#: core/songloader.cpp:156
|
||||
#: core/songloader.cpp:157
|
||||
msgid "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"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:307
|
||||
msgid "Queue track"
|
||||
@@ -3047,7 +3047,11 @@ msgstr "Relativ"
|
||||
msgid "Really cancel?"
|
||||
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 ""
|
||||
"Received invalid reply from web browser. Try the HTTPS option, or use "
|
||||
"another browser like Chromium or Chrome."
|
||||
@@ -3055,11 +3059,15 @@ msgstr ""
|
||||
"Mottok ugyldig svar fra nettleseren. Prøv HTTPS valget, eller bruk en annen "
|
||||
"nettleser som Chromium eller Chrome."
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:174
|
||||
msgid "Redirect missing token code!"
|
||||
msgstr ""
|
||||
|
||||
#: ../build/src/ui_internetcollectionviewcontainer.h:142
|
||||
msgid "Refresh catalogue"
|
||||
msgstr ""
|
||||
|
||||
#: equalizer/equalizer.cpp:151
|
||||
#: equalizer/equalizer.cpp:149
|
||||
msgid "Reggae"
|
||||
msgstr "Reggae"
|
||||
|
||||
@@ -3136,7 +3144,7 @@ msgstr "Gjenta spilleliste"
|
||||
msgid "Repeat track"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229
|
||||
msgid "Replace current playlist"
|
||||
@@ -3260,7 +3268,7 @@ msgstr "Gå tilbake til Strawberry"
|
||||
msgid "Right"
|
||||
msgstr "Høyre"
|
||||
|
||||
#: equalizer/equalizer.cpp:152
|
||||
#: equalizer/equalizer.cpp:150
|
||||
msgid "Rock"
|
||||
msgstr "Rock"
|
||||
|
||||
@@ -3329,7 +3337,7 @@ msgstr "Lagre spilleliste"
|
||||
msgid "Save playlist..."
|
||||
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"
|
||||
msgstr "Lagre forhåndsinnstilling"
|
||||
|
||||
@@ -3361,7 +3369,7 @@ msgstr "Skaler størrelse"
|
||||
msgid "Scrobbler"
|
||||
msgstr "Scrobbler"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:478
|
||||
#: scrobbler/scrobblingapi20.cpp:495
|
||||
#, qt-format
|
||||
msgid "Scrobbler %1 is not authenticated!"
|
||||
msgstr "Scrobbler %1 er ikke autentisert!"
|
||||
@@ -3571,7 +3579,7 @@ msgstr "Vis i samling…"
|
||||
msgid "Show in file browser"
|
||||
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
|
||||
msgid "Show in file browser..."
|
||||
msgstr "Vis i filbehandler…"
|
||||
@@ -3648,7 +3656,7 @@ msgstr "Størrelse"
|
||||
msgid "Size:"
|
||||
msgstr "Størrelse:"
|
||||
|
||||
#: equalizer/equalizer.cpp:154
|
||||
#: equalizer/equalizer.cpp:152
|
||||
msgid "Ska"
|
||||
msgstr "Ska"
|
||||
|
||||
@@ -3680,11 +3688,11 @@ msgstr "Lite albumomslag"
|
||||
msgid "Small sidebar"
|
||||
msgstr "Lite sidefelt"
|
||||
|
||||
#: equalizer/equalizer.cpp:153
|
||||
#: equalizer/equalizer.cpp:151
|
||||
msgid "Soft"
|
||||
msgstr "Myk"
|
||||
|
||||
#: equalizer/equalizer.cpp:155
|
||||
#: equalizer/equalizer.cpp:153
|
||||
msgid "Soft Rock"
|
||||
msgstr "Soft rock"
|
||||
|
||||
@@ -3807,7 +3815,7 @@ msgstr ""
|
||||
msgid "Strawberry was unable to find results for this file"
|
||||
msgstr "Strawberry klarte ikke å finne resultater for denne filen"
|
||||
|
||||
#: core/song.cpp:483
|
||||
#: core/song.cpp:476
|
||||
msgid "Stream"
|
||||
msgstr "Strøm"
|
||||
|
||||
@@ -3853,7 +3861,7 @@ msgstr "Foreslåtte etiketter"
|
||||
msgid "Summary"
|
||||
msgstr "Sammendrag"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:76
|
||||
#: analyzer/analyzercontainer.cpp:75
|
||||
#, qt-format
|
||||
msgid "Super high (%1 fps)"
|
||||
msgstr "Superhøy (%1 bilder/sek)"
|
||||
@@ -3882,7 +3890,7 @@ msgstr "Etikett-henter"
|
||||
msgid "Target bitrate"
|
||||
msgstr "Ønsket bitrate"
|
||||
|
||||
#: equalizer/equalizer.cpp:156
|
||||
#: equalizer/equalizer.cpp:154
|
||||
msgid "Techno"
|
||||
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"
|
||||
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
|
||||
msgid "Tidal"
|
||||
msgstr "Tidal"
|
||||
@@ -4141,11 +4149,11 @@ msgstr "UUID"
|
||||
msgid "Ultra wide band (UWB)"
|
||||
msgstr "Ultrabredt bånd (UWB)"
|
||||
|
||||
#: core/song.cpp:487 core/song.cpp:489 core/song.cpp:533
|
||||
#: context/contextalbumsmodel.cpp:385 collection/collectionmodel.cpp:398
|
||||
#: collection/collectionmodel.cpp:403 collection/collectionmodel.cpp:407
|
||||
#: collection/collectionmodel.cpp:411 collection/collectionmodel.cpp:415
|
||||
#: collection/collectionmodel.cpp:1362 collection/savedgroupingmanager.cpp:137
|
||||
#: core/song.cpp:480 core/song.cpp:482 core/song.cpp:526
|
||||
#: context/contextalbumsmodel.cpp:367 collection/collectionmodel.cpp:399
|
||||
#: collection/collectionmodel.cpp:404 collection/collectionmodel.cpp:408
|
||||
#: collection/collectionmodel.cpp:412 collection/collectionmodel.cpp:416
|
||||
#: collection/collectionmodel.cpp:1342 collection/savedgroupingmanager.cpp:137
|
||||
#: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542
|
||||
#: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490
|
||||
#: dialogs/edittagdialog.cpp:538
|
||||
@@ -4303,7 +4311,7 @@ msgstr "Bruk av menyen for å legge til et spor vil…"
|
||||
msgid "Variable bit rate"
|
||||
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
|
||||
#: internet/internetsearchmodel.cpp:106
|
||||
msgid "Various artists"
|
||||
@@ -4441,7 +4449,7 @@ msgstr "Du er innlogget"
|
||||
msgid "You can change the way the songs in the collection are organised."
|
||||
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."
|
||||
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!"
|
||||
msgstr "Samlingen din er tom!"
|
||||
|
||||
#: equalizer/equalizer.cpp:157
|
||||
#: equalizer/equalizer.cpp:155
|
||||
msgid "Zero"
|
||||
msgstr "Null"
|
||||
|
||||
@@ -4558,7 +4566,7 @@ msgstr "innstillinger"
|
||||
msgid "p&lughw"
|
||||
msgstr "p&lughw"
|
||||
|
||||
#: core/song.cpp:486
|
||||
#: core/song.cpp:479
|
||||
msgid "qobuz"
|
||||
msgstr ""
|
||||
|
||||
@@ -4584,7 +4592,7 @@ msgstr "sorter sanger"
|
||||
msgid "stop"
|
||||
msgstr "stopp"
|
||||
|
||||
#: core/song.cpp:485
|
||||
#: core/song.cpp:478
|
||||
msgid "subsonic"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ msgstr " с"
|
||||
msgid " seconds"
|
||||
msgstr " секунд"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:145 scrobbler/scrobblingapi20.cpp:154
|
||||
#: scrobbler/scrobblingapi20.cpp:150 scrobbler/scrobblingapi20.cpp:159
|
||||
#, qt-format
|
||||
msgid "%1 Scrobbler Authentication"
|
||||
msgstr "Аутентификация скроблерра %1"
|
||||
@@ -827,7 +827,7 @@ msgstr "Внешний вид"
|
||||
msgid "Append files/URLs to the playlist"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:302 device/deviceview.cpp:228
|
||||
msgid "Append to current playlist"
|
||||
@@ -841,7 +841,7 @@ msgstr "Добавить в плейлист"
|
||||
msgid "Apply compression to prevent clipping"
|
||||
msgstr "Применить сжатие для предотвращения искажений"
|
||||
|
||||
#: equalizer/equalizer.cpp:229
|
||||
#: equalizer/equalizer.cpp:227
|
||||
#, qt-format
|
||||
msgid "Are you sure you want to delete the \"%1\" preset?"
|
||||
msgstr "Вы действительно хотите удалить профиль \"%1\"?"
|
||||
@@ -1043,7 +1043,7 @@ msgstr "Буфер"
|
||||
msgid "Buffer duration"
|
||||
msgstr "Размер буфера"
|
||||
|
||||
#: engine/gstengine.cpp:622
|
||||
#: engine/gstengine.cpp:636
|
||||
msgid "Buffering"
|
||||
msgstr "Буферизация"
|
||||
|
||||
@@ -1051,11 +1051,11 @@ msgstr "Буферизация"
|
||||
msgid "C&onsole"
|
||||
msgstr "&Консоль"
|
||||
|
||||
#: core/song.cpp:481
|
||||
#: core/song.cpp:474
|
||||
msgid "CD"
|
||||
msgstr "CD"
|
||||
|
||||
#: core/songloader.cpp:195
|
||||
#: core/songloader.cpp:196
|
||||
msgid "CD playback is only available with the GStreamer engine."
|
||||
msgstr "Воспроизведение CD доступно только с движком GStreamer."
|
||||
|
||||
@@ -1091,7 +1091,7 @@ msgstr "Выбрать цвет…"
|
||||
msgid "Choose font..."
|
||||
msgstr "Выбрать шрифт…"
|
||||
|
||||
#: equalizer/equalizer.cpp:140
|
||||
#: equalizer/equalizer.cpp:138
|
||||
msgid "Classical"
|
||||
msgstr "Классический"
|
||||
|
||||
@@ -1141,11 +1141,11 @@ msgstr "Закрыть плейлист"
|
||||
msgid "Closing this window will stop searching for album covers."
|
||||
msgstr "Закрытие этого окна остановит поиск обложек для альбомов."
|
||||
|
||||
#: equalizer/equalizer.cpp:141
|
||||
#: equalizer/equalizer.cpp:139
|
||||
msgid "Club"
|
||||
msgstr "Клубный"
|
||||
|
||||
#: core/mainwindow.cpp:276 core/song.cpp:480
|
||||
#: core/mainwindow.cpp:276 core/song.cpp:473
|
||||
#: ../build/src/ui_collectionsettingspage.h:244
|
||||
msgid "Collection"
|
||||
msgstr "Фонотека"
|
||||
@@ -1247,7 +1247,7 @@ msgstr "Копировать обложку альбома"
|
||||
msgid "Copy to collection..."
|
||||
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
|
||||
#: widgets/fileviewlist.cpp:49
|
||||
msgid "Copy to device..."
|
||||
@@ -1266,7 +1266,7 @@ msgstr ""
|
||||
"Невозможно создать элемент GStreamer «%1» - убедитесь, что у вас установлены "
|
||||
"все необходимые дополнения GStreamer"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:154
|
||||
#: scrobbler/scrobblingapi20.cpp:159
|
||||
#, qt-format
|
||||
msgid ""
|
||||
"Could not open URL. Please open this URL in your browser:<br /><a href="
|
||||
@@ -1275,12 +1275,12 @@ msgstr ""
|
||||
"Не удалось открыть URL. Откройте этот URL в своем браузере:<br /><a href="
|
||||
"\"%1\">%1</a>"
|
||||
|
||||
#: core/songloader.cpp:260
|
||||
#: core/songloader.cpp:261
|
||||
#, qt-format
|
||||
msgid "Could not open file %1"
|
||||
msgstr "Не удалось открыть файл %1"
|
||||
|
||||
#: core/songloader.cpp:436
|
||||
#: core/songloader.cpp:437
|
||||
#, qt-format
|
||||
msgid "Couldn't create gstreamer source element for %1"
|
||||
msgstr "Не удалось создать исходный элемент gstreamer для %1"
|
||||
@@ -1412,7 +1412,7 @@ msgstr "Ctrl+T"
|
||||
msgid "Ctrl+Up"
|
||||
msgstr "Ctrl+Up"
|
||||
|
||||
#: equalizer/equalizer.cpp:139
|
||||
#: equalizer/equalizer.cpp:137
|
||||
msgid "Custom"
|
||||
msgstr "Пользовательский"
|
||||
|
||||
@@ -1432,7 +1432,7 @@ msgstr "Пользовательский…"
|
||||
msgid "D-Bus path"
|
||||
msgstr "Путь D-Bus"
|
||||
|
||||
#: equalizer/equalizer.cpp:142
|
||||
#: equalizer/equalizer.cpp:140
|
||||
msgid "Dance"
|
||||
msgstr "Танцевальный"
|
||||
|
||||
@@ -1477,7 +1477,7 @@ msgstr "Удалить с устройства..."
|
||||
msgid "Delete from disk..."
|
||||
msgstr "Удалить с диска…"
|
||||
|
||||
#: equalizer/equalizer.cpp:228 ../build/src/ui_equalizer.h:174
|
||||
#: equalizer/equalizer.cpp:226 ../build/src/ui_equalizer.h:174
|
||||
msgid "Delete preset"
|
||||
msgstr "Удалить профиль"
|
||||
|
||||
@@ -1505,7 +1505,7 @@ msgstr "Назначение"
|
||||
msgid "Details..."
|
||||
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_backendsettingspage.h:428
|
||||
msgid "Device"
|
||||
@@ -1631,12 +1631,12 @@ msgstr "Изменить теги"
|
||||
msgid "Edit track information"
|
||||
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
|
||||
msgid "Edit track information..."
|
||||
msgstr "Изменить информацию о треке"
|
||||
|
||||
#: context/contextalbumsview.cpp:373 collection/collectionview.cpp:350
|
||||
#: context/contextalbumsview.cpp:286 collection/collectionview.cpp:350
|
||||
msgid "Edit tracks information..."
|
||||
msgstr "Изменить информацию о треках"
|
||||
|
||||
@@ -1729,7 +1729,7 @@ msgid "Equivalent to --log-levels *:3"
|
||||
msgstr "Аналогично --log-levels *:3"
|
||||
|
||||
#: 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"
|
||||
msgstr "Ошибка"
|
||||
|
||||
@@ -1865,11 +1865,11 @@ msgstr "Получать весь альбом при поиске песен"
|
||||
msgid "Fetching cover error"
|
||||
msgstr "Ошибка получения обложки"
|
||||
|
||||
#: core/song.cpp:479
|
||||
#: core/song.cpp:472
|
||||
msgid "File"
|
||||
msgstr "Файл"
|
||||
|
||||
#: core/song.cpp:1006
|
||||
#: core/song.cpp:999
|
||||
#, qt-format
|
||||
msgid "File %1 is not recognized as a valid audio file."
|
||||
msgstr "Файл %1 не распознается как допустимый аудиофайл."
|
||||
@@ -1988,15 +1988,15 @@ msgstr "Фреймов на буфер"
|
||||
msgid "Frozen"
|
||||
msgstr "Замороженный"
|
||||
|
||||
#: equalizer/equalizer.cpp:143
|
||||
#: equalizer/equalizer.cpp:141
|
||||
msgid "Full Bass"
|
||||
msgstr "Бас"
|
||||
|
||||
#: equalizer/equalizer.cpp:145
|
||||
#: equalizer/equalizer.cpp:143
|
||||
msgid "Full Bass + Treble"
|
||||
msgstr "Бас + высокие частоты"
|
||||
|
||||
#: equalizer/equalizer.cpp:144
|
||||
#: equalizer/equalizer.cpp:142
|
||||
msgid "Full Treble"
|
||||
msgstr "Высокие частоты"
|
||||
|
||||
@@ -2148,7 +2148,7 @@ msgstr "Помощь"
|
||||
msgid "High"
|
||||
msgstr "Высокое"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:75
|
||||
#: analyzer/analyzercontainer.cpp:74
|
||||
#, qt-format
|
||||
msgid "High (%1 fps)"
|
||||
msgstr "Высокая (%1 fps)"
|
||||
@@ -2232,7 +2232,7 @@ msgstr "Проверка целостности"
|
||||
msgid "Intro tracks"
|
||||
msgstr "Вступительные треки"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:190
|
||||
#: scrobbler/scrobblingapi20.cpp:194
|
||||
msgid "Invalid reply from web browser. Missing token."
|
||||
msgstr "Неверный ответ от веб-браузера. Отсутствует токен."
|
||||
|
||||
@@ -2264,11 +2264,11 @@ msgstr "Сохранять оригинальные файлы"
|
||||
msgid "Language"
|
||||
msgstr "Язык"
|
||||
|
||||
#: equalizer/equalizer.cpp:146
|
||||
#: equalizer/equalizer.cpp:144
|
||||
msgid "Laptop/Headphones"
|
||||
msgstr "Наушники/ноутбук"
|
||||
|
||||
#: equalizer/equalizer.cpp:147
|
||||
#: equalizer/equalizer.cpp:145
|
||||
msgid "Large Hall"
|
||||
msgstr "Большой зал"
|
||||
|
||||
@@ -2307,7 +2307,7 @@ msgstr "Длина"
|
||||
msgid "Libre.fm"
|
||||
msgstr "Libre.fm"
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:147
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:152
|
||||
msgid "ListenBrainz Authentication"
|
||||
msgstr "Аутентификация ListenBrainz"
|
||||
|
||||
@@ -2315,7 +2315,7 @@ msgstr "Аутентификация ListenBrainz"
|
||||
msgid "Listenbrainz"
|
||||
msgstr "Listenbrainz"
|
||||
|
||||
#: equalizer/equalizer.cpp:148
|
||||
#: equalizer/equalizer.cpp:146
|
||||
msgid "Live"
|
||||
msgstr "Лайв"
|
||||
|
||||
@@ -2351,7 +2351,7 @@ msgstr "Загрузка устройства MTP"
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Загрузка база данных iPod"
|
||||
|
||||
#: collection/collectionmodel.cpp:179
|
||||
#: collection/collectionmodel.cpp:180
|
||||
msgid "Loading songs"
|
||||
msgstr "Загрузка песен"
|
||||
|
||||
@@ -2363,7 +2363,7 @@ msgstr "Загрузка композиций"
|
||||
msgid "Loading tracks info"
|
||||
msgstr "Загрузка информации о треках"
|
||||
|
||||
#: collection/collectionmodel.cpp:172 ../build/src/ui_organisedialog.h:271
|
||||
#: collection/collectionmodel.cpp:173 ../build/src/ui_organisedialog.h:271
|
||||
msgid "Loading..."
|
||||
msgstr "Загрузка…"
|
||||
|
||||
@@ -2387,7 +2387,7 @@ msgstr "Профиль Long term prediction (LTP)"
|
||||
msgid "Love"
|
||||
msgstr "Нравится"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:73
|
||||
#: analyzer/analyzercontainer.cpp:72
|
||||
#, qt-format
|
||||
msgid "Low (%1 fps)"
|
||||
msgstr "Низкая (%1 fps)"
|
||||
@@ -2444,7 +2444,7 @@ msgstr "Максимальный битрейт"
|
||||
msgid "Maximum number of login attempts reached."
|
||||
msgstr "Достигнуто максимальное количество попыток входа в систему."
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:74
|
||||
#: analyzer/analyzercontainer.cpp:73
|
||||
#, qt-format
|
||||
msgid "Medium (%1 fps)"
|
||||
msgstr "Средняя (%1 fps)"
|
||||
@@ -2570,7 +2570,7 @@ msgstr "Музыка"
|
||||
msgid "Mute"
|
||||
msgstr "Приглушить звук"
|
||||
|
||||
#: equalizer/equalizer.cpp:211 collection/savedgroupingmanager.cpp:55
|
||||
#: equalizer/equalizer.cpp:209 collection/savedgroupingmanager.cpp:55
|
||||
#: ../build/src/ui_deviceproperties.h:368
|
||||
msgid "Name"
|
||||
msgstr "Имя"
|
||||
@@ -2617,7 +2617,7 @@ msgstr "Дальше"
|
||||
msgid "Next week"
|
||||
msgstr "На следующей неделе"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:86
|
||||
#: analyzer/analyzercontainer.cpp:85
|
||||
msgid "No analyzer"
|
||||
msgstr "Без анализатора"
|
||||
|
||||
@@ -2653,7 +2653,7 @@ msgid "None"
|
||||
msgstr "Ничего"
|
||||
|
||||
#: 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"
|
||||
msgstr "Ни одна из выбранных песен не была скопирована на устройство"
|
||||
|
||||
@@ -2727,7 +2727,7 @@ msgstr "При запуске"
|
||||
msgid "Opacity"
|
||||
msgstr "Непрозрачность"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:145
|
||||
#: scrobbler/scrobblingapi20.cpp:150
|
||||
#, qt-format
|
||||
msgid ""
|
||||
"Open URL in web browser?<br /><a href=\"%1\">%1</a><br />Press \"Save\" to "
|
||||
@@ -2749,7 +2749,7 @@ msgstr "Открыть аудио &CD..."
|
||||
msgid "Open device"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:304 device/deviceview.cpp:230
|
||||
#: ../build/src/ui_behavioursettingspage.h:274
|
||||
@@ -2781,7 +2781,7 @@ msgstr "Opus"
|
||||
msgid "Organise Files"
|
||||
msgstr "Упорядочить файлы"
|
||||
|
||||
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:366
|
||||
#: core/mainwindow.cpp:625 context/contextalbumsview.cpp:279
|
||||
#: collection/collectionview.cpp:342
|
||||
msgid "Organise files..."
|
||||
msgstr "Упорядочить файлы…"
|
||||
@@ -2838,7 +2838,7 @@ msgstr "&Плейлист"
|
||||
msgid "Partition label"
|
||||
msgstr "Метка раздела"
|
||||
|
||||
#: equalizer/equalizer.cpp:149
|
||||
#: equalizer/equalizer.cpp:147
|
||||
msgid "Party"
|
||||
msgstr "Вечеринка"
|
||||
|
||||
@@ -2932,12 +2932,12 @@ msgstr "Плейлисты"
|
||||
msgid "Please close your browser and return to Strawberry."
|
||||
msgstr "Пожалуйста, закройте браузер и вернитесь в Strawberry."
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:147
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:152
|
||||
#, qt-format
|
||||
msgid "Please open this URL in your browser:<br /><a href=\"%1\">%1</a>"
|
||||
msgstr "Откройте этот URL в своем браузере:<br /><a href=\"%1\">%1</a>"
|
||||
|
||||
#: equalizer/equalizer.cpp:150
|
||||
#: equalizer/equalizer.cpp:148
|
||||
msgid "Pop"
|
||||
msgstr "Поп"
|
||||
|
||||
@@ -2975,7 +2975,7 @@ msgstr "Приоритетные имена файлов обложек (чер
|
||||
msgid "Preferred format"
|
||||
msgstr "Предпочитаемый формат"
|
||||
|
||||
#: core/songloader.cpp:156
|
||||
#: core/songloader.cpp:157
|
||||
msgid "Preload function was not set for blocking operation."
|
||||
msgstr ""
|
||||
"Функция предварительной нагрузки не была установлена для операции "
|
||||
@@ -3061,7 +3061,7 @@ msgstr "Очередь выбранных треков для последующ
|
||||
msgid "Queue to play next"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:307
|
||||
msgid "Queue track"
|
||||
@@ -3083,7 +3083,11 @@ msgstr "&Относительно"
|
||||
msgid "Really cancel?"
|
||||
msgstr "Действительно отменить?"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:184
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:178
|
||||
msgid "Received invalid reply from web browser."
|
||||
msgstr ""
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:198
|
||||
msgid ""
|
||||
"Received invalid reply from web browser. Try the HTTPS option, or use "
|
||||
"another browser like Chromium or Chrome."
|
||||
@@ -3091,11 +3095,15 @@ msgstr ""
|
||||
"Получен неверный ответ от веб-браузера. Попробуйте опцию HTTPS или "
|
||||
"используйте другой браузер, такой как Chromium или Chrome."
|
||||
|
||||
#: scrobbler/listenbrainzscrobbler.cpp:174
|
||||
msgid "Redirect missing token code!"
|
||||
msgstr ""
|
||||
|
||||
#: ../build/src/ui_internetcollectionviewcontainer.h:142
|
||||
msgid "Refresh catalogue"
|
||||
msgstr "Обновить каталог"
|
||||
|
||||
#: equalizer/equalizer.cpp:151
|
||||
#: equalizer/equalizer.cpp:149
|
||||
msgid "Reggae"
|
||||
msgstr "Регги"
|
||||
|
||||
@@ -3172,7 +3180,7 @@ msgstr "Повторять плейлист"
|
||||
msgid "Repeat track"
|
||||
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
|
||||
#: internet/internetcollectionview.cpp:303 device/deviceview.cpp:229
|
||||
msgid "Replace current playlist"
|
||||
@@ -3296,7 +3304,7 @@ msgstr "Вернуться в Strawberry"
|
||||
msgid "Right"
|
||||
msgstr "Правый канал"
|
||||
|
||||
#: equalizer/equalizer.cpp:152
|
||||
#: equalizer/equalizer.cpp:150
|
||||
msgid "Rock"
|
||||
msgstr "Рок"
|
||||
|
||||
@@ -3365,7 +3373,7 @@ msgstr "Сохранить плейлист"
|
||||
msgid "Save playlist..."
|
||||
msgstr "Сохранить плейлист…"
|
||||
|
||||
#: equalizer/equalizer.cpp:211 ../build/src/ui_equalizer.h:171
|
||||
#: equalizer/equalizer.cpp:209 ../build/src/ui_equalizer.h:171
|
||||
msgid "Save preset"
|
||||
msgstr "Сохранить профиль"
|
||||
|
||||
@@ -3397,7 +3405,7 @@ msgstr "Размер масштабирования"
|
||||
msgid "Scrobbler"
|
||||
msgstr "Скробблер"
|
||||
|
||||
#: scrobbler/scrobblingapi20.cpp:478
|
||||
#: scrobbler/scrobblingapi20.cpp:495
|
||||
#, qt-format
|
||||
msgid "Scrobbler %1 is not authenticated!"
|
||||
msgstr "Скробблер %1 не аутентифицирован!"
|
||||
@@ -3607,7 +3615,7 @@ msgstr "Показать в фонотеке…"
|
||||
msgid "Show in file browser"
|
||||
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
|
||||
msgid "Show in file browser..."
|
||||
msgstr "Открыть в диспетчере файлов"
|
||||
@@ -3684,7 +3692,7 @@ msgstr "Размер"
|
||||
msgid "Size:"
|
||||
msgstr "Размер:"
|
||||
|
||||
#: equalizer/equalizer.cpp:154
|
||||
#: equalizer/equalizer.cpp:152
|
||||
msgid "Ska"
|
||||
msgstr "Ска"
|
||||
|
||||
@@ -3716,11 +3724,11 @@ msgstr "Маленькая обложка альбома"
|
||||
msgid "Small sidebar"
|
||||
msgstr "Узкая боковая панель"
|
||||
|
||||
#: equalizer/equalizer.cpp:153
|
||||
#: equalizer/equalizer.cpp:151
|
||||
msgid "Soft"
|
||||
msgstr "Мягкий"
|
||||
|
||||
#: equalizer/equalizer.cpp:155
|
||||
#: equalizer/equalizer.cpp:153
|
||||
msgid "Soft Rock"
|
||||
msgstr "Софт-рок"
|
||||
|
||||
@@ -3847,7 +3855,7 @@ msgstr ""
|
||||
msgid "Strawberry was unable to find results for this file"
|
||||
msgstr "Strawberry не смог найти результаты по запросу для этого файла"
|
||||
|
||||
#: core/song.cpp:483
|
||||
#: core/song.cpp:476
|
||||
msgid "Stream"
|
||||
msgstr "Поток"
|
||||
|
||||
@@ -3893,7 +3901,7 @@ msgstr "Предлагаемые теги"
|
||||
msgid "Summary"
|
||||
msgstr "Сводка"
|
||||
|
||||
#: analyzer/analyzercontainer.cpp:76
|
||||
#: analyzer/analyzercontainer.cpp:75
|
||||
#, qt-format
|
||||
msgid "Super high (%1 fps)"
|
||||
msgstr "Очень высокая (%1 fps)"
|
||||
@@ -3922,7 +3930,7 @@ msgstr "Сборщик тегов"
|
||||
msgid "Target bitrate"
|
||||
msgstr "Целевой битрейт"
|
||||
|
||||
#: equalizer/equalizer.cpp:156
|
||||
#: equalizer/equalizer.cpp:154
|
||||
msgid "Techno"
|
||||
msgstr "Техно"
|
||||
|
||||
@@ -4080,7 +4088,7 @@ msgstr "Эта опция может быть изменена в настрой
|
||||
msgid "This type of device is not supported: %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
|
||||
msgid "Tidal"
|
||||
msgstr "Tidal"
|
||||
@@ -4182,11 +4190,11 @@ msgstr "UUID"
|
||||
msgid "Ultra wide band (UWB)"
|
||||
msgstr "Ультраширокая полоса пропускания (UWB)"
|
||||
|
||||
#: core/song.cpp:487 core/song.cpp:489 core/song.cpp:533
|
||||
#: context/contextalbumsmodel.cpp:385 collection/collectionmodel.cpp:398
|
||||
#: collection/collectionmodel.cpp:403 collection/collectionmodel.cpp:407
|
||||
#: collection/collectionmodel.cpp:411 collection/collectionmodel.cpp:415
|
||||
#: collection/collectionmodel.cpp:1362 collection/savedgroupingmanager.cpp:137
|
||||
#: core/song.cpp:480 core/song.cpp:482 core/song.cpp:526
|
||||
#: context/contextalbumsmodel.cpp:367 collection/collectionmodel.cpp:399
|
||||
#: collection/collectionmodel.cpp:404 collection/collectionmodel.cpp:408
|
||||
#: collection/collectionmodel.cpp:412 collection/collectionmodel.cpp:416
|
||||
#: collection/collectionmodel.cpp:1342 collection/savedgroupingmanager.cpp:137
|
||||
#: playlist/playlistdelegates.cpp:353 playlist/playlistmanager.cpp:542
|
||||
#: playlist/playlistmanager.cpp:543 dialogs/edittagdialog.cpp:490
|
||||
#: dialogs/edittagdialog.cpp:538
|
||||
@@ -4346,7 +4354,7 @@ msgstr "После добавления песни через меню…"
|
||||
msgid "Variable bit rate"
|
||||
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
|
||||
#: internet/internetsearchmodel.cpp:106
|
||||
msgid "Various artists"
|
||||
@@ -4487,7 +4495,7 @@ msgstr "Вы вошли в систему."
|
||||
msgid "You can change the way the songs in the collection are organised."
|
||||
msgstr "Можно изменить способ организации композиций в фонотеке."
|
||||
|
||||
#: core/songloader.cpp:137 core/songloader.cpp:142
|
||||
#: core/songloader.cpp:138 core/songloader.cpp:143
|
||||
msgid "You need GStreamer for this URL."
|
||||
msgstr "Вам нужен GStreamer для этого URL."
|
||||
|
||||
@@ -4524,7 +4532,7 @@ msgstr "Для применения языка потребуется перез
|
||||
msgid "Your collection is empty!"
|
||||
msgstr "Ваша фонотека пуста!"
|
||||
|
||||
#: equalizer/equalizer.cpp:157
|
||||
#: equalizer/equalizer.cpp:155
|
||||
msgid "Zero"
|
||||
msgstr "По-умолчанию"
|
||||
|
||||
@@ -4603,7 +4611,7 @@ msgstr "настройки"
|
||||
msgid "p&lughw"
|
||||
msgstr "p&lughw"
|
||||
|
||||
#: core/song.cpp:486
|
||||
#: core/song.cpp:479
|
||||
msgid "qobuz"
|
||||
msgstr "qobuz"
|
||||
|
||||
@@ -4629,7 +4637,7 @@ msgstr "сортировать песни"
|
||||
msgid "stop"
|
||||
msgstr "стоп"
|
||||
|
||||
#: core/song.cpp:485
|
||||
#: core/song.cpp:478
|
||||
msgid "subsonic"
|
||||
msgstr "subsonic"
|
||||
|
||||
|
||||
@@ -474,8 +474,12 @@ void PlayingWidget::contextMenuEvent(QContextMenuEvent* e) {
|
||||
menu_->popup(mapToGlobal(e->pos()));
|
||||
}
|
||||
|
||||
void PlayingWidget::mouseReleaseEvent(QMouseEvent*) {
|
||||
void PlayingWidget::mouseDoubleClickEvent(QMouseEvent* e) {
|
||||
|
||||
// Same behaviour as right-click > Show Fullsize
|
||||
if (e->button() == Qt::LeftButton && song_.is_valid()) {
|
||||
album_cover_choice_controller_->ShowCover(song_, image_original_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class PlayingWidget : public QWidget {
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void resizeEvent(QResizeEvent*);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent*);
|
||||
void mouseDoubleClickEvent(QMouseEvent*);
|
||||
void dragEnterEvent(QDragEnterEvent *e);
|
||||
void dropEvent(QDropEvent *e);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "test_utils.h"
|
||||
|
||||
#include "core/timeconstants.h"
|
||||
#include "core/song.h"
|
||||
#include "core/database.h"
|
||||
#include "core/logging.h"
|
||||
@@ -43,7 +44,7 @@ class CollectionBackendTest : public ::testing::Test {
|
||||
virtual void SetUp() {
|
||||
database_.reset(new MemoryDatabase(nullptr));
|
||||
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) {
|
||||
@@ -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
|
||||
|
||||
@@ -44,7 +44,7 @@ class CollectionModelTest : public ::testing::Test {
|
||||
void SetUp() {
|
||||
database_.reset(new MemoryDatabase(nullptr));
|
||||
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));
|
||||
|
||||
added_dir_ = false;
|
||||
|
||||
Reference in New Issue
Block a user