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

Marvin Scholz epirat07 at gmail.com
Sun Sep 10 00:16:41 CEST 2017


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)



More information about the vlc-devel mailing list