#!/usr/bin/env lua --Copyright (c) 2015 Igor Bogomazov --Permission is hereby granted, free of charge, to any person obtaining a copy --of this software and associated documentation files (the "Software"), to deal --in the Software without restriction, including without limitation the rights --to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --copies of the Software, and to permit persons to whom the Software is --furnished to do so, subject to the following conditions: --The above copyright notice and this permission notice shall be included in --all copies or substantial portions of the Software. --THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --SOFTWARE. -- 2015051100 edition -- Info: gopher://ygrex.ru/1/debian/parse-youtube/ local curl = assert(require "curl"); local rex = assert(require "rex_gnu"); -- curl callback local function http_recv(dest, input) dest[#dest + 1] = input; return #input; end; -- download a page specified by URL local function getpage(url) local c = assert(curl.easy_init()); local head, body = {}, {}; c:setopt(curl.OPT_FOLLOWLOCATION, 1); c:setopt(curl.OPT_URL, url); c:setopt( curl.OPT_HEADERFUNCTION, function(...) return http_recv(head, ...) end ); c:setopt( curl.OPT_WRITEFUNCTION, function(...) return http_recv(body, ...) end ); assert(c:perform()); head = table.concat(head); body = table.concat(body); if not rex.find(head, [[^HTTP/[0-9]\.[0-9] 200 ]]) then io.stderr:write( ("HTTP 200 expected, received:\n%s\n"):format(head) ); os.exit(1); end; return body; end; -- split a string local function split(str, sep) local r = {}; local idx, a, b; repeat idx = (b or 0) + 1; a, b = str:find(sep, idx, true); if b then r[#r + 1] = str:sub(idx, b - #sep); else r[#r + 1] = str:sub(idx); end; until a == nil; return r; end; -- convert array of "key=value" to associative array pairs local function assoc(array) local r = {}; for i = 1, #array do local key, value = array[i]:match "^([^=]+)=(.*)$"; if key then r[key] = value; else io.stderr:write(("k=v parse failure: %s.\n"):format( array[i] )); end; end; return r; end; -- extract a stream map data local function parse_page(body) local pattern = [["url_encoded_fmt_stream_map"[ \t]*:[ \t]*"([^"]+)"]] local raw = rex.match(body, pattern); if not raw then io.stderr:write( ("Pattern not found on the page: %s.\n"):format( pattern ) ); os.exit(1); end; local map = split(raw, ","); for imap = 1, #map do map[imap] = assoc(split(curl.unescape(map[imap]), [[\u0026]])); end; return map; end; -- show parsed stream map data local function show(map) for ivar = 1, #map do local var = map[ivar]; print(" type:", var["type"]); print(" qlty:", var["quality"]); print(" conn:", var["conn"]); print("stream:", var["stream"]); print(" url:", var["url"]); print(); end; end; -- entry point local function main(...) local url = ...; show(parse_page(getpage(url))); end; return main(...);