Skip to content

Instantly share code, notes, and snippets.

@Seneral
Last active August 7, 2022 16:57
Show Gist options
  • Save Seneral/bd64281ae30a6010e97d956ecd63238e to your computer and use it in GitHub Desktop.
Save Seneral/bd64281ae30a6010e97d956ecd63238e to your computer and use it in GitHub Desktop.
DOESN'T WORK ANYMORE - for workaround see https://gist.github.com/p3g4asus/597050997e01f8fd1fcf473fe6545a4fModified VLC YouTube playlist parsing script to support playlists with more than 200 videos. Verified it works with 1000+ videos, no duplicates and all included (minus deleted videos). Official version claims to support 100+, but actually o…
--[[
Youtube playlist importer for VLC media player 1.1 and 2.0
Copyright 2012 Guillaume Le Maout
Authors: Guillaume Le Maout
Contact: http://addons.videolan.org/messages/?action=newmessage&username=exebetche
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
--]]
--[[
MODified by Kai Gillmann, 19.01.2013, kaigillmann@googlemail.com:
VLC HAS already a youtube importer, but not for playlists. IMO this mentioned one is
better than this one, because it opens the video in the best possible video resolution.
So i decided to remove all parts of the code which is not responsible for list handling.
Now this lua script parses the list, as wanted, but for each video opened, the vlc default
Youtube script is used, so the videos will be displayed properly.
--]]
--[[
Patched by Aaron Hill (https://github.com/seraku24), 2018-05-16:
The original script was failing in VLC 3.x due to an overzealous probe function.
This patch makes the probe function more restrictive to avoid false positives.
--]]
-- Helper function to get a parameter's value in a URL
function get_url_param( url, name )
local _, _, res = string.find( url, "[&?]"..name.."=([^&]*)" )
return res
end
-- Probe function.
function probe()
if vlc.access ~= "http" and vlc.access ~= "https" then
return false
end
return string.match(vlc.path:match("([^/]+)"),"%w+.youtube.com") and (
not string.match(vlc.path, "list_ajax") and string.match(vlc.path, "[?&]list="))
end
-- Parse function.
function parse()
if string.match( vlc.path, "list=" ) then
local playlist_parsed, playlistData, line, s, item
local p = {}
local id_ref = {}
local index = 0
local playlistID = get_url_param( vlc.path, "list" )
local videoID = get_url_param( vlc.path, "v" )
local playlistURL = "https://www.youtube.com/list_ajax?action_get_list=1&style=xml&list="..playlistID
local prevLoaded = 0
while true do
playlistData = ""
line = ""
s = nil
s = vlc.stream(playlistURL.."&index="..index)
while line do
playlistData = playlistData..line
line = s:readline()
end
playlist_parsed = nil
playlist_parsed = parse_xml(playlistData).root.video
if playlist_parsed == nil then
playlist_parsed = {}
end
for i, video in ipairs(playlist_parsed) do
if not id_ref[video.encrypted_id.CDATA] then
vlc.msg.dbg(i.." "..video.encrypted_id.CDATA)
id_ref[video.encrypted_id.CDATA] = true
item = nil
item = {}
if video.encrypted_id
and video.encrypted_id.CDATA then
item.path = "http://www.youtube.com/watch?v="..video.encrypted_id.CDATA
end
if video.title
and video.title.CDATA then
item.title = video.title.CDATA
end
if video.artist
and video.artist.CDATA then
item.artist = video.artist.CDATA
end
if video.thumbnail
and video.thumbnail.CDATA then
item.arturl = video.thumbnail.CDATA
end
if video.description
and video.description.CDATA then
item.description = video.description.CDATA
end
--~ item.rating = video.rating
table.insert (p, item)
end
end
if #p > prevLoaded or index == 100 then
vlc.msg.dbg("Playlist-Youtube: Loaded " ..#p.. " videos...")
index = index + 100
prevLoaded = #p
else
vlc.msg.dbg("Playlist-Youtube: Finished loading " ..#p.. " videos!")
return p
end
end
end
end
function parse_xml(data)
local tree = {}
local stack = {}
local tmp = {}
local tmpTag = ""
local level = 0
table.insert(stack, tree)
for op, tag, attr, empty, val in string.gmatch(
data,
"<(%p?)([^%s>/]+)([^>]-)(%/?)>[%s\r\n\t]*([^<]*)[%s\r\n\t]*") do
if op=="?" then
--~ DOCTYPE
elseif op=="/" then
if level>0 then
level = level - 1
table.remove(stack)
end
else
level = level + 1
if op=="!" then
stack[level]['CDATA'] = vlc.strings.resolve_xml_special_chars(
string.gsub(tag..attr, "%[CDATA%[(.+)%]%]", "%1"))
attr = ""
level = level - 1
elseif type(stack[level][tag]) == "nil" then
stack[level][tag] = {}
table.insert(stack, stack[level][tag])
else
if type(stack[level][tag][1]) == "nil" then
tmp = nil
tmp = stack[level][tag]
stack[level][tag] = nil
stack[level][tag] = {}
table.insert(stack[level][tag], tmp)
end
tmp = nil
tmp = {}
table.insert(stack[level][tag], tmp)
table.insert(stack, tmp)
end
if val~="" then
stack[level][tag]['CDATA'] = {}
stack[level][tag]['CDATA'] = vlc.strings.resolve_xml_special_chars(val)
end
if attr ~= "" then
stack[level][tag]['ATTR'] = {}
string.gsub(attr,
"(%w+)=([\"'])(.-)%2",
function (name, _, value)
stack[level][tag]['ATTR'][name] = value
end)
end
if empty ~= "" then
level = level - 1
table.remove(stack)
end
end
end
return tree
end
@mothership1101
Copy link

Thanks, keep us posted if someone updates the .lua .. I still can't get it to work in VLC

@p3g4asus
Copy link

Thank you for your instructions. Is re-sending Youtube-received cookies mandatory? That would be a very nasty problem to solve in vlc lua interface. Vlc lua api does not support specifying cookies and vlc lua plugin is compiled with LUA_DL_DLL NOT defined and so it does not allow using lua standard socket.http dll lib. Honestly I don't know how to solve this issue without re-compiling vlc.

Huh, I checked again and it's not necessary. I just kept sending them because previous APIs needed them (not sure which exactly - EDIT: comment aajx API needed them).
So yeah, should work without! Sorry for the confusion.

Thank you for testing. Another problem is the POST request. VLC lua api does not support it. Would GET work anyway? If I click show more link in the youtube playlist page for mobile, a GET is sent (for example: https://m.youtube.com/playlist?ctoken=4qmFsgI8EiRWTFBMOEJ3aXJhTVFMVktlZk02WVl3WHd1YTVnV2tvbVhIcS0aFENBRjZCbEJVT2tOQ1VRJTNEJTNE&pbj=1) so I assume GET should work anyway. Do you think it can work?

Thank you for your instructions. Is re-sending Youtube-received cookies mandatory? That would be a very nasty problem to solve in vlc lua interface. Vlc lua api does not support specifying cookies and vlc lua plugin is compiled with LUA_DL_DLL NOT defined and so it does not allow using lua standard socket.http dll lib. Honestly I don't know how to solve this issue without re-compiling vlc.

Huh, I checked again and it's not necessary. I just kept sending them because previous APIs needed them (not sure which exactly - EDIT: comment aajx API needed them).
So yeah, should work without! Sorry for the confusion.

Thank you for testing. Another problem is the POST request. VLC lua api does not support it. Would GET work anyway? If I click show more link in the youtube playlist page for mobile, a GET is sent (for example: https://m.youtube.com/playlist?ctoken=4qmFsgI8EiRWTFBMOEJ3aXJhTVFMVktlZk02WVl3WHd1YTVnV2tvbVhIcS0aFENBRjZCbEJVT2tOQ1VRJTNEJTNE&pbj=1) so I assume GET should work anyway. Do you think it can work?

Oh nice find. This API is apparently still open for both the desktop and mobile domain. Quick tests show it'll work just fine, even without cookies. You do need custom headers though with stuff extractred from the page (take a look at yt_getRequestHeadersYoutube without the cookie parameter).
Then, in javascript terms, the following returns the correct result you can parse:

fetch("https://youtube.com/playlist?ctoken=" + conToken + "&pbj=1", {
	method: "GET",
	headers: yt_getRequestHeadersYoutube("application/x-www-form-urlencoded", false)
});

Pretty easy. Although I wouldn't trust on it working forever, since they seem to be moving towards a different interface now (same but with POST, with a generic key in the query and the conToken in the body).

Unfortunately even this type of GET is impossible to reproduce with vlc lua API. I just realized that it supports only customizing referrer and user agent in http GET requests. And unfortunately additional x-youtube headers are mandatory. GET requests without them do not work. Would it be possible to make a simple GET request to FlagPlayer web interface to obtain youtube playlist details in json format?
Thanks in advance

@Seneral
Copy link
Author

Seneral commented Feb 16, 2021

Oh nice find. This API is apparently still open for both the desktop and mobile domain. Quick tests show it'll work just fine, even without cookies. You do need custom headers though with stuff extractred from the page (take a look at yt_getRequestHeadersYoutube without the cookie parameter).
Then, in javascript terms, the following returns the correct result you can parse:

fetch("https://youtube.com/playlist?ctoken=" + conToken + "&pbj=1", {
	method: "GET",
	headers: yt_getRequestHeadersYoutube("application/x-www-form-urlencoded", false)
});

Pretty easy. Although I wouldn't trust on it working forever, since they seem to be moving towards a different interface now (same but with POST, with a generic key in the query and the conToken in the body).

Unfortunately even this type of GET is impossible to reproduce with vlc lua API. I just realized that it supports only customizing referrer and user agent in http GET requests. And unfortunately additional x-youtube headers are mandatory. GET requests without them do not work. Would it be possible to make a simple GET request to FlagPlayer web interface to obtain youtube playlist details in json format?
Thanks in advance

That is quite worrying. I'd recommend creating an issue on VLC to try to fix this, because setting headers should really be in their radar.
Technically you could write a server that does these requests for you, but it'd be a lot of rewriting for the one I'm using and I'd have to open it up to other sources, which is generally a bad idea for puplic cors servers which so many options enabled as I have.
And finally, even if you did, YouTube has IP matching protection enabled on some copyrighted videos - VLC wouldnt be able to play back the URL you get, which means you'd have to stream the audio/video file over that server as well.
While there is really no sensible reason hlto have a separate server running for a host application like VLC. For a website, sure, they have unavoidable restrictions set by the browser, but in this case I'd push towards VLC improving their web request interface.

@p3g4asus
Copy link

Oh nice find. This API is apparently still open for both the desktop and mobile domain. Quick tests show it'll work just fine, even without cookies. You do need custom headers though with stuff extractred from the page (take a look at yt_getRequestHeadersYoutube without the cookie parameter).
Then, in javascript terms, the following returns the correct result you can parse:

fetch("https://youtube.com/playlist?ctoken=" + conToken + "&pbj=1", {
	method: "GET",
	headers: yt_getRequestHeadersYoutube("application/x-www-form-urlencoded", false)
});

Pretty easy. Although I wouldn't trust on it working forever, since they seem to be moving towards a different interface now (same but with POST, with a generic key in the query and the conToken in the body).

Unfortunately even this type of GET is impossible to reproduce with vlc lua API. I just realized that it supports only customizing referrer and user agent in http GET requests. And unfortunately additional x-youtube headers are mandatory. GET requests without them do not work. Would it be possible to make a simple GET request to FlagPlayer web interface to obtain youtube playlist details in json format?
Thanks in advance

That is quite worrying. I'd recommend creating an issue on VLC to try to fix this, because setting headers should really be in their radar.
Technically you could write a server that does these requests for you, but it'd be a lot of rewriting for the one I'm using and I'd have to open it up to other sources, which is generally a bad idea for puplic cors servers which so many options enabled as I have.
And finally, even if you did, YouTube has IP matching protection enabled on some copyrighted videos - VLC wouldnt be able to play back the URL you get, which means you'd have to stream the audio/video file over that server as well.
While there is really no sensible reason hlto have a separate server running for a host application like VLC. For a website, sure, they have unavoidable restrictions set by the browser, but in this case I'd push towards VLC improving their web request interface.

You are definitely right. I did not think about the implications my request would have. VLC http interface should definitely be extended to support at least setting headers. Before they add that support, playing youtube palylist will be nearly impossible.
The only way I can think of is using os.execute() api to execute:
youtube-dl -j --flat-playlist "https://<yourYoutubePlaylist>" > outfile.txt
and parsing outfile.txt
Obviously it will be ugly, slow and it will need youtube-dl.exe to be copied in the same dir as the lua playlist plugin file. But it should work. I will try this solution and I will post it when ready.

@p3g4asus
Copy link

p3g4asus commented Feb 17, 2021

Here you can find the updated version of this playlist script.
Disclaimer: this version works only under Windows. It can be easily ported but I have no way to test it on other OS at the moment.
Installation (under Windows): place this file in the lua playlist vlc folder together with JSON.lua and youtube-dl.exe

@Seneral
Copy link
Author

Seneral commented Feb 17, 2021

Looking good, will try it out later and somehow attempt to forward everyone there.
(Btw currently there seem to be two copies of the script in there)

@NeuroCPP
Copy link

@p3g4asus is it broken OR is it due to some youtube api updates recently?
doesn't seem to work for me though..
Stay Strong

@p3g4asus
Copy link

p3g4asus commented Jun 24, 2021

@p3g4asus is it broken OR is it due to some youtube api updates recently?
doesn't seem to work for me though..
Stay Strong

It still does work for most videos. I recommend you keep your youtube-dl executable file updated downloading it periodically from its official website

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment