Last active
August 1, 2016 09:15
-
-
Save tausackhn/9cb96073b60b6c52edb8 to your computer and use it in GitHub Desktop.
Getting Twitch.tv live source playlist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Twitch Playlists | |
// @namespace taus-guit | |
// @description Getting m3u8 playlist on live twitch channel. | |
// @include *.twitch.tv/* | |
// @version 1 | |
// @grant GM_setClipboard | |
// @grant GM_addStyle | |
// @grant GM_xmlhttpRequest | |
// ==/UserScript== | |
GM_addStyle(" \ | |
#playlistPanel { \ | |
position:absolute; \ | |
top:20%; \ | |
left:60px; \ | |
color:#D3D3D3; \ | |
width:420px; \ | |
height:110px; \ | |
z-index:9002; \ | |
border-radius:3px; \ | |
border:1px solid #000; \ | |
background-color:#1e1e1e; \ | |
box-shadow:-1px 0 0 rgba(0,0,0,0.3),-2px 0 0 rgba(0,0,0,0.2),-3px 0 0 rgba(0,0,0,0.1),1px 0 0 rgba(0,0,0,0.3),2px 0 0 rgba(0,0,0,0.2),3px 0 0 rgba(0,0,0,0.1),0 -1px 0 rgba(0,0,0,0.3),0 -2px 0 rgba(0,0,0,0.2),0 -3px 0 rgba(0,0,0,0.1),0 1px 0 rgba(0,0,0,0.3),0 2px 0 rgba(0,0,0,0.2),0 3px 0 rgba(0,0,0,0.1); \ | |
} \ | |
#playlistPanel #header { \ | |
border-top-left-radius: 3px; \ | |
border-top-right-radius: 3px; \ | |
background-color: #282828; \ | |
width: 100%; \ | |
border-bottom: 1px solid #111; \ | |
font: 12.5px/1.5 \"Helvetica Neue\",Helvetica,Arial,sans-serif; \ | |
font-size: 20px; \ | |
color: #ADAFAE; \ | |
} \ | |
#playlistPanel #close { \ | |
font-size: 30px; \ | |
float: right; \ | |
right: 20px; \ | |
top: 0px; \ | |
position: absolute; \ | |
color: #FFF; \ | |
text-shadow: 0px 1px 0px #000; \ | |
opacity: 0.2; \ | |
} \ | |
#playlistPanel #close:hover, #playlistPanel #close:focus { \ | |
color: #FFF; \ | |
text-decoration: none; \ | |
cursor: pointer; \ | |
opacity: 0.4; \ | |
} \ | |
#playlistPanel #logo { \ | |
text-shadow: 1px 1px 3px #000; \ | |
font-size: 25px; \ | |
vertical-align: bottom; \ | |
padding: 10px; \ | |
padding-right: 15px; \ | |
} \ | |
#playlistPanel #svg_logo { \ | |
vertical-align: top; \ | |
fill: rgba(255, 255, 255, 0.5); \ | |
} \ | |
#playlistPanel .buttonsPanel { \ | |
padding: 10px; \ | |
} \ | |
#playlistPanel .playlist_button { \ | |
min-width: calc(20% - 10px); \ | |
margin: 5px; \ | |
} \ | |
#playlistPanel #live_text { \ | |
font-size: 10px; \ | |
visibility: hidden; \ | |
margin-left: 20px; \ | |
} \ | |
"); | |
function createPlaylistButton(name) { | |
panel = document.querySelector(".buttonsPanel"); | |
var button = document.createElement('button'); | |
button.className = "playlist_button button primary"; | |
button.value = name; | |
button.innerHTML = "<span>" + name + "</span>"; | |
button.disabled = "disabled"; | |
return button; | |
} | |
function getPlaylistByUrl(url) { | |
var reName = /\.tv\/([^\/]*)/; | |
var reNameId = /\.tv\/([^/]*)\/v\/([0-9]*)/; | |
if ((list = reNameId.exec(url)) != null) { | |
getVodPlaylist(list[1], list[2]); | |
} else { | |
list = reName.exec(url); | |
getLivePlaylist(list[1]); | |
} | |
} | |
function getLivePlaylist(name) { | |
var tokenURL = "https://api.twitch.tv/api/channels/" + name + "/access_token"; | |
var playlistURLs = []; | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: tokenURL, | |
onload: function(s) { | |
var token = JSON.parse(s.responseText); | |
var playlistsURL = "http://usher.justin.tv/api/channel/hls/" + name + ".m3u8" + | |
"?p=435238" + | |
"&sig=" + token.sig + | |
"&token=" + token.token + | |
"&allow_source=true" + | |
"&player=twitchweb"; | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: playlistsURL, | |
onload: function(response) { | |
s = response.responseText; | |
if (response.status != 404) { | |
var reURL = /NAME=\"([^\"]*)\"(?:.*\n)+?(http[\S]*)/g; | |
var match; | |
while ((match = reURL.exec(s)) !== null) { | |
playlistURLs.push({'name': match[1], 'url': match[2]}); | |
} | |
playlistURLs.forEach(function(item, i, arr) { | |
var button = document.querySelector(".playlist_button[value=\"" + item.name + "\"]"); | |
button.removeAttribute("disabled"); | |
button.onclick = function() { | |
GM_setClipboard(item.url, "text"); | |
notify(item.name + " playlist URL copied"); | |
} | |
}); | |
} else { | |
document.querySelector("#live_text").style.visibility = "visible"; | |
} | |
} | |
}); | |
} | |
}); | |
} | |
function getVodPlaylist(name, id) { | |
var tokenURL = "https://api.twitch.tv/api/vods/" + id + "/access_token"; | |
var playlistURLs = []; | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: tokenURL, | |
onload: function(s) { | |
var token = JSON.parse(s.responseText); | |
var playlistsURL = "http://usher.twitch.tv/vod/" + id + | |
"?nauthsig=" + token.sig + | |
"&nauth=" + token.token + | |
"&allow_source=true"; | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: playlistsURL, | |
onload: function(response) { | |
m3u8list = response.responseText; | |
if (response.status != 404) { | |
var reURL = /NAME=\"([^\"]*)\"(?:.*\n)+?(http[\S]*)/g; | |
var match; | |
while ((match = reURL.exec(m3u8list)) !== null) { | |
playlistURLs.push({'name': match[1], 'url': match[2]}); | |
} | |
playlistURLs.forEach(function(item, i, arr) { | |
var button = document.querySelector(".playlist_button[value=\"" + item.name + "\"]"); | |
button.removeAttribute("disabled"); | |
button.onclick = function() { | |
GM_setClipboard(item.url, "text"); | |
notify(item.name + " playlist URL copied"); | |
} | |
}); | |
} else { | |
document.querySelector("#live_text").style.visibility = "visible"; | |
} | |
} | |
}) | |
} | |
}) | |
} | |
function notify(text) { | |
// Notify when copied to clipboard | |
if (!("Notification" in window)) { | |
alert(text); | |
} else if (Notification.permission === "granted") { | |
// If it's okay let's create a notification | |
var notification = new Notification(text); | |
} else if (Notification.permission !== 'denied') { | |
Notification.requestPermission(function(permission) { | |
// If the user accepts, let's create a notification | |
if (permission === "granted") { | |
var notification = new Notification(text); | |
} | |
}); | |
} | |
}; | |
function addButton() { | |
var smallMoreButton = document.querySelector("#small_more"); | |
if (null != smallMoreButton) { | |
console.log('Call'); | |
var item = document.createElement('dd'); | |
item.id = "live_url"; | |
item.className = "warp__item"; | |
var button = document.createElement('a'); | |
button.className = "warp__tipsy"; | |
button.title = "Get live broadcast URL"; | |
button.innerHTML = '<figure class="warp__avatar"><svg viewBox="0 0 512 512" version="1.1" id="playlist_button" x="0px" y="0px" width="16px" height="16px"> \ | |
<path d="M14,434 14,479 164,479 164,434"></path> \ | |
<path d="M14,334 14,379 164,379 164,334"></path> \ | |
<path d="M14,234 14,279 284,279 284,234"></path> \ | |
<path d="M14,134 14,179 284,179 284,134"></path> \ | |
<path d="M14,34 14,79 284,79 284,34"></path> \ | |
<path d="M253.803,471.326c-20.471-5.189-39.48-20.199-44.87-41.26c-5.641-20.109,1.74-41.701,14.399-57.58 c23.49-29.4,63.79-44.471,100.78-37.52c0.3-98.411-0.02-196.831,0.16-295.241c8.92,13.15,18.55,26.01,30.59,36.51 c25.59,23.2,57.43,37.94,83.74,60.2c22.68,19.311,42.37,43.26,52.42,71.62c7.37,20.28,8.87,42.55,4.58,63.68 c-5.65,27.109-22.28,42.379-40.2,63.301c6.1-25.25,13.06-42.271,5.26-67.611c-5.189-17.57-16.58-32.82-30.76-44.209 c-16.21-13.09-35.2-22.13-54.27-30.23c-0.25,65.02-0.03,130.051-0.11,195.081c1,22.449-10.45,43.93-26.85,58.68 C323.553,469.467,286.843,479.816,253.803,471.326"></path> \ | |
</svg></figure>'; | |
var body = document.body || document.getElementsByTagName('body')[0]; | |
var playlistPanel = document.createElement('div'); | |
playlistPanel.id = "playlistPanel"; | |
playlistPanel.style = "display: none;"; | |
body.appendChild(playlistPanel); | |
playlistPanel.innerHTML = '<div id="header"> \ | |
<span id="logo"> \ | |
<svg viewBox="0 0 512 512" version="1.1" id="svg_logo" x="0px" y="0px" width="30px" height="45px"> \ | |
<path d="M14,434 14,479 164,479 164,434"></path> \ | |
<path d="M14,334 14,379 164,379 164,334"></path> \ | |
<path d="M14,234 14,279 284,279 284,234"></path> \ | |
<path d="M14,134 14,179 284,179 284,134"></path> \ | |
<path d="M14,34 14,79 284,79 284,34"></path> \ | |
<path d="M253.803,471.326c-20.471-5.189-39.48-20.199-44.87-41.26c-5.641-20.109,1.74-41.701,14.399-57.58 c23.49-29.4,63.79-44.471,100.78-37.52c0.3-98.411-0.02-196.831,0.16-295.241c8.92,13.15,18.55,26.01,30.59,36.51 c25.59,23.2,57.43,37.94,83.74,60.2c22.68,19.311,42.37,43.26,52.42,71.62c7.37,20.28,8.87,42.55,4.58,63.68 c-5.65,27.109-22.28,42.379-40.2,63.301c6.1-25.25,13.06-42.271,5.26-67.611c-5.189-17.57-16.58-32.82-30.76-44.209 c-16.21-13.09-35.2-22.13-54.27-30.23c-0.25,65.02-0.03,130.051-0.11,195.081c1,22.449-10.45,43.93-26.85,58.68 C323.553,469.467,286.843,479.816,253.803,471.326"></path> \ | |
</svg> \ | |
</span> \ | |
Broadcast .m3u8 urls \ | |
<span id="live_text">Channel is offline</span> \ | |
<span id="close">×</span></div> \ | |
<div class="buttonsPanel"></div>'; | |
qualityList = ["Source", "High", "Medium", "Low", "Mobile"]; | |
qualityList.forEach(function(item, i, arr) { | |
buttonsPanel = document.querySelector(".buttonsPanel"); | |
buttonsPanel.appendChild(createPlaylistButton(item)); | |
}); | |
playlistPanel.childNodes[0].lastChild.onclick = function() { | |
playlistPanel.style = "display: none;"; | |
buttons = document.querySelectorAll("button.playlist_button"); | |
for (var i = 0; i < buttons.length; ++i) { | |
buttons[i].disabled = "disabled"; | |
} | |
document.querySelector("#live_text").style.visibility = "hidden"; | |
}; | |
button.onclick = function() { | |
playlists = getPlaylistByUrl(document.URL); | |
playlistPanel.style = "display: block;"; | |
}; | |
item.appendChild(button); | |
smallMoreButton.parentNode.insertBefore(item, smallMoreButton); | |
} | |
} | |
window.setTimeout(addButton, 4000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment