import java.net.URL; import java.net.URLEncoder import org.serviio.library.metadata.* import org.serviio.library.online.* import groovy.json.* /** *

twitch/justin.tv Serviio plugin

* *

Usage instructions

*

Add streams as a Web Resource with * "http://www.twitch.tv/CHANNELNAME" as URL.

* *

VERSION HISTORY

*

* * @version 13 * @author bog * */ class Twitch extends WebResourceUrlExtractor { final Integer VERSION = 13 final String VALID_FEED_URL = "^https?://(?:[^\\.]*.)?twitch\\.tv/([a-zA-Z0-9_]+).*\$" final String VALID_VOD_URL = "^https?://(?:[^\\.]*.)?twitch\\.tv/([a-zA-Z0-9_]+)/(b|c)/(\\d+)[^\\d]*\$" final String TWITCH_HLS_API_PLAYLIST_URL = "http://usher.twitch.tv/select/%s.json?nauthsig=%s&nauth=%s&allow_source=true" final String TWITCH_VOD_API_URL = "https://api.twitch.tv/api/videos/%s%s" final String TWITCH_VOD_API_INFO = "https://api.twitch.tv/kraken/videos/%s%s" final String TWITCH_ACCESSTOKEN_API = "http://api.twitch.tv/api/channels/%s/access_token" final String TWITCH_STREAM_API = "https://api.twitch.tv/kraken/streams/%s" final static Boolean isWindows = System.getProperty("os.name").startsWith("Windows"); int getVersion() { return VERSION } String getExtractorName() { return 'twitch.tv' } boolean extractorMatches(URL feedUrl) { return (feedUrl ==~ VALID_FEED_URL) || (feedUrl ==~ VALID_VOD_URL) } WebResourceContainer extractItems(URL resourceUrl, int maxItemsToRetrieve) { def items, title def channelName = (String) (resourceUrl =~ VALID_FEED_URL)[0][1] // extract channel name from url if(resourceUrl ==~ VALID_VOD_URL) { def urlKind = (resourceUrl =~ VALID_VOD_URL)[0][2] // "b" or "c" def vodId vodId = (resourceUrl =~ VALID_VOD_URL)[0][3] as Integer title = "${channelName} VOD ${vodId}" items = extractVods(vodId, urlKind) } else if(resourceUrl ==~ VALID_FEED_URL) { // it's a stream title = "${channelName} Stream" items = extractHlsStream(channelName) } // create and fill the container def container = new WebResourceContainer() container.setTitle(title) container.setItems(items) return container } List extractVods(Integer vodId, String urlKind) { def type // type can be 'b' or 'a' depending on urlKind if (urlKind == "b") { type = "a" } else { type = "c" } def info = new JsonSlurper().parseText(new URL(String.format(TWITCH_VOD_API_INFO, type, vodId)).text) def title = info.title def preview = info.preview def json = new JsonSlurper().parseText(new URL(String.format(TWITCH_VOD_API_URL, type, vodId)).text) def items = [] json.chunks.each { chunk, part -> def ptNr = 1 part.each { data -> items += new WebResourceItem(title: "[${chunk}, ${ptNr}/${part.size()}] " + title, additionalInfo: [ expiresImmediately: false, cacheKey: title, url: data.url, thumbnailUrl: preview, live: false ]) ptNr++ } } return items } List extractHlsStream(String channelName) { def items = [] // prepare list def tokenJson = new JsonSlurper().parseText(new URL(String.format(TWITCH_ACCESSTOKEN_API, channelName.toLowerCase())).text) def token = tokenJson.token def sig = tokenJson.sig //getting stream thubnail def streamJson = new JsonSlurper().parseText(new URL(String.format(TWITCH_STREAM_API, channelName.toLowerCase())).text) def thumbnailUrl if (streamJson.stream) { thumbnailUrl = streamJson.stream.preview.medium } def playlist = new URL(String.format(TWITCH_HLS_API_PLAYLIST_URL, channelName.toLowerCase(), sig, token)).text def m = playlist =~ /(?s)NAME="([^"]*)".*?BANDWIDTH=(\d+).*?(http:\/\/.+?)[\n\r]/ while(m.find()) { // a generic string should be enough for identifying purposes def title = channelName + "-hls" + " [${m.group(1)}/${(Float.parseFloat(m.group(2))/1024) as Integer}K]" items += new WebResourceItem(title: title, additionalInfo: [ expiresImmediately: true, cacheKey: title, url: m.group(3), thumbnailUrl: thumbnailUrl, live: true ]) } return items } ContentURLContainer extractUrl(WebResourceItem arg0, PreferredQuality arg1) { def c = new ContentURLContainer() if(arg0 != null) { c.setExpiresImmediately(arg0.additionalInfo.expiresImmediately) c.setCacheKey(arg0.additionalInfo.cacheKey) c.setContentUrl(arg0.additionalInfo.url) c.setLive(arg0.additionalInfo.live) c.setThumbnailUrl(arg0.additionalInfo.thumbnailUrl) } return c } static void main(args) { Twitch twitch = new Twitch() def url = "" if(!args[0].contains("http")) url = "http://www.twitch.tv/" + args[0] else url = args[0] twitch.extractItems(new URL(url), 123).getItems().each { it-> ContentURLContainer result = twitch.extractUrl(it, PreferredQuality.HIGH) println result } } }