[vlc-devel] [PATCH] lua: Add Twitch.tv playlist script

Jean-Baptiste Kempf jb at videolan.org
Sun Sep 10 00:28:23 CEST 2017


Great idea.

On Sun, 10 Sep 2017, at 00:16, Marvin Scholz wrote:
> Add a script which can handle twitch.tv stream or video urls
> ---
>  share/Makefile.am             |   2 +
>  share/lua/playlist/twitch.lua | 269
>  ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 271 insertions(+)
>  create mode 100644 share/lua/playlist/twitch.lua
> 
> diff --git a/share/Makefile.am b/share/Makefile.am
> index 82f8da5ac3..773ebf843a 100644
> --- a/share/Makefile.am
> +++ b/share/Makefile.am
> @@ -149,6 +149,7 @@ nobase_vlcdata_DATA += \
>  	lua/playlist/vimeo.luac \
>  	lua/playlist/vocaroo.luac \
>  	lua/playlist/youtube.luac \
> +       lua/playlist/twitch.luac \
>  	lua/sd/fmc.luac \
>  	lua/sd/icecast.luac \
>  	lua/sd/icast.luac \
> @@ -290,6 +291,7 @@ EXTRA_DIST += \
>  	lua/playlist/vimeo.lua \
>  	lua/playlist/vocaroo.lua \
>  	lua/playlist/youtube.lua \
> +       lua/playlist/twitch.lua \
>  	lua/playlist/zapiks.lua \
>  	lua/sd/README.txt \
>  	lua/sd/fmc.lua \
> diff --git a/share/lua/playlist/twitch.lua
> b/share/lua/playlist/twitch.lua
> new file mode 100644
> index 0000000000..519ee21a2c
> --- /dev/null
> +++ b/share/lua/playlist/twitch.lua
> @@ -0,0 +1,269 @@
> +--[[
> +Resolve Twitch channel and video URLs to the actual stream URL
> +
> + $Id$
> + Copyright © 2017 the VideoLAN team
> +
> + Author: Marvin Scholz <epirat07 at gmail dot com>
> +
> + 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.
> +--]]
> +
> +-- Probe function
> +function probe()
> +    return (vlc.access == "http" or vlc.access == "https")
> +        and ( vlc.path:match("^www%.twitch%.tv/videos/.+") or
> +              vlc.path:match("^www%.twitch%.tv/.+") )
> +end
> +
> +-- Download and parse a JSON document from the specified URL
> +-- Returns: obj, pos, err (see dkjson docs)
> +function parse_json(url)
> +    local json   = require("dkjson")
> +    local stream = vlc.stream(url)
> +    local string = ""
> +    local line   = ""
> +
> +    if not stream then
> +        return nil, nil, "Failed creating VLC stream"
> +    end
> +
> +    while true do
> +        line = stream:readline()
> +
> +        if not line then
> +            break
> +        end
> +
> +        string = string .. line
> +    end
> +
> +    return json.decode(string)
> +end
> +
> +-- Request access token from Twitch
> +-- Returns: token, signature, err
> +function get_access_token(channel)
> +    if (channel == nil or channel == "") then
> +        return nil, nil, "Invalid channel"
> +    end
> +
> +    local url = "https://api.twitch.tv/api/channels/" .. channel ..
> "/access_token?client_id=jzkbprff40iqj646a697cyrvl0zt2m6"
> +    local obj, pos, err = parse_json(url)
> +
> +    if err then
> +        return nil, nil, "Error getting JSON object: " .. err
> +    end
> +
> +    -- In case of error, the API will return an object with
> +    -- error and message given
> +    if obj.error then
> +        local err = "Access token error: " .. obj.error
> +        if obj.message then
> +            err = err .. " (" .. obj.message .. ")"
> +        end
> +        return nil, nil, err
> +    end
> +
> +    -- Check if API response matches expectations (sig and token)
> +    if (obj.token == nil) or (obj.sig == nil) then
> +        return nil, nil, "Access token error: Invalid API response
> (token or sig missing)"
> +    end
> +
> +    return obj.token, obj.sig, nil
> +end
> +
> +-- Request stream info from Twitch
> +-- Returns: obj, err
> +function get_stream_info(channel)
> +    if (channel == nil or channel == "") then
> +        return nil, "Invalid channel"
> +    end
> +
> +    local test = vlc.strings.encode_uri_component("foo%bar")
> +    vlc.msg.err(test)
> +
> +    local url = "https://api.twitch.tv/api/channels/" .. channel ..
> "?client_id=jzkbprff40iqj646a697cyrvl0zt2m6"
> +    local obj, pos, err = parse_json(url)
> +
> +    if err then
> +        return nil, "Error getting JSON object: " .. err
> +    end
> +
> +    -- In case of error, the API will return an object with
> +    -- error and message given
> +    if obj.error then
> +        local err = "Stream info error: " .. obj.error
> +        if obj.message then
> +            err = err .. " (" .. obj.message .. ")"
> +        end
> +        return nil, err
> +    end
> +
> +    return obj, nil
> +end
> +
> +-- Make a request to the Twitch API endpoint given by url
> +-- Returns: obj, err
> +function twitch_api_req(url)
> +    local obj, pos, err = parse_json(url ..
> "?client_id=jzkbprff40iqj646a697cyrvl0zt2m6")
> +
> +    if err then
> +        return nil, "Error getting JSON object: " .. err
> +    end
> +
> +    -- In case of error, the API will return an object with
> +    -- error and message given
> +    if obj.error then
> +        local err = "Twitch API error: " .. obj.error
> +        if obj.message then
> +            err = err .. " (" .. obj.message .. ")"
> +        end
> +        return nil, err
> +    end
> +
> +    return obj, nil
> +end
> +
> +function handle_stream()
> +
> +end
> +
> +-- Parser for twitch video urls
> +function parse_video()
> +    local playlist = {}
> +    local item = {}
> +    local url, obj, err = nil
> +
> +    -- Parse video id out of url
> +    local video_id = vlc.path:match("^www%.twitch%.tv/videos/(.+)")
> +
> +    if video_id == nil then
> +        vlc.msg.err("Twitch: Failed to parse twitch url for video id")
> +        return playlist
> +    end
> +
> +    vlc.msg.dbg("Twitch: Loading video url for " .. video_id)
> +
> +    -- Request video token (required for the video stream)
> +    url = "https://api.twitch.tv/api/vods/" ..  video_id ..
> "/access_token"
> +    obj, err = twitch_api_req(url)
> +
> +    if err then
> +        vlc.msg.err("Error getting request token from Twitch: " .. err)
> +        return playlist
> +    end
> +
> +    -- Construct stream url
> +    local stream_url = "http://usher.twitch.tv/vod/" .. video_id
> +    stream_url = stream_url .. "?player=twitchweb"
> +    stream_url = stream_url .. "&nauth=" ..
> vlc.strings.encode_uri_component(obj.token)
> +    stream_url = stream_url .. "&nauthsig=" .. obj.sig
> +    stream_url = stream_url ..
> "&allow_audio_only=true&allow_source=true"
> +
> +    item["path"]   = stream_url
> +
> +    -- Set base information
> +    item["name"]   = "Twitch: " .. video_id
> +
> +    -- Request video information
> +    url = "https://api.twitch.tv/kraken/videos/v" .. video_id
> +    obj, err = twitch_api_req(url)
> +
> +    if err then
> +        vlc.msg.warn("Error getting video info from Twitch: " .. err)
> +        table.insert(playlist, item)
> +        return playlist
> +    end
> +
> +    -- Overwrite with now obtained better info
> +    item["name"]        = "Twitch: " .. obj.title
> +    item["artist"]      = obj.channel.display_name
> +    item["description"] = obj.description
> +    item["url"]         = vlc.path
> +
> +    table.insert(playlist, item)
> +
> +    return playlist
> +end
> +
> +-- Parser for twitch stream urls
> +function parse_stream()
> +    local playlist = {}
> +    local item = {}
> +    local url, obj, err = nil
> +
> +    -- Parse channel name out of url
> +    local channel = vlc.path:match("^www%.twitch%.tv/(.+)")
> +
> +    if channel == nil then
> +        vlc.msg.err("Twitch: Failed to parse twitch url for channel
> name")
> +        return playlist
> +    end
> +
> +    vlc.msg.dbg("Twitch: Loading stream url for " .. channel)
> +
> +    -- Request channel token (required for the stream m3u8)
> +    url = "https://api.twitch.tv/api/channels/" .. channel ..
> "/access_token"
> +    obj, err = twitch_api_req(url)
> +
> +    if err then
> +        vlc.msg.err("Error getting request token from Twitch: " .. err)
> +        return playlist
> +    end
> +
> +    -- Construct stream url
> +    local stream_url = "http://usher.twitch.tv/api/channel/hls/" ..
> channel .. ".m3u8"
> +    stream_url = stream_url .. "?player=twitchweb"
> +    stream_url = stream_url .. "&token=" ..
> vlc.strings.encode_uri_component(obj.token)
> +    stream_url = stream_url .. "&sig=" .. obj.sig
> +    stream_url = stream_url ..
> "&allow_audio_only=true&allow_source=true&type=any"
> +
> +    item["path"]   = stream_url
> +
> +    -- Set base information
> +    item["name"]   = "Twitch: " .. channel
> +    item["artist"] = channel
> +
> +    -- Request channel information
> +    url = "https://api.twitch.tv/api/channels/" .. channel
> +    obj, err = twitch_api_req(url)
> +
> +    if err then
> +        vlc.msg.warn("Error getting channel info from Twitch: " .. err)
> +        table.insert(playlist, item)
> +        return playlist
> +    end
> +
> +    -- Overwrite with now obtained better info
> +    item["name"]        = "Twitch: " .. obj.display_name
> +    item["nowplaying"]  = obj.display_name .. " playing " .. obj.game
> +    item["artist"]      = obj.display_name
> +    item["description"] = obj.status
> +    item["url"]         = vlc.path
> +
> +    table.insert(playlist, item)
> +
> +    return playlist
> +end
> +
> +-- Parse function
> +function parse()
> +    if vlc.path:match("twitch%.tv/videos/.+") then
> +        return parse_video()
> +    else
> +        return parse_stream()
> +    end
> +end
> -- 
> 2.11.0 (Apple Git-81)
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


-- 
Jean-Baptiste Kempf -  President
+33 672 704 734


More information about the vlc-devel mailing list