<?php
/**
* Jano Radio Manifest Generator
*
* Usage web:
* 1) Copy this file into the folder that contains your audio files.
* 2) Open https://example.com/music/jano-radio-manifest.php
* 3) In WordPress > Jano Radio > Morceaux, import that URL.
*
* Optional protection:
* Set $token = 'choose-a-long-secret'; then call:
* https://example.com/music/jano-radio-manifest.php?token=choose-a-long-secret
*
* Usage CLI:
* php jano-radio-manifest.php > index.json
*/
$token = ''; // Optional: put a secret here, for example 'change-this-long-secret'.
$audioExtensions = array('mp3', 'm4a', 'aac', 'ogg', 'oga', 'opus', 'wav', 'flac');
$defaultDuration = 180;
$maxFiles = 5000;
$recursive = true;
if (PHP_SAPI !== 'cli') {
if ($token !== '' && (!isset($_GET['token']) || hash_equals($token, (string) $_GET['token']) === false)) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo "Forbidden\n";
exit;
}
if (isset($_GET['duration'])) {
$defaultDuration = max(1, (int) $_GET['duration']);
}
if (isset($_GET['max'])) {
$maxFiles = min(20000, max(1, (int) $_GET['max']));
}
if (isset($_GET['recursive'])) {
$recursive = (string) $_GET['recursive'] !== '0';
}
}
$root = realpath(__DIR__);
if ($root === false || !is_dir($root)) {
output_manifest(array(), $defaultDuration);
exit;
}
$files = collect_audio_files($root, $audioExtensions, $recursive, $maxFiles);
natcasesort($files);
$files = array_values($files);
$tracks = array();
foreach ($files as $relativePath) {
$tracks[] = array(
'url' => encode_relative_url($relativePath),
'title' => title_from_filename($relativePath),
'artist' => '',
'duration' => $defaultDuration,
);
}
output_manifest($tracks, $defaultDuration);
function collect_audio_files($root, $audioExtensions, $recursive, $maxFiles)
{
$out = array();
$root = rtrim(str_replace('\\', '/', $root), '/');
if ($recursive) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileInfo) {
if (!$fileInfo->isFile()) {
continue;
}
$path = str_replace('\\', '/', $fileInfo->getRealPath());
if (should_skip_path($path, $root)) {
continue;
}
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (!in_array($ext, $audioExtensions, true)) {
continue;
}
$out[] = ltrim(substr($path, strlen($root)), '/');
if (count($out) >= $maxFiles) {
break;
}
}
} else {
foreach (scandir($root) as $name) {
if ($name === '.' || $name === '..' || substr($name, 0, 1) === '.') {
continue;
}
$path = $root . '/' . $name;
if (!is_file($path)) {
continue;
}
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (!in_array($ext, $audioExtensions, true)) {
continue;
}
$out[] = $name;
if (count($out) >= $maxFiles) {
break;
}
}
}
return $out;
}
function should_skip_path($path, $root)
{
$relative = ltrim(substr($path, strlen($root)), '/');
foreach (explode('/', $relative) as $segment) {
if ($segment === '' || substr($segment, 0, 1) === '.') {
return true;
}
}
return false;
}
function encode_relative_url($relativePath)
{
$relativePath = str_replace('\\', '/', $relativePath);
$parts = explode('/', $relativePath);
$encoded = array();
foreach ($parts as $part) {
$encoded[] = rawurlencode($part);
}
return implode('/', $encoded);
}
function title_from_filename($relativePath)
{
$base = basename(str_replace('\\', '/', $relativePath));
$base = preg_replace('/\.[a-z0-9]{2,8}$/i', '', $base);
$base = rawurldecode($base);
$base = str_replace(array('_', '-'), ' ', $base);
$base = preg_replace('/\s+/', ' ', $base);
return trim($base) !== '' ? trim($base) : 'Morceau audio';
}
function output_manifest($tracks, $defaultDuration)
{
$manifest = array(
'generated_at' => gmdate('c'),
'default_duration' => $defaultDuration,
'tracks' => $tracks,
);
if (PHP_SAPI !== 'cli') {
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
}
echo json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
echo "\n";
}