Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions scripts/build-rss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function generateRSSFeed(episodes: PodcastEpisode[], trailers: PodcastTrailer[],
<itunes:duration>${episode.duration ? formatDurationForRSS(episode.duration) : '00:00'}</itunes:duration>
<itunes:explicit>${episode.explicit ? 'yes' : 'no'}</itunes:explicit>
${episode.imageUrl ? `<itunes:image href="${escapeXml(episode.imageUrl)}" />` : ''}
${transcriptUrl ? `<podcast:transcript url="${escapeXml(transcriptUrl)}" type="text/plain" />` : ''}
${transcriptUrl ? `<podcast:transcript url="${escapeXml(transcriptUrl)}" type="${escapeXml(episode.transcriptType || 'text/plain')}" />` : ''}
${chaptersUrl ? `<podcast:chapters url="${escapeXml(chaptersUrl)}" type="application/json+chapters" />` : ''}
${episode.content ? `<content:encoded><![CDATA[${episode.content}]]></content:encoded>` : ''}
${episode.value && episode.value.enabled && episode.value.recipients && episode.value.recipients.length > 0 ?
Expand Down Expand Up @@ -219,8 +219,9 @@ function eventToPodcastEpisode(event: NostrEvent): PodcastEpisode {
publishDate = new Date(event.created_at * 1000);
}

// Extract transcript URL from tag
// Extract transcript URL and optional MIME type from tag
const transcriptUrl = tags.get('transcript')?.[0];
const transcriptType = tags.get('transcript')?.[1] || 'text/plain';

// Extract chapters URL from tag
const chaptersUrl = tags.get('chapters')?.[0];
Expand Down Expand Up @@ -257,6 +258,7 @@ function eventToPodcastEpisode(event: NostrEvent): PodcastEpisode {
videoType,
imageUrl,
transcriptUrl,
transcriptType,
chaptersUrl,
duration,
episodeNumber,
Expand Down
29 changes: 25 additions & 4 deletions src/hooks/usePublishEpisode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ import { PODCAST_KINDS, isPodcastCreator } from '@/lib/podcastConfig';
import { usePodcastConfig } from '@/hooks/usePodcastConfig';
import { addOP3Prefix } from '@/lib/op3Utils';

/** Infer transcript MIME type from file extension (File.type is unreliable for .srt) */
function inferTranscriptMime(filename: string, fileType: string): string {
const ext = filename.split('.').pop()?.toLowerCase();
const mimeMap: Record<string, string> = {
srt: 'application/x-subrip',
vtt: 'text/vtt',
json: 'application/json',
html: 'text/html',
txt: 'text/plain',
};
return mimeMap[ext ?? ''] || fileType || 'text/plain';
}

/**
* Hook for publishing podcast episodes (creator only)
*/
Expand Down Expand Up @@ -94,10 +107,12 @@ export function usePublishEpisode() {

// Upload transcript file if provided
let transcriptUrl = episodeData.transcriptUrl;
let transcriptType = episodeData.transcriptType;
if (episodeData.transcriptFile) {
try {
const transcriptTags = await uploadFile(episodeData.transcriptFile);
transcriptUrl = transcriptTags[0][1];
transcriptType = inferTranscriptMime(episodeData.transcriptFile.name, episodeData.transcriptFile.type);
} catch (error) {
throw new Error(`Failed to upload transcript file: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
Expand Down Expand Up @@ -172,9 +187,11 @@ export function usePublishEpisode() {
tags.push(['season', episodeData.seasonNumber.toString()]);
}

// Add transcript URL if provided
// Add transcript URL and MIME type if provided
if (transcriptUrl) {
tags.push(['transcript', transcriptUrl]);
transcriptType
? tags.push(['transcript', transcriptUrl, transcriptType])
: tags.push(['transcript', transcriptUrl]);
}

// Add chapters URL if provided
Expand Down Expand Up @@ -309,10 +326,12 @@ export function useUpdateEpisode() {

// Upload transcript file if provided
let transcriptUrl = episodeData.transcriptUrl;
let transcriptType = episodeData.transcriptType;
if (episodeData.transcriptFile) {
try {
const transcriptTags = await uploadFile(episodeData.transcriptFile);
transcriptUrl = transcriptTags[0][1];
transcriptType = inferTranscriptMime(episodeData.transcriptFile.name, episodeData.transcriptFile.type);
} catch (error) {
throw new Error(`Failed to upload transcript file: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
Expand Down Expand Up @@ -415,9 +434,11 @@ export function useUpdateEpisode() {
tags.push(['season', episodeData.seasonNumber.toString()]);
}

// Add transcript URL if provided
// Add transcript URL and MIME type if provided
if (transcriptUrl) {
tags.push(['transcript', transcriptUrl]);
transcriptType
? tags.push(['transcript', transcriptUrl, transcriptType])
: tags.push(['transcript', transcriptUrl]);
}

// Add chapters URL if provided
Expand Down
2 changes: 2 additions & 0 deletions src/types/podcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface PodcastEpisode {
tags: string[];
// Transcript and chapters as URL references
transcriptUrl?: string;
transcriptType?: string; // MIME type (e.g. 'application/x-subrip', 'text/vtt')
chaptersUrl?: string;
guests?: PodcastGuest[];
externalRefs?: ExternalReference[];
Expand Down Expand Up @@ -187,6 +188,7 @@ export interface RSSItem {
image?: string;
// Transcript and chapters as URL references
transcriptUrl?: string;
transcriptType?: string; // MIME type (e.g. 'application/x-subrip', 'text/vtt')
chaptersUrl?: string;
funding?: Array<{
url: string;
Expand Down