Skip to content

Instantly share code, notes, and snippets.

@lubobill1990
Last active December 24, 2024 16:26
Show Gist options
  • Save lubobill1990/9fb074255706e7194a9b5920cec57810 to your computer and use it in GitHub Desktop.
Save lubobill1990/9fb074255706e7194a9b5920cec57810 to your computer and use it in GitHub Desktop.
import { from, of, Observable, merge, interval } from "rxjs";
import {
mergeMap,
map,
toArray,
catchError,
distinct,
share,
take,
last,
} from "rxjs/operators";
function getIntervalSourceFromArray<T>(stringArray: T[], i: number = 1000) {
const observable: Observable<T> = interval(i).pipe(
take(stringArray.length), // take as many items as there are in the array
map((index) => stringArray[index]) // map the emitted index to the corresponding string from the array
);
return observable;
}
let i = 0;
function getCachedMeetingInsight(
transcriptUrl: string
): Promise<string | null> {
console.log(`Fetching cached insight for ${transcriptUrl}`);
i += 1;
if (i % 2 === 0) {
// Simulating a cache miss for demonstration
console.log(`Insight cache missed of ${transcriptUrl}.`);
return Promise.resolve(null);
}
console.log(`Cached insight fetched of ${transcriptUrl}.`);
return Promise.resolve(`This is cached insight of ${transcriptUrl}.`);
}
function getRawTranscriptFromTranscriptUrl(url: string): Promise<string> {
console.log(`Fetching raw transcript from ${url}`);
return Promise.resolve(`Raw transcript content for ${url}`);
}
function getMeetingInsightFromRawTranscript(
rawTranscript: string
): Promise<string> {
console.log(`Generating meeting insight from raw transcript`);
return Promise.resolve(`Insight from: ${rawTranscript}`);
}
function getOpeningScriptAndTopics(
rawTranscriptList: string[]
): [Observable<string>, Observable<string>] {
console.log("Generating opening script and topics");
const openingScriptLines$ = getIntervalSourceFromArray([
"Opening line 1",
"Opening line 2",
"Opening line 3",
]);
const topics$ = getIntervalSourceFromArray(["Topic 1", "Topic 2", "Topic 3"]);
return [openingScriptLines$, topics$];
}
function generateTopicPodcastScript(
topic: string,
openingScript: string,
transcriptList: string[]
): Observable<string> {
console.log(`Generating podcast script for topic: ${topic}`);
return getIntervalSourceFromArray(
[
`Script line 1 of topic ${topic} with opening script ${openingScript}`,
`Script line 2 of topic ${topic} with opening script ${openingScript}`,
`Script line 3 of topic ${topic} with opening script ${openingScript}`,
],
500
);
}
function generatePodcastAudioBuffers(
podcastScript: string
): Observable<ArrayBuffer> {
console.log(`Generating audio buffer for: ${podcastScript}`);
return getIntervalSourceFromArray(
[new ArrayBuffer(1024), new ArrayBuffer(1024), new ArrayBuffer(1024)],
500
);
}
let filePathIndex = 0;
function getNextFilePath(): string {
filePathIndex += 1;
return `file_${filePathIndex}.mp3`;
}
function writeAudioBufferToFile(
filePath: string,
audioBuffer: ArrayBuffer
): Promise<void> {
console.log(`Writing audio buffer to file: ${filePath}`);
return Promise.resolve();
}
function generatePodcast(transcriptUrls: string[]): Observable<string[]> {
return from(transcriptUrls).pipe(
// Step 1: For each transcript URL, get the meeting insight
mergeMap((url) =>
from(getCachedMeetingInsight(url)).pipe(
mergeMap((cachedInsight) => {
if (cachedInsight) {
// Use cached insight if available
return of(cachedInsight);
} else {
// Fetch raw transcript and generate meeting insight
return from(getRawTranscriptFromTranscriptUrl(url)).pipe(
mergeMap((rawTranscript) =>
getMeetingInsightFromRawTranscript(rawTranscript)
),
catchError((err) => {
console.error(`Failed to process transcript URL: ${url}`, err);
return of(""); // Fallback to empty string
})
);
}
})
)
),
toArray(), // Step 2: Concatenate meeting insights into an array
mergeMap((meetingInsights) => {
// Step 3: Generate opening script and topics from the insights
const [openingScriptLines$, topics$] =
getOpeningScriptAndTopics(meetingInsights);
const openingfilePath = getNextFilePath();
console.log(`Generated filePath for opening script: ${openingfilePath}`);
// Fork openingScriptLines$ so we can reuse it
const openingScriptLinesForked$ = openingScriptLines$.pipe(share());
// Step 4: Process and store the opening script audio
const openingAudio$ = openingScriptLines$.pipe(
mergeMap((line) =>
generatePodcastAudioBuffers(line).pipe(
mergeMap((audioBuffer) => {
return from(
writeAudioBufferToFile(openingfilePath, audioBuffer)
).pipe(map(() => openingfilePath));
})
)
)
);
// Step 5 & 6: Process each topic to generate scripts and audio
const topicsAudio$ = from(
openingScriptLinesForked$.pipe(
toArray(),
map((lines) => lines.join(" "))
)
).pipe(
mergeMap((openingScript) => {
return topics$.pipe(
mergeMap((topic) => {
const filePath = getNextFilePath();
console.log(`Generated filePath for topic script: ${filePath}`);
const topicPodcastFile$ = generateTopicPodcastScript(
topic,
openingScript, // Pass the joined opening script lines
meetingInsights
).pipe(
mergeMap((scriptLine) =>
generatePodcastAudioBuffers(scriptLine).pipe(
mergeMap((audioBuffer) => {
return from(
writeAudioBufferToFile(filePath, audioBuffer)
).pipe(map(() => filePath));
})
)
)
);
return topicPodcastFile$;
}),
);
})
);
// Combine the opening audio and topic audios into one observable
return merge(openingAudio$, topicsAudio$).pipe(distinct(), toArray());
})
);
}
// Usage Example
const transcriptUrls = [
"https://example.com/transcript1",
"https://example.com/transcript2",
];
generatePodcast(transcriptUrls).subscribe({
next: (audioFilePaths) => {
console.log("Generated podcast audio files:", audioFilePaths);
},
error: (err) => {
console.error("Error generating podcast:", err);
},
complete: () => {
console.log("Podcast generation complete.");
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment