Created
March 20, 2015 01:16
-
-
Save Xkeeper0/64e343301ffaec82ae75 to your computer and use it in GitHub Desktop.
Hitbox.tv past-broadcast download script
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
#!/usr/bin/php | |
<?php | |
// Oh, um. | |
// This code is pretty bad. I will add comments and put it into a repo later maybe... | |
// It needs to probably be fixed up too, this is pretty ugly so far | |
// I just wanted to proof-of-concept it. | |
// | |
// This uses somewhat undocumented API pokes to get video details and download them | |
// You can only download your own recordings. May not work with VODs, who knows! | |
// | |
// Feel free to contact me about bugs/priase if it works: | |
// @xkeepah (twitter), xkeeper0 (github) | |
// may change at any time because ~hitbox~ | |
$thumb_domain = "http://edge.sf.hitbox.tv"; | |
print tty_bold() . tty_color(2) . "Hitbox Video Downloader". tty_color(7) ." [r1]". tty_reset() ."\n"; | |
$username = prompt("Username"); | |
$password = prompt("Password", "", true); | |
//$app = prompt("'app' [desktop]", "desktop"); | |
$app = "desktop"; // does this even matter | |
$loginRaw = web("http://api.hitbox.tv/auth/token", | |
array( | |
'login' => $username, | |
'pass' => $password, | |
'app' => $app, | |
) | |
); | |
$login = @json_decode($loginRaw); | |
if (!$login) { | |
die("ERR: API returned \"$loginRaw\"\n"); | |
} | |
print "Authenticated OK! Fetching video list...\n"; | |
$auth = $login->authToken; | |
$recordingsRaw = web("https://www.hitbox.tv/api/recordings/". $username ."?authToken=". $auth ."&limit=20&search="); | |
$recordings = @json_decode($recordingsRaw); | |
if (!$login) { | |
die("ERR: API returned \"$recordingsRaw\"\n"); | |
} | |
//var_dump($recordings); | |
print tty_bold() . tty_color(7) . "-- Recordings List -----------------------------------------------------------------------------\n" . tty_reset(); | |
foreach ($recordings as $num => $recording) { | |
print "[". tty_bold() . tty_color(1) . sprintf("%2d", $num + 1) . tty_reset() . "] ". | |
tty_bold() . tty_color(2) . $recording->rec_date_added . tty_reset() . " - ". | |
tty_bold() . tty_color(7) . $recording->rec_title . tty_reset() . " - ". | |
sprintf("(%dx%d, ", $recording->rec_info->width, $recording->rec_info->height) . | |
$recording->rec_duration .")\n". | |
" Thumbnail: ". tty_color(4) ."http://edge.sf.hitbox.tv" . $recording->thumbnail_large . tty_reset() . | |
"\n"; | |
} | |
print "\n"; | |
while (true) { | |
$videoInput = prompt("Download which video? [q quits]"); | |
if ($videoInput == "q") { | |
die("Exiting\n"); | |
} | |
$videoId = intval($videoInput) - 1; | |
if (!isset($recordings[$videoId])) { | |
print "Invalid video number.\n"; | |
continue; | |
} | |
break; | |
} | |
print "Selected video:\n"; | |
print tty_bold() . tty_color(2) . $recordings[$videoId]->rec_date_added . tty_reset() . " - ". | |
tty_bold() . tty_color(7) . $recordings[$videoId]->rec_title . tty_reset() . " - ". | |
sprintf("(%dx%d, ", $recordings[$videoId]->rec_info->width, $recordings[$videoId]->rec_info->height) . | |
$recordings[$videoId]->rec_duration .")\n". | |
" Thumbnail: ". tty_color(4) ."http://edge.sf.hitbox.tv" . $recordings[$videoId]->thumbnail_large . tty_reset() . | |
"\n"; | |
print "Fetching video information... "; | |
$videoDataRaw = web("http://www.hitbox.tv/api/player/recconfig/xkeeper/". $recordings[$videoId]->rec_session); | |
$videoData = @json_decode($videoDataRaw); | |
if (!$videoData) { | |
die("ERR: Invalid video data response: \"$videoDataRaw\""); | |
} | |
$playlist = web($videoData->clip->url); | |
$clipWebDir = preg_replace('#[^/]+$#si', "", $videoData->clip->url); | |
$playlistLines = explode("\n", $playlist); | |
$clipSegments = array(); | |
foreach($playlistLines as $line) { | |
$line = trim($line); | |
if ($line && $line{0} != "#") { | |
$clipSegments[] = $line; | |
} | |
} | |
$clipSegmentCount = count($clipSegments); | |
print "done.\n\nNeed to download $clipSegmentCount segments.\n"; | |
$autoConvert = prompt("Auto-run ffmpeg to convert to MP4? [Y/n]", "y"); | |
print "\nDownloading video segments...\n"; | |
$i = 0; | |
$fs = 0; | |
$file = $recordings[$videoId]->rec_session .".ts"; | |
foreach ($clipSegments as $segment) { | |
$i++; | |
print "[". tty_bold() . sprintf("%4.1f%%", ($i - 1) / $clipSegmentCount * 100) ." ". tty_color(2) . sprintf("%3d", $i) . tty_reset() . tty_color(2) . sprintf("/%3d", $clipSegmentCount) . tty_reset() ."] Downloading ... "; | |
$temp = file_get_contents($clipWebDir . $segment); | |
file_put_contents($file, $temp, FILE_APPEND); | |
$sz = strlen($temp); | |
$fs += $sz; | |
printf("%.2fMB (%.2fMB total)\n", $sz / 1048576, $fs / 1048576); | |
} | |
unset($temp); | |
print "Video saved to $file (total size: $fs bytes)\n"; | |
if (strtolower($autoConvert) == "y") { | |
print "Running ffmpeg (output will be $file.mp4) ...\n"; | |
$cmd = "ffmpeg -i ". escapeshellarg($file) ." -absf aac_adtstoasc -c copy ". escapeshellarg("$file.mp4"); | |
print "ffmpeg command: \" $cmd \"\n"; | |
shell_exec($cmd); | |
} | |
print "\nDone.\n"; | |
function web($url, $post = null) { | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_URL, $url); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_TIMEOUT, 60); | |
if ($post) { | |
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); | |
} | |
curl_setopt($ch, CURLOPT_VERBOSE, 0); | |
curl_setopt($ch, CURLOPT_HEADER, 0); | |
$result = curl_exec($ch); | |
curl_close($ch); | |
return $result; | |
} | |
function prompt($q, $default = "", $hide = false) { | |
print $q .": "; | |
if ($hide) { | |
shell_exec("stty -echo"); | |
} | |
$in = trim(fgets(STDIN)); | |
if ($hide) { | |
shell_exec("stty echo"); | |
print "\n"; // Hiding input eats the newline | |
} | |
return $in ? $in : $default; | |
} | |
function tty_bold() { | |
return "\x1b[1m"; | |
} | |
function tty_reset() { | |
return "\x1b[0m"; | |
} | |
function tty_color($color) { | |
return "\x1b[". (30 + $color) ."m"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment