Created
August 31, 2022 14:18
-
-
Save mailinglists35/9f799157fe83131873aae9813841d8ce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Settings. | |
$feedName = 'Opus codec'; | |
$feedDesc = 'Opus codec'; | |
$feedURL = 'https://example.com/podcast/'; | |
$coverImageFileName = 'audiobooks.png'; | |
$ffprobePath = '/usr/bin/ffprobe'; | |
$ffmpegPath = '/usr/bin/ffmpeg'; | |
function outputRSS($books) { | |
global $feedName; | |
global $feedDesc; | |
global $feedURL; | |
global $coverImageFileName; | |
setlocale(LC_CTYPE, 'en_US.UTF-8'); | |
header('Content-type: text/xml'); | |
echo '<?xml version="1.0"?>' . "\n"; | |
echo '<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">' . "\n"; | |
echo ' <channel>' . "\n"; | |
// add this to not be indexed in apple/google | |
// echo ' <itunes:block>yes</itunes:block>' . "\n"; | |
// add this to restrict further to not be able to be added in native ios/android podcast apps | |
// echo ' <googleplay:block>yes</googleplay:block>' . "\n"; | |
echo ' <title>' . htmlspecialchars($feedName) . '</title>' . "\n"; | |
echo ' <link>' . htmlspecialchars($feedURL) . '</link>' . "\n"; | |
echo ' <description>' . htmlspecialchars($feedDesc) . '</description>' . "\n"; | |
echo ' <image>' . "\n"; | |
echo ' <url>' . htmlspecialchars($feedURL . $coverImageFileName) . '</url>' . "\n"; | |
echo ' <title>' . htmlspecialchars($feedName) . '</title>' . "\n"; | |
echo ' <link>' . htmlspecialchars($feedURL) . '</link>' . "\n"; | |
echo ' </image>' . "\n"; | |
foreach($books as $book_filename => $book_details) { | |
echo ' <item>' . "\n"; | |
echo ' <title>' . htmlspecialchars($book_details['title']) . '</title>' . "\n"; | |
echo ' <itunes:title>' . htmlspecialchars($book_details['title']) . '</itunes:title>' . "\n"; | |
echo ' <description>' . htmlspecialchars($book_details['description']) . '</description>' . "\n"; | |
echo ' <author>' . htmlspecialchars($book_details['author']) . '</author>' . "\n"; | |
echo ' <pubDate>' . htmlspecialchars($book_details['published_date']) . '</pubDate>' . "\n"; | |
echo ' <link>' . htmlspecialchars($book_details['url']) . '</link>' . "\n"; | |
echo ' <guid>' . htmlspecialchars($book_details['url']) . '</guid>' . "\n"; | |
echo ' <enclosure url="' . htmlspecialchars($book_details['url']) . '" ' . | |
'length="' . htmlspecialchars($book_details['size']) . '" ' . | |
'type="audio/mpeg" />' . "\n"; | |
if (isset($book_details['cover'])) { | |
echo ' <itunes:image href="' . htmlspecialchars($book_details['cover']) . '" />' . "\n"; | |
} | |
echo ' </item>' . "\n"; | |
} | |
echo ' </channel>' . "\n"; | |
echo '</rss>' . "\n"; | |
} | |
function main() { | |
global $feedURL; | |
global $ffprobePath; | |
global $ffmpegPath; | |
// Read the cache file, if it exists. | |
$cache_file = '/tmp/.cache.opus.json'; | |
$cached_book_details = array(); | |
if (file_exists($cache_file)) { | |
$cached_book_details = json_decode(file_get_contents($cache_file), 2); | |
} | |
// Loop through each book (mp3) file. | |
$book_details = array(); | |
foreach (glob('*24k*.opus') as $book_file) { | |
$size = filesize($book_file); | |
// Reuse cached book details if available and the file size hasn't changed. | |
if (isset($cached_book_details[$book_file]) && | |
isset($cached_book_details[$book_file]['size']) && | |
$cached_book_details[$book_file]['size'] == $size) { | |
$book_details[$book_file] = $cached_book_details[$book_file]; | |
continue; | |
} | |
// Book details aren't already cached, so figure them out now. | |
$details = array(); | |
// Read the book's ID3 tags (if any). | |
$id3_tags = json_decode(shell_exec( | |
escapeshellarg($ffprobePath) . ' ' . | |
'-print_format json -show_entries stream=codec_name:format ' . | |
'-select_streams a:0 -v quiet ' . | |
escapeshellarg($book_file) | |
), true)['format']['tags']; | |
// Take note of the book's title. | |
if (isset($id3_tags['title'])) { | |
$details['title'] = $id3_tags['title']; | |
} else { | |
$details['title'] = preg_replace('/\.opus$/', '', $book_file); | |
} | |
// Take note of the book's author. | |
if (isset($id3_tags['artist'])) { | |
$details['author'] = $id3_tags['artist']; | |
} else if (isset($id3_tags['composer'])) { | |
$details['author'] = $id3_tags['composer']; | |
} else { | |
$details['author'] = 'Unknown'; | |
} | |
// Take note of the book's description. | |
// Note: Add extra newlines so that the description is easier to read in | |
// mobile podcast apps. | |
if (isset($id3_tags['description'])) { | |
$details['description'] = preg_replace("/\n+/", "\n\n", | |
$id3_tags['description']); | |
} else { | |
$details['description'] = ''; | |
} | |
// Append the author and narrator details to the description. | |
$details['description'] .= "\n\n" . 'Author: ' . $details['author'] . "\n"; | |
if (isset($id3_tags['narrated_by'])) { | |
$details['description'] .= 'Narrator: ' . $id3_tags['narrated_by']; | |
} | |
// Fetch the cover art (if any is available) from the file if the image is | |
// not yet saved. | |
$cover_file = preg_replace('/\.mp3$/', '.jpg', $book_file); | |
if (file_exists($cover_file) || | |
shell_exec( | |
escapeshellarg($ffmpegPath) . ' -loglevel quiet -i ' . | |
escapeshellarg($book_file) . ' -vcodec copy ' . | |
escapeshellarg($cover_file) | |
)) { | |
$details['cover'] = $feedURL . rawurlencode($cover_file); | |
} | |
// Take note of when the book was published. | |
if (isset($id3_tags['year'])) { | |
$details['published_date'] = date(DATE_RSS, strtotime($id3_tags['year'])); | |
} else { | |
$details['published_date'] = date(DATE_RSS, filemtime($book_file)); | |
} | |
// Take note of the book's other details. | |
$details['url'] = $feedURL . rawurlencode($book_file); | |
$details['size'] = $size; | |
// Save the book's details to the associative array. | |
$book_details[$book_file] = $details; | |
} | |
// Write all the book details to the cache file to save work next time. | |
file_put_contents($cache_file, json_encode($book_details)); | |
// Finally, output the RSS! | |
outputRSS($book_details); | |
} | |
// Ensure the feed URL ends with a '/' and that the ffmpeg paths look correct. | |
// Hopefully that should make configuration less error-prone. | |
if (substr($feedURL, -1) != '/') { | |
$feedURL .= '/'; | |
} | |
if (!file_exists($ffprobePath) || !file_exists($ffmpegPath)) { | |
fwrite(STDERR, 'Incorrect $ffprobePath or $ffmpegPath.' . PHP_EOL); | |
exit(1); | |
} | |
main(); | |
?> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment