Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playback Stops in the Middle of Streaming #175

Open
jhamilt5 opened this issue May 26, 2017 · 9 comments
Open

Playback Stops in the Middle of Streaming #175

jhamilt5 opened this issue May 26, 2017 · 9 comments

Comments

@jhamilt5
Copy link

I'm writing an application that leverages both pychromecast and gmusicapi to create a collaborative music queue to stream to my Google Home. I send streaming URLs to my Google Home like so:

from gmusicapi import Mobileclient
import pychromecast
from getpass import getpass

email = input("Email: ")
pwd = getpass()
android_id = 'XXXXXXXXXXXXXXXX' # ID of registered google play music device
api = Mobileclient()
api.login(email, pwd, android_id)    # successful login

cast = pychromecast.Chromecast('192.168.10.104')
mc = cast.media_controller

track_id = 'Tpa22h67vzxazrkpbtmuu543h24'   # The Google Play Store ID for a song
stream_url = api.get_stream_url(track_id, device_id=android_id)   # yields a url pointing to a streaming source for an mp3
mc.play_media(stream_url, 'audio/mpeg')

My Google Home chimes, letting me know that a new app has started, and shortly after, the music begins playing. However, around 75% of the way through the song, playback stops, and it cannot be restarted with successive calls to play_media() or with voice commands.

According to this issue on gmusicapi it appears to be happening because the stream_url is only valid for ~90 seconds, and the song hasn't completely buffered.

Is it possible in play_media() to force a complete load of the song instead of a streamed buffer?

@anthonyzhub
Copy link

Hi,

I am having the same issue as well. However, everything works fine on a chromecast.

@stefan-sherwood
Copy link

stefan-sherwood commented Sep 5, 2019

This just started happening to me about a week ago with untouched code that has worked for years, almost exactly as shown above except that I'm using pychromecast.get_chromecasts() instead of explicitly providing the IP. It happens 100% of the time (previously never happened).

@gallmerci
Copy link

Same issue for me. The code was working long time but the music stops randomly. Does anyone already have a workaround or an idea how to debug this?

@stefan-sherwood
Copy link

I solved it by downloading the whole file and then playing from the local version. Because the download finishes before Google expires the link, it doesn't get cut off. A bit of a nuisance but it does work.

@gallmerci
Copy link

Thanks. But not really a satisfying solution :(

@stefan-sherwood
Copy link

It means that my code behaves as it did before Google broke it so I'm ok with it.

@gallmerci
Copy link

gallmerci commented Oct 11, 2019

Good point.
I implemented now a logic in my server application that tries to identify this situation by checking player state and idle reason. Then I just get a fresh URL and restart the song at the same position when it stopped playing.
Of course, you hear the pause but it is just for 1-2 secs and so far I am quite satisfied with this workaround :)

@anthonyzhub
Copy link

@gallmerci Great job!

@stefan-sherwood You downloaded the file? I have some songs on my computer that I use for testing, but Google Home automatically stops a song after ~10 seconds. I am aware that the link expires before the song finishes, so how were you able to overcome this problem?

@stefan-sherwood
Copy link

I'm using Python 3.7 on Windows 10. I stripped out a bunch of error handling to make it easier to read.

self.cache_and_stream_gmusic_track_on_chromecast( track_id )


# Serve locally cached songs using a subclassed BaseHTTPRequestHandler:
def do_GET( self ):
	track_id = self.get_track_id_from_url( self.path )
	file = self.get_music_fullpath( track_id )

	seek_to = 0
	if "Range" in self.headers:
		res = re.match( "(?P<units>[^=]*)=(?P<start>\\d+)-(?P<end>\\d*)", self.headers[ "Range" ] )
		if res:
			seek_to = int( res.group( "start" ) )
			self.logger.log( f"Requested byte offset: {seek_to}" )

	with open( file, 'rb' ) as f:
		if seek_to:
			print( f"Seeking to {seek_to}" )
			f.seek( seek_to )
		data = f.read()

	self.send_response( 200 )
	self.send_header( "Accept-Ranges", "bytes" )
	self.send_header( "Content-Type", "audio/mpeg" ) 
	self.send_header( "Content-Disposition", f"filename={file} )
	self.send_header( "Content-Length", f"{len( data )}" )

# Connect to Google Music API
def get_music_api( self ):
	self.music = gmusicapi.Mobileclient( debug_logging = False )
	if not music.oauth_login( device_id = music.FROM_MAC_ADDRESS ):
		self.logger.err( "GMusic login failed" )

def get_music_url( self, track_id ):
	return f"{self.server_ip}/{self.get_cache_path( track_id )}"

def get_music_fullpath( self, track_id ):
	return f"{self.library_loc}\\{self.get_cache_path( track_id )}"

# Retrieve and cache a song:
def get_cache_path( self, track_id ):
	from urllib.request import urlretrieve

	url = self.music.get_stream_url( track_id )
	path = f"library\\{store_id}"
	thing = urlretrieve( url, path )

	if thing[1].get_content_maintype() == 'audio':
		self.db.add_song_to_cache( store_id, thing[0] )
		return path
	else:
		self.logger.error( Fetch of Song ID {store_id} returned a non-audio type")

def get_stream_target( self, device_name ):
	import pychromecast
	devices = pychromecast.get_chromecasts()
	return next( (dev for dev in devices if dev.name == device_name), None )

def cache_and_stream_gmusic_track_on_chromecast( self, song_id ):
	url = self.get_music_url( song_id )
	self.streamer = self.get_stream_target( 'Whole House' )
	self.streamer.media_controller.play_media( song_url, 'audio/mpeg', metadata = song_info, current_time=offset )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants