Created
March 11, 2015 14:17
-
-
Save mbafford/3ce0141b680177fd6170 to your computer and use it in GitHub Desktop.
You can't skip Pandora ads on Sonos (not even with UPNP commands), but you can mute them. This just watches for a Pandora ad playing and mutes the audio until the ad has finished. A much better approach is to just pay for Pandora.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import soco | |
import time | |
import re | |
# pandora song: | |
# {u'album': 'Everything All The Time', u'artist': 'Band Of Horses', u'title': 'The Funeral', u'uri': 'pndrradio-http://audio-ch1-t1-2-v4v6.pandora.com/access/?version=4&lid=3729520&token=slyAWqqgjJ8Bl3TgMB%2F03C4tamOHsLj%2BOQLq1T8xbiwSzkcoc253HeYbNFTcOP7K6Oy9GOq6tcaManOhyKDpECIiCENj7hjBsH%2FJVhJnDjLzyPRWjg8q0LtX1mn80yFiurvB1r0G0Be76YBZJUswLdWtNZtfYSO2y2WdYHSge8Ul2hML1MWvkv23AQjw9%2BAKPmIHXX8nUzfcfFfLfmyKk%2Flxwh2cf%2BvDrJIbnnVZfvlfirWhshENZDm7BBofIlUqR0qsPXWbYN5m7WD4bS3XCK4dedE0QtHIlBaGvNIwNrorp3j5EoZM%2BybBEigsDXg%2B9To1w30%2BRbV8V3o6hyM5lpviYM4icmRBYVVjmA4hlWOE7aOFqcNAPA%3D%3D&a=http%3a%2f%2fcont-sv5-3.pandora.com%2fimages%2fpublic%2famz%2f0%2f2%2f0%2f9%2f098787069020_500W_500H.jpg&m=94f084d4cda66036692600d16036898d258a387c7a546916b33f7d907698ddf0e59d86721b571e2bfd793b4d25efc3cc856420f223031811&f=1&r=0&g=-5.93&x=000020aa', u'playlist_position': '4', u'duration': '0:05:22', u'position': '0:03:09', u'album_art': 'http://cont-sv5-3.pandora.com/images/public/amz/0/2/0/9/098787069020_500W_500H.jpg', u'metadata': '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="-1" parentID="-1" restricted="true"><res protocolInfo="pandora.com-pndrradio-http:*:audio/mpeg:*" duration="0:05:22">pndrradio-http://audio-ch1-t1-2-v4v6.pandora.com/access/?version=4&lid=3729520&token=slyAWqqgjJ8Bl3TgMB%2F03C4tamOHsLj%2BOQLq1T8xbiwSzkcoc253HeYbNFTcOP7K6Oy9GOq6tcaManOhyKDpECIiCENj7hjBsH%2FJVhJnDjLzyPRWjg8q0LtX1mn80yFiurvB1r0G0Be76YBZJUswLdWtNZtfYSO2y2WdYHSge8Ul2hML1MWvkv23AQjw9%2BAKPmIHXX8nUzfcfFfLfmyKk%2Flxwh2cf%2BvDrJIbnnVZfvlfirWhshENZDm7BBofIlUqR0qsPXWbYN5m7WD4bS3XCK4dedE0QtHIlBaGvNIwNrorp3j5EoZM%2BybBEigsDXg%2B9To1w30%2BRbV8V3o6hyM5lpviYM4icmRBYVVjmA4hlWOE7aOFqcNAPA%3D%3D&a=http%3a%2f%2fcont-sv5-3.pandora.com%2fimages%2fpublic%2famz%2f0%2f2%2f0%2f9%2f098787069020_500W_500H.jpg&m=94f084d4cda66036692600d16036898d258a387c7a546916b33f7d907698ddf0e59d86721b571e2bfd793b4d25efc3cc856420f223031811&f=1&r=0&g=-5.93&x=000020aa</res><r:streamContent></r:streamContent><upnp:albumArtURI>http://cont-sv5-3.pandora.com/images/public/amz/0/2/0/9/098787069020_500W_500H.jpg</upnp:albumArtURI><dc:title>The Funeral</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:creator>Band Of Horses</dc:creator><upnp:album>Everything All The Time</upnp:album></item></DIDL-Lite>'} | |
# pandora ad: | |
# {u'album': u'', u'artist': u'', u'title': ' ', u'uri': 'pndrradioad://http://t2-1.p-cdn.com/access/?version=4&lid=3729520&token=JWKN59qxNu2lJGzFLFmI4%2BbPJWonBt%2FM5B9I%2Bliu%2FUNPq7DkOKDU%2BjXEDimK4q3M5WlxBSaxUtp2Sa4tUbALw7FutSpYL6ndhgqeCSu3KOHm1gIHjg48lwngIcwbh1K9QBGUBAroEq2kep%2Fou977EuksnPUF1BbxRBLKziwUyfsDHgLvRADYtLiBDt2lwJb25X1kMCdrD%2BK4gEq7BUxLglha0VlsOB1MrhZmjcv8l8Tn0kFqAJiW6pY54e5f2MGueBFfCRL8bjKoYN99cZjsjPjfvBem5%2BfrGzsu%2FiN1VhNL3AzHo64Ytap1heZEER6wmcneMSj3PsvSKKRmlZYrmh3nCY%2F7s5VYZdqvpMKfLNs%3D&a=http%3a%2f%2fcont-1.p-cdn.com%2fimages%2fpublic%2fdevicead%2fs%2fm%2f2%2fr%2fdaarv2190082q3r2ms_500W_500H.jpg&m=ADU-3729520-6-15520-none-0-1423412705129%3aADU-3729520-6-15520-none-1-1423412705130%3aADU-3729520-6-15520-none-2-1423412705130&f=0&r=0&g=0.000&x=000020aa', u'playlist_position': '3', u'duration': '0:00:16', u'position': '0:00:03', u'album_art': 'http://cont-1.p-cdn.com/images/public/devicead/s/m/2/r/daarv2190082q3r2ms_500W_500H.jpg', u'metadata': '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="-1" parentID="-1" restricted="true"><res protocolInfo="pandora.com-pndrradioad:*:audio/mpeg:*" duration="0:00:16">pndrradioad://http://t2-1.p-cdn.com/access/?version=4&lid=3729520&token=JWKN59qxNu2lJGzFLFmI4%2BbPJWonBt%2FM5B9I%2Bliu%2FUNPq7DkOKDU%2BjXEDimK4q3M5WlxBSaxUtp2Sa4tUbALw7FutSpYL6ndhgqeCSu3KOHm1gIHjg48lwngIcwbh1K9QBGUBAroEq2kep%2Fou977EuksnPUF1BbxRBLKziwUyfsDHgLvRADYtLiBDt2lwJb25X1kMCdrD%2BK4gEq7BUxLglha0VlsOB1MrhZmjcv8l8Tn0kFqAJiW6pY54e5f2MGueBFfCRL8bjKoYN99cZjsjPjfvBem5%2BfrGzsu%2FiN1VhNL3AzHo64Ytap1heZEER6wmcneMSj3PsvSKKRmlZYrmh3nCY%2F7s5VYZdqvpMKfLNs%3D&a=http%3a%2f%2fcont-1.p-cdn.com%2fimages%2fpublic%2fdevicead%2fs%2fm%2f2%2fr%2fdaarv2190082q3r2ms_500W_500H.jpg&m=ADU-3729520-6-15520-none-0-1423412705129%3aADU-3729520-6-15520-none-1-1423412705130%3aADU-3729520-6-15520-none-2-1423412705130&f=0&r=0&g=0.000&x=000020aa</res><r:streamContent></r:streamContent><upnp:albumArtURI>http://cont-1.p-cdn.com/images/public/devicead/s/m/2/r/daarv2190082q3r2ms_500W_500H.jpg</upnp:albumArtURI><dc:title> </dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class></item></DIDL-Lite>'} | |
def isPandora (uri): return uri.startswith("pndrradio") | |
def isPandoraAd(uri): return uri.startswith("pndrradioad") | |
# take the hours:minutes:seconds (e.g. 0:03:12) format from the soco/pandora | |
# library for duration or position values and convert into raw seconds | |
def hmsToSecs(hms): | |
m = re.match(r'([0-9]+):([0-9]+):([0-9]+)', hms) | |
if m: | |
return int(m.group(1))*60*60 + int(m.group(2))*60 + int(m.group(3)) | |
else: | |
return 0 | |
if __name__ == '__main__': | |
# store the old volume for the zones that are currently being paused | |
# because o0 | |
# zone was (no longer) being paused by us | |
pausedZones = {} | |
debug = True | |
while True: | |
try: | |
if debug: print("Checking zones.") | |
# for each playing zone find the one with the smallest amount of seconds left until the current track ends | |
minTrackSecsLeft = 240 | |
for zone in soco.discover(): | |
playing = zone.get_current_transport_info()['current_transport_state'] == 'PLAYING' | |
if debug: print("Checking: %s (%s)" % ( zone.player_name, zone )) | |
#if debug: print(" %s" % ( zone.get_current_transport_info() )) | |
if not zone.is_coordinator: continue | |
if not playing: continue | |
current = zone.get_current_track_info() | |
uri = current['uri'] | |
#if debug: print(" URI: %s" % ( uri ) ) | |
print(" Current Track: %s (%s) / %s" % ( current['title'], current['album'], current['artist'] ) ) | |
if not isPandora(uri): | |
if debug: print(" Zone is not playing a Pandora radio station. Skipping.") | |
continue | |
dur = hmsToSecs( current['duration'] ) | |
pos = hmsToSecs( current['position'] ) | |
minTrackSecsLeft = min(minTrackSecsLeft, dur-pos) | |
if debug: print(" Current position: %s / %s ( %d s )" % ( current['position'], current['duration'], dur-pos )) | |
# no longer an ad and we'd previously muted this zone | |
if not isPandoraAd(uri) and zone.player_name in pausedZones and zone.volume == 1: | |
if debug: print(" Zone is muted and ad has finished. Unmuting.") | |
zone.volume = pausedZones[zone.player_name] | |
del(pausedZones[zone.player_name]) | |
# ad is playing, volume isn't muted - take the current volume and store it then mute | |
if isPandoraAd(uri) and zone.volume > 1: | |
if debug: print(" Zone was not muted and ad is playing. Muting!") | |
pausedZones[zone.player_name] = zone.volume | |
zone.volume = 1 | |
except Exception as ex: | |
if debug: print(ex) | |
pass | |
sleepTime = min( 30, max( minTrackSecsLeft-2, 1 )) | |
if debug: print("Zone with the next ending track has %d seconds left. Sleeping for %d seconds." % ( minTrackSecsLeft, sleepTime ) ) | |
time.sleep(sleepTime) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment