summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/ampache.cfg.php.dist91
-rwxr-xr-xdocs/CHANGELOG1
-rw-r--r--lib/class/media.interface.php39
-rw-r--r--lib/class/radio.class.php20
-rw-r--r--lib/class/song.class.php133
-rw-r--r--lib/class/stream.class.php55
-rw-r--r--lib/class/video.class.php23
-rw-r--r--lib/init.php2
-rw-r--r--play/index.php69
9 files changed, 210 insertions, 223 deletions
diff --git a/config/ampache.cfg.php.dist b/config/ampache.cfg.php.dist
index 4829b298..4d6434c8 100644
--- a/config/ampache.cfg.php.dist
+++ b/config/ampache.cfg.php.dist
@@ -7,7 +7,7 @@
; if this config file is up to date
; this is compared against a value hard-coded
; into the init script
-config_version = 11
+config_version = 12
;###################
; Path Vars #
@@ -515,43 +515,58 @@ refresh_limit = "60"
; These are commands used to transcode non-streaming
; formats to the target file type for streaming.
; This can be useful in re-encoding file types that don't stream
-; very well, or if your player doesn't support some file types.
-; This is also the string used when 'downsampling' is selected
-; as some people have complained its not bloody obvious, any programs
-; referenced in the downsample commands must be installed manually and in
-; the web server path, and executable by the web server
-; REQUIRED variables
-; transcode_TYPE = true/false ## True to force transcode regardless of prefs
-; transcode_TYPE_target = TARGET_FILE_TYPE
-; transcode_cmd_TYPE = TRANSCODE_COMMAND
-; %FILE% = filename
-; %OFFSET% = offset
-; %SAMPLE% = sample rate
-; %EOF% = end of file in min.sec
-
-; List of filetypes to transcode
-transcode_m4a = true
-transcode_m4a_target = mp3
-;transcode_flac = true
-transcode_flac_target = mp3
-;transcode_mp3 = false
-transcode_mp3_target = mp3
-;transcode_ogg = false
-transcode_ogg_target = mp3
-
-; These are the commands that will be run to transcode the file
-transcode_cmd_flac = "flac -dc %FILE% | lame -b %SAMPLE% -S - - "
-transcode_cmd_m4a = "faad -f 2 -w %FILE% | lame -r -b %SAMPLE% -S - -"
-transcode_cmd_mp3 = "mp3splt -qnf %FILE% %OFFSET% %EOF% -o - | lame --mp3input -q 3 -b %SAMPLE% -S - -"
-transcode_cmd_ogg = "oggsplt -qn %FILE% %OFFSET% %EOF% -o - | oggdec -Q -o - - | lame -S -q 3 -b %SAMPLE% -S - -"
-
-; Alternative command works better for some people
-;transcode_cmd_m4a = "alac %FILE% | lame -h -b %SAMPLE% -S - -"
-;transcode_cmd_ogg = "mp3splt -qn %FILE% %OFFSET% %EOF% -o - | oggdec -Q -o - - | lame -S -q 3 -b %SAMPLE% -S - -"
-;transcode_cmd_flac = "flac -dc %FILE% | lame -rb %SAMPLE% -S - -"
-
-; This line seems to work better for windows, switch if needed
-;transcode_cmd_mp3 = "lame -q 3 -b %SAMPLE% -S %FILE% - -"
+; very well, or if your player doesn't support some file types.
+;
+; 'Downsampling' will also use these commands.
+;
+; To state the bleeding obvious, any programs referenced in the downsample
+; commands must be installed, in the web server's search path (or referenced
+; by their full path), and executable by the web server.
+
+; Input type selection
+; TYPE is the extension. 'allowed' certifies that transcoding works properly for
+; this input format. 'required' further forbids the direct streaming of a format
+; (e.g. if you store everything in FLAC, but don't want to ever stream that.)
+; transcode_TYPE = {allowed|required|false}
+; DEFAULT: false
+;transcode_m4a = allowed
+;transcode_flac = required
+;transcode_mp3 = allowed
+
+; Default output format
+; DEFAULT: none
+;encode_target = mp3
+
+; Override the default output format on a per-type basis
+; encode_target_TYPE = TYPE
+; DEFAULT: none
+;encode_target_flac = ogg
+
+; Command configuration. Substitutions will be made as follows:
+; %FILE% => filename
+; %SAMPLE% => target sample rate
+; You can do fancy things like VBR, but consider whether the consequences are
+; acceptable in your environment.
+
+; Master transcode command
+; transcode_cmd should be a single command that supports multiple file types,
+; such as ffmpeg or avconv. It's still possible to make a configuration that's
+; equivalent to the old default, but if you find that necessary you should be
+; clever enough to figure out how on your own.
+; DEFAULT: none
+;transcode_cmd = "ffmpeg -i %FILE"
+
+; Specific transcode commands
+; It shouldn't be necessary in most cases, but you can override the transcode
+; command for specific source formats. It still needs to accept the
+; encoding arguments, so the easiest approach is to use your normal command as
+; a clearing-house.
+; transcode_cmd_TYPE = TRANSCODE_CMD
+;transcode_cmd_mid = "timidity -Or -o – %FILE% | ffmpeg -i pipe:0"
+
+; encode_args_TYPE = TRANSCODE_CMD_ARGS
+;encode_args_mp3 = "-vn -b:a %SAMPLE%K -c:a mp3 -f mp3 pipe:1"
+;encode_args_ogg = "-vn -b:a %SAMPLE%K -c:a vorbis -f ogg pipe:1"
;######################################################
; these options allow you to configure your rss-feed
diff --git a/docs/CHANGELOG b/docs/CHANGELOG
index 54f1fe27..68c7b73b 100755
--- a/docs/CHANGELOG
+++ b/docs/CHANGELOG
@@ -4,6 +4,7 @@
--------------------------------------------------------------------------
v.3.6-FUTURE
+ - Made transcoding and its configuration more flexible
- Made transcoded streams more standards compliant by not sending a random
value as the Content-Length or claiming that ranged requests are
supported
diff --git a/lib/class/media.interface.php b/lib/class/media.interface.php
index 617e12b5..b82b5ea8 100644
--- a/lib/class/media.interface.php
+++ b/lib/class/media.interface.php
@@ -1,8 +1,6 @@
<?php
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
/**
- * media Interface
- *
*
* LICENSE: GNU General Public License, version 2 (GPLv2)
* Copyright (c) 2001 - 2011 Ampache.org All Rights Reserved
@@ -19,11 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @package Ampache
- * @copyright 2001 - 2011 Ampache.org
- * @license http://opensource.org/licenses/gpl-2.0 GPLv2
- * @link http://www.ampache.org/
*/
/**
@@ -32,51 +25,45 @@
* This defines how the media file classes should
* work, this lists all required functions and the expected
* input
- *
- * @package Ampache
- * @copyright 2001 - 2011 Ampache.org
- * @license http://opensource.org/licenses/gpl-2.0 GPLv2
- * @link http://www.ampache.org/
- * @see Video
- * @see Radio
- * @see Random
- * @see Song
*/
interface media {
/**
* format
*
- * @return
+ * Creates the gussied-up member variables for output
*/
public function format();
/**
- * native_stream
+ * get_stream_types
*
- * @return mixed
+ * Returns an array of strings; current types are 'native'
+ * and 'transcode'
*/
- public function native_stream();
+ public function get_stream_types();
/**
* play_url
*
- * @param int $oid ID
- * @return mixed
+ * Returns the url to stream the specified object
+ *
*/
public static function play_url($oid);
/**
- * stream_cmd
+ * get_transcode_settings
*
- * @return mixed
+ * Should only be called if 'transcode' was returned by get_stream_types
+ * Returns a raw transcode command for this item; the optional target
+ * parameter can be used to request a specific format instead of the
+ * default from the configuration file.
*/
- public function stream_cmd();
+ public function get_transcode_settings($target = null);
/**
* has_flag
*
- * @return mixed
*/
public function has_flag();
diff --git a/lib/class/radio.class.php b/lib/class/radio.class.php
index d4be4f76..a8d33ef3 100644
--- a/lib/class/radio.class.php
+++ b/lib/class/radio.class.php
@@ -188,13 +188,11 @@ class Radio extends database_object implements media {
} // delete
/**
- * native_stream
+ * get_stream_types
* This is needed by the media interface
*/
- public function native_stream() {
-
-
-
+ public function get_stream_types() {
+ return array('foreign');
} // native_stream
/**
@@ -220,13 +218,13 @@ class Radio extends database_object implements media {
} // has_flag
/**
- * stream_cmd
- * Needed by the media interface
+ * get_transcode_settings
+ *
+ * This will probably never be implemented
*/
- public function stream_cmd() {
-
-
- } // stream_cmd
+ public function get_transcode_settings() {
+ return false;
+ }
} //end of radio class
diff --git a/lib/class/song.class.php b/lib/class/song.class.php
index 70f6e881..ab38524a 100644
--- a/lib/class/song.class.php
+++ b/lib/class/song.class.php
@@ -62,11 +62,7 @@ class Song extends database_object implements media {
public $mbid; // MusicBrainz ID
/* Setting Variables */
- public $_transcoded = false;
- public $resampled = false;
public $_fake = false; // If this is a 'construct_from_array' object
- public $transcoded_from;
- private $_transcode_cmd;
/**
* Constructor
@@ -85,8 +81,9 @@ class Song extends database_object implements media {
foreach ($info as $key=>$value) {
$this->$key = $value;
}
- // Format the Type of the song
- $this->format_type();
+ $data = pathinfo($this->file);
+ $this->type = strtolower($data['extension']);
+ $this->mime = self::type_to_mime($this->type);
}
return true;
@@ -222,61 +219,53 @@ class Song extends database_object implements media {
} // fill_ext_info
/**
- * format_type
- * gets the type of song we are trying to
- * play, used to set mime headers and to trick
- * players into playing them correctly
+ * type_to_mime
+ *
+ * Returns the mime type for the specified file extension/type
*/
- public function format_type($override='') {
-
- // If we pass an override for downsampling or whatever then use it
- if (!empty($override)) {
- $this->type = $override;
- }
- else {
- $data = pathinfo($this->file);
- $this->type = strtolower($data['extension']);
- }
-
- switch ($this->type) {
+ public static function type_to_mime($type) {
+ // FIXME: This should really be done the other way around.
+ // Store the mime type in the database, and provide a function
+ // to make it a human-friendly type.
+ switch ($type) {
case 'spx':
case 'ogg':
- $this->mime = "application/ogg";
+ return 'application/ogg';
break;
case 'wma':
case 'asf':
- $this->mime = "audio/x-ms-wma";
+ return 'audio/x-ms-wma';
break;
case 'mp3':
case 'mpeg3':
- $this->mime = "audio/mpeg";
+ return 'audio/mpeg';
break;
case 'rm':
case 'ra':
- $this->mime = "audio/x-realaudio";
+ return 'audio/x-realaudio';
break;
case 'flac';
- $this->mime = "audio/x-flac";
+ return 'audio/x-flac';
break;
case 'wv':
- $this->mime = 'audio/x-wavpack';
+ return 'audio/x-wavpack';
break;
case 'aac':
case 'mp4':
case 'm4a':
- $this->mime = "audio/mp4";
+ return 'audio/mp4';
break;
case 'mpc':
- $this->mime = "audio/x-musepack";
+ return 'audio/x-musepack';
break;
default:
- $this->mime = "audio/mpeg";
+ return 'audio/mpeg';
break;
}
return true;
- } // format_type
+ }
/**
* get_album_name
@@ -923,63 +912,51 @@ class Song extends database_object implements media {
} // get_recently_played
- /**
- * native_stream
- * This returns true/false if this can be natively streamed
- */
- public function native_stream() {
-
- if ($this->_transcoded) { return false; }
-
- $conf_var = 'transcode_' . $this->type;
+ public function get_stream_types() {
+ $types = array();
+ $transcode = Config::get('transcode_' . $this->type);
- if (Config::get($conf_var)) {
- $this->set_transcode();
- return false;
+ if ($transcode != 'required') {
+ $types[] = 'native';
+ }
+ if (make_bool($transcode)) {
+ $types[] = 'transcode';
}
- return true;
-
- } // end native_stream
-
- /**
- * set_transcode
- *
- * We want to transcode, set up the variables correctly
- */
- public function set_transcode() {
- if ($this->_transcoded) { return; }
+ return $types;
+ } // end stream_types
- $conf_type = 'transcode_' . $this->type . '_target';
- $conf_cmd = 'transcode_cmd_' . $this->type;
+ public function get_transcode_settings($target = null) {
+ $source = $this->type;
- $this->_transcoded = true;
- $this->transcoded_from = $this->type;
- $this->_transcode_cmd = Config::get($conf_cmd);
- $this->format_type(Config::get($conf_type));
- if ($this->type == $this->transcoded_from) {
- $this->_resampled = true;
+ if ($target) {
+ debug_event('transcode', 'Explicit format request', 5);
+ }
+ else if ($target = Config::get('encode_target_' . $source)) {
+ debug_event('transcode', 'Defaulting to configured target format for ' . $source, 5);
+ }
+ else if ($target = Config::get('encode_target')) {
+ debug_event('transcode', 'Using default target format', 5);
+ }
+ else {
+ $target = $source;
+ debug_event('transcode', 'No default target for ' . $source . ', choosing to resample', 5);
}
- debug_event('transcode', 'Transcoding from ' .
- $this->transcoded_from . ' to ' . $this->type, 5);
- }
+ debug_event('transcode', 'Transcoding from ' . $source . ' to ' . $target, 5);
- /**
- * stream_cmd
- *
- * test if the song type streams natively and
- * if not returns a transcoding command from the config
- */
- public function stream_cmd() {
+ $cmd = Config::get('transcode_cmd_' . $source) ?: Config::get('transcode_cmd');
+ $args = Config::get('encode_args_' . $target);
- if ($this->native_stream()) {
- return null;
+ if (!$args) {
+ debug_event('transcode', 'Target format ' . $target . ' is not properly configured', 2);
+ return false;
}
-
- return $this->_transcode_cmd;
- } // end stream_cmd
+ debug_event('transcode', 'Command: ' . $cmd . ' Arguments: ' . $args, 5);
+ return array('format' => $target,
+ 'command' => $cmd . ' ' . $args);
+ }
} // end of song class
?>
diff --git a/lib/class/stream.class.php b/lib/class/stream.class.php
index 871f8394..4a25885c 100644
--- a/lib/class/stream.class.php
+++ b/lib/class/stream.class.php
@@ -142,17 +142,18 @@ class Stream {
* start_transcode
*
* This is a rather complex function that starts the transcoding or
- * resampling of a song and returns the opened file handle. A reference
- * to the song object is passed so that the changes we make in here
- * affect the external object, References++
+ * resampling of a song and returns the opened file handle.
*/
- public static function start_transcode(&$song, $song_name = 0) {
+ public static function start_transcode($song) {
+ $transcode_settings = $song->get_transcode_settings();
+ // Bail out early if we're unutterably broken
+ if ($transcode_settings == false) {
+ debug_event('stream', 'Transcode requested, but get_transcode_settings failed', 2);
+ return false;
+ }
- // Check to see if bitrates are set.
- // If so let's go ahead and optimize!
$max_bitrate = Config::get('max_bit_rate');
$min_bitrate = Config::get('min_bit_rate');
- $time = time();
// FIXME: This should be configurable for each output type
$user_sample_rate = Config::get('sample_rate');
@@ -161,10 +162,6 @@ class Stream {
$min_bitrate = $user_sample_rate;
}
- if (!$song_name) {
- $song_name = $song->f_artist_full . " - " . $song->title . "." . $song->type;
- }
-
// Are there site-wide constraints? (Dynamic downsampling.)
if ($max_bitrate > 1 ) {
$sql = 'SELECT COUNT(*) FROM `now_playing` ' .
@@ -180,15 +177,16 @@ class Stream {
$results = Dba::fetch_row($db_results);
$active_streams = intval($results[0]) ?: 0;
- debug_event('transcode', 'Active streams: ' . $active_streams, 5);
+ debug_event('stream', 'Active transcoding streams: ' . $active_streams, 5);
// We count as one for the algorithm
+ // FIXME: Should this reflect the actual bit rates?
$active_streams++;
$sample_rate = floor($max_bitrate / $active_streams);
// Exit if this would be insane
if ($sample_rate < ($min_bitrate ?: 8)) {
- debug_event('transcode', 'Max bandwidth already allocated. Active streams: ' . $active_streams, 2);
+ debug_event('stream', 'Max transcode bandwidth already allocated. Active streams: ' . $active_streams, 2);
header('HTTP/1.1 503 Service Temporarily Unavailable');
exit();
}
@@ -203,26 +201,23 @@ class Stream {
$sample_rate = $user_sample_rate;
}
- debug_event('transcode', 'Configured bitrate is ' . $sample_rate, 5);
+ debug_event('stream', 'Configured bitrate is ' . $sample_rate, 5);
- /* Validate the bitrate */
+ // Validate the bitrate
$sample_rate = self::validate_bitrate($sample_rate);
// Never upsample a song
- if ($song->resampled && ($sample_rate * 1000) > $song->bitrate) {
- debug_event('transcode', 'Clamping bitrate to avoid upsampling to ' . $sample_rate, 5);
+ if ($song->type == $transcode_settings['format'] && ($sample_rate * 1000) > $song->bitrate) {
+ debug_event('stream', 'Clamping bitrate to avoid upsampling to ' . $sample_rate, 5);
$sample_rate = self::validate_bitrate($song->bitrate / 1000);
}
- debug_event('transcode', 'Final bitrate is ' . $sample_rate, 5);
+ debug_event('stream', 'Final transcode bitrate is ' . $sample_rate, 5);
$song_file = scrub_arg($song->file);
- $transcode_command = $song->stream_cmd();
- if ($transcode_command == null) {
- debug_event('downsample', 'song->stream_cmd() returned null', 2);
- return null;
- }
+ // Finalise the command line
+ $command = $transcode_settings['command'];
$string_map = array(
'%FILE%' => $song_file,
@@ -230,20 +225,20 @@ class Stream {
);
foreach ($string_map as $search => $replace) {
- $transcode_command = str_replace($search, $replace, $transcode_command, $ret);
+ $command = str_replace($search, $replace, $command, $ret);
if (!$ret) {
debug_event('downsample', "$search not in downsample command", 5);
}
}
- debug_event('downsample', "Downsample command: $transcode_command", 3);
+ debug_event('downsample', "Downsample command: $command", 3);
- $fp = popen($transcode_command, 'rb');
-
- // Return our new handle
- return $fp;
+ return array(
+ 'handle' => popen($command, 'rb'),
+ 'format' => $transcode_settings['format']
+ );
- } // start_downsample
+ }
/**
* validate_bitrate
diff --git a/lib/class/video.class.php b/lib/class/video.class.php
index 22bafa19..3802048a 100644
--- a/lib/class/video.class.php
+++ b/lib/class/video.class.php
@@ -95,13 +95,9 @@ class Video extends database_object implements media {
} // format
- /**
- * native_stream
- * This returns true or false on the downsampling mojo
- */
- public function native_stream() {
+ public function get_stream_types() {
- return true;
+ return array('native');
} // native_stream
@@ -126,16 +122,13 @@ class Video extends database_object implements media {
} // play_url
/**
- * stream_cmd
- * test and see if the video needs to be natively streamed
- * if not it returns the transocding command from the config file
- * we can't use this->type because its been formated for the downsampling
+ * get_transcode_settings
+ *
+ * FIXME: Video transcoding is not implemented
*/
- public function stream_cmd() {
-
-
-
- } // stream_cmd
+ public function get_transcode_settings($target = null) {
+ return false;
+ }
/**
* has_flag
diff --git a/lib/init.php b/lib/init.php
index 51f16561..a93f1800 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -71,7 +71,7 @@ if ($link) {
/** This is the version.... fluf nothing more... **/
$results['version'] = '3.6-Alpha4-DEV';
-$results['int_config_version'] = '11';
+$results['int_config_version'] = '12';
if ($results['force_ssl']) {
$http_type = 'https://';
diff --git a/play/index.php b/play/index.php
index 86932f81..3909c234 100644
--- a/play/index.php
+++ b/play/index.php
@@ -309,32 +309,49 @@ if (Config::get('track_user_ip')) {
$GLOBALS['user']->insert_ip_history();
}
-$downsample_remote = false;
+$force_downsample = false;
if (Config::get('downsample_remote')) {
if (!Access::check_network('network', $GLOBALS['user']->id,'0')) {
- debug_event('downsample', 'Address ' . $_SERVER['REMOTE_ADDR'] . ' is not in a network defined as local', 5);
- $downsample_remote = true;
+ debug_event('play', 'Downsampling enabled for non-local address ' . $_SERVER['REMOTE_ADDR'], 5);
+ $force_downsample = true;
}
}
-// If they are downsampling, or if the song is not a native stream or it's non-local
-if (((Config::get('transcode') == 'always' && !$video) ||
- !$media->native_stream() ||
- $downsample_remote) && Config::get('transcode') != 'never') {
- debug_event('downsample',
- 'Decided to transcode. Transcode:' . Config::get('transcode') .
- ' Native Stream: ' . ($media->native_stream() ? 'true' : 'false') .
- ' Remote: ' . ($downsample_remote ? 'true' : 'false'), 5);
+// Determine whether to transcode
+$transcode = false;
+$transcode_cfg = Config::get('transcode');
+$valid_types = $media->get_stream_types();
+if ($transcode_cfg != 'never' && in_array('transcode', $valid_types)) {
+ if ($transcode_cfg == 'always') {
+ $transcode = true;
+ debug_event('play', 'Transcoding due to always', 5);
+ }
+ else if ($force_downsample) {
+ $transcode = true;
+ debug_event('play', 'Transcoding due to downsample_remote', 5);
+ }
+ else if (!in_array('native', $valid_types)) {
+ $transcode = true;
+ debug_event('play', 'Transcoding because native streaming is unavailable', 5);
+ }
+ else {
+ debug_event('play', 'Decided not to transcode', 5);
+ }
+}
+
+if ($transcode) {
header('Accept-Ranges: none');
- $media->set_transcode();
- $fp = Stream::start_transcode($media, $media_name, $start);
- $media_name = $media->f_artist_full . " - " . $media->title . "." . $media->type;
- $transcoded = true;
-} // end if downsampling
+ $transcoder = Stream::start_transcode($media);
+ $fp = $transcoder['handle'];
+ $media_name = $media->f_artist_full . " - " . $media->title . "." . $transcoder['format'];
+}
+else if (!in_array('native', $valid_types)) {
+ debug_event('play', 'Not transcoding and native streaming is not supported, aborting', 2);
+ exit();
+}
else {
header('Accept-Ranges: bytes');
$fp = fopen($media->file, 'rb');
- $transcoded = false;
}
if (!is_resource($fp)) {
@@ -347,7 +364,7 @@ if (get_class($media) == 'Song') {
Stream::insert_now_playing($media->id,$uid,$media->time,$sid,get_class($media));
}
-if ($transcoded) {
+if ($transcode) {
$stream_size = null;
}
else {
@@ -368,8 +385,8 @@ if ($start > 0 || $end > 0 ) {
$stream_size = $media->size - $start;
}
- if ($transcoded) {
- debug_event('play', 'Bad client behaviour. Content-Range header received, which we cannot fulfill due to transcoding', 1);
+ if ($transcode) {
+ debug_event('play', 'Bad client behaviour. Content-Range header received, which we cannot fulfill due to transcoding', 2);
$stream_size = null;
}
else {
@@ -385,19 +402,23 @@ else {
debug_event('play','Starting stream of ' . $media->file . ' with size ' . $media->size, 5);
}
-$browser->downloadHeaders($media_name, $media->mime, false, $stream_size);
+$mime = $transcode
+ ? $media->type_to_mime($transcoder['format'])
+ : $media->mime;
+
+$browser->downloadHeaders($media_name, $mime, false, $stream_size);
$bytes_streamed = 0;
// Actually do the streaming
do {
- $read_size = $transcoded
+ $read_size = $transcode
? 2048
: min(2048, $stream_size - $bytes_streamed);
$buf = fread($fp, $read_size);
print($buf);
$bytes_streamed += strlen($buf);
-} while (!feof($fp) && (connection_status() == 0) && ($transcoded || $bytes_streamed < $stream_size));
+} while (!feof($fp) && (connection_status() == 0) && ($transcode || $bytes_streamed < $stream_size));
$real_bytes_streamed = $bytes_streamed;
// Need to make sure enough bytes were sent.
@@ -439,7 +460,7 @@ else {
// We do this regardless of play amount.
if ($demo_id) { $democratic->delete_from_oid($oid,'song'); }
-if ($transcoded) {
+if ($transcode) {
pclose($fp);
}
else {