pub / yt2rss

Transforms a youtube channel into a podcast RSS feed to insert into a podcatcher
git clone https://src.jayvii.de/pub/yt2rss.git
Home | Log | Files | Exports | Refs | Submodules | README | LICENSE | RSS

commit d7a6deb16a50e05b0e761ed64987e9c93eca4995
parent 689f9dee3a583d1f845c9596879aa03874b6fad8
Author: JayVii <jayvii[AT]posteo[DOT]de>
Date:   Thu, 21 Nov 2024 18:46:07 +0100

feat: code cleanup

Diffstat:
Mindex.php | 97++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
1 file changed, 59 insertions(+), 38 deletions(-)

diff --git a/index.php b/index.php @@ -15,6 +15,11 @@ function analyze_video($video_file) { return $video_info; } +function ytdl_status($video_id) { + $pid = passthru("ps ax | grep -v grep | grep " . $video_id); + return strlen($pid) > 0; +} + // Authentification if ($_GET["auth"] != $auth_key && !is_null($auth_key)) { $auth = false; @@ -22,26 +27,41 @@ if ($_GET["auth"] != $auth_key && !is_null($auth_key)) { $auth = true; } -if (!is_null($_GET["channel"]) && $auth) { +if (array_key_exists("video", $_GET)) { + $video = basename($_GET["video"]); +} else { + $video = null; +} +if (array_key_exists("channel", $_GET)) { + $channel = basename($_GET["channel"]); +} else { + $channel = null; +} + + + +if (!empty($channel) && $auth) { // Fetch Youtube XML Feed $channel_xml = file( - "https://www.youtube.com/feeds/videos.xml?channel_id=" . basename($_GET["channel"]) + "https://www.youtube.com/feeds/videos.xml?channel_id=" . $channel ); + // Replace un-parsable items $channel_xml = str_replace( array("yt:", "media:"), array("yt_", "media_"), $channel_xml ); + // Cast Array to string $channel_xml = implode(PHP_EOL, $channel_xml); + // Parse XML $channel_xml = simplexml_load_string($channel_xml); $channel_xml = json_encode($channel_xml); $channel_xml = json_decode($channel_xml, true); - // Construct Podcatcher XML $rss_xml = "<rss " . "version=\"2.0\" " . @@ -64,8 +84,8 @@ if (!is_null($_GET["channel"]) && $auth) { "", $channel_xml["id"] ); - $rss_xml = $rss_xml . "<link>https://www.youtube.com/channel/" . - basename($_GET["channel"]) . "</link>\n"; + $rss_xml = $rss_xml . "<link>https://www.youtube.com/channel/" . $channel . + "</link>\n"; $rss_xml = $rss_xml . "<description>" . str_replace( array("&"), @@ -75,6 +95,7 @@ if (!is_null($_GET["channel"]) && $auth) { "</description>\n"; $rss_xml = $rss_xml . "<pubDate>" . $channel_xml["published"] . "</pubDate>\n"; + // FIXME: fetch channel image rather than first video image $video_id = str_replace( array("yt_video:"), @@ -85,7 +106,8 @@ if (!is_null($_GET["channel"]) && $auth) { $video_id . "/hqdefault.jpg\"/>\n"; $rss_xml = $rss_xml . "<atom:link href=\"https://" . $_SERVER["SERVER_NAME"] . - "/?channel=" . basename($_GET["channel"]); + "/?channel=" . $channel; + // Inject auth key if (!is_null($auth_key)) { $rss_xml = $rss_xml . "&amp;auth=" . $auth_key; } @@ -94,17 +116,21 @@ if (!is_null($_GET["channel"]) && $auth) { // Add media items foreach ($channel_xml["entry"] as $entry) { + // Skip if title matches "exclude" if (!is_null($_GET["exclude"])) { $excluded = strstr($entry["title"], rawurldecode($_GET["exclude"])); if ($excluded) continue; } + // Skip if title does not match "include" if (!is_null($_GET["include"])) { $included = strstr($entry["title"], rawurldecode($_GET["include"])); if (!$included) continue; } + $video_id = str_replace(array("yt_video:"), "", $entry["id"]); + // Get Video Length, size and type if (file_exists($video_id . ".opus")) { $video_info = analyze_video($video_id . ".opus"); @@ -119,7 +145,8 @@ if (!is_null($_GET["channel"]) && $auth) { $rss_xml = $rss_xml . "<item>\n"; $rss_xml = $rss_xml . "<title>" . str_replace(array("&"), "&amp;", $entry["title"]) . "</title>\n"; - // FIXME: fetch true description! + + // Add description $rss_xml = $rss_xml . "<description>" . "Video-Link:" . PHP_EOL . $entry["link"]["@attributes"]["href"] . PHP_EOL . PHP_EOL . @@ -128,6 +155,8 @@ if (!is_null($_GET["channel"]) && $auth) { "&amp;", $entry["media_group"]["media_description"] ) . "</description>\n"; + + // Add author $rss_xml = $rss_xml . "<itunes:author>" . str_replace( array("&"), @@ -135,6 +164,7 @@ if (!is_null($_GET["channel"]) && $auth) { $entry["author"]["name"] ) . "</itunes:author>\n"; + $rss_xml = $rss_xml . "<pubDate>" . $entry["published"] . "</pubDate>\n"; $rss_xml = $rss_xml . "<itunes:image href=\"https://i1.ytimg.com/vi/" . @@ -142,6 +172,7 @@ if (!is_null($_GET["channel"]) && $auth) { $rss_xml = $rss_xml . "<enclosure url=\"https://" . $_SERVER["SERVER_NAME"] . "/?video=" . $video_id; + // Add auth key if (!is_null($auth_key)) { $rss_xml = $rss_xml . "&amp;auth=" . $auth_key; } @@ -157,54 +188,44 @@ if (!is_null($_GET["channel"]) && $auth) { print_r($rss_xml); die(); -} else if (!is_null($_GET["video"]) && $auth) { +} else if (!empty($video) && $auth) { + + // Re-try downloading as long as the file does not exist and ytdl does not + // run on the current video $download_retry = 0; while ( - !file_exists(basename($_GET["video"]) . ".opus") && + !file_exists($video . ".opus") && + !ytdl_status($video_id) && $download_retry <= 3 ) { $download_retry++; - if ( - strlen( - system("ps ax | grep -v grep | grep " . basename($_GET["video"])) - ) < 1 - ) { - passthru( - "yt-dlp " . - "-x " . - "--audio-format opus " . - "-o '%(id)s.%(ext)s' " . - "https://www.youtube.com/watch?v=" . basename($_GET["video"]) - ); - } + passthru( + "yt-dlp " . + "-x " . + "--audio-format opus " . + "-o '%(id)s.%(ext)s' " . + "https://www.youtube.com/watch?v=" . $video + ); } + // If file has been downloaded properly, check whether the file is valid - if (!(analyze_video(basename($_GET["video"]) . ".opus")["playtime_seconds"] > 0)) { - // Remove if unvalid + if (!(analyze_video($video . ".opus")["playtime_seconds"] > 0)) { unlink(basename($_GET["video"]) . ".opus"); } + // If file still exists, return to user - if ( - file_exists(basename($_GET["video"]) . ".opus") && - strlen( - system("ps ax | grep -v grep | grep " . basename($_GET["video"])) - ) < 1 - ) { + if (file_exists($video . ".opus") && !ytdl_status($video)) { header("content-type: audio/ogg; codec=opus"); - header( - "content-length: " . filesize(basename($_GET["video"]) . ".opus") - ); - header( - "content-disposition: inline; filename=" . - basename($_GET["video"]) . ".opus" - ); - readfile(basename($_GET['video']) . ".opus"); + header("content-length: " . filesize($video . ".opus")); + header("content-disposition: inline; filename=" . $video . ".opus"); + readfile($video . ".opus"); } else { // otherwise return error and exit http_response_code(404); } die(); } else { + ?> <html>