Filter out VP9 streams, sort by quality
[playVideoOnDreamboxProxy.git] / www / functions.php
1 <?php
2 function getPageUrl()
3 {
4     global $argv, $argc;
5     if (php_sapi_name() == 'cli') {
6         if ($argc < 2) {
7             errorInput('No URL given as command line parameter');
8         }
9         $pageUrl = $argv[1];
10     } else if (!isset($_SERVER['CONTENT_TYPE'])) {
11         errorInput('Content type header missing');
12     } else if ($_SERVER['CONTENT_TYPE'] == 'text/plain') {
13         //Android app
14         $pageUrl = file_get_contents('php://input');
15     } else if ($_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded') {
16         //Web form
17         if (!isset($_POST['url'])) {
18             errorInput('"url" POST parameter missing');
19         }
20         $pageUrl = $_POST['url'];
21     } else {
22         errorInput('Content type is not text/plain but ' . $_SERVER['CONTENT_TYPE']);
23     }
24
25     $parts = parse_url($pageUrl);
26     if ($parts === false) {
27         errorInput('Invalid URL in POST data');
28     } else if (!isset($parts['scheme'])) {
29         errorInput('Invalid URL in POST data: No scheme');
30     } else if ($parts['scheme'] !== 'http' && $parts['scheme'] !== 'https') {
31         errorInput('Invalid URL in POST data: Non-HTTP scheme');
32     }
33     return $pageUrl;
34 }
35
36 function getYoutubeDlJson($pageUrl, $youtubedlPath)
37 {
38     $cmd = $youtubedlPath
39         . ' --no-playlist'//would otherwise cause multiple json blocks
40         . ' --quiet'
41         . ' --dump-json'
42         . ' ' . escapeshellarg($pageUrl);
43
44     $descriptors = [
45         1 => ['pipe', 'w'],//stdout
46         2 => ['pipe', 'w'],//stderr
47     ];
48     $proc = proc_open($cmd, $descriptors, $pipes);
49     if ($proc === false) {
50         errorOut('Error running youtube-dl');
51     }
52     $stdout = stream_get_contents($pipes[1]);
53     $stderr = stream_get_contents($pipes[2]);
54
55     $exitCode = proc_close($proc);
56
57     if ($exitCode === 0) {
58         //stdout contains the JSON data
59         return $stdout;
60     }
61
62     if (strlen($stderr)) {
63         $lines = explode("\n", trim($stderr));
64         $lastLine = end($lines);
65     } else {
66         $lines = explode("\n", trim($stdout));
67         $lastLine = end($lines);
68     }
69
70     if ($exitCode === 127) {
71         errorOut(
72             'youtube-dl not found at ' . $youtubedlPath,
73             '500 youtube-dl not found'
74         );
75     } else if (strpos($lastLine, 'Unsupported URL') !== false) {
76         errorOut(
77             'Unsupported URL  at ' . $pageUrl,
78             '406 Unsupported URL (No video found)'
79         );
80     }
81
82     errorOut('youtube-dl error: ' . $lastLine);
83 }
84
85 function extractVideoUrlFromJson($json)
86 {
87     $data = json_decode($json);
88     if ($data === null) {
89         errorOut('Cannot decode JSON: ' . json_last_error_msg());
90     }
91
92     $safeFormats = [];
93     foreach ($data->formats as $format) {
94         if (strpos($format->format, 'hls') !== false) {
95             //dreambox 7080hd does not play hls files
96             continue;
97         }
98         if (strpos($format->format, 'vp9') !== false) {
99             //dreambox 7080hd does not play VP9 video streams
100             continue;
101         }
102         if ($format->protocol == 'http_dash_segments') {
103             //split up into multiple small files
104             continue;
105         }
106         if ($format->ext == 'flv') {
107             //Internal data flow error
108             continue;
109         }
110         $safeFormats[] = $format;
111     }
112
113     $url = null;
114
115     //filter: best quality
116     usort($safeFormats, function ($a, $b) {
117         return ($b->quality ?? 0) - ($a->quality ?? 0);
118     });
119     foreach ($safeFormats as $format) {
120         $url = $format->url;
121         break;
122     }
123
124     if ($url === null) {
125         //use URL chosen by youtube-dl
126         $url = $data->url;
127     }
128
129     if ($url == '') {
130         errorOut(
131             'No video URL found',
132             '406 No video URL found'
133         );
134     }
135     return $url;
136 }
137
138 function playVideoOnDreambox($videoUrl, $dreamboxUrl)
139 {
140     ini_set('track_errors', 1);
141     $xml = @file_get_contents($dreamboxUrl . '/web/session');
142     if ($xml === false) {
143         if (!isset($http_response_header)) {
144             errorOut(
145                 'Error fetching dreambox web interface token: '
146                 . $GLOBALS['lastError']
147             );
148         }
149
150         list($http, $code, $message) = explode(
151             ' ', $http_response_header[0], 3
152         );
153         if ($code == 401) {
154             //dreambox web interface authentication has been enabled
155             errorOut(
156                 'Error: Web interface authentication is required',
157                 '401 Dreambox web authentication required'
158             );
159         } else {
160             errorOut(
161                 'Failed to fetch dreambox session token: ' . $php_errormsg,
162                 $code . ' ' . $message
163             );
164         }
165     }
166     $sx = simplexml_load_string($xml);
167     $token = (string) $sx;
168
169     $playUrl = $dreamboxUrl
170         . '/web/mediaplayerplay'
171         . '?file=4097:0:1:0:0:0:0:0:0:0:'
172         . str_replace('%3A', '%253A', rawurlencode($videoUrl));
173
174     $ctx = stream_context_create(
175         array(
176             'http' => array(
177                 'method'  => 'POST',
178                 'header'  => 'Content-type: application/x-www-form-urlencoded',
179                 'content' => 'sessionid=' . $token,
180                 //'ignore_errors' => true
181             )
182         )
183     );
184     $ret = file_get_contents($playUrl, false, $ctx);
185     if ($ret !== false) {
186         if (php_sapi_name() != 'cli') {
187             header('HTTP/1.0 200 OK');
188         }
189         echo "Video play request sent to dreambox\n";
190         exit(0);
191     } else {
192         errorOut(
193             'Failed to send video play request to dreambox: ' . $php_errormsg
194         );
195     }
196 }
197
198 function errorHandlerStore($number, $message, $file, $line)
199 {
200     $GLOBALS['lastError'] = $message;
201     return false;
202 }
203 $GLOBALS['lastError'] = null;
204 ?>