Skip to content

Instantly share code, notes, and snippets.

@Kubik-Rubik
Forked from SniperSister/readmedia.php
Last active May 20, 2025 20:12
Show Gist options
  • Save Kubik-Rubik/ffa9660a5b4d38407ed8439514e1e19c to your computer and use it in GitHub Desktop.
Save Kubik-Rubik/ffa9660a5b4d38407ed8439514e1e19c to your computer and use it in GitHub Desktop.
readmedia.php for Joomla 4 and 5
<?php
/**
* @copyright Copyright (C) 2018 David Jardin - djumla GmbH
* @package Readmedia
* @version 1.0.0
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://www.djumla.de
*
* 1.0.0-KRJE
* - Updated version to support video streams in Safari (Webkit) - Viktor Vogel - https://www.kubik-rubik.de
*/
declare(strict_types=1);
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Factory;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
use Joomla\Session\Session as SessionAlias;
use Joomla\Session\SessionInterface;
/* Initialize Joomla framework */
const _JEXEC = 1;
// Load system defines
if (file_exists(__DIR__ . '/defines.php')) {
require_once __DIR__ . '/defines.php';
}
if (!defined('_JDEFINES')) {
define('JPATH_BASE', __DIR__);
require_once JPATH_BASE . '/includes/defines.php';
}
require_once JPATH_BASE . '/includes/framework.php';
/* Create the Application */
$container = Factory::getContainer();
$container->alias('session.web', 'session.web.site')
->alias('session', 'session.web.site')
->alias('JSession', 'session.web.site')
->alias(Session::class, 'session.web.site')
->alias(SessionAlias::class, 'session.web.site')
->alias(SessionInterface::class, 'session.web.site');
// Instantiate the application.
$app = $container->get(SiteApplication::class);
// Load the extension Namespaces
JLoader::register('JNamespacePsr4Map', JPATH_LIBRARIES . '/namespacemap.php');
$extensionPsr4Loader = new JNamespacePsr4Map();
$extensionPsr4Loader->load();
// Set the application as global app
Factory::$application = $app;
// Actual permission check starts here
if (!Factory::getApplication()->getSession()->get('user')->id) {
header('HTTP/1.0 403 access denied');
exit('Access denied');
}
$requestedFile = urldecode(str_replace(Uri::root(), '', Uri::current()));
$location = __DIR__ . '/' . $requestedFile;
if (!file_exists($location)) {
header('HTTP/1.0 404 file not found');
exit('File not found');
}
$size = filesize($location);
$time = date('r', filemtime($location));
$fm = @fopen($location, 'rb');
if (!$fm) {
header('HTTP/1.0 500 Internal Server Error');
exit();
}
$start = 0;
$end = $size - 1;
$length = $size;
if ($_SERVER['HTTP_RANGE'] ?? null) {
if (preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches)) {
$start = (int)$matches[1];
if (($matches[2] ?? null) && is_numeric($matches[2])) {
$end = (int)$matches[2];
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $start-$end/$size");
}
} else {
header('HTTP/1.1 200 OK');
}
header('Content-Type: ' . mime_content_type($location));
header('Accept-Ranges: bytes');
header('Content-Length: ' . $length);
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Content-Disposition: inline; filename=' . basename($location));
header('Content-Transfer-Encoding: binary');
header("Last-Modified: $time");
header('Connection: close');
fseek($fm, $start);
$bufferSize = 1024 * 16;
while (!feof($fm) && ($pos = ftell($fm)) <= $end && connection_status() === CONNECTION_NORMAL) {
if ($pos + $bufferSize > $end) {
$bufferSize = $end - $pos + 1;
}
echo fread($fm, $bufferSize);
flush();
}
fclose($fm);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment