~ubuntu-branches/debian/sid/ampache/sid

« back to all changes in this revision

Viewing changes to play/index.php

  • Committer: Package Import Robot
  • Author(s): Charlie Smotherman
  • Date: 2013-08-27 13:19:48 UTC
  • mfrom: (1.2.9)
  • Revision ID: package-import@ubuntu.com-20130827131948-1czew0zxn6u70dtv
Tags: 3.6-rzb2752+dfsg-1
* New upsteam snapshot.  Contains important bug fixes to the installer.
* Correct typo in ampache-common.postrm.
* Remove courtousy copy of php-getid3, during repack.  Closes: #701526
* Update package to use dh_linktree to make the needed sym links to the
  needed system libs that were removed during repack.
* Update debian/rules to reflect upstreams removing/moving of modules.
* Update debian/ampache-common.install to reflect upstreams removal of files.
* Updated to use new apache2.4 API. Closes: #669756
* Updated /debian/po/de.po thx David Prévot for the patch.  Closes:  #691963
* M3U import is now ordered, fixed upstream.  Closes: #684984
* Text input area has been resized so IPv6 addresses will now fit, fixed
  upstream.  Closes:  #716230
* Added ampache-common.preinst to make sure that the courtousy copies of code
  dirs are empty so dh_linktree can do it's magic on upgrades.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
<?php
2
 
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
 
2
/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
3
3
/**
4
 
 * Play
5
 
 *
6
4
 *
7
5
 * LICENSE: GNU General Public License, version 2 (GPLv2)
8
 
 * Copyright (c) 2001 - 2011 Ampache.org All Rights Reserved
 
6
 * Copyright 2001 - 2013 Ampache.org
9
7
 *
10
8
 * This program is free software; you can redistribute it and/or
11
9
 * modify it under the terms of the GNU General Public License v2
20
18
 * along with this program; if not, write to the Free Software
21
19
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
20
 *
23
 
 * @package     Ampache
24
 
 * @copyright   2001 - 2011 Ampache.org
25
 
 * @license     http://opensource.org/licenses/gpl-2.0 GPLv2
26
 
 * @link        http://www.ampache.org/
27
21
 */
28
22
 
29
23
/*
38
32
ob_end_clean();
39
33
 
40
34
/* These parameters had better come in on the url. */
41
 
$uid            = scrub_in($_REQUEST['uid']);
42
 
$oid            = $_REQUEST['song'] ? scrub_in($_REQUEST['song']) : scrub_in($_REQUEST['oid']);
43
 
$sid            = scrub_in($_REQUEST['ssid']);
44
 
$xml_rpc        = scrub_in($_REQUEST['xml_rpc']);
45
 
$video          = make_bool($_REQUEST['video']);
 
35
$uid         = scrub_in($_REQUEST['uid']);
 
36
$oid         = $_REQUEST['oid']
 
37
            // FIXME: Any place that doesn't use oid should be fixed
 
38
            ? scrub_in($_REQUEST['oid'])
 
39
            : scrub_in($_REQUEST['song']);
 
40
$sid         = scrub_in($_REQUEST['ssid']);
 
41
$video        = make_bool($_REQUEST['video']);
 
42
$type        = scrub_in($_REQUEST['type']);
 
43
$transcode_to   = scrub_in($_REQUEST['transcode_to']);
 
44
 
 
45
if ($video) {
 
46
    // FIXME: Compatibility hack, should eventually be removed
 
47
    $type = 'video';
 
48
}
 
49
 
 
50
if (!$type) {
 
51
    // FIXME: Compatibility hack, should eventually be removed
 
52
    $type = 'song';
 
53
}
 
54
 
 
55
if ($type == 'playlist') {
 
56
    $playlist_type = scrub_in($_REQUEST['playlist_type']);
 
57
    $oid = $sid;
 
58
}
46
59
 
47
60
/* This is specifically for tmp playlist requests */
48
 
$demo_id        = scrub_in($_REQUEST['demo_id']);
49
 
$random         = scrub_in($_REQUEST['random']);
50
 
 
51
 
// Parse byte range request
52
 
$n = sscanf($_SERVER['HTTP_RANGE'], "bytes=%d-%d",$start,$end);
 
61
$demo_id    = scrub_in($_REQUEST['demo_id']);
 
62
$random        = scrub_in($_REQUEST['random']);
53
63
 
54
64
/* First things first, if we don't have a uid/oid stop here */
55
65
if (empty($oid) && empty($demo_id) && empty($random)) {
56
 
        debug_event('play', 'No object UID specified, nothing to play', 2);
57
 
        header('HTTP/1.1 400 Nothing To Play');
58
 
        exit;
59
 
}
60
 
 
61
 
// If we're XML-RPC and it's enabled, use system user
62
 
if (isset($xml_rpc) AND Config::get('xml_rpc') AND !isset($uid)) {
63
 
        $uid = '-1';
64
 
}
65
 
 
66
 
if (!isset($uid)) {
67
 
        debug_event('play', 'No user specified', 2);
68
 
        header('HTTP/1.1 400 No User Specified');
69
 
        exit;
 
66
    debug_event('play', 'No object UID specified, nothing to play', 2);
 
67
    header('HTTP/1.1 400 Nothing To Play');
 
68
    exit;
 
69
}
 
70
 
 
71
if (empty($uid)) {
 
72
    debug_event('play', 'No user specified', 2);
 
73
    header('HTTP/1.1 400 No User Specified');
 
74
    exit;
70
75
}
71
76
 
72
77
/* Misc Housework */
75
80
 
76
81
/* If the user has been disabled (true value) */
77
82
if (make_bool($GLOBALS['user']->disabled)) {
78
 
        debug_event('access_denied', "$user->username is currently disabled, stream access denied",'3');
79
 
        header('HTTP/1.1 403 User Disabled');
80
 
        exit;
 
83
    debug_event('UI::access_denied', "$user->username is currently disabled, stream access denied",'3');
 
84
    header('HTTP/1.1 403 User Disabled');
 
85
    exit;
81
86
}
82
87
 
83
88
// If require session is set then we need to make sure we're legit
84
89
if (Config::get('require_session')) {
85
 
        if (!Config::get('require_localnet_session') AND Access::check_network('network',$GLOBALS['user']->id,'5')) {
86
 
                debug_event('play', 'Streaming access allowed for local network IP ' . $_SERVER['REMOTE_ADDR'],'5');
87
 
        }
88
 
        elseif(!Stream::session_exists($sid)) {
89
 
                debug_event('access_denied', 'Streaming access denied: ' . $GLOBALS['user']->username . "'s session has expired", 3);
90
 
                header('HTTP/1.1 403 Session Expired');
91
 
                exit;
92
 
        }
 
90
    if (!Config::get('require_localnet_session') AND Access::check_network('network',$GLOBALS['user']->id,'5')) {
 
91
        debug_event('play', 'Streaming access allowed for local network IP ' . $_SERVER['REMOTE_ADDR'],'5');
 
92
    }
 
93
    else if(!Session::exists('stream', $sid)) {
 
94
        debug_event('UI::access_denied', 'Streaming access denied: ' . $GLOBALS['user']->username . "'s session has expired", 3);
 
95
            header('HTTP/1.1 403 Session Expired');
 
96
        exit;
 
97
    }
93
98
 
94
 
        // Now that we've confirmed the session is valid
95
 
        // extend it
96
 
        Stream::extend_session($sid,$uid);
 
99
    // Now that we've confirmed the session is valid
 
100
    // extend it
 
101
    Session::extend($sid, 'stream');
97
102
}
98
103
 
99
104
 
101
106
$GLOBALS['user']->update_last_seen();
102
107
 
103
108
/* If we are in demo mode.. die here */
104
 
if (Config::get('demo_mode') || (!Access::check('interface','25') AND !isset($xml_rpc))) {
105
 
        debug_event('access_denied', "Streaming Access Denied:" .Config::get('demo_mode') . "is the value of demo_mode. Current user level is " . $GLOBALS['user']->access,'3');
106
 
        access_denied();
107
 
        exit;
 
109
if (Config::get('demo_mode') || (!Access::check('interface','25') )) {
 
110
    debug_event('UI::access_denied', "Streaming Access Denied:" .Config::get('demo_mode') . "is the value of demo_mode. Current user level is " . $GLOBALS['user']->access,'3');
 
111
    UI::access_denied();
 
112
    exit;
108
113
}
109
114
 
110
115
/*
112
117
   that they have enough access to play this mojo
113
118
*/
114
119
if (Config::get('access_control')) {
115
 
        if (!Access::check_network('stream',$GLOBALS['user']->id,'25') AND
116
 
                !Access::check_network('network',$GLOBALS['user']->id,'25')) {
117
 
                debug_event('access_denied', "Streaming Access Denied: " . $_SERVER['REMOTE_ADDR'] . " does not have stream level access",'3');
118
 
                access_denied();
119
 
                exit;
120
 
        }
 
120
    if (!Access::check_network('stream',$GLOBALS['user']->id,'25') AND
 
121
        !Access::check_network('network',$GLOBALS['user']->id,'25')) {
 
122
        debug_event('UI::access_denied', "Streaming Access Denied: " . $_SERVER['REMOTE_ADDR'] . " does not have stream level access",'3');
 
123
        UI::access_denied();
 
124
        exit;
 
125
    }
121
126
} // access_control is enabled
122
127
 
 
128
// Handle playlist downloads
 
129
if ($type == 'playlist') {
 
130
    $playlist = new Stream_Playlist($oid);
 
131
    // Some rudimentary security
 
132
    if ($uid != $playlist->user) {
 
133
        UI::access_denied();
 
134
        exit;
 
135
    }
 
136
    $playlist->generate_playlist($playlist_type, false);
 
137
    exit;
 
138
}
 
139
 
123
140
/**
124
141
 * If we've got a tmp playlist then get the
125
142
 * current song, and do any other crazyness
126
143
 * we need to
127
144
 */
128
145
if ($demo_id) {
129
 
        $democratic = new Democratic($demo_id);
130
 
        $democratic->set_parent();
131
 
 
132
 
        // If there is a cooldown we need to make sure this song isn't a repeat
133
 
        if (!$democratic->cooldown) {
134
 
                /* This takes into account votes etc and removes the */
135
 
                $oid = $democratic->get_next_object();
136
 
        }
137
 
        else {
138
 
                // Pull history
139
 
                $oid = $democratic->get_next_object($song_cool_check);
140
 
                $oids = $democratic->get_cool_songs();
141
 
                while (in_array($oid,$oids)) {
142
 
                        $song_cool_check++;
143
 
                        $oid = $democratic->get_next_object($song_cool_check);
144
 
                        if ($song_cool_check >= '5') { break; }
145
 
                } // while we've got the 'new' song in old the array
146
 
 
147
 
        } // end if we've got a cooldown
 
146
    $democratic = new Democratic($demo_id);
 
147
    $democratic->set_parent();
 
148
 
 
149
    // If there is a cooldown we need to make sure this song isn't a repeat
 
150
    if (!$democratic->cooldown) {
 
151
        /* This takes into account votes etc and removes the */
 
152
        $oid = $democratic->get_next_object();
 
153
    }
 
154
    else {
 
155
        // Pull history
 
156
        $oid = $democratic->get_next_object($song_cool_check);
 
157
        $oids = $democratic->get_cool_songs();
 
158
        while (in_array($oid,$oids)) {
 
159
            $song_cool_check++;
 
160
            $oid = $democratic->get_next_object($song_cool_check);
 
161
            if ($song_cool_check >= '5') { break; }
 
162
        } // while we've got the 'new' song in old the array
 
163
 
 
164
    } // end if we've got a cooldown
148
165
} // if democratic ID passed
149
166
 
150
167
/**
151
168
 * if we are doing random let's pull the random object
152
169
 */
153
170
if ($random) {
154
 
        if ($start < 1) {
155
 
                $oid = Random::get_single_song($_REQUEST['type']);
156
 
                // Save this one incase we do a seek
157
 
                $_SESSION['random']['last'] = $oid;
158
 
        }
159
 
        else {
160
 
                $oid = $_SESSION['random']['last'];
161
 
        }
 
171
    if ($start < 1) {
 
172
        $oid = Random::get_single_song($_REQUEST['random_type']);
 
173
        // Save this one in case we do a seek
 
174
        $_SESSION['random']['last'] = $oid;
 
175
    }
 
176
    else {
 
177
        $oid = $_SESSION['random']['last'];
 
178
    }
162
179
} // if random
163
180
 
164
 
if (!$video) {
165
 
        /* Base Checks passed create the song object */
166
 
        $media = new Song($oid);
167
 
        $media->format();
 
181
if ($type == 'song') {
 
182
    /* Base Checks passed create the song object */
 
183
    $media = new Song($oid);
 
184
    $media->format();
168
185
}
169
186
else {
170
 
        $media = new Video($oid);
171
 
        $media->format();
 
187
    $media = new Video($oid);
 
188
    $media->format();
172
189
}
173
190
 
174
191
// Build up the catalog for our current object
176
193
 
177
194
/* If the song is disabled */
178
195
if (!make_bool($media->enabled)) {
179
 
        debug_event('Play',"Error: $media->file is currently disabled, song skipped",'5');
180
 
        // Check to see if this is a democratic playlist, if so remove it completely
181
 
        if ($demo_id) { $democratic->delete_from_oid($oid,'song'); }
182
 
        exit;
 
196
    debug_event('Play',"Error: $media->file is currently disabled, song skipped",'5');
 
197
    // Check to see if this is a democratic playlist, if so remove it completely
 
198
    if ($demo_id) { $democratic->delete_from_oid($oid,'song'); }
 
199
    exit;
183
200
}
184
201
 
185
202
// If we are running in Legalize mode, don't play songs already playing
186
203
if (Config::get('lock_songs')) {
187
 
        if (!Stream::check_lock_media($media->id,get_class($media))) {
188
 
                exit;
189
 
        }
 
204
    if (!Stream::check_lock_media($media->id,get_class($media))) {
 
205
        exit;
 
206
    }
190
207
}
191
208
 
192
 
/* Check to see if this is a 'remote' catalog */
193
209
if ($catalog->catalog_type == 'remote') {
194
 
 
195
 
        preg_match("/(.+)\/play\/index.+/",$media->file,$match);
196
 
 
197
 
        $token = xmlRpcClient::ampache_handshake($match['1'],$catalog->key);
198
 
 
199
 
        // If we don't get anything back we failed and should bail now
200
 
        if (!$token) {
201
 
                debug_event('xmlrpc-stream','Error Unable to get Token from ' . $match['1'] . ' check target servers logs','1');
202
 
                exit;
203
 
        }
204
 
 
205
 
        $sid   = xmlRpcClient::ampache_create_stream_session($match['1'],$token);
206
 
 
207
 
        $extra_info = "&xml_rpc=1&sid=$sid";
208
 
        header("Location: " . $media->file . $extra_info);
209
 
        debug_event('xmlrpc-stream',"Start XML-RPC Stream - " . $media->file . $extra_info,'5');
210
 
 
211
 
        /* If this is a voting tmp playlist remove the entry, we do this regardless of play amount */
212
 
        if ($demo_id) { $democratic->delete_from_oid($oid,'song'); } // if democratic
213
 
 
214
 
        exit;
215
 
} // end if remote catalog
 
210
    $remote_handle = $catalog->connect();
 
211
 
 
212
    // If we don't get anything back we failed and should bail now
 
213
    if (!$remote_handle) {
 
214
        debug_event('play', 'Connection to remote server failed', 1);
 
215
        exit;
 
216
    }
 
217
 
 
218
    $handshake = $remote_handle->info();
 
219
    $url = $media->file . '&ssid=' . $handshake['auth'];
 
220
 
 
221
    header('Location: ' . $url);
 
222
    debug_event('play', 'Started remote stream - ' . $url, 5);
 
223
 
 
224
    // Handle democratic removal 
 
225
    if ($demo_id) {
 
226
        $democratic->delete_from_oid($oid, 'song');
 
227
    }
 
228
 
 
229
    exit;
 
230
}
216
231
 
217
232
/* If we don't have a file, or the file is not readable */
218
 
if (!$media->file OR !is_readable($media->file)) {
219
 
 
220
 
        // We need to make sure this isn't democratic play, if it is then remove the song
221
 
        // from the vote list
222
 
        if (is_object($tmp_playlist)) {
223
 
                $tmp_playlist->delete_track($oid);
224
 
        }
225
 
        // FIXME: why are these separate?
226
 
        // Remove the song votes if this is a democratic song
227
 
        if ($demo_id) { $democratic->delete_from_oid($oid,'song'); }
228
 
 
229
 
        debug_event('play', "Song $media->file ($media->title) does not have a valid filename specified", 2);
230
 
        header('HTTP/1.1 404 Invalid song, file not found or file unreadable');
231
 
        exit;
232
 
}
233
 
 
234
 
// make fread binary safe
235
 
// This feature has been DEPRECATED as of PHP 5.3.0
236
 
if(version_compare(PHP_VERSION, '5.3.0', '<=')) {
237
 
        set_magic_quotes_runtime(0);
 
233
if (!$media->file || !Core::is_readable($media->file)) {
 
234
 
 
235
    // We need to make sure this isn't democratic play, if it is then remove
 
236
    // the song from the vote list
 
237
    if (is_object($tmp_playlist)) {
 
238
        $tmp_playlist->delete_track($oid);
 
239
    }
 
240
    // FIXME: why are these separate?
 
241
    // Remove the song votes if this is a democratic song
 
242
    if ($demo_id) { $democratic->delete_from_oid($oid,'song'); }
 
243
 
 
244
    debug_event('play', "Song $media->file ($media->title) does not have a valid filename specified", 2);
 
245
    header('HTTP/1.1 404 Invalid song, file not found or file unreadable');
 
246
    exit;
238
247
}
239
248
 
240
249
// don't abort the script if user skips this song because we need to update now_playing
252
261
 */
253
262
if ($_GET['action'] == 'download' AND Config::get('download')) {
254
263
 
255
 
        // STUPID IE
256
 
        $media->format_pattern();
257
 
        $media_name = str_replace(array('?','/','\\'),"_",$media->f_file);
258
 
 
259
 
        $browser->downloadHeaders($media_name,$media->mime,false,$media->size);
260
 
        $fp = fopen($media->file,'rb');
261
 
        $bytesStreamed = 0;
262
 
 
263
 
        if (!is_resource($fp)) {
 
264
    // STUPID IE
 
265
    $media->format_pattern();
 
266
    $media_name = str_replace(array('?','/','\\'),"_",$media->f_file);
 
267
 
 
268
    $browser->downloadHeaders($media_name,$media->mime,false,$media->size);
 
269
    $fp = fopen($media->file,'rb');
 
270
    $bytesStreamed = 0;
 
271
 
 
272
    if (!is_resource($fp)) {
264
273
                debug_event('Play',"Error: Unable to open $media->file for downloading",'2');
265
 
                exit();
266
 
        }
267
 
 
268
 
        // Check to see if we should be throttling because we can get away with it
269
 
        if (Config::get('rate_limit') > 0) {
270
 
                while (!feof($fp)) {
271
 
                        echo fread($fp,round(Config::get('rate_limit')*1024));
272
 
                        $bytesStreamed += round(Config::get('rate_limit')*1024);
273
 
                        flush();
274
 
                        sleep(1);
275
 
                }
276
 
        }
277
 
        else {
278
 
                fpassthru($fp);
279
 
        }
280
 
 
281
 
        // Make sure that a good chunk of the song has been played
282
 
        if ($bytesStreamed >= $media->size) {
283
 
                debug_event('Play','Downloaded, Registering stats for ' . $media->title,'5');
284
 
                $GLOBALS['user']->update_stats($media->id);
285
 
        } // if enough bytes are streamed
286
 
 
287
 
        fclose($fp);
288
 
        exit();
 
274
        exit();
 
275
        }
 
276
 
 
277
    // Check to see if we should be throttling because we can get away with it
 
278
    if (Config::get('rate_limit') > 0) {
 
279
        while (!feof($fp)) {
 
280
            echo fread($fp,round(Config::get('rate_limit')*1024));
 
281
            $bytesStreamed += round(Config::get('rate_limit')*1024);
 
282
            flush();
 
283
            sleep(1);
 
284
        }
 
285
    }
 
286
    else {
 
287
        fpassthru($fp);
 
288
    }
 
289
 
 
290
    // Make sure that a good chunk of the song has been played
 
291
    if ($bytesStreamed >= $media->size) {
 
292
            debug_event('Play','Downloaded, Registering stats for ' . $media->title,'5');
 
293
            $GLOBALS['user']->update_stats($media->id);
 
294
    } // if enough bytes are streamed
 
295
 
 
296
    fclose($fp);
 
297
    exit();
289
298
 
290
299
} // if they are trying to download and they can
291
300
 
292
 
header("Accept-Ranges: bytes" );
293
 
 
294
301
// Prevent the script from timing out
295
302
set_time_limit(0);
296
303
 
297
304
// We're about to start. Record this user's IP.
298
305
if (Config::get('track_user_ip')) {
299
 
        $GLOBALS['user']->insert_ip_history();
 
306
    $GLOBALS['user']->insert_ip_history();
300
307
}
301
308
 
 
309
$force_downsample = false;
302
310
if (Config::get('downsample_remote')) {
303
 
        if (!Access::check_network('network', $GLOBALS['user']->id,'0')) {
304
 
                debug_event('downsample', 'Address ' . $_SERVER['REMOTE_ADDR'] . ' is not in a network defined as local', 5);
305
 
                $remote = true;
306
 
        }
307
 
}
308
 
 
309
 
// If they are downsampling, or if the song is not a native stream or it's non-local
310
 
if (((Config::get('transcode') == 'always' AND  !$video) ||
311
 
        !$media->native_stream() ||
312
 
        isset($remote)) && Config::get('transcode') != 'never') {
313
 
        debug_event('downsample',
314
 
                'Decided to transcode. Transcode:' . Config::get('transcode') . 
315
 
                ' Native Stream: ' . ($media->native_stream() ? 'true' : 'false') .
316
 
                ' Remote: ' . ($remote ? 'true' : 'false'), 5);
317
 
        $media->set_transcode();
318
 
        $fp = Stream::start_transcode($media, $media_name, $start);
319
 
        $media_name = $media->f_artist_full . " - " . $media->title . "." . $media->type;
320
 
        $transcoded = true;
321
 
} // end if downsampling
 
311
    if (!Access::check_network('network', $GLOBALS['user']->id,'0')) {
 
312
        debug_event('play', 'Downsampling enabled for non-local address ' . $_SERVER['REMOTE_ADDR'], 5);
 
313
        $force_downsample = true;
 
314
    }
 
315
}
 
316
 
 
317
// Determine whether to transcode
 
318
$transcode = false;
 
319
$transcode_cfg = Config::get('transcode');
 
320
// transcode_to should only have an effect if the song is the wrong format
 
321
$transcode_to = $transcode_to == $media->type ? null : $transcode_to;
 
322
$valid_types = $media->get_stream_types();
 
323
if ($transcode_cfg != 'never' && in_array('transcode', $valid_types)) {
 
324
    if ($transcode_to) {
 
325
        $transcode = true;
 
326
        debug_event('play', 'Transcoding due to explicit request for ' . $transcode_to, 5);
 
327
    }
 
328
    else if ($transcode_cfg == 'always') {
 
329
        $transcode = true;
 
330
        debug_event('play', 'Transcoding due to always', 5);
 
331
    }
 
332
    else if ($force_downsample) {
 
333
        $transcode = true;
 
334
        debug_event('play', 'Transcoding due to downsample_remote', 5);
 
335
    }
 
336
    else if (!in_array('native', $valid_types)) {
 
337
        $transcode = true;
 
338
        debug_event('play', 'Transcoding because native streaming is unavailable', 5);
 
339
    }
 
340
    else {
 
341
        debug_event('play', 'Decided not to transcode', 5);
 
342
    }
 
343
}
 
344
else if ($transcode_to) {
 
345
    debug_event('play', 'Transcoding is impossible but we received an explicit request for ' . $transcode_to, 2);
 
346
}
 
347
 
 
348
if ($transcode) {
 
349
    header('Accept-Ranges: none');
 
350
    $transcoder = Stream::start_transcode($media, $transcode_to);
 
351
    $fp = $transcoder['handle'];
 
352
    $media_name = $media->f_artist_full . " - " . $media->title . "." . $transcoder['format'];
 
353
}
 
354
else if (!in_array('native', $valid_types)) {
 
355
    debug_event('play', 'Not transcoding and native streaming is not supported, aborting', 2);
 
356
    exit();
 
357
}
322
358
else {
323
 
        $fp = fopen($media->file, 'rb');
324
 
        $transcoded = false; 
 
359
    header('Accept-Ranges: bytes');
 
360
    $fp = fopen($media->file, 'rb');
325
361
}
326
362
 
327
363
if (!is_resource($fp)) {
328
 
        debug_event('play', "Failed to open $media->file for streaming", 2);
329
 
        exit();
 
364
    debug_event('play', "Failed to open $media->file for streaming", 2);
 
365
    exit();
330
366
}
331
367
 
332
368
// Put this song in the now_playing table only if it's a song for now...
333
369
if (get_class($media) == 'Song') {
334
 
        Stream::insert_now_playing($media->id,$uid,$media->time,$sid,get_class($media));
335
 
}
 
370
    Stream::insert_now_playing($media->id,$uid,$media->time,$sid,get_class($media));
 
371
}
 
372
 
 
373
if ($transcode) {
 
374
    $stream_size = null;
 
375
}
 
376
else {
 
377
    $stream_size = $media->size;
 
378
}
 
379
 
 
380
// Handle Content-Range
 
381
 
 
382
sscanf($_SERVER['HTTP_RANGE'], "bytes=%d-%d", $start, $end);
336
383
 
337
384
if ($start > 0 || $end > 0 ) {
338
 
        // Calculate stream size from byte range
339
 
        if(isset($end)) {
340
 
                $end = min($end,$media->size-1);
341
 
                $stream_size = ($end-$start)+1;
342
 
        }
343
 
        else {
344
 
                $stream_size = $media->size - $start;
345
 
        }
346
 
 
347
 
        debug_event('play', 'Content-Range header received, skipping ' . $start . ' bytes out of ' . $media->size, 5);
348
 
        $browser->downloadHeaders($media_name, $media->mime, false, $media->size);
349
 
        if (!$transcoded) {
350
 
                fseek($fp, $start);
351
 
        }
352
 
        $range = $start ."-". $end . "/" . $media->size;
353
 
        header('HTTP/1.1 206 Partial Content');
354
 
        header("Content-Range: bytes $range");
355
 
        header("Content-Length: $stream_size");
 
385
    // Calculate stream size from byte range
 
386
    if (isset($end)) {
 
387
        $end = min($end, $media->size - 1);
 
388
        $stream_size = ($end - $start) + 1;
 
389
    }
 
390
    else {
 
391
        $stream_size = $media->size - $start;
 
392
    }
 
393
 
 
394
    if ($transcode) {
 
395
        debug_event('play', 'Bad client behaviour. Content-Range header received, which we cannot fulfill due to transcoding', 2);
 
396
        $stream_size = null;
 
397
    }
 
398
    else {
 
399
        debug_event('play', 'Content-Range header received, skipping ' . $start . ' bytes out of ' . $media->size, 5);
 
400
        fseek($fp, $start);
 
401
 
 
402
        $range = $start . '-' . $end . '/' . $media->size;
 
403
        header('HTTP/1.1 206 Partial Content');
 
404
        header('Content-Range: bytes ' . $range);
 
405
    }
356
406
}
357
407
else {
358
 
        debug_event('play','Starting stream of ' . $media->file . ' with size ' . $media->size, 5);
359
 
        header("Content-Length: $media->size");
360
 
        $browser->downloadHeaders($media_name, $media->mime, false, $media->size);
361
 
        $stream_size = $media->size;
 
408
    debug_event('play','Starting stream of ' . $media->file . ' with size ' . $media->size, 5);
362
409
}
363
410
 
 
411
$mime = $transcode 
 
412
    ? $media->type_to_mime($transcoder['format'])
 
413
    : $media->mime;
 
414
 
 
415
$browser->downloadHeaders($media_name, $mime, false, $stream_size);
 
416
 
364
417
$bytes_streamed = 0;
365
418
 
366
419
// Actually do the streaming
367
420
do {
368
 
        $buf = fread($fp, min(2048, $stream_size - $bytes_streamed));
369
 
        print($buf);
370
 
        $bytes_streamed += strlen($buf);
371
 
} while (!feof($fp) && (connection_status() == 0) && ($bytes_streamed < $stream_size));
 
421
    $read_size = $transcode
 
422
        ? 2048
 
423
        : min(2048, $stream_size - $bytes_streamed);
 
424
    $buf = fread($fp, $read_size);
 
425
    print($buf);
 
426
    $bytes_streamed += strlen($buf);
 
427
} while (!feof($fp) && (connection_status() == 0) && ($transcode || $bytes_streamed < $stream_size));
372
428
 
 
429
$real_bytes_streamed = $bytes_streamed;
373
430
// Need to make sure enough bytes were sent.
374
431
if($bytes_streamed < $stream_size && (connection_status() == 0)) {
375
 
        print(str_repeat(' ', $stream_size - $bytes_streamed));
 
432
    print(str_repeat(' ', $stream_size - $bytes_streamed));
 
433
    $bytes_streamed = $stream_size;
376
434
}
377
435
 
378
436
// Make sure that a good chunk of the song has been played
379
 
if ($bytes_streamed > $media->size / 2) {
380
 
        // This check looks suspicious
381
 
        if (get_class($media) == 'Song') {
382
 
                debug_event('play', 'Registering stats for ' . $media->title, 5);
383
 
                $GLOBALS['user']->update_stats($media->id);
384
 
                $media->set_played();
385
 
        }
 
437
$target = 131072;
 
438
if ($stream_size) {
 
439
    if ($stream_size > 1048576) {
 
440
        $target = 262144;
 
441
    }
 
442
    else if ($stream_size < 360448) {
 
443
        $target = $stream_size / 1.1;
 
444
    }
 
445
    else {
 
446
        $target = $stream_size / 4;
 
447
    }
 
448
}
386
449
 
 
450
if ($start > $target) {
 
451
    debug_event('play', 'Content-Range was more than ' . $target . ' into the file, not collecting stats', 5);
 
452
}
 
453
else if ($bytes_streamed > $target) {
 
454
    // FIXME: This check looks suspicious
 
455
    if (get_class($media) == 'Song') {
 
456
        debug_event('play', 'Registering stats for ' . $media->title, 5);
 
457
        $GLOBALS['user']->update_stats($media->id);
 
458
        $media->set_played();
 
459
    }
387
460
}
388
461
else {
389
 
        debug_event('play', $bytes_streamed .' of ' . $media->size . ' streamed; not collecting stats', 5);
 
462
    debug_event('play', $bytes_streamed .' of ' . $stream_size . ' streamed; not collecting stats', 5);
390
463
}
391
464
 
392
465
// If this is a democratic playlist remove the entry.
393
466
// We do this regardless of play amount.
394
467
if ($demo_id) { $democratic->delete_from_oid($oid,'song'); }
395
468
 
396
 
if ($transcoded) {
397
 
        pclose($fp);
 
469
if ($transcode) {
 
470
    $stderr = fread($transcoder['stderr'], 8192);
 
471
    fclose($transcoder['stderr']);
 
472
    fclose($fp);
 
473
    proc_close($transcoder['process']);
 
474
    debug_event('transcode_cmd', $stderr, 5);
398
475
}
399
476
else {
400
 
        fclose($fp);
 
477
    fclose($fp);
401
478
}
402
479
 
403
 
debug_event('play', 'Stream ended at ' . $bytes_streamed . ' bytes out of ' . $media->size, 5);
 
480
debug_event('play', 'Stream ended at ' . $bytes_streamed . ' (' . $real_bytes_streamed . ') bytes out of ' . $stream_size, 5);
404
481
 
405
482
?>