Reply To: Playlists and Podcasts

#13264
fizze
Participant

This is my version of the enclosurecache.php.
It’s modified to be more SQL-compliant, and thus also works with sqlite. (on the slug)

It’s not really nice, at it looks for the php binary in /opt/bin. Make sure the web-server user has execute permissions!

I tried to get in touch with some of the gregarius devs to update the site rep, but this proves to be difficult.

The chances are sparse, and I’ve added a few debug hooks to check permissions, amongst others. It is currently working like a charm for me, though. I’ve added a cron job that refreshes gregarius daily at 4am, and another cron job that triggers a scan in firefly at 5am. This way I can enjoy my newest podcasts during breakfast. 🙂


<?php
###############################################################################
# Gregarius - A PHP based RSS aggregator.
# Copyright (C) 2003 - 2005 Marco Bonetti
#
###############################################################################
# This program is free software and open source software; you can redistribute
# it and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the License,
# or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# 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 or visit
# http://www.gnu.org/licenses/gpl.html
#
###############################################################################
# E-mail: mbonetti at users dot sourceforge dot net
# Web page: http://sourceforge.net/projects/gregarius
#
###############################################################################


/// Name: Enclosure Cache
/// Author: Keith D. Zimmerman
/// Description: Provides a local cache of enclosures found in the items
/// Version: 0.1
/// Configuration: __enclosure_cache_config

/**
* Changes:
*
* 0.1: First release
*
*/

require_once(dirname( __FILE__ ) . "/../init.php");

rss_set_hook("rss.plugins.items.newiid", "__enclosure_cache_go_get_it");
rss_set_hook('rss.plugins.javascript','__enclosure_cache_register');
rss_set_hook('rss.plugins.ajax.exports','__enclosure_cache_appendAJAXfunction');
rss_set_hook("rss.plugins.items.enclosure", "__enclosure_cache_link");

define ('EC_DOWNLOAD_DIR_CONFIG_KEY','downloadPath');
define ('EC_DOWNLOAD_PREFIX_CONFIG_KEY','downloadHttpPrefix');
define ('EC_AUTODOWNLOAD_CONFIG_KEY','autoDownload');
define ('EC_OVERWRITE_ENC_CONFIG_KEY','overwriteEnclosure');

//configuration function
function __enclosure_cache_config() {
if (rss_plugins_is_submit()) {
rss_plugins_add_option(EC_DOWNLOAD_DIR_CONFIG_KEY, $_REQUEST, 'string');
rss_plugins_add_option(EC_DOWNLOAD_PREFIX_CONFIG_KEY, $_REQUEST, 'string');
rss_plugins_add_option(EC_AUTODOWNLOAD_CONFIG_KEY, isset( $_REQUEST ) ? $_REQUEST : "", 'string');
rss_plugins_add_option(EC_OVERWRITE_ENC_CONFIG_KEY, isset( $_REQUEST ) ? $_REQUEST : "", 'string');
return;
}

echo "

n";
echo "n";
echo "
nn";
echo "
nn";
echo "
n";
echo "n";
echo "

n";
}

//hooks
function __enclosure_cache_go_get_it($params){
$newIid = $params[0];
$item = $params[1];

//if configured for autodownload and this item has an enclosure, go get it...
if (rss_plugins_get_option( EC_AUTODOWNLOAD_CONFIG_KEY ) == 1 && array_key_exists('enclosure@url', $item) ) {
$enclosure = $item;
__exp__enclosure_cache( $newIid, $enclosure, 1 );
}

return $params;
}

function __enclosure_cache_register($js) {
$js[] = getPath(). RSS_PLUGINS_DIR . "/enclosurecache.js";
return $js;
}

function __enclosure_cache_appendAJAXfunction($exp) {
$exp[]='__exp__enclosure_cache';
return $exp;
}

function __enclosure_cache_link($dummy){
$id = rss_item_id();
$url = rss_item_enclosure();

$savepath = realpath( rss_plugins_get_option( EC_DOWNLOAD_DIR_CONFIG_KEY ) );
if( file_exists( $savepath . '/' . basename( $url ) ) )
echo " (LOCAL CACHE)";
else
{
$bDone = ( __exp__enclosure_cache( $id, $url, 0 ) == "$id||$url||DONE" );
if( !$bDone )
echo "__enclosure_cache_js_timer( $id, "$url" );";
?> | <a href="#" onclick="javascript: __enclosure_cache_download(, ''); return false;"

id='__ec__link'>save in cache<pre style='display: inline;' id='__ec__status'>

<?php
}

return $dummy;
}
//end hooks

//exported function
function __exp__enclosure_cache($id, $url, $start) {
if( $start == 1 )
{
if( !is_writable( rss_plugins_get_option( EC_DOWNLOAD_DIR_CONFIG_KEY ) ) )
$ret = "ERROR: No permissions to write in " . rss_plugins_get_option( EC_DOWNLOAD_DIR_CONFIG_KEY ) . "!";
else
{
# $savepath = realpath( rss_plugins_get_option( EC_DOWNLOAD_DIR_CONFIG_KEY ) );

# $ret = exec('echo "'. __FILE__. '">'. $savepath .'/'.'gregarius.log');
# $ret = exec( 'echo "/opt/bin/php -f ' . __FILE__ . ' -- --id ' . $id . ' --url ' . $url . '"> '. $savepath .'/'.'gregarius.log' );
# $ret = $savepath .'/'.'gregarius.log';
system( '/opt/bin/php -f ' . __FILE__ . ' -- --id ' . $id . ' --url ' . $url . ' &' );
$ret = "Starting Download...";
# $ret = ( 'php -f ' . __FILE__ . ' -- --id ' . $id . ' --url ' . $url . ' &' );
}
}
else
{
$savepath = realpath( rss_plugins_get_option( EC_DOWNLOAD_DIR_CONFIG_KEY ) );
if( file_exists( $savepath . '/' . $id . '_error.txt' ) )
{
$error_file = fopen( $savepath . '/' . $id . '_error.txt', 'r' );
fwrite($error_file,"test!");
while( !flock( $error_file, LOCK_EX ) )
{
// wait for 100 ms
usleep(100000);
}

if( file_exists( $savepath . '/' . $id . '_status.txt' ) )
{
$status_file = fopen( $savepath . '/' . $id . '_status.txt', 'r' );
$ret = fread( $status_file, 2048 /*arbitrary max*/ );
fclose( $status_file );
}
else
{
$ret = "ERROR: " . fread( $error_file, 2048 /*arbitrary max*/ );
}

fclose( $error_file );
}
else
$ret = "DONE";
}
return $id . "||" . $url . "||" . $ret;
}

//helper function
function __enclosure_cache_filename_mangler($item_id, $enclosure_file_name){
$enclosure_base_name = basename( $enclosure_file_name );
$exten = strchr($enclosure_base_name, ".");
return substr($enclosure_base_name, 0, -strlen($exten)) . "_" . $item_id . $exten;
}

//handle command line mode !
$cline = isset($argv) && !$_REQUEST;
if($cline)
{
$forced_args = array(
array("--id", "FULL"),
array("--url","FULL") );

$parser = new __enclosure_cache_arg_parser($forced_args);
$id = $parser->get_full_passed( "--id" );
$url = $parser->get_full_passed( "--url" );

$savepath = realpath( rss_plugins_get_option( EC_DOWNLOAD_DIR_CONFIG_KEY ) );
# $savepath = "/tmp";
$status_file = fopen( $savepath . '/' . $id . '_status.txt', 'w' );
# $status_file = fopen( '/tmp/enc_status.txt', 'w' );
$error_file = fopen( $savepath . '/' . $id . '_error.txt', 'w' );
# fwrite($error_file, $savepath);
# fwrite($status_file, $savepath);

$http = new __enclosure_cache_HTTPRequest( $url, $id, $savepath, $error_file, $status_file );
$rez = $http->Download();

if( !$rez )
fwrite( $error_file, $http->errstr );

//update SQL
if( $rez )
{
if( rss_plugins_get_option( EC_OVERWRITE_ENC_CONFIG_KEY ) == 1 )
{
// $sql = "update " . getTable( "item" ) . " set description=concat(description,'" .
$sql = "update " . getTable( "item" ) . " set description=description||'" .
rss_real_escape_string( "

Original enclosure link

" ) .
"', enclosure='" .
rss_real_escape_string( rss_plugins_get_option( EC_DOWNLOAD_PREFIX_CONFIG_KEY ) . '/' . $http->FileName() ) .
"' where id=$id";
}
else
{
// $sql = "update " . getTable( "item" ) . " set description=concat(description,'" .
$sql = "update " . getTable( "item" ) . " set description=description||'" .
rss_real_escape_string( "

FileName() . "'>Local enclosure cache link

" ) .
"' where id=$id";
}

rss_query( $sql, false );
if( rss_sql_error() != 0 )
{
$rez = FALSE;
fwrite( $error_file, "SQL: $sqln" );
fwrite( $error_file, rss_sql_error_message() );
}
}

fwrite( $error_file, "nn" );
fclose( $error_file );
if( $rez )
unlink( $savepath . '/' . $id . '_error.txt' );
fclose( $status_file );
unlink( $savepath . '/' . $id . '_status.txt' );

rss_invalidate_cache();
}

//supporting classes

//this class copied and modified from http://us3.php.net/manual/en/function.fopen.php#58099
class __enclosure_cache_HTTPRequest
{
var $_fp; // HTTP socket
var $_url; // full URL
var $_host; // HTTP host
var $_protocol; // protocol (HTTP/HTTPS)
var $_uri; // request URI
var $_port; // port

var $_http_status; //first line of response

var $errno; //error!
var $errstr;
var $_filename; //the filename
var $_savepath; //where to save downloaded files
var $_sizegotten; //how many bytes downloaded
var $_sizetotal; //how many bytes total download
var $_id; //id of the rss item

var $_error_file; //used for flock
var $_status_file; //status reports

// constructor
function __enclosure_cache_HTTPRequest($url, $id, $savepath, $errorfile, $statusfile)
{
$this->_error_file = $errorfile;
$this->_status_file = $statusfile;
$this->_savepath = $savepath;
$this->_id = $id;
$this->_url = $url;
$this->_scan_url();

$this->_sizegotten = 0;
$this->_sizetotal = 0;
}

// scan url
function _scan_url()
{
$this->_filename = __enclosure_cache_filename_mangler( $this->_id, $this->_url );

$req = $this->_url;

$pos = strpos($req, '://');
$this->_protocol = strtolower(substr($req, 0, $pos));

$req = substr($req, $pos+3);
$pos = strpos($req, '/');
if($pos === false)
$pos = strlen($req);
$host = substr($req, 0, $pos);

if(strpos($host, ':') !== false)
{
list($this->_host, $this->_port) = explode(':', $host);
}
else
{
$this->_host = $host;
$this->_port = ($this->_protocol == 'https') ? 443 : 80;
}

$this->_uri = substr($req, $pos);
if($this->_uri == '')
$this->_uri = '/';
}

// download URL
function Download()
{
$crlf = "rn";

// generate request
$req = 'GET ' . $this->_uri . ' HTTP/1.0' . $crlf
. 'Host: ' . $this->_host . $crlf
. $crlf;

// fetch
$this->_fp = fsockopen(($this->_protocol == 'https' ? 'ssl://' : '') . $this->_host, $this->_port, $this->errno, $this->errstr);
if( !$this->_fp )
{
$errs = $this->errstr;
$this->errstr = "Attempting to open '" . $this->_url . "' failed:n" . $errs;
return FALSE;
}

$handle = false;
$lasttick = 0;
$response = "";
fwrite($this->_fp, $req);
while(is_resource($this->_fp) && $this->_fp && !feof($this->_fp))
{
//status dump
if( ( time() - $lasttick >= 2 ) && flock( $this->_error_file, LOCK_EX ) )
{
fseek( $this->_status_file, 0 );
ftruncate( $this->_status_file, 0 );
fwrite( $this->_status_file, "Downloading: " . $this->_filename . "  " );
if( $this->_sizetotal )
fwrite( $this->_status_file, sprintf( "%.1f", $this->_sizegotten / $this->_sizetotal * 100.0 ) . "% (" );
fwrite( $this->_status_file, $this->_sizegotten );
if( $this->_sizetotal )
fwrite( $this->_status_file, " of " . $this->_sizetotal . " bytes)" );
else
fwrite( $this->_status_file, " bytes of an unknown file size." );
flock( $this->_error_file, LOCK_UN );
$lasttick = time();
}

$response .= fread($this->_fp, 2048);
if( $handle === false )
{
// split header and body
$pos = strpos($response, $crlf . $crlf);
if($pos !== false)
{
$header = substr($response, 0, $pos);
$body = substr($response, $pos + 2 * strlen($crlf));

// parse headers
$headers = array();
$lines = explode($crlf, $header);
foreach($lines as $line)
if(($pos = strpos($line, ':')) !== false)
$headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos+1));
else if( !isset( $this->_http_status ) )
$this->_http_status = $line;

// redirection?
if(isset($headers))
{
# $http = new __enclosure_cache_HTTPRequest($headers, $this->_savepath);
$http = new __enclosure_cache_HTTPRequest($headers, $this->_id, $this->_savepath, $this->_error_file, $this->_status_file);
fclose($this->_fp);
return $http->Download();
}
// filename? (we certainly hope so!)
else
{
if(isset($headers))
$this->_sizetotal = $headers + 0;
if(isset($headers))
{
if( preg_match( "/filenames*=s**([^'"s]+)*/i", $headers, $matches ) > 0 )
$this->_filename = __enclosure_cache_filename_mangler( $this->_id, $matches[1] );
}
}

$handle = fopen( $this->_savepath . '/' . $this->_filename, "wb");
if( !$handle )
{
$this->errstr = "cannot create file: '" . $this->_savepath . '/' . $this->_filename . "'";
fclose($this->_fp);
return FALSE;
}
$this->_sizegotten = strlen( $body );
fwrite( $handle, $body );
$response = "";
}
}
else
{
$this->_sizegotten += strlen( $response );
fwrite( $handle, $response );
$response = "";
}
}

if( $response != "" )
{
$handle = fopen( $this->_savepath . '/' . $this->_filename, "wb");
if( !$handle )
{
$this->errstr = "cannot create file: '" . $this->_savepath . '/' . $this->_filename . "'";
fclose($this->_fp);
return FALSE;
}
fwrite( $handle, $response );
}

fclose( $handle );
fclose($this->_fp);

if( preg_match( "/200s*OK/i", $this->_http_status ) != 1 )
{
$this->errstr = $this->_http_status;
return FALSE;
}

return TRUE;
}

function FileName()
{
return ($this->_filename);
}
}

//This class from http://us2.php.net/manual/en/features.commandline.php#52475

/**********************************************
* Simple argv[] parser for CLI scripts
* Diego Mendes Rodrigues - S?o Paulo - Brazil
* diego.m.rodrigues [at] gmail [dot] com
* May/2005
**********************************************/

class __enclosure_cache_arg_parser {
var $argc;
var $argv;
var $parsed;
var $force_this;

function __enclosure_cache_arg_parser($force_this="") {
global $argc, $argv;
$this->argc = $argc;
$this->argv = $argv;
$this->parsed = array();

array_push($this->parsed,
array($this->argv[0]) );

if ( !empty($force_this) )
if ( is_array($force_this) )
$this->force_this = $force_this;

//Sending parameters to $parsed
if ( $this->argc > 1 ) {
for($i=1 ; $iargc ; $i++) {
//We only have passed -xxxx
if ( substr($this->argv[$i],0,1) == "-" ) {
//Se temos -xxxx xxxx
if ( $this->argc > ($i+1) ) {
if ( substr($this->argv[$i+1],0,1) != "-" ) {
array_push($this->parsed,
array($this->argv[$i],
$this->argv[$i+1]) );
$i++;
continue;
}
}
}
//We have passed -xxxxx1 xxxxx2
array_push($this->parsed,
array($this->argv[$i]) );
}
}

//Testing if all necessary parameters have been passed
$this->force();
}

//Testing if one parameter have benn passed
function passed($argumento) {
for($i=0 ; $iargc ; $i++)
if ( $this->parsed[$i][0] == $argumento )
return $i;
return 0;
}

//Testing if you have passed a estra argument, -xxxx1 xxxxx2
function full_passed($argumento) {
$findArg = $this->passed($argumento);
if ( $findArg )
if ( count($this->parsed[$findArg] ) > 1 )
return $findArg;
return 0;
}

//Returns xxxxx2 at a " -xxxx1 xxxxx2" call
function get_full_passed($argumento) {
$findArg = $this->full_passed($argumento);

if ( $findArg )
return $this->parsed[$findArg][1];

return;
}

//Necessary parameters to script
function force() {
if ( is_array( $this->force_this ) ) {
for($i=0 ; $iforce_this) ; $i++) {
if ( $this->force_this[$i][1] == "SIMPLE"
&& !$this->passed($this->force_this[$i][0])
)
die("nnMissing " . $this->force_this[$i][0] . "nn");

if ( $this->force_this[$i][1] == "FULL"
&& !$this->full_passed($this->force_this[$i][0])
)
die("nnMissing " . $this->force_this[$i][0] ." nn");
}
}
}
}
?>