-
-
Save stefansundin/c200324149bb00001fef5a252a120fc2 to your computer and use it in GitHub Desktop.
| --[[ | |
| 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.1 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 | |
| - Load a clip: https://clips.twitch.tv/AmazonianKnottyLapwingSwiftRage | |
| This playlist parser utilizes a proxy since v0.3.0 since Twitch completely shut down their old API. | |
| VLC can't make POST requests so there isn't another way to work around the problem. | |
| For information about the proxy please go to https://github.com/stefansundin/media-resolver | |
| You can run the proxy on your own computer but you'll have to edit the proxy_host variable below. | |
| 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.1: Added back support for clips. | |
| - 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 | |
| --]] | |
| -- Set this variable to true to only add the first stream: | |
| local only_add_one_stream = false | |
| -- You can change the order of the streams using this variable: | |
| local preferred_streams = {} | |
| -- Examples: | |
| -- local preferred_streams = { "1080p", "720p" } | |
| -- local preferred_streams = { "360p", "160p", "720p60", "720p" } | |
| -- local preferred_streams = { "Audio Only" } | |
| -- | |
| -- Technical note: | |
| -- The sort algorithm is not stable. This means that streams not listed here may become jumbled by the sorting (the sorting only happens if this list has entries in it). | |
| -- You can modify this variable to only include certain streams: | |
| local allowed_streams = {} | |
| -- Examples: | |
| -- local allowed_streams = { "1080p" } | |
| -- local allowed_streams = { "360p", "720p" } | |
| -- local allowed_streams = { "360p" } | |
| -- It does a substring match, so you can use "1080p" to allow both "1080p" and "1080p60". | |
| -- You can modify this variable to skip certain streams: | |
| local skipped_streams = {} | |
| -- Examples: | |
| -- local skipped_streams = { "1080p", "Audio Only" } | |
| -- It does a substring match, so you can use "1080p" to block both "1080p" and "1080p60". | |
| 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(item) | |
| vlc.msg.info("Getting streams from " .. item.path) | |
| -- #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(item.path) | |
| if not stream then | |
| return { { path="", name="Error fetching streams" } } | |
| end | |
| local items = {} | |
| local name = "error" | |
| local add_stream = true | |
| -- 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 == "audio_only" then | |
| name = "Audio Only" | |
| end | |
| add_stream = true | |
| if #allowed_streams > 0 then | |
| add_stream = false | |
| for _, stream in ipairs(allowed_streams) do | |
| if string.find(name, stream) then | |
| add_stream = true | |
| end | |
| end | |
| end | |
| for _, stream in ipairs(skipped_streams) do | |
| if string.find(name, stream) then | |
| add_stream = false | |
| end | |
| end | |
| -- elseif string.find(line, "^#.*RESOLUTION=") then | |
| -- resolution = string.match(line, "RESOLUTION=\"?([0-9x]+)\"?") | |
| elseif string.find(line, "^http") and add_stream then | |
| -- Make a copy and overwrite some attributes | |
| local new_item = {} | |
| for k, v in pairs(item) do | |
| new_item[k] = v | |
| end | |
| new_item.path = line | |
| new_item.name = item.name.." ["..name.."]" | |
| table.insert(items, new_item) | |
| if only_add_one_stream then | |
| break | |
| end | |
| end | |
| end | |
| if #preferred_streams > 0 then | |
| table.sort(items, sort_streams) | |
| 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 proxy_resolve(url) | |
| -- Don't just copy this URL to your own app without understanding it. | |
| -- Do NOT use the "v=1" query parameter if you use this in another app! | |
| local proxy_host = "https://media-resolver.fly.dev" | |
| -- If you are running your own media resolver then comment out the line above and uncomment the line below: | |
| -- local proxy_host = "http://localhost:8080" | |
| return proxy_host.."/resolve?v=1&output=json&url="..url_encode(url) | |
| end | |
| function string.starts(haystack, needle) | |
| return string.sub(haystack, 1, string.len(needle)) == needle | |
| end | |
| function sort_streams(a, b) | |
| local i = table.index(preferred_streams, a.name) | |
| local j = table.index(preferred_streams, b.name) | |
| if i == nil and j == nil then | |
| return false | |
| elseif i ~= nil and j == nil then | |
| return true | |
| elseif i == nil and j ~= nil then | |
| return false | |
| end | |
| return i < j | |
| end | |
| function table.index(t, x) | |
| for i, v in pairs(t) do | |
| if string.find(x, v) then | |
| return i | |
| end | |
| end | |
| return nil | |
| 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/") or string.starts(vlc.path, "clips.twitch.tv/")) | |
| end | |
| function parse() | |
| local url = vlc.access.."://"..vlc.path | |
| local data = get_json(proxy_resolve(url)) | |
| if data.error then | |
| return { { path="", name="Error: "..data.error } } | |
| end | |
| local items = {} | |
| for _, item in ipairs(data) do | |
| if string.find(item.path, ".m3u8") then | |
| -- We resolve m3u8 files into quality options here because we won't get a chance to do it later | |
| for _, stream in ipairs(get_streams(item)) do | |
| table.insert(items, stream) | |
| end | |
| else | |
| table.insert(items, item) | |
| end | |
| end | |
| return items | |
| 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}"' |
Hello, how can I change the resolution? broadcast or video quality at 720p or 480p
@Khomchyk1992 I have pushed some updates to the script to allow the resolution to be picked more easily. I have NOT released this on addons.videolan.org yet, so for now please download the twitch.lua file manually here.
You have to edit a few variables in the code. They are named only_add_one_stream, preferred_streams, allowed_streams, and skipped_streams. Hopefully they should let you do what you want.
Please write a comment here if they solve your problem, and if you have any feedback. Thanks.
@Khomchyk1992 I have pushed some updates to the script to allow the resolution to be picked more easily. I have NOT released this on addons.videolan.org yet, so for now please download the
twitch.luafile manually here.You have to edit a few variables in the code. They are named
only_add_one_stream,preferred_streams,allowed_streams, andskipped_streams. Hopefully they should let you do what you want.Please write a comment here if they solve your problem, and if you have any feedback. Thanks.
Thank you very much, set your value, everything works.
Thanks. Works great.