mirror of
https://github.com/thorsten-gehrig/alexa-remote-control.git
synced 2024-11-22 08:49:02 +01:00
PR #139 from adn77/master
* Playing TuneIn works again using new entertainment API endpoint * Added playmusic (Alexa.Music.PlaySearchPhrase) as command
This commit is contained in:
commit
aa0b9261d9
@ -22,9 +22,7 @@ DEVICEVOLNAME - a list of device names with specific volume settings (space se
|
||||
DEVICEVOLSPEAK - a list of speak volume levels - matching the devices above
|
||||
DEVICEVOLNORMAL - a list of normal volume levels- matching the devices above
|
||||
(current playing volume takes precedence for normal volume)
|
||||
USE_ANNOUNCEMENT_FOR_SPEAK - Announcements can be made to multiple devices, while
|
||||
regular SPEAK cannot but the announcement feature has
|
||||
to be turned on for those devices. Also supports SSML!
|
||||
|
||||
```
|
||||
You will very likely want to set the language to:
|
||||
```
|
||||
@ -41,9 +39,11 @@ alexa-remote-control [-d <device>|ALL] -e <pause|play|next|prev|fwd|rwd|shuffle|
|
||||
-e : run command, additional SEQUENCECMDs:
|
||||
weather,traffic,flashbriefing,goodmorning,singasong,tellstory,
|
||||
speak:'<text/ssml>',automation:'<routine name>',sound:<soundeffect_name>,
|
||||
textcommand:'<anything you would otherwise say to Alexa>'
|
||||
textcommand:'<anything you would otherwise say to Alexa>',
|
||||
playmusic:<channel e.g. TUNEIN, AMAZON_MUSIC>:'<music name>'
|
||||
|
||||
-b : connect/disconnect/list bluetooth device
|
||||
-c : list 'playmusic' channels
|
||||
-q : query queue
|
||||
-n : query notifications
|
||||
-r : play tunein radio
|
||||
|
@ -67,6 +67,9 @@
|
||||
# (thanks to Ingo Fischer)
|
||||
# 2021-05-27: v0.18 complete rework of sequence commands especially for TTS
|
||||
# Announcement feature is no longer required due to inconsistent SSML handling
|
||||
# 2021-09-02: v0.19 Playing TuneIn works again using new entertainment API endpoint
|
||||
# Added playmusic (Alexa.Music.PlaySearchPhrase) as command, for available channels use "-c"
|
||||
# Note: playmusic is not multi-room capable, doing so might lead to unexpected results
|
||||
#
|
||||
###
|
||||
#
|
||||
@ -74,6 +77,7 @@
|
||||
# - requires cURL for web communication
|
||||
# - (GNU) sed and awk for extraction
|
||||
# - jq as command line JSON parser (optional for the fancy bits)
|
||||
# - base64 for B64 encoding (make sure "-w 0" option is available on your platform)
|
||||
# - oathtool as OATH one-time password tool (optional for two-factor authentication)
|
||||
#
|
||||
##########################################
|
||||
@ -167,7 +171,10 @@ TTS=""
|
||||
UTTERANCE=""
|
||||
SEQUENCECMD=""
|
||||
SEQUENCEVAL=""
|
||||
SEARCHPHRASE=""
|
||||
PROVIDERID=""
|
||||
STATIONID=""
|
||||
CHANNEL=""
|
||||
QUEUE=""
|
||||
SONG=""
|
||||
ALBUM=""
|
||||
@ -188,15 +195,17 @@ NOTIFICATIONS=""
|
||||
usage()
|
||||
{
|
||||
echo "$0 [-d <device>|ALL] -e <pause|play|next|prev|fwd|rwd|shuffle|repeat|vol:<0-100>> |"
|
||||
echo " -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -n | -r <\"station name\"|stationid> |"
|
||||
echo " -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -n | -r <\"station name\"|stationId> |"
|
||||
echo " -s <trackID|'Artist' 'Album'> | -t <ASIN> | -u <seedID> | -v <queueID> | -w <playlistId> |"
|
||||
echo " -i | -p | -P | -S | -a | -m <multiroom_device> [device_1 .. device_X] | -lastalexa | -lastcommand | -z | -l | -h"
|
||||
echo
|
||||
echo " -e : run command, additional SEQUENCECMDs:"
|
||||
echo " weather,traffic,flashbriefing,goodmorning,singasong,tellstory,"
|
||||
echo " speak:'<text/ssml>',automation:'<routine name>',sound:<soundeffect_name>,"
|
||||
echo " textcommand:'<anything you would otherwise say to Alexa>'"
|
||||
echo " textcommand:'<anything you would otherwise say to Alexa>',"
|
||||
echo " playmusic:<channel e.g. TUNEIN, AMAZON_MUSIC>:'<music name>'"
|
||||
echo " -b : connect/disconnect/list bluetooth device"
|
||||
echo " -c : list 'playmusic' channels"
|
||||
echo " -q : query queue"
|
||||
echo " -n : query notifications"
|
||||
echo " -r : play tunein radio"
|
||||
@ -222,7 +231,7 @@ usage()
|
||||
while [ "$#" -gt 0 ] ; do
|
||||
case "$1" in
|
||||
--version)
|
||||
echo "v0.18"
|
||||
echo "v0.19"
|
||||
exit 0
|
||||
;;
|
||||
-d)
|
||||
@ -341,6 +350,9 @@ while [ "$#" -gt 0 ] ; do
|
||||
-a)
|
||||
LIST="true"
|
||||
;;
|
||||
-c)
|
||||
CHANNEL="true"
|
||||
;;
|
||||
-i)
|
||||
TYPE="IMPORTED"
|
||||
;;
|
||||
@ -455,6 +467,13 @@ case "$COMMAND" in
|
||||
tellstory)
|
||||
SEQUENCECMD='Alexa.TellStory.Play'
|
||||
;;
|
||||
playmusic:*)
|
||||
SEQUENCECMD='Alexa.Music.PlaySearchPhrase'
|
||||
PROVIDERID=${COMMAND#*:}
|
||||
PROVIDERID=${PROVIDERID%:*}
|
||||
SEQUENCEVAL=',\"musicProviderId\":\"'${PROVIDERID}'\",'
|
||||
SEARCHPHRASE=$(echo ${COMMAND##*:} | sed s/\"/\'/g)
|
||||
;;
|
||||
"")
|
||||
;;
|
||||
*)
|
||||
@ -626,6 +645,26 @@ list_devices()
|
||||
jq -r '.devices[].accountName' ${DEVLIST}
|
||||
}
|
||||
|
||||
#
|
||||
# sanitize search phrase
|
||||
# ARG1 - sequence command (e.g. Alexa.Music.PlaySearchPhrase)
|
||||
# ARG2 - musicProviderID ( TUNEIN, AMASON_MUSIC, CLOUDPLAYER, SPOTIFY, APPLE_MUSIC, DEEZER, I_HEART_RADIO )
|
||||
# ARG3 - search phrase
|
||||
#
|
||||
sanitize_search()
|
||||
{
|
||||
if [ -n "$1" -a -n "$2" -a -n "$3" ] ; then
|
||||
JSON='{"type":"'${1}'","operationPayload":"{\"locale\":\"'${TTS_LOCALE}'\",\"musicProviderId\":\"'${2}'\",\"searchPhrase\":\"'${3}'\"}"}'
|
||||
else
|
||||
JSON='{"type":"'${SEQUENCECMD}'","operationPayload":"{\"locale\":\"'${TTS_LOCALE}'\",\"musicProviderId\":\"'${PROVIDERID}'\",\"searchPhrase\":\"'${SEARCHPHRASE}'\"}"}'
|
||||
fi
|
||||
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X POST -d "${JSON}" \
|
||||
"https://${ALEXA}/api/behaviors/operation/validate" | jq -r '.operationPayload.sanitizedSearchPhrase'
|
||||
}
|
||||
|
||||
#
|
||||
# build node_to_execute string
|
||||
# ARG1 - SEQUENCECMD
|
||||
@ -684,6 +723,14 @@ if [ -n "${SEQUENCECMD}" ] ; then
|
||||
VOLUMEPRENODESTOEXECUTE=''
|
||||
VOLUMEPOSTNODESTOEXECUTE=''
|
||||
NODESTOEXECUTE=''
|
||||
|
||||
# sanitize search phrase
|
||||
if [ -n "${SEARCHPHRASE}" -a -n "${PROVIDERID}" ] ; then
|
||||
SEQUENCEVAL=${SEQUENCEVAL}'\"searchPhrase\":\"'${SEARCHPHRASE}'\",\"sanitizedSearchPhrase\":\"'$(sanitize_search)'\"'
|
||||
fi
|
||||
|
||||
# iterate over member devices if target is multiroom
|
||||
# !!! this is no true multi-room - it just tries to play on every member device in parallel !!!
|
||||
if [ "${DEVICEFAMILY}" = "WHA" ] ; then
|
||||
MEMBERDEVICESERIALS=$(jq --arg device "${DEVICE}" -r '.devices[] | select(.accountName == $device) | .clusterMembers[]' ${DEVLIST})
|
||||
for DEVICESERIALNUMBER in $MEMBERDEVICESERIALS ; do
|
||||
@ -691,7 +738,8 @@ if [ -n "${SEQUENCECMD}" ] ; then
|
||||
NODESTOEXECUTE=$(add_node "$(node)" "${NODESTOEXECUTE}")
|
||||
|
||||
# add volume setting per device - the WHA volume is unrelyable
|
||||
if [ $SPEAKVOL -gt 0 -o -n "${DEVICEVOLSPEAK}" ] ; then
|
||||
# don't set volume if Alexa.Music.PlaySearchPhrase is used
|
||||
if [ \( $SPEAKVOL -gt 0 -o -n "${DEVICEVOLSPEAK}" \) -a "${SEQUENCECMD}" != "Alexa.Music.PlaySearchPhrase" ] ; then
|
||||
DEVICE=$(jq --arg device "${DEVICESERIALNUMBER}" -r '.devices[] | select(.serialNumber == $device) | .accountName' ${DEVLIST})
|
||||
get_volumes
|
||||
VOLUMEPRENODESTOEXECUTE=$(add_node $(node Alexa.DeviceControls.Volume ',\"value\":\"'${SVOL}'\"') ${VOLUMEPRENODESTOEXECUTE})
|
||||
@ -705,7 +753,9 @@ if [ -n "${SEQUENCECMD}" ] ; then
|
||||
fi
|
||||
else
|
||||
NODESTOEXECUTE=$(add_node "$(node)" "${NODESTOEXECUTE}")
|
||||
if [ $SPEAKVOL -gt 0 -o -n "${DEVICEVOLSPEAK}" ] ; then
|
||||
|
||||
# don't set volume if Alexa.Music.PlaySearchPhrase is used
|
||||
if [ \( $SPEAKVOL -gt 0 -o -n "${DEVICEVOLSPEAK}" \) -a "${SEQUENCECMD}" != "Alexa.Music.PlaySearchPhrase" ] ; then
|
||||
get_volumes
|
||||
VOLUMEPRENODESTOEXECUTE=$(add_node $(node Alexa.DeviceControls.Volume ',\"value\":\"'${SVOL}'\"') ${VOLUMEPRENODESTOEXECUTE})
|
||||
VOLUMEPOSTNODESTOEXECUTE=$(add_node $(node Alexa.DeviceControls.Volume ',\"value\":\"'${VOL}'\"') ${VOLUMEPOSTNODESTOEXECUTE})
|
||||
@ -730,7 +780,7 @@ if [ -n "${SEQUENCECMD}" ] ; then
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X POST -d @"${TMP}/.alexa.cmd" \
|
||||
"https://${ALEXA}/api/behaviors/preview"
|
||||
|
||||
# rm -f "${TMP}/.alexa.cmd"
|
||||
rm -f "${TMP}/.alexa.cmd"
|
||||
else
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
|
||||
@ -744,10 +794,12 @@ fi
|
||||
#
|
||||
play_radio()
|
||||
{
|
||||
JSON='{"contentToken":"music:'$(echo '["music/tuneIn/stationId","'${STATIONID}'"]|{"previousPageId":"TuneIn_SEARCH"}'| base64 -w 0| base64 -w 0 )'"}'
|
||||
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X POST\
|
||||
"https://${ALEXA}/api/tunein/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&guideId=${STATIONID}&contentType=station&callSign=&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}"
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X PUT -d "${JSON}" \
|
||||
"https://${ALEXA}/api/entertainment/v1/player/queue?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}"
|
||||
}
|
||||
|
||||
#
|
||||
@ -897,6 +949,14 @@ show_queue()
|
||||
"https://${ALEXA}/api/np/queue?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}" | jq '.'
|
||||
}
|
||||
|
||||
get_music_channels()
|
||||
{
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X GET \
|
||||
"https://${ALEXA}/api/behaviors/entities?skillId=amzn1.ask.1p.music" | jq -r '.[] | select( .supportedProperties[] == "Alexa.Music.PlaySearchPhrase" ) | "\(.id) - \(.displayName) \(.description)"'
|
||||
}
|
||||
|
||||
#
|
||||
# device specific SPEAKVOL/NORMALVOL (sets SVOL/VOL)
|
||||
#
|
||||
@ -1103,7 +1163,7 @@ rm -f ${TMP}/.alexa.*.list
|
||||
rm -f ${TMP}/.alexa.volume.*
|
||||
}
|
||||
|
||||
if [ -z "$LASTALEXA" -a -z "$LASTCOMMAND" -a -z "$BLUETOOTH" -a -z "$LEMUR" -a -z "$PLIST" -a -z "$HIST" -a -z "$SEEDID" -a -z "$ASIN" -a -z "$PRIME" -a -z "$TYPE" -a -z "$QUEUE" -a -z "$NOTIFICATIONS" -a -z "$LIST" -a -z "$COMMAND" -a -z "$STATIONID" -a -z "$SONG" -a -z "$GETVOL" -a -n "$LOGOFF" ] ; then
|
||||
if [ -z "$LASTALEXA" -a -z "$LASTCOMMAND" -a -z "$CHANNEL" -a -z "$BLUETOOTH" -a -z "$LEMUR" -a -z "$PLIST" -a -z "$HIST" -a -z "$SEEDID" -a -z "$ASIN" -a -z "$PRIME" -a -z "$TYPE" -a -z "$QUEUE" -a -z "$NOTIFICATIONS" -a -z "$LIST" -a -z "$COMMAND" -a -z "$STATIONID" -a -z "$SONG" -a -z "$GETVOL" -a -n "$LOGOFF" ] ; then
|
||||
echo "only logout option present, logging off ..."
|
||||
log_off
|
||||
exit 0
|
||||
@ -1139,6 +1199,11 @@ if [ -n "$LOGIN" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "$CHANNEL" ] ; then
|
||||
get_music_channels
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "$COMMAND" -o -n "$QUEUE" -o -n "$NOTIFICATIONS" -o -n "$GETVOL" ] ; then
|
||||
if [ "${DEVICE}" = "ALL" ] ; then
|
||||
for DEVICE in $( jq -r '.devices[] | select( ( .deviceFamily == "ECHO" or .deviceFamily == "KNIGHT" or .deviceFamily == "ROOK" or .deviceFamily == "WHA" ) and .online == true ) | .accountName' ${DEVLIST} | sed -r 's/ /%20/g') ; do
|
||||
|
@ -4,6 +4,7 @@
|
||||
# alex(at)loetzimmer.de
|
||||
#
|
||||
# 2021-01-28: v0.17c (for updates see http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html)
|
||||
# 2021-09-02: v0.17d includes fixes for playing tunein (base64 required)
|
||||
#
|
||||
# !!! THIS IS THE FINAL VERSION !!!
|
||||
#
|
||||
@ -15,6 +16,7 @@
|
||||
# (no BASHisms were used, should run with any shell)
|
||||
# - requires cURL for web communication
|
||||
# - (GNU) sed and awk for extraction
|
||||
# - base64 for B64 encoding (make sure "-w 0" option is available on your platform)
|
||||
# - oathtool as OATH one-time password tool (optional for two-factor authentication)
|
||||
#
|
||||
##########################################
|
||||
@ -148,7 +150,7 @@ usage()
|
||||
while [ "$#" -gt 0 ] ; do
|
||||
case "$1" in
|
||||
--version)
|
||||
echo "v0.17a"
|
||||
echo "v0.17d"
|
||||
exit 0
|
||||
;;
|
||||
-d)
|
||||
@ -719,10 +721,12 @@ fi
|
||||
#
|
||||
play_radio()
|
||||
{
|
||||
JSON='{"contentToken":"music:'$(echo '["music/tuneIn/stationId","'${STATIONID}'"]|{"previousPageId":"TuneIn_SEARCH"}'| base64 -w 0| base64 -w 0 )'"}'
|
||||
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X POST\
|
||||
"https://${ALEXA}/api/tunein/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&guideId=${STATIONID}&contentType=station&callSign=&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}"
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X PUT -d "${JSON}" \
|
||||
"https://${ALEXA}/api/entertainment/v1/player/queue?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}"
|
||||
}
|
||||
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user