const VERSION = "0.1.0"; interface CliArgs { version: boolean; query: string | null; play: string | null; } function parseArgs(): CliArgs { const args = process.argv.slice(2); const result: CliArgs = { version: false, query: null, play: null, }; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg === "--version" || arg === "-v") { result.version = true; } else if (arg === "--query" || arg === "-q") { result.query = args[i + 1] || ""; i++; } else if (arg === "--play" || arg === "-p") { result.play = args[i + 1] || ""; i++; } } return result; } const cliArgs = parseArgs(); if (cliArgs.version) { console.log(`PodTUI version ${VERSION}`); process.exit(0); } if (cliArgs.query !== null || cliArgs.play !== null) { import("./utils/feeds-persistence").then(async ({ loadFeedsFromFile }) => { const feeds = await loadFeedsFromFile(); if (cliArgs.query !== null) { const query = cliArgs.query; const normalizedQuery = query.toLowerCase(); const matches = feeds.filter((feed) => { const title = feed.podcast.title.toLowerCase(); return title.includes(normalizedQuery); }); if (matches.length === 0) { console.log(`No shows found matching: ${query}`); if (feeds.length > 0) { console.log("\nAvailable shows:"); feeds.slice(0, 5).forEach((feed) => { console.log(` - ${feed.podcast.title}`); }); if (feeds.length > 5) { console.log(` ... and ${feeds.length - 5} more`); } } process.exit(0); } if (matches.length === 1) { const feed = matches[0]; console.log(`\n${feed.podcast.title}`); if (feed.podcast.description) { console.log(feed.podcast.description.substring(0, 200) + (feed.podcast.description.length > 200 ? "..." : "")); } console.log(`\nRecent episodes (${Math.min(5, feed.episodes.length)}):`); feed.episodes.slice(0, 5).forEach((ep, idx) => { const date = ep.pubDate instanceof Date ? ep.pubDate.toLocaleDateString() : String(ep.pubDate); console.log(` ${idx + 1}. ${ep.title} (${date})`); }); process.exit(0); } console.log(`\nClosest matches for "${query}":`); matches.slice(0, 5).forEach((feed, idx) => { console.log(` ${idx + 1}. ${feed.podcast.title}`); }); process.exit(0); } if (cliArgs.play !== null) { const playArg = cliArgs.play; const normalizedArg = playArg.toLowerCase(); let feedResult: typeof feeds[0] | null = null; let episodeResult: typeof feeds[0]["episodes"][0] | null = null; if (normalizedArg === "latest") { let latestFeed: typeof feeds[0] | null = null; let latestEpisode: typeof feeds[0]["episodes"][0] | null = null; let latestDate = 0; for (const feed of feeds) { if (feed.episodes.length > 0) { const ep = feed.episodes[0]; const epDate = ep.pubDate instanceof Date ? ep.pubDate.getTime() : Number(ep.pubDate); if (epDate > latestDate) { latestDate = epDate; latestFeed = feed; latestEpisode = ep; } } } feedResult = latestFeed; episodeResult = latestEpisode; } else { const parts = normalizedArg.split("/"); const showQuery = parts[0]; const episodeQuery = parts[1]; const matchingFeeds = feeds.filter((feed) => feed.podcast.title.toLowerCase().includes(showQuery) ); if (matchingFeeds.length === 0) { console.log(`No show found matching: ${showQuery}`); process.exit(1); } const feed = matchingFeeds[0]; if (!episodeQuery) { if (feed.episodes.length > 0) { feedResult = feed; episodeResult = feed.episodes[0]; } else { console.log(`No episodes available for: ${feed.podcast.title}`); process.exit(1); } } else if (episodeQuery === "latest") { feedResult = feed; episodeResult = feed.episodes[0]; } else { const matchingEpisode = feed.episodes.find((ep) => ep.title.toLowerCase().includes(episodeQuery) ); if (matchingEpisode) { feedResult = feed; episodeResult = matchingEpisode; } else { console.log(`Episode not found: ${episodeQuery}`); console.log(`Available episodes for ${feed.podcast.title}:`); feed.episodes.slice(0, 5).forEach((ep, idx) => { console.log(` ${idx + 1}. ${ep.title}`); }); process.exit(1); } } } if (!feedResult || !episodeResult) { console.log("Could not find episode to play"); process.exit(1); } console.log(`\nPlaying: ${episodeResult.title}`); console.log(`Show: ${feedResult.podcast.title}`); try { const { createAudioBackend } = await import("./utils/audio-player"); const backend = createAudioBackend(); if (episodeResult.audioUrl) { await backend.play(episodeResult.audioUrl); console.log("Playback started (use the UI to control)"); } else { console.log("No audio URL available for this episode"); process.exit(1); } } catch (err) { console.error("Playback error:", err); process.exit(1); } } }).catch((err) => { console.error("Error:", err); process.exit(1); }); } else { import("@opentui/solid").then(async ({ render, useRenderer }) => { const { App } = await import("./App"); const { ThemeProvider } = await import("./context/ThemeContext"); const toast = await import("./ui/toast"); const { KeybindProvider } = await import("./context/KeybindContext"); const { NavigationProvider } = await import("./context/NavigationContext"); const { DialogProvider } = await import("./ui/dialog"); const { CommandProvider } = await import("./ui/command"); function RendererSetup(props: { children: unknown }) { const renderer = useRenderer(); renderer.disableStdoutInterception(); return props.children; } render( () => ( ), { useThread: false }, ); }); }