Skip to content

Instantly share code, notes, and snippets.

@datguypiko
Forked from stefansundin/twitch-extension.lua
Created December 30, 2022 16:01
Show Gist options
  • Save datguypiko/45c2f87b408646229ffcaaee452de9c9 to your computer and use it in GitHub Desktop.
Save datguypiko/45c2f87b408646229ffcaaee452de9c9 to your computer and use it in GitHub Desktop.
VLC playlist parser for Twitch.tv - https://addons.videolan.org/p/1167220/
--[[
Twitch.tv extension v0.0.2 by Stefan Sundin
https://gist.github.com/stefansundin/c200324149bb00001fef5a252a120fc2
The only thing that this extension does is to act as a helper to seek to the
correct time when you open a twitch.tv url that contains a timestamp.
You must have the playlist parser installed as well!
Usage:
1. Install the playlist parser: https://addons.videolan.org/p/1167220/
2. Install this file in the lua/extensions/ directory:
- On Windows: %APPDATA%/vlc/lua/extensions/
- On Mac: $HOME/Library/Application Support/org.videolan.vlc/lua/extensions/
- On Linux: ~/.local/share/vlc/lua/extensions/
- On Linux (snap package): ~/snap/vlc/current/.local/share/vlc/lua/extensions/
To install the addon for all users, put the file here instead:
- On Windows: C:/Program Files/VideoLAN/VLC/lua/extensions/
- On Mac: /Applications/VLC.app/Contents/MacOS/share/lua/extensions/
- On Linux: /usr/lib/vlc/lua/extensions/
- On Linux (snap package): /snap/vlc/current/usr/lib/vlc/lua/extensions/
3. Open VLC and activate the extension via the menu.
NOTE: You must do this every time you restart VLC, as far as I know there is
no way for the extension to activate itself. :(
There is no feedback indicating that the extension was activated.
4. Open a twitch.tv url with a timestamp using "Open Network Stream..."
Download and install with one command on Mac:
curl -o "$HOME/Library/Application Support/org.videolan.vlc/lua/extensions/twitch-extension.lua" https://gist.githubusercontent.com/stefansundin/c200324149bb00001fef5a252a120fc2/raw/twitch-extension.lua
Download and install with one command on Linux:
curl -o ~/.local/share/vlc/lua/extensions/twitch-extension.lua https://gist.githubusercontent.com/stefansundin/c200324149bb00001fef5a252a120fc2/raw/twitch-extension.lua
Example url:
https://www.twitch.tv/gamesdonequick/video/113837699?t=23m56s
https://www.twitch.tv/videos/113837699?t=23m56s
https://player.twitch.tv/?video=v113837699&time=23m56s
Changelog:
- v0.0.2: Support new go.twitch.tv urls (beta site).
- v0.0.1: First version of extension.
--]]
require 'common'
function descriptor()
return {
title = "Twitch.tv Extension v0.0.2",
shortdesc = "Twitch.tv Extension v0.0.2",
version = "v0.0.2",
author = "Stefan Sundin",
url = "https://gist.github.com/stefansundin/c200324149bb00001fef5a252a120fc2",
description = "This extension is needed to support jumping to a twitch.tv timestamp indicated by ?t= in the URL. VLC extensions must be activated each time VLC is run. This is unfortunate and I have not found any workaround.",
capabilities = { "input-listener" }
}
end
function activate()
check_meta()
end
function input_changed()
check_meta()
end
-- The extension does not work if I name this string.starts, so weird..
function stringstarts(haystack, needle)
return string.sub(haystack, 1, string.len(needle)) == needle
end
function check_meta()
if vlc.input.is_playing() then
local item = vlc.item or vlc.input.item()
if item then
local meta = item:metas()
if meta and meta["url"] then
vlc.msg.info("Trying to parse t from: "..meta["url"])
if (stringstarts(meta["url"], "https://www.twitch.tv/") or stringstarts(meta["url"], "https://go.twitch.tv/") or stringstarts(meta["url"], "https://player.twitch.tv/")) and (string.find(meta["url"], "[?&]t=") or string.find(meta["url"], "[?&]time=")) then
local t = string.match(meta["url"], "[?&]t=([^&#]+)") or string.match(meta["url"], "[?&]time=([^&#]+)")
vlc.msg.info("t="..t)
local start = 0
local h = string.match(t, "(%d+)h")
if h then
-- vlc.msg.info("h: "..h)
start = start + tonumber(h)*3600
end
local m = string.match(t, "(%d+)m")
if m then
-- vlc.msg.info("m: "..m)
start = start + tonumber(m)*60
end
local s = string.match(t, "(%d+)s")
if s then
-- vlc.msg.info("s: "..s)
start = start + tonumber(s)
end
vlc.msg.info("Seeking to: "..start)
common.seek(start)
end
end
end
return true
end
end
--[[
Twitch.tv playlist parser v0.3.0 by Stefan Sundin
https://gist.github.com/stefansundin/c200324149bb00001fef5a252a120fc2
https://www.opencode.net/stefansundin/twitch.lua
https://addons.videolan.org/p/1167220/
Once installed, you can open a twitch.tv url using "Open Network Stream..."
Features:
- Load a channel and watch live: https://www.twitch.tv/speedgaming
- Load an archived video: https://www.twitch.tv/videos/113837699
Installation:
Put the file in the lua/playlist/ directory:
- On Windows: %APPDATA%/vlc/lua/playlist/
- On Mac: $HOME/Library/Application Support/org.videolan.vlc/lua/playlist/
- On Linux: ~/.local/share/vlc/lua/playlist/
- On Linux (snap package): ~/snap/vlc/current/.local/share/vlc/lua/playlist/
To install the addon for all users, put the file here instead:
- On Windows: C:/Program Files/VideoLAN/VLC/lua/playlist/
- On Mac: /Applications/VLC.app/Contents/MacOS/share/lua/playlist/
- On Linux: /usr/lib/vlc/lua/playlist/
- On Linux (snap package): /snap/vlc/current/usr/lib/vlc/lua/playlist/
On Linux, you can download and install with the following command:
mkdir -p ~/.local/share/vlc/lua/playlist/; curl -o ~/.local/share/vlc/lua/playlist/twitch.lua https://gist.githubusercontent.com/stefansundin/c200324149bb00001fef5a252a120fc2/raw/twitch.lua
On Mac, you can download and install with the following command:
mkdir -p "$HOME/Library/Application Support/org.videolan.vlc/lua/playlist/"; curl -o "$HOME/Library/Application Support/org.videolan.vlc/lua/playlist/twitch.lua" https://gist.githubusercontent.com/stefansundin/c200324149bb00001fef5a252a120fc2/raw/twitch.lua
Changelog:
- v0.3.0: Twitch finally completely shut down their old API. Because VLC scripts can't easily make POST requests it currently requires a proxy to work. To simplify things I removed all features besides loading a live channel and archived videos. I could potentially add back some of the features in the future but I am unsure if I have the will to do it.
- v0.2.3: Fix loading the list of a channel's old videos. Remove support for clips as it is broken and not easy to fix.
- v0.2.2: Fix 1080p on archived videos. Add audio only stream.
- v0.2.1: Skip live videos when loading /<channel>/videos.
- v0.2.0: Support new URLs. Most things seem to be working again.
- v0.1.3: Minor fix that prevented me from running this on Ubuntu 18.04 (snap package).
- v0.1.2: Support for /directory/game/<name>/videos/<type>.
- v0.1.1: Support for /<channel>/clips, /directory/game/<name>/clips. Add ability to load the next page.
- v0.1.0: Rewrote almost the whole thing. Support for /communities/<name>, /directory/game/<name>, /<channel>/videos/, collections.
- v0.0.6: Support new go.twitch.tv urls (beta site).
- v0.0.5: Fix a couple of minor issues.
- v0.0.4: Support new twitch.tv/videos/ urls.
- v0.0.3: Support for Twitch Clips.
- v0.0.2: You can now pick the stream quality you want. The twitch URL will expand to multiple playlist items.
Handy references:
https://github.com/videolan/vlc/blob/master/share/lua/README.txt
https://github.com/videolan/vlc/blob/7f6786ab6c8fb624726a63f07d79c23892827dfb/share/lua/playlist/appletrailers.lua#L34
--]]
function parse_json(str)
vlc.msg.dbg("Parsing JSON: " .. str)
local json = require("dkjson")
return json.decode(str)
end
function get_json(url)
vlc.msg.info("Getting JSON from " .. url)
local stream = vlc.stream(url)
local data = ""
local line = ""
if not stream then return false end
while true do
line = stream:readline()
if not line then break end
data = data .. line
end
return parse_json(data)
end
function get_streams(url, title)
vlc.msg.info("Getting items from " .. url)
-- #EXTM3U
-- #EXT-X-TWITCH-INFO:NODE="video-edge-c9010c.lax03",MANIFEST-NODE-TYPE="legacy",MANIFEST-NODE="video-edge-c9010c.lax03",SUPPRESS="false",SERVER-TIME="1483827093.91",USER-IP="76.94.205.190",SERVING-ID="4529b3c0570a46c8b3ed902f68b8368f",CLUSTER="lax03",ABS="false",BROADCAST-ID="24170411392",STREAM-TIME="5819.9121151",MANIFEST-CLUSTER="lax03"
-- #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="chunked",NAME="Source",AUTOSELECT=YES,DEFAULT=YES
-- #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2838000,RESOLUTION=1280x720,VIDEO="chunked"
local stream = vlc.stream(url)
if not stream then return false end
local items = {}
local name = "error"
-- local resolution = "error"
while true do
local line = stream:readline()
if not line then break end
if string.find(line, "^#.*NAME=") then
name = string.match(line, "NAME=\"?([a-zA-Z0-9_ \\(\\)]+)\"?")
if name == "1080p (source)" then
name = "1080p"
elseif name == "audio_only" then
name = "Audio Only"
end
-- elseif string.find(line, "^#.*RESOLUTION=") then
-- resolution = string.match(line, "RESOLUTION=\"?([0-9x]+)\"?")
elseif string.find(line, "^http") then
table.insert(items, { path=line, name=title.." ["..name.."]" })
-- Uncomment the line below to only have the first stream appear (i.e. best quality)
-- break
end
end
return items
end
function url_encode(str)
str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^%w %-%_%.%~])", function(c) return string.format("%%%02X", string.byte(c)) end)
str = string.gsub(str, " ", "+")
return str
end
function string.starts(haystack, needle)
return string.sub(haystack, 1, string.len(needle)) == needle
end
function probe()
return (vlc.access == "http" or vlc.access == "https") and (string.starts(vlc.path, "www.twitch.tv/") or string.starts(vlc.path, "go.twitch.tv/") or string.starts(vlc.path, "player.twitch.tv/"))
end
function parse()
local proxy_host = "https://vppp.fly.dev"
if string.find(vlc.path,"/video/") or string.find(vlc.path,"twitch.tv/videos/") or string.find(vlc.path,"video=") then
-- https://www.twitch.tv/gamesdonequick/video/113837699
-- https://www.twitch.tv/videos/113837699 (legacy url)
-- https://www.twitch.tv/gamesdonequick/v/113837699 (legacy url, will redirect to /videos/ so we don't need to check for it)
-- https://player.twitch.tv/?video=v113837699&parent=example.com ("v" is optional)
local video_id = string.match(vlc.path, "/video/(%d+)") or string.match(vlc.path, "/videos/(%d+)") or string.match(vlc.path, "video=v?(%d+)")
vlc.msg.info(string.format("video_id: %s", video_id))
local data = get_json(proxy_host.."/api/vods/"..video_id.."/access_token")
local url = "http://usher.twitch.tv/vod/"..video_id.."?player=twitchweb&nauthsig="..data["sig"].."&nauth="..url_encode(data["token"]).."&allow_audio_only=true&allow_source=true"
return get_streams(url, "Twitch Video: "..video_id)
else
-- https://www.twitch.tv/speedgaming
local channel = string.match(vlc.path, "twitch.tv/([^/?#]+)")
if channel == "directory" or channel == "recaps" then
-- https://www.twitch.tv/directory/game/Perfect%20Dark
-- https://www.twitch.tv/recaps/annual
return { { path="", name="Unsupported URL" } }
end
vlc.msg.info(string.format("channel: %s", channel))
local data = get_json(proxy_host.."/api/channels/"..channel.."/access_token")
local url = "http://usher.ttvnw.net/api/channel/hls/"..channel..".m3u8?player=twitchweb&token="..url_encode(data["token"]).."&sig="..data["sig"].."&allow_audio_only=true&allow_source=true&allow_spectre=true"
return get_streams(url, "Twitch Channel: "..channel)
end
end
URL=$(curl -sL https://www.twitch.tv/ | grep -oE 'https://static.twitchcdn.net/assets/core-[a-z0-9]+.js')
curl -sL "$URL" | grep -oE '"[a-z0-9]{30}"'
# old:
# find the client_id that is used on twitch.tv
# curl -sL https://web-cdn.ttvnw.net/global.js | grep -oP '"[a-z0-9]{31}"'
# on mac:
# curl -sL https://web-cdn.ttvnw.net/global.js | grep -oE '"[a-z0-9]{31}"'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment