Skip to content

Instantly share code, notes, and snippets.

@Xkeeper0
Created March 20, 2015 01:16
Show Gist options
  • Save Xkeeper0/64e343301ffaec82ae75 to your computer and use it in GitHub Desktop.
Save Xkeeper0/64e343301ffaec82ae75 to your computer and use it in GitHub Desktop.
Hitbox.tv past-broadcast download script
#!/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