Skip to content

Instantly share code, notes, and snippets.

@hlorand
Last active October 2, 2025 19:32
Show Gist options
  • Select an option

  • Save hlorand/de105497d35829b439670ffa7058f0a5 to your computer and use it in GitHub Desktop.

Select an option

Save hlorand/de105497d35829b439670ffa7058f0a5 to your computer and use it in GitHub Desktop.
MP3 and YouTube video to Apple Podcasts feed generator
<?php
/*
MP3 and YouTube Video to Apple Podcasts Feed Generator
======================================================
What is this?
-------------
Apple Podcasts can receive custom podcast feed URLs (RSS feeds).
You can generate your own feed from individual MP3 files and YouTube videos you want to listen to.
This is especially useful if you have an Apple Watch and want to quickly send MP3 files to it.
You can upload MP3 files or download MP3s from YouTube videos via POST requests or GET requests using /?video=VIDEO_URL.
This script generates an RSS feed from the mp3 files in the current folder.
Installation
------------
Place this PHP file in a folder on your server, set an upload password in the code below,
and download **yt-dlp** (YouTube downloader) if you want to transcode YouTube videos to MP3 locally:
curl -L [https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp) -o ./yt-dlp
chmod a+rx ./yt-dlp
How to use
----------
Upload your own MP3 or download a YouTube video as MP3:
https://yourserver.com/podcast/
In Apple Podcasts: go to **File > Follow Podcast at URL...** and enter:
https://yourserver.com/podcast/?feed
Now you can listen to the audio in the Podcasts app on any platform: Desktop, iPhone, or Apple Watch.
*/
$password = "YOURPASSWORD";
//////////////////////////////////////////////
$uploadDir = __DIR__ . "/";
// Serve RSS feed if ?feed parameter exists
if (isset($_GET['feed'])) {
// Determine scheme
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'
|| $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
// Get host
$host = $_SERVER['HTTP_HOST'];
// Get current script directory path (e.g. /podcast6/)
$scriptDir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/') . '/';
// Full base URL with scheme, host, and path
$siteUrl = $protocol . $host . $scriptDir;
$files = array_filter(scandir($uploadDir), function ($file) use ($uploadDir) {
return strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'mp3';
});
usort($files, function($a, $b) use ($uploadDir) {
return filemtime($uploadDir . $b) - filemtime($uploadDir . $a);
});
header("Content-Type: application/rss+xml; charset=utf-8");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
echo "<rss version=\"2.0\" xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\">\n";
echo "<channel>\n";
echo " <title>_hallgatni</title>\n";
echo " <link>{$siteUrl}</link>\n";
echo " <language>en-us</language>\n";
echo " <itunes:author>Author Name</itunes:author>\n";
echo " <itunes:subtitle>Podcast subtitle</itunes:subtitle>\n";
echo " <itunes:summary>This is an MP3 podcast feed generated in real time.</itunes:summary>\n";
echo " <description>This is an MP3 podcast feed generated in real time.</description>\n";
echo " <itunes:owner>\n";
echo " <itunes:name>Author Name</itunes:name>\n";
echo " <itunes:email>[email protected]</itunes:email>\n";
echo " </itunes:owner>\n";
echo " <itunes:image href='{$siteUrl}cover.jpg' />\n";
echo " <itunes:category text=\"Technology\" />\n";
foreach ($files as $file) {
$filePath = $uploadDir . $file;
$fileUrl = $siteUrl . rawurlencode($file);
$fileSize = filesize($filePath);
$fileMTime = filemtime($filePath);
$pubDate = date(DATE_RSS, $fileMTime);
$title = pathinfo($file, PATHINFO_FILENAME);
echo " <item>\n";
echo " <title>{$title}</title>\n";
echo " <itunes:title>{$title}</itunes:title>\n";
echo " <enclosure url=\"{$fileUrl}\" length=\"{$fileSize}\" type=\"audio/mpeg\" />\n";
echo " <guid>{$fileUrl}</guid>\n";
echo " <pubDate>{$pubDate}</pubDate>\n";
echo " <itunes:duration>00:00:00</itunes:duration> <!-- Extend with real duration if needed -->\n";
echo " </item>\n";
}
echo "</channel>\n";
echo "</rss>\n";
exit;
}
// Handle GET for quick YouTube download
if (isset($_GET['video'])) {
$youtubeUrl = trim($_GET['video']);
if (filter_var($youtubeUrl, FILTER_VALIDATE_URL)) {
$escapedUrl = escapeshellarg($youtubeUrl);
$command = "nohup ./yt-dlp -x --audio-format mp3 --audio-quality 9 --postprocessor-args \"-ac 1 -b:a 16k\" -o \"%(title)s.%(ext)s\" $escapedUrl > /dev/null 2>&1 &";
exec($command);
echo "Download started asynchronously for the given URL (GET mode).";
} else {
echo "Invalid URL provided.";
}
exit;
}
// Handle POST requests for upload or YouTube download
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// MP3 upload form processing
if (isset($_POST['ozslej']) && isset($_FILES['mp3file'])) {
if ($_POST['ozslej'] !== $password) {
die('Wrong password');
}
$file = $_FILES['mp3file'];
$fileName = basename($file['name']);
$fileTmpName = $file['tmp_name'];
$fileError = $file['error'];
if ($fileError !== UPLOAD_ERR_OK) {
die('Error uploading file');
}
$fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if ($fileExt !== 'mp3') {
die('File is not an mp3');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $fileTmpName);
finfo_close($finfo);
if ($mime !== 'audio/mpeg') {
die('File MIME type is not mp3');
}
$targetFile = $uploadDir . $fileName;
if (move_uploaded_file($fileTmpName, $targetFile)) {
echo 'File uploaded successfully: ' . htmlspecialchars($fileName);
} else {
die('Failed to move uploaded file');
}
exit;
}
// YouTube URL download form processing (requires password too)
if (isset($_POST['ozslej']) && isset($_POST['youtube_url'])) {
if ($_POST['ozslej'] !== $password) {
die('Wrong password');
}
$youtubeUrl = trim($_POST['youtube_url']);
if (filter_var($youtubeUrl, FILTER_VALIDATE_URL)) {
$escapedUrl = escapeshellarg($youtubeUrl);
// Run yt-dlp asynchronously with mono 16 kbps mp3 settings
$command = "nohup ./yt-dlp -x --audio-format mp3 --audio-quality 9 --postprocessor-args \"-ac 1 -b:a 16k\" -o \"%(title)s.%(ext)s\" $escapedUrl > /dev/null 2>&1 &";
exec($command);
echo "Download started asynchronously for the given URL.";
} else {
echo "Invalid URL provided.";
}
exit;
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<form method="post" enctype="multipart/form-data">
<input type="password" name="ozslej" required placeholder="Password">
<input type="file" name="mp3file" accept="audio/mpeg" required>
<button type="submit">Mehet</button>
</form>
<hr>
<form method="post">
<input type="password" name="ozslej" required placeholder="Password">
<input type="text" name="youtube_url" placeholder="YouTube URL" required style="width: 300px;">
<button type="submit">Dload</button>
</form>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment