about
[noxon-gateway.git] / www / transcode-cache.php
1 <?php
2 /**
3  * Transcode audio file URLs to .mp3, cache them and stream them while
4  * transcoding is in progress.
5  */
6 require_once __DIR__ . '/../src/header.php';
7
8 if (!isset($_GET['url'])) {
9     errorOut('url parameter missing');
10 }
11 $parts = parse_url($_GET['url']);
12 if ($parts === false || !isset($parts['scheme'])) {
13     errorOut('Invalid URL');
14 }
15 if ($parts['scheme'] !== 'http' && $parts['scheme'] !== 'https') {
16     errorOut('URL is neither http nor https');
17 }
18 $url = $_GET['url'];
19
20 if (!is_dir($cacheDir)) {
21     errorOut('Cache dir does not exist');
22 }
23 if (!is_writable($cacheDir)) {
24     errorOut('Cache dir not writable');
25 }
26
27 $cacheFileName = str_replace(array(':', '/'), '-', $url) . '.mp3';
28 $cacheFilePath = $cacheDir . $cacheFileName;
29
30 if (file_exists($cacheFilePath)) {
31     header('HTTP/1.0 302 Moved Temporarily');
32     header('Location: ' . $cacheDirUrl . urlencode($cacheFileName));
33     exit(1);
34 }
35
36 $cmd = 'ffmpeg'
37     . ' -loglevel error'
38     . ' -i ' . escapeshellarg($url)
39     . ' -f mp3'
40     . ' -';
41
42 $descriptorspec = array(
43     1 => array('pipe', 'w'),// stdout is a pipe that the child will write to
44     2 => array('pipe', 'w')//stderr
45 );
46
47 register_shutdown_function('shutdown');
48
49 $process = proc_open($cmd, $descriptorspec, $pipes);
50 if (is_resource($process)) {
51     $tmpCacheFile = tempnam(sys_get_temp_dir(), 'transcode-cache-');
52     $cacheHdl = fopen($tmpCacheFile, 'wb');
53     header('Content-type: audio/mpeg');
54     while ($data = fread($pipes[1], 1000)) {
55         //write cache
56         fwrite($cacheHdl, $data);
57         //output to browser
58         echo $data;
59         //TODO: maybe flush() and ob_flush();
60     }
61
62     $errors = stream_get_contents($pipes[2]);
63     fclose($pipes[1]);
64     fclose($pipes[2]);
65     $retval = proc_close($process);
66
67     fclose($cacheHdl);
68     if ($retval === 0) {
69         rename($tmpCacheFile, $cacheFilePath);
70     } else {
71         header('HTTP/1.0 500 Internal Server Error');
72         header('Content-type: text/plain');
73         echo "Error transcoding\n";
74         echo $errors . "\n";
75         unlink($tmpCacheFile);
76     }
77 }
78
79 function shutdown()
80 {
81     global $process, $pipes, $tmpCacheFile;
82
83     if (connection_aborted()) {
84         //end ffmpeg and clean temp file
85         fclose($pipes[1]);
86         fclose($pipes[2]);
87         proc_terminate($process);
88         unlink($tmpCacheFile);
89     }
90 }
91
92 function errorOut($msg)
93 {
94     header('HTTP/1.0 400 Bad request');
95     header('Content-type: text/plain');
96     echo $msg . "\n";
97     exit(1);
98 }
99 ?>