yt-transcript-strapi-plugin 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/index.js +21 -12
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +21 -12
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -109,10 +109,17 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
109
109
|
async getTranscript(ctx) {
|
|
110
110
|
const videoId = extractYouTubeID(ctx.params.videoId);
|
|
111
111
|
if (!videoId) return ctx.body = { error: "Invalid YouTube URL or ID", data: null };
|
|
112
|
+
console.log("Looking for transcript in database");
|
|
112
113
|
const found = await strapi2.plugin("yt-transcript").service("service").findTranscript(videoId);
|
|
113
|
-
if (found)
|
|
114
|
+
if (found) {
|
|
115
|
+
console.log("Transcript found.");
|
|
116
|
+
return ctx.body = { data: found };
|
|
117
|
+
}
|
|
118
|
+
console.log("Transcript not found. Fetching new transcript.");
|
|
114
119
|
const transcriptData = await strapi2.plugin("yt-transcript").service("service").getTranscript(videoId);
|
|
120
|
+
console.log("New transcript fetched.");
|
|
115
121
|
const readableTranscript = await strapi2.plugin("yt-transcript").service("service").generateHumanReadableTranscript(transcriptData.fullTranscript);
|
|
122
|
+
console.log("Human readable transcript generated.");
|
|
116
123
|
const payload = {
|
|
117
124
|
title: transcriptData.title,
|
|
118
125
|
transcript: transcriptData.transcript,
|
|
@@ -122,6 +129,7 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
122
129
|
transcriptWithTimeCodes: transcriptData.transcriptWithTimeCodes,
|
|
123
130
|
readableTranscript
|
|
124
131
|
};
|
|
132
|
+
console.log("Saving new transcript to database.");
|
|
125
133
|
const transcript2 = await strapi2.plugin("yt-transcript").service("service").saveTranscript(payload);
|
|
126
134
|
ctx.body = { data: transcript2 };
|
|
127
135
|
}
|
|
@@ -381,7 +389,9 @@ async function initializeModel({
|
|
|
381
389
|
});
|
|
382
390
|
}
|
|
383
391
|
const fetchTranscript = async (identifier) => {
|
|
392
|
+
console.log("Fetching Transcript - Calling fetchTranscript Utils");
|
|
384
393
|
const { Innertube } = await import("youtubei.js");
|
|
394
|
+
console.log("Creating YouTube instance");
|
|
385
395
|
const youtube = await Innertube.create({
|
|
386
396
|
lang: "en",
|
|
387
397
|
location: "US",
|
|
@@ -393,6 +403,7 @@ const fetchTranscript = async (identifier) => {
|
|
|
393
403
|
};
|
|
394
404
|
const info2 = await youtube.getInfo(identifier);
|
|
395
405
|
const transcriptData = await info2.getTranscript();
|
|
406
|
+
console.log("Transcript data fetched");
|
|
396
407
|
const transcriptWithTimeCodes = transcriptData?.transcript?.content?.body?.initial_segments.map(
|
|
397
408
|
(segment) => {
|
|
398
409
|
const segmentDuration = Number(segment.end_ms) - Number(segment.start_ms);
|
|
@@ -404,23 +415,21 @@ const fetchTranscript = async (identifier) => {
|
|
|
404
415
|
};
|
|
405
416
|
}
|
|
406
417
|
);
|
|
418
|
+
console.log("Transcript with time codes generated");
|
|
419
|
+
console.log("Cleaning thumbnail URL");
|
|
407
420
|
const fullTranscript = transcriptData?.transcript?.content?.body?.initial_segments.map(
|
|
408
421
|
(segment) => segment.snippet.text
|
|
409
422
|
).join(" ");
|
|
423
|
+
console.log("Full transcript generated");
|
|
410
424
|
const title = info2.basic_info.title;
|
|
411
425
|
const videoId = info2.basic_info.id;
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (processedThumbnailUrl) {
|
|
416
|
-
thumbnailUrl = cleanImageUrl(processedThumbnailUrl);
|
|
417
|
-
} else {
|
|
418
|
-
thumbnailUrl = "";
|
|
419
|
-
}
|
|
426
|
+
const thumbnailUrl = info2.basic_info.thumbnail[0].url;
|
|
427
|
+
const thumbnailUrlCleaned = cleanImageUrl(thumbnailUrl);
|
|
428
|
+
console.log("Returning transcript data");
|
|
420
429
|
return {
|
|
421
430
|
title,
|
|
422
431
|
videoId,
|
|
423
|
-
thumbnailUrl,
|
|
432
|
+
thumbnailUrl: thumbnailUrlCleaned || "",
|
|
424
433
|
fullTranscript,
|
|
425
434
|
transcriptWithTimeCodes
|
|
426
435
|
};
|
|
@@ -444,7 +453,6 @@ async function processTextChunks(chunks, model) {
|
|
|
444
453
|
}
|
|
445
454
|
async function generateModifiedTranscript(rawTranscript) {
|
|
446
455
|
const pluginSettings = await strapi.config.get("plugin::yt-transcript");
|
|
447
|
-
console.log("pluginSettings", pluginSettings);
|
|
448
456
|
if (!pluginSettings.openAIApiKey || !pluginSettings.model || !pluginSettings.temp || !pluginSettings.maxTokens) {
|
|
449
457
|
throw new Error("Missing required configuration for YTTranscript");
|
|
450
458
|
}
|
|
@@ -465,6 +473,7 @@ async function generateModifiedTranscript(rawTranscript) {
|
|
|
465
473
|
}
|
|
466
474
|
const service = ({ strapi: strapi2 }) => ({
|
|
467
475
|
async getTranscript(identifier) {
|
|
476
|
+
console.log("Fetching Transcript - Calling fetchTranscript Service");
|
|
468
477
|
const youtubeIdRegex = /^[a-zA-Z0-9_-]{11}$/;
|
|
469
478
|
const isValid = youtubeIdRegex.test(identifier);
|
|
470
479
|
if (!isValid) return { error: "Invalid video ID", data: null };
|
|
@@ -486,7 +495,7 @@ const service = ({ strapi: strapi2 }) => ({
|
|
|
486
495
|
return transcriptData;
|
|
487
496
|
},
|
|
488
497
|
async generateHumanReadableTranscript(transcript2) {
|
|
489
|
-
console.log("Generating human readable transcript:"
|
|
498
|
+
console.log("Generating human readable transcript:");
|
|
490
499
|
const modifiedTranscript = await generateModifiedTranscript(transcript2);
|
|
491
500
|
return modifiedTranscript;
|
|
492
501
|
}
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/register.ts","../../server/src/config/index.ts","../../server/src/content-types/transcript/index.ts","../../server/src/content-types/index.ts","../../server/src/utils/extract-youtube-id.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/index.ts","../../server/src/middlewares/index.ts","../../server/src/policies/index.ts","../../server/src/routes/content-api.ts","../../server/src/routes/admin.ts","../../server/src/routes/index.ts","../../node_modules/@langchain/textsplitters/dist/text_splitter.js","../../server/src/utils/openai.ts","../../server/src/utils/fetch-transcript.ts","../../server/src/services/service.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["import type { Core } from '@strapi/strapi';\n\nconst bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {\n // bootstrap phase\n};\n\nexport default bootstrap;\n","import type { Core } from '@strapi/strapi';\n\nconst destroy = ({ strapi }: { strapi: Core.Strapi }) => {\n // destroy phase\n};\n\nexport default destroy;\n","import type { Core } from '@strapi/strapi';\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n // register phase\n};\n\nexport default register;\n","export default {\n default: {},\n validator() {},\n};\n","import schema from './schema.json';\n\nexport default {\n schema,\n};","import transcript from './transcript';\n\nexport default {\n transcript,\n};\n\n\n","export function extractYouTubeID(urlOrID: string): string | null {\n // Regular expression for YouTube ID format\n const regExpID = /^[a-zA-Z0-9_-]{11}$/;\n\n // Check if the input is a YouTube ID\n if (regExpID.test(urlOrID)) {\n return urlOrID;\n }\n\n // Regular expression for standard YouTube links\n const regExpStandard = /youtube\\.com\\/watch\\?v=([a-zA-Z0-9_-]+)/;\n\n // Regular expression for YouTube Shorts links\n const regExpShorts = /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]+)/;\n\n // Check for standard YouTube link\n const matchStandard = urlOrID.match(regExpStandard);\n if (matchStandard) {\n return matchStandard[1];\n }\n\n // Check for YouTube Shorts link\n const matchShorts = urlOrID.match(regExpShorts);\n if (matchShorts) {\n return matchShorts[1];\n }\n\n // Return null if no match is found\n return null;\n}","import type { Core } from '@strapi/strapi';\nimport { extractYouTubeID } from '../utils/extract-youtube-id';\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(ctx) {\n const videoId = extractYouTubeID(ctx.params.videoId);\n\n if (!videoId) return (ctx.body = { error: 'Invalid YouTube URL or ID', data: null });\n\n const found = await strapi\n .plugin('yt-transcript')\n .service('service')\n .findTranscript(videoId);\n\n if (found) return (ctx.body = { data: found });\n\n const transcriptData = await strapi\n .plugin('yt-transcript')\n .service('service')\n .getTranscript(videoId);\n\n const readableTranscript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .generateHumanReadableTranscript(transcriptData.fullTranscript);\n\n const payload = {\n title: transcriptData.title,\n transcript: transcriptData.transcript,\n videoId: transcriptData.videoId,\n thumbnailUrl: transcriptData.thumbnailUrl,\n fullTranscript: transcriptData.fullTranscript,\n transcriptWithTimeCodes: transcriptData.transcriptWithTimeCodes,\n readableTranscript: readableTranscript,\n };\n\n // console.log('Payload:', payload);\n\n const transcript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .saveTranscript(payload);\n\n ctx.body = { data: transcript };\n },\n});\n\nexport default controller;\n","import controller from './controller';\n\nexport default {\n controller,\n};\n","export default {};\n","export default {};\n","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","\"use strict\";\n\nimport contentApi from \"./content-api\";\nimport admin from \"./admin\";\n\nexport default {\n \"content-api\": {\n type: \"content-api\",\n routes: [...contentApi],\n },\n admin: {\n type: \"admin\",\n routes: [...admin],\n },\n};","import { Document, BaseDocumentTransformer } from \"@langchain/core/documents\";\nimport { getEncoding } from \"@langchain/core/utils/tiktoken\";\nexport class TextSplitter extends BaseDocumentTransformer {\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"lc_namespace\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"langchain\", \"document_transformers\", \"text_splitters\"]\n });\n Object.defineProperty(this, \"chunkSize\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1000\n });\n Object.defineProperty(this, \"chunkOverlap\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 200\n });\n Object.defineProperty(this, \"keepSeparator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"lengthFunction\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.chunkSize = fields?.chunkSize ?? this.chunkSize;\n this.chunkOverlap = fields?.chunkOverlap ?? this.chunkOverlap;\n this.keepSeparator = fields?.keepSeparator ?? this.keepSeparator;\n this.lengthFunction =\n fields?.lengthFunction ?? ((text) => text.length);\n if (this.chunkOverlap >= this.chunkSize) {\n throw new Error(\"Cannot have chunkOverlap >= chunkSize\");\n }\n }\n async transformDocuments(documents, chunkHeaderOptions = {}) {\n return this.splitDocuments(documents, chunkHeaderOptions);\n }\n splitOnSeparator(text, separator) {\n let splits;\n if (separator) {\n if (this.keepSeparator) {\n const regexEscapedSeparator = separator.replace(/[/\\-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n splits = text.split(new RegExp(`(?=${regexEscapedSeparator})`));\n }\n else {\n splits = text.split(separator);\n }\n }\n else {\n splits = text.split(\"\");\n }\n return splits.filter((s) => s !== \"\");\n }\n async createDocuments(texts, \n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n metadatas = [], chunkHeaderOptions = {}) {\n // if no metadata is provided, we create an empty one for each text\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const _metadatas = metadatas.length > 0\n ? metadatas\n : [...Array(texts.length)].map(() => ({}));\n const { chunkHeader = \"\", chunkOverlapHeader = \"(cont'd) \", appendChunkOverlapHeader = false, } = chunkHeaderOptions;\n const documents = new Array();\n for (let i = 0; i < texts.length; i += 1) {\n const text = texts[i];\n let lineCounterIndex = 1;\n let prevChunk = null;\n let indexPrevChunk = -1;\n for (const chunk of await this.splitText(text)) {\n let pageContent = chunkHeader;\n // we need to count the \\n that are in the text before getting removed by the splitting\n const indexChunk = text.indexOf(chunk, indexPrevChunk + 1);\n if (prevChunk === null) {\n const newLinesBeforeFirstChunk = this.numberOfNewLines(text, 0, indexChunk);\n lineCounterIndex += newLinesBeforeFirstChunk;\n }\n else {\n const indexEndPrevChunk = indexPrevChunk + (await this.lengthFunction(prevChunk));\n if (indexEndPrevChunk < indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexEndPrevChunk, indexChunk);\n lineCounterIndex += numberOfIntermediateNewLines;\n }\n else if (indexEndPrevChunk > indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexChunk, indexEndPrevChunk);\n lineCounterIndex -= numberOfIntermediateNewLines;\n }\n if (appendChunkOverlapHeader) {\n pageContent += chunkOverlapHeader;\n }\n }\n const newLinesCount = this.numberOfNewLines(chunk);\n const loc = _metadatas[i].loc && typeof _metadatas[i].loc === \"object\"\n ? { ..._metadatas[i].loc }\n : {};\n loc.lines = {\n from: lineCounterIndex,\n to: lineCounterIndex + newLinesCount,\n };\n const metadataWithLinesNumber = {\n ..._metadatas[i],\n loc,\n };\n pageContent += chunk;\n documents.push(new Document({\n pageContent,\n metadata: metadataWithLinesNumber,\n }));\n lineCounterIndex += newLinesCount;\n prevChunk = chunk;\n indexPrevChunk = indexChunk;\n }\n }\n return documents;\n }\n numberOfNewLines(text, start, end) {\n const textSection = text.slice(start, end);\n return (textSection.match(/\\n/g) || []).length;\n }\n async splitDocuments(documents, chunkHeaderOptions = {}) {\n const selectedDocuments = documents.filter((doc) => doc.pageContent !== undefined);\n const texts = selectedDocuments.map((doc) => doc.pageContent);\n const metadatas = selectedDocuments.map((doc) => doc.metadata);\n return this.createDocuments(texts, metadatas, chunkHeaderOptions);\n }\n joinDocs(docs, separator) {\n const text = docs.join(separator).trim();\n return text === \"\" ? null : text;\n }\n async mergeSplits(splits, separator) {\n const docs = [];\n const currentDoc = [];\n let total = 0;\n for (const d of splits) {\n const _len = await this.lengthFunction(d);\n if (total + _len + currentDoc.length * separator.length >\n this.chunkSize) {\n if (total > this.chunkSize) {\n console.warn(`Created a chunk of size ${total}, +\nwhich is longer than the specified ${this.chunkSize}`);\n }\n if (currentDoc.length > 0) {\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n // Keep on popping if:\n // - we have a larger chunk than in the chunk overlap\n // - or if we still have any chunks and the length is long\n while (total > this.chunkOverlap ||\n (total + _len + currentDoc.length * separator.length >\n this.chunkSize &&\n total > 0)) {\n total -= await this.lengthFunction(currentDoc[0]);\n currentDoc.shift();\n }\n }\n }\n currentDoc.push(d);\n total += _len;\n }\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n return docs;\n }\n}\nexport class CharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"CharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"\\n\\n\"\n });\n this.separator = fields?.separator ?? this.separator;\n }\n async splitText(text) {\n // First we naively split the large input into a bunch of smaller ones.\n const splits = this.splitOnSeparator(text, this.separator);\n return this.mergeSplits(splits, this.keepSeparator ? \"\" : this.separator);\n }\n}\nexport const SupportedTextSplitterLanguages = [\n \"cpp\",\n \"go\",\n \"java\",\n \"js\",\n \"php\",\n \"proto\",\n \"python\",\n \"rst\",\n \"ruby\",\n \"rust\",\n \"scala\",\n \"swift\",\n \"markdown\",\n \"latex\",\n \"html\",\n \"sol\",\n];\nexport class RecursiveCharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"RecursiveCharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separators\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"\\n\\n\", \"\\n\", \" \", \"\"]\n });\n this.separators = fields?.separators ?? this.separators;\n this.keepSeparator = fields?.keepSeparator ?? true;\n }\n async _splitText(text, separators) {\n const finalChunks = [];\n // Get appropriate separator to use\n let separator = separators[separators.length - 1];\n let newSeparators;\n for (let i = 0; i < separators.length; i += 1) {\n const s = separators[i];\n if (s === \"\") {\n separator = s;\n break;\n }\n if (text.includes(s)) {\n separator = s;\n newSeparators = separators.slice(i + 1);\n break;\n }\n }\n // Now that we have the separator, split the text\n const splits = this.splitOnSeparator(text, separator);\n // Now go merging things, recursively splitting longer texts.\n let goodSplits = [];\n const _separator = this.keepSeparator ? \"\" : separator;\n for (const s of splits) {\n if ((await this.lengthFunction(s)) < this.chunkSize) {\n goodSplits.push(s);\n }\n else {\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n goodSplits = [];\n }\n if (!newSeparators) {\n finalChunks.push(s);\n }\n else {\n const otherInfo = await this._splitText(s, newSeparators);\n finalChunks.push(...otherInfo);\n }\n }\n }\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n }\n return finalChunks;\n }\n async splitText(text) {\n return this._splitText(text, this.separators);\n }\n static fromLanguage(language, options) {\n return new RecursiveCharacterTextSplitter({\n ...options,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(language),\n });\n }\n static getSeparatorsForLanguage(language) {\n if (language === \"cpp\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along function definitions\n \"\\nvoid \",\n \"\\nint \",\n \"\\nfloat \",\n \"\\ndouble \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"go\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n \"\\nvar \",\n \"\\nconst \",\n \"\\ntype \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"java\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along method definitions\n \"\\npublic \",\n \"\\nprotected \",\n \"\\nprivate \",\n \"\\nstatic \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"js\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n \"\\nconst \",\n \"\\nlet \",\n \"\\nvar \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n \"\\ndefault \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"php\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n // Split along class definitions\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nforeach \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"proto\") {\n return [\n // Split along message definitions\n \"\\nmessage \",\n // Split along service definitions\n \"\\nservice \",\n // Split along enum definitions\n \"\\nenum \",\n // Split along option definitions\n \"\\noption \",\n // Split along import statements\n \"\\nimport \",\n // Split along syntax declarations\n \"\\nsyntax \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"python\") {\n return [\n // First, try to split along class definitions\n \"\\nclass \",\n \"\\ndef \",\n \"\\n\\tdef \",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rst\") {\n return [\n // Split along section titles\n \"\\n===\\n\",\n \"\\n---\\n\",\n \"\\n***\\n\",\n // Split along directive markers\n \"\\n.. \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"ruby\") {\n return [\n // Split along method definitions\n \"\\ndef \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nunless \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\ndo \",\n \"\\nbegin \",\n \"\\nrescue \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rust\") {\n return [\n // Split along function definitions\n \"\\nfn \",\n \"\\nconst \",\n \"\\nlet \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\nloop \",\n \"\\nmatch \",\n \"\\nconst \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"scala\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n \"\\nobject \",\n // Split along method definitions\n \"\\ndef \",\n \"\\nval \",\n \"\\nvar \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nmatch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"swift\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n // Split along class definitions\n \"\\nclass \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"markdown\") {\n return [\n // First, try to split along Markdown headings (starting with level 2)\n \"\\n## \",\n \"\\n### \",\n \"\\n#### \",\n \"\\n##### \",\n \"\\n###### \",\n // Note the alternative syntax for headings (below) is not handled here\n // Heading level 2\n // ---------------\n // End of code block\n \"```\\n\\n\",\n // Horizontal lines\n \"\\n\\n***\\n\\n\",\n \"\\n\\n---\\n\\n\",\n \"\\n\\n___\\n\\n\",\n // Note that this splitter doesn't handle horizontal lines defined\n // by *three or more* of ***, ---, or ___, but this is not handled\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"latex\") {\n return [\n // First, try to split along Latex sections\n \"\\n\\\\chapter{\",\n \"\\n\\\\section{\",\n \"\\n\\\\subsection{\",\n \"\\n\\\\subsubsection{\",\n // Now split by environments\n \"\\n\\\\begin{enumerate}\",\n \"\\n\\\\begin{itemize}\",\n \"\\n\\\\begin{description}\",\n \"\\n\\\\begin{list}\",\n \"\\n\\\\begin{quote}\",\n \"\\n\\\\begin{quotation}\",\n \"\\n\\\\begin{verse}\",\n \"\\n\\\\begin{verbatim}\",\n // Now split by math environments\n \"\\n\\\\begin{align}\",\n \"$$\",\n \"$\",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"html\") {\n return [\n // First, try to split along HTML tags\n \"<body>\",\n \"<div>\",\n \"<p>\",\n \"<br>\",\n \"<li>\",\n \"<h1>\",\n \"<h2>\",\n \"<h3>\",\n \"<h4>\",\n \"<h5>\",\n \"<h6>\",\n \"<span>\",\n \"<table>\",\n \"<tr>\",\n \"<td>\",\n \"<th>\",\n \"<ul>\",\n \"<ol>\",\n \"<header>\",\n \"<footer>\",\n \"<nav>\",\n // Head\n \"<head>\",\n \"<style>\",\n \"<script>\",\n \"<meta>\",\n \"<title>\",\n // Normal type of lines\n \" \",\n \"\",\n ];\n }\n else if (language === \"sol\") {\n return [\n // Split along compiler informations definitions\n \"\\npragma \",\n \"\\nusing \",\n // Split along contract definitions\n \"\\ncontract \",\n \"\\ninterface \",\n \"\\nlibrary \",\n // Split along method definitions\n \"\\nconstructor \",\n \"\\ntype \",\n \"\\nfunction \",\n \"\\nevent \",\n \"\\nmodifier \",\n \"\\nerror \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo while \",\n \"\\nassembly \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else {\n throw new Error(`Language ${language} is not supported.`);\n }\n }\n}\n/**\n * Implementation of splitter which looks at tokens.\n */\nexport class TokenTextSplitter extends TextSplitter {\n static lc_name() {\n return \"TokenTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"encodingName\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"allowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"disallowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tokenizer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.encodingName = fields?.encodingName ?? \"gpt2\";\n this.allowedSpecial = fields?.allowedSpecial ?? [];\n this.disallowedSpecial = fields?.disallowedSpecial ?? \"all\";\n }\n async splitText(text) {\n if (!this.tokenizer) {\n this.tokenizer = await getEncoding(this.encodingName);\n }\n const splits = [];\n const input_ids = this.tokenizer.encode(text, this.allowedSpecial, this.disallowedSpecial);\n let start_idx = 0;\n while (start_idx < input_ids.length) {\n if (start_idx > 0) {\n start_idx -= this.chunkOverlap;\n }\n const end_idx = Math.min(start_idx + this.chunkSize, input_ids.length);\n const chunk_ids = input_ids.slice(start_idx, end_idx);\n splits.push(this.tokenizer.decode(chunk_ids));\n start_idx = end_idx;\n }\n return splits;\n }\n}\nexport class MarkdownTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"markdown\"),\n });\n }\n}\nexport class LatexTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"latex\"),\n });\n }\n}\n","import { ChatOpenAI } from \"@langchain/openai\";\n\ninterface InitializeModelProps {\n openAIApiKey: string;\n model: string;\n temp: number;\n maxTokens?: number;\n}\n\nexport async function initializeModel({\n openAIApiKey,\n model,\n temp,\n}: InitializeModelProps) {\n return new ChatOpenAI({\n temperature: temp,\n openAIApiKey: openAIApiKey,\n modelName: model,\n maxTokens: 1000,\n });\n}","export interface TranscriptSegment {\n text: string;\n start: number;\n end: number;\n duration: number;\n}\n\nexport interface TranscriptData {\n title: string;\n videoId: string;\n thumbnailUrl: string;\n fullTranscript: string;\n transcriptWithTimeCodes: TranscriptSegment[];\n}\n\nconst fetchTranscript = async (\n identifier: string\n): Promise<TranscriptData> => {\n const { Innertube } = await import('youtubei.js');\n\n const youtube = await Innertube.create({\n lang: 'en',\n location: 'US',\n retrieve_player: false,\n });\n\n try {\n const info = await youtube.getInfo(identifier);\n const transcriptData = await info.getTranscript();\n\n const transcriptWithTimeCodes: TranscriptSegment[] = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => {\n const segmentDuration = Number(segment.end_ms) - Number(segment.start_ms);\n return {\n text: segment.snippet.text,\n start: Number(segment.start_ms),\n end: Number(segment.end_ms),\n duration: segmentDuration,\n };\n }\n );\n\n function cleanImageUrl(url) {\n return url.split('?')[0];\n }\n\n const fullTranscript = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => segment.snippet.text\n ).join(' ');\n\n const title = info.basic_info.title;\n const videoId = info.basic_info.id;\n console.log(info.basic_info.thumbnail[0].url, \"what is this\");\n let thumbnailUrl = \"\";\n const processedThumbnailUrl = info.basic_info?.thumbnail?.[0]?.url;\n if (processedThumbnailUrl) {\n thumbnailUrl = cleanImageUrl(processedThumbnailUrl);\n } else {\n thumbnailUrl = \"\";\n }\n\n return {\n title,\n videoId,\n thumbnailUrl,\n fullTranscript,\n transcriptWithTimeCodes,\n };\n } catch (error) {\n console.error('Error fetching transcript:', error);\n throw error;\n }\n};\n\nexport default fetchTranscript;","import type { Core } from '@strapi/strapi';\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { TokenTextSplitter } from \"@langchain/textsplitters\";\nimport { PromptTemplate } from \"@langchain/core/prompts\";\n\nimport { initializeModel } from \"../utils/openai\";\nimport fetchTranscript from '../utils/fetch-transcript';\n\ninterface YTTranscriptConfig {\n openAIApiKey: string;\n model?: string;\n temp?: number;\n maxTokens?: number;\n}\n\nasync function processTextChunks(chunks: string[], model: ChatOpenAI) {\n const punctuationPrompt = PromptTemplate.fromTemplate(\n \"Add proper punctuation and capitalization to the following text chunk:\\n\\n{chunk}\"\n );\n const punctuationChain = punctuationPrompt.pipe(model);\n\n const processedChunks = await Promise.all(\n chunks.map(async (chunk) => {\n const result = await punctuationChain.invoke({ chunk });\n return result.content as string;\n })\n );\n\n return processedChunks.join(\" \");\n}\n\nexport async function generateModifiedTranscript (rawTranscript: string) {\n const pluginSettings = await strapi.config.get('plugin::yt-transcript') as YTTranscriptConfig; \n \n console.log('pluginSettings', pluginSettings);\n \n if (!pluginSettings.openAIApiKey || !pluginSettings.model || !pluginSettings.temp || !pluginSettings.maxTokens) {\n throw new Error('Missing required configuration for YTTranscript');\n }\n\n const chatModel = await initializeModel({\n openAIApiKey: pluginSettings.openAIApiKey,\n model: pluginSettings.model,\n temp: pluginSettings.temp,\n maxTokens: pluginSettings.maxTokens,\n });\n\n const splitter = new TokenTextSplitter({\n chunkSize: 1000,\n chunkOverlap: 200,\n });\n\n const transcriptChunks = await splitter.createDocuments([rawTranscript]);\n const chunkTexts = transcriptChunks.map(chunk => chunk.pageContent);\n const modifiedTranscript = await processTextChunks(chunkTexts, chatModel);\n\n return modifiedTranscript;\n}\n\nconst service = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(identifier: string) {\n const youtubeIdRegex = /^[a-zA-Z0-9_-]{11}$/;\n const isValid = youtubeIdRegex.test(identifier);\n if (!isValid) return { error: 'Invalid video ID', data: null };\n const transcriptData = await fetchTranscript(identifier);\n return transcriptData;\n },\n\n async saveTranscript(payload) {\n // console.log('Saving transcript:', payload);\n return await strapi.documents('plugin::yt-transcript.transcript').create({\n data: payload,\n });\n },\n\n async findTranscript(videoId) {\n console.log('Finding transcript for videoId:', videoId);\n const transcriptData = await strapi.documents('plugin::yt-transcript.transcript').findFirst({\n filters: { videoId },\n });\n\n console.log('Transcript found:', transcriptData?.title, 'found');\n\n if (!transcriptData) return null;\n return transcriptData;\n },\n\n async generateHumanReadableTranscript(transcript) {\n console.log('Generating human readable transcript:', transcript);\n const modifiedTranscript = await generateModifiedTranscript(transcript);\n return modifiedTranscript;\n },\n});\n\nexport default service;\n","import service from './service';\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport destroy from './destroy';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy,\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["strapi","transcript","BaseDocumentTransformer","documents","Document","doc","getEncoding","ChatOpenAI","info","PromptTemplate"],"mappings":"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","x_google_ignoreList":[14]}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/register.ts","../../server/src/config/index.ts","../../server/src/content-types/transcript/index.ts","../../server/src/content-types/index.ts","../../server/src/utils/extract-youtube-id.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/index.ts","../../server/src/middlewares/index.ts","../../server/src/policies/index.ts","../../server/src/routes/content-api.ts","../../server/src/routes/admin.ts","../../server/src/routes/index.ts","../../node_modules/@langchain/textsplitters/dist/text_splitter.js","../../server/src/utils/openai.ts","../../server/src/utils/fetch-transcript.ts","../../server/src/services/service.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["import type { Core } from '@strapi/strapi';\n\nconst bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {\n // bootstrap phase\n};\n\nexport default bootstrap;\n","import type { Core } from '@strapi/strapi';\n\nconst destroy = ({ strapi }: { strapi: Core.Strapi }) => {\n // destroy phase\n};\n\nexport default destroy;\n","import type { Core } from '@strapi/strapi';\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n // register phase\n};\n\nexport default register;\n","export default {\n default: {},\n validator() {},\n};\n","import schema from './schema.json';\n\nexport default {\n schema,\n};","import transcript from './transcript';\n\nexport default {\n transcript,\n};\n\n\n","export function extractYouTubeID(urlOrID: string): string | null {\n // Regular expression for YouTube ID format\n const regExpID = /^[a-zA-Z0-9_-]{11}$/;\n\n // Check if the input is a YouTube ID\n if (regExpID.test(urlOrID)) {\n return urlOrID;\n }\n\n // Regular expression for standard YouTube links\n const regExpStandard = /youtube\\.com\\/watch\\?v=([a-zA-Z0-9_-]+)/;\n\n // Regular expression for YouTube Shorts links\n const regExpShorts = /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]+)/;\n\n // Check for standard YouTube link\n const matchStandard = urlOrID.match(regExpStandard);\n if (matchStandard) {\n return matchStandard[1];\n }\n\n // Check for YouTube Shorts link\n const matchShorts = urlOrID.match(regExpShorts);\n if (matchShorts) {\n return matchShorts[1];\n }\n\n // Return null if no match is found\n return null;\n}","import type { Core } from '@strapi/strapi';\nimport { extractYouTubeID } from '../utils/extract-youtube-id';\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(ctx) {\n const videoId = extractYouTubeID(ctx.params.videoId);\n\n if (!videoId) return (ctx.body = { error: 'Invalid YouTube URL or ID', data: null });\n\n console.log(\"Looking for transcript in database\");\n\n const found = await strapi\n .plugin('yt-transcript')\n .service('service')\n .findTranscript(videoId);\n\n if (found) {\n console.log(\"Transcript found.\");\n return (ctx.body = { data: found });\n }\n\n console.log(\"Transcript not found. Fetching new transcript.\");\n\n const transcriptData = await strapi\n .plugin('yt-transcript')\n .service('service')\n .getTranscript(videoId);\n\n console.log(\"New transcript fetched.\");\n\n const readableTranscript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .generateHumanReadableTranscript(transcriptData.fullTranscript);\n\n console.log(\"Human readable transcript generated.\");\n\n const payload = {\n title: transcriptData.title,\n transcript: transcriptData.transcript,\n videoId: transcriptData.videoId,\n thumbnailUrl: transcriptData.thumbnailUrl,\n fullTranscript: transcriptData.fullTranscript,\n transcriptWithTimeCodes: transcriptData.transcriptWithTimeCodes,\n readableTranscript: readableTranscript,\n };\n\n console.log(\"Saving new transcript to database.\");\n\n const transcript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .saveTranscript(payload);\n\n ctx.body = { data: transcript };\n },\n});\n\nexport default controller;\n","import controller from './controller';\n\nexport default {\n controller,\n};\n","export default {};\n","export default {};\n","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","\"use strict\";\n\nimport contentApi from \"./content-api\";\nimport admin from \"./admin\";\n\nexport default {\n \"content-api\": {\n type: \"content-api\",\n routes: [...contentApi],\n },\n admin: {\n type: \"admin\",\n routes: [...admin],\n },\n};","import { Document, BaseDocumentTransformer } from \"@langchain/core/documents\";\nimport { getEncoding } from \"@langchain/core/utils/tiktoken\";\nexport class TextSplitter extends BaseDocumentTransformer {\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"lc_namespace\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"langchain\", \"document_transformers\", \"text_splitters\"]\n });\n Object.defineProperty(this, \"chunkSize\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1000\n });\n Object.defineProperty(this, \"chunkOverlap\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 200\n });\n Object.defineProperty(this, \"keepSeparator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"lengthFunction\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.chunkSize = fields?.chunkSize ?? this.chunkSize;\n this.chunkOverlap = fields?.chunkOverlap ?? this.chunkOverlap;\n this.keepSeparator = fields?.keepSeparator ?? this.keepSeparator;\n this.lengthFunction =\n fields?.lengthFunction ?? ((text) => text.length);\n if (this.chunkOverlap >= this.chunkSize) {\n throw new Error(\"Cannot have chunkOverlap >= chunkSize\");\n }\n }\n async transformDocuments(documents, chunkHeaderOptions = {}) {\n return this.splitDocuments(documents, chunkHeaderOptions);\n }\n splitOnSeparator(text, separator) {\n let splits;\n if (separator) {\n if (this.keepSeparator) {\n const regexEscapedSeparator = separator.replace(/[/\\-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n splits = text.split(new RegExp(`(?=${regexEscapedSeparator})`));\n }\n else {\n splits = text.split(separator);\n }\n }\n else {\n splits = text.split(\"\");\n }\n return splits.filter((s) => s !== \"\");\n }\n async createDocuments(texts, \n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n metadatas = [], chunkHeaderOptions = {}) {\n // if no metadata is provided, we create an empty one for each text\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const _metadatas = metadatas.length > 0\n ? metadatas\n : [...Array(texts.length)].map(() => ({}));\n const { chunkHeader = \"\", chunkOverlapHeader = \"(cont'd) \", appendChunkOverlapHeader = false, } = chunkHeaderOptions;\n const documents = new Array();\n for (let i = 0; i < texts.length; i += 1) {\n const text = texts[i];\n let lineCounterIndex = 1;\n let prevChunk = null;\n let indexPrevChunk = -1;\n for (const chunk of await this.splitText(text)) {\n let pageContent = chunkHeader;\n // we need to count the \\n that are in the text before getting removed by the splitting\n const indexChunk = text.indexOf(chunk, indexPrevChunk + 1);\n if (prevChunk === null) {\n const newLinesBeforeFirstChunk = this.numberOfNewLines(text, 0, indexChunk);\n lineCounterIndex += newLinesBeforeFirstChunk;\n }\n else {\n const indexEndPrevChunk = indexPrevChunk + (await this.lengthFunction(prevChunk));\n if (indexEndPrevChunk < indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexEndPrevChunk, indexChunk);\n lineCounterIndex += numberOfIntermediateNewLines;\n }\n else if (indexEndPrevChunk > indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexChunk, indexEndPrevChunk);\n lineCounterIndex -= numberOfIntermediateNewLines;\n }\n if (appendChunkOverlapHeader) {\n pageContent += chunkOverlapHeader;\n }\n }\n const newLinesCount = this.numberOfNewLines(chunk);\n const loc = _metadatas[i].loc && typeof _metadatas[i].loc === \"object\"\n ? { ..._metadatas[i].loc }\n : {};\n loc.lines = {\n from: lineCounterIndex,\n to: lineCounterIndex + newLinesCount,\n };\n const metadataWithLinesNumber = {\n ..._metadatas[i],\n loc,\n };\n pageContent += chunk;\n documents.push(new Document({\n pageContent,\n metadata: metadataWithLinesNumber,\n }));\n lineCounterIndex += newLinesCount;\n prevChunk = chunk;\n indexPrevChunk = indexChunk;\n }\n }\n return documents;\n }\n numberOfNewLines(text, start, end) {\n const textSection = text.slice(start, end);\n return (textSection.match(/\\n/g) || []).length;\n }\n async splitDocuments(documents, chunkHeaderOptions = {}) {\n const selectedDocuments = documents.filter((doc) => doc.pageContent !== undefined);\n const texts = selectedDocuments.map((doc) => doc.pageContent);\n const metadatas = selectedDocuments.map((doc) => doc.metadata);\n return this.createDocuments(texts, metadatas, chunkHeaderOptions);\n }\n joinDocs(docs, separator) {\n const text = docs.join(separator).trim();\n return text === \"\" ? null : text;\n }\n async mergeSplits(splits, separator) {\n const docs = [];\n const currentDoc = [];\n let total = 0;\n for (const d of splits) {\n const _len = await this.lengthFunction(d);\n if (total + _len + currentDoc.length * separator.length >\n this.chunkSize) {\n if (total > this.chunkSize) {\n console.warn(`Created a chunk of size ${total}, +\nwhich is longer than the specified ${this.chunkSize}`);\n }\n if (currentDoc.length > 0) {\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n // Keep on popping if:\n // - we have a larger chunk than in the chunk overlap\n // - or if we still have any chunks and the length is long\n while (total > this.chunkOverlap ||\n (total + _len + currentDoc.length * separator.length >\n this.chunkSize &&\n total > 0)) {\n total -= await this.lengthFunction(currentDoc[0]);\n currentDoc.shift();\n }\n }\n }\n currentDoc.push(d);\n total += _len;\n }\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n return docs;\n }\n}\nexport class CharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"CharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"\\n\\n\"\n });\n this.separator = fields?.separator ?? this.separator;\n }\n async splitText(text) {\n // First we naively split the large input into a bunch of smaller ones.\n const splits = this.splitOnSeparator(text, this.separator);\n return this.mergeSplits(splits, this.keepSeparator ? \"\" : this.separator);\n }\n}\nexport const SupportedTextSplitterLanguages = [\n \"cpp\",\n \"go\",\n \"java\",\n \"js\",\n \"php\",\n \"proto\",\n \"python\",\n \"rst\",\n \"ruby\",\n \"rust\",\n \"scala\",\n \"swift\",\n \"markdown\",\n \"latex\",\n \"html\",\n \"sol\",\n];\nexport class RecursiveCharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"RecursiveCharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separators\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"\\n\\n\", \"\\n\", \" \", \"\"]\n });\n this.separators = fields?.separators ?? this.separators;\n this.keepSeparator = fields?.keepSeparator ?? true;\n }\n async _splitText(text, separators) {\n const finalChunks = [];\n // Get appropriate separator to use\n let separator = separators[separators.length - 1];\n let newSeparators;\n for (let i = 0; i < separators.length; i += 1) {\n const s = separators[i];\n if (s === \"\") {\n separator = s;\n break;\n }\n if (text.includes(s)) {\n separator = s;\n newSeparators = separators.slice(i + 1);\n break;\n }\n }\n // Now that we have the separator, split the text\n const splits = this.splitOnSeparator(text, separator);\n // Now go merging things, recursively splitting longer texts.\n let goodSplits = [];\n const _separator = this.keepSeparator ? \"\" : separator;\n for (const s of splits) {\n if ((await this.lengthFunction(s)) < this.chunkSize) {\n goodSplits.push(s);\n }\n else {\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n goodSplits = [];\n }\n if (!newSeparators) {\n finalChunks.push(s);\n }\n else {\n const otherInfo = await this._splitText(s, newSeparators);\n finalChunks.push(...otherInfo);\n }\n }\n }\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n }\n return finalChunks;\n }\n async splitText(text) {\n return this._splitText(text, this.separators);\n }\n static fromLanguage(language, options) {\n return new RecursiveCharacterTextSplitter({\n ...options,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(language),\n });\n }\n static getSeparatorsForLanguage(language) {\n if (language === \"cpp\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along function definitions\n \"\\nvoid \",\n \"\\nint \",\n \"\\nfloat \",\n \"\\ndouble \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"go\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n \"\\nvar \",\n \"\\nconst \",\n \"\\ntype \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"java\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along method definitions\n \"\\npublic \",\n \"\\nprotected \",\n \"\\nprivate \",\n \"\\nstatic \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"js\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n \"\\nconst \",\n \"\\nlet \",\n \"\\nvar \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n \"\\ndefault \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"php\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n // Split along class definitions\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nforeach \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"proto\") {\n return [\n // Split along message definitions\n \"\\nmessage \",\n // Split along service definitions\n \"\\nservice \",\n // Split along enum definitions\n \"\\nenum \",\n // Split along option definitions\n \"\\noption \",\n // Split along import statements\n \"\\nimport \",\n // Split along syntax declarations\n \"\\nsyntax \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"python\") {\n return [\n // First, try to split along class definitions\n \"\\nclass \",\n \"\\ndef \",\n \"\\n\\tdef \",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rst\") {\n return [\n // Split along section titles\n \"\\n===\\n\",\n \"\\n---\\n\",\n \"\\n***\\n\",\n // Split along directive markers\n \"\\n.. \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"ruby\") {\n return [\n // Split along method definitions\n \"\\ndef \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nunless \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\ndo \",\n \"\\nbegin \",\n \"\\nrescue \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rust\") {\n return [\n // Split along function definitions\n \"\\nfn \",\n \"\\nconst \",\n \"\\nlet \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\nloop \",\n \"\\nmatch \",\n \"\\nconst \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"scala\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n \"\\nobject \",\n // Split along method definitions\n \"\\ndef \",\n \"\\nval \",\n \"\\nvar \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nmatch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"swift\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n // Split along class definitions\n \"\\nclass \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"markdown\") {\n return [\n // First, try to split along Markdown headings (starting with level 2)\n \"\\n## \",\n \"\\n### \",\n \"\\n#### \",\n \"\\n##### \",\n \"\\n###### \",\n // Note the alternative syntax for headings (below) is not handled here\n // Heading level 2\n // ---------------\n // End of code block\n \"```\\n\\n\",\n // Horizontal lines\n \"\\n\\n***\\n\\n\",\n \"\\n\\n---\\n\\n\",\n \"\\n\\n___\\n\\n\",\n // Note that this splitter doesn't handle horizontal lines defined\n // by *three or more* of ***, ---, or ___, but this is not handled\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"latex\") {\n return [\n // First, try to split along Latex sections\n \"\\n\\\\chapter{\",\n \"\\n\\\\section{\",\n \"\\n\\\\subsection{\",\n \"\\n\\\\subsubsection{\",\n // Now split by environments\n \"\\n\\\\begin{enumerate}\",\n \"\\n\\\\begin{itemize}\",\n \"\\n\\\\begin{description}\",\n \"\\n\\\\begin{list}\",\n \"\\n\\\\begin{quote}\",\n \"\\n\\\\begin{quotation}\",\n \"\\n\\\\begin{verse}\",\n \"\\n\\\\begin{verbatim}\",\n // Now split by math environments\n \"\\n\\\\begin{align}\",\n \"$$\",\n \"$\",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"html\") {\n return [\n // First, try to split along HTML tags\n \"<body>\",\n \"<div>\",\n \"<p>\",\n \"<br>\",\n \"<li>\",\n \"<h1>\",\n \"<h2>\",\n \"<h3>\",\n \"<h4>\",\n \"<h5>\",\n \"<h6>\",\n \"<span>\",\n \"<table>\",\n \"<tr>\",\n \"<td>\",\n \"<th>\",\n \"<ul>\",\n \"<ol>\",\n \"<header>\",\n \"<footer>\",\n \"<nav>\",\n // Head\n \"<head>\",\n \"<style>\",\n \"<script>\",\n \"<meta>\",\n \"<title>\",\n // Normal type of lines\n \" \",\n \"\",\n ];\n }\n else if (language === \"sol\") {\n return [\n // Split along compiler informations definitions\n \"\\npragma \",\n \"\\nusing \",\n // Split along contract definitions\n \"\\ncontract \",\n \"\\ninterface \",\n \"\\nlibrary \",\n // Split along method definitions\n \"\\nconstructor \",\n \"\\ntype \",\n \"\\nfunction \",\n \"\\nevent \",\n \"\\nmodifier \",\n \"\\nerror \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo while \",\n \"\\nassembly \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else {\n throw new Error(`Language ${language} is not supported.`);\n }\n }\n}\n/**\n * Implementation of splitter which looks at tokens.\n */\nexport class TokenTextSplitter extends TextSplitter {\n static lc_name() {\n return \"TokenTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"encodingName\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"allowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"disallowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tokenizer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.encodingName = fields?.encodingName ?? \"gpt2\";\n this.allowedSpecial = fields?.allowedSpecial ?? [];\n this.disallowedSpecial = fields?.disallowedSpecial ?? \"all\";\n }\n async splitText(text) {\n if (!this.tokenizer) {\n this.tokenizer = await getEncoding(this.encodingName);\n }\n const splits = [];\n const input_ids = this.tokenizer.encode(text, this.allowedSpecial, this.disallowedSpecial);\n let start_idx = 0;\n while (start_idx < input_ids.length) {\n if (start_idx > 0) {\n start_idx -= this.chunkOverlap;\n }\n const end_idx = Math.min(start_idx + this.chunkSize, input_ids.length);\n const chunk_ids = input_ids.slice(start_idx, end_idx);\n splits.push(this.tokenizer.decode(chunk_ids));\n start_idx = end_idx;\n }\n return splits;\n }\n}\nexport class MarkdownTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"markdown\"),\n });\n }\n}\nexport class LatexTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"latex\"),\n });\n }\n}\n","import { ChatOpenAI } from \"@langchain/openai\";\n\ninterface InitializeModelProps {\n openAIApiKey: string;\n model: string;\n temp: number;\n maxTokens?: number;\n}\n\nexport async function initializeModel({\n openAIApiKey,\n model,\n temp,\n}: InitializeModelProps) {\n return new ChatOpenAI({\n temperature: temp,\n openAIApiKey: openAIApiKey,\n modelName: model,\n maxTokens: 1000,\n });\n}","export interface TranscriptSegment {\n text: string;\n start: number;\n end: number;\n duration: number;\n}\n\nexport interface TranscriptData {\n title: string;\n videoId: string;\n thumbnailUrl: string;\n fullTranscript: string;\n transcriptWithTimeCodes: TranscriptSegment[];\n}\n\nconst fetchTranscript = async (\n identifier: string\n): Promise<TranscriptData> => {\n console.log(\"Fetching Transcript - Calling fetchTranscript Utils\");\n const { Innertube } = await import('youtubei.js');\n\n console.log(\"Creating YouTube instance\");\n\n const youtube = await Innertube.create({\n lang: 'en',\n location: 'US',\n retrieve_player: false,\n });\n\n try {\n const info = await youtube.getInfo(identifier);\n const transcriptData = await info.getTranscript();\n\n console.log(\"Transcript data fetched\");\n\n const transcriptWithTimeCodes: TranscriptSegment[] = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => {\n const segmentDuration = Number(segment.end_ms) - Number(segment.start_ms);\n return {\n text: segment.snippet.text,\n start: Number(segment.start_ms),\n end: Number(segment.end_ms),\n duration: segmentDuration,\n };\n }\n );\n\n console.log(\"Transcript with time codes generated\");\n\n function cleanImageUrl(url) {\n return url.split('?')[0];\n }\n\n console.log(\"Cleaning thumbnail URL\");\n\n const fullTranscript = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => segment.snippet.text\n ).join(' ');\n\n console.log(\"Full transcript generated\");\n\n const title = info.basic_info.title;\n const videoId = info.basic_info.id;\n const thumbnailUrl = info.basic_info.thumbnail[0].url;\n const thumbnailUrlCleaned = cleanImageUrl(thumbnailUrl);\n\n console.log(\"Returning transcript data\");\n\n return {\n title,\n videoId,\n thumbnailUrl: thumbnailUrlCleaned || \"\",\n fullTranscript,\n transcriptWithTimeCodes,\n };\n } catch (error) {\n console.error('Error fetching transcript:', error);\n throw error;\n }\n};\n\nexport default fetchTranscript;","import type { Core } from '@strapi/strapi';\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { TokenTextSplitter } from \"@langchain/textsplitters\";\nimport { PromptTemplate } from \"@langchain/core/prompts\";\n\nimport { initializeModel } from \"../utils/openai\";\nimport fetchTranscript from '../utils/fetch-transcript';\n\ninterface YTTranscriptConfig {\n openAIApiKey: string;\n model?: string;\n temp?: number;\n maxTokens?: number;\n}\n\nasync function processTextChunks(chunks: string[], model: ChatOpenAI) {\n const punctuationPrompt = PromptTemplate.fromTemplate(\n \"Add proper punctuation and capitalization to the following text chunk:\\n\\n{chunk}\"\n );\n const punctuationChain = punctuationPrompt.pipe(model);\n\n const processedChunks = await Promise.all(\n chunks.map(async (chunk) => {\n const result = await punctuationChain.invoke({ chunk });\n return result.content as string;\n })\n );\n\n return processedChunks.join(\" \");\n}\n\nexport async function generateModifiedTranscript (rawTranscript: string) {\n const pluginSettings = await strapi.config.get('plugin::yt-transcript') as YTTranscriptConfig; \n \n if (!pluginSettings.openAIApiKey || !pluginSettings.model || !pluginSettings.temp || !pluginSettings.maxTokens) {\n throw new Error('Missing required configuration for YTTranscript');\n }\n\n const chatModel = await initializeModel({\n openAIApiKey: pluginSettings.openAIApiKey,\n model: pluginSettings.model,\n temp: pluginSettings.temp,\n maxTokens: pluginSettings.maxTokens,\n });\n\n const splitter = new TokenTextSplitter({\n chunkSize: 1000,\n chunkOverlap: 200,\n });\n\n const transcriptChunks = await splitter.createDocuments([rawTranscript]);\n const chunkTexts = transcriptChunks.map(chunk => chunk.pageContent);\n const modifiedTranscript = await processTextChunks(chunkTexts, chatModel);\n return modifiedTranscript;\n}\n\nconst service = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(identifier: string) {\n console.log(\"Fetching Transcript - Calling fetchTranscript Service\");\n const youtubeIdRegex = /^[a-zA-Z0-9_-]{11}$/;\n const isValid = youtubeIdRegex.test(identifier);\n if (!isValid) return { error: 'Invalid video ID', data: null };\n const transcriptData = await fetchTranscript(identifier);\n return transcriptData;\n },\n\n async saveTranscript(payload) {\n // console.log('Saving transcript:', payload);\n return await strapi.documents('plugin::yt-transcript.transcript').create({\n data: payload,\n });\n },\n\n async findTranscript(videoId) {\n console.log('Finding transcript for videoId:', videoId);\n const transcriptData = await strapi.documents('plugin::yt-transcript.transcript').findFirst({\n filters: { videoId },\n });\n\n console.log('Transcript found:', transcriptData?.title, 'found');\n\n if (!transcriptData) return null;\n return transcriptData;\n },\n\n async generateHumanReadableTranscript(transcript) {\n console.log('Generating human readable transcript:');\n const modifiedTranscript = await generateModifiedTranscript(transcript);\n return modifiedTranscript;\n },\n});\n\nexport default service;\n","import service from './service';\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport destroy from './destroy';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy,\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["strapi","transcript","BaseDocumentTransformer","documents","Document","doc","getEncoding","ChatOpenAI","info","PromptTemplate"],"mappings":"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","x_google_ignoreList":[14]}
|
package/dist/server/index.mjs
CHANGED
|
@@ -86,10 +86,17 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
86
86
|
async getTranscript(ctx) {
|
|
87
87
|
const videoId = extractYouTubeID(ctx.params.videoId);
|
|
88
88
|
if (!videoId) return ctx.body = { error: "Invalid YouTube URL or ID", data: null };
|
|
89
|
+
console.log("Looking for transcript in database");
|
|
89
90
|
const found = await strapi2.plugin("yt-transcript").service("service").findTranscript(videoId);
|
|
90
|
-
if (found)
|
|
91
|
+
if (found) {
|
|
92
|
+
console.log("Transcript found.");
|
|
93
|
+
return ctx.body = { data: found };
|
|
94
|
+
}
|
|
95
|
+
console.log("Transcript not found. Fetching new transcript.");
|
|
91
96
|
const transcriptData = await strapi2.plugin("yt-transcript").service("service").getTranscript(videoId);
|
|
97
|
+
console.log("New transcript fetched.");
|
|
92
98
|
const readableTranscript = await strapi2.plugin("yt-transcript").service("service").generateHumanReadableTranscript(transcriptData.fullTranscript);
|
|
99
|
+
console.log("Human readable transcript generated.");
|
|
93
100
|
const payload = {
|
|
94
101
|
title: transcriptData.title,
|
|
95
102
|
transcript: transcriptData.transcript,
|
|
@@ -99,6 +106,7 @@ const controller = ({ strapi: strapi2 }) => ({
|
|
|
99
106
|
transcriptWithTimeCodes: transcriptData.transcriptWithTimeCodes,
|
|
100
107
|
readableTranscript
|
|
101
108
|
};
|
|
109
|
+
console.log("Saving new transcript to database.");
|
|
102
110
|
const transcript2 = await strapi2.plugin("yt-transcript").service("service").saveTranscript(payload);
|
|
103
111
|
ctx.body = { data: transcript2 };
|
|
104
112
|
}
|
|
@@ -358,7 +366,9 @@ async function initializeModel({
|
|
|
358
366
|
});
|
|
359
367
|
}
|
|
360
368
|
const fetchTranscript = async (identifier) => {
|
|
369
|
+
console.log("Fetching Transcript - Calling fetchTranscript Utils");
|
|
361
370
|
const { Innertube } = await import("youtubei.js");
|
|
371
|
+
console.log("Creating YouTube instance");
|
|
362
372
|
const youtube = await Innertube.create({
|
|
363
373
|
lang: "en",
|
|
364
374
|
location: "US",
|
|
@@ -370,6 +380,7 @@ const fetchTranscript = async (identifier) => {
|
|
|
370
380
|
};
|
|
371
381
|
const info2 = await youtube.getInfo(identifier);
|
|
372
382
|
const transcriptData = await info2.getTranscript();
|
|
383
|
+
console.log("Transcript data fetched");
|
|
373
384
|
const transcriptWithTimeCodes = transcriptData?.transcript?.content?.body?.initial_segments.map(
|
|
374
385
|
(segment) => {
|
|
375
386
|
const segmentDuration = Number(segment.end_ms) - Number(segment.start_ms);
|
|
@@ -381,23 +392,21 @@ const fetchTranscript = async (identifier) => {
|
|
|
381
392
|
};
|
|
382
393
|
}
|
|
383
394
|
);
|
|
395
|
+
console.log("Transcript with time codes generated");
|
|
396
|
+
console.log("Cleaning thumbnail URL");
|
|
384
397
|
const fullTranscript = transcriptData?.transcript?.content?.body?.initial_segments.map(
|
|
385
398
|
(segment) => segment.snippet.text
|
|
386
399
|
).join(" ");
|
|
400
|
+
console.log("Full transcript generated");
|
|
387
401
|
const title = info2.basic_info.title;
|
|
388
402
|
const videoId = info2.basic_info.id;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (processedThumbnailUrl) {
|
|
393
|
-
thumbnailUrl = cleanImageUrl(processedThumbnailUrl);
|
|
394
|
-
} else {
|
|
395
|
-
thumbnailUrl = "";
|
|
396
|
-
}
|
|
403
|
+
const thumbnailUrl = info2.basic_info.thumbnail[0].url;
|
|
404
|
+
const thumbnailUrlCleaned = cleanImageUrl(thumbnailUrl);
|
|
405
|
+
console.log("Returning transcript data");
|
|
397
406
|
return {
|
|
398
407
|
title,
|
|
399
408
|
videoId,
|
|
400
|
-
thumbnailUrl,
|
|
409
|
+
thumbnailUrl: thumbnailUrlCleaned || "",
|
|
401
410
|
fullTranscript,
|
|
402
411
|
transcriptWithTimeCodes
|
|
403
412
|
};
|
|
@@ -421,7 +430,6 @@ async function processTextChunks(chunks, model) {
|
|
|
421
430
|
}
|
|
422
431
|
async function generateModifiedTranscript(rawTranscript) {
|
|
423
432
|
const pluginSettings = await strapi.config.get("plugin::yt-transcript");
|
|
424
|
-
console.log("pluginSettings", pluginSettings);
|
|
425
433
|
if (!pluginSettings.openAIApiKey || !pluginSettings.model || !pluginSettings.temp || !pluginSettings.maxTokens) {
|
|
426
434
|
throw new Error("Missing required configuration for YTTranscript");
|
|
427
435
|
}
|
|
@@ -442,6 +450,7 @@ async function generateModifiedTranscript(rawTranscript) {
|
|
|
442
450
|
}
|
|
443
451
|
const service = ({ strapi: strapi2 }) => ({
|
|
444
452
|
async getTranscript(identifier) {
|
|
453
|
+
console.log("Fetching Transcript - Calling fetchTranscript Service");
|
|
445
454
|
const youtubeIdRegex = /^[a-zA-Z0-9_-]{11}$/;
|
|
446
455
|
const isValid = youtubeIdRegex.test(identifier);
|
|
447
456
|
if (!isValid) return { error: "Invalid video ID", data: null };
|
|
@@ -463,7 +472,7 @@ const service = ({ strapi: strapi2 }) => ({
|
|
|
463
472
|
return transcriptData;
|
|
464
473
|
},
|
|
465
474
|
async generateHumanReadableTranscript(transcript2) {
|
|
466
|
-
console.log("Generating human readable transcript:"
|
|
475
|
+
console.log("Generating human readable transcript:");
|
|
467
476
|
const modifiedTranscript = await generateModifiedTranscript(transcript2);
|
|
468
477
|
return modifiedTranscript;
|
|
469
478
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/register.ts","../../server/src/config/index.ts","../../server/src/content-types/transcript/index.ts","../../server/src/content-types/index.ts","../../server/src/utils/extract-youtube-id.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/index.ts","../../server/src/middlewares/index.ts","../../server/src/policies/index.ts","../../server/src/routes/content-api.ts","../../server/src/routes/admin.ts","../../server/src/routes/index.ts","../../node_modules/@langchain/textsplitters/dist/text_splitter.js","../../server/src/utils/openai.ts","../../server/src/utils/fetch-transcript.ts","../../server/src/services/service.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["import type { Core } from '@strapi/strapi';\n\nconst bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {\n // bootstrap phase\n};\n\nexport default bootstrap;\n","import type { Core } from '@strapi/strapi';\n\nconst destroy = ({ strapi }: { strapi: Core.Strapi }) => {\n // destroy phase\n};\n\nexport default destroy;\n","import type { Core } from '@strapi/strapi';\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n // register phase\n};\n\nexport default register;\n","export default {\n default: {},\n validator() {},\n};\n","import schema from './schema.json';\n\nexport default {\n schema,\n};","import transcript from './transcript';\n\nexport default {\n transcript,\n};\n\n\n","export function extractYouTubeID(urlOrID: string): string | null {\n // Regular expression for YouTube ID format\n const regExpID = /^[a-zA-Z0-9_-]{11}$/;\n\n // Check if the input is a YouTube ID\n if (regExpID.test(urlOrID)) {\n return urlOrID;\n }\n\n // Regular expression for standard YouTube links\n const regExpStandard = /youtube\\.com\\/watch\\?v=([a-zA-Z0-9_-]+)/;\n\n // Regular expression for YouTube Shorts links\n const regExpShorts = /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]+)/;\n\n // Check for standard YouTube link\n const matchStandard = urlOrID.match(regExpStandard);\n if (matchStandard) {\n return matchStandard[1];\n }\n\n // Check for YouTube Shorts link\n const matchShorts = urlOrID.match(regExpShorts);\n if (matchShorts) {\n return matchShorts[1];\n }\n\n // Return null if no match is found\n return null;\n}","import type { Core } from '@strapi/strapi';\nimport { extractYouTubeID } from '../utils/extract-youtube-id';\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(ctx) {\n const videoId = extractYouTubeID(ctx.params.videoId);\n\n if (!videoId) return (ctx.body = { error: 'Invalid YouTube URL or ID', data: null });\n\n const found = await strapi\n .plugin('yt-transcript')\n .service('service')\n .findTranscript(videoId);\n\n if (found) return (ctx.body = { data: found });\n\n const transcriptData = await strapi\n .plugin('yt-transcript')\n .service('service')\n .getTranscript(videoId);\n\n const readableTranscript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .generateHumanReadableTranscript(transcriptData.fullTranscript);\n\n const payload = {\n title: transcriptData.title,\n transcript: transcriptData.transcript,\n videoId: transcriptData.videoId,\n thumbnailUrl: transcriptData.thumbnailUrl,\n fullTranscript: transcriptData.fullTranscript,\n transcriptWithTimeCodes: transcriptData.transcriptWithTimeCodes,\n readableTranscript: readableTranscript,\n };\n\n // console.log('Payload:', payload);\n\n const transcript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .saveTranscript(payload);\n\n ctx.body = { data: transcript };\n },\n});\n\nexport default controller;\n","import controller from './controller';\n\nexport default {\n controller,\n};\n","export default {};\n","export default {};\n","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","\"use strict\";\n\nimport contentApi from \"./content-api\";\nimport admin from \"./admin\";\n\nexport default {\n \"content-api\": {\n type: \"content-api\",\n routes: [...contentApi],\n },\n admin: {\n type: \"admin\",\n routes: [...admin],\n },\n};","import { Document, BaseDocumentTransformer } from \"@langchain/core/documents\";\nimport { getEncoding } from \"@langchain/core/utils/tiktoken\";\nexport class TextSplitter extends BaseDocumentTransformer {\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"lc_namespace\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"langchain\", \"document_transformers\", \"text_splitters\"]\n });\n Object.defineProperty(this, \"chunkSize\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1000\n });\n Object.defineProperty(this, \"chunkOverlap\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 200\n });\n Object.defineProperty(this, \"keepSeparator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"lengthFunction\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.chunkSize = fields?.chunkSize ?? this.chunkSize;\n this.chunkOverlap = fields?.chunkOverlap ?? this.chunkOverlap;\n this.keepSeparator = fields?.keepSeparator ?? this.keepSeparator;\n this.lengthFunction =\n fields?.lengthFunction ?? ((text) => text.length);\n if (this.chunkOverlap >= this.chunkSize) {\n throw new Error(\"Cannot have chunkOverlap >= chunkSize\");\n }\n }\n async transformDocuments(documents, chunkHeaderOptions = {}) {\n return this.splitDocuments(documents, chunkHeaderOptions);\n }\n splitOnSeparator(text, separator) {\n let splits;\n if (separator) {\n if (this.keepSeparator) {\n const regexEscapedSeparator = separator.replace(/[/\\-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n splits = text.split(new RegExp(`(?=${regexEscapedSeparator})`));\n }\n else {\n splits = text.split(separator);\n }\n }\n else {\n splits = text.split(\"\");\n }\n return splits.filter((s) => s !== \"\");\n }\n async createDocuments(texts, \n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n metadatas = [], chunkHeaderOptions = {}) {\n // if no metadata is provided, we create an empty one for each text\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const _metadatas = metadatas.length > 0\n ? metadatas\n : [...Array(texts.length)].map(() => ({}));\n const { chunkHeader = \"\", chunkOverlapHeader = \"(cont'd) \", appendChunkOverlapHeader = false, } = chunkHeaderOptions;\n const documents = new Array();\n for (let i = 0; i < texts.length; i += 1) {\n const text = texts[i];\n let lineCounterIndex = 1;\n let prevChunk = null;\n let indexPrevChunk = -1;\n for (const chunk of await this.splitText(text)) {\n let pageContent = chunkHeader;\n // we need to count the \\n that are in the text before getting removed by the splitting\n const indexChunk = text.indexOf(chunk, indexPrevChunk + 1);\n if (prevChunk === null) {\n const newLinesBeforeFirstChunk = this.numberOfNewLines(text, 0, indexChunk);\n lineCounterIndex += newLinesBeforeFirstChunk;\n }\n else {\n const indexEndPrevChunk = indexPrevChunk + (await this.lengthFunction(prevChunk));\n if (indexEndPrevChunk < indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexEndPrevChunk, indexChunk);\n lineCounterIndex += numberOfIntermediateNewLines;\n }\n else if (indexEndPrevChunk > indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexChunk, indexEndPrevChunk);\n lineCounterIndex -= numberOfIntermediateNewLines;\n }\n if (appendChunkOverlapHeader) {\n pageContent += chunkOverlapHeader;\n }\n }\n const newLinesCount = this.numberOfNewLines(chunk);\n const loc = _metadatas[i].loc && typeof _metadatas[i].loc === \"object\"\n ? { ..._metadatas[i].loc }\n : {};\n loc.lines = {\n from: lineCounterIndex,\n to: lineCounterIndex + newLinesCount,\n };\n const metadataWithLinesNumber = {\n ..._metadatas[i],\n loc,\n };\n pageContent += chunk;\n documents.push(new Document({\n pageContent,\n metadata: metadataWithLinesNumber,\n }));\n lineCounterIndex += newLinesCount;\n prevChunk = chunk;\n indexPrevChunk = indexChunk;\n }\n }\n return documents;\n }\n numberOfNewLines(text, start, end) {\n const textSection = text.slice(start, end);\n return (textSection.match(/\\n/g) || []).length;\n }\n async splitDocuments(documents, chunkHeaderOptions = {}) {\n const selectedDocuments = documents.filter((doc) => doc.pageContent !== undefined);\n const texts = selectedDocuments.map((doc) => doc.pageContent);\n const metadatas = selectedDocuments.map((doc) => doc.metadata);\n return this.createDocuments(texts, metadatas, chunkHeaderOptions);\n }\n joinDocs(docs, separator) {\n const text = docs.join(separator).trim();\n return text === \"\" ? null : text;\n }\n async mergeSplits(splits, separator) {\n const docs = [];\n const currentDoc = [];\n let total = 0;\n for (const d of splits) {\n const _len = await this.lengthFunction(d);\n if (total + _len + currentDoc.length * separator.length >\n this.chunkSize) {\n if (total > this.chunkSize) {\n console.warn(`Created a chunk of size ${total}, +\nwhich is longer than the specified ${this.chunkSize}`);\n }\n if (currentDoc.length > 0) {\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n // Keep on popping if:\n // - we have a larger chunk than in the chunk overlap\n // - or if we still have any chunks and the length is long\n while (total > this.chunkOverlap ||\n (total + _len + currentDoc.length * separator.length >\n this.chunkSize &&\n total > 0)) {\n total -= await this.lengthFunction(currentDoc[0]);\n currentDoc.shift();\n }\n }\n }\n currentDoc.push(d);\n total += _len;\n }\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n return docs;\n }\n}\nexport class CharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"CharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"\\n\\n\"\n });\n this.separator = fields?.separator ?? this.separator;\n }\n async splitText(text) {\n // First we naively split the large input into a bunch of smaller ones.\n const splits = this.splitOnSeparator(text, this.separator);\n return this.mergeSplits(splits, this.keepSeparator ? \"\" : this.separator);\n }\n}\nexport const SupportedTextSplitterLanguages = [\n \"cpp\",\n \"go\",\n \"java\",\n \"js\",\n \"php\",\n \"proto\",\n \"python\",\n \"rst\",\n \"ruby\",\n \"rust\",\n \"scala\",\n \"swift\",\n \"markdown\",\n \"latex\",\n \"html\",\n \"sol\",\n];\nexport class RecursiveCharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"RecursiveCharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separators\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"\\n\\n\", \"\\n\", \" \", \"\"]\n });\n this.separators = fields?.separators ?? this.separators;\n this.keepSeparator = fields?.keepSeparator ?? true;\n }\n async _splitText(text, separators) {\n const finalChunks = [];\n // Get appropriate separator to use\n let separator = separators[separators.length - 1];\n let newSeparators;\n for (let i = 0; i < separators.length; i += 1) {\n const s = separators[i];\n if (s === \"\") {\n separator = s;\n break;\n }\n if (text.includes(s)) {\n separator = s;\n newSeparators = separators.slice(i + 1);\n break;\n }\n }\n // Now that we have the separator, split the text\n const splits = this.splitOnSeparator(text, separator);\n // Now go merging things, recursively splitting longer texts.\n let goodSplits = [];\n const _separator = this.keepSeparator ? \"\" : separator;\n for (const s of splits) {\n if ((await this.lengthFunction(s)) < this.chunkSize) {\n goodSplits.push(s);\n }\n else {\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n goodSplits = [];\n }\n if (!newSeparators) {\n finalChunks.push(s);\n }\n else {\n const otherInfo = await this._splitText(s, newSeparators);\n finalChunks.push(...otherInfo);\n }\n }\n }\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n }\n return finalChunks;\n }\n async splitText(text) {\n return this._splitText(text, this.separators);\n }\n static fromLanguage(language, options) {\n return new RecursiveCharacterTextSplitter({\n ...options,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(language),\n });\n }\n static getSeparatorsForLanguage(language) {\n if (language === \"cpp\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along function definitions\n \"\\nvoid \",\n \"\\nint \",\n \"\\nfloat \",\n \"\\ndouble \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"go\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n \"\\nvar \",\n \"\\nconst \",\n \"\\ntype \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"java\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along method definitions\n \"\\npublic \",\n \"\\nprotected \",\n \"\\nprivate \",\n \"\\nstatic \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"js\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n \"\\nconst \",\n \"\\nlet \",\n \"\\nvar \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n \"\\ndefault \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"php\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n // Split along class definitions\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nforeach \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"proto\") {\n return [\n // Split along message definitions\n \"\\nmessage \",\n // Split along service definitions\n \"\\nservice \",\n // Split along enum definitions\n \"\\nenum \",\n // Split along option definitions\n \"\\noption \",\n // Split along import statements\n \"\\nimport \",\n // Split along syntax declarations\n \"\\nsyntax \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"python\") {\n return [\n // First, try to split along class definitions\n \"\\nclass \",\n \"\\ndef \",\n \"\\n\\tdef \",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rst\") {\n return [\n // Split along section titles\n \"\\n===\\n\",\n \"\\n---\\n\",\n \"\\n***\\n\",\n // Split along directive markers\n \"\\n.. \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"ruby\") {\n return [\n // Split along method definitions\n \"\\ndef \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nunless \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\ndo \",\n \"\\nbegin \",\n \"\\nrescue \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rust\") {\n return [\n // Split along function definitions\n \"\\nfn \",\n \"\\nconst \",\n \"\\nlet \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\nloop \",\n \"\\nmatch \",\n \"\\nconst \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"scala\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n \"\\nobject \",\n // Split along method definitions\n \"\\ndef \",\n \"\\nval \",\n \"\\nvar \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nmatch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"swift\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n // Split along class definitions\n \"\\nclass \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"markdown\") {\n return [\n // First, try to split along Markdown headings (starting with level 2)\n \"\\n## \",\n \"\\n### \",\n \"\\n#### \",\n \"\\n##### \",\n \"\\n###### \",\n // Note the alternative syntax for headings (below) is not handled here\n // Heading level 2\n // ---------------\n // End of code block\n \"```\\n\\n\",\n // Horizontal lines\n \"\\n\\n***\\n\\n\",\n \"\\n\\n---\\n\\n\",\n \"\\n\\n___\\n\\n\",\n // Note that this splitter doesn't handle horizontal lines defined\n // by *three or more* of ***, ---, or ___, but this is not handled\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"latex\") {\n return [\n // First, try to split along Latex sections\n \"\\n\\\\chapter{\",\n \"\\n\\\\section{\",\n \"\\n\\\\subsection{\",\n \"\\n\\\\subsubsection{\",\n // Now split by environments\n \"\\n\\\\begin{enumerate}\",\n \"\\n\\\\begin{itemize}\",\n \"\\n\\\\begin{description}\",\n \"\\n\\\\begin{list}\",\n \"\\n\\\\begin{quote}\",\n \"\\n\\\\begin{quotation}\",\n \"\\n\\\\begin{verse}\",\n \"\\n\\\\begin{verbatim}\",\n // Now split by math environments\n \"\\n\\\\begin{align}\",\n \"$$\",\n \"$\",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"html\") {\n return [\n // First, try to split along HTML tags\n \"<body>\",\n \"<div>\",\n \"<p>\",\n \"<br>\",\n \"<li>\",\n \"<h1>\",\n \"<h2>\",\n \"<h3>\",\n \"<h4>\",\n \"<h5>\",\n \"<h6>\",\n \"<span>\",\n \"<table>\",\n \"<tr>\",\n \"<td>\",\n \"<th>\",\n \"<ul>\",\n \"<ol>\",\n \"<header>\",\n \"<footer>\",\n \"<nav>\",\n // Head\n \"<head>\",\n \"<style>\",\n \"<script>\",\n \"<meta>\",\n \"<title>\",\n // Normal type of lines\n \" \",\n \"\",\n ];\n }\n else if (language === \"sol\") {\n return [\n // Split along compiler informations definitions\n \"\\npragma \",\n \"\\nusing \",\n // Split along contract definitions\n \"\\ncontract \",\n \"\\ninterface \",\n \"\\nlibrary \",\n // Split along method definitions\n \"\\nconstructor \",\n \"\\ntype \",\n \"\\nfunction \",\n \"\\nevent \",\n \"\\nmodifier \",\n \"\\nerror \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo while \",\n \"\\nassembly \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else {\n throw new Error(`Language ${language} is not supported.`);\n }\n }\n}\n/**\n * Implementation of splitter which looks at tokens.\n */\nexport class TokenTextSplitter extends TextSplitter {\n static lc_name() {\n return \"TokenTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"encodingName\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"allowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"disallowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tokenizer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.encodingName = fields?.encodingName ?? \"gpt2\";\n this.allowedSpecial = fields?.allowedSpecial ?? [];\n this.disallowedSpecial = fields?.disallowedSpecial ?? \"all\";\n }\n async splitText(text) {\n if (!this.tokenizer) {\n this.tokenizer = await getEncoding(this.encodingName);\n }\n const splits = [];\n const input_ids = this.tokenizer.encode(text, this.allowedSpecial, this.disallowedSpecial);\n let start_idx = 0;\n while (start_idx < input_ids.length) {\n if (start_idx > 0) {\n start_idx -= this.chunkOverlap;\n }\n const end_idx = Math.min(start_idx + this.chunkSize, input_ids.length);\n const chunk_ids = input_ids.slice(start_idx, end_idx);\n splits.push(this.tokenizer.decode(chunk_ids));\n start_idx = end_idx;\n }\n return splits;\n }\n}\nexport class MarkdownTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"markdown\"),\n });\n }\n}\nexport class LatexTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"latex\"),\n });\n }\n}\n","import { ChatOpenAI } from \"@langchain/openai\";\n\ninterface InitializeModelProps {\n openAIApiKey: string;\n model: string;\n temp: number;\n maxTokens?: number;\n}\n\nexport async function initializeModel({\n openAIApiKey,\n model,\n temp,\n}: InitializeModelProps) {\n return new ChatOpenAI({\n temperature: temp,\n openAIApiKey: openAIApiKey,\n modelName: model,\n maxTokens: 1000,\n });\n}","export interface TranscriptSegment {\n text: string;\n start: number;\n end: number;\n duration: number;\n}\n\nexport interface TranscriptData {\n title: string;\n videoId: string;\n thumbnailUrl: string;\n fullTranscript: string;\n transcriptWithTimeCodes: TranscriptSegment[];\n}\n\nconst fetchTranscript = async (\n identifier: string\n): Promise<TranscriptData> => {\n const { Innertube } = await import('youtubei.js');\n\n const youtube = await Innertube.create({\n lang: 'en',\n location: 'US',\n retrieve_player: false,\n });\n\n try {\n const info = await youtube.getInfo(identifier);\n const transcriptData = await info.getTranscript();\n\n const transcriptWithTimeCodes: TranscriptSegment[] = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => {\n const segmentDuration = Number(segment.end_ms) - Number(segment.start_ms);\n return {\n text: segment.snippet.text,\n start: Number(segment.start_ms),\n end: Number(segment.end_ms),\n duration: segmentDuration,\n };\n }\n );\n\n function cleanImageUrl(url) {\n return url.split('?')[0];\n }\n\n const fullTranscript = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => segment.snippet.text\n ).join(' ');\n\n const title = info.basic_info.title;\n const videoId = info.basic_info.id;\n console.log(info.basic_info.thumbnail[0].url, \"what is this\");\n let thumbnailUrl = \"\";\n const processedThumbnailUrl = info.basic_info?.thumbnail?.[0]?.url;\n if (processedThumbnailUrl) {\n thumbnailUrl = cleanImageUrl(processedThumbnailUrl);\n } else {\n thumbnailUrl = \"\";\n }\n\n return {\n title,\n videoId,\n thumbnailUrl,\n fullTranscript,\n transcriptWithTimeCodes,\n };\n } catch (error) {\n console.error('Error fetching transcript:', error);\n throw error;\n }\n};\n\nexport default fetchTranscript;","import type { Core } from '@strapi/strapi';\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { TokenTextSplitter } from \"@langchain/textsplitters\";\nimport { PromptTemplate } from \"@langchain/core/prompts\";\n\nimport { initializeModel } from \"../utils/openai\";\nimport fetchTranscript from '../utils/fetch-transcript';\n\ninterface YTTranscriptConfig {\n openAIApiKey: string;\n model?: string;\n temp?: number;\n maxTokens?: number;\n}\n\nasync function processTextChunks(chunks: string[], model: ChatOpenAI) {\n const punctuationPrompt = PromptTemplate.fromTemplate(\n \"Add proper punctuation and capitalization to the following text chunk:\\n\\n{chunk}\"\n );\n const punctuationChain = punctuationPrompt.pipe(model);\n\n const processedChunks = await Promise.all(\n chunks.map(async (chunk) => {\n const result = await punctuationChain.invoke({ chunk });\n return result.content as string;\n })\n );\n\n return processedChunks.join(\" \");\n}\n\nexport async function generateModifiedTranscript (rawTranscript: string) {\n const pluginSettings = await strapi.config.get('plugin::yt-transcript') as YTTranscriptConfig; \n \n console.log('pluginSettings', pluginSettings);\n \n if (!pluginSettings.openAIApiKey || !pluginSettings.model || !pluginSettings.temp || !pluginSettings.maxTokens) {\n throw new Error('Missing required configuration for YTTranscript');\n }\n\n const chatModel = await initializeModel({\n openAIApiKey: pluginSettings.openAIApiKey,\n model: pluginSettings.model,\n temp: pluginSettings.temp,\n maxTokens: pluginSettings.maxTokens,\n });\n\n const splitter = new TokenTextSplitter({\n chunkSize: 1000,\n chunkOverlap: 200,\n });\n\n const transcriptChunks = await splitter.createDocuments([rawTranscript]);\n const chunkTexts = transcriptChunks.map(chunk => chunk.pageContent);\n const modifiedTranscript = await processTextChunks(chunkTexts, chatModel);\n\n return modifiedTranscript;\n}\n\nconst service = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(identifier: string) {\n const youtubeIdRegex = /^[a-zA-Z0-9_-]{11}$/;\n const isValid = youtubeIdRegex.test(identifier);\n if (!isValid) return { error: 'Invalid video ID', data: null };\n const transcriptData = await fetchTranscript(identifier);\n return transcriptData;\n },\n\n async saveTranscript(payload) {\n // console.log('Saving transcript:', payload);\n return await strapi.documents('plugin::yt-transcript.transcript').create({\n data: payload,\n });\n },\n\n async findTranscript(videoId) {\n console.log('Finding transcript for videoId:', videoId);\n const transcriptData = await strapi.documents('plugin::yt-transcript.transcript').findFirst({\n filters: { videoId },\n });\n\n console.log('Transcript found:', transcriptData?.title, 'found');\n\n if (!transcriptData) return null;\n return transcriptData;\n },\n\n async generateHumanReadableTranscript(transcript) {\n console.log('Generating human readable transcript:', transcript);\n const modifiedTranscript = await generateModifiedTranscript(transcript);\n return modifiedTranscript;\n },\n});\n\nexport default service;\n","import service from './service';\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport destroy from './destroy';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy,\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["strapi","transcript","doc","info"],"mappings":";;;;AAEA,MAAM,YAAY,CAAC,EAAE,QAAAA,cAAsC;AAE3D;ACFA,MAAM,UAAU,CAAC,EAAE,QAAAA,cAAsC;AAEzD;ACFA,MAAM,WAAW,CAAC,EAAE,QAAAA,cAAsC;AAE1D;ACJA,MAAe,SAAA;AAAA,EACb,SAAS,CAAC;AAAA,EACV,YAAY;AAAA,EAAA;AACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDA,MAAe,aAAA;AAAA,EACb;AACF;ACFA,MAAe,eAAA;AAAA,EACb;AACF;ACJO,SAAS,iBAAiB,SAAgC;AAE/D,QAAM,WAAW;AAGb,MAAA,SAAS,KAAK,OAAO,GAAG;AACnB,WAAA;AAAA,EAAA;AAIT,QAAM,iBAAiB;AAGvB,QAAM,eAAe;AAGf,QAAA,gBAAgB,QAAQ,MAAM,cAAc;AAClD,MAAI,eAAe;AACjB,WAAO,cAAc,CAAC;AAAA,EAAA;AAIlB,QAAA,cAAc,QAAQ,MAAM,YAAY;AAC9C,MAAI,aAAa;AACf,WAAO,YAAY,CAAC;AAAA,EAAA;AAIf,SAAA;AACT;AC3BA,MAAM,aAAa,CAAC,EAAE,QAAAA,eAAuC;AAAA,EAC3D,MAAM,cAAc,KAAK;AACvB,UAAM,UAAU,iBAAiB,IAAI,OAAO,OAAO;AAE/C,QAAA,CAAC,QAAiB,QAAA,IAAI,OAAO,EAAE,OAAO,6BAA6B,MAAM,KAAK;AAE5E,UAAA,QAAQ,MAAMA,QACjB,OAAO,eAAe,EACtB,QAAQ,SAAS,EACjB,eAAe,OAAO;AAEzB,QAAI,MAAe,QAAA,IAAI,OAAO,EAAE,MAAM,MAAM;AAEtC,UAAA,iBAAiB,MAAMA,QAC1B,OAAO,eAAe,EACtB,QAAQ,SAAS,EACjB,cAAc,OAAO;AAElB,UAAA,qBAAqB,MAAMA,QAC9B,OAAO,eAAe,EACtB,QAAQ,SAAS,EACjB,gCAAgC,eAAe,cAAc;AAEhE,UAAM,UAAU;AAAA,MACd,OAAO,eAAe;AAAA,MACtB,YAAY,eAAe;AAAA,MAC3B,SAAS,eAAe;AAAA,MACxB,cAAc,eAAe;AAAA,MAC7B,gBAAgB,eAAe;AAAA,MAC/B,yBAAyB,eAAe;AAAA,MACxC;AAAA,IACF;AAIM,UAAAC,cAAa,MAAMD,QACtB,OAAO,eAAe,EACtB,QAAQ,SAAS,EACjB,eAAe,OAAO;AAErB,QAAA,OAAO,EAAE,MAAMC,YAAW;AAAA,EAAA;AAElC;AC1CA,MAAe,cAAA;AAAA,EACb;AACF;ACJA,MAAA,cAAe,CAAC;ACAhB,MAAA,WAAe,CAAC;ACAhB,MAAe,aAAA;AAAA,EACb;AAAA,IACE,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,MACN,UAAU,CAAA;AAAA,IAAC;AAAA,EACb;AAEJ;ACTA,MAAe,QAAA;AAAA,EACb;AAAA,IACE,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,MACN,UAAU,CAAA;AAAA,IAAC;AAAA,EACb;AAEJ;ACJA,MAAe,SAAA;AAAA,EACb,eAAe;AAAA,IACb,MAAM;AAAA,IACN,QAAQ,CAAC,GAAG,UAAU;AAAA,EACxB;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,CAAC,GAAG,KAAK;AAAA,EAAA;AAErB;ACZO,MAAM,qBAAqB,wBAAwB;AAAA,EACtD,YAAY,QAAQ;AAChB,UAAM,MAAM;AACZ,WAAO,eAAe,MAAM,gBAAgB;AAAA,MACxC,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO,CAAC,aAAa,yBAAyB,gBAAgB;AAAA,IAC1E,CAAS;AACD,WAAO,eAAe,MAAM,aAAa;AAAA,MACrC,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,WAAO,eAAe,MAAM,gBAAgB;AAAA,MACxC,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,WAAO,eAAe,MAAM,iBAAiB;AAAA,MACzC,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,WAAO,eAAe,MAAM,kBAAkB;AAAA,MAC1C,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,SAAK,YAAY,QAAQ,aAAa,KAAK;AAC3C,SAAK,eAAe,QAAQ,gBAAgB,KAAK;AACjD,SAAK,gBAAgB,QAAQ,iBAAiB,KAAK;AACnD,SAAK,iBACD,QAAQ,mBAAmB,CAAC,SAAS,KAAK;AAC9C,QAAI,KAAK,gBAAgB,KAAK,WAAW;AACrC,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACnE;AAAA,EACA;AAAA,EACI,MAAM,mBAAmB,WAAW,qBAAqB,IAAI;AACzD,WAAO,KAAK,eAAe,WAAW,kBAAkB;AAAA,EAChE;AAAA,EACI,iBAAiB,MAAM,WAAW;AAC9B,QAAI;AACJ,QAAI,WAAW;AACX,UAAI,KAAK,eAAe;AACpB,cAAM,wBAAwB,UAAU,QAAQ,0BAA0B,MAAM;AAChF,iBAAS,KAAK,MAAM,IAAI,OAAO,MAAM,qBAAqB,GAAG,CAAC;AAAA,MAC9E,OACiB;AACD,iBAAS,KAAK,MAAM,SAAS;AAAA,MAC7C;AAAA,IACA,OACa;AACD,eAAS,KAAK,MAAM,EAAE;AAAA,IAClC;AACQ,WAAO,OAAO,OAAO,CAAC,MAAM,MAAM,EAAE;AAAA,EAC5C;AAAA,EACI,MAAM,gBAAgB,OAEtB,YAAY,CAAA,GAAI,qBAAqB,IAAI;AAGrC,UAAM,aAAa,UAAU,SAAS,IAChC,YACA,CAAC,GAAG,MAAM,MAAM,MAAM,CAAC,EAAE,IAAI,OAAO,CAAA,EAAG;AAC7C,UAAM,EAAE,cAAc,IAAI,qBAAqB,aAAa,2BAA2B,MAAK,IAAM;AAClG,UAAM,YAAY,IAAI,MAAO;AAC7B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACtC,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,mBAAmB;AACvB,UAAI,YAAY;AAChB,UAAI,iBAAiB;AACrB,iBAAW,SAAS,MAAM,KAAK,UAAU,IAAI,GAAG;AAC5C,YAAI,cAAc;AAElB,cAAM,aAAa,KAAK,QAAQ,OAAO,iBAAiB,CAAC;AACzD,YAAI,cAAc,MAAM;AACpB,gBAAM,2BAA2B,KAAK,iBAAiB,MAAM,GAAG,UAAU;AAC1E,8BAAoB;AAAA,QACxC,OACqB;AACD,gBAAM,oBAAoB,iBAAkB,MAAM,KAAK,eAAe,SAAS;AAC/E,cAAI,oBAAoB,YAAY;AAChC,kBAAM,+BAA+B,KAAK,iBAAiB,MAAM,mBAAmB,UAAU;AAC9F,gCAAoB;AAAA,UAC5C,WAC6B,oBAAoB,YAAY;AACrC,kBAAM,+BAA+B,KAAK,iBAAiB,MAAM,YAAY,iBAAiB;AAC9F,gCAAoB;AAAA,UAC5C;AACoB,cAAI,0BAA0B;AAC1B,2BAAe;AAAA,UACvC;AAAA,QACA;AACgB,cAAM,gBAAgB,KAAK,iBAAiB,KAAK;AACjD,cAAM,MAAM,WAAW,CAAC,EAAE,OAAO,OAAO,WAAW,CAAC,EAAE,QAAQ,WACxD,EAAE,GAAG,WAAW,CAAC,EAAE,IAAG,IACtB,CAAE;AACR,YAAI,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,IAAI,mBAAmB;AAAA,QAC1B;AACD,cAAM,0BAA0B;AAAA,UAC5B,GAAG,WAAW,CAAC;AAAA,UACf;AAAA,QACH;AACD,uBAAe;AACf,kBAAU,KAAK,IAAI,SAAS;AAAA,UACxB;AAAA,UACA,UAAU;AAAA,QAC9B,CAAiB,CAAC;AACF,4BAAoB;AACpB,oBAAY;AACZ,yBAAiB;AAAA,MACjC;AAAA,IACA;AACQ,WAAO;AAAA,EACf;AAAA,EACI,iBAAiB,MAAM,OAAO,KAAK;AAC/B,UAAM,cAAc,KAAK,MAAM,OAAO,GAAG;AACzC,YAAQ,YAAY,MAAM,KAAK,KAAK,CAAE,GAAE;AAAA,EAChD;AAAA,EACI,MAAM,eAAe,WAAW,qBAAqB,IAAI;AACrD,UAAM,oBAAoB,UAAU,OAAO,CAAC,QAAQ,IAAI,gBAAgB,MAAS;AACjF,UAAM,QAAQ,kBAAkB,IAAI,CAAC,QAAQ,IAAI,WAAW;AAC5D,UAAM,YAAY,kBAAkB,IAAI,CAAC,QAAQ,IAAI,QAAQ;AAC7D,WAAO,KAAK,gBAAgB,OAAO,WAAW,kBAAkB;AAAA,EACxE;AAAA,EACI,SAAS,MAAM,WAAW;AACtB,UAAM,OAAO,KAAK,KAAK,SAAS,EAAE,KAAM;AACxC,WAAO,SAAS,KAAK,OAAO;AAAA,EACpC;AAAA,EACI,MAAM,YAAY,QAAQ,WAAW;AACjC,UAAM,OAAO,CAAE;AACf,UAAM,aAAa,CAAE;AACrB,QAAI,QAAQ;AACZ,eAAW,KAAK,QAAQ;AACpB,YAAM,OAAO,MAAM,KAAK,eAAe,CAAC;AACxC,UAAI,QAAQ,OAAO,WAAW,SAAS,UAAU,SAC7C,KAAK,WAAW;AAChB,YAAI,QAAQ,KAAK,WAAW;AACxB,kBAAQ,KAAK,2BAA2B,KAAK;AAAA,qCAC5B,KAAK,SAAS,EAAE;AAAA,QACrD;AACgB,YAAI,WAAW,SAAS,GAAG;AACvB,gBAAMC,OAAM,KAAK,SAAS,YAAY,SAAS;AAC/C,cAAIA,SAAQ,MAAM;AACd,iBAAK,KAAKA,IAAG;AAAA,UACrC;AAIoB,iBAAO,QAAQ,KAAK,gBACf,QAAQ,OAAO,WAAW,SAAS,UAAU,SAC1C,KAAK,aACL,QAAQ,GAAI;AAChB,qBAAS,MAAM,KAAK,eAAe,WAAW,CAAC,CAAC;AAChD,uBAAW,MAAO;AAAA,UAC1C;AAAA,QACA;AAAA,MACA;AACY,iBAAW,KAAK,CAAC;AACjB,eAAS;AAAA,IACrB;AACQ,UAAM,MAAM,KAAK,SAAS,YAAY,SAAS;AAC/C,QAAI,QAAQ,MAAM;AACd,WAAK,KAAK,GAAG;AAAA,IACzB;AACQ,WAAO;AAAA,EACf;AACA;AA2dO,MAAM,0BAA0B,aAAa;AAAA,EAChD,OAAO,UAAU;AACb,WAAO;AAAA,EACf;AAAA,EACI,YAAY,QAAQ;AAChB,UAAM,MAAM;AACZ,WAAO,eAAe,MAAM,gBAAgB;AAAA,MACxC,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,WAAO,eAAe,MAAM,kBAAkB;AAAA,MAC1C,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,WAAO,eAAe,MAAM,qBAAqB;AAAA,MAC7C,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,WAAO,eAAe,MAAM,aAAa;AAAA,MACrC,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACnB,CAAS;AACD,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,iBAAiB,QAAQ,kBAAkB,CAAE;AAClD,SAAK,oBAAoB,QAAQ,qBAAqB;AAAA,EAC9D;AAAA,EACI,MAAM,UAAU,MAAM;AAClB,QAAI,CAAC,KAAK,WAAW;AACjB,WAAK,YAAY,MAAM,YAAY,KAAK,YAAY;AAAA,IAChE;AACQ,UAAM,SAAS,CAAE;AACjB,UAAM,YAAY,KAAK,UAAU,OAAO,MAAM,KAAK,gBAAgB,KAAK,iBAAiB;AACzF,QAAI,YAAY;AAChB,WAAO,YAAY,UAAU,QAAQ;AACjC,UAAI,YAAY,GAAG;AACf,qBAAa,KAAK;AAAA,MAClC;AACY,YAAM,UAAU,KAAK,IAAI,YAAY,KAAK,WAAW,UAAU,MAAM;AACrE,YAAM,YAAY,UAAU,MAAM,WAAW,OAAO;AACpD,aAAO,KAAK,KAAK,UAAU,OAAO,SAAS,CAAC;AAC5C,kBAAY;AAAA,IACxB;AACQ,WAAO;AAAA,EACf;AACA;ACtrBA,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,SAAO,IAAI,WAAW;AAAA,IACpB,aAAa;AAAA,IACb;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,EAAA,CACZ;AACH;ACLA,MAAM,kBAAkB,OACtB,eAC4B;AAC5B,QAAM,EAAE,UAAA,IAAc,MAAM,OAAO,aAAa;AAE1C,QAAA,UAAU,MAAM,UAAU,OAAO;AAAA,IACrC,MAAM;AAAA,IACN,UAAU;AAAA,IACV,iBAAiB;AAAA,EAAA,CAClB;AAEG,MAAA;AAgBO,QAAA,gBAAT,SAAuB,KAAK;AAC1B,aAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAAA,IACzB;AAjBA,UAAMC,QAAO,MAAM,QAAQ,QAAQ,UAAU;AACvC,UAAA,iBAAiB,MAAMA,MAAK,cAAc;AAEhD,UAAM,0BAA+C,gBAAgB,YAAY,SAAS,MAAM,iBAAiB;AAAA,MAC/G,CAAC,YAAY;AACX,cAAM,kBAAkB,OAAO,QAAQ,MAAM,IAAI,OAAO,QAAQ,QAAQ;AACjE,eAAA;AAAA,UACL,MAAM,QAAQ,QAAQ;AAAA,UACtB,OAAO,OAAO,QAAQ,QAAQ;AAAA,UAC9B,KAAK,OAAO,QAAQ,MAAM;AAAA,UAC1B,UAAU;AAAA,QACZ;AAAA,MAAA;AAAA,IAEJ;AAMA,UAAM,iBAAiB,gBAAgB,YAAY,SAAS,MAAM,iBAAiB;AAAA,MACjF,CAAC,YAAY,QAAQ,QAAQ;AAAA,IAAA,EAC7B,KAAK,GAAG;AAEJ,UAAA,QAAQA,MAAK,WAAW;AACxB,UAAA,UAAUA,MAAK,WAAW;AAChC,YAAQ,IAAIA,MAAK,WAAW,UAAU,CAAC,EAAE,KAAK,cAAc;AAC5D,QAAI,eAAe;AACnB,UAAM,wBAAwBA,MAAK,YAAY,YAAY,CAAC,GAAG;AAC/D,QAAI,uBAAuB;AACzB,qBAAe,cAAc,qBAAqB;AAAA,IAAA,OAC7C;AACU,qBAAA;AAAA,IAAA;AAGV,WAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,WACO,OAAO;AACN,YAAA,MAAM,8BAA8B,KAAK;AAC3C,UAAA;AAAA,EAAA;AAEV;ACzDA,eAAe,kBAAkB,QAAkB,OAAmB;AACpE,QAAM,oBAAoB,eAAe;AAAA,IACvC;AAAA,EACF;AACM,QAAA,mBAAmB,kBAAkB,KAAK,KAAK;AAE/C,QAAA,kBAAkB,MAAM,QAAQ;AAAA,IACpC,OAAO,IAAI,OAAO,UAAU;AAC1B,YAAM,SAAS,MAAM,iBAAiB,OAAO,EAAE,OAAO;AACtD,aAAO,OAAO;AAAA,IACf,CAAA;AAAA,EACH;AAEO,SAAA,gBAAgB,KAAK,GAAG;AACjC;AAEA,eAAsB,2BAA4B,eAAuB;AACvE,QAAM,iBAAiB,MAAM,OAAO,OAAO,IAAI,uBAAuB;AAE9D,UAAA,IAAI,kBAAkB,cAAc;AAExC,MAAA,CAAC,eAAe,gBAAgB,CAAC,eAAe,SAAS,CAAC,eAAe,QAAQ,CAAC,eAAe,WAAW;AACxG,UAAA,IAAI,MAAM,iDAAiD;AAAA,EAAA;AAG7D,QAAA,YAAY,MAAM,gBAAgB;AAAA,IACtC,cAAc,eAAe;AAAA,IAC7B,OAAO,eAAe;AAAA,IACtB,MAAM,eAAe;AAAA,IACrB,WAAW,eAAe;AAAA,EAAA,CAC3B;AAEK,QAAA,WAAW,IAAI,kBAAkB;AAAA,IACrC,WAAW;AAAA,IACX,cAAc;AAAA,EAAA,CACf;AAED,QAAM,mBAAmB,MAAM,SAAS,gBAAgB,CAAC,aAAa,CAAC;AACvE,QAAM,aAAa,iBAAiB,IAAI,CAAA,UAAS,MAAM,WAAW;AAClE,QAAM,qBAAqB,MAAM,kBAAkB,YAAY,SAAS;AAEjE,SAAA;AACT;AAEA,MAAM,UAAU,CAAC,EAAE,QAAAH,eAAuC;AAAA,EACxD,MAAM,cAAc,YAAoB;AACtC,UAAM,iBAAiB;AACjB,UAAA,UAAU,eAAe,KAAK,UAAU;AAC9C,QAAI,CAAC,QAAS,QAAO,EAAE,OAAO,oBAAoB,MAAM,KAAK;AACvD,UAAA,iBAAiB,MAAM,gBAAgB,UAAU;AAChD,WAAA;AAAA,EACT;AAAA,EAEA,MAAM,eAAe,SAAS;AAE5B,WAAO,MAAMA,QAAO,UAAU,kCAAkC,EAAE,OAAO;AAAA,MACvE,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAAA,EAEA,MAAM,eAAe,SAAS;AACpB,YAAA,IAAI,mCAAmC,OAAO;AACtD,UAAM,iBAAmB,MAAMA,QAAO,UAAU,kCAAkC,EAAE,UAAU;AAAA,MAC5F,SAAS,EAAE,QAAQ;AAAA,IAAA,CACpB;AAED,YAAQ,IAAI,qBAAqB,gBAAgB,OAAO,OAAO;AAE3D,QAAA,CAAC,eAAuB,QAAA;AACrB,WAAA;AAAA,EACT;AAAA,EAEA,MAAM,gCAAgCC,aAAY;AACxC,YAAA,IAAI,yCAAyCA,WAAU;AACzD,UAAA,qBAAqB,MAAM,2BAA2BA,WAAU;AAC/D,WAAA;AAAA,EAAA;AAEX;AC1FA,MAAe,WAAA;AAAA,EACb;AACF;ACcA,MAAe,QAAA;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","x_google_ignoreList":[14]}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/register.ts","../../server/src/config/index.ts","../../server/src/content-types/transcript/index.ts","../../server/src/content-types/index.ts","../../server/src/utils/extract-youtube-id.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/index.ts","../../server/src/middlewares/index.ts","../../server/src/policies/index.ts","../../server/src/routes/content-api.ts","../../server/src/routes/admin.ts","../../server/src/routes/index.ts","../../node_modules/@langchain/textsplitters/dist/text_splitter.js","../../server/src/utils/openai.ts","../../server/src/utils/fetch-transcript.ts","../../server/src/services/service.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["import type { Core } from '@strapi/strapi';\n\nconst bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {\n // bootstrap phase\n};\n\nexport default bootstrap;\n","import type { Core } from '@strapi/strapi';\n\nconst destroy = ({ strapi }: { strapi: Core.Strapi }) => {\n // destroy phase\n};\n\nexport default destroy;\n","import type { Core } from '@strapi/strapi';\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n // register phase\n};\n\nexport default register;\n","export default {\n default: {},\n validator() {},\n};\n","import schema from './schema.json';\n\nexport default {\n schema,\n};","import transcript from './transcript';\n\nexport default {\n transcript,\n};\n\n\n","export function extractYouTubeID(urlOrID: string): string | null {\n // Regular expression for YouTube ID format\n const regExpID = /^[a-zA-Z0-9_-]{11}$/;\n\n // Check if the input is a YouTube ID\n if (regExpID.test(urlOrID)) {\n return urlOrID;\n }\n\n // Regular expression for standard YouTube links\n const regExpStandard = /youtube\\.com\\/watch\\?v=([a-zA-Z0-9_-]+)/;\n\n // Regular expression for YouTube Shorts links\n const regExpShorts = /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]+)/;\n\n // Check for standard YouTube link\n const matchStandard = urlOrID.match(regExpStandard);\n if (matchStandard) {\n return matchStandard[1];\n }\n\n // Check for YouTube Shorts link\n const matchShorts = urlOrID.match(regExpShorts);\n if (matchShorts) {\n return matchShorts[1];\n }\n\n // Return null if no match is found\n return null;\n}","import type { Core } from '@strapi/strapi';\nimport { extractYouTubeID } from '../utils/extract-youtube-id';\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(ctx) {\n const videoId = extractYouTubeID(ctx.params.videoId);\n\n if (!videoId) return (ctx.body = { error: 'Invalid YouTube URL or ID', data: null });\n\n console.log(\"Looking for transcript in database\");\n\n const found = await strapi\n .plugin('yt-transcript')\n .service('service')\n .findTranscript(videoId);\n\n if (found) {\n console.log(\"Transcript found.\");\n return (ctx.body = { data: found });\n }\n\n console.log(\"Transcript not found. Fetching new transcript.\");\n\n const transcriptData = await strapi\n .plugin('yt-transcript')\n .service('service')\n .getTranscript(videoId);\n\n console.log(\"New transcript fetched.\");\n\n const readableTranscript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .generateHumanReadableTranscript(transcriptData.fullTranscript);\n\n console.log(\"Human readable transcript generated.\");\n\n const payload = {\n title: transcriptData.title,\n transcript: transcriptData.transcript,\n videoId: transcriptData.videoId,\n thumbnailUrl: transcriptData.thumbnailUrl,\n fullTranscript: transcriptData.fullTranscript,\n transcriptWithTimeCodes: transcriptData.transcriptWithTimeCodes,\n readableTranscript: readableTranscript,\n };\n\n console.log(\"Saving new transcript to database.\");\n\n const transcript = await strapi\n .plugin('yt-transcript')\n .service('service')\n .saveTranscript(payload);\n\n ctx.body = { data: transcript };\n },\n});\n\nexport default controller;\n","import controller from './controller';\n\nexport default {\n controller,\n};\n","export default {};\n","export default {};\n","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","export default [\n {\n method: 'GET',\n path: '/yt-transcript/:videoId',\n handler: 'controller.getTranscript',\n config: { \n policies: [], \n }, \n },\n];","\"use strict\";\n\nimport contentApi from \"./content-api\";\nimport admin from \"./admin\";\n\nexport default {\n \"content-api\": {\n type: \"content-api\",\n routes: [...contentApi],\n },\n admin: {\n type: \"admin\",\n routes: [...admin],\n },\n};","import { Document, BaseDocumentTransformer } from \"@langchain/core/documents\";\nimport { getEncoding } from \"@langchain/core/utils/tiktoken\";\nexport class TextSplitter extends BaseDocumentTransformer {\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"lc_namespace\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"langchain\", \"document_transformers\", \"text_splitters\"]\n });\n Object.defineProperty(this, \"chunkSize\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1000\n });\n Object.defineProperty(this, \"chunkOverlap\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 200\n });\n Object.defineProperty(this, \"keepSeparator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"lengthFunction\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.chunkSize = fields?.chunkSize ?? this.chunkSize;\n this.chunkOverlap = fields?.chunkOverlap ?? this.chunkOverlap;\n this.keepSeparator = fields?.keepSeparator ?? this.keepSeparator;\n this.lengthFunction =\n fields?.lengthFunction ?? ((text) => text.length);\n if (this.chunkOverlap >= this.chunkSize) {\n throw new Error(\"Cannot have chunkOverlap >= chunkSize\");\n }\n }\n async transformDocuments(documents, chunkHeaderOptions = {}) {\n return this.splitDocuments(documents, chunkHeaderOptions);\n }\n splitOnSeparator(text, separator) {\n let splits;\n if (separator) {\n if (this.keepSeparator) {\n const regexEscapedSeparator = separator.replace(/[/\\-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n splits = text.split(new RegExp(`(?=${regexEscapedSeparator})`));\n }\n else {\n splits = text.split(separator);\n }\n }\n else {\n splits = text.split(\"\");\n }\n return splits.filter((s) => s !== \"\");\n }\n async createDocuments(texts, \n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n metadatas = [], chunkHeaderOptions = {}) {\n // if no metadata is provided, we create an empty one for each text\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const _metadatas = metadatas.length > 0\n ? metadatas\n : [...Array(texts.length)].map(() => ({}));\n const { chunkHeader = \"\", chunkOverlapHeader = \"(cont'd) \", appendChunkOverlapHeader = false, } = chunkHeaderOptions;\n const documents = new Array();\n for (let i = 0; i < texts.length; i += 1) {\n const text = texts[i];\n let lineCounterIndex = 1;\n let prevChunk = null;\n let indexPrevChunk = -1;\n for (const chunk of await this.splitText(text)) {\n let pageContent = chunkHeader;\n // we need to count the \\n that are in the text before getting removed by the splitting\n const indexChunk = text.indexOf(chunk, indexPrevChunk + 1);\n if (prevChunk === null) {\n const newLinesBeforeFirstChunk = this.numberOfNewLines(text, 0, indexChunk);\n lineCounterIndex += newLinesBeforeFirstChunk;\n }\n else {\n const indexEndPrevChunk = indexPrevChunk + (await this.lengthFunction(prevChunk));\n if (indexEndPrevChunk < indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexEndPrevChunk, indexChunk);\n lineCounterIndex += numberOfIntermediateNewLines;\n }\n else if (indexEndPrevChunk > indexChunk) {\n const numberOfIntermediateNewLines = this.numberOfNewLines(text, indexChunk, indexEndPrevChunk);\n lineCounterIndex -= numberOfIntermediateNewLines;\n }\n if (appendChunkOverlapHeader) {\n pageContent += chunkOverlapHeader;\n }\n }\n const newLinesCount = this.numberOfNewLines(chunk);\n const loc = _metadatas[i].loc && typeof _metadatas[i].loc === \"object\"\n ? { ..._metadatas[i].loc }\n : {};\n loc.lines = {\n from: lineCounterIndex,\n to: lineCounterIndex + newLinesCount,\n };\n const metadataWithLinesNumber = {\n ..._metadatas[i],\n loc,\n };\n pageContent += chunk;\n documents.push(new Document({\n pageContent,\n metadata: metadataWithLinesNumber,\n }));\n lineCounterIndex += newLinesCount;\n prevChunk = chunk;\n indexPrevChunk = indexChunk;\n }\n }\n return documents;\n }\n numberOfNewLines(text, start, end) {\n const textSection = text.slice(start, end);\n return (textSection.match(/\\n/g) || []).length;\n }\n async splitDocuments(documents, chunkHeaderOptions = {}) {\n const selectedDocuments = documents.filter((doc) => doc.pageContent !== undefined);\n const texts = selectedDocuments.map((doc) => doc.pageContent);\n const metadatas = selectedDocuments.map((doc) => doc.metadata);\n return this.createDocuments(texts, metadatas, chunkHeaderOptions);\n }\n joinDocs(docs, separator) {\n const text = docs.join(separator).trim();\n return text === \"\" ? null : text;\n }\n async mergeSplits(splits, separator) {\n const docs = [];\n const currentDoc = [];\n let total = 0;\n for (const d of splits) {\n const _len = await this.lengthFunction(d);\n if (total + _len + currentDoc.length * separator.length >\n this.chunkSize) {\n if (total > this.chunkSize) {\n console.warn(`Created a chunk of size ${total}, +\nwhich is longer than the specified ${this.chunkSize}`);\n }\n if (currentDoc.length > 0) {\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n // Keep on popping if:\n // - we have a larger chunk than in the chunk overlap\n // - or if we still have any chunks and the length is long\n while (total > this.chunkOverlap ||\n (total + _len + currentDoc.length * separator.length >\n this.chunkSize &&\n total > 0)) {\n total -= await this.lengthFunction(currentDoc[0]);\n currentDoc.shift();\n }\n }\n }\n currentDoc.push(d);\n total += _len;\n }\n const doc = this.joinDocs(currentDoc, separator);\n if (doc !== null) {\n docs.push(doc);\n }\n return docs;\n }\n}\nexport class CharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"CharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separator\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"\\n\\n\"\n });\n this.separator = fields?.separator ?? this.separator;\n }\n async splitText(text) {\n // First we naively split the large input into a bunch of smaller ones.\n const splits = this.splitOnSeparator(text, this.separator);\n return this.mergeSplits(splits, this.keepSeparator ? \"\" : this.separator);\n }\n}\nexport const SupportedTextSplitterLanguages = [\n \"cpp\",\n \"go\",\n \"java\",\n \"js\",\n \"php\",\n \"proto\",\n \"python\",\n \"rst\",\n \"ruby\",\n \"rust\",\n \"scala\",\n \"swift\",\n \"markdown\",\n \"latex\",\n \"html\",\n \"sol\",\n];\nexport class RecursiveCharacterTextSplitter extends TextSplitter {\n static lc_name() {\n return \"RecursiveCharacterTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"separators\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"\\n\\n\", \"\\n\", \" \", \"\"]\n });\n this.separators = fields?.separators ?? this.separators;\n this.keepSeparator = fields?.keepSeparator ?? true;\n }\n async _splitText(text, separators) {\n const finalChunks = [];\n // Get appropriate separator to use\n let separator = separators[separators.length - 1];\n let newSeparators;\n for (let i = 0; i < separators.length; i += 1) {\n const s = separators[i];\n if (s === \"\") {\n separator = s;\n break;\n }\n if (text.includes(s)) {\n separator = s;\n newSeparators = separators.slice(i + 1);\n break;\n }\n }\n // Now that we have the separator, split the text\n const splits = this.splitOnSeparator(text, separator);\n // Now go merging things, recursively splitting longer texts.\n let goodSplits = [];\n const _separator = this.keepSeparator ? \"\" : separator;\n for (const s of splits) {\n if ((await this.lengthFunction(s)) < this.chunkSize) {\n goodSplits.push(s);\n }\n else {\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n goodSplits = [];\n }\n if (!newSeparators) {\n finalChunks.push(s);\n }\n else {\n const otherInfo = await this._splitText(s, newSeparators);\n finalChunks.push(...otherInfo);\n }\n }\n }\n if (goodSplits.length) {\n const mergedText = await this.mergeSplits(goodSplits, _separator);\n finalChunks.push(...mergedText);\n }\n return finalChunks;\n }\n async splitText(text) {\n return this._splitText(text, this.separators);\n }\n static fromLanguage(language, options) {\n return new RecursiveCharacterTextSplitter({\n ...options,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(language),\n });\n }\n static getSeparatorsForLanguage(language) {\n if (language === \"cpp\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along function definitions\n \"\\nvoid \",\n \"\\nint \",\n \"\\nfloat \",\n \"\\ndouble \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"go\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n \"\\nvar \",\n \"\\nconst \",\n \"\\ntype \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"java\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n // Split along method definitions\n \"\\npublic \",\n \"\\nprotected \",\n \"\\nprivate \",\n \"\\nstatic \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"js\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n \"\\nconst \",\n \"\\nlet \",\n \"\\nvar \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nswitch \",\n \"\\ncase \",\n \"\\ndefault \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"php\") {\n return [\n // Split along function definitions\n \"\\nfunction \",\n // Split along class definitions\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nforeach \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"proto\") {\n return [\n // Split along message definitions\n \"\\nmessage \",\n // Split along service definitions\n \"\\nservice \",\n // Split along enum definitions\n \"\\nenum \",\n // Split along option definitions\n \"\\noption \",\n // Split along import statements\n \"\\nimport \",\n // Split along syntax declarations\n \"\\nsyntax \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"python\") {\n return [\n // First, try to split along class definitions\n \"\\nclass \",\n \"\\ndef \",\n \"\\n\\tdef \",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rst\") {\n return [\n // Split along section titles\n \"\\n===\\n\",\n \"\\n---\\n\",\n \"\\n***\\n\",\n // Split along directive markers\n \"\\n.. \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"ruby\") {\n return [\n // Split along method definitions\n \"\\ndef \",\n \"\\nclass \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nunless \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\ndo \",\n \"\\nbegin \",\n \"\\nrescue \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"rust\") {\n return [\n // Split along function definitions\n \"\\nfn \",\n \"\\nconst \",\n \"\\nlet \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nwhile \",\n \"\\nfor \",\n \"\\nloop \",\n \"\\nmatch \",\n \"\\nconst \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"scala\") {\n return [\n // Split along class definitions\n \"\\nclass \",\n \"\\nobject \",\n // Split along method definitions\n \"\\ndef \",\n \"\\nval \",\n \"\\nvar \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\nmatch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"swift\") {\n return [\n // Split along function definitions\n \"\\nfunc \",\n // Split along class definitions\n \"\\nclass \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo \",\n \"\\nswitch \",\n \"\\ncase \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"markdown\") {\n return [\n // First, try to split along Markdown headings (starting with level 2)\n \"\\n## \",\n \"\\n### \",\n \"\\n#### \",\n \"\\n##### \",\n \"\\n###### \",\n // Note the alternative syntax for headings (below) is not handled here\n // Heading level 2\n // ---------------\n // End of code block\n \"```\\n\\n\",\n // Horizontal lines\n \"\\n\\n***\\n\\n\",\n \"\\n\\n---\\n\\n\",\n \"\\n\\n___\\n\\n\",\n // Note that this splitter doesn't handle horizontal lines defined\n // by *three or more* of ***, ---, or ___, but this is not handled\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"latex\") {\n return [\n // First, try to split along Latex sections\n \"\\n\\\\chapter{\",\n \"\\n\\\\section{\",\n \"\\n\\\\subsection{\",\n \"\\n\\\\subsubsection{\",\n // Now split by environments\n \"\\n\\\\begin{enumerate}\",\n \"\\n\\\\begin{itemize}\",\n \"\\n\\\\begin{description}\",\n \"\\n\\\\begin{list}\",\n \"\\n\\\\begin{quote}\",\n \"\\n\\\\begin{quotation}\",\n \"\\n\\\\begin{verse}\",\n \"\\n\\\\begin{verbatim}\",\n // Now split by math environments\n \"\\n\\\\begin{align}\",\n \"$$\",\n \"$\",\n // Now split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else if (language === \"html\") {\n return [\n // First, try to split along HTML tags\n \"<body>\",\n \"<div>\",\n \"<p>\",\n \"<br>\",\n \"<li>\",\n \"<h1>\",\n \"<h2>\",\n \"<h3>\",\n \"<h4>\",\n \"<h5>\",\n \"<h6>\",\n \"<span>\",\n \"<table>\",\n \"<tr>\",\n \"<td>\",\n \"<th>\",\n \"<ul>\",\n \"<ol>\",\n \"<header>\",\n \"<footer>\",\n \"<nav>\",\n // Head\n \"<head>\",\n \"<style>\",\n \"<script>\",\n \"<meta>\",\n \"<title>\",\n // Normal type of lines\n \" \",\n \"\",\n ];\n }\n else if (language === \"sol\") {\n return [\n // Split along compiler informations definitions\n \"\\npragma \",\n \"\\nusing \",\n // Split along contract definitions\n \"\\ncontract \",\n \"\\ninterface \",\n \"\\nlibrary \",\n // Split along method definitions\n \"\\nconstructor \",\n \"\\ntype \",\n \"\\nfunction \",\n \"\\nevent \",\n \"\\nmodifier \",\n \"\\nerror \",\n \"\\nstruct \",\n \"\\nenum \",\n // Split along control flow statements\n \"\\nif \",\n \"\\nfor \",\n \"\\nwhile \",\n \"\\ndo while \",\n \"\\nassembly \",\n // Split by the normal type of lines\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n ];\n }\n else {\n throw new Error(`Language ${language} is not supported.`);\n }\n }\n}\n/**\n * Implementation of splitter which looks at tokens.\n */\nexport class TokenTextSplitter extends TextSplitter {\n static lc_name() {\n return \"TokenTextSplitter\";\n }\n constructor(fields) {\n super(fields);\n Object.defineProperty(this, \"encodingName\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"allowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"disallowedSpecial\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tokenizer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.encodingName = fields?.encodingName ?? \"gpt2\";\n this.allowedSpecial = fields?.allowedSpecial ?? [];\n this.disallowedSpecial = fields?.disallowedSpecial ?? \"all\";\n }\n async splitText(text) {\n if (!this.tokenizer) {\n this.tokenizer = await getEncoding(this.encodingName);\n }\n const splits = [];\n const input_ids = this.tokenizer.encode(text, this.allowedSpecial, this.disallowedSpecial);\n let start_idx = 0;\n while (start_idx < input_ids.length) {\n if (start_idx > 0) {\n start_idx -= this.chunkOverlap;\n }\n const end_idx = Math.min(start_idx + this.chunkSize, input_ids.length);\n const chunk_ids = input_ids.slice(start_idx, end_idx);\n splits.push(this.tokenizer.decode(chunk_ids));\n start_idx = end_idx;\n }\n return splits;\n }\n}\nexport class MarkdownTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"markdown\"),\n });\n }\n}\nexport class LatexTextSplitter extends RecursiveCharacterTextSplitter {\n constructor(fields) {\n super({\n ...fields,\n separators: RecursiveCharacterTextSplitter.getSeparatorsForLanguage(\"latex\"),\n });\n }\n}\n","import { ChatOpenAI } from \"@langchain/openai\";\n\ninterface InitializeModelProps {\n openAIApiKey: string;\n model: string;\n temp: number;\n maxTokens?: number;\n}\n\nexport async function initializeModel({\n openAIApiKey,\n model,\n temp,\n}: InitializeModelProps) {\n return new ChatOpenAI({\n temperature: temp,\n openAIApiKey: openAIApiKey,\n modelName: model,\n maxTokens: 1000,\n });\n}","export interface TranscriptSegment {\n text: string;\n start: number;\n end: number;\n duration: number;\n}\n\nexport interface TranscriptData {\n title: string;\n videoId: string;\n thumbnailUrl: string;\n fullTranscript: string;\n transcriptWithTimeCodes: TranscriptSegment[];\n}\n\nconst fetchTranscript = async (\n identifier: string\n): Promise<TranscriptData> => {\n console.log(\"Fetching Transcript - Calling fetchTranscript Utils\");\n const { Innertube } = await import('youtubei.js');\n\n console.log(\"Creating YouTube instance\");\n\n const youtube = await Innertube.create({\n lang: 'en',\n location: 'US',\n retrieve_player: false,\n });\n\n try {\n const info = await youtube.getInfo(identifier);\n const transcriptData = await info.getTranscript();\n\n console.log(\"Transcript data fetched\");\n\n const transcriptWithTimeCodes: TranscriptSegment[] = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => {\n const segmentDuration = Number(segment.end_ms) - Number(segment.start_ms);\n return {\n text: segment.snippet.text,\n start: Number(segment.start_ms),\n end: Number(segment.end_ms),\n duration: segmentDuration,\n };\n }\n );\n\n console.log(\"Transcript with time codes generated\");\n\n function cleanImageUrl(url) {\n return url.split('?')[0];\n }\n\n console.log(\"Cleaning thumbnail URL\");\n\n const fullTranscript = transcriptData?.transcript?.content?.body?.initial_segments.map(\n (segment) => segment.snippet.text\n ).join(' ');\n\n console.log(\"Full transcript generated\");\n\n const title = info.basic_info.title;\n const videoId = info.basic_info.id;\n const thumbnailUrl = info.basic_info.thumbnail[0].url;\n const thumbnailUrlCleaned = cleanImageUrl(thumbnailUrl);\n\n console.log(\"Returning transcript data\");\n\n return {\n title,\n videoId,\n thumbnailUrl: thumbnailUrlCleaned || \"\",\n fullTranscript,\n transcriptWithTimeCodes,\n };\n } catch (error) {\n console.error('Error fetching transcript:', error);\n throw error;\n }\n};\n\nexport default fetchTranscript;","import type { Core } from '@strapi/strapi';\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { TokenTextSplitter } from \"@langchain/textsplitters\";\nimport { PromptTemplate } from \"@langchain/core/prompts\";\n\nimport { initializeModel } from \"../utils/openai\";\nimport fetchTranscript from '../utils/fetch-transcript';\n\ninterface YTTranscriptConfig {\n openAIApiKey: string;\n model?: string;\n temp?: number;\n maxTokens?: number;\n}\n\nasync function processTextChunks(chunks: string[], model: ChatOpenAI) {\n const punctuationPrompt = PromptTemplate.fromTemplate(\n \"Add proper punctuation and capitalization to the following text chunk:\\n\\n{chunk}\"\n );\n const punctuationChain = punctuationPrompt.pipe(model);\n\n const processedChunks = await Promise.all(\n chunks.map(async (chunk) => {\n const result = await punctuationChain.invoke({ chunk });\n return result.content as string;\n })\n );\n\n return processedChunks.join(\" \");\n}\n\nexport async function generateModifiedTranscript (rawTranscript: string) {\n const pluginSettings = await strapi.config.get('plugin::yt-transcript') as YTTranscriptConfig; \n \n if (!pluginSettings.openAIApiKey || !pluginSettings.model || !pluginSettings.temp || !pluginSettings.maxTokens) {\n throw new Error('Missing required configuration for YTTranscript');\n }\n\n const chatModel = await initializeModel({\n openAIApiKey: pluginSettings.openAIApiKey,\n model: pluginSettings.model,\n temp: pluginSettings.temp,\n maxTokens: pluginSettings.maxTokens,\n });\n\n const splitter = new TokenTextSplitter({\n chunkSize: 1000,\n chunkOverlap: 200,\n });\n\n const transcriptChunks = await splitter.createDocuments([rawTranscript]);\n const chunkTexts = transcriptChunks.map(chunk => chunk.pageContent);\n const modifiedTranscript = await processTextChunks(chunkTexts, chatModel);\n return modifiedTranscript;\n}\n\nconst service = ({ strapi }: { strapi: Core.Strapi }) => ({\n async getTranscript(identifier: string) {\n console.log(\"Fetching Transcript - Calling fetchTranscript Service\");\n const youtubeIdRegex = /^[a-zA-Z0-9_-]{11}$/;\n const isValid = youtubeIdRegex.test(identifier);\n if (!isValid) return { error: 'Invalid video ID', data: null };\n const transcriptData = await fetchTranscript(identifier);\n return transcriptData;\n },\n\n async saveTranscript(payload) {\n // console.log('Saving transcript:', payload);\n return await strapi.documents('plugin::yt-transcript.transcript').create({\n data: payload,\n });\n },\n\n async findTranscript(videoId) {\n console.log('Finding transcript for videoId:', videoId);\n const transcriptData = await strapi.documents('plugin::yt-transcript.transcript').findFirst({\n filters: { videoId },\n });\n\n console.log('Transcript found:', transcriptData?.title, 'found');\n\n if (!transcriptData) return null;\n return transcriptData;\n },\n\n async generateHumanReadableTranscript(transcript) {\n console.log('Generating human readable transcript:');\n const modifiedTranscript = await generateModifiedTranscript(transcript);\n return modifiedTranscript;\n },\n});\n\nexport default service;\n","import service from './service';\n\nexport default {\n service,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport destroy from './destroy';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy,\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["strapi","transcript","doc","info"],"mappings":"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","x_google_ignoreList":[14]}
|
package/package.json
CHANGED