UNPKG

peertube-plugin-transposer-connector

Version:

Transposer connector is a PeerTube language tool plugin to transcribe and translate with Whisper

361 lines (310 loc) 12 kB
const { v4: uuidv4 } = require("uuid"); class LanguageMetadataDAO { constructor(peertubeHelpers) { this.peertubeHelpers = peertubeHelpers; this.initializeDatabase(); } async initializeDatabase() { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Initializing database table fairkom_language_metadata` ); const createTableQuery = ` CREATE TABLE IF NOT EXISTS fairkom_language_metadata ( id VARCHAR(36) PRIMARY KEY, fk_video_id INTEGER REFERENCES video(id) ON DELETE CASCADE, language TEXT, attribute TEXT, value TEXT, type TEXT, created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, modified_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; try { const result = await this.peertubeHelpers.database.query( createTableQuery ); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Successfully created/verified fairkom_language_metadata table` ); } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to create fairkom_language_metadata table: ${error.message}` ); } } // Helper function to escape SQL strings escapeSQL(value) { if (value === null || value === undefined) { return "NULL"; } if (typeof value === "number" || typeof value === "boolean") { return value.toString(); } // For strings, replace single quotes with two single quotes (SQL standard) return value.toString().replace(/'/g, "''"); } async formatResult(result) { if (!result || result.length === 0) { return {}; } const groupedAttributes = {}; result.forEach((item) => { const { attribute, language, value } = item; if (!groupedAttributes[attribute]) { groupedAttributes[attribute] = {}; } groupedAttributes[attribute][language] = value; }); return groupedAttributes; } async addLanguagedMetadata(fkVideoId, metadata) { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Starting metadata addition process for video ${fkVideoId}` ); if (!metadata) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: No metadata provided for video ${fkVideoId}` ); throw new Error("Metadata is missing."); } try { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Deleting existing metadata for video ${fkVideoId}` ); const deleteQuery = ` DELETE FROM fairkom_language_metadata WHERE fk_video_id = '${this.escapeSQL(fkVideoId)}' `; await this.peertubeHelpers.database.query(deleteQuery); const insertQueries = []; let totalLanguages = Object.keys(metadata).length; let totalAttributes = 0; for (const language in metadata) { const attributes = metadata[language]; for (const attribute in attributes) { const value = attributes[attribute]; if (value !== null && value !== undefined) { var metadataId = uuidv4(); var type = attribute == "tags" ? "array" : "string"; insertQueries.push([ metadataId, fkVideoId, language, attribute, value, type, ]); totalAttributes++; } } } if (insertQueries.length === 0) { this.peertubeHelpers.logger.warn( `[transposer-connector] LanguageMetadataDAO: No valid metadata entries found to insert for video ${fkVideoId}` ); throw new Error("No valid metadata found to insert."); } this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Preparing to insert ${insertQueries.length} metadata entries for video ${fkVideoId} (${totalLanguages} languages, ${totalAttributes} total attributes)` ); const query = ` INSERT INTO fairkom_language_metadata (id, fk_video_id, language, attribute, value, type) VALUES ${insertQueries .map((queryValues) => { // Properly escape special characters, especially single quotes const escapedValues = queryValues.map((value) => this.escapeSQL(value) ); return `('${escapedValues.join("','")}')`; }) .join(",")} `; this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Executing metadata insert query for video ${fkVideoId}` ); const result = await this.peertubeHelpers.database.query(query); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Successfully inserted ${insertQueries.length} metadata entries for video ${fkVideoId}` ); return true; } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to add metadata for video ${fkVideoId}: ${error.message}` ); throw error; } } async formatResult(result) { if (!result || result.length === 0) { return {}; } const groupedByLanguage = {}; result.forEach((item) => { const { attribute, language, value } = item; if (!groupedByLanguage[language]) { groupedByLanguage[language] = {}; } groupedByLanguage[language][attribute] = value; }); return groupedByLanguage; } async updateLanguageMetadata(fkVideoId, language, attribute, value) { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Updating metadata for video ${fkVideoId}, language ${language}, attribute ${attribute}` ); const escapedValue = this.escapeSQL(value); const escapedFkVideoId = this.escapeSQL(fkVideoId); const escapedLanguage = this.escapeSQL(language); const escapedAttribute = this.escapeSQL(attribute); const updateQuery = ` UPDATE fairkom_language_metadata SET value = '${escapedValue}', modified_date = CURRENT_TIMESTAMP WHERE fk_video_id = '${escapedFkVideoId}' AND language = '${escapedLanguage}' AND attribute = '${escapedAttribute}' `; try { const result = await this.peertubeHelpers.database.query(updateQuery); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Successfully updated metadata for video ${fkVideoId}, language ${language}, attribute ${attribute}, rows affected: ${result.rowCount}` ); return result.rowCount; // Use rowCount for PostgreSQL } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to update metadata for video ${fkVideoId}, language ${language}, attribute ${attribute}: ${error.message}` ); throw error; } } async findLanguageMetadata(fkVideoId, language, attribute) { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Finding specific metadata for video ${fkVideoId}, language ${language}, attribute ${attribute}` ); const escapedFkVideoId = this.escapeSQL(fkVideoId); const escapedLanguage = this.escapeSQL(language); const escapedAttribute = this.escapeSQL(attribute); const findQuery = ` SELECT * FROM fairkom_language_metadata WHERE fk_video_id = ${escapedFkVideoId} AND language = '${escapedLanguage}' AND attribute = '${escapedAttribute}'; `; try { const result = await this.peertubeHelpers.database.query(findQuery); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Found ${ result[0] ? 1 : 0 } metadata entry for video ${fkVideoId}, language ${language}, attribute ${attribute}` ); return result[0]; } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to find metadata for video ${fkVideoId}, language ${language}, attribute ${attribute}: ${error.message}` ); throw error; } } async findLanguageMetadataById(fkVideoId) { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Finding all metadata for video ${fkVideoId}` ); const escapedFkVideoId = this.escapeSQL(fkVideoId); const findlanguageQuery = ` SELECT attribute, "language", MAX(value) AS value FROM fairkom_language_metadata WHERE fk_video_id = '${escapedFkVideoId}' GROUP BY attribute, "language" ORDER BY attribute; `; try { const result = await this.peertubeHelpers.database.query( findlanguageQuery ); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Found ${ result[0] ? result[0].length : 0 } metadata entries for video ${fkVideoId}` ); const formatedResult = await this.formatResult(result[0]); return formatedResult; } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to find metadata for video ${fkVideoId}: ${error.message}` ); throw error; } } async findLanguageMetadataByIdAndLanguages(fkVideoId, languages) { const languageFilter = languages && languages.length > 0 ? ` (filtered by languages: ${languages.join(", ")})` : " (all languages)"; this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Finding metadata for video ${fkVideoId}${languageFilter}` ); const escapedFkVideoId = this.escapeSQL(fkVideoId); let languageFilterSQL = ""; if (languages && languages.length > 0) { const escapedLanguages = languages.map((lang) => this.escapeSQL(lang)); languageFilterSQL = `AND "language" IN ('${escapedLanguages.join( "','" )}')`; } const findlanguageQuery = ` SELECT attribute, "language", MAX(value) AS value FROM fairkom_language_metadata WHERE fk_video_id = '${escapedFkVideoId}' ${languageFilterSQL} GROUP BY attribute, "language" ORDER BY attribute; `; try { const result = await this.peertubeHelpers.database.query( findlanguageQuery ); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Found ${ result[0] ? result[0].length : 0 } metadata entries for video ${fkVideoId}${languageFilter}` ); return result[0]; } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to find metadata for video ${fkVideoId}${languageFilter}: ${error.message}` ); throw error; } } async findLanguageMetadataByUuid(fkVideoUuid) { this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Finding metadata by UUID ${fkVideoUuid}` ); const escapedFkVideoUuid = this.escapeSQL(fkVideoUuid); const findlanguageQuery = ` SELECT * FROM fairkom_language_metadata WHERE video_uuid = '${escapedFkVideoUuid}' ORDER BY created_date DESC; `; try { const result = await this.peertubeHelpers.database.query( findlanguageQuery ); this.peertubeHelpers.logger.info( `[transposer-connector] LanguageMetadataDAO: Found ${ result[0] ? result[0].length : 0 } metadata entries for UUID ${fkVideoUuid}` ); return result[0]; } catch (error) { this.peertubeHelpers.logger.error( `[transposer-connector] LanguageMetadataDAO: Failed to find metadata by UUID ${fkVideoUuid}: ${error.message}` ); throw error; } } } module.exports = { LanguageMetadataDAO, };