123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
<?php
class Audio {
private $webPath;
private $allowedAudioExtensions = [
'mp3', 'wav', 'aac', 'flac', 'm4a', 'ogg', 'oga', 'opus'
];
public function __construct($webPath) {
$this->webPath = $webPath;
}
public function isAudioViewable($extension) {
global $config;
if (empty($extension) || trim($extension) === '') {
return false;
}
$extension = strtolower($extension);
if (!in_array($extension, $this->allowedAudioExtensions)) {
return false;
}
$settingKey = getExtensionSetting($extension, 'viewing');
if ($settingKey !== null) {
return isset($config['viewable_files'][$settingKey]) ?
$config['viewable_files'][$settingKey] : false;
}
return false;
}
public function renderAudioPlayer($fullPath, $filename, $extension, $currentPath) {
global $router, $config;
$pathParts = explode('/', trim($currentPath, '/'));
$lastPart = end($pathParts);
if ($lastPart === $filename) {
array_pop($pathParts);
$currentPath = implode('/', $pathParts);
}
$this->serveAudioView($fullPath, $filename, $extension, $currentPath);
exit;
}
private function serveAudioView($fullPath, $filename, $extension, $currentPath) {
global $router, $config, $iconType;
$fileSize = filesize($fullPath);
$fileSizeFormatted = $this->formatBytes($fileSize);
$fileModified = date('Y-m-d H:i:s', filemtime($fullPath));
$audioInfo = $this->getAudioInfo($fullPath, $extension);
$downloadUrl = $router->generateFileURL($currentPath, $filename, 'download');
$viewUrl = $router->generateFileURL($currentPath, $filename, 'view', 'default');
$originalRawUrl = $router->generateFileURL($currentPath, $filename, 'view', 'raw');
$audioDisplayUrl = $originalRawUrl;
$currentFullUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$backUrl = preg_replace('/\/[^\/]+\/\?.*$/', '/', $currentFullUrl);
if (!preg_match('/\?/', $currentFullUrl)) {
$backUrl = preg_replace('/\/[^\/]+\/$/', '/', $currentFullUrl);
}
$securityStatus = getSecurityStatus();
$lockIcon = $securityStatus['secure']
? $this->webPath . '/.indexer_files/icons/app/green.png'
: $this->webPath . '/.indexer_files/icons/app/red.png';
$mimeType = $this->getAudioMimeType($extension);
$canPlayNatively = $this->canPlayInBrowser($extension);
$displayTitle = $audioInfo['title'] ?: $filename;
$displayArtist = $audioInfo['artist'];
$hasCoverArt = !empty($audioInfo['cover_art']);
$audioIconPath = getIconPath('file', $extension);
$useIcon = ($iconType !== 'disabled');
$useEmoji = ($iconType === 'emoji' || $audioIconPath === null);
$iconSrc = $audioIconPath;
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="application-name" content="5q12's Indexer">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Indexer">
<meta name="mobile-web-app-capable" content="yes">
<meta name="theme-color" content="#2a2a2a">
<link rel="manifest" href="<?php echo $this->webPath; ?>/.indexer_files/local_api/manifest.json">
<title><?php echo htmlspecialchars($displayTitle); ?></title>
<link rel="icon" type="image/x-icon" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/icon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/32x32.png">
<link rel="icon" type="image/png" sizes="48x48" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/48x48.png">
<link rel="icon" type="image/png" sizes="96x96" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/96x96.png">
<link rel="icon" type="image/png" sizes="144x144" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/144x144.png">
<link rel="icon" type="image/png" sizes="192x192" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/192x192.png">
<link rel="apple-touch-icon" sizes="180x180" href="<?php echo $this->webPath; ?>/.indexer_files/favicon/180x180.png">
<link rel="stylesheet" href="<?php echo $this->webPath; ?>/.indexer_files/local_api/style/base-2.0.0-r0.min.css">
<link rel="stylesheet" href="<?php echo $this->webPath; ?>/.indexer_files/local_api/style/audio-2.0.0-r0.min.css">
</head>
<body>
<div class="security-bar">
<span class="security-lock" data-tooltip="<?php echo $securityStatus['secure'] ? 'Connection is secure (HTTPS)' : 'Connection is not secure - Consider using HTTPS'; ?>">
<img src="<?php echo htmlspecialchars($lockIcon); ?>" alt="<?php echo $securityStatus['secure'] ? 'Secure' : 'Not Secure'; ?>">
</span>
<div class="security-bar-filename"><?php echo htmlspecialchars($filename); ?></div>
<div class="security-bar-buttons">
<a href="<?php echo htmlspecialchars($originalRawUrl); ?>" class="security-bar-btn">Raw</a>
<a href="#" class="security-bar-btn active">View</a>
</div>
</div>
<div class="audio-container">
<div class="audio-player-wrapper">
<?php if (!$canPlayNatively): ?>
<div class="error-message">
This audio format (<?php echo strtoupper(htmlspecialchars($extension)); ?>) may not be natively supported by your browser.<br>
Please download the file or try opening it in a compatible media player.
</div>
<?php else: ?>
<div class="audio-player">
<?php if ($hasCoverArt): ?>
<div class="audio-cover-art">
<img src="<?php echo htmlspecialchars($audioInfo['cover_art']); ?>" alt="Cover Art">
</div>
<?php elseif (!$useIcon): ?>
<?php elseif ($useEmoji): ?>
<div class="audio-icon-emoji">🎵</div>
<?php else: ?>
<div class="audio-icon-img">
<img src="<?php echo htmlspecialchars($iconSrc); ?>" alt="<?php echo strtoupper(htmlspecialchars($extension)); ?>">
</div>
<?php endif; ?>
<div class="audio-metadata">
<div class="audio-title"><?php echo htmlspecialchars($displayTitle); ?></div>
<?php if ($displayArtist): ?>
<div class="audio-artist"><?php echo htmlspecialchars($displayArtist); ?></div>
<?php endif; ?>
<?php if ($audioInfo['album']): ?>
<div class="audio-album"><?php echo htmlspecialchars($audioInfo['album']); ?></div>
<?php endif; ?>
</div>
<audio controls preload="metadata">
<source src="<?php echo htmlspecialchars($audioDisplayUrl); ?>" type="<?php echo htmlspecialchars($mimeType); ?>">
Your browser does not support the audio element.
</audio>
</div>
<?php endif; ?>
</div>
</div>
<div class="audio-info">
<div class="info-grid">
<div class="info-label">Filename:</div>
<div class="info-value"><?php echo htmlspecialchars($filename); ?></div>
<?php if ($audioInfo['title'] && $audioInfo['title'] !== $filename): ?>
<div class="info-label">Title:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['title']); ?></div>
<?php endif; ?>
<?php if ($displayArtist): ?>
<div class="info-label">Artist:</div>
<div class="info-value"><?php echo htmlspecialchars($displayArtist); ?></div>
<?php endif; ?>
<?php if ($audioInfo['album']): ?>
<div class="info-label">Album:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['album']); ?></div>
<?php endif; ?>
<?php if ($audioInfo['year']): ?>
<div class="info-label">Year:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['year']); ?></div>
<?php endif; ?>
<?php if ($audioInfo['genre']): ?>
<div class="info-label">Genre:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['genre']); ?></div>
<?php endif; ?>
<div class="info-label">Type:</div>
<div class="info-value"><?php echo strtoupper(htmlspecialchars($extension)); ?> Audio</div>
<?php if ($audioInfo['duration']): ?>
<div class="info-label">Duration:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['duration']); ?></div>
<?php endif; ?>
<?php if ($audioInfo['bitrate']): ?>
<div class="info-label">Bitrate:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['bitrate']); ?></div>
<?php endif; ?>
<?php if ($audioInfo['sample_rate']): ?>
<div class="info-label">Sample Rate:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['sample_rate']); ?></div>
<?php endif; ?>
<?php if ($audioInfo['channels']): ?>
<div class="info-label">Channels:</div>
<div class="info-value"><?php echo htmlspecialchars($audioInfo['channels']); ?></div>
<?php endif; ?>
<div class="info-label">File Size:</div>
<div class="info-value"><?php echo htmlspecialchars($fileSizeFormatted); ?></div>
<div class="info-label">Modified:</div>
<div class="info-value"><?php echo htmlspecialchars($fileModified); ?></div>
</div>
<div class="action-buttons">
<a href="<?php echo htmlspecialchars($backUrl); ?>" class="action-btn primary">← Back to Folder</a>
<a href="<?php echo htmlspecialchars($originalRawUrl); ?>" class="action-btn" target="_blank">Open Raw</a>
<a href="<?php echo htmlspecialchars($downloadUrl); ?>" class="action-btn">Download</a>
</div>
</div>
</body>
</html>
<?php
}
private function getAudioMimeType($extension) {
$mimeTypes = [
'mp3' => 'audio/mpeg',
'wav' => 'audio/wav',
'aac' => 'audio/aac',
'flac' => 'audio/flac',
'm4a' => 'audio/mp4',
'ogg' => 'audio/ogg',
'oga' => 'audio/ogg',
'opus' => 'audio/ogg',
];
return $mimeTypes[$extension] ?? 'audio/mpeg';
}
private function canPlayInBrowser($extension) {
$nativelySupported = ['mp3', 'wav', 'ogg', 'oga', 'opus', 'm4a', 'aac', 'flac'];
if (in_array($extension, $nativelySupported)) {
return true;
}
return false;
}
private function getAudioInfo($fullPath, $extension) {
$info = [
'title' => null,
'artist' => null,
'album' => null,
'year' => null,
'genre' => null,
'duration' => null,
'bitrate' => null,
'sample_rate' => null,
'channels' => null,
'cover_art' => null
];
$getID3Path = dirname(__FILE__) . '/getid3/getid3.php';
if (!file_exists($getID3Path)) {
return $info;
}
require_once($getID3Path);
try {
$getID3 = new getID3;
$fileInfo = $getID3->analyze($fullPath);
if (isset($fileInfo['tags'])) {
foreach (['id3v2', 'id3v1', 'vorbiscomment', 'quicktime', 'riff', 'asf'] as $tagFormat) {
if (isset($fileInfo['tags'][$tagFormat])) {
$tags = $fileInfo['tags'][$tagFormat];
if (!$info['title'] && isset($tags['title'][0])) {
$info['title'] = $tags['title'][0];
}
if (!$info['artist'] && isset($tags['artist'][0])) {
$info['artist'] = $tags['artist'][0];
}
if (!$info['album'] && isset($tags['album'][0])) {
$info['album'] = $tags['album'][0];
}
if (!$info['year'] && isset($tags['year'][0])) {
$info['year'] = $tags['year'][0];
}
if (!$info['genre'] && isset($tags['genre'][0])) {
$info['genre'] = $tags['genre'][0];
}
}
}
}
if (isset($fileInfo['playtime_seconds'])) {
$seconds = round($fileInfo['playtime_seconds']);
$minutes = floor($seconds / 60);
$seconds = $seconds % 60;
$info['duration'] = sprintf('%d:%02d', $minutes, $seconds);
}
if (isset($fileInfo['audio']['bitrate'])) {
$bitrate = round($fileInfo['audio']['bitrate'] / 1000);
$info['bitrate'] = $bitrate . ' kbps';
}
if (isset($fileInfo['audio']['sample_rate'])) {
$sampleRate = round($fileInfo['audio']['sample_rate'] / 1000, 1);
$info['sample_rate'] = $sampleRate . ' kHz';
}
if (isset($fileInfo['audio']['channels'])) {
$channels = $fileInfo['audio']['channels'];
if ($channels == 1) {
$info['channels'] = 'Mono';
} elseif ($channels == 2) {
$info['channels'] = 'Stereo';
} else {
$info['channels'] = $channels . ' channels';
}
}
if (isset($fileInfo['comments']['picture'][0])) {
$picture = $fileInfo['comments']['picture'][0];
if (isset($picture['data'])) {
$imageData = $picture['data'];
$imageMime = isset($picture['image_mime']) ? $picture['image_mime'] : 'image/jpeg';
$info['cover_art'] = 'data:' . $imageMime . ';base64,' . base64_encode($imageData);
}
}
} catch (Exception $e) {
}
return $info;
}
private function formatBytes($size) {
if ($size === null) return '-';
$units = ['B', 'KB', 'MB', 'GB'];
for ($i = 0; $size > 1024 && $i < count($units) - 1; $i++) {
$size /= 1024;
}
return round($size, 2) . ' ' . $units[$i];
}
}
?>