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
JavaScript
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,
};