Add episode transcripts, expand chatbot Q&A database to 28 entries, and build scripts
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { execSync } from 'child_process'
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { randomUUID } from 'crypto'
|
||||
|
||||
const BASE = "/Users/nate.emmert/Documents/github/Siteforge/Verse by Verse with Nate Complete Series"
|
||||
const CHATBOT_FILE = "/Users/nate.emmert/Documents/github/Siteforge/data/chatbot-content.json"
|
||||
|
||||
const FILES = [
|
||||
{ file: `${BASE}/Done/Verse_by_Verse_with_Nate_Episode02.docx`, ep: 2 },
|
||||
{ file: `${BASE}/Done/Verse_by_Verse_with_Nate_Episode03.docx`, ep: 3 },
|
||||
{ file: `${BASE}/Done/Verse_by_Verse_with_Nate_Episode04_Updated.docx`, ep: 4 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode05.docx`, ep: 5 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode06_expanded.docx`, ep: 6 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode07.docx`, ep: 7 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode08.docx`, ep: 8 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode09.docx`, ep: 9 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode10.docx`, ep: 10 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode11.docx`, ep: 11 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode12.docx`, ep: 12 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode13.docx`, ep: 13 },
|
||||
{ file: `${BASE}/Verse_by_Verse_with_Nate_Episode14.docx`, ep: 14 },
|
||||
]
|
||||
|
||||
function extractText(filePath) {
|
||||
const xml = execSync(`unzip -p "${filePath}" word/document.xml 2>/dev/null`, { encoding: 'utf8' })
|
||||
return xml
|
||||
.replace(/<[^>]+>/g, ' ')
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'")
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
}
|
||||
|
||||
function parseEpisode(raw, epNum) {
|
||||
// Extract episode subtitle and passage reference from header
|
||||
const headerMatch = raw.match(/EPISODE\s+\d+\s*[—\-\u2013\u2014]+\s*(.+?)\s+(Titus\s+[\d:]+(?:\s*[\-\u2013\u2014]+\s*[\d:]+)?)\s*·/i)
|
||||
const subtitle = headerMatch ? headerMatch[1].trim().replace(/\s+/g, ' ') : ''
|
||||
const passageRef = headerMatch ? headerMatch[2].trim() : 'Titus'
|
||||
const episodeTitle = `Episode ${epNum} — ${subtitle || 'Verse by Verse with Nate'}`
|
||||
|
||||
// Find where the actual teaching content starts
|
||||
let contentStart = raw.indexOf('SEGMENT 1')
|
||||
if (contentStart === -1) contentStart = raw.indexOf('WHO WAS PAUL')
|
||||
if (contentStart === -1) contentStart = raw.indexOf('COLD OPEN')
|
||||
if (contentStart === -1) contentStart = 400
|
||||
|
||||
const rawContent = raw.slice(contentStart, contentStart + 4000)
|
||||
const content = rawContent
|
||||
.replace(/\[[^\]]{0,100}\]/g, '') // remove [stage directions]
|
||||
.replace(/[✝🎙️📖💬🧠💡🔑✅◀▶]/gu, '') // remove emoji
|
||||
.replace(/SEGMENT\s+\d+\s*[—\-\u2013]+\s*/g, '\n\n') // turn SEGMENT headers into breaks
|
||||
.replace(/\s{2,}/g, ' ')
|
||||
.trim()
|
||||
|
||||
// Build keyword list
|
||||
const verseRefs = [...new Set((raw.match(/Titus\s+\d+:\d+/g) || []))].slice(0, 5).map(k => k.toLowerCase())
|
||||
const titleWords = subtitle.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').split(/\s+/).filter(w => w.length > 3)
|
||||
const keywords = [...new Set([
|
||||
'titus', `episode ${epNum}`, passageRef.toLowerCase(),
|
||||
...verseRefs, ...titleWords
|
||||
])].slice(0, 20)
|
||||
|
||||
return {
|
||||
id: randomUUID(),
|
||||
type: 'episode',
|
||||
title: `${episodeTitle} (${passageRef})`,
|
||||
content: content.slice(0, 3900),
|
||||
keywords,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
|
||||
// Load existing entries (keep the 8 hand-written ones)
|
||||
const existing = JSON.parse(readFileSync(CHATBOT_FILE, 'utf8'))
|
||||
// Remove any previously generated episode entries to avoid duplication
|
||||
const baseEntries = existing.filter(e => e.type !== 'episode')
|
||||
|
||||
const newEntries = []
|
||||
for (const { file, ep } of FILES) {
|
||||
try {
|
||||
const raw = extractText(file)
|
||||
const entry = parseEpisode(raw, ep)
|
||||
newEntries.push(entry)
|
||||
console.log(`✓ Ep ${ep}: ${entry.title.slice(0, 80)}`)
|
||||
} catch (err) {
|
||||
console.error(`✗ Ep ${ep}: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const combined = [...baseEntries, ...newEntries]
|
||||
writeFileSync(CHATBOT_FILE, JSON.stringify(combined, null, 2), 'utf8')
|
||||
console.log(`\nDone. ${newEntries.length} episode entries added. Total: ${combined.length}`)
|
||||
Reference in New Issue
Block a user