Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
85d4340bfe
3rdparty/qocoa
CMakeLists.txtChangelogdata
debian
dist
ext
clementine-tagreader
libclementine-common/core
libclementine-remote
libclementine-tagreader
src
CMakeLists.txt
analyzers
analyzerbase.cppanalyzerbase.hanalyzercontainer.cppanalyzercontainer.hbaranalyzer.cppbaranalyzer.hblockanalyzer.cppblockanalyzer.hboomanalyzer.cppboomanalyzer.hfht.cppfht.hnyancatanalyzer.cpprainbowdashanalyzer.cpprainbowdashanalyzer.hsonogram.cppsonogram.hturbine.cpp
config.h.incore
database.cppglobalshortcuts.cppglobalshortcuts.hmetatypes.cppmpris1.cpporganiseformat.cppplayer.cppplayer.hsong.cppsong.hurlhandler.hutilities.cpp
engines
globalsearch
globalsearchmodel.cppglobalsearchview.cppgroovesharksearchprovider.cppgroovesharksearchprovider.hsearchprovider.cppsearchprovider.h
internet
amazon
core
digitally
grooveshark
groovesharkradio.cppgroovesharkradio.hgroovesharkservice.cppgroovesharkservice.hgroovesharksettingspage.cppgroovesharksettingspage.hgroovesharksettingspage.uigroovesharkurlhandler.cppgroovesharkurlhandler.h
lastfm
somafm
spotify
subsonic
vk
library
25
3rdparty/qocoa/qsearchfield_mac.mm
vendored
25
3rdparty/qocoa/qsearchfield_mac.mm
vendored
@ -176,6 +176,8 @@ void QSearchField::setText(const QString &text)
|
||||
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[pimpl->nsSearchField setStringValue:fromQString(text)];
|
||||
[pimpl->nsSearchField selectText:pimpl->nsSearchField];
|
||||
[[pimpl->nsSearchField currentEditor] setSelectedRange:NSMakeRange([[pimpl->nsSearchField stringValue] length], 0)];
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
@ -198,12 +200,25 @@ QString QSearchField::placeholderText() const {
|
||||
|
||||
void QSearchField::setFocus(Qt::FocusReason reason)
|
||||
{
|
||||
Q_ASSERT(pimpl);
|
||||
if (!pimpl)
|
||||
return;
|
||||
/* Do nothing: we were previously using makeFirstResponder on search field, but
|
||||
* that resulted in having the text being selected (and I didn't find any way to
|
||||
* deselect it) which would result in the user erasing the first letter he just
|
||||
* typed, after using setText (e.g. if the user typed a letter while having
|
||||
* focus on the playlist, which means we call setText and give focus to the
|
||||
* search bar).
|
||||
* Instead now the focus will take place when calling selectText in setText.
|
||||
* This obviously breaks the purpose of this function, but we never call only
|
||||
* setFocus on a search box in Clementine (i.e. without a call to setText
|
||||
* shortly after).
|
||||
*/
|
||||
|
||||
if ([pimpl->nsSearchField acceptsFirstResponder])
|
||||
[[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField];
|
||||
// Q_ASSERT(pimpl);
|
||||
// if (!pimpl)
|
||||
// return;
|
||||
|
||||
// if ([pimpl->nsSearchField acceptsFirstResponder]) {
|
||||
// [[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField];
|
||||
// }
|
||||
}
|
||||
|
||||
void QSearchField::setFocus()
|
||||
|
@ -58,6 +58,7 @@ find_package(FFTW3)
|
||||
|
||||
pkg_check_modules(CDIO libcdio)
|
||||
pkg_check_modules(CHROMAPRINT REQUIRED libchromaprint)
|
||||
pkg_search_module(CRYPTOPP cryptopp libcrypto++)
|
||||
pkg_check_modules(GIO gio-2.0)
|
||||
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
|
||||
@ -71,7 +72,6 @@ pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.7)
|
||||
pkg_check_modules(LIBPULSE libpulse)
|
||||
pkg_check_modules(LIBXML libxml-2.0)
|
||||
pkg_check_modules(QCA qca2)
|
||||
pkg_check_modules(QJSON REQUIRED QJson)
|
||||
pkg_check_modules(SPOTIFY libspotify>=12.1.45)
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
||||
@ -273,10 +273,11 @@ optional_component(LIBPULSE ON "Pulse audio integration"
|
||||
|
||||
optional_component(VISUALISATIONS ON "Visualisations")
|
||||
|
||||
if(NOT HAVE_SPOTIFY_BLOB AND NOT QCA_FOUND)
|
||||
message(FATAL_ERROR "Either QCA must be available or the non-GPL Spotify "
|
||||
if(NOT HAVE_SPOTIFY_BLOB AND NOT CRYPTOPP_FOUND)
|
||||
message(FATAL_ERROR "Either crypto++ must be available or the non-GPL Spotify "
|
||||
"code must be compiled in")
|
||||
elseif(QCA_FOUND)
|
||||
elseif(CRYPTOPP_FOUND)
|
||||
set(HAVE_CRYPTOPP ON)
|
||||
set(HAVE_SPOTIFY_DOWNLOADER ON)
|
||||
endif()
|
||||
|
||||
|
52
Changelog
52
Changelog
@ -2,9 +2,11 @@ Next release:
|
||||
Major features:
|
||||
* Vk.com support
|
||||
* Seafile support
|
||||
* Amazon Cloud Drive support
|
||||
* Add Ampache compatibility (through Subsonic service)
|
||||
* Add new analyzer "Rainbow Dash"
|
||||
* Answer to the ultimate question of life, the universe and everything
|
||||
* Add "Psychedelic Colour" mode to all analyzers
|
||||
|
||||
Other features:
|
||||
* Add left click to fullsize cover on playing widget
|
||||
@ -24,9 +26,8 @@ Next release:
|
||||
* Add a source icon for CD tracks
|
||||
* Allow user to remove directories
|
||||
* Add ability to remove unavailable items from playlist
|
||||
* Adds an import button to the transcode UI, allowing the user to pull in
|
||||
all files in a folder heirarchy to be transcoded
|
||||
* Allow user to pull in all files in a folder heirarchy to be transcoded
|
||||
* Add an import button to the transcode UI, allowing the user to pull in
|
||||
all files in a folder hierarchy to be transcoded
|
||||
* Make it impossible to collapse either side of the MainWindow splitter
|
||||
* Add menu items for updating and doing a full rescan of Google Drive
|
||||
* Increase Soundcloud cover image size
|
||||
@ -52,6 +53,22 @@ Next release:
|
||||
* Ability to add tracks to Spotify starred playlist by drag and drop
|
||||
* Add HipHop and Kuduro equalizers
|
||||
* Add AZLyrics lyric provider
|
||||
* Remember current playlist between restarts
|
||||
* (OSX) Use Alt+Tab to switch between playlist tabs
|
||||
* IDv3 tag lyrics support
|
||||
* Improve handling of Spotify Top Tracks and compilations
|
||||
* Scroll to last played track when switching playlists
|
||||
* Add stop after each song repeat mode
|
||||
* Sort discs numerically when using Group by disc
|
||||
* Add ability for sort by group and performer in the library view
|
||||
* Parse the year of a disc from musicbrainz
|
||||
* Add track intro mode
|
||||
* Add ability to add a search term with tab and space in the smart
|
||||
playlist window
|
||||
* Add love/ban (lastfm) global shortcuts
|
||||
* Add support for "original year" tags
|
||||
* Send album artist to Last.fm with liblastfm >= 1.0.0
|
||||
* Add sample rate selection
|
||||
|
||||
Bugfixes:
|
||||
* Fix crash when click on a SoundCloud entry in internet tab
|
||||
@ -90,8 +107,6 @@ Next release:
|
||||
* Fix moodbars not generating correctly
|
||||
* Fix socket leak in moodbar
|
||||
* Fix memory leak in tagreader
|
||||
* Remove Ubuntu One support
|
||||
* Remove Discogs support
|
||||
* Fix crash when trying to fingerprint but missing a plugin
|
||||
* Fix infinite scan with Subsonic when the library is empty
|
||||
* Fix shortcut/media keys issues on Mac
|
||||
@ -101,6 +116,26 @@ Next release:
|
||||
* Fix laggy interface on Mac
|
||||
* Fix crash in GrooveShark
|
||||
* Fix playback breaks in Spotify
|
||||
* Fix memory leaks
|
||||
* Fix crash when stopping song that is fading after pausing
|
||||
* Fix crash when trying to download a track but there is no current one playing
|
||||
* (OSX) Fix Soundcloud API Search which simply doesn't work
|
||||
* Fix default spinner gif image which shows white pixels around the image
|
||||
* Fix setting album artist tag for FLAC files if it already exists
|
||||
* Fix crash when Clementine lists the albums on Ampache
|
||||
* Fix Last.fm scrobbling after seek
|
||||
* Fix metadata not processed properly for some streams (Akamai)
|
||||
* Fix save state when the song was paused
|
||||
* (Windows) Fix context menu for the "now playing widget"
|
||||
* Fix some issues in Boom and Turbine analyzers
|
||||
* Fix song continuously rewinding when seeking using keyboard arrow keys
|
||||
* Fix OSD re-posistioning which doesn't work on multiple monitors
|
||||
* Fix Sonogram state while paused
|
||||
|
||||
Removed features:
|
||||
* Remove Ubuntu One support
|
||||
* Remove Discogs support
|
||||
* Remove GrooveShark support
|
||||
|
||||
Build system changes:
|
||||
* Update to gstreamer 1.0
|
||||
@ -113,6 +148,13 @@ Next release:
|
||||
dependency
|
||||
* (Debian/Ubuntu) Add libmygpo-qt-dev (=> 1.0.7)
|
||||
* Remove internal copy of libechonest and add it as dependency
|
||||
* (Windows) Uninstall a previous install of Clementine when installing a new
|
||||
version
|
||||
* (Windows) Remember the last installation path
|
||||
* (GNU/Linux) Follow freedesktop.org specifications for icons
|
||||
* (GNU/Linux) Add a 128x128 version of the Clementine icon
|
||||
* (OSX) Use a GTlsDatabase for gstreamer SSL
|
||||
* Use libcrypto++ instead of QCA
|
||||
|
||||
|
||||
|
||||
|
19
data/Equifax_Secure_Certificate_Authority.pem
Normal file
19
data/Equifax_Secure_Certificate_Authority.pem
Normal file
@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
|
||||
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
|
||||
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
|
||||
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
|
||||
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
|
||||
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
|
||||
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
|
||||
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
|
||||
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
|
||||
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
|
||||
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
|
||||
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
|
||||
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
|
||||
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
|
||||
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
|
||||
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
|
||||
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
|
||||
-----END CERTIFICATE-----
|
@ -1,5 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>Equifax_Secure_Certificate_Authority.pem</file>
|
||||
<file>blank.ttf</file>
|
||||
<file>clementine_remote_qr.png</file>
|
||||
<file>clementine-spotify-public.pem</file>
|
||||
@ -9,7 +10,6 @@
|
||||
<file>currenttrack_pause.png</file>
|
||||
<file>currenttrack_play.png</file>
|
||||
<file>globalsearch.css</file>
|
||||
<file>grooveshark-valicert-ca.pem</file>
|
||||
<file>hypnotoad.gif</file>
|
||||
<file>icon_large_grey.png</file>
|
||||
<file>icon_large.png</file>
|
||||
@ -320,7 +320,6 @@
|
||||
<file>providers/dropbox.png</file>
|
||||
<file>providers/echonest.png</file>
|
||||
<file>providers/googledrive.png</file>
|
||||
<file>providers/grooveshark.png</file>
|
||||
<file>providers/itunes.png</file>
|
||||
<file>providers/jamendo.png</file>
|
||||
<file>providers/jazzradio.png</file>
|
||||
@ -332,7 +331,6 @@
|
||||
<file>providers/myspace.png</file>
|
||||
<file>providers/podcast16.png</file>
|
||||
<file>providers/podcast32.png</file>
|
||||
<file>providers/radiogfm.png</file>
|
||||
<file>providers/rockradio.png</file>
|
||||
<file>providers/skydrive.png</file>
|
||||
<file>providers/somafm.png</file>
|
||||
@ -390,6 +388,7 @@
|
||||
<file>schema/schema-49.sql</file>
|
||||
<file>schema/schema-4.sql</file>
|
||||
<file>schema/schema-5.sql</file>
|
||||
<file>schema/schema-50.sql</file>
|
||||
<file>schema/schema-6.sql</file>
|
||||
<file>schema/schema-7.sql</file>
|
||||
<file>schema/schema-8.sql</file>
|
||||
|
@ -1,43 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
|
||||
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
|
||||
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
|
||||
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
|
||||
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
|
||||
NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
|
||||
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
|
||||
YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
|
||||
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
|
||||
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
|
||||
dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
|
||||
WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
|
||||
v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
|
||||
UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
|
||||
IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
|
||||
W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
|
||||
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
|
||||
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
|
||||
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
|
||||
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
|
||||
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
|
||||
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
|
||||
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
|
||||
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
|
||||
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
|
||||
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
|
||||
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
|
||||
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
|
||||
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
|
||||
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
|
||||
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
|
||||
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
|
||||
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
|
||||
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
|
||||
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
|
||||
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
|
||||
ReYNnyicsbkqWletNw+vHX/bvZ8=
|
||||
-----END CERTIFICATE-----
|
@ -51,7 +51,7 @@
|
||||
<invalidIndicator value=""iTotalRecords": 0"/>
|
||||
<invalidIndicator value="lyrics not available"/>
|
||||
</provider>
|
||||
<provider name="letras.mus.br" title="" charset="iso-8859-1" url="http://letras.terra.com.br/winamp.php?musica={title}&artista={artist}">
|
||||
<provider name="letras.mus.br" title="" charset="utf-8" url="http://letras.terra.com.br/winamp.php?musica={title}&artista={artist}">
|
||||
<urlFormat replace="_@,;&\/"" with="_"/>
|
||||
<urlFormat replace=" " with="+"/>
|
||||
<extract>
|
||||
@ -207,7 +207,7 @@
|
||||
<invalidIndicator value="Sorry, we have no"/>
|
||||
<invalidIndicator value="This is an upcoming album and we do not have the"/>
|
||||
</provider>
|
||||
<provider name="tekstowo.pl (Polish translations)" title="{artist} - {title} - tekst" charset="utf-8" url="http://www.tekstowo.pl/piosenka,{artist},{title}.html">
|
||||
<provider name="tekstowo.pl (Original lyric language)" title="{artist} - {title} - tekst" charset="utf-8" url="http://www.tekstowo.pl/piosenka,{artist},{title}.html">
|
||||
<urlFormat replace=" _@,;&\/'"." with="_"/>
|
||||
<extract>
|
||||
<item begin="<div class="song-text">" end="<a href="javascript:;""/>
|
||||
@ -219,6 +219,15 @@
|
||||
<item begin="<h2>" end="</h2><br />"/>
|
||||
</exclude>
|
||||
</provider>
|
||||
<provider name="tekstowo.pl (Translated to Polish)" title="{artist} - {title} - tekst" charset="utf-8" url="http://www.tekstowo.pl/piosenka,{artist},{title}.html">
|
||||
<urlFormat replace=" _@,;&\/'"." with="_"/>
|
||||
<extract>
|
||||
<item begin="<div id="translation" class=" end="<a href="/>
|
||||
</extract>
|
||||
<exclude>
|
||||
<item begin=""id-" end="">"/>
|
||||
</exclude>
|
||||
</provider>
|
||||
<provider name="teksty.org" title="{artist} - {title} - tekst" charset="utf-8" url="http://teksty.org/{artist},{title},tekst-piosenki">
|
||||
<urlFormat replace=" _@,;&\/"'" with="-"/>
|
||||
<urlFormat replace="." with=""/>
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 1.7 KiB |
Binary file not shown.
Before ![]() (image error) Size: 676 B |
@ -58,7 +58,11 @@ CREATE TABLE device_%deviceid_songs (
|
||||
etag TEXT,
|
||||
|
||||
performer TEXT,
|
||||
grouping TEXT
|
||||
grouping TEXT,
|
||||
lyrics TEXT,
|
||||
|
||||
originalyear INTEGER,
|
||||
effective_originalyear INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
|
||||
|
@ -46,7 +46,10 @@ CREATE TABLE jamendo.songs (
|
||||
|
||||
performer TEXT,
|
||||
grouping TEXT,
|
||||
lyrics TEXT
|
||||
lyrics TEXT,
|
||||
|
||||
originalyear INTEGER,
|
||||
effective_originalyear INTEGER
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE jamendo.songs_fts USING fts3(
|
||||
|
9
data/schema/schema-50.sql
Normal file
9
data/schema/schema-50.sql
Normal file
@ -0,0 +1,9 @@
|
||||
ALTER TABLE %allsongstables ADD COLUMN originalyear INTEGER;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN effective_originalyear INTEGER;
|
||||
|
||||
UPDATE songs SET originalyear = -1;
|
||||
|
||||
UPDATE songs SET effective_originalyear = -1;
|
||||
|
||||
UPDATE schema_version SET version=50;
|
6
debian/control
vendored
6
debian/control
vendored
@ -15,6 +15,7 @@ Build-Depends: debhelper (>= 7),
|
||||
libboost-serialization-dev,
|
||||
libcdio-cdda1,
|
||||
libchromaprint-dev,
|
||||
libcrypto++-dev,
|
||||
libechonest-dev,
|
||||
libglew1.5-dev |
|
||||
libglew-dev,
|
||||
@ -25,14 +26,12 @@ Build-Depends: debhelper (>= 7),
|
||||
libgstreamer1.0-dev,
|
||||
libgstreamer-plugins-base1.0-dev,
|
||||
libgpod-dev,
|
||||
libimobiledevice-dev,
|
||||
libplist-dev,
|
||||
libusbmuxd-dev,
|
||||
libmtp-dev,
|
||||
libqjson-dev,
|
||||
protobuf-compiler,
|
||||
libprotobuf-dev,
|
||||
libqca2-dev,
|
||||
libfftw3-dev,
|
||||
libsparsehash-dev,
|
||||
libsqlite3-dev,
|
||||
@ -49,8 +48,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
|
||||
gstreamer1.0-plugins-good,
|
||||
gstreamer1.0-plugins-ugly,
|
||||
gstreamer1.0-pulseaudio,
|
||||
libprojectm-data | projectm-data,
|
||||
libqca2-plugin-ossl
|
||||
libprojectm-data | projectm-data
|
||||
Description: Modern music player and library organiser inspired by Amarok 1.4
|
||||
Clementine focuses on a fast and easy-to-use interface for searching and
|
||||
playing your music.
|
||||
|
520
dist/cacert.pem
vendored
520
dist/cacert.pem
vendored
@ -1,7 +1,7 @@
|
||||
##
|
||||
## Bundle of CA Root Certificates
|
||||
##
|
||||
## Certificate data from Mozilla downloaded on: Wed Sep 3 03:12:03 2014
|
||||
## Certificate data from Mozilla as of: Wed Apr 22 03:12:04 2015
|
||||
##
|
||||
## This is a bundle of X.509 certificates of public Certificate Authorities
|
||||
## (CA). These were automatically extracted from Mozilla's root certificates
|
||||
@ -13,66 +13,11 @@
|
||||
## an Apache+mod_ssl webserver for SSL client authentication.
|
||||
## Just configure this file as the SSLCACertificateFile.
|
||||
##
|
||||
## Conversion done with mk-ca-bundle.pl verison 1.22.
|
||||
## SHA1: c4540021427a6fa29e5f50db9f12d48c97d33889
|
||||
## Conversion done with mk-ca-bundle.pl version 1.25.
|
||||
## SHA1: ed3c0bbfb7912bcc00cd2033b0cb85c98d10559c
|
||||
##
|
||||
|
||||
|
||||
GTE CyberTrust Global Root
|
||||
==========================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg
|
||||
Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG
|
||||
A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz
|
||||
MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL
|
||||
Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0
|
||||
IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u
|
||||
sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql
|
||||
HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID
|
||||
AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW
|
||||
M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF
|
||||
NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Thawte Server CA
|
||||
================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
|
||||
DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
|
||||
dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE
|
||||
AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j
|
||||
b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV
|
||||
BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u
|
||||
c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG
|
||||
A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
|
||||
ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl
|
||||
/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7
|
||||
1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J
|
||||
GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ
|
||||
GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Thawte Premium Server CA
|
||||
========================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT
|
||||
DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs
|
||||
dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE
|
||||
AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl
|
||||
ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT
|
||||
AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU
|
||||
VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2
|
||||
aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ
|
||||
cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
|
||||
aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh
|
||||
Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/
|
||||
qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm
|
||||
SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf
|
||||
8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t
|
||||
UCemDaYj+bvLpgcUQg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Equifax Secure CA
|
||||
=================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
@ -93,25 +38,6 @@ BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
|
||||
70+sB3c4
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Verisign Class 3 Public Primary Certification Authority - G2
|
||||
============================================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT
|
||||
MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
|
||||
eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
|
||||
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
|
||||
dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT
|
||||
MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
|
||||
eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
|
||||
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
|
||||
dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO
|
||||
FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71
|
||||
lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB
|
||||
MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT
|
||||
1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD
|
||||
Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
GlobalSign Root CA
|
||||
==================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
@ -248,40 +174,6 @@ Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
|
||||
RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Equifax Secure Global eBusiness CA
|
||||
==================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
|
||||
RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp
|
||||
bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx
|
||||
HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds
|
||||
b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV
|
||||
PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN
|
||||
qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn
|
||||
hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
|
||||
BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs
|
||||
MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN
|
||||
I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY
|
||||
NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Equifax Secure eBusiness CA 1
|
||||
=============================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
|
||||
RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB
|
||||
LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE
|
||||
ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz
|
||||
IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ
|
||||
1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a
|
||||
IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk
|
||||
MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW
|
||||
Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF
|
||||
AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5
|
||||
lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+
|
||||
KpYrtWKmpj29f5JZzVoqgrI3eQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
AddTrust Low-Value Services Root
|
||||
================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
@ -527,59 +419,6 @@ gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
|
||||
X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
America Online Root Certification Authority 1
|
||||
=============================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT
|
||||
QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp
|
||||
Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG
|
||||
A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg
|
||||
T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG
|
||||
v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z
|
||||
DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh
|
||||
sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP
|
||||
8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T
|
||||
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z
|
||||
o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf
|
||||
GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF
|
||||
VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft
|
||||
3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
|
||||
Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
|
||||
sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
America Online Root Certification Authority 2
|
||||
=============================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Visa eCommerce Root
|
||||
===================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
@ -1777,33 +1616,6 @@ JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk
|
||||
vQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
TC TrustCenter Class 3 CA II
|
||||
============================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC
|
||||
REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy
|
||||
IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw
|
||||
MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1
|
||||
c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE
|
||||
AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W
|
||||
yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo
|
||||
6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ
|
||||
uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk
|
||||
2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB
|
||||
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB
|
||||
7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90
|
||||
Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU
|
||||
cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i
|
||||
SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
|
||||
TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE
|
||||
O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8
|
||||
yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9
|
||||
IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal
|
||||
092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc
|
||||
5A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
TC TrustCenter Universal CA I
|
||||
=============================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
@ -2421,28 +2233,6 @@ yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
|
||||
LXpUq3DDfSJlgnCW
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi
|
||||
===================================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
|
||||
EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz
|
||||
ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3
|
||||
MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0
|
||||
cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u
|
||||
aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY
|
||||
8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y
|
||||
jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI
|
||||
JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk
|
||||
9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD
|
||||
AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG
|
||||
SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d
|
||||
F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq
|
||||
D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4
|
||||
Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq
|
||||
fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
GlobalSign Root CA - R3
|
||||
=======================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
@ -3892,3 +3682,307 @@ ONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Leie2uPAmvylezkolwQOQv
|
||||
T8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR12KvxAmLBsX5VYc8T1yaw15zLKYs4SgsO
|
||||
kI26oQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
COMODO RSA Certification Authority
|
||||
==================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
LaZRfyHBNVOFBkpdn627G190
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
USERTrust RSA Certification Authority
|
||||
=====================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
USERTrust ECC Certification Authority
|
||||
=====================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC
|
||||
VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
|
||||
biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC
|
||||
VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
|
||||
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2
|
||||
0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez
|
||||
nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV
|
||||
HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB
|
||||
HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu
|
||||
9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
GlobalSign ECC Root CA - R4
|
||||
===========================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb
|
||||
R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
|
||||
EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
|
||||
R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
|
||||
EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl
|
||||
OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P
|
||||
AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV
|
||||
MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF
|
||||
JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
GlobalSign ECC Root CA - R5
|
||||
===========================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb
|
||||
R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
|
||||
EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
|
||||
R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
|
||||
EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6
|
||||
SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS
|
||||
h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
|
||||
BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx
|
||||
uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
|
||||
yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Staat der Nederlanden Root CA - G3
|
||||
==================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Staat der Nederlanden EV Root CA
|
||||
================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
IdenTrust Commercial Root CA 1
|
||||
==============================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
cGzM7vRX+Bi6hG6H
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
IdenTrust Public Sector Root CA 1
|
||||
=================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
3Wl9af0AVqW3rLatt8o+Ae+c
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Entrust Root Certification Authority - G2
|
||||
=========================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV
|
||||
BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy
|
||||
bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug
|
||||
b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw
|
||||
HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
|
||||
DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx
|
||||
OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s
|
||||
eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP
|
||||
/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz
|
||||
HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU
|
||||
s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y
|
||||
TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx
|
||||
AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6
|
||||
0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z
|
||||
iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
|
||||
Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi
|
||||
nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+
|
||||
vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO
|
||||
e4pIb4tF9g==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Entrust Root Certification Authority - EC1
|
||||
==========================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx
|
||||
FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn
|
||||
YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl
|
||||
ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
|
||||
IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw
|
||||
FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs
|
||||
LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg
|
||||
dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
|
||||
IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy
|
||||
AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef
|
||||
9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
|
||||
FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h
|
||||
vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8
|
||||
kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
CFCA EV ROOT
|
||||
============
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
4
dist/clementine.spec.in
vendored
4
dist/clementine.spec.in
vendored
@ -13,11 +13,11 @@ BuildRequires: desktop-file-utils liblastfm-devel taglib-devel gettext
|
||||
BuildRequires: qt4-devel boost-devel gcc-c++ glew-devel libgpod-devel
|
||||
BuildRequires: cmake gstreamer1-devel gstreamer1-plugins-base-devel
|
||||
BuildRequires: libmtp-devel protobuf-devel protobuf-compiler libcdio-devel
|
||||
BuildRequires: qjson-devel qca2-devel fftw-devel sparsehash-devel
|
||||
BuildRequires: qjson-devel cryptopp-devel fftw-devel sparsehash-devel
|
||||
BuildRequires: sqlite-devel pulseaudio-libs-devel libechonest-devel
|
||||
BuildRequires: libchromaprint-devel
|
||||
|
||||
Requires: libgpod protobuf-lite libcdio qjson qca-ossl sqlite
|
||||
Requires: libgpod protobuf-lite libcdio qjson sqlite
|
||||
|
||||
# GStreamer codec dependencies
|
||||
Requires: gstreamer1-plugins-ugly
|
||||
|
107
dist/windows/clementine.nsi.in
vendored
107
dist/windows/clementine.nsi.in
vendored
@ -6,9 +6,8 @@
|
||||
!define PRODUCT_DISPLAY_VERSION "@CLEMENTINE_VERSION_DISPLAY@"
|
||||
!define PRODUCT_DISPLAY_VERSION_SHORT "@CLEMENTINE_VERSION_DISPLAY@"
|
||||
!define PRODUCT_WEB_SITE "http://www.clementine-player.org/"
|
||||
!define PRODUCT_ROOT_KEY "HKLM"
|
||||
!define PRODUCT_INST_KEY "Software\Clementine"
|
||||
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
|
||||
@NORMAL@!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Clementine"
|
||||
@PORTABLE@!define PRODUCT_INSTALL_DIR "C:\Clementine"
|
||||
|
||||
@ -108,9 +107,8 @@ Name "${PRODUCT_NAME}"
|
||||
OutFile "${PRODUCT_NAME}Setup-@CLEMENTINE_VERSION_SPARKLE@.exe"
|
||||
InstallDir "${PRODUCT_INSTALL_DIR}"
|
||||
|
||||
; Registry key to check for directory (so if you install again, it will
|
||||
; overwrite the old one automatically)
|
||||
InstallDirRegKey ${PRODUCT_ROOT_KEY} ${PRODUCT_INST_KEY} "Install_Dir"
|
||||
; Get the path where Clementine was installed previously and set it as default path
|
||||
InstallDirRegKey ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
||||
|
||||
ShowInstDetails show
|
||||
ShowUnInstDetails show
|
||||
@ -120,7 +118,7 @@ ShowUnInstDetails show
|
||||
; Check for previous installation, and call the uninstaller if any
|
||||
Function CheckPreviousInstall
|
||||
|
||||
ReadRegStr $R0 ${PRODUCT_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
||||
ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
|
||||
StrCmp $R0 "" done
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
||||
@ -131,7 +129,7 @@ Function CheckPreviousInstall
|
||||
; Run the uninstaller
|
||||
uninst:
|
||||
ClearErrors
|
||||
ExecWait '$R0 _?=$INSTDIR' ; Do not copy the uninstaller to a temp file
|
||||
ExecWait '$R0' ; Do not copy the uninstaller to a temp file
|
||||
|
||||
done:
|
||||
|
||||
@ -252,9 +250,9 @@ Section "Delete old files" oldfiles
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstqtdemux.dll"
|
||||
|
||||
; No longer using built-in libechonest
|
||||
Delete "libechonest.dll"
|
||||
Delete "$INSTDIR\libechonest.dll"
|
||||
; No longer using built-in chromaprint
|
||||
Delete "libchromaprint.dll"
|
||||
Delete "$INSTDIR\libchromaprint.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Clementine" Clementine
|
||||
@ -283,6 +281,7 @@ Section "Clementine" Clementine
|
||||
File "libgnutls-28.dll"
|
||||
File "libgobject-2.0-0.dll"
|
||||
File "libgpg-error-0.dll"
|
||||
File "libgpod.dll"
|
||||
File "libgstapp-1.0-0.dll"
|
||||
File "libgstaudio-1.0-0.dll"
|
||||
File "libgstbase-1.0-0.dll"
|
||||
@ -316,6 +315,7 @@ Section "Clementine" Clementine
|
||||
File "libqjson.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libspotify.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
File "libstdc++-6.dll"
|
||||
File "libtag.dll"
|
||||
File "libtasn1-6.dll"
|
||||
@ -342,9 +342,6 @@ Section "Clementine" Clementine
|
||||
@NORMAL@ StrCpy $R0 $0
|
||||
@NORMAL@ IntCmp $R0 6 HasDefaultPrograms NoDefaultPrograms HasDefaultPrograms
|
||||
|
||||
; Write the installation path into the registry
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} ${PRODUCT_INST_KEY} "Install_Dir" "$INSTDIR"
|
||||
|
||||
@NORMAL@ HasDefaultPrograms:
|
||||
@NORMAL@ ; Register Clementine with Default Programs
|
||||
@NORMAL@ Var /GLOBAL AppIcon
|
||||
@ -1038,14 +1035,14 @@ Section "Uninstaller"
|
||||
; Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\clementine.ico"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_DISPLAY_VERSION}"
|
||||
@NORMAL@ WriteRegDWORD ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMajor" "${PRODUCT_VERSION_MAJOR}"
|
||||
@NORMAL@ WriteRegDWORD ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMinor" "${PRODUCT_VERSION_MINOR}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\clementine.ico"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_DISPLAY_VERSION}"
|
||||
@NORMAL@ WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMajor" "${PRODUCT_VERSION_MAJOR}"
|
||||
@NORMAL@ WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "VersionMinor" "${PRODUCT_VERSION_MINOR}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
|
||||
@NORMAL@ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
@ -1073,71 +1070,66 @@ Section "Uninstall"
|
||||
completed:
|
||||
|
||||
; Delete all the files
|
||||
Delete "$INSTDIR\avcodec-53.dll"
|
||||
Delete "$INSTDIR\avformat-53.dll"
|
||||
Delete "$INSTDIR\avutil-51.dll"
|
||||
Delete "$INSTDIR\clementine.ico"
|
||||
Delete "$INSTDIR\clementine.exe"
|
||||
Delete "$INSTDIR\clementine-tagreader.exe"
|
||||
Delete "$INSTDIR\clementine-spotifyblob.exe"
|
||||
Delete "$INSTDIR\glew32.dll"
|
||||
Delete "$INSTDIR\intl.dll"
|
||||
Delete "$INSTDIR\libcdio-16.dll"
|
||||
Delete "$INSTDIR\libchromaprint.dll"
|
||||
Delete "$INSTDIR\libeay32.dll"
|
||||
Delete "$INSTDIR\libexpat-1.dll"
|
||||
Delete "$INSTDIR\libechonest.dll"
|
||||
Delete "$INSTDIR\libfaac.dll"
|
||||
Delete "$INSTDIR\libfaad.dll"
|
||||
Delete "$INSTDIR\libffi-6.dll"
|
||||
Delete "$INSTDIR\libfftw3-3.dll"
|
||||
Delete "$INSTDIR\libFLAC.dll"
|
||||
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
|
||||
Delete "$INSTDIR\libgcrypt-11.dll"
|
||||
Delete "$INSTDIR\libgdk_pixbuf-2.0-0.dll"
|
||||
Delete "$INSTDIR\libgcrypt-20.dll"
|
||||
Delete "$INSTDIR\libgio-2.0-0.dll"
|
||||
Delete "$INSTDIR\libglib-2.0-0.dll"
|
||||
Delete "$INSTDIR\libgmodule-2.0-0.dll"
|
||||
Delete "$INSTDIR\libgmp-10.dll"
|
||||
Delete "$INSTDIR\libgnutls-26.dll"
|
||||
Delete "$INSTDIR\libgnutls-28.dll"
|
||||
Delete "$INSTDIR\libgobject-2.0-0.dll"
|
||||
Delete "$INSTDIR\libgpg-error-0.dll"
|
||||
Delete "$INSTDIR\libgstapp-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstaudio-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstbase-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstcdda-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstcontroller-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstdataprotocol-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstfft-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstinterfaces-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstnet-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstnetbuffer-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstpbutils-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstreamer-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstriff-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstrtp-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstrtsp-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgstsdp-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgsttag-0.10-0.dll"
|
||||
Delete "$INSTDIR\libgpod.dll"
|
||||
Delete "$INSTDIR\libgstapp-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstaudio-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstbase-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstcontroller-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstfft-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstnet-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstpbutils-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstreamer-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstriff-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstrtp-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstrtsp-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstsdp-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgsttag-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgstvideo-1.0-0.dll"
|
||||
Delete "$INSTDIR\libgthread-2.0-0.dll"
|
||||
Delete "$INSTDIR\libhogweed-2-4.dll"
|
||||
Delete "$INSTDIR\libiconv-2.dll"
|
||||
Delete "$INSTDIR\libid3tag.dll"
|
||||
Delete "$INSTDIR\libimobiledevice-1.dll"
|
||||
Delete "$INSTDIR\libintl-8.dll"
|
||||
Delete "$INSTDIR\liblastfm.dll"
|
||||
Delete "$INSTDIR\libmad.dll"
|
||||
Delete "$INSTDIR\libmms-0.dll"
|
||||
Delete "$INSTDIR\libmp3lame-0.dll"
|
||||
Delete "$INSTDIR\libnettle-4-6.dll"
|
||||
Delete "$INSTDIR\libogg-0.dll"
|
||||
Delete "$INSTDIR\liboil-0.3-0.dll"
|
||||
Delete "$INSTDIR\liborc-0.4-0.dll"
|
||||
Delete "$INSTDIR\liborc-test-0.4-0.dll"
|
||||
Delete "$INSTDIR\libplist.dll"
|
||||
Delete "$INSTDIR\libpng14-14.dll"
|
||||
Delete "$INSTDIR\libprotobuf-7.dll"
|
||||
Delete "$INSTDIR\libprotobuf-8.dll"
|
||||
Delete "$INSTDIR\libqjson.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libspotify.dll"
|
||||
Delete "$INSTDIR\libstdc++-6.dll"
|
||||
Delete "$INSTDIR\libtag.dll"
|
||||
Delete "$INSTDIR\libtasn1-3.dll"
|
||||
Delete "$INSTDIR\libusbmuxd.dll"
|
||||
Delete "$INSTDIR\libtasn1-6.dll"
|
||||
Delete "$INSTDIR\libvorbis-0.dll"
|
||||
Delete "$INSTDIR\libvorbisenc-2.dll"
|
||||
Delete "$INSTDIR\libxml2-2.dll"
|
||||
@ -1161,32 +1153,32 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstasf.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudioconvert.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudiofx.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudioparsers.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudioresample.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstaudiotestsrc.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstautodetect.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstcdio.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstcoreelements.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstdecodebin2.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsoundsink.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstequalizer.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstfaac.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstfaad.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstffmpeg.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstflac.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstgdp.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstgio.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgsticydemux.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstid3demux.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstisomp4.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstlame.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstlibav.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstmad.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstmms.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstmpegaudioparse.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstogg.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstqtdemux.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstplayback.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstreplaygain.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstsouphttpsrc.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstspectrum.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstspeex.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstsouphttpsrc.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgsttaglib.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgsttcp.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgsttypefindfunctions.dll"
|
||||
@ -1786,8 +1778,7 @@ Section "Uninstall"
|
||||
@NORMAL@ RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||
|
||||
; Remove the entry from 'installed programs list'
|
||||
@NORMAL@ DeleteRegKey ${PRODUCT_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
|
||||
@NORMAL@ DeleteRegKey ${PRODUCT_ROOT_KEY} "${PRODUCT_INST_KEY}"
|
||||
@NORMAL@ DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
|
||||
|
||||
; Check the OS. If Vista or newer, use Default Programs
|
||||
@NORMAL@ nsisos::osversion
|
||||
|
@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
|
||||
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
|
||||
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
|
||||
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
|
||||
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
|
||||
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
|
||||
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
|
||||
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
|
||||
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
|
||||
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
|
||||
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
|
||||
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
|
||||
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
|
||||
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
|
||||
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
|
||||
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
|
||||
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
|
||||
-----END CERTIFICATE-----
|
@ -1,5 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/certs">
|
||||
<file>Equifax_Secure_Certificate_Authority.pem</file>
|
||||
<file>godaddy-root.pem</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -57,6 +57,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
QSslSocket::addDefaultCaCertificates(
|
||||
QSslCertificate::fromPath(":/certs/godaddy-root.pem", QSsl::Pem));
|
||||
QSslSocket::addDefaultCaCertificates(QSslCertificate::fromPath(
|
||||
":/certs/Equifax_Secure_Certificate_Authority.pem", QSsl::Pem));
|
||||
|
||||
TagReaderWorker worker(&socket);
|
||||
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
#include "closure.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
namespace _detail {
|
||||
@ -65,11 +63,3 @@ void DoInAMinuteOrSo(QObject* receiver, const char* slot) {
|
||||
int msec = (60 + (qrand() % 60)) * kMsecPerSec;
|
||||
DoAfter(receiver, slot, msec);
|
||||
}
|
||||
|
||||
void DoAfter(std::function<void()> callback, int msec) {
|
||||
QTimer* timer = new QTimer;
|
||||
timer->setSingleShot(true);
|
||||
NewClosure(timer, SIGNAL(timeout()), callback);
|
||||
QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
|
||||
timer->start(msec);
|
||||
}
|
||||
|
@ -18,12 +18,14 @@
|
||||
#ifndef CLOSURE_H
|
||||
#define CLOSURE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <QMetaMethod>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QTimer>
|
||||
|
||||
namespace _detail {
|
||||
|
||||
@ -188,7 +190,19 @@ _detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||
}
|
||||
|
||||
void DoAfter(QObject* receiver, const char* slot, int msec);
|
||||
void DoAfter(std::function<void()> callback, int msec);
|
||||
void DoAfter(std::function<void()> callback, std::chrono::milliseconds msec);
|
||||
void DoInAMinuteOrSo(QObject* receiver, const char* slot);
|
||||
|
||||
template <typename R, typename P>
|
||||
void DoAfter(
|
||||
std::function<void()> callback, std::chrono::duration<R, P> duration) {
|
||||
QTimer* timer = new QTimer;
|
||||
timer->setSingleShot(true);
|
||||
NewClosure(timer, SIGNAL(timeout()), callback);
|
||||
QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
|
||||
std::chrono::milliseconds msec =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(duration);
|
||||
timer->start(msec.count());
|
||||
}
|
||||
|
||||
#endif // CLOSURE_H
|
||||
|
@ -266,3 +266,17 @@ void DumpStackTrace() {
|
||||
}
|
||||
|
||||
} // namespace logging
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
QString print_duration(T duration, const std::string& unit) {
|
||||
return QString("%1%2").arg(duration.count()).arg(unit.c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QDebug operator<<(QDebug dbg, std::chrono::seconds secs) {
|
||||
dbg.nospace() << print_duration(secs, "s");
|
||||
return dbg.space();
|
||||
}
|
||||
|
@ -21,6 +21,9 @@
|
||||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef QT_NO_DEBUG_STREAM
|
||||
@ -61,4 +64,6 @@ void GLog(const char* domain, int level, const char* message, void* user_data);
|
||||
extern const char* kDefaultLogLevels;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, std::chrono::seconds secs);
|
||||
|
||||
#endif // LOGGING_H
|
||||
|
@ -56,6 +56,7 @@ enum MsgType {
|
||||
DOWNLOAD_TOTAL_SIZE = 53;
|
||||
GLOBAL_SEARCH_RESULT = 54;
|
||||
TRANSCODING_FILES = 55;
|
||||
GLOBAL_SEARCH_STATUS = 56;
|
||||
}
|
||||
|
||||
// Valid Engine states
|
||||
@ -68,6 +69,24 @@ enum EngineState {
|
||||
|
||||
// Song Metadata
|
||||
message SongMetadata {
|
||||
enum Type {
|
||||
UNKNOWN = 0;
|
||||
ASF = 1;
|
||||
FLAC = 2;
|
||||
MP4 = 3;
|
||||
MPC = 4;
|
||||
MPEG = 5;
|
||||
OGGFLAC = 6;
|
||||
OGGSPEEX = 7;
|
||||
OGGVORBIS = 8;
|
||||
AIFF = 9;
|
||||
WAV = 10;
|
||||
TRUEAUDIO = 11;
|
||||
CDDA = 12;
|
||||
OGGOPUS = 13;
|
||||
STREAM = 99;
|
||||
}
|
||||
|
||||
optional int32 id = 1; // unique id of the song
|
||||
optional int32 index = 2; // Index of the current row of the active playlist
|
||||
optional string title = 3;
|
||||
@ -87,6 +106,9 @@ message SongMetadata {
|
||||
optional int32 file_size = 17;
|
||||
optional float rating = 18; // 0 (0 stars) to 1 (5 stars)
|
||||
optional string url = 19;
|
||||
optional string art_automatic = 20;
|
||||
optional string art_manual = 21;
|
||||
optional Type type = 22;
|
||||
}
|
||||
|
||||
// Playlist informations
|
||||
@ -104,6 +126,8 @@ enum RepeatMode {
|
||||
Repeat_Track = 1;
|
||||
Repeat_Album = 2;
|
||||
Repeat_Playlist = 3;
|
||||
Repeat_OneByOne = 4;
|
||||
Repeat_Intro = 5;
|
||||
}
|
||||
|
||||
// Valid Shuffle modes
|
||||
@ -215,6 +239,7 @@ message RequestInsertUrls {
|
||||
optional int32 position = 3 [default=-1];
|
||||
optional bool play_now = 4 [default=false];
|
||||
optional bool enqueue = 5 [default=false];
|
||||
repeated SongMetadata songs = 6;
|
||||
}
|
||||
|
||||
// Client want to change track
|
||||
@ -297,6 +322,7 @@ message ResponseGlobalSearch {
|
||||
optional string query = 2;
|
||||
optional string search_provider = 3;
|
||||
repeated SongMetadata song_metadata = 4;
|
||||
optional bytes search_provider_icon = 5;
|
||||
}
|
||||
|
||||
message ResponseTranscoderStatus {
|
||||
@ -304,9 +330,20 @@ message ResponseTranscoderStatus {
|
||||
optional int32 total = 2;
|
||||
}
|
||||
|
||||
enum GlobalSearchStatus {
|
||||
GlobalSearchStarted = 1;
|
||||
GlobalSearchFinished = 2;
|
||||
}
|
||||
|
||||
message ResponseGlobalSearchStatus {
|
||||
optional int32 id = 1;
|
||||
optional string query = 2;
|
||||
optional GlobalSearchStatus status = 3;
|
||||
}
|
||||
|
||||
// The message itself
|
||||
message Message {
|
||||
optional int32 version = 1 [default=19];
|
||||
optional int32 version = 1 [default=21];
|
||||
optional MsgType type = 2 [default=UNKNOWN]; // What data is in the message?
|
||||
|
||||
optional RequestConnect request_connect = 21;
|
||||
@ -341,4 +378,5 @@ message Message {
|
||||
optional ResponseDownloadTotalSize response_download_total_size = 36;
|
||||
optional ResponseGlobalSearch response_global_search = 38;
|
||||
optional ResponseTranscoderStatus response_transcoder_status = 39;
|
||||
optional ResponseGlobalSearchStatus response_global_search_status = 40;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTextCodec>
|
||||
#include <QUrl>
|
||||
#include <QVector>
|
||||
|
||||
#include <aifffile.h>
|
||||
#include <asffile.h>
|
||||
@ -48,6 +49,7 @@
|
||||
#include <textidentificationframe.h>
|
||||
#include <trueaudiofile.h>
|
||||
#include <tstring.h>
|
||||
#include <unsynchronizedlyricsframe.h>
|
||||
#include <vorbisfile.h>
|
||||
#include <wavfile.h>
|
||||
|
||||
@ -106,6 +108,14 @@ const char* TagReader::kMP4_FMPS_Playcount_ID =
|
||||
const char* TagReader::kMP4_FMPS_Score_ID =
|
||||
"----:com.apple.iTunes:FMPS_Rating_Amarok_Score";
|
||||
|
||||
namespace {
|
||||
// Tags containing the year the album was originally released (in contrast to
|
||||
// other tags that contain the release year of the current edition)
|
||||
const char* kMP4_OriginalYear_ID = "----:com.apple.iTunes:ORIGINAL YEAR";
|
||||
const char* kASF_OriginalDate_ID = "WM/OriginalReleaseTime";
|
||||
const char* kASF_OriginalYear_ID = "WM/OriginalReleaseYear";
|
||||
}
|
||||
|
||||
TagReader::TagReader()
|
||||
: factory_(new TagLibFileRefFactory),
|
||||
network_(new QNetworkAccessManager),
|
||||
@ -189,11 +199,21 @@ void TagReader::ReadFile(const QString& filename,
|
||||
compilation =
|
||||
TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
||||
|
||||
if (!map["TDOR"].isEmpty()) {
|
||||
song->set_originalyear(
|
||||
map["TDOR"].front()->toString().substr(0, 4).toInt());
|
||||
} else if (!map["TORY"].isEmpty()) {
|
||||
song->set_originalyear(
|
||||
map["TORY"].front()->toString().substr(0, 4).toInt());
|
||||
}
|
||||
|
||||
if (!map["USLT"].isEmpty()) {
|
||||
lyrics = TStringToQString((map["USLT"].front())->toString()).trimmed();
|
||||
qLog(Debug) << "Read ULST lyrics " << lyrics;
|
||||
} else if (!map["SYLT"].isEmpty())
|
||||
lyrics = TStringToQString((map["SYLT"].front())->toString()).trimmed();
|
||||
Decode(map["USLT"].front()->toString(), nullptr,
|
||||
song->mutable_lyrics());
|
||||
} else if (!map["SYLT"].isEmpty()) {
|
||||
Decode(map["SYLT"].front()->toString(), nullptr,
|
||||
song->mutable_lyrics());
|
||||
}
|
||||
|
||||
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
|
||||
@ -310,6 +330,15 @@ void TagReader::ReadFile(const QString& filename,
|
||||
Decode(items["\251grp"].toStringList().toString(" "), nullptr,
|
||||
song->mutable_grouping());
|
||||
}
|
||||
|
||||
if (items.contains(kMP4_OriginalYear_ID)) {
|
||||
song->set_originalyear(
|
||||
TStringToQString(
|
||||
items[kMP4_OriginalYear_ID].toStringList().toString('\n'))
|
||||
.left(4)
|
||||
.toInt());
|
||||
}
|
||||
|
||||
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
}
|
||||
@ -350,6 +379,22 @@ void TagReader::ReadFile(const QString& filename,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes_map.contains(kASF_OriginalDate_ID)) {
|
||||
const TagLib::ASF::AttributeList& attributes =
|
||||
attributes_map[kASF_OriginalDate_ID];
|
||||
if (!attributes.isEmpty()) {
|
||||
song->set_originalyear(
|
||||
TStringToQString(attributes.front().toString()).left(4).toInt());
|
||||
}
|
||||
} else if (attributes_map.contains(kASF_OriginalYear_ID)) {
|
||||
const TagLib::ASF::AttributeList& attributes =
|
||||
attributes_map[kASF_OriginalYear_ID];
|
||||
if (!attributes.isEmpty()) {
|
||||
song->set_originalyear(
|
||||
TStringToQString(attributes.front().toString()).left(4).toInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (tag) {
|
||||
@ -486,6 +531,13 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
||||
Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
|
||||
}
|
||||
|
||||
if (!map["ORIGINALDATE"].isEmpty())
|
||||
song->set_originalyear(
|
||||
TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt());
|
||||
else if (!map["ORIGINALYEAR"].isEmpty())
|
||||
song->set_originalyear(
|
||||
TStringToQString(map["ORIGINALYEAR"].front()).toInt());
|
||||
|
||||
if (!map["BPM"].isEmpty())
|
||||
song->set_bpm(TStringToQString(map["BPM"].front()).trimmed().toFloat());
|
||||
|
||||
@ -537,8 +589,10 @@ void TagReader::SetVorbisComments(
|
||||
"COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"),
|
||||
true);
|
||||
|
||||
vorbis_comments->addField("ALBUM ARTIST",
|
||||
// Try to be coherent, the two forms are used but the first one is preferred
|
||||
vorbis_comments->addField("ALBUMARTIST",
|
||||
StdStringToTaglibString(song.albumartist()), true);
|
||||
vorbis_comments->removeField("ALBUM ARTIST");
|
||||
}
|
||||
|
||||
void TagReader::SetFMPSStatisticsVorbisComments(
|
||||
@ -626,7 +680,7 @@ bool TagReader::SaveFile(const QString& filename,
|
||||
SetTextFrame("TCOM", song.composer(), tag);
|
||||
SetTextFrame("TIT1", song.grouping(), tag);
|
||||
SetTextFrame("TOPE", song.performer(), tag);
|
||||
SetTextFrame("USLT", song.lyrics(), tag);
|
||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
||||
// Skip TPE1 (which is the artist) here because we already set it
|
||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
||||
@ -840,19 +894,67 @@ void TagReader::SetTextFrame(const char* id, const QString& value,
|
||||
void TagReader::SetTextFrame(const char* id, const std::string& value,
|
||||
TagLib::ID3v2::Tag* tag) const {
|
||||
TagLib::ByteVector id_vector(id);
|
||||
QVector<TagLib::ByteVector> frames_buffer;
|
||||
|
||||
// Remove the frame if it already exists
|
||||
// Store and clear existing frames
|
||||
while (tag->frameListMap().contains(id_vector) &&
|
||||
tag->frameListMap()[id_vector].size() != 0) {
|
||||
frames_buffer.push_back(tag->frameListMap()[id_vector].front()->render());
|
||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||
}
|
||||
|
||||
// Create and add a new frame
|
||||
TagLib::ID3v2::TextIdentificationFrame* frame =
|
||||
new TagLib::ID3v2::TextIdentificationFrame(id_vector,
|
||||
// If no frames stored create empty frame
|
||||
if (frames_buffer.isEmpty()) {
|
||||
TagLib::ID3v2::TextIdentificationFrame frame(id_vector,
|
||||
TagLib::String::UTF8);
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
tag->addFrame(frame);
|
||||
frames_buffer.push_back(frame.render());
|
||||
}
|
||||
|
||||
// Update and add the frames
|
||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size();
|
||||
lyrics_index++) {
|
||||
TagLib::ID3v2::TextIdentificationFrame* frame =
|
||||
new TagLib::ID3v2::TextIdentificationFrame(
|
||||
frames_buffer.at(lyrics_index));
|
||||
if (lyrics_index == 0) {
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
}
|
||||
// add frame takes ownership and clears the memory
|
||||
tag->addFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void TagReader::SetUnsyncLyricsFrame(const std::string& value,
|
||||
TagLib::ID3v2::Tag* tag) const {
|
||||
TagLib::ByteVector id_vector("USLT");
|
||||
QVector<TagLib::ByteVector> frames_buffer;
|
||||
|
||||
// Store and clear existing frames
|
||||
while (tag->frameListMap().contains(id_vector) &&
|
||||
tag->frameListMap()[id_vector].size() != 0) {
|
||||
frames_buffer.push_back(tag->frameListMap()[id_vector].front()->render());
|
||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||
}
|
||||
|
||||
// If no frames stored create empty frame
|
||||
if (frames_buffer.isEmpty()) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
||||
frame.setDescription("Clementine editor");
|
||||
frames_buffer.push_back(frame.render());
|
||||
}
|
||||
|
||||
// Update and add the frames
|
||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size();
|
||||
lyrics_index++) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame =
|
||||
new TagLib::ID3v2::UnsynchronizedLyricsFrame(
|
||||
frames_buffer.at(lyrics_index));
|
||||
if (lyrics_index == 0) {
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
}
|
||||
// add frame takes ownership and clears the memory
|
||||
tag->addFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
bool TagReader::IsMediaFile(const QString& filename) const {
|
||||
|
@ -87,12 +87,12 @@ class TagReader {
|
||||
void SetFMPSStatisticsVorbisComments(
|
||||
TagLib::Ogg::XiphComment* vorbis_comments,
|
||||
const pb::tagreader::SongMetadata& song) const;
|
||||
void SetFMPSRatingVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
||||
const pb::tagreader::SongMetadata& song)
|
||||
const;
|
||||
void SetFMPSRatingVorbisComments(
|
||||
TagLib::Ogg::XiphComment* vorbis_comments,
|
||||
const pb::tagreader::SongMetadata& song) const;
|
||||
|
||||
pb::tagreader::SongMetadata_Type GuessFileType(TagLib::FileRef* fileref)
|
||||
const;
|
||||
pb::tagreader::SongMetadata_Type GuessFileType(
|
||||
TagLib::FileRef* fileref) const;
|
||||
|
||||
void SetUserTextFrame(const QString& description, const QString& value,
|
||||
TagLib::ID3v2::Tag* tag) const;
|
||||
@ -104,6 +104,8 @@ class TagReader {
|
||||
TagLib::ID3v2::Tag* tag) const;
|
||||
void SetTextFrame(const char* id, const std::string& value,
|
||||
TagLib::ID3v2::Tag* tag) const;
|
||||
void SetUnsyncLyricsFrame(const std::string& value,
|
||||
TagLib::ID3v2::Tag* tag) const;
|
||||
|
||||
private:
|
||||
static const char* kMP4_FMPS_Rating_ID;
|
||||
|
@ -52,6 +52,7 @@ message SongMetadata {
|
||||
optional string performer = 31;
|
||||
optional string grouping = 32;
|
||||
optional string lyrics = 33;
|
||||
optional int32 originalyear = 34;
|
||||
}
|
||||
|
||||
message ReadFileRequest {
|
||||
|
@ -147,7 +147,6 @@ set(SOURCES
|
||||
globalsearch/globalsearchsettingspage.cpp
|
||||
globalsearch/globalsearchsortmodel.cpp
|
||||
globalsearch/globalsearchview.cpp
|
||||
globalsearch/groovesharksearchprovider.cpp
|
||||
globalsearch/icecastsearchprovider.cpp
|
||||
globalsearch/librarysearchprovider.cpp
|
||||
globalsearch/savedradiosearchprovider.cpp
|
||||
@ -167,10 +166,6 @@ set(SOURCES
|
||||
internet/digitally/digitallyimportedsettingspage.cpp
|
||||
internet/digitally/digitallyimportedurlhandler.cpp
|
||||
internet/core/geolocator.cpp
|
||||
internet/grooveshark/groovesharkradio.cpp
|
||||
internet/grooveshark/groovesharkservice.cpp
|
||||
internet/grooveshark/groovesharksettingspage.cpp
|
||||
internet/grooveshark/groovesharkurlhandler.cpp
|
||||
internet/icecast/icecastbackend.cpp
|
||||
internet/icecast/icecastfilterwidget.cpp
|
||||
internet/icecast/icecastmodel.cpp
|
||||
@ -464,7 +459,6 @@ set(HEADERS
|
||||
globalsearch/globalsearchmodel.h
|
||||
globalsearch/globalsearchsettingspage.h
|
||||
globalsearch/globalsearchview.h
|
||||
globalsearch/groovesharksearchprovider.h
|
||||
globalsearch/searchprovider.h
|
||||
globalsearch/simplesearchprovider.h
|
||||
globalsearch/soundcloudsearchprovider.h
|
||||
@ -476,9 +470,6 @@ set(HEADERS
|
||||
internet/digitally/digitallyimportedservicebase.h
|
||||
internet/digitally/digitallyimportedsettingspage.h
|
||||
internet/core/geolocator.h
|
||||
internet/grooveshark/groovesharkservice.h
|
||||
internet/grooveshark/groovesharksettingspage.h
|
||||
internet/grooveshark/groovesharkurlhandler.h
|
||||
internet/icecast/icecastbackend.h
|
||||
internet/icecast/icecastfilterwidget.h
|
||||
internet/icecast/icecastmodel.h
|
||||
@ -694,7 +685,6 @@ set(UI
|
||||
globalsearch/suggestionwidget.ui
|
||||
|
||||
internet/digitally/digitallyimportedsettingspage.ui
|
||||
internet/grooveshark/groovesharksettingspage.ui
|
||||
internet/icecast/icecastfilterwidget.ui
|
||||
internet/core/internetshowsettingspage.ui
|
||||
internet/core/internetviewcontainer.ui
|
||||
@ -855,7 +845,7 @@ optional_source(HAVE_SPOTIFY_DOWNLOADER
|
||||
HEADERS
|
||||
internet/spotify/spotifyblobdownloader.h
|
||||
INCLUDE_DIRECTORIES
|
||||
${QCA_INCLUDE_DIRS}
|
||||
${CRYPTOPP_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Platform specific - OS X
|
||||
@ -1255,6 +1245,7 @@ target_link_libraries(clementine_lib
|
||||
${ECHONEST_LIBRARIES}
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GLIB_LIBRARIES}
|
||||
${GIO_LIBRARIES}
|
||||
${QJSON_LIBRARIES}
|
||||
${QT_LIBRARIES}
|
||||
${GSTREAMER_BASE_LIBRARIES}
|
||||
@ -1315,9 +1306,9 @@ endif(HAVE_BREAKPAD)
|
||||
|
||||
if(HAVE_SPOTIFY_DOWNLOADER)
|
||||
target_link_libraries(clementine_lib
|
||||
${QCA_LIBRARIES}
|
||||
${CRYPTOPP_LIBRARIES}
|
||||
)
|
||||
link_directories(${QCA_LIBRARY_DIRS})
|
||||
link_directories(${CRYPTOPP_LIBRARY_DIRS})
|
||||
endif(HAVE_SPOTIFY_DOWNLOADER)
|
||||
|
||||
if(HAVE_LIBPULSE)
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2003, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009, 2011-2012, David Sansome <me@davidsansome.com>
|
||||
Copyright 2010, 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -33,6 +33,7 @@
|
||||
#include <QtDebug>
|
||||
|
||||
#include "engines/enginebase.h"
|
||||
#include "core/arraysize.h"
|
||||
|
||||
// INSTRUCTIONS Base2D
|
||||
// 1. do anything that depends on height() in init(), Base2D will call it before
|
||||
@ -45,7 +46,8 @@
|
||||
// TODO(David Sansome): make an INSTRUCTIONS file
|
||||
// can't mod scope in analyze you have to use transform
|
||||
|
||||
// TODO(John Maguire): for 2D use setErasePixmap Qt function insetead of m_background
|
||||
// TODO(John Maguire): for 2D use setErasePixmap Qt function insetead of
|
||||
// m_background
|
||||
|
||||
// make the linker happy only for gcc < 4.0
|
||||
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
|
||||
@ -53,20 +55,29 @@
|
||||
template class Analyzer::Base<QWidget>;
|
||||
#endif
|
||||
|
||||
static const int sBarkBands[] = {
|
||||
100, 200, 300, 400, 510, 630, 770, 920, 1080, 1270, 1480, 1720,
|
||||
2000, 2320, 2700, 3150, 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500};
|
||||
|
||||
static const int sBarkBandCount = arraysize(sBarkBands);
|
||||
|
||||
Analyzer::Base::Base(QWidget* parent, uint scopeSize)
|
||||
: QWidget(parent),
|
||||
m_timeout(40) // msec
|
||||
timeout_(40) // msec
|
||||
,
|
||||
m_fht(new FHT(scopeSize)),
|
||||
m_engine(nullptr),
|
||||
m_lastScope(512),
|
||||
current_chunk_(0),
|
||||
fht_(new FHT(scopeSize)),
|
||||
engine_(nullptr),
|
||||
lastScope_(512),
|
||||
new_frame_(false),
|
||||
is_playing_(false) {}
|
||||
is_playing_(false),
|
||||
barkband_table_(QList<uint>()),
|
||||
prev_color_index_(0),
|
||||
bands_(0),
|
||||
psychedelic_enabled_(false) {}
|
||||
|
||||
void Analyzer::Base::hideEvent(QHideEvent*) { m_timer.stop(); }
|
||||
void Analyzer::Base::hideEvent(QHideEvent*) { timer_.stop(); }
|
||||
|
||||
void Analyzer::Base::showEvent(QShowEvent*) { m_timer.start(timeout(), this); }
|
||||
void Analyzer::Base::showEvent(QShowEvent*) { timer_.start(timeout(), this); }
|
||||
|
||||
void Analyzer::Base::transform(Scope& scope) {
|
||||
// this is a standard transformation that should give
|
||||
@ -74,16 +85,16 @@ void Analyzer::Base::transform(Scope& scope) {
|
||||
|
||||
// NOTE resizing here is redundant as FHT routines only calculate FHT::size()
|
||||
// values
|
||||
// scope.resize( m_fht->size() );
|
||||
// scope.resize( fht_->size() );
|
||||
|
||||
float* front = static_cast<float*>(&scope.front());
|
||||
|
||||
float* f = new float[m_fht->size()];
|
||||
m_fht->copy(&f[0], front);
|
||||
m_fht->logSpectrum(front, &f[0]);
|
||||
m_fht->scale(front, 1.0 / 20);
|
||||
float* f = new float[fht_->size()];
|
||||
fht_->copy(&f[0], front);
|
||||
fht_->logSpectrum(front, &f[0]);
|
||||
fht_->scale(front, 1.0 / 20);
|
||||
|
||||
scope.resize(m_fht->size() / 2); // second half of values are rubbish
|
||||
scope.resize(fht_->size() / 2); // second half of values are rubbish
|
||||
delete[] f;
|
||||
}
|
||||
|
||||
@ -91,30 +102,30 @@ void Analyzer::Base::paintEvent(QPaintEvent* e) {
|
||||
QPainter p(this);
|
||||
p.fillRect(e->rect(), palette().color(QPalette::Window));
|
||||
|
||||
switch (m_engine->state()) {
|
||||
switch (engine_->state()) {
|
||||
case Engine::Playing: {
|
||||
const Engine::Scope& thescope = m_engine->scope(m_timeout);
|
||||
const Engine::Scope& thescope = engine_->scope(timeout_);
|
||||
int i = 0;
|
||||
|
||||
// convert to mono here - our built in analyzers need mono, but the
|
||||
// engines provide interleaved pcm
|
||||
for (uint x = 0; static_cast<int>(x) < m_fht->size(); ++x) {
|
||||
m_lastScope[x] =
|
||||
static_cast<double>(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
|
||||
for (uint x = 0; static_cast<int>(x) < fht_->size(); ++x) {
|
||||
lastScope_[x] = static_cast<double>(thescope[i] + thescope[i + 1]) /
|
||||
(2 * (1 << 15));
|
||||
i += 2;
|
||||
}
|
||||
|
||||
is_playing_ = true;
|
||||
transform(m_lastScope);
|
||||
analyze(p, m_lastScope, new_frame_);
|
||||
transform(lastScope_);
|
||||
analyze(p, lastScope_, new_frame_);
|
||||
|
||||
// scope.resize( m_fht->size() );
|
||||
// scope.resize( fht_->size() );
|
||||
|
||||
break;
|
||||
}
|
||||
case Engine::Paused:
|
||||
is_playing_ = false;
|
||||
analyze(p, m_lastScope, new_frame_);
|
||||
analyze(p, lastScope_, new_frame_);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -131,9 +142,9 @@ int Analyzer::Base::resizeExponent(int exp) {
|
||||
else if (exp > 9)
|
||||
exp = 9;
|
||||
|
||||
if (exp != m_fht->sizeExp()) {
|
||||
delete m_fht;
|
||||
m_fht = new FHT(exp);
|
||||
if (exp != fht_->sizeExp()) {
|
||||
delete fht_;
|
||||
fht_ = new FHT(exp);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
@ -154,7 +165,7 @@ int Analyzer::Base::resizeForBands(int bands) {
|
||||
exp = 9;
|
||||
|
||||
resizeExponent(exp);
|
||||
return m_fht->size() / 2;
|
||||
return fht_->size() / 2;
|
||||
}
|
||||
|
||||
void Analyzer::Base::demo(QPainter& p) {
|
||||
@ -175,6 +186,67 @@ void Analyzer::Base::demo(QPainter& p) {
|
||||
++t;
|
||||
}
|
||||
|
||||
void Analyzer::Base::psychedelicModeChanged(bool enabled) {
|
||||
psychedelic_enabled_ = enabled;
|
||||
}
|
||||
|
||||
int Analyzer::Base::BandFrequency(int band) const {
|
||||
return ((kSampleRate / 2) * band + kSampleRate / 4) / bands_;
|
||||
}
|
||||
|
||||
void Analyzer::Base::updateBandSize(const int scopeSize) {
|
||||
// prevent possible dbz in BandFrequency
|
||||
if (scopeSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bands_ = scopeSize;
|
||||
|
||||
barkband_table_.clear();
|
||||
barkband_table_.reserve(bands_ + 1);
|
||||
|
||||
int barkband = 0;
|
||||
for (int i = 0; i < bands_ + 1; ++i) {
|
||||
if (barkband < sBarkBandCount - 1 &&
|
||||
BandFrequency(i) >= sBarkBands[barkband]) {
|
||||
barkband++;
|
||||
}
|
||||
|
||||
barkband_table_.append(barkband);
|
||||
}
|
||||
}
|
||||
|
||||
QColor Analyzer::Base::getPsychedelicColor(const Scope& scope,
|
||||
const int ampFactor,
|
||||
const int bias) {
|
||||
if (scope.size() > barkband_table_.length()) {
|
||||
return palette().color(QPalette::Highlight);
|
||||
}
|
||||
|
||||
// Calculate total magnitudes for different bark bands.
|
||||
double bands[sBarkBandCount]{};
|
||||
|
||||
for (int i = 0; i < barkband_table_.size(); ++i) {
|
||||
bands[barkband_table_[i]] += scope[i];
|
||||
}
|
||||
|
||||
// Now divide the bark bands into thirds and compute their total amplitudes.
|
||||
double rgb[3]{};
|
||||
for (int i = 0; i < sBarkBandCount - 1; ++i) {
|
||||
rgb[(i * 3) / sBarkBandCount] += bands[i] * bands[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
// bias colours for a threshold around normally amplified audio
|
||||
rgb[i] = (int)((sqrt(rgb[i]) * ampFactor) + bias);
|
||||
if (rgb[i] > 255) {
|
||||
rgb[i] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return QColor::fromRgb(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
void Analyzer::Base::polishEvent() {
|
||||
init(); // virtual
|
||||
}
|
||||
@ -211,7 +283,7 @@ void Analyzer::initSin(Scope& v, const uint size) {
|
||||
|
||||
void Analyzer::Base::timerEvent(QTimerEvent* e) {
|
||||
QWidget::timerEvent(e);
|
||||
if (e->timerId() != m_timer.timerId()) return;
|
||||
if (e->timerId() != timer_.timerId()) return;
|
||||
|
||||
new_frame_ = true;
|
||||
update();
|
||||
|
@ -3,7 +3,7 @@
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -58,21 +58,22 @@ class Base : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~Base() { delete m_fht; }
|
||||
~Base() { delete fht_; }
|
||||
|
||||
uint timeout() const { return m_timeout; }
|
||||
uint timeout() const { return timeout_; }
|
||||
|
||||
void set_engine(EngineBase* engine) { m_engine = engine; }
|
||||
void set_engine(EngineBase* engine) { engine_ = engine; }
|
||||
|
||||
void changeTimeout(uint newTimeout) {
|
||||
m_timeout = newTimeout;
|
||||
if (m_timer.isActive()) {
|
||||
m_timer.stop();
|
||||
m_timer.start(m_timeout, this);
|
||||
timeout_ = newTimeout;
|
||||
if (timer_.isActive()) {
|
||||
timer_.stop();
|
||||
timer_.start(timeout_, this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void framerateChanged() {}
|
||||
virtual void psychedelicModeChanged(bool);
|
||||
|
||||
protected:
|
||||
explicit Base(QWidget*, uint scopeSize = 7);
|
||||
@ -86,21 +87,32 @@ class Base : public QWidget {
|
||||
|
||||
int resizeExponent(int);
|
||||
int resizeForBands(int);
|
||||
int BandFrequency(int) const;
|
||||
void updateBandSize(const int);
|
||||
QColor getPsychedelicColor(const Scope&, const int, const int);
|
||||
virtual void init() {}
|
||||
virtual void transform(Scope&);
|
||||
virtual void analyze(QPainter& p, const Scope&, bool new_frame) = 0;
|
||||
virtual void demo(QPainter& p);
|
||||
|
||||
protected:
|
||||
QBasicTimer m_timer;
|
||||
uint m_timeout;
|
||||
FHT* m_fht;
|
||||
EngineBase* m_engine;
|
||||
Scope m_lastScope;
|
||||
int current_chunk_;
|
||||
static const int kSampleRate =
|
||||
44100; // we shouldn't need to care about ultrasonics
|
||||
|
||||
QBasicTimer timer_;
|
||||
uint timeout_;
|
||||
FHT* fht_;
|
||||
EngineBase* engine_;
|
||||
Scope lastScope_;
|
||||
|
||||
bool new_frame_;
|
||||
bool is_playing_;
|
||||
|
||||
QList<uint> barkband_table_;
|
||||
double prev_colors_[10][3];
|
||||
int prev_color_index_;
|
||||
int bands_;
|
||||
bool psychedelic_enabled_;
|
||||
};
|
||||
|
||||
void interpolate(const Scope&, Scope&);
|
||||
|
@ -3,7 +3,7 @@
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2011-2012, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2013, Vasily Fomin <vasili.fomin@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -57,6 +57,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
|
||||
visualisation_action_(nullptr),
|
||||
double_click_timer_(new QTimer(this)),
|
||||
ignore_next_click_(false),
|
||||
psychedelic_colors_on_(false),
|
||||
current_analyzer_(nullptr),
|
||||
engine_(nullptr) {
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
@ -88,6 +89,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
|
||||
disable_action_->setCheckable(true);
|
||||
group_->addAction(disable_action_);
|
||||
|
||||
context_menu_->addSeparator();
|
||||
psychedelic_enable_ = context_menu_->addAction(
|
||||
tr("Use Psychedelic Colors"), this, SLOT(TogglePsychedelicColors()));
|
||||
psychedelic_enable_->setCheckable(true);
|
||||
|
||||
context_menu_->addSeparator();
|
||||
// Visualisation action gets added in SetActions
|
||||
|
||||
@ -145,6 +151,14 @@ void AnalyzerContainer::DisableAnalyzer() {
|
||||
Save();
|
||||
}
|
||||
|
||||
void AnalyzerContainer::TogglePsychedelicColors() {
|
||||
psychedelic_colors_on_ = !psychedelic_colors_on_;
|
||||
if (current_analyzer_) {
|
||||
current_analyzer_->psychedelicModeChanged(psychedelic_colors_on_);
|
||||
}
|
||||
SavePsychedelic();
|
||||
}
|
||||
|
||||
void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||
QObject* instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
|
||||
|
||||
@ -161,6 +175,7 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||
current_framerate_ =
|
||||
current_framerate_ == 0 ? kMediumFramerate : current_framerate_;
|
||||
current_analyzer_->changeTimeout(1000 / current_framerate_);
|
||||
current_analyzer_->psychedelicModeChanged(psychedelic_colors_on_);
|
||||
|
||||
layout()->addWidget(current_analyzer_);
|
||||
|
||||
@ -183,6 +198,10 @@ void AnalyzerContainer::Load() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
// Colours
|
||||
psychedelic_colors_on_ = s.value("psychedelic", false).toBool();
|
||||
psychedelic_enable_->setChecked(psychedelic_colors_on_);
|
||||
|
||||
// Analyzer
|
||||
QString type = s.value("type", "BlockAnalyzer").toString();
|
||||
if (type.isEmpty()) {
|
||||
@ -227,6 +246,13 @@ void AnalyzerContainer::Save() {
|
||||
: QVariant());
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SavePsychedelic() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
s.setValue("psychedelic", psychedelic_colors_on_);
|
||||
}
|
||||
|
||||
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
|
||||
QAction* action =
|
||||
context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
|
||||
|
@ -4,6 +4,7 @@
|
||||
Copyright 2011-2012, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2013, Vasily Fomin <vasili.fomin@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2015, Mark Furneaux <mark@furneaux.ca>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -40,7 +41,7 @@ class AnalyzerContainer : public QWidget {
|
||||
static const char* kSettingsGroup;
|
||||
static const char* kSettingsFramerate;
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void WheelEvent(int delta);
|
||||
|
||||
protected:
|
||||
@ -53,6 +54,7 @@ class AnalyzerContainer : public QWidget {
|
||||
void ChangeFramerate(int new_framerate);
|
||||
void DisableAnalyzer();
|
||||
void ShowPopupMenu();
|
||||
void TogglePsychedelicColors();
|
||||
|
||||
private:
|
||||
static const int kLowFramerate;
|
||||
@ -63,6 +65,7 @@ class AnalyzerContainer : public QWidget {
|
||||
void Load();
|
||||
void Save();
|
||||
void SaveFramerate(int framerate);
|
||||
void SavePsychedelic();
|
||||
template <typename T>
|
||||
void AddAnalyzerType();
|
||||
void AddFramerate(const QString& name, int framerate);
|
||||
@ -80,11 +83,13 @@ class AnalyzerContainer : public QWidget {
|
||||
QList<int> framerate_list_;
|
||||
QList<QAction*> actions_;
|
||||
QAction* disable_action_;
|
||||
QAction* psychedelic_enable_;
|
||||
|
||||
QAction* visualisation_action_;
|
||||
QTimer* double_click_timer_;
|
||||
QPoint last_click_pos_;
|
||||
bool ignore_next_click_;
|
||||
bool psychedelic_colors_on_;
|
||||
|
||||
Analyzer::Base* current_analyzer_;
|
||||
EngineBase* engine_;
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2003, Mark Kretschmann <markey@web.de>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
@ -23,7 +23,6 @@
|
||||
/* Original Author: Mark Kretschmann <markey@web.de> 2003
|
||||
*/
|
||||
|
||||
|
||||
#include "baranalyzer.h"
|
||||
#include <cmath>
|
||||
#include <QtDebug>
|
||||
@ -36,19 +35,20 @@ const char* BarAnalyzer::kName =
|
||||
|
||||
BarAnalyzer::BarAnalyzer(QWidget* parent) : Analyzer::Base(parent, 8) {
|
||||
// roof pixmaps don't depend on size() so we do in the ctor
|
||||
m_bg = parent->palette().color(QPalette::Background);
|
||||
bg_ = parent->palette().color(QPalette::Background);
|
||||
|
||||
QColor fg(parent->palette().color(QPalette::Highlight).lighter(150));
|
||||
|
||||
double dr = static_cast<double>(m_bg.red() - fg.red()) /
|
||||
(NUM_ROOFS - 1); // -1 because we start loop below at 0
|
||||
double dg = static_cast<double>(m_bg.green() - fg.green()) / (NUM_ROOFS - 1);
|
||||
double db = static_cast<double>(m_bg.blue() - fg.blue()) / (NUM_ROOFS - 1);
|
||||
double dr = static_cast<double>(bg_.red() - fg.red()) /
|
||||
(kNumRoofs - 1); // -1 because we start loop below at 0
|
||||
double dg = static_cast<double>(bg_.green() - fg.green()) / (kNumRoofs - 1);
|
||||
double db = static_cast<double>(bg_.blue() - fg.blue()) / (kNumRoofs - 1);
|
||||
|
||||
for (uint i = 0; i < NUM_ROOFS; ++i) {
|
||||
m_pixRoof[i] = QPixmap(COLUMN_WIDTH, 1);
|
||||
m_pixRoof[i].fill(QColor(fg.red() + static_cast<int>(dr * i), fg.green() + static_cast<int>(dg * i),
|
||||
fg.blue() + static_cast<int>(db * i)));
|
||||
for (uint i = 0; i < kNumRoofs; ++i) {
|
||||
pixRoof_[i] = QPixmap(kColumnWidth, 1);
|
||||
pixRoof_[i].fill(QColor(fg.red() + static_cast<int>(dr * i),
|
||||
fg.green() + static_cast<int>(dg * i),
|
||||
fg.blue() + static_cast<int>(db * i)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,46 +58,66 @@ void BarAnalyzer::resizeEvent(QResizeEvent* e) { init(); }
|
||||
|
||||
void BarAnalyzer::init() {
|
||||
const double MAX_AMPLITUDE = 1.0;
|
||||
const double F = static_cast<double>(height() - 2) / (log10(255) * MAX_AMPLITUDE);
|
||||
const double F =
|
||||
static_cast<double>(height() - 2) / (log10(255) * MAX_AMPLITUDE);
|
||||
|
||||
BAND_COUNT = width() / 5;
|
||||
MAX_DOWN = static_cast<int>(0 - (qMax(1, height() / 50)));
|
||||
MAX_UP = static_cast<int>(qMax(1, height() / 25));
|
||||
band_count_ = width() / 5;
|
||||
max_down_ = static_cast<int>(0 - (qMax(1, height() / 50)));
|
||||
max_up_ = static_cast<int>(qMax(1, height() / 25));
|
||||
|
||||
barVector.resize(BAND_COUNT, 0);
|
||||
roofVector.resize(BAND_COUNT, height() - 5);
|
||||
roofVelocityVector.resize(BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR);
|
||||
m_roofMem.resize(BAND_COUNT);
|
||||
m_scope.resize(BAND_COUNT);
|
||||
barVector_.resize(band_count_, 0);
|
||||
roofVector_.resize(band_count_, height() - 5);
|
||||
roofVelocityVector_.resize(band_count_, kRoofVelocityReductionFactor);
|
||||
roofMem_.resize(band_count_);
|
||||
scope_.resize(band_count_);
|
||||
|
||||
// generate a list of values that express amplitudes in range 0-MAX_AMP as
|
||||
// ints from 0-height() on log scale
|
||||
for (uint x = 0; x < 256; ++x) {
|
||||
m_lvlMapper[x] = static_cast<uint>(F * log10(x + 1));
|
||||
lvlMapper_[x] = static_cast<uint>(F * log10(x + 1));
|
||||
}
|
||||
|
||||
m_pixBarGradient = QPixmap(height() * COLUMN_WIDTH, height());
|
||||
m_pixCompose = QPixmap(size());
|
||||
pixBarGradient_ = QPixmap(height() * kColumnWidth, height());
|
||||
pixCompose_ = QPixmap(size());
|
||||
canvas_ = QPixmap(size());
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
QPainter p(&m_pixBarGradient);
|
||||
QColor rgb(palette().color(QPalette::Highlight));
|
||||
updateBandSize(band_count_);
|
||||
colorChanged();
|
||||
setMinimumSize(QSize(band_count_ * kColumnWidth, 10));
|
||||
}
|
||||
|
||||
for (int x = 0, r = rgb.red(), g = rgb.green(), b = rgb.blue(), r2 = 255 - r; x < height();
|
||||
++x) {
|
||||
void BarAnalyzer::colorChanged() {
|
||||
if (pixBarGradient_.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPainter p(&pixBarGradient_);
|
||||
QColor rgb;
|
||||
if (psychedelic_enabled_) {
|
||||
rgb = getPsychedelicColor(scope_, 50, 100);
|
||||
} else {
|
||||
rgb = palette().color(QPalette::Highlight);
|
||||
}
|
||||
|
||||
for (int x = 0, r = rgb.red(), g = rgb.green(), b = rgb.blue(), r2 = 255 - r;
|
||||
x < height(); ++x) {
|
||||
for (int y = x; y > 0; --y) {
|
||||
const double fraction = static_cast<double>(y) / height();
|
||||
|
||||
// p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 *
|
||||
// fraction) ) );
|
||||
p.setPen(QColor(r + static_cast<int>(r2 * fraction), g, b));
|
||||
p.drawLine(x * COLUMN_WIDTH, height() - y, (x + 1) * COLUMN_WIDTH,
|
||||
p.drawLine(x * kColumnWidth, height() - y, (x + 1) * kColumnWidth,
|
||||
height() - y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setMinimumSize(QSize(BAND_COUNT * COLUMN_WIDTH, 10));
|
||||
void BarAnalyzer::psychedelicModeChanged(bool enabled) {
|
||||
psychedelic_enabled_ = enabled;
|
||||
// reset colours back to normal
|
||||
colorChanged();
|
||||
}
|
||||
|
||||
void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||
@ -107,20 +127,25 @@ void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||
}
|
||||
// Analyzer::interpolate( s, m_bands );
|
||||
|
||||
Scope& v = m_scope;
|
||||
Analyzer::interpolate(s, v);
|
||||
Analyzer::interpolate(s, scope_);
|
||||
QPainter canvas_painter(&canvas_);
|
||||
|
||||
// update the graphics with the new colour
|
||||
if (psychedelic_enabled_) {
|
||||
colorChanged();
|
||||
}
|
||||
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
for (uint i = 0, x = 0, y2; i < v.size(); ++i, x += COLUMN_WIDTH + 1) {
|
||||
for (uint i = 0, x = 0, y2; i < scope_.size(); ++i, x += kColumnWidth + 1) {
|
||||
// assign pre[log10]'d value
|
||||
y2 = static_cast<uint>(v[i] *
|
||||
256); // 256 will be optimised to a bitshift //no, it's a float
|
||||
y2 = m_lvlMapper[(y2 > 255) ? 255 : y2]; // lvlMapper is array of ints with
|
||||
// values 0 to height()
|
||||
y2 = static_cast<uint>(
|
||||
scope_[i] *
|
||||
256); // 256 will be optimised to a bitshift //no, it's a float
|
||||
y2 = lvlMapper_[(y2 > 255) ? 255 : y2]; // lvlMapper is array of ints with
|
||||
// values 0 to height()
|
||||
|
||||
int change = y2 - barVector[i];
|
||||
int change = y2 - barVector_[i];
|
||||
|
||||
// using the best of Markey's, piggz and Max's ideas on the way to shift the
|
||||
// bars
|
||||
@ -129,54 +154,52 @@ void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||
// 2. shift large upwards with a bias towards last value
|
||||
// 3. fall downwards at a constant pace
|
||||
|
||||
/*if ( change > MAX_UP ) //anything too much greater than 2 gives "jitter"
|
||||
/*if ( change > max_up_ ) //anything too much greater than 2 gives "jitter"
|
||||
//add some dynamics - makes the value slightly closer to what it was last time
|
||||
y2 = ( barVector[i] + MAX_UP );
|
||||
//y2 = ( barVector[i] * 2 + y2 ) / 3;
|
||||
y2 = ( barVector_[i] + max_up_ );
|
||||
//y2 = ( barVector_[i] * 2 + y2 ) / 3;
|
||||
else*/ if (change <
|
||||
MAX_DOWN)
|
||||
y2 = barVector[i] + MAX_DOWN;
|
||||
max_down_)
|
||||
y2 = barVector_[i] + max_down_;
|
||||
|
||||
if (static_cast<int>(y2) > roofVector[i]) {
|
||||
roofVector[i] = static_cast<int>(y2);
|
||||
roofVelocityVector[i] = 1;
|
||||
if (static_cast<int>(y2) > roofVector_[i]) {
|
||||
roofVector_[i] = static_cast<int>(y2);
|
||||
roofVelocityVector_[i] = 1;
|
||||
}
|
||||
|
||||
// remember where we are
|
||||
barVector[i] = y2;
|
||||
barVector_[i] = y2;
|
||||
|
||||
if (m_roofMem[i].size() > NUM_ROOFS)
|
||||
m_roofMem[i].erase(m_roofMem[i].begin());
|
||||
if (roofMem_[i].size() > kNumRoofs) roofMem_[i].erase(roofMem_[i].begin());
|
||||
|
||||
// blt last n roofs, a.k.a motion blur
|
||||
for (uint c = 0; c < m_roofMem[i].size(); ++c)
|
||||
// bitBlt( m_pComposePixmap, x, m_roofMem[i]->at( c ), m_roofPixmaps[ c ]
|
||||
for (uint c = 0; c < roofMem_[i].size(); ++c)
|
||||
// bitBlt( m_pComposePixmap, x, roofMem_[i]->at( c ), m_roofPixmaps[ c ]
|
||||
// );
|
||||
// bitBlt( canvas(), x, m_roofMem[i][c], &m_pixRoof[ NUM_ROOFS - 1 - c ]
|
||||
// bitBlt( canvas(), x, roofMem_[i][c], &pixRoof_[ kNumRoofs - 1 - c ]
|
||||
// );
|
||||
canvas_painter.drawPixmap(x, m_roofMem[i][c],
|
||||
m_pixRoof[NUM_ROOFS - 1 - c]);
|
||||
canvas_painter.drawPixmap(x, roofMem_[i][c], pixRoof_[kNumRoofs - 1 - c]);
|
||||
|
||||
// blt the bar
|
||||
canvas_painter.drawPixmap(x, height() - y2, *gradient(), y2 * COLUMN_WIDTH,
|
||||
height() - y2, COLUMN_WIDTH, y2);
|
||||
canvas_painter.drawPixmap(x, height() - y2, *gradient(), y2 * kColumnWidth,
|
||||
height() - y2, kColumnWidth, y2);
|
||||
/*bitBlt( canvas(), x, height() - y2,
|
||||
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2,
|
||||
gradient(), y2 * kColumnWidth, height() - y2, kColumnWidth, y2,
|
||||
Qt::CopyROP );*/
|
||||
|
||||
m_roofMem[i].push_back(height() - roofVector[i] - 2);
|
||||
roofMem_[i].push_back(height() - roofVector_[i] - 2);
|
||||
|
||||
// set roof parameters for the NEXT draw
|
||||
if (roofVelocityVector[i] != 0) {
|
||||
if (roofVelocityVector[i] > 32) // no reason to do == 32
|
||||
roofVector[i] -=
|
||||
(roofVelocityVector[i] - 32) / 20; // trivial calculation
|
||||
if (roofVelocityVector_[i] != 0) {
|
||||
if (roofVelocityVector_[i] > 32) // no reason to do == 32
|
||||
roofVector_[i] -=
|
||||
(roofVelocityVector_[i] - 32) / 20; // trivial calculation
|
||||
|
||||
if (roofVector[i] < 0) {
|
||||
roofVector[i] = 0; // not strictly necessary
|
||||
roofVelocityVector[i] = 0;
|
||||
if (roofVector_[i] < 0) {
|
||||
roofVector_[i] = 0; // not strictly necessary
|
||||
roofVelocityVector_[i] = 0;
|
||||
} else {
|
||||
++roofVelocityVector[i];
|
||||
++roofVelocityVector_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2005, Mark Kretschmann <markey@web.de>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof A. Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
@ -39,44 +39,45 @@ class BarAnalyzer : public Analyzer::Base {
|
||||
|
||||
void init();
|
||||
virtual void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
|
||||
// virtual void transform( Scope& );
|
||||
virtual void psychedelicModeChanged(bool);
|
||||
|
||||
/**
|
||||
* Resizes the widget to a new geometry according to @p e
|
||||
* @param e The resize-event
|
||||
*/
|
||||
void resizeEvent(QResizeEvent* e);
|
||||
void colorChanged();
|
||||
|
||||
uint BAND_COUNT;
|
||||
int MAX_DOWN;
|
||||
int MAX_UP;
|
||||
static const uint ROOF_HOLD_TIME = 48;
|
||||
static const int ROOF_VELOCITY_REDUCTION_FACTOR = 32;
|
||||
static const uint NUM_ROOFS = 16;
|
||||
static const uint COLUMN_WIDTH = 4;
|
||||
uint band_count_;
|
||||
int max_down_;
|
||||
int max_up_;
|
||||
static const uint kRoofHoldTime = 48;
|
||||
static const int kRoofVelocityReductionFactor = 32;
|
||||
static const uint kNumRoofs = 16;
|
||||
static const uint kColumnWidth = 4;
|
||||
|
||||
static const char* kName;
|
||||
|
||||
protected:
|
||||
QPixmap m_pixRoof[NUM_ROOFS];
|
||||
// vector<uint> m_roofMem[BAND_COUNT];
|
||||
QPixmap pixRoof_[kNumRoofs];
|
||||
// vector<uint> roofMem_[band_count_];
|
||||
|
||||
// Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope
|
||||
// every iteration
|
||||
uint m_lvlMapper[256];
|
||||
std::vector<aroofMemVec> m_roofMem;
|
||||
std::vector<uint> barVector; // positions of bars
|
||||
std::vector<int> roofVector; // positions of roofs
|
||||
std::vector<uint> roofVelocityVector; // speed that roofs falls
|
||||
uint lvlMapper_[256];
|
||||
std::vector<aroofMemVec> roofMem_;
|
||||
std::vector<uint> barVector_; // positions of bars
|
||||
std::vector<int> roofVector_; // positions of roofs
|
||||
std::vector<uint> roofVelocityVector_; // speed that roofs falls
|
||||
|
||||
const QPixmap* gradient() const { return &m_pixBarGradient; }
|
||||
const QPixmap* gradient() const { return &pixBarGradient_; }
|
||||
|
||||
private:
|
||||
QPixmap m_pixBarGradient;
|
||||
QPixmap m_pixCompose;
|
||||
QPixmap pixBarGradient_;
|
||||
QPixmap pixCompose_;
|
||||
QPixmap canvas_;
|
||||
Analyzer::Scope m_scope; // so we don't create a vector every frame
|
||||
QColor m_bg;
|
||||
Analyzer::Scope scope_; // so we don't create a vector every frame
|
||||
QColor bg_;
|
||||
};
|
||||
|
||||
#endif // ANALYZERS_BARANALYZER_H_
|
||||
|
@ -3,7 +3,7 @@
|
||||
Copyright 2005, Mark Kretschmann <markey@web.de>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -33,35 +33,34 @@
|
||||
#include <cstdlib>
|
||||
#include <QPainter>
|
||||
|
||||
const uint BlockAnalyzer::HEIGHT = 2;
|
||||
const uint BlockAnalyzer::WIDTH = 4;
|
||||
const uint BlockAnalyzer::MIN_ROWS = 3; // arbituary
|
||||
const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary
|
||||
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
|
||||
const uint BlockAnalyzer::FADE_SIZE = 90;
|
||||
const uint BlockAnalyzer::kHeight = 2;
|
||||
const uint BlockAnalyzer::kWidth = 4;
|
||||
const uint BlockAnalyzer::kMinRows = 3; // arbituary
|
||||
const uint BlockAnalyzer::kMinColumns = 32; // arbituary
|
||||
const uint BlockAnalyzer::kMaxColumns = 256; // must be 2**n
|
||||
const uint BlockAnalyzer::kFadeSize = 90;
|
||||
|
||||
const char* BlockAnalyzer::kName =
|
||||
QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
||||
|
||||
BlockAnalyzer::BlockAnalyzer(QWidget* parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
m_columns(0),
|
||||
m_rows(0),
|
||||
m_y(0),
|
||||
m_barPixmap(1, 1),
|
||||
m_topBarPixmap(WIDTH, HEIGHT),
|
||||
m_scope(MIN_COLUMNS),
|
||||
m_store(1 << 8, 0),
|
||||
m_fade_bars(FADE_SIZE),
|
||||
m_fade_pos(1 << 8, 50),
|
||||
m_fade_intensity(1 << 8, 32) {
|
||||
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1,
|
||||
MIN_ROWS * (HEIGHT + 1) - 1);
|
||||
columns_(0),
|
||||
rows_(0),
|
||||
y_(0),
|
||||
barPixmap_(1, 1),
|
||||
topBarPixmap_(kWidth, kHeight),
|
||||
scope_(kMinColumns),
|
||||
store_(1 << 8, 0),
|
||||
fade_bars_(kFadeSize),
|
||||
fade_pos_(1 << 8, 50),
|
||||
fade_intensity_(1 << 8, 32) {
|
||||
setMinimumSize(kMinColumns * (kWidth + 1) - 1, kMinRows * (kHeight + 1) - 1);
|
||||
// -1 is padding, no drawing takes place there
|
||||
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
|
||||
setMaximumWidth(kMaxColumns * (kWidth + 1) - 1);
|
||||
|
||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||
for (uint i = 0; i < FADE_SIZE; ++i) m_fade_bars[i] = QPixmap(1, 1);
|
||||
for (uint i = 0; i < kFadeSize; ++i) fade_bars_[i] = QPixmap(1, 1);
|
||||
}
|
||||
|
||||
BlockAnalyzer::~BlockAnalyzer() {}
|
||||
@ -69,41 +68,44 @@ BlockAnalyzer::~BlockAnalyzer() {}
|
||||
void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
QWidget::resizeEvent(e);
|
||||
|
||||
m_background = QPixmap(size());
|
||||
background_ = QPixmap(size());
|
||||
canvas_ = QPixmap(size());
|
||||
|
||||
const uint oldRows = m_rows;
|
||||
const uint oldRows = rows_;
|
||||
|
||||
// all is explained in analyze()..
|
||||
// +1 to counter -1 in maxSizes, trust me we need this!
|
||||
m_columns = qMax(static_cast<uint>(static_cast<double>(width() + 1) / (WIDTH + 1)), MAX_COLUMNS);
|
||||
m_rows = static_cast<uint>(static_cast<double>(height() + 1) / (HEIGHT + 1));
|
||||
columns_ = qMin(
|
||||
static_cast<uint>(static_cast<double>(width() + 1) / (kWidth + 1)) + 1,
|
||||
kMaxColumns);
|
||||
rows_ = static_cast<uint>(static_cast<double>(height() + 1) / (kHeight + 1));
|
||||
|
||||
// this is the y-offset for drawing from the top of the widget
|
||||
m_y = (height() - (m_rows * (HEIGHT + 1)) + 2) / 2;
|
||||
y_ = (height() - (rows_ * (kHeight + 1)) + 2) / 2;
|
||||
|
||||
m_scope.resize(m_columns);
|
||||
scope_.resize(columns_);
|
||||
|
||||
if (m_rows != oldRows) {
|
||||
m_barPixmap = QPixmap(WIDTH, m_rows * (HEIGHT + 1));
|
||||
if (rows_ != oldRows) {
|
||||
barPixmap_ = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||
|
||||
for (uint i = 0; i < FADE_SIZE; ++i)
|
||||
m_fade_bars[i] = QPixmap(WIDTH, m_rows * (HEIGHT + 1));
|
||||
for (uint i = 0; i < kFadeSize; ++i)
|
||||
fade_bars_[i] = QPixmap(kWidth, rows_ * (kHeight + 1));
|
||||
|
||||
m_yscale.resize(m_rows + 1);
|
||||
yscale_.resize(rows_ + 1);
|
||||
|
||||
const uint PRE = 1,
|
||||
PRO = 1; // PRE and PRO allow us to restrict the range somewhat
|
||||
|
||||
for (uint z = 0; z < m_rows; ++z)
|
||||
m_yscale[z] = 1 - (log10(PRE + z) / log10(PRE + m_rows + PRO));
|
||||
for (uint z = 0; z < rows_; ++z)
|
||||
yscale_[z] = 1 - (log10(PRE + z) / log10(PRE + rows_ + PRO));
|
||||
|
||||
m_yscale[m_rows] = 0;
|
||||
yscale_[rows_] = 0;
|
||||
|
||||
determineStep();
|
||||
paletteChange(palette());
|
||||
}
|
||||
|
||||
updateBandSize(columns_);
|
||||
drawBackground();
|
||||
}
|
||||
|
||||
@ -113,9 +115,9 @@ void BlockAnalyzer::determineStep() {
|
||||
// I calculated the value 30 based on some trial and error
|
||||
|
||||
// the fall time of 30 is too slow on framerates above 50fps
|
||||
const double fallTime = timeout() < 20 ? 20 * m_rows : 30 * m_rows;
|
||||
const double fallTime = timeout() < 20 ? 20 * rows_ : 30 * rows_;
|
||||
|
||||
m_step = static_cast<double>(m_rows * timeout()) / fallTime;
|
||||
step_ = static_cast<double>(rows_ * timeout()) / fallTime;
|
||||
}
|
||||
|
||||
void BlockAnalyzer::framerateChanged() { // virtual
|
||||
@ -127,15 +129,14 @@ void BlockAnalyzer::transform(Analyzer::Scope& s) {
|
||||
|
||||
float* front = static_cast<float*>(&s.front());
|
||||
|
||||
m_fht->spectrum(front);
|
||||
m_fht->scale(front, 1.0 / 20);
|
||||
fht_->spectrum(front);
|
||||
fht_->scale(front, 1.0 / 20);
|
||||
|
||||
// the second half is pretty dull, so only show it if the user has a large
|
||||
// analyzer
|
||||
// by setting to m_scope.size() if large we prevent interpolation of large
|
||||
// by setting to scope_.size() if large we prevent interpolation of large
|
||||
// analyzers, this is good!
|
||||
s.resize(m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2
|
||||
: m_scope.size());
|
||||
s.resize(scope_.size() <= kMaxColumns / 2 ? kMaxColumns / 2 : scope_.size());
|
||||
}
|
||||
|
||||
void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||
@ -150,7 +151,7 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||
// y represents the number of blanks
|
||||
// y starts from the top and increases in units of blocks
|
||||
|
||||
// m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
|
||||
// yscale_ looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
|
||||
// if it contains 6 elements there are 5 rows in the analyzer
|
||||
|
||||
if (!new_frame) {
|
||||
@ -160,51 +161,55 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||
|
||||
QPainter canvas_painter(&canvas_);
|
||||
|
||||
Analyzer::interpolate(s, m_scope);
|
||||
Analyzer::interpolate(s, scope_);
|
||||
|
||||
// update the graphics with the new colour
|
||||
if (psychedelic_enabled_) {
|
||||
paletteChange(QPalette());
|
||||
}
|
||||
|
||||
// Paint the background
|
||||
canvas_painter.drawPixmap(0, 0, m_background);
|
||||
canvas_painter.drawPixmap(0, 0, background_);
|
||||
|
||||
for (uint y, x = 0; x < m_scope.size(); ++x) {
|
||||
for (uint y, x = 0; x < scope_.size(); ++x) {
|
||||
// determine y
|
||||
for (y = 0; m_scope[x] < m_yscale[y]; ++y)
|
||||
continue;
|
||||
for (y = 0; scope_[x] < yscale_[y]; ++y) continue;
|
||||
|
||||
// this is opposite to what you'd think, higher than y
|
||||
// means the bar is lower than y (physically)
|
||||
if (static_cast<float>(y) > m_store[x])
|
||||
y = static_cast<int>(m_store[x] += m_step);
|
||||
if (static_cast<float>(y) > store_[x])
|
||||
y = static_cast<int>(store_[x] += step_);
|
||||
else
|
||||
m_store[x] = y;
|
||||
store_[x] = y;
|
||||
|
||||
// if y is lower than m_fade_pos, then the bar has exceeded the height of
|
||||
// if y is lower than fade_pos_, then the bar has exceeded the kHeight of
|
||||
// the fadeout
|
||||
// if the fadeout is quite faded now, then display the new one
|
||||
if (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) {
|
||||
m_fade_pos[x] = y;
|
||||
m_fade_intensity[x] = FADE_SIZE;
|
||||
if (y <= fade_pos_[x] /*|| fade_intensity_[x] < kFadeSize / 3*/) {
|
||||
fade_pos_[x] = y;
|
||||
fade_intensity_[x] = kFadeSize;
|
||||
}
|
||||
|
||||
if (m_fade_intensity[x] > 0) {
|
||||
const uint offset = --m_fade_intensity[x];
|
||||
const uint y = m_y + (m_fade_pos[x] * (HEIGHT + 1));
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y, m_fade_bars[offset], 0, 0,
|
||||
WIDTH, height() - y);
|
||||
if (fade_intensity_[x] > 0) {
|
||||
const uint offset = --fade_intensity_[x];
|
||||
const uint y = y_ + (fade_pos_[x] * (kHeight + 1));
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), y, fade_bars_[offset], 0, 0,
|
||||
kWidth, height() - y);
|
||||
}
|
||||
|
||||
if (m_fade_intensity[x] == 0) m_fade_pos[x] = m_rows;
|
||||
if (fade_intensity_[x] == 0) fade_pos_[x] = rows_;
|
||||
|
||||
// REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing,
|
||||
// m_rows means none are
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, *bar(),
|
||||
0, y * (HEIGHT + 1), bar()->width(),
|
||||
// REMEMBER: y is a number from 0 to rows_, 0 means all blocks are glowing,
|
||||
// rows_ means none are
|
||||
canvas_painter.drawPixmap(x * (kWidth + 1), y * (kHeight + 1) + y_, *bar(),
|
||||
0, y * (kHeight + 1), bar()->width(),
|
||||
bar()->height());
|
||||
}
|
||||
|
||||
for (uint x = 0; x < m_store.size(); ++x)
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1),
|
||||
static_cast<int>(m_store[x]) * (HEIGHT + 1) + m_y,
|
||||
m_topBarPixmap);
|
||||
for (uint 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_);
|
||||
}
|
||||
@ -232,6 +237,12 @@ static inline void adjustToLimits(int& b, int& f, uint& amount) {
|
||||
}
|
||||
}
|
||||
|
||||
void BlockAnalyzer::psychedelicModeChanged(bool enabled) {
|
||||
psychedelic_enabled_ = enabled;
|
||||
// reset colours back to normal
|
||||
paletteChange(QPalette());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clever contrast function
|
||||
*
|
||||
@ -305,7 +316,8 @@ QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) {
|
||||
if (static_cast<int>(_amount) > 0) adjustToLimits(bs, fs, _amount);
|
||||
|
||||
// see if we need to adjust the hue
|
||||
if (static_cast<int>(_amount) > 0) fh += static_cast<int>(_amount); // cycles around;
|
||||
if (static_cast<int>(_amount) > 0)
|
||||
fh += static_cast<int>(_amount); // cycles around;
|
||||
|
||||
return QColor::fromHsv(fh, fs, fv);
|
||||
}
|
||||
@ -327,23 +339,34 @@ QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) {
|
||||
|
||||
void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
const QColor bg = palette().color(QPalette::Background);
|
||||
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
|
||||
QColor fg;
|
||||
|
||||
m_topBarPixmap.fill(fg);
|
||||
if (psychedelic_enabled_) {
|
||||
fg = getPsychedelicColor(scope_, 10, 75);
|
||||
} else {
|
||||
fg = ensureContrast(bg, palette().color(QPalette::Highlight));
|
||||
}
|
||||
|
||||
const double dr = 15 * static_cast<double>(bg.red() - fg.red()) / (m_rows * 16);
|
||||
const double dg = 15 * static_cast<double>(bg.green() - fg.green()) / (m_rows * 16);
|
||||
const double db = 15 * static_cast<double>(bg.blue() - fg.blue()) / (m_rows * 16);
|
||||
topBarPixmap_.fill(fg);
|
||||
|
||||
const double dr =
|
||||
15 * static_cast<double>(bg.red() - fg.red()) / (rows_ * 16);
|
||||
const double dg =
|
||||
15 * static_cast<double>(bg.green() - fg.green()) / (rows_ * 16);
|
||||
const double db =
|
||||
15 * static_cast<double>(bg.blue() - fg.blue()) / (rows_ * 16);
|
||||
const int r = fg.red(), g = fg.green(), b = fg.blue();
|
||||
|
||||
bar()->fill(bg);
|
||||
|
||||
QPainter p(bar());
|
||||
for (int y = 0; static_cast<uint>(y) < m_rows; ++y)
|
||||
|
||||
for (int y = 0; static_cast<uint>(y) < rows_; ++y)
|
||||
// graduate the fg color
|
||||
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT,
|
||||
QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y),
|
||||
b + static_cast<int>(db * y)));
|
||||
p.fillRect(
|
||||
0, y * (kHeight + 1), kWidth, kHeight,
|
||||
QColor(r + static_cast<int>(dr * y), g + static_cast<int>(dg * y),
|
||||
b + static_cast<int>(db * y)));
|
||||
|
||||
{
|
||||
const QColor bg = palette().color(QPalette::Background).dark(112);
|
||||
@ -360,13 +383,15 @@ void BlockAnalyzer::paletteChange(const QPalette&) {
|
||||
const int r = bg.red(), g = bg.green(), b = bg.blue();
|
||||
|
||||
// Precalculate all fade-bar pixmaps
|
||||
for (uint y = 0; y < FADE_SIZE; ++y) {
|
||||
m_fade_bars[y].fill(palette().color(QPalette::Background));
|
||||
QPainter f(&m_fade_bars[y]);
|
||||
for (int z = 0; static_cast<uint>(z) < m_rows; ++z) {
|
||||
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
|
||||
f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT,
|
||||
QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y), b + static_cast<int>(db * Y)));
|
||||
for (uint y = 0; y < kFadeSize; ++y) {
|
||||
fade_bars_[y].fill(palette().color(QPalette::Background));
|
||||
QPainter f(&fade_bars_[y]);
|
||||
for (int z = 0; static_cast<uint>(z) < rows_; ++z) {
|
||||
const double Y = 1.0 - (log10(kFadeSize - y) / log10(kFadeSize));
|
||||
f.fillRect(
|
||||
0, z * (kHeight + 1), kWidth, kHeight,
|
||||
QColor(r + static_cast<int>(dr * Y), g + static_cast<int>(dg * Y),
|
||||
b + static_cast<int>(db * Y)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -378,11 +403,16 @@ void BlockAnalyzer::drawBackground() {
|
||||
const QColor bg = palette().color(QPalette::Background);
|
||||
const QColor bgdark = bg.dark(112);
|
||||
|
||||
m_background.fill(bg);
|
||||
background_.fill(bg);
|
||||
|
||||
QPainter p(&m_background);
|
||||
for (int x = 0; (uint)x < m_columns; ++x)
|
||||
for (int y = 0; (uint)y < m_rows; ++y)
|
||||
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, WIDTH, HEIGHT,
|
||||
QPainter p(&background_);
|
||||
|
||||
if (p.paintEngine() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int x = 0; (uint)x < columns_; ++x)
|
||||
for (int y = 0; (uint)y < rows_; ++y)
|
||||
p.fillRect(x * (kWidth + 1), y * (kHeight + 1) + y_, kWidth, kHeight,
|
||||
bgdark);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2003-2005, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof A. Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -39,12 +39,12 @@ class BlockAnalyzer : public Analyzer::Base {
|
||||
Q_INVOKABLE BlockAnalyzer(QWidget*);
|
||||
~BlockAnalyzer();
|
||||
|
||||
static const uint HEIGHT;
|
||||
static const uint WIDTH;
|
||||
static const uint MIN_ROWS;
|
||||
static const uint MIN_COLUMNS;
|
||||
static const uint MAX_COLUMNS;
|
||||
static const uint FADE_SIZE;
|
||||
static const uint kHeight;
|
||||
static const uint kWidth;
|
||||
static const uint kMinRows;
|
||||
static const uint kMinColumns;
|
||||
static const uint kMaxColumns;
|
||||
static const uint kFadeSize;
|
||||
|
||||
static const char* kName;
|
||||
|
||||
@ -54,29 +54,30 @@ class BlockAnalyzer : public Analyzer::Base {
|
||||
virtual void resizeEvent(QResizeEvent*);
|
||||
virtual void paletteChange(const QPalette&);
|
||||
virtual void framerateChanged();
|
||||
virtual void psychedelicModeChanged(bool);
|
||||
|
||||
void drawBackground();
|
||||
void determineStep();
|
||||
|
||||
private:
|
||||
QPixmap* bar() { return &m_barPixmap; }
|
||||
QPixmap* bar() { return &barPixmap_; }
|
||||
|
||||
uint m_columns, m_rows; // number of rows and columns of blocks
|
||||
uint m_y; // y-offset from top of widget
|
||||
QPixmap m_barPixmap;
|
||||
QPixmap m_topBarPixmap;
|
||||
QPixmap m_background;
|
||||
uint columns_, rows_; // number of rows and columns of blocks
|
||||
uint y_; // y-offset from top of widget
|
||||
QPixmap barPixmap_;
|
||||
QPixmap topBarPixmap_;
|
||||
QPixmap background_;
|
||||
QPixmap canvas_;
|
||||
Analyzer::Scope m_scope; // so we don't create a vector every frame
|
||||
std::vector<float> m_store; // current bar heights
|
||||
std::vector<float> m_yscale;
|
||||
Analyzer::Scope scope_; // so we don't create a vector every frame
|
||||
std::vector<float> store_; // current bar kHeights
|
||||
std::vector<float> yscale_;
|
||||
|
||||
// FIXME why can't I namespace these? c++ issue?
|
||||
std::vector<QPixmap> m_fade_bars;
|
||||
std::vector<uint> m_fade_pos;
|
||||
std::vector<int> m_fade_intensity;
|
||||
std::vector<QPixmap> fade_bars_;
|
||||
std::vector<uint> fade_pos_;
|
||||
std::vector<int> fade_intensity_;
|
||||
|
||||
float m_step; // rows to fall per frame
|
||||
float step_; // rows to fall per frame
|
||||
};
|
||||
|
||||
#endif // ANALYZERS_BLOCKANALYZER_H_
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2004, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -28,69 +28,78 @@
|
||||
|
||||
using Analyzer::Scope;
|
||||
|
||||
const uint BoomAnalyzer::kColumnWidth = 4;
|
||||
const uint BoomAnalyzer::kMaxBandCount = 256;
|
||||
const uint BoomAnalyzer::kMinBandCount = 32;
|
||||
|
||||
const char* BoomAnalyzer::kName =
|
||||
QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
|
||||
|
||||
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
K_barHeight(1.271) // 1.471
|
||||
bands_(0),
|
||||
scope_(kMinBandCount),
|
||||
fg_(palette().color(QPalette::Highlight)),
|
||||
K_barHeight_(1.271) // 1.471
|
||||
,
|
||||
F_peakSpeed(1.103) // 1.122
|
||||
F_peakSpeed_(1.103) // 1.122
|
||||
,
|
||||
F(1.0),
|
||||
bar_height(BAND_COUNT, 0),
|
||||
peak_height(BAND_COUNT, 0),
|
||||
peak_speed(BAND_COUNT, 0.01),
|
||||
barPixmap(COLUMN_WIDTH, 50) {}
|
||||
F_(1.0),
|
||||
bar_height_(kMaxBandCount, 0),
|
||||
peak_height_(kMaxBandCount, 0),
|
||||
peak_speed_(kMaxBandCount, 0.01),
|
||||
barPixmap_(kColumnWidth, 50) {
|
||||
setMinimumWidth(kMinBandCount * (kColumnWidth + 1) - 1);
|
||||
setMaximumWidth(kMaxBandCount * (kColumnWidth + 1) - 1);
|
||||
}
|
||||
|
||||
void BoomAnalyzer::changeK_barHeight(int newValue) {
|
||||
K_barHeight = static_cast<double>(newValue) / 1000;
|
||||
K_barHeight_ = static_cast<double>(newValue) / 1000;
|
||||
}
|
||||
|
||||
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
|
||||
F_peakSpeed = static_cast<double>(newValue) / 1000;
|
||||
F_peakSpeed_ = static_cast<double>(newValue) / 1000;
|
||||
}
|
||||
|
||||
void BoomAnalyzer::resizeEvent(QResizeEvent*) { init(); }
|
||||
void BoomAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
QWidget::resizeEvent(e);
|
||||
|
||||
void BoomAnalyzer::init() {
|
||||
const uint HEIGHT = height() - 2;
|
||||
const double h = 1.2 / HEIGHT;
|
||||
|
||||
F = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
|
||||
bands_ = qMin(
|
||||
static_cast<uint>(static_cast<double>(width() + 1) / (kColumnWidth + 1)) +
|
||||
1,
|
||||
kMaxBandCount);
|
||||
scope_.resize(bands_);
|
||||
|
||||
barPixmap = QPixmap(COLUMN_WIDTH - 2, HEIGHT);
|
||||
F_ = static_cast<double>(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
|
||||
|
||||
barPixmap_ = QPixmap(kColumnWidth - 2, HEIGHT);
|
||||
canvas_ = QPixmap(size());
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
QPainter p(&barPixmap);
|
||||
QPainter p(&barPixmap_);
|
||||
for (uint y = 0; y < HEIGHT; ++y) {
|
||||
const double F = static_cast<double>(y) * h;
|
||||
|
||||
p.setPen(QColor(qMax(0, 255 - static_cast<int>(229.0 * F)),
|
||||
qMax(0, 255 - static_cast<int>(229.0 * F)),
|
||||
qMax(0, 255 - static_cast<int>(191.0 * F))));
|
||||
p.drawLine(0, y, COLUMN_WIDTH - 2, y);
|
||||
p.drawLine(0, y, kColumnWidth - 2, y);
|
||||
}
|
||||
|
||||
updateBandSize(bands_);
|
||||
}
|
||||
|
||||
void BoomAnalyzer::transform(Scope& s) {
|
||||
float* front = static_cast<float*>(&s.front());
|
||||
|
||||
m_fht->spectrum(front);
|
||||
m_fht->scale(front, 1.0 / 60);
|
||||
fht_->spectrum(front);
|
||||
fht_->scale(front, 1.0 / 50);
|
||||
|
||||
Scope scope(32, 0);
|
||||
|
||||
const uint xscale[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 19, 24, 29, 36,
|
||||
43, 52, 63, 76, 91, 108, 129, 153, 182, 216, 255};
|
||||
|
||||
for (uint j, i = 0; i < 32; i++)
|
||||
for (j = xscale[i]; j < xscale[i + 1]; j++)
|
||||
if (s[j] > scope[i]) scope[i] = s[j];
|
||||
|
||||
s = scope;
|
||||
s.resize(scope_.size() <= kMaxBandCount / 2 ? kMaxBandCount / 2
|
||||
: scope_.size());
|
||||
}
|
||||
|
||||
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
@ -104,47 +113,70 @@ void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
QPainter canvas_painter(&canvas_);
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
|
||||
h = log10(scope[i] * 256.0) * F;
|
||||
Analyzer::interpolate(scope, scope_);
|
||||
|
||||
// update the graphics with the new colour
|
||||
if (psychedelic_enabled_) {
|
||||
paletteChange(QPalette());
|
||||
}
|
||||
|
||||
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
|
||||
h = log10(scope_[i] * 256.0) * F_;
|
||||
|
||||
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
||||
|
||||
if (h > bar_height[i]) {
|
||||
bar_height[i] = h;
|
||||
if (h > bar_height_[i]) {
|
||||
bar_height_[i] = h;
|
||||
|
||||
if (h > peak_height[i]) {
|
||||
peak_height[i] = h;
|
||||
peak_speed[i] = 0.01;
|
||||
if (h > peak_height_[i]) {
|
||||
peak_height_[i] = h;
|
||||
peak_speed_[i] = 0.01;
|
||||
} else {
|
||||
goto peak_handling;
|
||||
}
|
||||
} else {
|
||||
if (bar_height[i] > 0.0) {
|
||||
bar_height[i] -= K_barHeight; // 1.4
|
||||
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
|
||||
if (bar_height_[i] > 0.0) {
|
||||
bar_height_[i] -= K_barHeight_; // 1.4
|
||||
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
|
||||
}
|
||||
|
||||
peak_handling:
|
||||
|
||||
if (peak_height[i] > 0.0) {
|
||||
peak_height[i] -= peak_speed[i];
|
||||
peak_speed[i] *= F_peakSpeed; // 1.12
|
||||
if (peak_height_[i] > 0.0) {
|
||||
peak_height_[i] -= peak_speed_[i];
|
||||
peak_speed_[i] *= F_peakSpeed_; // 1.12
|
||||
|
||||
if (peak_height[i] < bar_height[i]) peak_height[i] = bar_height[i];
|
||||
if (peak_height[i] < 0.0) peak_height[i] = 0.0;
|
||||
if (peak_height_[i] < bar_height_[i]) peak_height_[i] = bar_height_[i];
|
||||
if (peak_height_[i] < 0.0) peak_height_[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
y = height() - uint(bar_height[i]);
|
||||
canvas_painter.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
|
||||
canvas_painter.setPen(palette().color(QPalette::Highlight));
|
||||
if (bar_height[i] > 0)
|
||||
canvas_painter.drawRect(x, y, COLUMN_WIDTH - 1, height() - y - 1);
|
||||
y = height() - uint(bar_height_[i]);
|
||||
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
|
||||
canvas_painter.setPen(fg_);
|
||||
if (bar_height_[i] > 0)
|
||||
canvas_painter.drawRect(x, y, kColumnWidth - 1, height() - y - 1);
|
||||
|
||||
y = height() - uint(peak_height[i]);
|
||||
canvas_painter.setPen(palette().color(QPalette::Base));
|
||||
canvas_painter.drawLine(x, y, x + COLUMN_WIDTH - 1, y);
|
||||
y = height() - uint(peak_height_[i]);
|
||||
canvas_painter.setPen(palette().color(QPalette::Midlight));
|
||||
canvas_painter.drawLine(x, y, x + kColumnWidth - 1, y);
|
||||
}
|
||||
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
}
|
||||
|
||||
void BoomAnalyzer::psychedelicModeChanged(bool enabled) {
|
||||
psychedelic_enabled_ = enabled;
|
||||
// reset colours back to normal
|
||||
paletteChange(QPalette());
|
||||
}
|
||||
|
||||
void BoomAnalyzer::paletteChange(const QPalette&) {
|
||||
if (psychedelic_enabled_) {
|
||||
fg_ = getPsychedelicColor(scope_, 50, 100);
|
||||
} else {
|
||||
// the highlight colour changes when the main window loses focus,
|
||||
// so we use save and use the focused colour
|
||||
fg_ = palette().color(QPalette::Highlight);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2004, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
@ -27,10 +27,6 @@
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
/**
|
||||
@author Max Howell
|
||||
*/
|
||||
|
||||
class BoomAnalyzer : public Analyzer::Base {
|
||||
Q_OBJECT
|
||||
|
||||
@ -39,9 +35,9 @@ class BoomAnalyzer : public Analyzer::Base {
|
||||
|
||||
static const char* kName;
|
||||
|
||||
virtual void init();
|
||||
virtual void transform(Analyzer::Scope& s);
|
||||
virtual void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
|
||||
virtual void psychedelicModeChanged(bool);
|
||||
|
||||
public slots:
|
||||
void changeK_barHeight(int);
|
||||
@ -49,17 +45,23 @@ class BoomAnalyzer : public Analyzer::Base {
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* e);
|
||||
void paletteChange(const QPalette&);
|
||||
|
||||
static const uint COLUMN_WIDTH = 4;
|
||||
static const uint BAND_COUNT = 32;
|
||||
static const uint kColumnWidth;
|
||||
static const uint kMaxBandCount;
|
||||
static const uint kMinBandCount;
|
||||
|
||||
double K_barHeight, F_peakSpeed, F;
|
||||
uint bands_;
|
||||
Analyzer::Scope scope_;
|
||||
QColor fg_;
|
||||
|
||||
std::vector<float> bar_height;
|
||||
std::vector<float> peak_height;
|
||||
std::vector<float> peak_speed;
|
||||
double K_barHeight_, F_peakSpeed_, F_;
|
||||
|
||||
QPixmap barPixmap;
|
||||
std::vector<float> bar_height_;
|
||||
std::vector<float> peak_height_;
|
||||
std::vector<float> peak_speed_;
|
||||
|
||||
QPixmap barPixmap_;
|
||||
QPixmap canvas_;
|
||||
};
|
||||
|
||||
|
@ -24,70 +24,69 @@
|
||||
#include <string.h>
|
||||
#include "fht.h"
|
||||
|
||||
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
|
||||
FHT::FHT(int n) : buf_(0), tab_(0), log_(0) {
|
||||
if (n < 3) {
|
||||
m_num = 0;
|
||||
m_exp2 = -1;
|
||||
num_ = 0;
|
||||
exp2_ = -1;
|
||||
return;
|
||||
}
|
||||
m_exp2 = n;
|
||||
m_num = 1 << n;
|
||||
exp2_ = n;
|
||||
num_ = 1 << n;
|
||||
if (n > 3) {
|
||||
m_buf = new float[m_num];
|
||||
m_tab = new float[m_num * 2];
|
||||
buf_ = new float[num_];
|
||||
tab_ = new float[num_ * 2];
|
||||
makeCasTable();
|
||||
}
|
||||
}
|
||||
|
||||
FHT::~FHT() {
|
||||
delete[] m_buf;
|
||||
delete[] m_tab;
|
||||
delete[] m_log;
|
||||
delete[] buf_;
|
||||
delete[] tab_;
|
||||
delete[] log_;
|
||||
}
|
||||
|
||||
void FHT::makeCasTable(void) {
|
||||
float d, *costab, *sintab;
|
||||
int ul, ndiv2 = m_num / 2;
|
||||
int ul, ndiv2 = num_ / 2;
|
||||
|
||||
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num;
|
||||
ul++) {
|
||||
for (costab = tab_, sintab = tab_ + num_ / 2 + 1, ul = 0; ul < num_; ul++) {
|
||||
d = M_PI * ul / ndiv2;
|
||||
*costab = *sintab = cos(d);
|
||||
|
||||
costab += 2, sintab += 2;
|
||||
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
|
||||
if (sintab > tab_ + num_ * 2) sintab = tab_ + 1;
|
||||
}
|
||||
}
|
||||
|
||||
float* FHT::copy(float* d, float* s) {
|
||||
return static_cast<float*>(memcpy(d, s, m_num * sizeof(float)));
|
||||
return static_cast<float*>(memcpy(d, s, num_ * sizeof(float)));
|
||||
}
|
||||
|
||||
float* FHT::clear(float* d) {
|
||||
return static_cast<float*>(memset(d, 0, m_num * sizeof(float)));
|
||||
return static_cast<float*>(memset(d, 0, num_ * sizeof(float)));
|
||||
}
|
||||
|
||||
void FHT::scale(float* p, float d) {
|
||||
for (int i = 0; i < (m_num / 2); i++) *p++ *= d;
|
||||
for (int i = 0; i < (num_ / 2); i++) *p++ *= d;
|
||||
}
|
||||
|
||||
void FHT::ewma(float* d, float* s, float w) {
|
||||
for (int i = 0; i < (m_num / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||
for (int i = 0; i < (num_ / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||
}
|
||||
|
||||
void FHT::logSpectrum(float* out, float* p) {
|
||||
int n = m_num / 2, i, j, k, *r;
|
||||
if (!m_log) {
|
||||
m_log = new int[n];
|
||||
int n = num_ / 2, i, j, k, *r;
|
||||
if (!log_) {
|
||||
log_ = new int[n];
|
||||
float f = n / log10(static_cast<double>(n));
|
||||
for (i = 0, r = m_log; i < n; i++, r++) {
|
||||
for (i = 0, r = log_; i < n; i++, r++) {
|
||||
j = static_cast<int>(rint(log10(i + 1.0) * f));
|
||||
*r = j >= n ? n - 1 : j;
|
||||
}
|
||||
}
|
||||
semiLogSpectrum(p);
|
||||
*out++ = *p = *p / 100;
|
||||
for (k = i = 1, r = m_log; i < n; i++) {
|
||||
for (k = i = 1, r = log_; i < n; i++) {
|
||||
j = *r++;
|
||||
if (i == j) {
|
||||
*out++ = p[i];
|
||||
@ -102,7 +101,7 @@ void FHT::logSpectrum(float* out, float* p) {
|
||||
void FHT::semiLogSpectrum(float* p) {
|
||||
float e;
|
||||
power2(p);
|
||||
for (int i = 0; i < (m_num / 2); i++, p++) {
|
||||
for (int i = 0; i < (num_ / 2); i++, p++) {
|
||||
e = 10.0 * log10(sqrt(*p * .5));
|
||||
*p = e < 0 ? 0 : e;
|
||||
}
|
||||
@ -110,31 +109,31 @@ void FHT::semiLogSpectrum(float* p) {
|
||||
|
||||
void FHT::spectrum(float* p) {
|
||||
power2(p);
|
||||
for (int i = 0; i < (m_num / 2); i++, p++)
|
||||
for (int i = 0; i < (num_ / 2); i++, p++)
|
||||
*p = static_cast<float>(sqrt(*p * .5));
|
||||
}
|
||||
|
||||
void FHT::power(float* p) {
|
||||
power2(p);
|
||||
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5;
|
||||
for (int i = 0; i < (num_ / 2); i++) *p++ *= .5;
|
||||
}
|
||||
|
||||
void FHT::power2(float* p) {
|
||||
int i;
|
||||
float* q;
|
||||
_transform(p, m_num, 0);
|
||||
_transform(p, num_, 0);
|
||||
|
||||
*p = (*p * *p), *p += *p, p++;
|
||||
|
||||
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
|
||||
for (i = 1, q = p + num_ - 2; i < (num_ / 2); i++, --q)
|
||||
*p = (*p * *p) + (*q * *q), p++;
|
||||
}
|
||||
|
||||
void FHT::transform(float* p) {
|
||||
if (m_num == 8)
|
||||
if (num_ == 8)
|
||||
transform8(p);
|
||||
else
|
||||
_transform(p, m_num, 0);
|
||||
_transform(p, num_, 0);
|
||||
}
|
||||
|
||||
void FHT::transform8(float* p) {
|
||||
@ -173,19 +172,19 @@ void FHT::_transform(float* p, int n, int k) {
|
||||
int i, j, ndiv2 = n / 2;
|
||||
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
|
||||
|
||||
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
|
||||
for (i = 0, t1 = buf_, t2 = buf_ + ndiv2, pp = &p[k]; i < ndiv2; i++)
|
||||
*t1++ = *pp++, *t2++ = *pp++;
|
||||
|
||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
||||
memcpy(p + k, buf_, sizeof(float) * n);
|
||||
|
||||
_transform(p, ndiv2, k);
|
||||
_transform(p, ndiv2, k + ndiv2);
|
||||
|
||||
j = m_num / ndiv2 - 1;
|
||||
t1 = m_buf;
|
||||
j = num_ / ndiv2 - 1;
|
||||
t1 = buf_;
|
||||
t2 = t1 + ndiv2;
|
||||
t3 = p + k + ndiv2;
|
||||
ptab = m_tab;
|
||||
ptab = tab_;
|
||||
pp = p + k;
|
||||
|
||||
a = *ptab++ * *t3++;
|
||||
@ -202,5 +201,5 @@ void FHT::_transform(float* p, int n, int k) {
|
||||
*t1++ = *pp + a;
|
||||
*t2++ = *pp++ - a;
|
||||
}
|
||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
||||
memcpy(p + k, buf_, sizeof(float) * n);
|
||||
}
|
||||
|
@ -32,11 +32,11 @@
|
||||
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
|
||||
*/
|
||||
class FHT {
|
||||
int m_exp2;
|
||||
int m_num;
|
||||
float* m_buf;
|
||||
float* m_tab;
|
||||
int* m_log;
|
||||
int exp2_;
|
||||
int num_;
|
||||
float* buf_;
|
||||
float* tab_;
|
||||
int* log_;
|
||||
|
||||
/**
|
||||
* Create a table of "cas" (cosine and sine) values.
|
||||
@ -59,8 +59,8 @@ class FHT {
|
||||
explicit FHT(int);
|
||||
|
||||
~FHT();
|
||||
inline int sizeExp() const { return m_exp2; }
|
||||
inline int size() const { return m_num; }
|
||||
inline int sizeExp() const { return exp2_; }
|
||||
inline int size() const { return num_; }
|
||||
float* copy(float*, float*);
|
||||
float* clear(float*);
|
||||
void scale(float*, float);
|
||||
|
@ -57,7 +57,7 @@ NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
||||
}
|
||||
}
|
||||
|
||||
void NyanCatAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
|
||||
void NyanCatAnalyzer::transform(Scope& s) { fht_->spectrum(&s.front()); }
|
||||
|
||||
void NyanCatAnalyzer::timerEvent(QTimerEvent* e) {
|
||||
if (e->timerId() == timer_id_) {
|
||||
@ -74,7 +74,8 @@ void NyanCatAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
buffer_[1] = QPixmap();
|
||||
|
||||
available_rainbow_width_ = width() - kCatWidth + kRainbowOverlap;
|
||||
px_per_frame_ = static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
|
||||
px_per_frame_ =
|
||||
static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
|
||||
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
|
||||
}
|
||||
|
||||
@ -112,11 +113,13 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||
QPointF* dest = polyline;
|
||||
float* source = history_;
|
||||
|
||||
const float top_of_cat = static_cast<float>(height()) / 2 - static_cast<float>(kCatHeight) / 2;
|
||||
const float top_of_cat =
|
||||
static_cast<float>(height()) / 2 - static_cast<float>(kCatHeight) / 2;
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
// Calculate the Y position of this band.
|
||||
const float y =
|
||||
static_cast<float>(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) + top_of_cat;
|
||||
static_cast<float>(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) +
|
||||
top_of_cat;
|
||||
|
||||
// Add each point in the line.
|
||||
for (int x = 0; x < kHistorySize; ++x) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, David Sansome <me@davidsansome.com>
|
||||
|
||||
@ -57,7 +57,7 @@ RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
|
||||
}
|
||||
}
|
||||
|
||||
void RainbowDashAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
|
||||
void RainbowDashAnalyzer::transform(Scope& s) { fht_->spectrum(&s.front()); }
|
||||
|
||||
void RainbowDashAnalyzer::timerEvent(QTimerEvent* e) {
|
||||
if (e->timerId() == timer_id_) {
|
||||
@ -74,7 +74,8 @@ void RainbowDashAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
buffer_[1] = QPixmap();
|
||||
|
||||
available_rainbow_width_ = width() - kDashWidth + kRainbowOverlap;
|
||||
px_per_frame_ = static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
|
||||
px_per_frame_ =
|
||||
static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
|
||||
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
|
||||
}
|
||||
|
||||
@ -111,12 +112,13 @@ void RainbowDashAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||
QPointF* dest = polyline;
|
||||
float* source = history_;
|
||||
|
||||
const float top_of_Dash = static_cast<float>(height()) / 2 - static_cast<float>(kRainbowHeight) / 2;
|
||||
const float top_of_Dash = static_cast<float>(height()) / 2 -
|
||||
static_cast<float>(kRainbowHeight) / 2;
|
||||
for (int band = 0; band < kRainbowBands; ++band) {
|
||||
// Calculate the Y position of this band.
|
||||
const float y =
|
||||
static_cast<float>(kRainbowHeight) / (kRainbowBands + 1) * (band + 0.5) +
|
||||
top_of_Dash;
|
||||
const float y = static_cast<float>(kRainbowHeight) / (kRainbowBands + 1) *
|
||||
(band + 0.5) +
|
||||
top_of_Dash;
|
||||
|
||||
// Add each point in the line.
|
||||
for (int x = 0; x < kHistorySize; ++x) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2014, Alibek Omarov <a1ba.omarov@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2004, Melchior FRANZ <mfranz@kde.org>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2010, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
@ -31,7 +31,8 @@ using Analyzer::Scope;
|
||||
const char* Sonogram::kName =
|
||||
QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
||||
|
||||
Sonogram::Sonogram(QWidget* parent) : Analyzer::Base(parent, 9) {}
|
||||
Sonogram::Sonogram(QWidget* parent)
|
||||
: Analyzer::Base(parent, 9), scope_size_(128) {}
|
||||
|
||||
Sonogram::~Sonogram() {}
|
||||
|
||||
@ -45,10 +46,16 @@ void Sonogram::resizeEvent(QResizeEvent* e) {
|
||||
|
||||
canvas_ = QPixmap(size());
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
updateBandSize(scope_size_);
|
||||
}
|
||||
|
||||
void Sonogram::psychedelicModeChanged(bool enabled) {
|
||||
psychedelic_enabled_ = enabled;
|
||||
updateBandSize(scope_size_);
|
||||
}
|
||||
|
||||
void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||
if (!new_frame) {
|
||||
if (!new_frame || engine_->state() == Engine::Paused) {
|
||||
p.drawPixmap(0, 0, canvas_);
|
||||
return;
|
||||
}
|
||||
@ -60,20 +67,45 @@ void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||
canvas_painter.drawPixmap(0, 0, canvas_, 1, 0, x, -1);
|
||||
|
||||
Scope::const_iterator it = s.begin(), end = s.end();
|
||||
for (int y = height() - 1; y;) {
|
||||
if (it >= end || *it < .005)
|
||||
c = palette().color(QPalette::Background);
|
||||
else if (*it < .05)
|
||||
c.setHsv(95, 255, 255 - static_cast<int>(*it * 4000.0));
|
||||
else if (*it < 1.0)
|
||||
c.setHsv(95 - static_cast<int>(*it * 90.0), 255, 255);
|
||||
else
|
||||
c = Qt::red;
|
||||
if (scope_size_ != s.size()) {
|
||||
scope_size_ = s.size();
|
||||
updateBandSize(scope_size_);
|
||||
}
|
||||
|
||||
canvas_painter.setPen(c);
|
||||
canvas_painter.drawPoint(x, y--);
|
||||
if (psychedelic_enabled_) {
|
||||
c = getPsychedelicColor(s, 20, 100);
|
||||
for (int y = height() - 1; y;) {
|
||||
if (it >= end || *it < .005) {
|
||||
c = palette().color(QPalette::Background);
|
||||
} else if (*it < .05) {
|
||||
c.setHsv(c.hue(), c.saturation(), 255 - static_cast<int>(*it * 4000.0));
|
||||
} else if (*it < 1.0) {
|
||||
c.setHsv((c.hue() + static_cast<int>(*it * 90.0)) % 255, 255, 255);
|
||||
} else {
|
||||
c = getPsychedelicColor(s, 10, 50);
|
||||
}
|
||||
|
||||
if (it < end) ++it;
|
||||
canvas_painter.setPen(c);
|
||||
canvas_painter.drawPoint(x, y--);
|
||||
|
||||
if (it < end) ++it;
|
||||
}
|
||||
} else {
|
||||
for (int y = height() - 1; y;) {
|
||||
if (it >= end || *it < .005)
|
||||
c = palette().color(QPalette::Background);
|
||||
else if (*it < .05)
|
||||
c.setHsv(95, 255, 255 - static_cast<int>(*it * 4000.0));
|
||||
else if (*it < 1.0)
|
||||
c.setHsv(95 - static_cast<int>(*it * 90.0), 255, 255);
|
||||
else
|
||||
c = Qt::red;
|
||||
|
||||
canvas_painter.setPen(c);
|
||||
canvas_painter.drawPoint(x, y--);
|
||||
|
||||
if (it < end) ++it;
|
||||
}
|
||||
}
|
||||
|
||||
canvas_painter.end();
|
||||
@ -83,11 +115,11 @@ void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||
|
||||
void Sonogram::transform(Scope& scope) {
|
||||
float* front = static_cast<float*>(&scope.front());
|
||||
m_fht->power2(front);
|
||||
m_fht->scale(front, 1.0 / 256);
|
||||
scope.resize(m_fht->size() / 2);
|
||||
fht_->power2(front);
|
||||
fht_->scale(front, 1.0 / 256);
|
||||
scope.resize(fht_->size() / 2);
|
||||
}
|
||||
|
||||
void Sonogram::demo(QPainter& p) {
|
||||
analyze(p, Scope(m_fht->size(), 0), new_frame_);
|
||||
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2015, Mark Furneaux <mark@furneaux.ca>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -25,10 +26,7 @@
|
||||
#define ANALYZERS_SONOGRAM_H_
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
/**
|
||||
@author Melchior FRANZ
|
||||
*/
|
||||
#include "engines/enginebase.h"
|
||||
|
||||
class Sonogram : public Analyzer::Base {
|
||||
Q_OBJECT
|
||||
@ -43,8 +41,10 @@ class Sonogram : public Analyzer::Base {
|
||||
void transform(Analyzer::Scope&);
|
||||
void demo(QPainter& p);
|
||||
void resizeEvent(QResizeEvent*);
|
||||
void psychedelicModeChanged(bool);
|
||||
|
||||
QPixmap canvas_;
|
||||
int scope_size_;
|
||||
};
|
||||
|
||||
#endif // ANALYZERS_SONOGRAM_H_
|
||||
|
@ -2,7 +2,7 @@
|
||||
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
|
||||
Copyright 2003, Max Howell <max.howell@methylblue.com>
|
||||
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||
Copyright 2014, Mark Furneaux <mark@romaco.ca>
|
||||
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
/* Original Author: Stanislav Karchebny <berkus@users.sf.net> 2003
|
||||
* Original Author: Max Howell <max.howell@methylblue.com> 2003
|
||||
* Original Author: Max Howell <max.howell@methylblue.com> 2003
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
@ -42,58 +42,64 @@ void TurbineAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||
|
||||
float h;
|
||||
const uint hd2 = height() / 2;
|
||||
const uint MAX_HEIGHT = hd2 - 1;
|
||||
const uint kMaxHeight = hd2 - 1;
|
||||
|
||||
QPainter canvas_painter(&canvas_);
|
||||
canvas_.fill(palette().color(QPalette::Background));
|
||||
|
||||
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
|
||||
h = log10(scope[i] * 256.0) * F * 0.5;
|
||||
Analyzer::interpolate(scope, scope_);
|
||||
|
||||
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
||||
// update the graphics with the new colour
|
||||
if (psychedelic_enabled_) {
|
||||
paletteChange(QPalette());
|
||||
}
|
||||
|
||||
if (h > bar_height[i]) {
|
||||
bar_height[i] = h;
|
||||
for (uint i = 0, x = 0, y; i < bands_; ++i, x += kColumnWidth + 1) {
|
||||
h = log10(scope_[i] * 256.0) * F_ * 0.5;
|
||||
|
||||
if (h > peak_height[i]) {
|
||||
peak_height[i] = h;
|
||||
peak_speed[i] = 0.01;
|
||||
if (h > kMaxHeight) h = kMaxHeight;
|
||||
|
||||
if (h > bar_height_[i]) {
|
||||
bar_height_[i] = h;
|
||||
|
||||
if (h > peak_height_[i]) {
|
||||
peak_height_[i] = h;
|
||||
peak_speed_[i] = 0.01;
|
||||
} else {
|
||||
goto peak_handling;
|
||||
}
|
||||
} else {
|
||||
if (bar_height[i] > 0.0) {
|
||||
bar_height[i] -= K_barHeight; // 1.4
|
||||
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
|
||||
if (bar_height_[i] > 0.0) {
|
||||
bar_height_[i] -= K_barHeight_; // 1.4
|
||||
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
|
||||
}
|
||||
|
||||
peak_handling:
|
||||
|
||||
if (peak_height[i] > 0.0) {
|
||||
peak_height[i] -= peak_speed[i];
|
||||
peak_speed[i] *= F_peakSpeed; // 1.12
|
||||
if (peak_height_[i] > 0.0) {
|
||||
peak_height_[i] -= peak_speed_[i];
|
||||
peak_speed_[i] *= F_peakSpeed_; // 1.12
|
||||
|
||||
if (peak_height[i] < bar_height[i]) peak_height[i] = bar_height[i];
|
||||
if (peak_height[i] < 0.0) peak_height[i] = 0.0;
|
||||
if (peak_height_[i] < bar_height_[i]) peak_height_[i] = bar_height_[i];
|
||||
if (peak_height_[i] < 0.0) peak_height_[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
y = hd2 - static_cast<uint>(bar_height[i]);
|
||||
canvas_painter.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
|
||||
canvas_painter.drawPixmap(x + 1, hd2, barPixmap, 0,
|
||||
static_cast<int>(bar_height[i]),
|
||||
-1, -1);
|
||||
y = hd2 - static_cast<uint>(bar_height_[i]);
|
||||
canvas_painter.drawPixmap(x + 1, y, barPixmap_, 0, y, -1, -1);
|
||||
canvas_painter.drawPixmap(x + 1, hd2, barPixmap_, 0,
|
||||
static_cast<int>(bar_height_[i]), -1, -1);
|
||||
|
||||
canvas_painter.setPen(palette().color(QPalette::Highlight));
|
||||
if (bar_height[i] > 0)
|
||||
canvas_painter.drawRect(x, y, COLUMN_WIDTH - 1,
|
||||
static_cast<int>(bar_height[i]) * 2 - 1);
|
||||
canvas_painter.setPen(fg_);
|
||||
if (bar_height_[i] > 0)
|
||||
canvas_painter.drawRect(x, y, kColumnWidth - 1,
|
||||
static_cast<int>(bar_height_[i]) * 2 - 1);
|
||||
|
||||
const uint x2 = x + COLUMN_WIDTH - 1;
|
||||
canvas_painter.setPen(palette().color(QPalette::Base));
|
||||
y = hd2 - uint(peak_height[i]);
|
||||
const uint x2 = x + kColumnWidth - 1;
|
||||
canvas_painter.setPen(palette().color(QPalette::Midlight));
|
||||
y = hd2 - uint(peak_height_[i]);
|
||||
canvas_painter.drawLine(x, y, x2, y);
|
||||
y = hd2 + uint(peak_height[i]);
|
||||
y = hd2 + uint(peak_height_[i]);
|
||||
canvas_painter.drawLine(x, y, x2, y);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#cmakedefine HAVE_AUDIOCD
|
||||
#cmakedefine HAVE_BOX
|
||||
#cmakedefine HAVE_BREAKPAD
|
||||
#cmakedefine HAVE_CRYPTOPP
|
||||
#cmakedefine HAVE_DBUS
|
||||
#cmakedefine HAVE_DEVICEKIT
|
||||
#cmakedefine HAVE_DROPBOX
|
||||
@ -36,7 +37,6 @@
|
||||
#cmakedefine HAVE_LIBMTP
|
||||
#cmakedefine HAVE_LIBPULSE
|
||||
#cmakedefine HAVE_MOODBAR
|
||||
#cmakedefine HAVE_QCA
|
||||
#cmakedefine HAVE_SEAFILE
|
||||
#cmakedefine HAVE_SKYDRIVE
|
||||
#cmakedefine HAVE_SPARKLE
|
||||
|
@ -47,7 +47,7 @@
|
||||
#include <QVariant>
|
||||
|
||||
const char* Database::kDatabaseFilename = "clementine.db";
|
||||
const int Database::kSchemaVersion = 49;
|
||||
const int Database::kSchemaVersion = 50;
|
||||
const char* Database::kMagicAllSongsTables = "%allsongstables";
|
||||
|
||||
int Database::sNextConnectionId = 1;
|
||||
|
@ -75,6 +75,10 @@ GlobalShortcuts::GlobalShortcuts(QWidget* parent)
|
||||
AddShortcut("toggle_last_fm_scrobbling",
|
||||
tr("Enable/disable Last.fm scrobbling"),
|
||||
SIGNAL(ToggleScrobbling()));
|
||||
AddShortcut("love_last_fm_scrobbling", tr("Love (Last.fm scrobbling)"),
|
||||
SIGNAL(Love()));
|
||||
AddShortcut("ban_last_fm_scrobbling", tr("Ban (Last.fm scrobbling)"),
|
||||
SIGNAL(Ban()));
|
||||
|
||||
AddRatingShortcut("rate_zero_star", tr("Rate the current song 0 stars"),
|
||||
rating_signals_mapper_, 0);
|
||||
|
@ -60,7 +60,7 @@ class GlobalShortcuts : public QWidget {
|
||||
void Unregister();
|
||||
void Register();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void Play();
|
||||
void Pause();
|
||||
void PlayPause();
|
||||
@ -80,6 +80,8 @@ class GlobalShortcuts : public QWidget {
|
||||
void CycleShuffleMode();
|
||||
void CycleRepeatMode();
|
||||
void ToggleScrobbling();
|
||||
void Love();
|
||||
void Ban();
|
||||
|
||||
private:
|
||||
void AddShortcut(const QString& id, const QString& name, const char* signal,
|
||||
|
@ -77,6 +77,7 @@ void RegisterMetaTypes() {
|
||||
qRegisterMetaType<PodcastEpisodeList>("PodcastEpisodeList");
|
||||
qRegisterMetaType<PodcastList>("PodcastList");
|
||||
qRegisterMetaType<QList<CoverSearchResult>>("QList<CoverSearchResult>");
|
||||
qRegisterMetaType<QList<int>>("QList<int>");
|
||||
qRegisterMetaType<QList<PlaylistItemPtr>>("QList<PlaylistItemPtr>");
|
||||
qRegisterMetaType<PlaylistSequence::RepeatMode>(
|
||||
"PlaylistSequence::RepeatMode");
|
||||
|
@ -244,7 +244,8 @@ int Mpris1Player::GetCaps(Engine::State state) const {
|
||||
if (playlists->active()->next_row() != -1) {
|
||||
caps |= CAN_GO_NEXT;
|
||||
}
|
||||
if (playlists->active()->previous_row() != -1) {
|
||||
if (playlists->active()->previous_row() != -1 ||
|
||||
app_->player()->PreviousWouldRestartTrack()) {
|
||||
caps |= CAN_GO_PREV;
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ const QStringList OrganiseFormat::kKnownTags = QStringList() << "title"
|
||||
<< "extension"
|
||||
<< "performer"
|
||||
<< "grouping"
|
||||
<< "lyrics";
|
||||
<< "lyrics"
|
||||
<< "originalyear";
|
||||
|
||||
// From http://en.wikipedia.org/wiki/8.3_filename#Directory_table
|
||||
const char OrganiseFormat::kInvalidFatCharacters[] = "\"*/\\:<>?|";
|
||||
@ -200,6 +201,8 @@ QString OrganiseFormat::TagValue(const QString& tag, const Song& song) const {
|
||||
value = song.comment();
|
||||
else if (tag == "year")
|
||||
value = QString::number(song.year());
|
||||
else if (tag == "originalyear")
|
||||
value = QString::number(song.effective_originalyear());
|
||||
else if (tag == "track")
|
||||
value = QString::number(song.track());
|
||||
else if (tag == "disc")
|
||||
|
@ -63,7 +63,8 @@ Player::Player(Application* app, QObject* parent)
|
||||
nb_errors_received_(0),
|
||||
volume_before_mute_(50),
|
||||
last_pressed_previous_(QDateTime::currentDateTime()),
|
||||
menu_previousmode_(PreviousBehaviour_DontRestart) {
|
||||
menu_previousmode_(PreviousBehaviour_DontRestart),
|
||||
seek_step_sec_(10) {
|
||||
settings_.beginGroup("Player");
|
||||
|
||||
SetVolume(settings_.value("volume", 50).toInt());
|
||||
@ -103,12 +104,24 @@ void Player::ReloadSettings() {
|
||||
|
||||
menu_previousmode_ = PreviousBehaviour(
|
||||
s.value("menu_previousmode", PreviousBehaviour_DontRestart).toInt());
|
||||
|
||||
seek_step_sec_ = s.value("seek_step_sec", 10).toInt();
|
||||
|
||||
s.endGroup();
|
||||
|
||||
engine_->ReloadSettings();
|
||||
}
|
||||
|
||||
void Player::HandleLoadResult(const UrlHandler::LoadResult& result) {
|
||||
// Might've been an async load, so check we're still on the same item
|
||||
shared_ptr<PlaylistItem> item = app_->playlist_manager()->active()->current_item();
|
||||
if (!item) {
|
||||
loading_async_ = QUrl();
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->Url() != result.original_url_) return;
|
||||
|
||||
switch (result.type_) {
|
||||
case UrlHandler::LoadResult::NoMoreTracks:
|
||||
qLog(Debug) << "URL handler for" << result.original_url_
|
||||
@ -119,14 +132,6 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult& result) {
|
||||
break;
|
||||
|
||||
case UrlHandler::LoadResult::TrackAvailable: {
|
||||
// Might've been an async load, so check we're still on the same item
|
||||
int current_index = app_->playlist_manager()->active()->current_row();
|
||||
if (current_index == -1) return;
|
||||
|
||||
shared_ptr<PlaylistItem> item =
|
||||
app_->playlist_manager()->active()->item_at(current_index);
|
||||
if (!item || item->Url() != result.original_url_) return;
|
||||
|
||||
qLog(Debug) << "URL handler for" << result.original_url_ << "returned"
|
||||
<< result.media_url_;
|
||||
|
||||
@ -301,6 +306,13 @@ void Player::StopAfterCurrent() {
|
||||
app_->playlist_manager()->active()->current_row());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void Player::Previous() { PreviousItem(Engine::Manual); }
|
||||
|
||||
void Player::PreviousItem(Engine::TrackChangeFlags change) {
|
||||
@ -436,23 +448,19 @@ void Player::SeekTo(int seconds) {
|
||||
qBound(0ll, qint64(seconds) * kNsecPerSec, length_nanosec);
|
||||
engine_->Seek(nanosec);
|
||||
|
||||
// If we seek the track we don't want to submit it to last.fm
|
||||
qLog(Info) << "Track seeked to" << nanosec << "ns - not scrobbling";
|
||||
if (app_->playlist_manager()->active()->get_lastfm_status() ==
|
||||
Playlist::LastFM_New) {
|
||||
app_->playlist_manager()->active()->set_lastfm_status(
|
||||
Playlist::LastFM_Seeked);
|
||||
}
|
||||
// If we seek the track we need to move the scrobble point
|
||||
qLog(Info) << "Track seeked to" << nanosec << "ns - updating scrobble point";
|
||||
app_->playlist_manager()->active()->UpdateScrobblePoint(nanosec);
|
||||
|
||||
emit Seeked(nanosec / 1000);
|
||||
}
|
||||
|
||||
void Player::SeekForward() {
|
||||
SeekTo(engine()->position_nanosec() / kNsecPerSec + 10);
|
||||
SeekTo(engine()->position_nanosec() / kNsecPerSec + seek_step_sec_);
|
||||
}
|
||||
|
||||
void Player::SeekBackward() {
|
||||
SeekTo(engine()->position_nanosec() / kNsecPerSec - 10);
|
||||
SeekTo(engine()->position_nanosec() / kNsecPerSec - seek_step_sec_);
|
||||
}
|
||||
|
||||
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
||||
@ -593,6 +601,8 @@ void Player::TrackAboutToEnd() {
|
||||
next_item->Metadata().end_nanosec());
|
||||
}
|
||||
|
||||
void Player::IntroPointReached() { NextInternal(Engine::Intro); }
|
||||
|
||||
void Player::ValidSongRequested(const QUrl& url) {
|
||||
emit SongChangeRequestProcessed(url, true);
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ class PlayerInterface : public QObject {
|
||||
virtual void Play() = 0;
|
||||
virtual void ShowOSD() = 0;
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void Playing();
|
||||
void Paused();
|
||||
void Stopped();
|
||||
@ -142,6 +142,8 @@ class Player : public PlayerInterface {
|
||||
|
||||
const UrlHandler* HandlerForUrl(const QUrl& url) const;
|
||||
|
||||
bool PreviousWouldRestartTrack() const;
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
|
||||
@ -163,6 +165,7 @@ class Player : public PlayerInterface {
|
||||
void Pause();
|
||||
void Stop(bool stop_after = false);
|
||||
void StopAfterCurrent();
|
||||
void IntroPointReached();
|
||||
void Play();
|
||||
void ShowOSD();
|
||||
void TogglePrettyOSD();
|
||||
@ -209,6 +212,7 @@ class Player : public PlayerInterface {
|
||||
|
||||
QDateTime last_pressed_previous_;
|
||||
PreviousBehaviour menu_previousmode_;
|
||||
int seek_step_sec_;
|
||||
};
|
||||
|
||||
#endif // CORE_PLAYER_H_
|
||||
|
@ -112,7 +112,9 @@ const QStringList Song::kColumns = QStringList() << "title"
|
||||
<< "etag"
|
||||
<< "performer"
|
||||
<< "grouping"
|
||||
<< "lyrics";
|
||||
<< "lyrics"
|
||||
<< "originalyear"
|
||||
<< "effective_originalyear";
|
||||
|
||||
const QString Song::kColumnSpec = Song::kColumns.join(", ");
|
||||
const QString Song::kBindSpec =
|
||||
@ -157,6 +159,7 @@ struct Song::Private : public QSharedData {
|
||||
int disc_;
|
||||
float bpm_;
|
||||
int year_;
|
||||
int originalyear_;
|
||||
QString genre_;
|
||||
QString comment_;
|
||||
bool compilation_; // From the file tag
|
||||
@ -230,6 +233,7 @@ Song::Private::Private()
|
||||
disc_(-1),
|
||||
bpm_(-1),
|
||||
year_(-1),
|
||||
originalyear_(-1),
|
||||
compilation_(false),
|
||||
sampler_(false),
|
||||
forced_compilation_on_(false),
|
||||
@ -285,6 +289,10 @@ int Song::track() const { return d->track_; }
|
||||
int Song::disc() const { return d->disc_; }
|
||||
float Song::bpm() const { return d->bpm_; }
|
||||
int Song::year() const { return d->year_; }
|
||||
int Song::originalyear() const { return d->originalyear_; }
|
||||
int Song::effective_originalyear() const {
|
||||
return d->originalyear_ < 0 ? d->year_ : d->originalyear_;
|
||||
}
|
||||
const QString& Song::genre() const { return d->genre_; }
|
||||
const QString& Song::comment() const { return d->comment_; }
|
||||
bool Song::is_compilation() const {
|
||||
@ -342,6 +350,7 @@ void Song::set_track(int v) { d->track_ = v; }
|
||||
void Song::set_disc(int v) { d->disc_ = v; }
|
||||
void Song::set_bpm(float v) { d->bpm_ = v; }
|
||||
void Song::set_year(int v) { d->year_ = v; }
|
||||
void Song::set_originalyear(int v) { d->originalyear_ = v; }
|
||||
void Song::set_genre(const QString& v) { d->genre_ = v; }
|
||||
void Song::set_comment(const QString& v) { d->comment_ = v; }
|
||||
void Song::set_compilation(bool v) { d->compilation_ = v; }
|
||||
@ -499,6 +508,7 @@ void Song::InitFromProtobuf(const pb::tagreader::SongMetadata& pb) {
|
||||
d->disc_ = pb.disc();
|
||||
d->bpm_ = pb.bpm();
|
||||
d->year_ = pb.year();
|
||||
d->originalyear_ = pb.originalyear();
|
||||
d->genre_ = QStringFromStdString(pb.genre());
|
||||
d->comment_ = QStringFromStdString(pb.comment());
|
||||
d->compilation_ = pb.compilation();
|
||||
@ -545,6 +555,7 @@ void Song::ToProtobuf(pb::tagreader::SongMetadata* pb) const {
|
||||
pb->set_disc(d->disc_);
|
||||
pb->set_bpm(d->bpm_);
|
||||
pb->set_year(d->year_);
|
||||
pb->set_originalyear(d->originalyear_);
|
||||
pb->set_genre(DataCommaSizeFromQString(d->genre_));
|
||||
pb->set_comment(DataCommaSizeFromQString(d->comment_));
|
||||
pb->set_compilation(d->compilation_);
|
||||
@ -585,6 +596,7 @@ void Song::InitFromQuery(const SqlRow& q, bool reliable_metadata, int col) {
|
||||
d->disc_ = toint(col + 7);
|
||||
d->bpm_ = tofloat(col + 8);
|
||||
d->year_ = toint(col + 9);
|
||||
d->originalyear_ = toint(col + 41);
|
||||
d->genre_ = tostr(col + 10);
|
||||
d->comment_ = tostr(col + 11);
|
||||
d->compilation_ = q.value(col + 12).toBool();
|
||||
@ -957,6 +969,8 @@ void Song::BindToQuery(QSqlQuery* query) const {
|
||||
query->bindValue(":performer", strval(d->performer_));
|
||||
query->bindValue(":grouping", strval(d->grouping_));
|
||||
query->bindValue(":lyrics", strval(d->lyrics_));
|
||||
query->bindValue(":originalyear", intval(d->originalyear_));
|
||||
query->bindValue(":effective_originalyear", intval(this->effective_originalyear()));
|
||||
|
||||
#undef intval
|
||||
#undef notnullintval
|
||||
@ -984,6 +998,9 @@ void Song::ToLastFM(lastfm::Track* track, bool prefer_album_artist) const {
|
||||
} else {
|
||||
mtrack.setArtist(d->artist_);
|
||||
}
|
||||
#if LASTFM_MAJOR_VERSION >= 1
|
||||
mtrack.setAlbumArtist(d->albumartist_);
|
||||
#endif
|
||||
mtrack.setAlbum(d->album_);
|
||||
mtrack.setTitle(d->title_);
|
||||
mtrack.setDuration(length_nanosec() / kNsecPerSec);
|
||||
@ -1056,7 +1073,8 @@ bool Song::IsMetadataEqual(const Song& other) const {
|
||||
d->performer_ == other.d->performer_ &&
|
||||
d->grouping_ == other.d->grouping_ && d->track_ == other.d->track_ &&
|
||||
d->disc_ == other.d->disc_ && qFuzzyCompare(d->bpm_, other.d->bpm_) &&
|
||||
d->year_ == other.d->year_ && d->genre_ == other.d->genre_ &&
|
||||
d->year_ == other.d->year_ && d->originalyear_ == other.d->originalyear_ &&
|
||||
d->genre_ == other.d->genre_ &&
|
||||
d->comment_ == other.d->comment_ &&
|
||||
d->compilation_ == other.d->compilation_ &&
|
||||
d->beginning_ == other.d->beginning_ &&
|
||||
|
@ -176,6 +176,8 @@ class Song {
|
||||
int disc() const;
|
||||
float bpm() const;
|
||||
int year() const;
|
||||
int originalyear() const;
|
||||
int effective_originalyear() const;
|
||||
const QString& genre() const;
|
||||
const QString& comment() const;
|
||||
bool is_compilation() const;
|
||||
@ -255,6 +257,7 @@ class Song {
|
||||
void set_disc(int v);
|
||||
void set_bpm(float v);
|
||||
void set_year(int v);
|
||||
void set_originalyear(int v);
|
||||
void set_genre(const QString& v);
|
||||
void set_genre_id3(int id);
|
||||
void set_comment(const QString& v);
|
||||
|
@ -52,7 +52,7 @@ class UrlHandler : public QObject {
|
||||
TrackAvailable,
|
||||
};
|
||||
|
||||
LoadResult(const QUrl& original_url = QUrl(), Type type = NoMoreTracks,
|
||||
LoadResult(const QUrl& original_url, Type type = NoMoreTracks,
|
||||
const QUrl& media_url = QUrl(), qint64 length_nanosec_ = -1);
|
||||
|
||||
// The url that the playlist item has in Url().
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <QIODevice>
|
||||
#include <QMetaEnum>
|
||||
#include <QMouseEvent>
|
||||
#include <QProcess>
|
||||
#include <QStringList>
|
||||
#include <QTcpServer>
|
||||
#include <QtDebug>
|
||||
@ -58,7 +59,6 @@
|
||||
#include <sys/statvfs.h>
|
||||
#elif defined(Q_OS_WIN32)
|
||||
#include <windows.h>
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -76,7 +76,6 @@
|
||||
#include "CoreServices/CoreServices.h"
|
||||
#include "IOKit/ps/IOPowerSources.h"
|
||||
#include "IOKit/ps/IOPSKeys.h"
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
namespace Utilities {
|
||||
@ -411,7 +410,9 @@ void OpenInFileBrowser(const QList<QUrl>& urls) {
|
||||
#elif defined(Q_OS_WIN32)
|
||||
ShowFileInExplorer(path);
|
||||
#else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(directory));
|
||||
QStringList args;
|
||||
args << directory;
|
||||
QProcess::startDetached("xdg-open", args);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ enum TrackChangeType {
|
||||
First = 0x01,
|
||||
Manual = 0x02,
|
||||
Auto = 0x04,
|
||||
Intro = 0x08,
|
||||
|
||||
// Any of:
|
||||
SameAlbum = 0x10,
|
||||
|
0
src/engines/enginebase.h
Executable file → Normal file
0
src/engines/enginebase.h
Executable file → Normal file
43
src/engines/gstengine.cpp
Executable file → Normal file
43
src/engines/gstengine.cpp
Executable file → Normal file
@ -64,6 +64,15 @@
|
||||
#include "engines/directsounddevicefinder.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
#include "core/mac_startup.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
#undef signals
|
||||
#include <gio/gio.h>
|
||||
#endif
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::vector;
|
||||
|
||||
@ -94,6 +103,7 @@ GstEngine::GstEngine(TaskManager* task_manager)
|
||||
buffer_duration_nanosec_(1 * kNsecPerSec), // 1s
|
||||
buffer_min_fill_(33),
|
||||
mono_playback_(false),
|
||||
sample_rate_(kAutoSampleRate),
|
||||
seek_timer_(new QTimer(this)),
|
||||
timer_id_(-1),
|
||||
next_element_id_(0),
|
||||
@ -106,6 +116,13 @@ GstEngine::GstEngine(TaskManager* task_manager)
|
||||
connect(seek_timer_, SIGNAL(timeout()), SLOT(SeekNow()));
|
||||
|
||||
ReloadSettings();
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
QDir resources_dir(mac::GetResourcesPath());
|
||||
QString ca_cert_path = resources_dir.filePath("cacert.pem");
|
||||
GError* error = nullptr;
|
||||
tls_database_ = g_tls_file_database_new(ca_cert_path.toUtf8().data(), &error);
|
||||
#endif
|
||||
}
|
||||
|
||||
GstEngine::~GstEngine() {
|
||||
@ -114,6 +131,10 @@ GstEngine::~GstEngine() {
|
||||
current_pipeline_.reset();
|
||||
|
||||
qDeleteAll(device_finders_);
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
g_object_unref(tls_database_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GstEngine::Init() {
|
||||
@ -184,6 +205,7 @@ void GstEngine::ReloadSettings() {
|
||||
buffer_min_fill_ = s.value("bufferminfill", 33).toInt();
|
||||
|
||||
mono_playback_ = s.value("monoplayback", false).toBool();
|
||||
sample_rate_ = s.value("samplerate", kAutoSampleRate).toInt();
|
||||
}
|
||||
|
||||
qint64 GstEngine::position_nanosec() const {
|
||||
@ -282,8 +304,8 @@ void GstEngine::UpdateScope(int chunk_length) {
|
||||
gst_buffer_map(latest_buffer_, &map, GST_MAP_READ);
|
||||
|
||||
// determine where to split the buffer
|
||||
int chunk_density = (map.size * kNsecPerMsec) /
|
||||
GST_BUFFER_DURATION(latest_buffer_);
|
||||
int chunk_density =
|
||||
(map.size * kNsecPerMsec) / GST_BUFFER_DURATION(latest_buffer_);
|
||||
|
||||
int chunk_size = chunk_length * chunk_density;
|
||||
|
||||
@ -301,10 +323,9 @@ void GstEngine::UpdateScope(int chunk_length) {
|
||||
|
||||
// make sure we don't go beyond the end of the buffer
|
||||
if (scope_chunk_ == scope_chunks_ - 1) {
|
||||
bytes =
|
||||
qMin(static_cast<Engine::Scope::size_type>(
|
||||
map.size - (chunk_size * scope_chunk_)),
|
||||
scope_.size() * sizeof(sample_type));
|
||||
bytes = qMin(static_cast<Engine::Scope::size_type>(
|
||||
map.size - (chunk_size * scope_chunk_)),
|
||||
scope_.size() * sizeof(sample_type));
|
||||
} else {
|
||||
bytes = qMin(static_cast<Engine::Scope::size_type>(chunk_size),
|
||||
scope_.size() * sizeof(sample_type));
|
||||
@ -360,7 +381,9 @@ bool GstEngine::Load(const QUrl& url, Engine::TrackChangeFlags change,
|
||||
|
||||
bool crossfade =
|
||||
current_pipeline_ && ((crossfade_enabled_ && change & Engine::Manual) ||
|
||||
(autocrossfade_enabled_ && change & Engine::Auto));
|
||||
(autocrossfade_enabled_ && change & Engine::Auto) ||
|
||||
((crossfade_enabled_ || autocrossfade_enabled_) &&
|
||||
change & Engine::Intro));
|
||||
|
||||
if (change & Engine::Auto && change & Engine::SameAlbum &&
|
||||
!crossfade_same_album_)
|
||||
@ -747,9 +770,8 @@ GstEngine::PluginDetailsList GstEngine::GetPluginList(
|
||||
if (QString(gst_element_factory_get_klass(factory)).contains(classname)) {
|
||||
PluginDetails details;
|
||||
details.name = QString::fromUtf8(gst_plugin_feature_get_name(p->data));
|
||||
details.description = QString::fromUtf8(
|
||||
gst_element_factory_get_metadata(factory,
|
||||
GST_ELEMENT_METADATA_DESCRIPTION));
|
||||
details.description = QString::fromUtf8(gst_element_factory_get_metadata(
|
||||
factory, GST_ELEMENT_METADATA_DESCRIPTION));
|
||||
ret << details;
|
||||
}
|
||||
p = g_list_next(p);
|
||||
@ -768,6 +790,7 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
|
||||
ret->set_buffer_duration_nanosec(buffer_duration_nanosec_);
|
||||
ret->set_buffer_min_fill(buffer_min_fill_);
|
||||
ret->set_mono_playback(mono_playback_);
|
||||
ret->set_sample_rate(sample_rate_);
|
||||
|
||||
ret->AddBufferConsumer(this);
|
||||
for (BufferConsumer* consumer : buffer_consumers_) {
|
||||
|
25
src/engines/gstengine.h
Executable file → Normal file
25
src/engines/gstengine.h
Executable file → Normal file
@ -24,10 +24,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "bufferconsumer.h"
|
||||
#include "enginebase.h"
|
||||
#include "core/boundfuturewatcher.h"
|
||||
#include "core/timeconstants.h"
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QHash>
|
||||
@ -36,7 +33,10 @@
|
||||
#include <QStringList>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "bufferconsumer.h"
|
||||
#include "enginebase.h"
|
||||
#include "core/boundfuturewatcher.h"
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
class QTimer;
|
||||
class QTimerEvent;
|
||||
@ -45,6 +45,11 @@ class DeviceFinder;
|
||||
class GstEnginePipeline;
|
||||
class TaskManager;
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
struct _GTlsDatabase;
|
||||
typedef struct _GTlsDatabase GTlsDatabase;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @class GstEngine
|
||||
* @short GStreamer engine plugin
|
||||
@ -66,6 +71,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
};
|
||||
typedef QList<OutputDetails> OutputDetailsList;
|
||||
|
||||
static const int kAutoSampleRate = -1;
|
||||
static const char* kSettingsGroup;
|
||||
static const char* kAutoSink;
|
||||
|
||||
@ -115,6 +121,10 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
void AddBufferConsumer(BufferConsumer* consumer);
|
||||
void RemoveBufferConsumer(BufferConsumer* consumer);
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
GTlsDatabase* tls_database() const { return tls_database_; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void SetVolumeSW(uint percent);
|
||||
void timerEvent(QTimerEvent*);
|
||||
@ -206,6 +216,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
int buffer_min_fill_;
|
||||
|
||||
bool mono_playback_;
|
||||
int sample_rate_;
|
||||
|
||||
mutable bool can_decode_success_;
|
||||
mutable bool can_decode_last_;
|
||||
@ -228,6 +239,10 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
int scope_chunks_;
|
||||
|
||||
QList<DeviceFinder*> device_finders_;
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
GTlsDatabase* tls_database_;
|
||||
#endif
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(GstEngine::OutputDetails)
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QPair>
|
||||
#include <QRegExp>
|
||||
#include <QUuid>
|
||||
|
||||
#include "bufferconsumer.h"
|
||||
@ -67,6 +69,7 @@ GstEnginePipeline::GstEnginePipeline(GstEngine* engine)
|
||||
buffer_min_fill_(33),
|
||||
buffering_(false),
|
||||
mono_playback_(false),
|
||||
sample_rate_(GstEngine::kAutoSampleRate),
|
||||
end_offset_nanosec_(-1),
|
||||
next_beginning_offset_nanosec_(-1),
|
||||
next_end_offset_nanosec_(-1),
|
||||
@ -75,6 +78,7 @@ GstEnginePipeline::GstEnginePipeline(GstEngine* engine)
|
||||
pipeline_is_initialised_(false),
|
||||
pipeline_is_connected_(false),
|
||||
pending_seek_nanosec_(-1),
|
||||
last_known_position_ns_(0),
|
||||
volume_percent_(100),
|
||||
volume_modifier_(1.0),
|
||||
pipeline_(nullptr),
|
||||
@ -124,6 +128,8 @@ void GstEnginePipeline::set_mono_playback(bool enabled) {
|
||||
mono_playback_ = enabled;
|
||||
}
|
||||
|
||||
void GstEnginePipeline::set_sample_rate(int rate) { sample_rate_ = rate; }
|
||||
|
||||
bool GstEnginePipeline::ReplaceDecodeBin(GstElement* new_bin) {
|
||||
if (!new_bin) return false;
|
||||
|
||||
@ -384,7 +390,26 @@ bool GstEnginePipeline::Init() {
|
||||
gst_element_link(probe_queue, probe_converter);
|
||||
gst_element_link_many(audio_queue, equalizer_preamp_, equalizer_,
|
||||
stereo_panorama_, volume_, audioscale_, convert,
|
||||
audiosink_, nullptr);
|
||||
nullptr);
|
||||
|
||||
// add caps for fixed sample rate and mono, but only if requested
|
||||
if (sample_rate_ != GstEngine::kAutoSampleRate && sample_rate_ > 0) {
|
||||
GstCaps* caps = gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT,
|
||||
sample_rate_, nullptr);
|
||||
if (mono_playback_) {
|
||||
gst_caps_set_simple(caps, "channels", G_TYPE_INT, 1, nullptr);
|
||||
}
|
||||
|
||||
gst_element_link_filtered(convert, audiosink_, caps);
|
||||
gst_caps_unref(caps);
|
||||
} else if (mono_playback_) {
|
||||
GstCaps* capsmono =
|
||||
gst_caps_new_simple("audio/x-raw", "channels", G_TYPE_INT, 1, nullptr);
|
||||
gst_element_link_filtered(convert, audiosink_, capsmono);
|
||||
gst_caps_unref(capsmono);
|
||||
} else {
|
||||
gst_element_link(convert, audiosink_);
|
||||
}
|
||||
|
||||
// Add probes and handlers.
|
||||
gst_pad_add_probe(gst_element_get_static_pad(probe_converter, "src"),
|
||||
@ -601,15 +626,43 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage* msg) {
|
||||
emit Error(id(), message, domain, code);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* Streams served by Akamai tend to have a weird tag format embedded.
|
||||
*
|
||||
* Example:
|
||||
* All Things Dance - text="Evolution" song_spot="T" MediaBaseId="0"
|
||||
* itunesTrackId="0" amgTrackId="0" amgArtistId="0" TAID="0" TPID="0"
|
||||
* cartcutId="0"
|
||||
*/
|
||||
QPair<QString, QString> ParseAkamaiTag(const QString& tag) {
|
||||
QRegExp re("(.*) - text=\"([^\"]+)");
|
||||
re.indexIn(tag);
|
||||
if (re.capturedTexts().length() >= 3) {
|
||||
return qMakePair(re.cap(1), re.cap(2));
|
||||
}
|
||||
return qMakePair(tag, QString());
|
||||
}
|
||||
|
||||
bool IsAkamaiTag(const QString& tag) { return tag.contains("- text=\""); }
|
||||
}
|
||||
|
||||
void GstEnginePipeline::TagMessageReceived(GstMessage* msg) {
|
||||
GstTagList* taglist = nullptr;
|
||||
gst_message_parse_tag(msg, &taglist);
|
||||
|
||||
Engine::SimpleMetaBundle bundle;
|
||||
bundle.title = ParseTag(taglist, GST_TAG_TITLE);
|
||||
bundle.artist = ParseTag(taglist, GST_TAG_ARTIST);
|
||||
bundle.comment = ParseTag(taglist, GST_TAG_COMMENT);
|
||||
bundle.album = ParseTag(taglist, GST_TAG_ALBUM);
|
||||
if (IsAkamaiTag(bundle.title)) {
|
||||
QPair<QString, QString> artistTitlePair = ParseAkamaiTag(bundle.title);
|
||||
bundle.artist = artistTitlePair.first;
|
||||
bundle.title = artistTitlePair.second;
|
||||
} else {
|
||||
bundle.artist = ParseTag(taglist, GST_TAG_ARTIST);
|
||||
bundle.comment = ParseTag(taglist, GST_TAG_COMMENT);
|
||||
bundle.album = ParseTag(taglist, GST_TAG_ALBUM);
|
||||
}
|
||||
|
||||
gst_tag_list_free(taglist);
|
||||
|
||||
@ -890,25 +943,12 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin,
|
||||
g_object_set(element, "device",
|
||||
instance->source_device().toLocal8Bit().constData(), nullptr);
|
||||
}
|
||||
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element),
|
||||
"extra-headers") &&
|
||||
instance->url().host().contains("grooveshark")) {
|
||||
// Grooveshark streaming servers will answer with a 400 error 'Bad request'
|
||||
// if we don't specify 'Range' field in HTTP header.
|
||||
// Maybe it could be useful in some other cases, but for now, I prefer to
|
||||
// keep this grooveshark specific.
|
||||
GstStructure* headers;
|
||||
headers = gst_structure_new("extra-headers", "Range", G_TYPE_STRING,
|
||||
"bytes=0-", nullptr);
|
||||
g_object_set(element, "extra-headers", headers, nullptr);
|
||||
gst_structure_free(headers);
|
||||
}
|
||||
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element),
|
||||
"extra-headers") &&
|
||||
instance->url().host().contains("amazonaws.com")) {
|
||||
GstStructure* headers = gst_structure_new(
|
||||
"extra-headers", "Authorization", G_TYPE_STRING,
|
||||
instance->url().fragment().toAscii().data(), nullptr);
|
||||
GstStructure* headers =
|
||||
gst_structure_new("extra-headers", "Authorization", G_TYPE_STRING,
|
||||
instance->url().fragment().toAscii().data(), nullptr);
|
||||
g_object_set(element, "extra-headers", headers, nullptr);
|
||||
gst_structure_free(headers);
|
||||
}
|
||||
@ -921,11 +961,10 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin,
|
||||
nullptr);
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
// Override the CA cert path for Soup on Mac to our shipped version.
|
||||
QDir resources_dir(mac::GetResourcesPath());
|
||||
QString ca_cert_path = resources_dir.filePath("cacert.pem");
|
||||
g_object_set(element, "tls-database", instance->engine_->tls_database(),
|
||||
nullptr);
|
||||
g_object_set(element, "ssl-use-system-ca-file", false, nullptr);
|
||||
g_object_set(element, "ssl-ca-file", ca_cert_path.toUtf8().data(), nullptr);
|
||||
g_object_set(element, "ssl-strict", TRUE, nullptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -958,10 +997,11 @@ void GstEnginePipeline::TransitionToNext() {
|
||||
}
|
||||
|
||||
qint64 GstEnginePipeline::position() const {
|
||||
gint64 value = 0;
|
||||
gst_element_query_position(pipeline_, GST_FORMAT_TIME, &value);
|
||||
if (pipeline_is_initialised_)
|
||||
gst_element_query_position(pipeline_, GST_FORMAT_TIME,
|
||||
&last_known_position_ns_);
|
||||
|
||||
return value;
|
||||
return last_known_position_ns_;
|
||||
}
|
||||
|
||||
qint64 GstEnginePipeline::length() const {
|
||||
@ -1015,6 +1055,7 @@ bool GstEnginePipeline::Seek(qint64 nanosec) {
|
||||
}
|
||||
|
||||
pending_seek_nanosec_ = -1;
|
||||
last_known_position_ns_ = nanosec;
|
||||
return gst_element_seek_simple(pipeline_, GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_FLUSH, nanosec);
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ class GstEnginePipeline : public QObject {
|
||||
void set_buffer_duration_nanosec(qint64 duration_nanosec);
|
||||
void set_buffer_min_fill(int percent);
|
||||
void set_mono_playback(bool enabled);
|
||||
void set_sample_rate(int rate);
|
||||
|
||||
// Creates the pipeline, returns false on error
|
||||
bool InitFromUrl(const QUrl& url, qint64 end_nanosec);
|
||||
@ -220,6 +221,7 @@ signals:
|
||||
bool buffering_;
|
||||
|
||||
bool mono_playback_;
|
||||
int sample_rate_;
|
||||
|
||||
// The URL that is currently playing, and the URL that is to be preloaded
|
||||
// when the current track is close to finishing.
|
||||
@ -258,6 +260,13 @@ signals:
|
||||
bool pipeline_is_connected_;
|
||||
qint64 pending_seek_nanosec_;
|
||||
|
||||
// We can only use gst_element_query_position() when the pipeline is in
|
||||
// PAUSED nor PLAYING state. Whenever we get a new position (e.g. after a
|
||||
// correct call to gst_element_query_position() or after a seek), we store
|
||||
// it here so that we can use it when using gst_element_query_position() is
|
||||
// not possible.
|
||||
mutable gint64 last_known_position_ns_;
|
||||
|
||||
int volume_percent_;
|
||||
qreal volume_modifier_;
|
||||
|
||||
|
@ -115,7 +115,15 @@ QStandardItem* GlobalSearchModel::BuildContainers(const Song& s,
|
||||
case LibraryModel::GroupBy_YearAlbum:
|
||||
year = qMax(0, s.year());
|
||||
display_text = LibraryModel::PrettyYearAlbum(year, s.album());
|
||||
sort_text = LibraryModel::SortTextForYear(year) + s.album();
|
||||
sort_text = LibraryModel::SortTextForNumber(year) + s.album();
|
||||
unique_tag = s.album_id();
|
||||
has_album_icon = true;
|
||||
break;
|
||||
|
||||
case LibraryModel::GroupBy_OriginalYearAlbum:
|
||||
year = qMax(0, s.effective_originalyear());
|
||||
display_text = LibraryModel::PrettyYearAlbum(year, s.album());
|
||||
sort_text = LibraryModel::SortTextForNumber(year) + s.album();
|
||||
unique_tag = s.album_id();
|
||||
has_album_icon = true;
|
||||
break;
|
||||
@ -123,7 +131,13 @@ QStandardItem* GlobalSearchModel::BuildContainers(const Song& s,
|
||||
case LibraryModel::GroupBy_Year:
|
||||
year = qMax(0, s.year());
|
||||
display_text = QString::number(year);
|
||||
sort_text = LibraryModel::SortTextForYear(year) + " ";
|
||||
sort_text = LibraryModel::SortTextForNumber(year) + " ";
|
||||
break;
|
||||
|
||||
case LibraryModel::GroupBy_OriginalYear:
|
||||
year = qMax(0, s.effective_originalyear());
|
||||
display_text = QString::number(year);
|
||||
sort_text = LibraryModel::SortTextForNumber(year) + " ";
|
||||
break;
|
||||
|
||||
case LibraryModel::GroupBy_Composer:
|
||||
@ -249,7 +263,8 @@ void GlobalSearchModel::GetChildResults(
|
||||
if (is_provider) {
|
||||
// Go through all the items (through the proxy to keep them ordered) and
|
||||
// add the ones belonging to this provider to our list
|
||||
for (int i = 0; i < proxy_->rowCount(invisibleRootItem()->index()); ++i) {
|
||||
for (int i = 0; i < proxy_->rowCount(invisibleRootItem()->index());
|
||||
++i) {
|
||||
QModelIndex child_index =
|
||||
proxy_->index(i, 0, invisibleRootItem()->index());
|
||||
const QStandardItem* child_item =
|
||||
|
@ -337,7 +337,8 @@ void GlobalSearchView::LazyLoadArt(const QModelIndex& proxy_index) {
|
||||
proxy_index.data(LibraryModel::Role_ContainerType).toInt());
|
||||
if (container_type != LibraryModel::GroupBy_Album &&
|
||||
container_type != LibraryModel::GroupBy_AlbumArtist &&
|
||||
container_type != LibraryModel::GroupBy_YearAlbum) {
|
||||
container_type != LibraryModel::GroupBy_YearAlbum &&
|
||||
container_type != LibraryModel::GroupBy_OriginalYearAlbum) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -559,6 +560,11 @@ void GlobalSearchView::GroupByClicked(QAction* action) {
|
||||
}
|
||||
|
||||
void GlobalSearchView::SetGroupBy(const LibraryModel::Grouping& g) {
|
||||
// Clear requests: changing "group by" on the models will cause all the items to be removed/added
|
||||
// again, so all the QModelIndex here will become invalid. New requests will be created for those
|
||||
// songs when they will be displayed again anyway (when GlobalSearchItemDelegate::paint will call
|
||||
// LazyLoadArt)
|
||||
art_requests_.clear();
|
||||
// Update the models
|
||||
front_model_->SetGroupBy(g, true);
|
||||
back_model_->SetGroupBy(g, false);
|
||||
|
@ -1,132 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "groovesharksearchprovider.h"
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "internet/grooveshark/groovesharkservice.h"
|
||||
|
||||
GroovesharkSearchProvider::GroovesharkSearchProvider(Application* app,
|
||||
QObject* parent)
|
||||
: SearchProvider(app, parent), service_(nullptr) {}
|
||||
|
||||
void GroovesharkSearchProvider::Init(GroovesharkService* service) {
|
||||
service_ = service;
|
||||
SearchProvider::Init(
|
||||
"Grooveshark", "grooveshark", QIcon(":providers/grooveshark.png"),
|
||||
WantsDelayedQueries | ArtIsProbablyRemote | CanShowConfig);
|
||||
|
||||
connect(service_, SIGNAL(SimpleSearchResults(int, SongList)),
|
||||
SLOT(SearchDone(int, SongList)));
|
||||
connect(service_, SIGNAL(AlbumSearchResult(int, QList<quint64>)),
|
||||
SLOT(AlbumSearchResult(int, QList<quint64>)));
|
||||
connect(service_, SIGNAL(AlbumSongsLoaded(quint64, SongList)),
|
||||
SLOT(AlbumSongsLoaded(quint64, SongList)));
|
||||
|
||||
cover_loader_options_.desired_height_ = kArtHeight;
|
||||
cover_loader_options_.pad_output_image_ = true;
|
||||
cover_loader_options_.scale_output_image_ = true;
|
||||
|
||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)),
|
||||
SLOT(AlbumArtLoaded(quint64, QImage)));
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::SearchAsync(int id, const QString& query) {
|
||||
const int service_id = service_->SimpleSearch(query);
|
||||
pending_searches_[service_id] = PendingState(id, TokenizeQuery(query));
|
||||
;
|
||||
|
||||
const int album_id = service_->SearchAlbums(query);
|
||||
pending_searches_[album_id] = PendingState(id, TokenizeQuery(query));
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::SearchDone(int id, const SongList& songs) {
|
||||
// Map back to the original id.
|
||||
const PendingState state = pending_searches_.take(id);
|
||||
const int global_search_id = state.orig_id_;
|
||||
|
||||
ResultList ret;
|
||||
for (const Song& song : songs) {
|
||||
Result result(this);
|
||||
result.metadata_ = song;
|
||||
|
||||
ret << result;
|
||||
}
|
||||
|
||||
emit ResultsAvailable(global_search_id, ret);
|
||||
MaybeSearchFinished(global_search_id);
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::AlbumSearchResult(
|
||||
int id, const QList<quint64>& albums_ids) {
|
||||
// Map back to the original id.
|
||||
const PendingState state = pending_searches_.take(id);
|
||||
const int global_search_id = state.orig_id_;
|
||||
if (albums_ids.isEmpty()) {
|
||||
MaybeSearchFinished(global_search_id);
|
||||
return;
|
||||
}
|
||||
for (const quint64 album_id : albums_ids) {
|
||||
pending_searches_[album_id] = PendingState(global_search_id, QStringList());
|
||||
}
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::MaybeSearchFinished(int id) {
|
||||
if (pending_searches_.keys(PendingState(id, QStringList())).isEmpty()) {
|
||||
emit SearchFinished(id);
|
||||
}
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::LoadArtAsync(int id, const Result& result) {
|
||||
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(
|
||||
cover_loader_options_, result.metadata_);
|
||||
cover_loader_tasks_[loader_id] = id;
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::AlbumArtLoaded(quint64 id,
|
||||
const QImage& image) {
|
||||
if (!cover_loader_tasks_.contains(id)) {
|
||||
return;
|
||||
}
|
||||
int original_id = cover_loader_tasks_.take(id);
|
||||
emit ArtLoaded(original_id, image);
|
||||
}
|
||||
|
||||
bool GroovesharkSearchProvider::IsLoggedIn() {
|
||||
return (service_ && service_->IsLoggedIn());
|
||||
}
|
||||
|
||||
void GroovesharkSearchProvider::ShowConfig() { service_->ShowConfig(); }
|
||||
|
||||
void GroovesharkSearchProvider::AlbumSongsLoaded(quint64 id,
|
||||
const SongList& songs) {
|
||||
const PendingState state = pending_searches_.take(id);
|
||||
const int global_search_id = state.orig_id_;
|
||||
ResultList ret;
|
||||
for (const Song& s : songs) {
|
||||
Result result(this);
|
||||
result.metadata_ = s;
|
||||
ret << result;
|
||||
}
|
||||
|
||||
emit ResultsAvailable(global_search_id, ret);
|
||||
MaybeSearchFinished(global_search_id);
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GROOVESHARKSEARCHPROVIDER_H
|
||||
#define GROOVESHARKSEARCHPROVIDER_H
|
||||
|
||||
#include "searchprovider.h"
|
||||
#include "covers/albumcoverloaderoptions.h"
|
||||
#include "internet/grooveshark/groovesharkservice.h"
|
||||
|
||||
class AlbumCoverLoader;
|
||||
|
||||
class GroovesharkSearchProvider : public SearchProvider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GroovesharkSearchProvider(Application* app, QObject* parent = nullptr);
|
||||
void Init(GroovesharkService* service);
|
||||
|
||||
// SearchProvider
|
||||
void SearchAsync(int id, const QString& query) override;
|
||||
void LoadArtAsync(int id, const Result& result) override;
|
||||
bool IsLoggedIn() override;
|
||||
void ShowConfig() override;
|
||||
InternetService* internet_service() override { return service_; }
|
||||
|
||||
private slots:
|
||||
void SearchDone(int id, const SongList& songs);
|
||||
void AlbumSearchResult(int id, const QList<quint64>& albums_ids);
|
||||
void AlbumArtLoaded(quint64 id, const QImage& image);
|
||||
void AlbumSongsLoaded(quint64 id, const SongList& songs);
|
||||
|
||||
private:
|
||||
void MaybeSearchFinished(int id);
|
||||
|
||||
GroovesharkService* service_;
|
||||
QMap<int, PendingState> pending_searches_;
|
||||
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
QMap<quint64, int> cover_loader_tasks_;
|
||||
};
|
||||
|
||||
#endif
|
@ -35,6 +35,7 @@ void SearchProvider::Init(const QString& name, const QString& id,
|
||||
id_ = id;
|
||||
icon_ = icon;
|
||||
hints_ = hints;
|
||||
icon_as_image_ = QImage(icon.pixmap(48, 48).toImage());
|
||||
}
|
||||
|
||||
void SearchProvider::SetHint(Hint hint, bool set) {
|
||||
@ -89,11 +90,12 @@ void BlockingSearchProvider::SearchAsync(int id, const QString& query) {
|
||||
void BlockingSearchProvider::BlockingSearchFinished() {
|
||||
BoundFutureWatcher<ResultList, int>* watcher =
|
||||
static_cast<BoundFutureWatcher<ResultList, int>*>(sender());
|
||||
watcher->deleteLater();
|
||||
|
||||
const int id = watcher->data();
|
||||
emit ResultsAvailable(id, watcher->result());
|
||||
emit SearchFinished(id);
|
||||
|
||||
watcher->deleteLater();
|
||||
}
|
||||
|
||||
QImage SearchProvider::ScaleAndPad(const QImage& image) {
|
||||
|
@ -104,6 +104,7 @@ class SearchProvider : public QObject {
|
||||
const QString& name() const { return name_; }
|
||||
const QString& id() const { return id_; }
|
||||
const QIcon& icon() const { return icon_; }
|
||||
const QImage& icon_as_image() const { return icon_as_image_; }
|
||||
|
||||
Hints hints() const { return hints_; }
|
||||
bool wants_delayed_queries() const { return hints() & WantsDelayedQueries; }
|
||||
@ -190,6 +191,7 @@ signals:
|
||||
QString id_;
|
||||
QIcon icon_;
|
||||
Hints hints_;
|
||||
QImage icon_as_image_;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(SearchProvider::Result)
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "internet/amazon/amazonclouddrive.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
#include <QtGlobal>
|
||||
@ -37,6 +38,7 @@
|
||||
#include "library/librarybackend.h"
|
||||
#include "ui/settingsdialog.h"
|
||||
|
||||
using std::chrono::seconds;
|
||||
using std::placeholders::_1;
|
||||
|
||||
const char* AmazonCloudDrive::kServiceName = "Amazon Cloud Drive";
|
||||
@ -62,7 +64,7 @@ AmazonCloudDrive::AmazonCloudDrive(Application* app, InternetModel* parent)
|
||||
: CloudFileService(app, parent, kServiceName, kServiceId,
|
||||
QIcon(":/providers/amazonclouddrive.png"),
|
||||
SettingsDialog::Page_AmazonCloudDrive),
|
||||
network_(new NetworkAccessManager) {
|
||||
network_(new NetworkAccessManager(this)) {
|
||||
app->player()->RegisterUrlHandler(new AmazonUrlHandler(this, this));
|
||||
}
|
||||
|
||||
@ -197,16 +199,16 @@ void AmazonCloudDrive::MonitorReply(QNetworkReply* reply,
|
||||
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (code >= 500) { // Retry with exponential backoff.
|
||||
int max_delay_s = std::pow(std::min(retries + 1, 8), 2);
|
||||
int delay_s = qrand() % max_delay_s;
|
||||
seconds delay(qrand() % max_delay_s);
|
||||
qLog(Debug) << "Request failed with code:" << code << "- retrying after"
|
||||
<< delay_s << "seconds";
|
||||
<< delay << "seconds";
|
||||
DoAfter([=]() {
|
||||
if (post_data.isEmpty()) {
|
||||
Get(reply->request(), done, retries + 1);
|
||||
} else {
|
||||
Post(reply->request(), post_data, done, retries + 1);
|
||||
}
|
||||
}, delay_s * kMsecPerSec);
|
||||
}, delay);
|
||||
} else {
|
||||
// Request failed permanently.
|
||||
done(reply);
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include <QtDebug>
|
||||
|
||||
#include "internet/digitally/digitallyimportedservicebase.h"
|
||||
#include "internet/grooveshark/groovesharkservice.h"
|
||||
#include "internet/icecast/icecastservice.h"
|
||||
#include "internet/core/internetmimedata.h"
|
||||
#include "internet/core/internetservice.h"
|
||||
@ -90,11 +89,9 @@ InternetModel::InternetModel(Application* app, QObject* parent)
|
||||
AddService(new DigitallyImportedService(app, this));
|
||||
AddService(new IcecastService(app, this));
|
||||
AddService(new JamendoService(app, this));
|
||||
AddService(new GroovesharkService(app, this));
|
||||
AddService(new JazzRadioService(app, this));
|
||||
AddService(new MagnatuneService(app, this));
|
||||
AddService(new PodcastService(app, this));
|
||||
AddService(new RadioGFMService(app, this));
|
||||
AddService(new RockRadioService(app, this));
|
||||
AddService(new SavedRadio(app, this));
|
||||
AddService(new RadioTunesService(app, this));
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "internet/core/oauthenticator.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QSslError>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
||||
@ -114,6 +115,7 @@ void OAuthenticator::RequestAccessToken(const QByteArray& code,
|
||||
"application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* reply = network_.post(request, post_data.toUtf8());
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(SslErrors(QList<QSslError>)));
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(FetchAccessTokenFinished(QNetworkReply*)), reply);
|
||||
}
|
||||
@ -189,3 +191,9 @@ void OAuthenticator::RefreshAccessTokenFinished(QNetworkReply* reply) {
|
||||
SetExpiryTime(result["expires_in"].toInt());
|
||||
emit Finished();
|
||||
}
|
||||
|
||||
void OAuthenticator::SslErrors(const QList<QSslError>& errors) {
|
||||
for (const QSslError& error : errors) {
|
||||
qLog(Debug) << error.errorString();
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ class OAuthenticator : public QObject {
|
||||
void RedirectArrived(LocalRedirectServer* server, QUrl url);
|
||||
void FetchAccessTokenFinished(QNetworkReply* reply);
|
||||
void RefreshAccessTokenFinished(QNetworkReply* reply);
|
||||
void SslErrors(const QList<QSslError>& errors);
|
||||
|
||||
private:
|
||||
static const char* kRemoteURL;
|
||||
|
@ -87,6 +87,7 @@ void DigitallyImportedUrlHandler::LoadPlaylistFinished(QIODevice* device) {
|
||||
// Failed to get playlist?
|
||||
if (songs.count() == 0) {
|
||||
service_->StreamError(tr("Error loading di.fm playlist"));
|
||||
emit AsyncLoadComplete(LoadResult(last_original_url_));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,62 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "groovesharkradio.h"
|
||||
|
||||
#include "groovesharkservice.h"
|
||||
#include "core/logging.h"
|
||||
#include "internet/core/internetplaylistitem.h"
|
||||
|
||||
GroovesharkRadio::GroovesharkRadio(GroovesharkService* service)
|
||||
: service_(service), tag_id_(0), use_tag_(false), first_time_(true) {}
|
||||
|
||||
GroovesharkRadio::GroovesharkRadio(GroovesharkService* service, int tag_id)
|
||||
: service_(service), tag_id_(tag_id), use_tag_(true), first_time_(true) {}
|
||||
|
||||
void GroovesharkRadio::Load(const QByteArray& data) {}
|
||||
|
||||
QByteArray GroovesharkRadio::Save() const { return QByteArray(); }
|
||||
|
||||
PlaylistItemList GroovesharkRadio::Generate() {
|
||||
PlaylistItemList items;
|
||||
if (first_time_) {
|
||||
Song song;
|
||||
if (use_tag_) {
|
||||
song = service_->StartAutoplayTag(tag_id_, autoplay_state_);
|
||||
} else {
|
||||
song = service_->StartAutoplay(autoplay_state_);
|
||||
}
|
||||
// If the song url isn't valid, stop here
|
||||
if (!song.is_valid()) {
|
||||
return items;
|
||||
}
|
||||
PlaylistItemPtr playlist_item =
|
||||
PlaylistItemPtr(new InternetPlaylistItem(service_, song));
|
||||
items << playlist_item;
|
||||
first_time_ = false;
|
||||
}
|
||||
Song song = service_->GetAutoplaySong(autoplay_state_);
|
||||
if (!song.is_valid()) {
|
||||
return items;
|
||||
}
|
||||
PlaylistItemPtr playlist_item =
|
||||
PlaylistItemPtr(new InternetPlaylistItem(service_, song));
|
||||
items << playlist_item;
|
||||
return items;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_GROOVESHARK_GROOVESHARKRADIO_H_
|
||||
#define INTERNET_GROOVESHARK_GROOVESHARKRADIO_H_
|
||||
|
||||
#include "smartplaylists/generator.h"
|
||||
|
||||
class GroovesharkService;
|
||||
|
||||
class GroovesharkRadio : public smart_playlists::Generator {
|
||||
public:
|
||||
// Start Grooveshark radio for a particular type of music
|
||||
GroovesharkRadio(GroovesharkService* service, int tag_id);
|
||||
// Start Grooveshark radio based on last artists and songs you listen to
|
||||
explicit GroovesharkRadio(GroovesharkService* service);
|
||||
|
||||
QString type() const { return "Grooveshark"; }
|
||||
void Load(const QByteArray& data);
|
||||
QByteArray Save() const;
|
||||
PlaylistItemList Generate();
|
||||
PlaylistItemList GenerateMore(int count) { return Generate(); }
|
||||
bool is_dynamic() const { return true; }
|
||||
|
||||
private:
|
||||
GroovesharkService* service_;
|
||||
int tag_id_;
|
||||
// Boolean to specify if we should use tag. If not, we will used autoplay
|
||||
// without tag
|
||||
bool use_tag_;
|
||||
// For Generate: indicates if it's the first time we generate songs
|
||||
bool first_time_;
|
||||
QVariantMap autoplay_state_;
|
||||
};
|
||||
|
||||
#endif // INTERNET_GROOVESHARK_GROOVESHARKRADIO_H_
|
File diff suppressed because it is too large
Load Diff
@ -1,338 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011-2014, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2011-2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_GROOVESHARK_GROOVESHARKSERVICE_H_
|
||||
#define INTERNET_GROOVESHARK_GROOVESHARKSERVICE_H_
|
||||
|
||||
#include "internet/core/internetmodel.h"
|
||||
#include "internet/core/internetservice.h"
|
||||
|
||||
#include <QSslError>
|
||||
|
||||
class GroovesharkUrlHandler;
|
||||
class NetworkAccessManager;
|
||||
class Playlist;
|
||||
class SearchBoxWidget;
|
||||
|
||||
class QMenu;
|
||||
class QNetworkReply;
|
||||
class QNetworkRequest;
|
||||
class QSortFilterProxyModel;
|
||||
|
||||
class GroovesharkService : public InternetService {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GroovesharkService(Application* app, InternetModel* parent);
|
||||
~GroovesharkService();
|
||||
|
||||
enum Role {
|
||||
Role_UserPlaylistId = InternetModel::RoleCount,
|
||||
Role_PlaylistType
|
||||
};
|
||||
|
||||
enum PlaylistType {
|
||||
UserPlaylist = Qt::UserRole,
|
||||
// Favorites and Library list are like playlists, but we want to do special
|
||||
// treatments in some cases
|
||||
UserFavorites,
|
||||
UserLibrary,
|
||||
SubscribedPlaylist
|
||||
};
|
||||
|
||||
// Values are persisted - don't change.
|
||||
enum LoginState {
|
||||
LoginState_LoggedIn = 1,
|
||||
LoginState_AuthFailed = 2,
|
||||
LoginState_NoPremium = 3,
|
||||
LoginState_OtherError = 4
|
||||
};
|
||||
|
||||
// Internet Service methods
|
||||
QStandardItem* CreateRootItem();
|
||||
void LazyPopulate(QStandardItem* parent);
|
||||
|
||||
void ItemDoubleClicked(QStandardItem* item);
|
||||
smart_playlists::GeneratorPtr CreateGenerator(QStandardItem* item);
|
||||
void DropMimeData(const QMimeData* data, const QModelIndex& index);
|
||||
QList<QAction*> playlistitem_actions(const Song& song);
|
||||
void ShowContextMenu(const QPoint& global_pos);
|
||||
QWidget* HeaderWidget() const;
|
||||
|
||||
// User should be logged in to be able to generate streaming urls
|
||||
QUrl GetStreamingUrlFromSongId(const QString& song_id,
|
||||
const QString& artist_id, QString* server_id,
|
||||
QString* stream_key, qint64* length_nanosec);
|
||||
void Login(const QString& username, const QString& password);
|
||||
void Logout();
|
||||
bool IsLoggedIn() const { return !session_id_.isEmpty(); }
|
||||
void RetrieveUserPlaylists();
|
||||
void RetrieveUserFavorites();
|
||||
void RetrieveUserLibrarySongs();
|
||||
void RetrievePopularSongs();
|
||||
void RetrievePopularSongsMonth();
|
||||
void RetrievePopularSongsToday();
|
||||
void RetrieveSubscribedPlaylists();
|
||||
void RetrieveAutoplayTags();
|
||||
void SetPlaylistSongs(int playlist_id, const QList<int>& songs_ids);
|
||||
void RemoveFromPlaylist(int playlist_id,
|
||||
const QList<int>& songs_ids_to_remove);
|
||||
// Refresh playlist_id playlist , or create it if it doesn't exist
|
||||
void RefreshPlaylist(int playlist_id);
|
||||
void DeletePlaylist(int playlist_id);
|
||||
void RenamePlaylist(int playlist_id);
|
||||
void AddUserFavoriteSong(int song_id);
|
||||
void RemoveFromFavorites(const QList<int>& songs_ids_to_remove);
|
||||
void AddUserLibrarySongs(const QList<int>& songs_ids);
|
||||
void RemoveFromLibrary(const QList<int>& songs_ids_to_remove);
|
||||
void GetSongUrlToShare(int song_id);
|
||||
void GetPlaylistUrlToShare(int playlist_id);
|
||||
// Start autoplay for the given tag_id, fill the autoplay_state, returns a
|
||||
// first song to play
|
||||
Song StartAutoplayTag(int tag_id, QVariantMap& autoplay_state);
|
||||
Song StartAutoplay(QVariantMap& autoplay_state);
|
||||
// Get another autoplay song. autoplay_state is the autoplay_state received
|
||||
// from StartAutoplayTag
|
||||
Song GetAutoplaySong(QVariantMap& autoplay_state);
|
||||
void MarkStreamKeyOver30Secs(const QString& stream_key,
|
||||
const QString& server_id);
|
||||
void MarkSongComplete(const QString& song_id, const QString& stream_key,
|
||||
const QString& server_id);
|
||||
|
||||
// Persisted in the settings and updated on each Login().
|
||||
LoginState login_state() const { return login_state_; }
|
||||
const QString& session_id() { return session_id_; }
|
||||
|
||||
int SimpleSearch(const QString& query);
|
||||
int SearchAlbums(const QString& query);
|
||||
void GetAlbumSongs(quint64 album_id);
|
||||
|
||||
static const char* kServiceName;
|
||||
static const char* kSettingsGroup;
|
||||
|
||||
signals:
|
||||
void LoginFinished(bool success);
|
||||
void SimpleSearchResults(int id, SongList songs);
|
||||
// AlbumSearchResult emits the search id and the Grooveshark ids of the
|
||||
// albums found. Albums' songs will be loaded asynchronously and
|
||||
// AlbumSongsLoaded will be emitted, containing the actual Albums' songs.
|
||||
void AlbumSearchResult(int id, QList<quint64> albums_ids);
|
||||
void AlbumSongsLoaded(quint64 id, SongList songs);
|
||||
|
||||
public slots:
|
||||
void Search(const QString& text, bool now = false);
|
||||
void ShowConfig();
|
||||
// Refresh all Grooveshark's items, and re-fill them
|
||||
void RefreshItems();
|
||||
|
||||
protected:
|
||||
struct PlaylistInfo {
|
||||
PlaylistInfo() {}
|
||||
PlaylistInfo(int id, QString name, QStandardItem* item = nullptr)
|
||||
: id_(id), name_(name), item_(item) {}
|
||||
|
||||
bool operator<(const PlaylistInfo other) const {
|
||||
return name_.localeAwareCompare(other.name_) < 0;
|
||||
}
|
||||
|
||||
int id_;
|
||||
QString name_;
|
||||
QStandardItem* item_;
|
||||
QList<int> songs_ids_;
|
||||
};
|
||||
|
||||
private slots:
|
||||
void SessionCreated(QNetworkReply* reply);
|
||||
void DoSearch();
|
||||
void SearchSongsFinished(QNetworkReply* reply);
|
||||
void SimpleSearchFinished(QNetworkReply* reply, int id);
|
||||
void SearchAlbumsFinished(QNetworkReply* reply, int id);
|
||||
void GetAlbumSongsFinished(QNetworkReply* reply, quint64 album_id);
|
||||
void Authenticated(QNetworkReply* reply);
|
||||
void UserPlaylistsRetrieved(QNetworkReply* reply);
|
||||
void UserFavoritesRetrieved(QNetworkReply* reply, int task_id);
|
||||
void UserLibrarySongsRetrieved(QNetworkReply* reply, int task_id);
|
||||
void PopularSongsMonthRetrieved(QNetworkReply* reply);
|
||||
void PopularSongsTodayRetrieved(QNetworkReply* reply);
|
||||
void SubscribedPlaylistsRetrieved(QNetworkReply* reply);
|
||||
void AutoplayTagsRetrieved(QNetworkReply* reply);
|
||||
void PlaylistSongsRetrieved(QNetworkReply* reply, int playlist_id, int request_id);
|
||||
void PlaylistSongsSet(QNetworkReply* reply, int playlist_id, int task_id);
|
||||
void CreateNewPlaylist();
|
||||
void NewPlaylistCreated(QNetworkReply* reply, const QString& name);
|
||||
void DeleteCurrentPlaylist();
|
||||
void RenameCurrentPlaylist();
|
||||
void PlaylistDeleted(QNetworkReply* reply, int playlist_id);
|
||||
void PlaylistRenamed(QNetworkReply* reply, int playlist_id,
|
||||
const QString& new_name);
|
||||
void AddCurrentSongToUserFavorites() {
|
||||
AddUserFavoriteSong(current_song_id_);
|
||||
}
|
||||
void AddCurrentSongToUserLibrary() {
|
||||
AddUserLibrarySongs(QList<int>() << current_song_id_);
|
||||
}
|
||||
void AddCurrentSongToPlaylist(QAction* action);
|
||||
void UserFavoriteSongAdded(QNetworkReply* reply, int task_id);
|
||||
void UserLibrarySongAdded(QNetworkReply* reply, int task_id);
|
||||
void GetCurrentSongUrlToShare();
|
||||
void SongUrlToShareReceived(QNetworkReply* reply);
|
||||
void GetCurrentPlaylistUrlToShare();
|
||||
void PlaylistUrlToShareReceived(QNetworkReply* reply);
|
||||
void RemoveCurrentFromPlaylist();
|
||||
void RemoveCurrentFromFavorites();
|
||||
void RemoveCurrentFromLibrary();
|
||||
void SongsRemovedFromFavorites(QNetworkReply* reply, int task_id);
|
||||
void SongsRemovedFromLibrary(QNetworkReply* reply, int task_id);
|
||||
void StreamMarked(QNetworkReply* reply);
|
||||
void SongMarkedAsComplete(QNetworkReply* reply);
|
||||
|
||||
void RequestSslErrors(const QList<QSslError>& errors);
|
||||
|
||||
void Homepage();
|
||||
|
||||
private:
|
||||
void EnsureMenuCreated();
|
||||
void EnsureItemsCreated();
|
||||
void RemoveItems();
|
||||
void EnsureConnected();
|
||||
void ClearSearchResults();
|
||||
|
||||
// Create a playlist item, with data set as excepted. Doesn't fill the item
|
||||
// with songs rows.
|
||||
QStandardItem* CreatePlaylistItem(const QString& playlist_name,
|
||||
int playlist_id);
|
||||
|
||||
void AuthenticateSession();
|
||||
void InitCountry();
|
||||
|
||||
// Create a request for the given method, with the given params.
|
||||
// If need_authentication is true, add session_id to params.
|
||||
// Returns the reply object created
|
||||
QNetworkReply* CreateRequest(const QString& method_name,
|
||||
const QList<QPair<QString, QVariant>>& params,
|
||||
bool use_https = false);
|
||||
// Convenient function which block until 'reply' replies, or timeout after 10
|
||||
// seconds. Returns false if reply has timeouted
|
||||
bool WaitForReply(QNetworkReply* reply);
|
||||
// Convenient function for extracting result from reply
|
||||
QVariantMap ExtractResult(QNetworkReply* reply);
|
||||
// Convenient function for extracting songs from grooveshark result. result
|
||||
// should be the "result" field of most Grooveshark replies
|
||||
SongList ExtractSongs(const QVariantMap& result);
|
||||
// Convenient function for extracting song from grooveshark result.
|
||||
// result_song should be the song field ('song', 'nextSong', ...) of the
|
||||
// Grooveshark reply
|
||||
Song ExtractSong(const QVariantMap& result_song);
|
||||
// Convenient functions for extracting Grooveshark songs ids
|
||||
QList<int> ExtractSongsIds(const QVariantMap& result);
|
||||
QList<int> ExtractSongsIds(const QList<QUrl>& urls);
|
||||
int ExtractSongId(
|
||||
const QUrl& url); // Returns 0 if url is not a Grooveshark url
|
||||
// Convenient function for extracting basic playlist info (only 'id' and
|
||||
// 'name': QStandardItem still need to be created), and sort them by name
|
||||
QList<PlaylistInfo> ExtractPlaylistInfo(const QVariantMap& result);
|
||||
|
||||
void ResetSessionId();
|
||||
|
||||
// Sort songs alphabetically only if the "sort_alphabetically" option has been
|
||||
// checked in the preferences settings.
|
||||
void SortSongsAlphabeticallyIfNeeded(SongList* songs) const;
|
||||
|
||||
GroovesharkUrlHandler* url_handler_;
|
||||
|
||||
QString pending_search_;
|
||||
|
||||
int next_pending_search_id_;
|
||||
int next_pending_playlist_retrieve_id_;
|
||||
|
||||
QSet<int> pending_retrieve_playlists_;
|
||||
|
||||
QMap<int, PlaylistInfo> playlists_;
|
||||
QMap<int, PlaylistInfo> subscribed_playlists_;
|
||||
|
||||
QStandardItem* root_;
|
||||
QStandardItem* search_;
|
||||
QStandardItem* popular_month_;
|
||||
QStandardItem* popular_today_;
|
||||
QStandardItem* stations_;
|
||||
QStandardItem* grooveshark_radio_;
|
||||
QStandardItem* favorites_;
|
||||
// Grooveshark Library (corresponds to Grooveshark 'MyMusic' actually, but
|
||||
// called 'Library' in the API).
|
||||
// Nothing to do with Clementine's local library
|
||||
QStandardItem* library_;
|
||||
QStandardItem* playlists_parent_;
|
||||
QStandardItem* subscribed_playlists_parent_;
|
||||
|
||||
NetworkAccessManager* network_;
|
||||
|
||||
QMenu* context_menu_;
|
||||
// IDs kept when showing menu, to know what the user has clicked on, to be
|
||||
// able to perform actions on corresponding items
|
||||
int current_song_id_;
|
||||
int current_playlist_id_;
|
||||
|
||||
QAction* create_playlist_;
|
||||
QAction* delete_playlist_;
|
||||
QAction* rename_playlist_;
|
||||
QAction* remove_from_playlist_;
|
||||
QAction* remove_from_favorites_;
|
||||
QAction* remove_from_library_;
|
||||
QAction* get_url_to_share_song_;
|
||||
QAction* get_url_to_share_playlist_;
|
||||
QList<QAction*> playlistitem_actions_;
|
||||
|
||||
SearchBoxWidget* search_box_;
|
||||
|
||||
QTimer* search_delay_;
|
||||
QNetworkReply* last_search_reply_;
|
||||
|
||||
QString username_;
|
||||
QString password_; // In fact, password's md5 hash
|
||||
QString user_id_;
|
||||
QString session_id_;
|
||||
QMap<QString, QVariant> country_;
|
||||
// The last artists and songs ids th users has listened to. Used for autoplay
|
||||
QList<int> last_artists_ids_;
|
||||
QList<int> last_songs_ids_;
|
||||
QByteArray api_key_;
|
||||
|
||||
LoginState login_state_;
|
||||
|
||||
// Tasks' ids: we need to keep them in mind to be able to update task status
|
||||
// on each step
|
||||
int task_popular_id_;
|
||||
int task_playlists_id_;
|
||||
int task_search_id_;
|
||||
|
||||
static const char* kUrl;
|
||||
static const char* kUrlCover;
|
||||
static const char* kHomepage;
|
||||
|
||||
static const int kSongSearchLimit;
|
||||
static const int kSongSimpleSearchLimit;
|
||||
static const int kAlbumSearchLimit;
|
||||
static const int kSearchDelayMsec;
|
||||
|
||||
static const char* kApiKey;
|
||||
static const char* kApiSecret;
|
||||
};
|
||||
|
||||
#endif // INTERNET_GROOVESHARK_GROOVESHARKSERVICE_H_
|
@ -1,131 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "groovesharksettingspage.h"
|
||||
#include "ui_groovesharksettingspage.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSettings>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "groovesharkservice.h"
|
||||
#include "internet/core/internetmodel.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/network.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
GroovesharkSettingsPage::GroovesharkSettingsPage(SettingsDialog* dialog)
|
||||
: SettingsPage(dialog),
|
||||
ui_(new Ui_GroovesharkSettingsPage),
|
||||
service_(InternetModel::Service<GroovesharkService>()),
|
||||
validated_(false) {
|
||||
ui_->setupUi(this);
|
||||
|
||||
setWindowIcon(QIcon(":/providers/grooveshark.png"));
|
||||
|
||||
connect(ui_->login, SIGNAL(clicked()), SLOT(Login()));
|
||||
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
|
||||
connect(ui_->login_state, SIGNAL(LoginClicked()), SLOT(Login()));
|
||||
|
||||
connect(service_, SIGNAL(LoginFinished(bool)), SLOT(LoginFinished(bool)));
|
||||
|
||||
ui_->login_state->AddCredentialField(ui_->username);
|
||||
ui_->login_state->AddCredentialField(ui_->password);
|
||||
ui_->login_state->AddCredentialGroup(ui_->account_group);
|
||||
}
|
||||
|
||||
GroovesharkSettingsPage::~GroovesharkSettingsPage() { delete ui_; }
|
||||
|
||||
void GroovesharkSettingsPage::Login() {
|
||||
if (service_->IsLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoginInProgress);
|
||||
service_->Login(ui_->username->text(), ui_->password->text());
|
||||
}
|
||||
|
||||
void GroovesharkSettingsPage::Load() {
|
||||
QSettings s;
|
||||
s.beginGroup(GroovesharkService::kSettingsGroup);
|
||||
|
||||
original_username_ = s.value("username").toString();
|
||||
|
||||
ui_->username->setText(original_username_);
|
||||
validated_ = false;
|
||||
|
||||
UpdateLoginState();
|
||||
|
||||
ui_->sort_alphabetically->setChecked(s.value("sort_alphabetically").toBool());
|
||||
}
|
||||
|
||||
void GroovesharkSettingsPage::Save() {
|
||||
QSettings s;
|
||||
s.beginGroup(GroovesharkService::kSettingsGroup);
|
||||
|
||||
s.setValue("username", ui_->username->text());
|
||||
s.setValue("sessionid", service_->session_id());
|
||||
const bool old_sort_value = s.value("sort_alphabetically").toBool();
|
||||
const bool new_sort_value = ui_->sort_alphabetically->isChecked();
|
||||
if (old_sort_value != new_sort_value) {
|
||||
s.setValue("sort_alphabetically", new_sort_value);
|
||||
service_->RefreshItems();
|
||||
}
|
||||
}
|
||||
|
||||
void GroovesharkSettingsPage::LoginFinished(bool success) {
|
||||
validated_ = success;
|
||||
|
||||
Save();
|
||||
UpdateLoginState();
|
||||
}
|
||||
|
||||
void GroovesharkSettingsPage::UpdateLoginState() {
|
||||
const bool logged_in = service_->IsLoggedIn();
|
||||
|
||||
ui_->login_state->SetLoggedIn(
|
||||
logged_in ? LoginStateWidget::LoggedIn : LoginStateWidget::LoggedOut,
|
||||
ui_->username->text());
|
||||
ui_->login_state->SetAccountTypeVisible(!logged_in);
|
||||
|
||||
switch (service_->login_state()) {
|
||||
case GroovesharkService::LoginState_NoPremium:
|
||||
ui_->login_state->SetAccountTypeText(
|
||||
tr("You do not have a Grooveshark Anywhere account."));
|
||||
break;
|
||||
|
||||
case GroovesharkService::LoginState_AuthFailed:
|
||||
ui_->login_state->SetAccountTypeText(
|
||||
tr("Your username or password was incorrect."));
|
||||
break;
|
||||
|
||||
default:
|
||||
ui_->login_state->SetAccountTypeText(
|
||||
tr("A Grooveshark Anywhere account is required."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GroovesharkSettingsPage::Logout() {
|
||||
service_->Logout();
|
||||
UpdateLoginState();
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_GROOVESHARK_GROOVESHARKSETTINGSPAGE_H_
|
||||
#define INTERNET_GROOVESHARK_GROOVESHARKSETTINGSPAGE_H_
|
||||
|
||||
#include "ui/settingspage.h"
|
||||
|
||||
class NetworkAccessManager;
|
||||
class Ui_GroovesharkSettingsPage;
|
||||
class GroovesharkService;
|
||||
|
||||
class GroovesharkSettingsPage : public SettingsPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GroovesharkSettingsPage(SettingsDialog* dialog);
|
||||
~GroovesharkSettingsPage();
|
||||
|
||||
void Load();
|
||||
void Save();
|
||||
|
||||
private slots:
|
||||
void Login();
|
||||
void LoginFinished(bool success);
|
||||
void Logout();
|
||||
|
||||
private:
|
||||
void UpdateLoginState();
|
||||
|
||||
private:
|
||||
Ui_GroovesharkSettingsPage* ui_;
|
||||
GroovesharkService* service_;
|
||||
|
||||
bool validated_;
|
||||
QString original_username_;
|
||||
QString original_password_;
|
||||
};
|
||||
|
||||
#endif // INTERNET_GROOVESHARK_GROOVESHARKSETTINGSPAGE_H_
|
@ -1,128 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GroovesharkSettingsPage</class>
|
||||
<widget class="QWidget" name="GroovesharkSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>480</width>
|
||||
<height>184</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Grooveshark</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>By default, Grooveshark sorts songs on date added</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="LoginStateWidget" name="login_state" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="account_group">
|
||||
<property name="title">
|
||||
<string>Account details</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QWidget" name="login_container" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="username_label">
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="username"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="password_label">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="login">
|
||||
<property name="text">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="preferences_group">
|
||||
<property name="title">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="sort_alphabetically">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sort playlists songs alphabetically</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LoginStateWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/loginstatewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
@ -1,82 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "groovesharkurlhandler.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "groovesharkservice.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
GroovesharkUrlHandler::GroovesharkUrlHandler(GroovesharkService* service,
|
||||
QObject* parent)
|
||||
: UrlHandler(parent),
|
||||
service_(service),
|
||||
timer_mark_stream_key_(new QTimer(this)) {
|
||||
// We have to warn Grooveshark when user has listened for more than 30
|
||||
// seconds of a song, and when it ends. I guess this is used by Grooveshark
|
||||
// for statistics and user history.
|
||||
// To do this, we have TrackAboutToEnd method, and timer_mark_stream_key_
|
||||
// timer.
|
||||
// It is not perfect, as we may call Grooveshark MarkStreamKeyOver30Secs even
|
||||
// if user hasn't actually listen to 30 seconds (e.g. stream set to pause
|
||||
// state) but this is not a big deal and it should be accurate enough anyway.
|
||||
timer_mark_stream_key_->setInterval(30000);
|
||||
timer_mark_stream_key_->setSingleShot(true);
|
||||
connect(timer_mark_stream_key_, SIGNAL(timeout()),
|
||||
SLOT(MarkStreamKeyOver30Secs()));
|
||||
}
|
||||
|
||||
UrlHandler::LoadResult GroovesharkUrlHandler::StartLoading(const QUrl& url) {
|
||||
qint64 length_nanosec = 0;
|
||||
QUrl streaming_url;
|
||||
QStringList ids = url.toString().remove("grooveshark://").split("/");
|
||||
if (ids.size() < 3) {
|
||||
qLog(Error) << "Invalid grooveshark URL: " << url.toString();
|
||||
qLog(Error) << "Should be grooveshark://artist_id/album_id/song_id";
|
||||
} else {
|
||||
last_artist_id_ = ids[0];
|
||||
last_album_id_ = ids[1];
|
||||
last_song_id_ = ids[2];
|
||||
|
||||
streaming_url = service_->GetStreamingUrlFromSongId(
|
||||
last_song_id_, last_artist_id_, &last_server_id_, &last_stream_key_,
|
||||
&length_nanosec);
|
||||
qLog(Debug) << "Grooveshark Streaming URL: " << streaming_url;
|
||||
|
||||
timer_mark_stream_key_->start();
|
||||
}
|
||||
|
||||
return LoadResult(url, LoadResult::TrackAvailable, streaming_url,
|
||||
length_nanosec);
|
||||
}
|
||||
|
||||
void GroovesharkUrlHandler::TrackAboutToEnd() {
|
||||
if (timer_mark_stream_key_->isActive()) {
|
||||
timer_mark_stream_key_->stop();
|
||||
return;
|
||||
}
|
||||
service_->MarkSongComplete(last_song_id_, last_stream_key_, last_server_id_);
|
||||
}
|
||||
|
||||
void GroovesharkUrlHandler::TrackSkipped() { timer_mark_stream_key_->stop(); }
|
||||
|
||||
void GroovesharkUrlHandler::MarkStreamKeyOver30Secs() {
|
||||
service_->MarkStreamKeyOver30Secs(last_stream_key_, last_server_id_);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2011, Arnaud Bienner <arnaud.bienner@gmail.com>
|
||||
Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_GROOVESHARK_GROOVESHARKURLHANDLER_H_
|
||||
#define INTERNET_GROOVESHARK_GROOVESHARKURLHANDLER_H_
|
||||
|
||||
#include "core/urlhandler.h"
|
||||
|
||||
class GroovesharkService;
|
||||
class QTimer;
|
||||
|
||||
class GroovesharkUrlHandler : public UrlHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GroovesharkUrlHandler(GroovesharkService* service, QObject* parent);
|
||||
|
||||
QString scheme() const { return "grooveshark"; }
|
||||
QIcon icon() const { return QIcon(":providers/grooveshark.png"); }
|
||||
LoadResult StartLoading(const QUrl& url);
|
||||
void TrackAboutToEnd();
|
||||
void TrackSkipped();
|
||||
|
||||
private slots:
|
||||
void MarkStreamKeyOver30Secs();
|
||||
|
||||
private:
|
||||
GroovesharkService* service_;
|
||||
QTimer* timer_mark_stream_key_;
|
||||
QString last_artist_id_;
|
||||
QString last_album_id_;
|
||||
QString last_song_id_;
|
||||
QString last_server_id_;
|
||||
QString last_stream_key_;
|
||||
};
|
||||
|
||||
#endif // INTERNET_GROOVESHARK_GROOVESHARKURLHANDLER_H_
|
@ -345,7 +345,8 @@ void LastFMService::NowPlaying(const Song& song) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// TODO(John Maguire): validity was removed from liblastfm1 but might reappear, it should have
|
||||
// TODO(John Maguire): validity was removed from liblastfm1 but might reappear,
|
||||
// it should have
|
||||
// no impact as we get a different error when actually trying to scrobble.
|
||||
#endif
|
||||
|
||||
@ -382,6 +383,8 @@ void LastFMService::Love() {
|
||||
}
|
||||
|
||||
void LastFMService::Ban() {
|
||||
if (!IsAuthenticated()) ShowConfig();
|
||||
|
||||
lastfm::MutableTrack mtrack(last_track_);
|
||||
mtrack.ban();
|
||||
last_track_ = mtrack;
|
||||
|
@ -264,10 +264,3 @@ SomaFMService::SomaFMService(Application* app, InternetModel* parent)
|
||||
: SomaFMServiceBase(
|
||||
app, parent, "SomaFM", QUrl("http://somafm.com/channels.xml"),
|
||||
QUrl("http://somafm.com"), QUrl(), QIcon(":providers/somafm.png")) {}
|
||||
|
||||
RadioGFMService::RadioGFMService(Application* app, InternetModel* parent)
|
||||
: SomaFMServiceBase(app, parent, "Radio GFM",
|
||||
QUrl("http://streams.radio-gfm.net/channels.xml"),
|
||||
QUrl("http://www.radio-gfm.net"),
|
||||
QUrl("http://www.radio-gfm.net/spenden"),
|
||||
QIcon(":providers/radiogfm.png")) {}
|
||||
|
@ -109,11 +109,6 @@ class SomaFMService : public SomaFMServiceBase {
|
||||
SomaFMService(Application* app, InternetModel* parent);
|
||||
};
|
||||
|
||||
class RadioGFMService : public SomaFMServiceBase {
|
||||
public:
|
||||
RadioGFMService(Application* app, InternetModel* parent);
|
||||
};
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const SomaFMService::Stream& stream);
|
||||
QDataStream& operator>>(QDataStream& in, SomaFMService::Stream& stream);
|
||||
Q_DECLARE_METATYPE(SomaFMService::Stream)
|
||||
|
@ -21,25 +21,30 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "spotifyservice.h"
|
||||
#include "core/arraysize.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/network.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QIODevice>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QProgressDialog>
|
||||
|
||||
#ifdef HAVE_QCA
|
||||
#include <QtCrypto>
|
||||
#endif // HAVE_QCA
|
||||
#include <QSslKey>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
const char* SpotifyBlobDownloader::kSignatureSuffix = ".sha1";
|
||||
#ifdef HAVE_CRYPTOPP
|
||||
#include <cryptopp/pkcspad.h>
|
||||
#include <cryptopp/rsa.h>
|
||||
#endif // HAVE_CRYPTOPP
|
||||
|
||||
const char* SpotifyBlobDownloader::kSignatureSuffix = ".sha512";
|
||||
|
||||
SpotifyBlobDownloader::SpotifyBlobDownloader(const QString& version,
|
||||
const QString& path,
|
||||
@ -125,32 +130,11 @@ void SpotifyBlobDownloader::ReplyFinished() {
|
||||
file_data[filename] = reply->readAll();
|
||||
}
|
||||
|
||||
#ifdef HAVE_QCA
|
||||
// Load the public key
|
||||
QCA::ConvertResult conversion_result;
|
||||
QCA::PublicKey key = QCA::PublicKey::fromPEMFile(
|
||||
":/clementine-spotify-public.pem", &conversion_result);
|
||||
if (QCA::ConvertGood != conversion_result) {
|
||||
ShowError("Failed to load Spotify public key");
|
||||
if (!CheckSignature(file_data, signature_filenames)) {
|
||||
qLog(Warning) << "Signature checks failed";
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify signatures
|
||||
for (const QString& signature_filename : signature_filenames) {
|
||||
QString actual_filename = signature_filename;
|
||||
actual_filename.remove(kSignatureSuffix);
|
||||
|
||||
qLog(Debug) << "Verifying" << actual_filename << "against"
|
||||
<< signature_filename;
|
||||
|
||||
if (!key.verifyMessage(file_data[actual_filename],
|
||||
file_data[signature_filename], QCA::EMSA3_SHA1)) {
|
||||
ShowError("Invalid signature: " + actual_filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_QCA
|
||||
|
||||
// Make the destination directory and write the files into it
|
||||
QDir().mkpath(path_);
|
||||
|
||||
@ -195,6 +179,60 @@ void SpotifyBlobDownloader::ReplyFinished() {
|
||||
EmitFinished();
|
||||
}
|
||||
|
||||
bool SpotifyBlobDownloader::CheckSignature(
|
||||
const QMap<QString, QByteArray>& file_data,
|
||||
const QStringList& signature_filenames) {
|
||||
#ifdef HAVE_CRYPTOPP
|
||||
QFile public_key_file(":/clementine-spotify-public.pem");
|
||||
public_key_file.open(QIODevice::ReadOnly);
|
||||
const QByteArray public_key_data = ConvertPEMToDER(public_key_file.readAll());
|
||||
|
||||
try {
|
||||
CryptoPP::ByteQueue bytes;
|
||||
bytes.Put(reinterpret_cast<const byte*>(public_key_data.constData()),
|
||||
public_key_data.size());
|
||||
bytes.MessageEnd();
|
||||
|
||||
CryptoPP::RSA::PublicKey public_key;
|
||||
public_key.Load(bytes);
|
||||
|
||||
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA512>::Verifier verifier(
|
||||
public_key);
|
||||
|
||||
for (const QString& signature_filename : signature_filenames) {
|
||||
QString actual_filename = signature_filename;
|
||||
actual_filename.remove(kSignatureSuffix);
|
||||
|
||||
const bool result = verifier.VerifyMessage(
|
||||
reinterpret_cast<const byte*>(file_data[actual_filename].constData()),
|
||||
file_data[actual_filename].size(),
|
||||
reinterpret_cast<const byte*>(
|
||||
file_data[signature_filename].constData()),
|
||||
file_data[signature_filename].size());
|
||||
qLog(Debug) << "Verifying" << actual_filename << "against"
|
||||
<< signature_filename << result;
|
||||
if (!result) {
|
||||
ShowError("Invalid signature: " + actual_filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (std::exception e) {
|
||||
// This should only happen if we fail to parse our own key.
|
||||
qLog(Debug) << "Verifying spotify blob signature failed:" << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HAVE_CRYPTOPP
|
||||
}
|
||||
|
||||
QByteArray SpotifyBlobDownloader::ConvertPEMToDER(const QByteArray& pem) {
|
||||
QSslKey key(pem, QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
|
||||
Q_ASSERT(!key.isNull());
|
||||
return key.toDer();
|
||||
}
|
||||
|
||||
void SpotifyBlobDownloader::ReplyProgress() {
|
||||
int progress = 0;
|
||||
int total = 0;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef INTERNET_SPOTIFY_SPOTIFYBLOBDOWNLOADER_H_
|
||||
#define INTERNET_SPOTIFY_SPOTIFYBLOBDOWNLOADER_H_
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
@ -40,7 +41,7 @@ class SpotifyBlobDownloader : public QObject {
|
||||
|
||||
void Start();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void Finished();
|
||||
|
||||
private slots:
|
||||
@ -52,6 +53,10 @@ class SpotifyBlobDownloader : public QObject {
|
||||
void ShowError(const QString& message);
|
||||
void EmitFinished();
|
||||
|
||||
bool CheckSignature(const QMap<QString, QByteArray>& file_data,
|
||||
const QStringList& signature_filenames);
|
||||
static QByteArray ConvertPEMToDER(const QByteArray& pem);
|
||||
|
||||
private:
|
||||
QString version_;
|
||||
QString path_;
|
||||
|
@ -668,12 +668,14 @@ void SpotifyService::EnsureMenuCreated() {
|
||||
playlist_context_menu_->addAction(GetNewShowConfigAction());
|
||||
|
||||
song_context_menu_ = new QMenu;
|
||||
song_context_menu_->addActions(GetPlaylistActions());
|
||||
song_context_menu_->addSeparator();
|
||||
remove_from_playlist_ = song_context_menu_->addAction(
|
||||
IconLoader::Load("list-remove"), tr("Remove from playlist"), this,
|
||||
SLOT(RemoveCurrentFromPlaylist()));
|
||||
song_context_menu_->addAction(tr("Get a URL to share this Spotify song"),
|
||||
this, SLOT(GetCurrentSongUrlToShare()));
|
||||
|
||||
song_context_menu_->addSeparator();
|
||||
song_context_menu_->addAction(GetNewShowConfigAction());
|
||||
}
|
||||
|
||||
|
@ -264,6 +264,7 @@ void SubsonicService::ReloadDatabaseFinished() {
|
||||
void SubsonicService::OnLoginStateChanged(
|
||||
SubsonicService::LoginState newstate) {
|
||||
// TODO(Alan Briolat): library refresh logic?
|
||||
if (newstate != LoginState_Loggedin) library_backend_->DeleteAll();
|
||||
}
|
||||
|
||||
void SubsonicService::OnPingFinished(QNetworkReply* reply) {
|
||||
@ -392,9 +393,16 @@ void SubsonicLibraryScanner::OnGetAlbumListFinished(QNetworkReply* reply,
|
||||
int offset) {
|
||||
reply->deleteLater();
|
||||
|
||||
bool skip_read_albums = false;
|
||||
|
||||
QXmlStreamReader reader(reply);
|
||||
reader.readNextStartElement();
|
||||
Q_ASSERT(reader.name() == "subsonic-response");
|
||||
|
||||
if (reader.name() != "subsonic-response") {
|
||||
ParsingError("Not a subsonic-response. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader.attributes().value("status") != "ok") {
|
||||
reader.readNextStartElement();
|
||||
int error = reader.attributes().value("code").toString().toInt();
|
||||
@ -404,20 +412,32 @@ void SubsonicLibraryScanner::OnGetAlbumListFinished(QNetworkReply* reply,
|
||||
// whereas Subsonic returns empty albumList2 tag
|
||||
switch (error) {
|
||||
case SubsonicService::ApiError_NotFound:
|
||||
skip_read_albums = true;
|
||||
break;
|
||||
default:
|
||||
ParsingError("Response status not ok. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int albums_added = 0;
|
||||
reader.readNextStartElement();
|
||||
Q_ASSERT(reader.name() == "albumList2");
|
||||
while (reader.readNextStartElement()) {
|
||||
Q_ASSERT(reader.name() == "album");
|
||||
album_queue_ << reader.attributes().value("id").toString();
|
||||
albums_added++;
|
||||
reader.skipCurrentElement();
|
||||
if (!skip_read_albums) {
|
||||
reader.readNextStartElement();
|
||||
if (reader.name() != "albumList2") {
|
||||
ParsingError("albumList2 tag expected. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() != "album") {
|
||||
ParsingError("album tag expected. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
album_queue_ << reader.attributes().value("id").toString();
|
||||
albums_added++;
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
if (albums_added > 0) {
|
||||
@ -443,7 +463,12 @@ void SubsonicLibraryScanner::OnGetAlbumFinished(QNetworkReply* reply) {
|
||||
|
||||
QXmlStreamReader reader(reply);
|
||||
reader.readNextStartElement();
|
||||
Q_ASSERT(reader.name() == "subsonic-response");
|
||||
|
||||
if (reader.name() != "subsonic-response") {
|
||||
ParsingError("Not a subsonic-response. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader.attributes().value("status") != "ok") {
|
||||
// TODO(Alan Briolat): error handling
|
||||
return;
|
||||
@ -451,12 +476,20 @@ void SubsonicLibraryScanner::OnGetAlbumFinished(QNetworkReply* reply) {
|
||||
|
||||
// Read album information
|
||||
reader.readNextStartElement();
|
||||
Q_ASSERT(reader.name() == "album");
|
||||
if (reader.name() != "album") {
|
||||
ParsingError("album tag expected. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
QString album_artist = reader.attributes().value("artist").toString();
|
||||
|
||||
// Read song information
|
||||
while (reader.readNextStartElement()) {
|
||||
Q_ASSERT(reader.name() == "song");
|
||||
if (reader.name() != "song") {
|
||||
ParsingError("song tag expected. Aborting scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
Song song;
|
||||
QString id = reader.attributes().value("id").toString();
|
||||
song.set_title(reader.attributes().value("title").toString());
|
||||
@ -512,3 +545,9 @@ void SubsonicLibraryScanner::GetAlbum(const QString& id) {
|
||||
SLOT(OnGetAlbumFinished(QNetworkReply*)), reply);
|
||||
pending_requests_.insert(reply);
|
||||
}
|
||||
|
||||
void SubsonicLibraryScanner::ParsingError(const QString& message) {
|
||||
qLog(Warning) << "Subsonic parsing error: " << message;
|
||||
scanning_ = false;
|
||||
emit ScanFinished();
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class SubsonicService : public InternetService {
|
||||
|
||||
static const int kMaxRedirects;
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void LoginStateChanged(SubsonicService::LoginState newstate);
|
||||
|
||||
private:
|
||||
@ -167,7 +167,8 @@ class SubsonicLibraryScanner : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SubsonicLibraryScanner(SubsonicService* service, QObject* parent = nullptr);
|
||||
explicit SubsonicLibraryScanner(SubsonicService* service,
|
||||
QObject* parent = nullptr);
|
||||
~SubsonicLibraryScanner();
|
||||
|
||||
void Scan();
|
||||
@ -176,7 +177,7 @@ class SubsonicLibraryScanner : public QObject {
|
||||
static const int kAlbumChunkSize;
|
||||
static const int kConcurrentRequests;
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void ScanFinished();
|
||||
|
||||
private slots:
|
||||
@ -188,6 +189,7 @@ class SubsonicLibraryScanner : public QObject {
|
||||
private:
|
||||
void GetAlbumList(int offset);
|
||||
void GetAlbum(const QString& id);
|
||||
void ParsingError(const QString& message);
|
||||
|
||||
SubsonicService* service_;
|
||||
bool scanning_;
|
||||
|
@ -26,7 +26,7 @@ SubsonicUrlHandler::SubsonicUrlHandler(SubsonicService* service,
|
||||
|
||||
UrlHandler::LoadResult SubsonicUrlHandler::StartLoading(const QUrl& url) {
|
||||
if (service_->login_state() != SubsonicService::LoginState_Loggedin)
|
||||
return LoadResult();
|
||||
return LoadResult(url);
|
||||
|
||||
QUrl newurl = service_->BuildRequestUrl("stream");
|
||||
newurl.addQueryItem("id", url.host());
|
||||
|
@ -63,7 +63,8 @@ const char* VkService::kUrlScheme = "vk";
|
||||
const char* VkService::kDefCacheFilename = "%artist - %title";
|
||||
const int VkService::kMaxVkSongList = 6000;
|
||||
const int VkService::kMaxVkWallPostList = 100;
|
||||
const int VkService::kCustomSongCount = 50;
|
||||
const int VkService::kMaxVkSongCount = 300;
|
||||
const int VkService::kSearchDelayMsec = 400;
|
||||
|
||||
QString VkService::DefaultCacheDir() {
|
||||
return QDir::toNativeSeparators(
|
||||
@ -220,7 +221,8 @@ VkService::VkService(Application* app, InternetModel* parent)
|
||||
url_handler_(new VkUrlHandler(this, this)),
|
||||
audio_provider_(new Vreen::AudioProvider(client_.get())),
|
||||
cache_(new VkMusicCache(app_, this)),
|
||||
last_search_id_(0) {
|
||||
last_search_id_(0),
|
||||
search_delay_(new QTimer(this)) {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingGroup);
|
||||
|
||||
@ -245,7 +247,14 @@ VkService::VkService(Application* app, InternetModel* parent)
|
||||
VkSearchProvider* search_provider = new VkSearchProvider(app_, this);
|
||||
search_provider->Init(this);
|
||||
app_->global_search()->AddProvider(search_provider);
|
||||
|
||||
search_delay_->setInterval(kSearchDelayMsec);
|
||||
search_delay_->setSingleShot(true);
|
||||
connect(search_delay_, SIGNAL(timeout()), SLOT(DoLocalSearch()));
|
||||
|
||||
connect(search_box_, SIGNAL(TextChanged(QString)), SLOT(FindSongs(QString)));
|
||||
connect(this, SIGNAL(SongSearchResult(SearchID, SongList)),
|
||||
SLOT(SearchResultLoaded(SearchID, SongList)));
|
||||
|
||||
app_->player()->RegisterUrlHandler(url_handler_);
|
||||
}
|
||||
@ -662,7 +671,7 @@ void VkService::UpdateRecommendations() {
|
||||
CreateAndAppendRow(recommendations_item_, Type_Loading);
|
||||
|
||||
auto my_audio =
|
||||
audio_provider_->getRecommendationsForUser(0, kCustomSongCount, 0);
|
||||
audio_provider_->getRecommendationsForUser(0, kMaxVkSongCount, 0);
|
||||
|
||||
NewClosure(my_audio, SIGNAL(resultReady(QVariant)), this,
|
||||
SLOT(RecommendationsLoaded(Vreen::AudioItemListReply*)), my_audio);
|
||||
@ -673,7 +682,7 @@ void VkService::MoreRecommendations() {
|
||||
CreateAndAppendRow(recommendations_item_, Type_Loading);
|
||||
|
||||
auto my_audio = audio_provider_->getRecommendationsForUser(
|
||||
0, kCustomSongCount, recommendations_item_->rowCount() - 1);
|
||||
0, kMaxVkSongCount, recommendations_item_->rowCount() - 1);
|
||||
|
||||
NewClosure(my_audio, SIGNAL(resultReady(QVariant)), this,
|
||||
SLOT(RecommendationsLoaded(Vreen::AudioItemListReply*)), my_audio);
|
||||
@ -818,8 +827,8 @@ QStandardItem* VkService::AppendAlbumList(QStandardItem* parent, bool myself) {
|
||||
|
||||
if (myself) {
|
||||
item = new QStandardItem(QIcon(":vk/discography.png"), tr("My Albums"));
|
||||
// TODO(Ivan Leontiev): Do this better. We have incomplete MusicOwner instance
|
||||
// for logged in user.
|
||||
// TODO(Ivan Leontiev): Do this better. We have incomplete MusicOwner
|
||||
// instance for logged in user.
|
||||
owner.setId(UserID());
|
||||
my_albums_item_ = item;
|
||||
} else {
|
||||
@ -876,8 +885,8 @@ QStandardItem* VkService::AppendMusic(QStandardItem* parent, bool myself) {
|
||||
|
||||
if (myself) {
|
||||
item = new QStandardItem(QIcon(":vk/my_music.png"), tr("My Music"));
|
||||
// TODO(Ivan Leontiev): Do this better. We have incomplete MusicOwner instance
|
||||
// for logged in user.
|
||||
// TODO(Ivan Leontiev): Do this better. We have incomplete MusicOwner
|
||||
// instance for logged in user.
|
||||
owner.setId(UserID());
|
||||
my_music_item_ = item;
|
||||
} else {
|
||||
@ -962,8 +971,8 @@ void VkService::FindThisArtist() {
|
||||
void VkService::AddToMyMusic() {
|
||||
SongId id = ExtractIds(selected_song_.url());
|
||||
auto reply = audio_provider_->addToLibrary(id.audio_id, id.owner_id);
|
||||
connect(reply, SIGNAL(resultReady(QVariant)), this,
|
||||
SLOT(UpdateMusic(my_music_item_)));
|
||||
NewClosure(reply, SIGNAL(resultReady(QVariant)), this,
|
||||
SLOT(UpdateMusic(QStandardItem*)), my_music_item_);
|
||||
}
|
||||
|
||||
void VkService::AddToMyMusicCurrent() {
|
||||
@ -977,8 +986,8 @@ void VkService::RemoveFromMyMusic() {
|
||||
SongId id = ExtractIds(selected_song_.url());
|
||||
if (id.owner_id == UserID()) {
|
||||
auto reply = audio_provider_->removeFromLibrary(id.audio_id, id.owner_id);
|
||||
connect(reply, SIGNAL(resultReady(QVariant)), this,
|
||||
SLOT(UpdateMusic(my_music_item_)));
|
||||
NewClosure(reply, SIGNAL(resultReady(QVariant)), this,
|
||||
SLOT(UpdateMusic(QStandardItem*)), my_music_item_);
|
||||
} else {
|
||||
qLog(Error) << "Tried to delete song that not owned by user (" << UserID()
|
||||
<< selected_song_.url();
|
||||
@ -1003,29 +1012,40 @@ void VkService::CopyShareUrl() {
|
||||
* Search
|
||||
*/
|
||||
|
||||
void VkService::DoLocalSearch() {
|
||||
ClearStandardItem(search_result_item_);
|
||||
CreateAndAppendRow(search_result_item_, Type_Loading);
|
||||
SearchID id(SearchID::LocalSearch);
|
||||
|
||||
last_search_id_ = id.id();
|
||||
SongSearch(id, last_query_);
|
||||
}
|
||||
|
||||
void VkService::FindSongs(const QString& query) {
|
||||
last_query_ = query;
|
||||
|
||||
if (query.isEmpty()) {
|
||||
search_delay_->stop();
|
||||
root_item_->removeRow(search_result_item_->row());
|
||||
search_result_item_ = NULL;
|
||||
last_search_id_ = 0;
|
||||
} else {
|
||||
last_query_ = query;
|
||||
if (!search_result_item_) {
|
||||
CreateAndAppendRow(root_item_, Type_Search);
|
||||
connect(this, SIGNAL(SongSearchResult(SearchID, SongList)),
|
||||
SLOT(SearchResultLoaded(SearchID, SongList)));
|
||||
}
|
||||
ClearStandardItem(search_result_item_);
|
||||
CreateAndAppendRow(search_result_item_, Type_Loading);
|
||||
SongSearch(SearchID(SearchID::LocalSearch), query);
|
||||
return;
|
||||
}
|
||||
|
||||
search_delay_->start();
|
||||
|
||||
if (!search_result_item_) {
|
||||
CreateAndAppendRow(root_item_, Type_Search);
|
||||
}
|
||||
}
|
||||
|
||||
void VkService::FindMore() {
|
||||
RemoveLastRow(search_result_item_, Type_More);
|
||||
|
||||
CreateAndAppendRow(search_result_item_, Type_Loading);
|
||||
SearchID id(SearchID::MoreLocalSearch);
|
||||
SongSearch(id, last_query_, kCustomSongCount,
|
||||
|
||||
last_search_id_ = id.id();
|
||||
SongSearch(id, last_query_, kMaxVkSongCount,
|
||||
search_result_item_->rowCount() - 1);
|
||||
}
|
||||
|
||||
@ -1034,7 +1054,7 @@ void VkService::SearchResultLoaded(const SearchID& id, const SongList& songs) {
|
||||
return; // Result received when search is already over.
|
||||
}
|
||||
|
||||
if (id.id() >= last_search_id_) {
|
||||
if (id.id() == last_search_id_) {
|
||||
if (id.type() == SearchID::LocalSearch) {
|
||||
ClearStandardItem(search_result_item_);
|
||||
} else if (id.type() == SearchID::MoreLocalSearch) {
|
||||
@ -1043,8 +1063,6 @@ void VkService::SearchResultLoaded(const SearchID& id, const SongList& songs) {
|
||||
return; // Others request types ignored.
|
||||
}
|
||||
|
||||
last_search_id_ = id.id();
|
||||
|
||||
if (!songs.isEmpty()) {
|
||||
AppendSongs(search_result_item_, songs);
|
||||
CreateAndAppendRow(search_result_item_, Type_More);
|
||||
@ -1173,19 +1191,23 @@ UrlHandler::LoadResult VkService::GetSongResult(const QUrl& url) {
|
||||
if (media_url.isValid()) {
|
||||
Song song = FromAudioItem(audio_item);
|
||||
SongStarting(song);
|
||||
cache_->AddToCache(url, media_url);
|
||||
|
||||
if (cachingEnabled_) {
|
||||
cache_->AddToCache(url, media_url);
|
||||
}
|
||||
|
||||
return UrlHandler::LoadResult(url, UrlHandler::LoadResult::TrackAvailable,
|
||||
media_url, song.length_nanosec());
|
||||
}
|
||||
|
||||
return UrlHandler::LoadResult();
|
||||
return UrlHandler::LoadResult(url);
|
||||
}
|
||||
|
||||
UrlHandler::LoadResult VkService::GetGroupNextSongUrl(const QUrl& url) {
|
||||
QStringList tokens = url.path().split('/');
|
||||
if (tokens.count() < 3) {
|
||||
qLog(Error) << "Wrong url" << url;
|
||||
return UrlHandler::LoadResult();
|
||||
return UrlHandler::LoadResult(url);
|
||||
}
|
||||
|
||||
int gid = tokens[1].toInt();
|
||||
@ -1214,7 +1236,7 @@ UrlHandler::LoadResult VkService::GetGroupNextSongUrl(const QUrl& url) {
|
||||
}
|
||||
|
||||
qLog(Info) << "Unresolved group url" << url;
|
||||
return UrlHandler::LoadResult();
|
||||
return UrlHandler::LoadResult(url);
|
||||
}
|
||||
|
||||
/***
|
||||
@ -1444,7 +1466,7 @@ void VkService::AppendSongs(QStandardItem* parent, const SongList& songs) {
|
||||
void VkService::ReloadSettings() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingGroup);
|
||||
maxGlobalSearch_ = s.value("max_global_search", kCustomSongCount).toInt();
|
||||
maxGlobalSearch_ = s.value("max_global_search", kMaxVkSongCount).toInt();
|
||||
cachingEnabled_ = s.value("cache_enabled", false).toBool();
|
||||
cacheDir_ = s.value("cache_dir", DefaultCacheDir()).toString();
|
||||
cacheFilename_ = s.value("cache_filename", kDefCacheFilename).toString();
|
||||
|
@ -116,7 +116,8 @@ class VkService : public InternetService {
|
||||
static QString DefaultCacheDir();
|
||||
static const int kMaxVkSongList;
|
||||
static const int kMaxVkWallPostList;
|
||||
static const int kCustomSongCount;
|
||||
static const int kMaxVkSongCount;
|
||||
static const int kSearchDelayMsec;
|
||||
|
||||
enum ItemType {
|
||||
Type_Loading = InternetModel::TypeCount,
|
||||
@ -167,8 +168,8 @@ class VkService : public InternetService {
|
||||
// Return random song result from group playlist.
|
||||
UrlHandler::LoadResult GetGroupNextSongUrl(const QUrl& url);
|
||||
|
||||
void SongSearch(SearchID id, const QString& query, int count = 50,
|
||||
int offset = 0);
|
||||
void SongSearch(SearchID id, const QString& query,
|
||||
int count = kMaxVkSongCount, int offset = 0);
|
||||
void GroupSearch(SearchID id, const QString& query);
|
||||
|
||||
/* Settings */
|
||||
@ -181,7 +182,7 @@ class VkService : public InternetService {
|
||||
QString cacheFilename() const { return cacheFilename_; }
|
||||
bool isLoveAddToMyMusic() const { return love_is_add_to_mymusic_; }
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void NameUpdated(const QString& name);
|
||||
void ConnectionStateChanged(Vreen::Client::State state);
|
||||
void LoginSuccess(bool success);
|
||||
@ -195,6 +196,7 @@ class VkService : public InternetService {
|
||||
void UpdateRoot();
|
||||
void ShowConfig();
|
||||
void FindUserOrGroup(const QString& q);
|
||||
void DoLocalSearch();
|
||||
|
||||
private slots:
|
||||
/* Interface */
|
||||
@ -304,6 +306,7 @@ class VkService : public InternetService {
|
||||
// Keeping when more recent results recived.
|
||||
// Using for prevent loading tardy result instead.
|
||||
uint last_search_id_;
|
||||
QTimer* search_delay_;
|
||||
QString last_query_;
|
||||
Song selected_song_; // Store for context menu actions.
|
||||
Song current_song_; // Store for actions with now playing song.
|
||||
|
@ -30,7 +30,7 @@ VkUrlHandler::VkUrlHandler(VkService* service, QObject* parent)
|
||||
|
||||
UrlHandler::LoadResult VkUrlHandler::StartLoading(const QUrl& url) {
|
||||
QStringList args = url.path().split("/");
|
||||
LoadResult result;
|
||||
LoadResult result(url);
|
||||
|
||||
if (args.size() < 2) {
|
||||
qLog(Error)
|
||||
@ -58,6 +58,6 @@ UrlHandler::LoadResult VkUrlHandler::LoadNext(const QUrl& url) {
|
||||
if (url.host() == "group") {
|
||||
return StartLoading(url);
|
||||
} else {
|
||||
return LoadResult();
|
||||
return LoadResult(url);
|
||||
}
|
||||
}
|
||||
|
@ -76,11 +76,13 @@ GroupByDialog::GroupByDialog(QWidget* parent)
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_FileType, 5));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Genre, 6));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Year, 7));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_YearAlbum, 8));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Bitrate, 9));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Disc, 10));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Performer, 11));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Grouping, 12));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_OriginalYear, 8));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_YearAlbum, 9));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_OriginalYearAlbum, 10));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Bitrate, 11));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Disc, 12));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Performer, 13));
|
||||
p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Grouping, 14));
|
||||
|
||||
connect(ui_->button_box->button(QDialogButtonBox::Reset), SIGNAL(clicked()),
|
||||
SLOT(Reset()));
|
||||
|
@ -83,11 +83,21 @@
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
@ -98,6 +108,16 @@
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
@ -149,11 +169,21 @@
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
@ -164,6 +194,16 @@
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
@ -215,21 +255,41 @@
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Original year - Album</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bitrate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -115,6 +115,7 @@ Library::Library(Application* app, QObject* parent)
|
||||
|
||||
// full rescan revisions
|
||||
full_rescan_revisions_[26] = tr("CUE sheet support");
|
||||
full_rescan_revisions_[50] = tr("Original year tag support");
|
||||
|
||||
ReloadSettings();
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user