Start of some spotify support - builds a binary blob linked against libspotify that Clementine starts as a separate process.
This commit is contained in:
parent
4d19917b05
commit
e152e3a3e3
@ -46,6 +46,7 @@ find_package(OpenGL REQUIRED)
|
|||||||
find_package(Boost REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
find_package(Gettext REQUIRED)
|
find_package(Gettext REQUIRED)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
find_package(Protobuf)
|
||||||
|
|
||||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
||||||
pkg_check_modules(GSTREAMER gstreamer-0.10)
|
pkg_check_modules(GSTREAMER gstreamer-0.10)
|
||||||
@ -61,6 +62,7 @@ pkg_check_modules(USBMUXD libusbmuxd)
|
|||||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||||
pkg_check_modules(INDICATEQT indicate-qt)
|
pkg_check_modules(INDICATEQT indicate-qt)
|
||||||
pkg_check_modules(ARCHIVE libarchive)
|
pkg_check_modules(ARCHIVE libarchive)
|
||||||
|
pkg_check_modules(SPOTIFY libspotify)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
@ -103,11 +105,13 @@ find_path(LASTFM_INCLUDE_DIRS lastfm/ws.h)
|
|||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
find_library(GROWL Growl)
|
find_library(GROWL Growl)
|
||||||
|
|
||||||
option(ENABLE_SPARKLE "Sparkle updating" ON)
|
option(ENABLE_SPARKLE "Sparkle updating" ON)
|
||||||
find_library(SPARKLE Sparkle)
|
find_library(SPARKLE Sparkle)
|
||||||
if (ENABLE_SPARKLE AND SPARKLE)
|
if (ENABLE_SPARKLE AND SPARKLE)
|
||||||
set(HAVE_SPARKLE ON)
|
set(HAVE_SPARKLE ON)
|
||||||
endif (ENABLE_SPARKLE AND SPARKLE)
|
endif (ENABLE_SPARKLE AND SPARKLE)
|
||||||
|
|
||||||
# Uses Darwin kernel version.
|
# Uses Darwin kernel version.
|
||||||
# 9.8.0 -> 10.5/Leopard
|
# 9.8.0 -> 10.5/Leopard
|
||||||
# 10.4.0 -> 10.6/Snow Leopard
|
# 10.4.0 -> 10.6/Snow Leopard
|
||||||
@ -177,6 +181,7 @@ option(ENABLE_SCRIPTING_ARCHIVES "Enable support for loading scripts from archiv
|
|||||||
option(ENABLE_SCRIPTING_PYTHON "Enable python scripting" ON)
|
option(ENABLE_SCRIPTING_PYTHON "Enable python scripting" ON)
|
||||||
option(ENABLE_REMOTE "Enable support for using remote controls with Clementine" OFF)
|
option(ENABLE_REMOTE "Enable support for using remote controls with Clementine" OFF)
|
||||||
option(ENABLE_BREAKPAD "Enable crash reporting" OFF)
|
option(ENABLE_BREAKPAD "Enable crash reporting" OFF)
|
||||||
|
option(ENABLE_SPOTIFY "Enable spotify support" OFF)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
|
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
|
||||||
@ -222,6 +227,11 @@ if(ENABLE_BREAKPAD)
|
|||||||
set(HAVE_BREAKPAD ON)
|
set(HAVE_BREAKPAD ON)
|
||||||
endif(ENABLE_BREAKPAD)
|
endif(ENABLE_BREAKPAD)
|
||||||
|
|
||||||
|
if(ENABLE_SPOTIFY AND SPOTIFY_FOUND AND PROTOBUF_FOUND)
|
||||||
|
set(HAVE_SPOTIFY ON)
|
||||||
|
endif(ENABLE_SPOTIFY AND SPOTIFY_FOUND AND PROTOBUF_FOUND)
|
||||||
|
|
||||||
|
|
||||||
if(ENABLE_VISUALISATIONS)
|
if(ENABLE_VISUALISATIONS)
|
||||||
# When/if upstream accepts our patches then these options can be used to link
|
# When/if upstream accepts our patches then these options can be used to link
|
||||||
# to system installed projectM instead.
|
# to system installed projectM instead.
|
||||||
@ -357,6 +367,10 @@ if(HAVE_BREAKPAD)
|
|||||||
add_subdirectory(3rdparty/google-breakpad)
|
add_subdirectory(3rdparty/google-breakpad)
|
||||||
endif(HAVE_BREAKPAD)
|
endif(HAVE_BREAKPAD)
|
||||||
|
|
||||||
|
if(HAVE_SPOTIFY)
|
||||||
|
add_subdirectory(spotifyblob)
|
||||||
|
endif(HAVE_SPOTIFY)
|
||||||
|
|
||||||
# Uninstall support
|
# Uninstall support
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||||
@ -382,4 +396,5 @@ summary_add("Scripting support: loading archives" HAVE_LIBARCHIVE)
|
|||||||
summary_add("Scripting support: Python" HAVE_SCRIPTING_PYTHON)
|
summary_add("Scripting support: Python" HAVE_SCRIPTING_PYTHON)
|
||||||
summary_add("(Mac OS X) Sparkle integration" HAVE_SPARKLE)
|
summary_add("(Mac OS X) Sparkle integration" HAVE_SPARKLE)
|
||||||
summary_add("(unstable) Remote control support" HAVE_REMOTE)
|
summary_add("(unstable) Remote control support" HAVE_REMOTE)
|
||||||
|
summary_add("(unstable) Spotify support" HAVE_SPOTIFY)
|
||||||
summary_show()
|
summary_show()
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
<file>providers/magnatune.png</file>
|
<file>providers/magnatune.png</file>
|
||||||
<file>schema/schema-8.sql</file>
|
<file>schema/schema-8.sql</file>
|
||||||
<file>schema/schema-9.sql</file>
|
<file>schema/schema-9.sql</file>
|
||||||
|
<file>icons/svg/spotify.svg</file>
|
||||||
<file>icons/22x22/application-exit.png</file>
|
<file>icons/22x22/application-exit.png</file>
|
||||||
<file>icons/22x22/applications-internet.png</file>
|
<file>icons/22x22/applications-internet.png</file>
|
||||||
<file>icons/22x22/configure.png</file>
|
<file>icons/22x22/configure.png</file>
|
||||||
|
285
data/icons/svg/spotify.svg
Normal file
285
data/icons/svg/spotify.svg
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
id="svg2"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.46"
|
||||||
|
version="1.0"
|
||||||
|
sodipodi:docname="spotify.svg"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient7729">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#467700;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop7731" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#518900;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop7733" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient7721">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#1a4300;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop7723" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#215300;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop7725" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient7713">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#193d00;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop7715" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#1f4b00;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop7717" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient6355">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#23371c;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop6357" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#004100;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop6359" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient6347">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#abd033;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop6349" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#9ec02d;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop6351" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5519">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#88ae04;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop5521" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#73a400;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop5523" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 526.18109 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||||
|
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||||
|
id="perspective10" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective5505"
|
||||||
|
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||||
|
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 526.18109 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient5519"
|
||||||
|
id="linearGradient6853"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="119.5"
|
||||||
|
y1="91.362183"
|
||||||
|
x2="102.5"
|
||||||
|
y2="128.36218" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient6355"
|
||||||
|
id="radialGradient6855"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.1318093,0.197699,-0.1703116,0.9750189,-125.44033,-100.50883)"
|
||||||
|
cx="147.76869"
|
||||||
|
cy="120.16964"
|
||||||
|
fx="147.76869"
|
||||||
|
fy="120.16964"
|
||||||
|
r="14.451495" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient6347"
|
||||||
|
id="linearGradient6857"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(-125.45979,-77.491488)"
|
||||||
|
x1="149.09062"
|
||||||
|
y1="85.104187"
|
||||||
|
x2="139.1649"
|
||||||
|
y2="106.61218" />
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter7655"
|
||||||
|
x="-1.0712544"
|
||||||
|
width="3.1425087"
|
||||||
|
y="-1.0712544"
|
||||||
|
height="3.1425087">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="20.086019"
|
||||||
|
id="feGaussianBlur7657" />
|
||||||
|
</filter>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient7713"
|
||||||
|
id="linearGradient7719"
|
||||||
|
x1="38.714096"
|
||||||
|
y1="17.417631"
|
||||||
|
x2="31.1127"
|
||||||
|
y2="15.165503"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.999924,1.232938e-2,-1.232938e-2,0.999924,0.2587749,-0.2864042)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient7721"
|
||||||
|
id="linearGradient7727"
|
||||||
|
x1="35.708893"
|
||||||
|
y1="25.90625"
|
||||||
|
x2="27.400389"
|
||||||
|
y2="22.312458"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.965063,1.1899533e-2,-1.232938e-2,0.999924,1.1112913,-0.2758924)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient7729"
|
||||||
|
id="linearGradient7735"
|
||||||
|
x1="35.607697"
|
||||||
|
y1="31.414557"
|
||||||
|
x2="23.13369"
|
||||||
|
y2="26.433243"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.8596429,1.059967e-2,-1.232938e-2,0.999924,3.5039934,-0.2463897)" />
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter7785"
|
||||||
|
x="-0.089479765"
|
||||||
|
width="1.1789595"
|
||||||
|
y="-0.14834692"
|
||||||
|
height="1.2966938">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.5684595"
|
||||||
|
id="feGaussianBlur7787" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
gridtolerance="10000"
|
||||||
|
guidetolerance="10"
|
||||||
|
objecttolerance="10"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="5.6568542"
|
||||||
|
inkscape:cx="-0.10040691"
|
||||||
|
inkscape:cy="47.355194"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:snap-nodes="false"
|
||||||
|
inkscape:window-width="1440"
|
||||||
|
inkscape:window-height="853"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid5511"
|
||||||
|
visible="true"
|
||||||
|
enabled="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<g
|
||||||
|
id="g6847"
|
||||||
|
transform="translate(-0.29021,-0.7456946)">
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="opacity:0.64566926;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;filter:url(#filter7655);enable-background:accumulate"
|
||||||
|
id="path6363"
|
||||||
|
sodipodi:cx="112.5"
|
||||||
|
sodipodi:cy="109.86218"
|
||||||
|
sodipodi:rx="22.5"
|
||||||
|
sodipodi:ry="22.5"
|
||||||
|
d="M 135,109.86218 A 22.5,22.5 0 1 1 90,109.86218 A 22.5,22.5 0 1 1 135,109.86218 z"
|
||||||
|
transform="matrix(0.4666666,0,0,0.111111,-28.709782,33.038802)" />
|
||||||
|
<path
|
||||||
|
transform="translate(-88.70979,-85.616488)"
|
||||||
|
d="M 135,109.86218 A 22.5,22.5 0 1 1 90,109.86218 A 22.5,22.5 0 1 1 135,109.86218 z"
|
||||||
|
sodipodi:ry="22.5"
|
||||||
|
sodipodi:rx="22.5"
|
||||||
|
sodipodi:cy="109.86218"
|
||||||
|
sodipodi:cx="112.5"
|
||||||
|
id="path5513"
|
||||||
|
style="opacity:1;fill:url(#linearGradient6853);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="csccscc"
|
||||||
|
id="path5529"
|
||||||
|
d="M 7.6378996,39.420202 C 8.1682296,39.420202 11.29843,32.092852 23.23086,33.507062 C 35.16329,34.921272 36.54089,42.778952 36.54089,42.778952 C 34.49039,44.179572 32.3092,45.405922 29.55821,45.872552 C 29.55821,45.872552 26.55929,40.325532 21.1676,40.679082 C 15.77591,41.032632 13.91347,44.193172 13.91347,44.193172 C 11.14188,43.198092 8.9957096,41.593432 7.6378996,39.420202 z"
|
||||||
|
style="fill:url(#radialGradient6855);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccc"
|
||||||
|
id="path5531"
|
||||||
|
d="M 2.6652096,29.120692 C 35.315841,10.631207 30.66521,27.370692 44.66521,26.495692 C 44.14296,15.562843 35.20292,3.7456934 23.66521,3.7456934 C 6.6997296,4.3119254 1.9479196,20.58416 2.6652096,29.120692 z"
|
||||||
|
style="fill:url(#linearGradient6857);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7785)" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:url(#linearGradient7719);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="M 10.300245,11.619555 C 8.241027,12.012805 7.9425774,15.355405 10.765465,15.313073 C 21.977063,13.20149 31.048724,15.49889 39.670046,19.826041 C 40.808245,20.378971 42.647611,18.028703 41.02047,16.758511 C 31.316836,11.614062 19.960975,9.8780472 10.300245,11.619555 z"
|
||||||
|
id="path7661"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:url(#linearGradient7727);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="M 12.303532,19.144827 C 10.172718,19.381037 9.829391,22.969311 12.56196,22.64828 C 21.903655,21.50982 27.85363,22.62879 35.683323,25.808592 C 37.647192,26.873264 38.724998,23.482585 36.929338,22.604961 C 28.133565,18.889799 21.412131,18.726151 12.303532,19.144827 z"
|
||||||
|
id="path7666"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:url(#linearGradient7735);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
d="M 14.447653,25.703011 C 12.75416,26.30833 13.053805,29.148649 14.863112,29.052139 C 17.870832,28.722372 20.153948,28.706138 23.615532,29.097554 C 27.126432,29.494545 29.233258,30.435974 32.157356,31.453048 C 33.350406,31.961876 34.447173,29.764482 33.187538,28.528027 C 29.966426,27.274723 27.415802,26.174879 23.388506,25.719498 C 20.084015,25.348048 17.755584,25.371512 14.447653,25.703011 z"
|
||||||
|
id="path7668"
|
||||||
|
sodipodi:nodetypes="ccscccc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 12 KiB |
51
spotifyblob/CMakeLists.txt
Normal file
51
spotifyblob/CMakeLists.txt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
include_directories(${SPOTIFY_INCLUDE_DIRS})
|
||||||
|
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
link_directories(${SPOTIFY_LIBRARY_DIRS})
|
||||||
|
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ..)
|
||||||
|
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
main.cpp
|
||||||
|
spotifyblob.cpp
|
||||||
|
spotifyclient.cpp
|
||||||
|
|
||||||
|
../src/core/logging.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADERS
|
||||||
|
spotifyblob.h
|
||||||
|
spotifyclient.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(MESSAGES
|
||||||
|
messages.proto
|
||||||
|
)
|
||||||
|
|
||||||
|
qt4_wrap_cpp(MOC ${HEADERS})
|
||||||
|
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
||||||
|
|
||||||
|
|
||||||
|
add_library(clementine-spotifyblob-messages STATIC
|
||||||
|
${PROTO_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(clementine-spotifyblob-messages
|
||||||
|
${PROTOBUF_LIBRARY}
|
||||||
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(clementine-spotifyblob
|
||||||
|
${SOURCES}
|
||||||
|
${MOC}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(clementine-spotifyblob
|
||||||
|
${SPOTIFY_LIBRARIES}
|
||||||
|
${QT_LIBRARIES}
|
||||||
|
clementine-spotifyblob-messages
|
||||||
|
)
|
25
spotifyblob/main.cpp
Normal file
25
spotifyblob/main.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "spotifyblob.h"
|
||||||
|
#include "spotifyclient.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
QCoreApplication a(argc, argv);
|
||||||
|
|
||||||
|
logging::Init();
|
||||||
|
|
||||||
|
const QStringList arguments(a.arguments());
|
||||||
|
|
||||||
|
if (arguments.length() != 2) {
|
||||||
|
qFatal("Usage: %s port", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpotifyClient client;
|
||||||
|
SpotifyBlob blob(&client);
|
||||||
|
|
||||||
|
client.Init(arguments[1].toInt());
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
17
spotifyblob/messages.proto
Normal file
17
spotifyblob/messages.proto
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
message LoginRequest {
|
||||||
|
required string username = 1;
|
||||||
|
required string password = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoginResponse {
|
||||||
|
required bool success = 1;
|
||||||
|
required string error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestMessage {
|
||||||
|
optional LoginRequest login_request = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResponseMessage {
|
||||||
|
optional LoginResponse login_response = 1;
|
||||||
|
}
|
0
spotifyblob/spotify.cpp
Normal file
0
spotifyblob/spotify.cpp
Normal file
0
spotifyblob/spotify.h
Normal file
0
spotifyblob/spotify.h
Normal file
124
spotifyblob/spotifyblob.cpp
Normal file
124
spotifyblob/spotifyblob.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#include "spotifyblob.h"
|
||||||
|
#include "spotifyclient.h"
|
||||||
|
#include "spotifykey.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
SpotifyBlob::SpotifyBlob(SpotifyClient* client, QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
client_(client),
|
||||||
|
session_(NULL),
|
||||||
|
events_timer_(new QTimer(this)) {
|
||||||
|
memset(&spotify_callbacks_, 0, sizeof(spotify_callbacks_));
|
||||||
|
memset(&spotify_config_, 0, sizeof(spotify_config_));
|
||||||
|
|
||||||
|
spotify_callbacks_.logged_in = &LoggedIn;
|
||||||
|
spotify_callbacks_.notify_main_thread = &NotifyMainThread;
|
||||||
|
spotify_callbacks_.log_message = &LogMessage;
|
||||||
|
|
||||||
|
spotify_config_.api_version = SPOTIFY_API_VERSION; // From libspotify/api.h
|
||||||
|
spotify_config_.cache_location = strdup(QDir::tempPath().toLocal8Bit().constData());
|
||||||
|
spotify_config_.settings_location = strdup(QDir::tempPath().toLocal8Bit().constData());
|
||||||
|
spotify_config_.application_key = g_appkey;
|
||||||
|
spotify_config_.application_key_size = g_appkey_size;
|
||||||
|
spotify_config_.callbacks = &spotify_callbacks_;
|
||||||
|
spotify_config_.userdata = this;
|
||||||
|
spotify_config_.user_agent = "Clementine Player";
|
||||||
|
|
||||||
|
events_timer_->setSingleShot(true);
|
||||||
|
connect(events_timer_, SIGNAL(timeout()), SLOT(ProcessEvents()));
|
||||||
|
|
||||||
|
connect(client, SIGNAL(Login(QString,QString)), SLOT(Login(QString,QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SpotifyBlob::~SpotifyBlob() {
|
||||||
|
if (session_) {
|
||||||
|
sp_session_release(session_);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(const_cast<char*>(spotify_config_.cache_location));
|
||||||
|
free(const_cast<char*>(spotify_config_.settings_location));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::Login(const QString& username, const QString& password) {
|
||||||
|
sp_error error = sp_session_create(&spotify_config_, &session_);
|
||||||
|
if (error != SP_ERROR_OK) {
|
||||||
|
qWarning() << "Failed to create session" << sp_error_message(error);
|
||||||
|
client_->LoginCompleted(false, sp_error_message(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp_session_login(session_, username.toUtf8().constData(), password.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::LoggedIn(sp_session* session, sp_error error) {
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
SpotifyBlob* me = reinterpret_cast<SpotifyBlob*>(sp_session_userdata(session));
|
||||||
|
me->LoggedIn(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::LoggedIn(sp_error error) {
|
||||||
|
if (error != SP_ERROR_OK) {
|
||||||
|
qWarning() << "Failed to login" << sp_error_message(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
client_->LoginCompleted(error == SP_ERROR_OK, sp_error_message(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::NotifyMainThread(sp_session* session) {
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
SpotifyBlob* me = reinterpret_cast<SpotifyBlob*>(sp_session_userdata(session));
|
||||||
|
me->Notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by spotify from an internal thread to notify us that its events need processing.
|
||||||
|
void SpotifyBlob::Notify() {
|
||||||
|
metaObject()->invokeMethod(this, "ProcessEvents", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::ProcessEvents() {
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
int next_timeout_ms;
|
||||||
|
sp_session_process_events(session_, &next_timeout_ms);
|
||||||
|
qDebug() << next_timeout_ms << events_timer_;
|
||||||
|
events_timer_->start(next_timeout_ms);
|
||||||
|
qDebug() << "Started";
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::LogMessage(sp_session* session, const char* data) {
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::Search(const QString& query) {
|
||||||
|
sp_search_create(
|
||||||
|
session_,
|
||||||
|
query.toUtf8().constData(),
|
||||||
|
0, // track offset
|
||||||
|
10, // track count
|
||||||
|
0, // album offset
|
||||||
|
10, // album count
|
||||||
|
0, // artist offset
|
||||||
|
10, // artist count
|
||||||
|
&SearchComplete,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyBlob::SearchComplete(sp_search* result, void* userdata) {
|
||||||
|
sp_error error = sp_search_error(result);
|
||||||
|
if (error != SP_ERROR_OK) {
|
||||||
|
qWarning() << "Search failed";
|
||||||
|
sp_search_release(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int artists = sp_search_num_artists(result);
|
||||||
|
for (int i = 0; i < artists; ++i) {
|
||||||
|
sp_artist* artist = sp_search_artist(result, i);
|
||||||
|
qDebug() << "Found artist:" << sp_artist_name(artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
sp_search_release(result);
|
||||||
|
}
|
||||||
|
|
44
spotifyblob/spotifyblob.h
Normal file
44
spotifyblob/spotifyblob.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef SPOTIFY_H
|
||||||
|
#define SPOTIFY_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <libspotify/api.h>
|
||||||
|
|
||||||
|
class SpotifyClient;
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
|
class SpotifyBlob : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpotifyBlob(SpotifyClient* client, QObject* parent = 0);
|
||||||
|
~SpotifyBlob();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ProcessEvents();
|
||||||
|
void Login(const QString& username, const QString& password);
|
||||||
|
void Search(const QString& query);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Spotify callbacks.
|
||||||
|
static void LoggedIn(sp_session* session, sp_error error);
|
||||||
|
static void NotifyMainThread(sp_session* session);
|
||||||
|
static void LogMessage(sp_session* session, const char* data);
|
||||||
|
|
||||||
|
static void SearchComplete(sp_search* result, void* userdata);
|
||||||
|
|
||||||
|
void Notify();
|
||||||
|
void LoggedIn(sp_error error);
|
||||||
|
|
||||||
|
SpotifyClient* client_;
|
||||||
|
|
||||||
|
sp_session_config spotify_config_;
|
||||||
|
sp_session_callbacks spotify_callbacks_;
|
||||||
|
sp_session* session_;
|
||||||
|
|
||||||
|
QTimer* events_timer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPOTIFY_H
|
90
spotifyblob/spotifyclient.cpp
Normal file
90
spotifyblob/spotifyclient.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 "messages.pb.h"
|
||||||
|
#include "spotifyclient.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include <boost/scoped_array.hpp>
|
||||||
|
|
||||||
|
SpotifyClient::SpotifyClient(QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
socket_(new QTcpSocket(this))
|
||||||
|
{
|
||||||
|
connect(socket_, SIGNAL(readyRead()), SLOT(SocketReadyRead()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyClient::Init(quint16 port) {
|
||||||
|
qLog(Debug) << "port" << port;
|
||||||
|
|
||||||
|
socket_->connectToHost(QHostAddress::LocalHost, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyClient::SocketReadyRead() {
|
||||||
|
QDataStream s(socket_);
|
||||||
|
|
||||||
|
quint32 length = 0;
|
||||||
|
s >> length;
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::scoped_array<char> data(new char[length]);
|
||||||
|
s.readRawData(data.get(), length);
|
||||||
|
|
||||||
|
RequestMessage message;
|
||||||
|
if (!message.ParseFromArray(data.get(), length)) {
|
||||||
|
qLog(Error) << "Malformed protobuf message";
|
||||||
|
socket_->deleteLater();
|
||||||
|
socket_ = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qLog(Debug) << message.DebugString().c_str();
|
||||||
|
|
||||||
|
if (message.has_login_request()) {
|
||||||
|
const LoginRequest& r = message.login_request();
|
||||||
|
emit Login(QString::fromUtf8(r.username().data(), r.username().length()),
|
||||||
|
QString::fromUtf8(r.password().data(), r.password().length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyClient::SendMessage(const ResponseMessage& message) {
|
||||||
|
qLog(Debug) << message.DebugString().c_str();
|
||||||
|
|
||||||
|
std::string data(message.SerializeAsString());
|
||||||
|
|
||||||
|
QDataStream s(socket_);
|
||||||
|
s << quint32(data.length());
|
||||||
|
s.writeRawData(data.data(), data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyClient::LoginCompleted(bool success, const QString& error) {
|
||||||
|
const QByteArray error_bytes(error.toUtf8());
|
||||||
|
|
||||||
|
ResponseMessage message;
|
||||||
|
|
||||||
|
LoginResponse* response = message.mutable_login_response();
|
||||||
|
response->set_success(success);
|
||||||
|
response->set_error(error_bytes.constData(), error_bytes.length());
|
||||||
|
|
||||||
|
SendMessage(message);
|
||||||
|
}
|
32
spotifyblob/spotifyclient.h
Normal file
32
spotifyblob/spotifyclient.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef SPOTIFYCLIENT_H
|
||||||
|
#define SPOTIFYCLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class ResponseMessage;
|
||||||
|
|
||||||
|
class SpotifyClient : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpotifyClient(QObject* parent = 0);
|
||||||
|
|
||||||
|
void Init(quint16 port);
|
||||||
|
|
||||||
|
void LoginCompleted(bool success, const QString& error);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void Login(const QString& username, const QString& password);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void SocketReadyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendMessage(const ResponseMessage& message);
|
||||||
|
|
||||||
|
QTcpSocket* socket_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPOTIFYCLIENT_H
|
28
spotifyblob/spotifykey.h
Normal file
28
spotifyblob/spotifykey.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
const uint8_t g_appkey[] = {
|
||||||
|
0x01, 0x59, 0x4E, 0xAE, 0xF2, 0x64, 0x2B, 0x1F, 0x13, 0xF8, 0xB1, 0x2C, 0x0A, 0x4F, 0x8A, 0xCA,
|
||||||
|
0x5D, 0xB8, 0x23, 0x43, 0x12, 0xB2, 0x3A, 0x21, 0x64, 0x0B, 0x4C, 0x17, 0x39, 0xB6, 0x3B, 0x92,
|
||||||
|
0xE6, 0xB3, 0x56, 0xE6, 0x01, 0x61, 0x56, 0x81, 0xD6, 0x5A, 0x1E, 0x4A, 0x72, 0xA7, 0x34, 0x89,
|
||||||
|
0x3E, 0x64, 0x9D, 0xF9, 0x68, 0xB9, 0xD3, 0x0C, 0x20, 0x27, 0x05, 0x42, 0x53, 0x4B, 0x2A, 0xE0,
|
||||||
|
0xB7, 0xA9, 0xD7, 0x30, 0x78, 0xB7, 0xA6, 0x7C, 0x87, 0x6E, 0x8D, 0x2B, 0xAF, 0xB3, 0xF2, 0x09,
|
||||||
|
0xA4, 0x1D, 0x59, 0x15, 0xF4, 0x34, 0x4F, 0x91, 0x18, 0x1E, 0x6D, 0xC5, 0x24, 0x59, 0x30, 0xD3,
|
||||||
|
0x7E, 0x5C, 0x15, 0x3C, 0xA5, 0x85, 0xE3, 0xE8, 0x1D, 0x46, 0x35, 0x30, 0xA6, 0xBB, 0x2A, 0x07,
|
||||||
|
0x0E, 0x8E, 0x8A, 0x87, 0xD1, 0x2C, 0x9C, 0xDE, 0x29, 0xAC, 0x5B, 0x99, 0xA5, 0x06, 0xAA, 0x44,
|
||||||
|
0xA4, 0xC5, 0x52, 0xCE, 0x89, 0x70, 0xBD, 0x97, 0x1C, 0xA5, 0x36, 0xAE, 0xA3, 0xBB, 0xB4, 0x3B,
|
||||||
|
0xB9, 0x8F, 0x2E, 0xF1, 0x79, 0x03, 0x52, 0xC5, 0x4C, 0xDD, 0x82, 0x31, 0x89, 0x6C, 0xB6, 0xF8,
|
||||||
|
0x43, 0x3B, 0x23, 0xF9, 0x61, 0x6A, 0xDC, 0xD3, 0xF7, 0x1A, 0xEA, 0xAF, 0x11, 0xDE, 0xDA, 0x58,
|
||||||
|
0x3B, 0x0B, 0x33, 0xB0, 0x17, 0x99, 0x2E, 0x15, 0xDC, 0x20, 0x9A, 0x2F, 0x0C, 0x43, 0xBD, 0xB1,
|
||||||
|
0x4D, 0x85, 0xC8, 0x2A, 0x73, 0x65, 0x3A, 0xBC, 0xBF, 0x84, 0x90, 0xE5, 0x37, 0xD9, 0x4A, 0xC6,
|
||||||
|
0x46, 0xD4, 0x04, 0xB8, 0x7E, 0xA7, 0x36, 0xD8, 0xBC, 0xEF, 0x76, 0x4B, 0x67, 0xD8, 0x28, 0xAE,
|
||||||
|
0x4F, 0x00, 0x7B, 0xD7, 0xE7, 0xE4, 0xA6, 0x48, 0x11, 0x9B, 0x87, 0xA7, 0x65, 0xA4, 0x77, 0x82,
|
||||||
|
0x1A, 0xA6, 0xFC, 0x30, 0xF7, 0x5A, 0x79, 0x9E, 0x48, 0xD5, 0x8C, 0x4C, 0x70, 0x39, 0x31, 0x6B,
|
||||||
|
0x6F, 0xAD, 0x6C, 0xB0, 0x3F, 0x32, 0xE6, 0xAD, 0xEE, 0x02, 0x80, 0xF6, 0xEE, 0x7B, 0x4C, 0x50,
|
||||||
|
0xAD, 0x62, 0x3B, 0xA7, 0xFE, 0xEF, 0xE2, 0xFC, 0xE3, 0x70, 0x74, 0x12, 0x10, 0xAD, 0xE3, 0xF3,
|
||||||
|
0x5D, 0x98, 0xE2, 0xA4, 0xED, 0xF6, 0x91, 0x89, 0x90, 0x02, 0x20, 0xA4, 0x37, 0x5A, 0x7C, 0xB2,
|
||||||
|
0x04, 0x04, 0x70, 0x5E, 0x37, 0x43, 0x16, 0x95, 0xC1, 0x71, 0xA8, 0xD9, 0x7D, 0x2B, 0x46, 0x72,
|
||||||
|
0x5F,
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t g_appkey_size = sizeof(g_appkey);
|
@ -613,6 +613,21 @@ if(HAVE_LIBLASTFM)
|
|||||||
)
|
)
|
||||||
endif(HAVE_LIBLASTFM)
|
endif(HAVE_LIBLASTFM)
|
||||||
|
|
||||||
|
if(HAVE_SPOTIFY)
|
||||||
|
list(APPEND SOURCES
|
||||||
|
radio/spotifyconfig.cpp
|
||||||
|
radio/spotifyserver.cpp
|
||||||
|
radio/spotifyservice.cpp
|
||||||
|
)
|
||||||
|
list(APPEND HEADERS
|
||||||
|
radio/spotifyconfig.h
|
||||||
|
radio/spotifyserver.h
|
||||||
|
radio/spotifyservice.h
|
||||||
|
)
|
||||||
|
list(APPEND UI
|
||||||
|
radio/spotifyconfig.ui)
|
||||||
|
endif(HAVE_SPOTIFY)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
list(APPEND HEADERS core/macglobalshortcutbackend.h)
|
list(APPEND HEADERS core/macglobalshortcutbackend.h)
|
||||||
list(APPEND HEADERS devices/macdevicelister.h)
|
list(APPEND HEADERS devices/macdevicelister.h)
|
||||||
@ -995,6 +1010,10 @@ if(HAVE_BREAKPAD)
|
|||||||
endif (LINUX)
|
endif (LINUX)
|
||||||
endif(HAVE_BREAKPAD)
|
endif(HAVE_BREAKPAD)
|
||||||
|
|
||||||
|
if(HAVE_SPOTIFY)
|
||||||
|
target_link_libraries(clementine_lib clementine-spotifyblob-messages)
|
||||||
|
endif(HAVE_SPOTIFY)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_link_libraries(clementine_lib
|
target_link_libraries(clementine_lib
|
||||||
${GROWL}
|
${GROWL}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#cmakedefine HAVE_REMOTE
|
#cmakedefine HAVE_REMOTE
|
||||||
#cmakedefine HAVE_SCRIPTING_PYTHON
|
#cmakedefine HAVE_SCRIPTING_PYTHON
|
||||||
#cmakedefine HAVE_SPARKLE
|
#cmakedefine HAVE_SPARKLE
|
||||||
|
#cmakedefine HAVE_SPOTIFY
|
||||||
#cmakedefine HAVE_STATIC_SQLITE
|
#cmakedefine HAVE_STATIC_SQLITE
|
||||||
#cmakedefine HAVE_WIIMOTEDEV
|
#cmakedefine HAVE_WIIMOTEDEV
|
||||||
#cmakedefine LEOPARD
|
#cmakedefine LEOPARD
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
/* This file is part of Clementine.
|
/* This file is part of Clementine.
|
||||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
Clementine is free software: you can redistribute it and/or modify
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
it under the terms of the GNU General Public License as published by
|
you may not use this file except in compliance with the License.
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
You may obtain a copy of the License at
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Clementine is distributed in the hope that it will be useful,
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
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
|
Unless required by applicable law or agreed to in writing, software
|
||||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@ -37,8 +36,7 @@ static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
|||||||
static QtMsgHandler sOriginalMessageHandler = NULL;
|
static QtMsgHandler sOriginalMessageHandler = NULL;
|
||||||
|
|
||||||
|
|
||||||
static void GLog(const gchar* domain, GLogLevelFlags level,
|
void GLog(const char* domain, int level, const char* message, void* user_data) {
|
||||||
const gchar* message, gpointer user_data) {
|
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case G_LOG_FLAG_RECURSION:
|
case G_LOG_FLAG_RECURSION:
|
||||||
case G_LOG_FLAG_FATAL:
|
case G_LOG_FLAG_FATAL:
|
||||||
@ -84,9 +82,6 @@ void Init() {
|
|||||||
sClassLevels = new QMap<QString, Level>();
|
sClassLevels = new QMap<QString, Level>();
|
||||||
sNullDevice = new NullDevice;
|
sNullDevice = new NullDevice;
|
||||||
|
|
||||||
// Glib integration
|
|
||||||
g_log_set_default_handler(&GLog, NULL);
|
|
||||||
|
|
||||||
// Catch other messages from Qt
|
// Catch other messages from Qt
|
||||||
if (!sOriginalMessageHandler) {
|
if (!sOriginalMessageHandler) {
|
||||||
sOriginalMessageHandler = qInstallMsgHandler(MessageHandler);
|
sOriginalMessageHandler = qInstallMsgHandler(MessageHandler);
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
/* This file is part of Clementine.
|
/* This file is part of Clementine.
|
||||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
Clementine is free software: you can redistribute it and/or modify
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
it under the terms of the GNU General Public License as published by
|
you may not use this file except in compliance with the License.
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
You may obtain a copy of the License at
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Clementine is distributed in the hope that it will be useful,
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
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
|
Unless required by applicable law or agreed to in writing, software
|
||||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LOGGING_H
|
#ifndef LOGGING_H
|
||||||
@ -46,6 +45,8 @@ namespace logging {
|
|||||||
|
|
||||||
QDebug CreateLogger(Level level, const char* pretty_function, int line);
|
QDebug CreateLogger(Level level, const char* pretty_function, int line);
|
||||||
|
|
||||||
|
void GLog(const char* domain, int level, const char* message, void* user_data);
|
||||||
|
|
||||||
extern const char* kDefaultLogLevels;
|
extern const char* kDefaultLogLevels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,6 +314,7 @@ int main(int argc, char *argv[]) {
|
|||||||
// Initialise logging
|
// Initialise logging
|
||||||
logging::Init();
|
logging::Init();
|
||||||
logging::SetLevels(options.log_levels());
|
logging::SetLevels(options.log_levels());
|
||||||
|
g_log_set_default_handler(reinterpret_cast<GLogFunc>(&logging::GLog), NULL);
|
||||||
|
|
||||||
QtSingleApplication a(argc, argv);
|
QtSingleApplication a(argc, argv);
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
#ifdef HAVE_LIBLASTFM
|
#ifdef HAVE_LIBLASTFM
|
||||||
#include "lastfmservice.h"
|
#include "lastfmservice.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
#include "spotifyservice.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
@ -51,6 +54,9 @@ RadioModel::RadioModel(BackgroundThread<Database>* db_thread,
|
|||||||
|
|
||||||
#ifdef HAVE_LIBLASTFM
|
#ifdef HAVE_LIBLASTFM
|
||||||
AddService(new LastFMService(this));
|
AddService(new LastFMService(this));
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
AddService(new SpotifyService(this));
|
||||||
#endif
|
#endif
|
||||||
AddService(new SomaFMService(this));
|
AddService(new SomaFMService(this));
|
||||||
AddService(new MagnatuneService(this));
|
AddService(new MagnatuneService(this));
|
||||||
@ -70,6 +76,7 @@ void RadioModel::AddService(RadioService *service) {
|
|||||||
root->setData(QVariant::fromValue(service), Role_Service);
|
root->setData(QVariant::fromValue(service), Role_Service);
|
||||||
|
|
||||||
invisibleRootItem()->appendRow(root);
|
invisibleRootItem()->appendRow(root);
|
||||||
|
qDebug() << "Adding:" << service->name();
|
||||||
sServices->insert(service->name(), service);
|
sServices->insert(service->name(), service);
|
||||||
|
|
||||||
connect(service, SIGNAL(AsyncLoadFinished(PlaylistItem::SpecialLoadResult)), SIGNAL(AsyncLoadFinished(PlaylistItem::SpecialLoadResult)));
|
connect(service, SIGNAL(AsyncLoadFinished(PlaylistItem::SpecialLoadResult)), SIGNAL(AsyncLoadFinished(PlaylistItem::SpecialLoadResult)));
|
||||||
|
80
src/radio/spotifyconfig.cpp
Normal file
80
src/radio/spotifyconfig.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 "spotifyconfig.h"
|
||||||
|
|
||||||
|
#include "core/network.h"
|
||||||
|
#include "spotifyservice.h"
|
||||||
|
#include "radiomodel.h"
|
||||||
|
#include "ui_spotifyconfig.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
SpotifyConfig::SpotifyConfig(QWidget *parent)
|
||||||
|
: QWidget(parent),
|
||||||
|
network_(new NetworkAccessManager(this)),
|
||||||
|
ui_(new Ui_SpotifyConfig),
|
||||||
|
service_(RadioModel::Service<SpotifyService>()),
|
||||||
|
needs_validation_(true)
|
||||||
|
{
|
||||||
|
ui_->setupUi(this);
|
||||||
|
ui_->busy->hide();
|
||||||
|
connect(service_, SIGNAL(LoginFinished(bool)), SLOT(LoginFinished(bool)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SpotifyConfig::~SpotifyConfig() {
|
||||||
|
delete ui_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpotifyConfig::NeedsValidation() const {
|
||||||
|
// FIXME
|
||||||
|
return needs_validation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyConfig::Validate() {
|
||||||
|
ui_->busy->show();
|
||||||
|
service_->Login(ui_->username->text(), ui_->password->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyConfig::Load() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(SpotifyService::kSettingsGroup);
|
||||||
|
|
||||||
|
ui_->username->setText(s.value("username").toString());
|
||||||
|
ui_->password->setText(s.value("password").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyConfig::Save() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(SpotifyService::kSettingsGroup);
|
||||||
|
|
||||||
|
s.setValue("username", ui_->username->text());
|
||||||
|
s.setValue("password", ui_->password->text());
|
||||||
|
|
||||||
|
RadioModel::Service<SpotifyService>()->ReloadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyConfig::LoginFinished(bool success) {
|
||||||
|
qDebug() << Q_FUNC_INFO << success;
|
||||||
|
needs_validation_ = !success;
|
||||||
|
ui_->busy->hide();
|
||||||
|
emit ValidationComplete(success);
|
||||||
|
}
|
57
src/radio/spotifyconfig.h
Normal file
57
src/radio/spotifyconfig.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 SPOTIFYCONFIG_H
|
||||||
|
#define SPOTIFYCONFIG_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QAuthenticator;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class NetworkAccessManager;
|
||||||
|
class Ui_SpotifyConfig;
|
||||||
|
class SpotifyService;
|
||||||
|
|
||||||
|
class SpotifyConfig : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SpotifyConfig(QWidget* parent = 0);
|
||||||
|
~SpotifyConfig();
|
||||||
|
|
||||||
|
bool NeedsValidation() const;
|
||||||
|
void Validate();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ValidationComplete(bool success);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void Load();
|
||||||
|
void Save();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void LoginFinished(bool success);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkAccessManager* network_;
|
||||||
|
Ui_SpotifyConfig* ui_;
|
||||||
|
SpotifyService* service_;
|
||||||
|
|
||||||
|
bool needs_validation_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPOTIFYCONFIG_H
|
125
src/radio/spotifyconfig.ui
Normal file
125
src/radio/spotifyconfig.ui
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SpotifyConfig</class>
|
||||||
|
<widget class="QWidget" name="SpotifyConfig">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>448</width>
|
||||||
|
<height>310</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Account details</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout_3">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>A Spotify Premium account is required.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QWidget" name="login_container" native="true">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="username_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Username</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="username"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="password_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="password">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</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>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="busy" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Authenticating...</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="BusyIndicator" name="label_6">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>BusyIndicator</class>
|
||||||
|
<extends>QLabel</extends>
|
||||||
|
<header>widgets/busyindicator.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
108
src/radio/spotifyserver.cpp
Normal file
108
src/radio/spotifyserver.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 "spotifyserver.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
#include "spotifyblob/messages.pb.h"
|
||||||
|
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include <boost/scoped_array.hpp>
|
||||||
|
|
||||||
|
SpotifyServer::SpotifyServer(QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
server_(new QTcpServer(this)),
|
||||||
|
protocol_socket_(NULL)
|
||||||
|
{
|
||||||
|
connect(server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyServer::Init() {
|
||||||
|
if (!server_->listen(QHostAddress::LocalHost)) {
|
||||||
|
qLog(Error) << "Couldn't open server socket" << server_->errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SpotifyServer::server_port() const {
|
||||||
|
return server_->serverPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyServer::NewConnection() {
|
||||||
|
protocol_socket_ = server_->nextPendingConnection();
|
||||||
|
connect(protocol_socket_, SIGNAL(readyRead()), SLOT(ProtocolSocketReadyRead()));
|
||||||
|
|
||||||
|
qLog(Info) << "Connection from port" << protocol_socket_->peerPort();
|
||||||
|
|
||||||
|
emit ClientConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyServer::Login(const QString& username, const QString& password) {
|
||||||
|
const QByteArray username_bytes(username.toUtf8());
|
||||||
|
const QByteArray password_bytes(password.toUtf8());
|
||||||
|
|
||||||
|
RequestMessage message;
|
||||||
|
|
||||||
|
LoginRequest* request = message.mutable_login_request();
|
||||||
|
request->set_username(username_bytes.constData(), username_bytes.length());
|
||||||
|
request->set_password(password_bytes.constData(), password_bytes.length());
|
||||||
|
|
||||||
|
SendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyServer::SendMessage(const RequestMessage& message) {
|
||||||
|
qLog(Debug) << message.DebugString().c_str();
|
||||||
|
|
||||||
|
std::string data(message.SerializeAsString());
|
||||||
|
|
||||||
|
QDataStream s(protocol_socket_);
|
||||||
|
s << quint32(data.length());
|
||||||
|
s.writeRawData(data.data(), data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyServer::ProtocolSocketReadyRead() {
|
||||||
|
QDataStream s(protocol_socket_);
|
||||||
|
|
||||||
|
quint32 length = 0;
|
||||||
|
s >> length;
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::scoped_array<char> data(new char[length]);
|
||||||
|
s.readRawData(data.get(), length);
|
||||||
|
|
||||||
|
ResponseMessage message;
|
||||||
|
if (!message.ParseFromArray(data.get(), length)) {
|
||||||
|
qLog(Error) << "Malformed protobuf message";
|
||||||
|
protocol_socket_->deleteLater();
|
||||||
|
protocol_socket_ = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qLog(Debug) << message.DebugString().c_str();
|
||||||
|
|
||||||
|
if (message.has_login_response()) {
|
||||||
|
const LoginResponse& response = message.login_response();
|
||||||
|
if (!response.success()) {
|
||||||
|
qLog(Info) << QString::fromUtf8(response.error().data(), response.error().size());
|
||||||
|
}
|
||||||
|
emit LoginCompleted(response.success());
|
||||||
|
}
|
||||||
|
}
|
54
src/radio/spotifyserver.h
Normal file
54
src/radio/spotifyserver.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, 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 SPOTIFYSERVER_H
|
||||||
|
#define SPOTIFYSERVER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class RequestMessage;
|
||||||
|
|
||||||
|
class QTcpServer;
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class SpotifyServer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpotifyServer(QObject* parent = 0);
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Login(const QString& username, const QString& password);
|
||||||
|
|
||||||
|
int server_port() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ClientConnected();
|
||||||
|
void LoginCompleted(bool success);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void NewConnection();
|
||||||
|
void ProtocolSocketReadyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendMessage(const RequestMessage& message);
|
||||||
|
|
||||||
|
QTcpServer* server_;
|
||||||
|
QTcpSocket* protocol_socket_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPOTIFYSERVER_H
|
72
src/radio/spotifyservice.cpp
Normal file
72
src/radio/spotifyservice.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include "core/logging.h"
|
||||||
|
#include "radiomodel.h"
|
||||||
|
#include "spotifyserver.h"
|
||||||
|
#include "spotifyservice.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
const char* SpotifyService::kServiceName = "Spotify";
|
||||||
|
const char* SpotifyService::kSettingsGroup = "Spotify";
|
||||||
|
|
||||||
|
SpotifyService::SpotifyService(RadioModel* parent)
|
||||||
|
: RadioService(kServiceName, parent),
|
||||||
|
server_(NULL),
|
||||||
|
blob_process_(NULL) {
|
||||||
|
blob_path_ = QCoreApplication::applicationFilePath() + "-spotifyblob";
|
||||||
|
}
|
||||||
|
|
||||||
|
SpotifyService::~SpotifyService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
QStandardItem* SpotifyService::CreateRootItem() {
|
||||||
|
QStandardItem* item = new QStandardItem(QIcon(":icons/svg/spotify.svg"), kServiceName);
|
||||||
|
item->setData(true, RadioModel::Role_CanLazyLoad);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyService::LazyPopulate(QStandardItem* parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex SpotifyService::GetCurrentIndex() {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyService::Login(const QString& username, const QString& password) {
|
||||||
|
qLog(Debug) << Q_FUNC_INFO;
|
||||||
|
delete server_;
|
||||||
|
delete blob_process_;
|
||||||
|
|
||||||
|
server_ = new SpotifyServer(this);
|
||||||
|
blob_process_ = new QProcess(this);
|
||||||
|
blob_process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
|
|
||||||
|
connect(server_, SIGNAL(ClientConnected()), SLOT(ClientConnected()));
|
||||||
|
connect(server_, SIGNAL(LoginCompleted(bool)), SLOT(LoginCompleted(bool)));
|
||||||
|
|
||||||
|
connect(blob_process_,
|
||||||
|
SIGNAL(error(QProcess::ProcessError)),
|
||||||
|
SLOT(BlobProcessError(QProcess::ProcessError)));
|
||||||
|
|
||||||
|
pending_username_ = username;
|
||||||
|
pending_password_ = password;
|
||||||
|
|
||||||
|
server_->Init();
|
||||||
|
blob_process_->start(
|
||||||
|
blob_path_, QStringList() << QString::number(server_->server_port()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyService::ClientConnected() {
|
||||||
|
qLog(Debug) << "ClientConnected";
|
||||||
|
|
||||||
|
server_->Login(pending_username_, pending_password_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyService::LoginCompleted(bool success) {
|
||||||
|
emit LoginFinished(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpotifyService::BlobProcessError(QProcess::ProcessError error) {
|
||||||
|
qLog(Error) << "Failed to start blob process:" << error;
|
||||||
|
}
|
48
src/radio/spotifyservice.h
Normal file
48
src/radio/spotifyservice.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef SPOTIFYSERVICE_H
|
||||||
|
#define SPOTIFYSERVICE_H
|
||||||
|
|
||||||
|
#include "radioservice.h"
|
||||||
|
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class SpotifyServer;
|
||||||
|
|
||||||
|
class SpotifyService : public RadioService {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SpotifyService(RadioModel* parent);
|
||||||
|
virtual ~SpotifyService();
|
||||||
|
|
||||||
|
virtual QStandardItem* CreateRootItem();
|
||||||
|
virtual void LazyPopulate(QStandardItem* parent);
|
||||||
|
|
||||||
|
void Login(const QString& username, const QString& password);
|
||||||
|
void Search(const QString& query);
|
||||||
|
|
||||||
|
static const char* kServiceName;
|
||||||
|
static const char* kSettingsGroup;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void LoginFinished(bool success);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QModelIndex GetCurrentIndex();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ClientConnected();
|
||||||
|
void LoginCompleted(bool success);
|
||||||
|
void BlobProcessError(QProcess::ProcessError error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SpotifyServer* server_;
|
||||||
|
|
||||||
|
QString blob_path_;
|
||||||
|
QProcess* blob_process_;
|
||||||
|
|
||||||
|
QString pending_username_;
|
||||||
|
QString pending_password_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -45,6 +45,10 @@
|
|||||||
# include "remote/remoteconfig.h"
|
# include "remote/remoteconfig.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
# include "radio/spotifyconfig.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFontDialog>
|
#include <QFontDialog>
|
||||||
@ -118,6 +122,21 @@ SettingsDialog::SettingsDialog(BackgroundStreams* streams, QWidget* parent)
|
|||||||
connect(lastfm_config_, SIGNAL(ValidationComplete(bool)), SLOT(ValidationComplete(bool)));
|
connect(lastfm_config_, SIGNAL(ValidationComplete(bool)), SLOT(ValidationComplete(bool)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
ui_->list->insertItem(Page_Spotify, tr("Spotify"));
|
||||||
|
ui_->list->item(Page_Spotify)->setIcon(QIcon(":/icons/svg/spotify.svg"));
|
||||||
|
|
||||||
|
QWidget* spotify_page = new QWidget;
|
||||||
|
QVBoxLayout* spotify_layout = new QVBoxLayout;
|
||||||
|
spotify_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
spotify_config_ = new SpotifyConfig;
|
||||||
|
spotify_layout->addWidget(spotify_config_);
|
||||||
|
spotify_page->setLayout(spotify_layout);
|
||||||
|
|
||||||
|
ui_->stacked_widget->insertWidget(Page_Spotify, spotify_page);
|
||||||
|
connect(spotify_config_, SIGNAL(ValidationComplete(bool)), SLOT(ValidationComplete(bool)));
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_REMOTE
|
#ifdef HAVE_REMOTE
|
||||||
ui_->list->insertItem(Page_Remote, tr("Remote Control"));
|
ui_->list->insertItem(Page_Remote, tr("Remote Control"));
|
||||||
ui_->list->item(Page_Remote)->setIcon(IconLoader::Load("network-server"));
|
ui_->list->item(Page_Remote)->setIcon(IconLoader::Load("network-server"));
|
||||||
@ -312,6 +331,16 @@ void SettingsDialog::accept() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
if (spotify_config_->NeedsValidation()) {
|
||||||
|
spotify_config_->Validate();
|
||||||
|
ui_->buttonBox->setEnabled(false);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
spotify_config_->Save();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ui_->magnatune->NeedsValidation()) {
|
if (ui_->magnatune->NeedsValidation()) {
|
||||||
ui_->magnatune->Validate();
|
ui_->magnatune->Validate();
|
||||||
ui_->buttonBox->setEnabled(false);
|
ui_->buttonBox->setEnabled(false);
|
||||||
@ -524,6 +553,10 @@ void SettingsDialog::showEvent(QShowEvent*) {
|
|||||||
remote_config_->Load();
|
remote_config_->Load();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
spotify_config_->Load();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Magnatune
|
// Magnatune
|
||||||
ui_->magnatune->Load();
|
ui_->magnatune->Load();
|
||||||
|
|
||||||
|
@ -44,6 +44,9 @@ class Ui_SettingsDialog;
|
|||||||
#ifdef HAVE_REMOTE
|
#ifdef HAVE_REMOTE
|
||||||
class RemoteConfig;
|
class RemoteConfig;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
class SpotifyConfig;
|
||||||
|
#endif
|
||||||
|
|
||||||
class GstEngine;
|
class GstEngine;
|
||||||
|
|
||||||
@ -63,6 +66,9 @@ class SettingsDialog : public QDialog {
|
|||||||
Page_Library,
|
Page_Library,
|
||||||
#ifdef HAVE_LIBLASTFM
|
#ifdef HAVE_LIBLASTFM
|
||||||
Page_Lastfm,
|
Page_Lastfm,
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
Page_Spotify,
|
||||||
#endif
|
#endif
|
||||||
Page_Magnatune,
|
Page_Magnatune,
|
||||||
Page_BackgroundStreams,
|
Page_BackgroundStreams,
|
||||||
@ -126,6 +132,9 @@ class SettingsDialog : public QDialog {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_REMOTE
|
#ifdef HAVE_REMOTE
|
||||||
RemoteConfig* remote_config_;
|
RemoteConfig* remote_config_;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SPOTIFY
|
||||||
|
SpotifyConfig* spotify_config_;
|
||||||
#endif
|
#endif
|
||||||
const GstEngine* gst_engine_;
|
const GstEngine* gst_engine_;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user