vlc | branch: master | Pierre Ynard <[email protected]> | Sun Dec 1 09:31:28 2019 +0100| [3a12451bc2d565d29f21681dde6d3808ade304f5] | committer: Pierre Ynard
youtube.lua: support new stream parameter data structure Since at least last September, youtube has shown developments towards discontinuing the classic parameters that we currently rely on in the general case to play any video: the classic parameters are simply missing at least sometimes for some videos. Stream parameters remain available in another form under a new data structure, which seems available all the time. It seems likely that the classic parameters will in turn be phased out at some point. For now, we introduce support for the new-style parameters while keeping support for the classic ones. > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=3a12451bc2d565d29f21681dde6d3808ade304f5 --- share/lua/playlist/youtube.lua | 84 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/share/lua/playlist/youtube.lua b/share/lua/playlist/youtube.lua index 1f109b5e92..cc1386d56d 100644 --- a/share/lua/playlist/youtube.lua +++ b/share/lua/playlist/youtube.lua @@ -219,7 +219,7 @@ function stream_url( params, js_url ) return url end --- Parse and pick our video URL +-- Parse and pick our video stream URL (classic parameters) function pick_url( url_map, fmt, js_url ) for stream in string.gmatch( url_map, "[^,]+" ) do local itag = string.match( stream, "itag=(%d+)" ) @@ -230,6 +230,60 @@ function pick_url( url_map, fmt, js_url ) return nil end +-- Parse and pick our video stream URL (new-style parameters) +function pick_stream( stream_map, js_url ) + local pick = nil + + local fmt = tonumber( get_url_param( vlc.path, "fmt" ) ) + if fmt then + -- Legacy match from URL parameter + for stream in string.gmatch( stream_map, '{(.-)}' ) do + local itag = tonumber( string.match( stream, '"itag":(%d+)' ) ) + if fmt == itag then + pick = stream + break + end + end + else + -- Compare the different available formats listed with our + -- quality targets + local prefres = vlc.var.inherit( nil, "preferred-resolution" ) + local bestres = nil + + for stream in string.gmatch( stream_map, '{(.-)}' ) do + local height = tonumber( string.match( stream, '"height":(%d+)' ) ) + + -- Better than nothing + if not pick or ( height and ( not bestres + -- Better quality within limits + or ( ( prefres < 0 or height <= prefres ) and height > bestres ) + -- Lower quality more suited to limits + or ( prefres > -1 and bestres > prefres and height < bestres ) + ) ) then + bestres = height + pick = stream + end + end + end + + if not pick then + return nil + end + + -- Either the "url" or the "cipher" parameter is present, + -- depending on whether the URL signature is scrambled. + local cipher = string.match( pick, '"cipher":"(.-)"' ) + if cipher then + -- Scrambled signature: some assembly required + local url = stream_url( cipher, js_url ) + if url then + return url + end + end + -- Unscrambled signature, already included in ready-to-use URL + return string.match( pick, '"url":"(.-)"' ) +end + -- Probe function. function probe() return ( ( vlc.access == "http" or vlc.access == "https" ) @@ -301,6 +355,7 @@ function parse() js_url = string.gsub( js_url, "^//", vlc.access.."://" ) end + -- Classic parameters if not fmt then fmt_list = string.match( line, "\"fmt_list\": *\"(.-)\"" ) if fmt_list then @@ -311,11 +366,24 @@ function parse() url_map = string.match( line, "\"url_encoded_fmt_stream_map\": *\"(.-)\"" ) if url_map then + vlc.msg.dbg( "Found classic parameters for youtube video stream, parsing..." ) -- FIXME: do this properly url_map = string.gsub( url_map, "\\u0026", "&" ) path = pick_url( url_map, fmt, js_url ) end + -- New-style parameters + if not path then + local stream_map = string.match( line, '\\"formats\\":%[(.-)%]' ) + if stream_map then + vlc.msg.dbg( "Found new-style parameters for youtube video stream, parsing..." ) + stream_map = string.gsub( stream_map, '\\(["\\/])', '%1' ) + -- FIXME: do this properly + stream_map = string.gsub( stream_map, "\\u0026", "&" ) + path = pick_stream( stream_map, js_url ) + end + end + if not path then -- If this is a live stream, the URL map will be empty -- and we get the URL from this field instead @@ -354,6 +422,7 @@ function parse() elseif string.match( vlc.path, "/get_video_info%?" ) then -- video info API local line = vlc.readline() -- data is on one line only + -- Classic parameters local fmt = get_url_param( vlc.path, "fmt" ) if not fmt then local fmt_list = string.match( line, "&fmt_list=([^&]*)" ) @@ -365,10 +434,23 @@ function parse() local url_map = string.match( line, "&url_encoded_fmt_stream_map=([^&]*)" ) if url_map then + vlc.msg.dbg( "Found classic parameters for youtube video stream, parsing..." ) url_map = vlc.strings.decode_uri( url_map ) path = pick_url( url_map, fmt ) end + -- New-style parameters + if not path then + local stream_map = string.match( line, '%%22formats%%22%%3A%%5B(.-)%%5D' ) + if stream_map then + vlc.msg.dbg( "Found new-style parameters for youtube video stream, parsing..." ) + stream_map = vlc.strings.decode_uri( stream_map ) + -- FIXME: do this properly + stream_map = string.gsub( stream_map, "\\u0026", "&" ) + path = pick_stream( stream_map ) + end + end + if not path then -- If this is a live stream, the URL map will be empty -- and we get the URL from this field instead _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
