peertube-plugin-transposer-connector
Version:
Transposer connector is a PeerTube language tool plugin to transcribe and translate with Whisper
299 lines (262 loc) • 10.7 kB
JavaScript
const initTransposerController = (router, peertubeHelpers, settingsManager) => {
router.post("/transposer/translate/:videoId", async (req, res) => {
try {
const { videoId } = req.params;
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Received transcription request for video ${videoId}`
);
if (!videoId || isNaN(videoId)) {
peertubeHelpers.logger.warn(
`[transposer-connector] TransposerController: Invalid videoId provided: ${videoId}`
);
return res
.status(400)
.json({ success: false, message: "Invalid videoId provided" });
}
// Extract auth header from the request
const authHeader = req.headers.authorization;
const hasAuth = !!authHeader;
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Processing transcription request for video ${videoId}, auth header present: ${hasAuth}`
);
const translationResult = await triggerTransposer(
videoId,
peertubeHelpers,
settingsManager,
authHeader
);
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Transcription request completed for video ${videoId}, result: ${translationResult}`
);
res.json({ success: translationResult });
} catch (error) {
const videoId = req.params.videoId;
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: Exception in transcription endpoint for video ${videoId}: ${error.message}`
);
res.status(500).send("Internal Server Error");
}
});
async function triggerTransposer(
videoId,
peertubeHelpers,
settingsManager,
authHeader
) {
try {
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Starting transposer trigger process for video ${videoId}`
);
var webserverUrl = peertubeHelpers.config.getWebserverUrl();
const settings = await settingsManager.getSettings([
"trigger-transcription-and-translation",
]);
if (!webserverUrl) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: Missing webserver URL configuration`
);
return false;
}
if (!settings["trigger-transcription-and-translation"]) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: Missing trigger-transcription-and-translation setting in plugin configuration`
);
return false;
}
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Configuration validated for video ${videoId}, webserverUrl: ${webserverUrl}, triggerUrl: ${settings["trigger-transcription-and-translation"]}`
);
// Get video details to extract the language using auth header
let video;
try {
const headers = {
Accept: "application/json",
};
// Add auth header if available
if (authHeader) {
headers["Authorization"] = authHeader;
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Using authentication header for video ${videoId} API request`
);
}
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Fetching video metadata from PeerTube API for video ${videoId}`
);
const response = await fetch(
`${webserverUrl}/api/v1/videos/${videoId}`,
{
headers: headers,
}
);
if (!response.ok) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: PeerTube API returned error ${response.status} when fetching video ${videoId} metadata`
);
throw new Error(`Failed to fetch video info: ${response.status}`);
}
video = await response.json();
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Successfully fetched video metadata for video ${videoId}, name: "${
video.name
}", language: ${video.language?.id || "not set"}`
);
} catch (error) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: Exception while fetching video ${videoId} metadata: ${error.message}`
);
return false;
}
const language = video.language?.id;
var fileDownloadUrl = video.files?.[0]?.fileDownloadUrl;
if (!fileDownloadUrl) {
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: No direct fileDownloadUrl found for video ${videoId}, falling back to getVideoFiles() method`
);
const videoFiles = await getVideoFiles(videoId, peertubeHelpers);
if (videoFiles.length === 0) {
peertubeHelpers.logger.warn(
`[transposer-connector] TransposerController: No video files found for video ${videoId}, cannot proceed with transcription`
);
return false;
}
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Found ${videoFiles.length} video files for video ${videoId}, using smallest file (${videoFiles[0].size} bytes)`
);
const { url } = videoFiles[0];
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Using getVideoFiles() method, selected file URL for video ${videoId}`
);
fileDownloadUrl = url;
} else {
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Using direct fileDownloadUrl from video metadata for video ${videoId}`
);
}
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Video ${videoId} file URL obtained, triggering external transcription service`
);
const triggerUrl = settings["trigger-transcription-and-translation"];
await triggerTranscriptionAndTranslation(
videoId,
fileDownloadUrl,
language,
triggerUrl,
webserverUrl,
peertubeHelpers
);
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: External transcription service triggered successfully for video ${videoId}`
);
return true;
} catch (error) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: Failed to trigger transcription and translation for video ${videoId}: ${error.message}`
);
return false;
}
}
async function getVideoFiles(id, peertubeHelpers) {
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Fetching video files from PeerTube for video ${id}`
);
const videoFilesData = await peertubeHelpers.videos.getFiles(id);
if (!videoFilesData) {
peertubeHelpers.logger.warn(
`[transposer-connector] TransposerController: No video files data returned from PeerTube for video ${id}`
);
return [];
}
const webVideoFiles = videoFilesData.webVideo?.videoFiles || [];
const hlsVideoFiles = videoFilesData.hls?.videoFiles || [];
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Video ${id} has ${webVideoFiles.length} web video files and ${hlsVideoFiles.length} HLS video files`
);
if (webVideoFiles.length === 0 && hlsVideoFiles.length === 0) {
peertubeHelpers.logger.warn(
`[transposer-connector] TransposerController: No video files available for video ${id}`
);
return [];
}
const combinedVideoFiles = webVideoFiles.concat(hlsVideoFiles);
const sortedVideoFiles = combinedVideoFiles.sort(
({ size: sizeA }, { size: sizeB }) => sizeA - sizeB
);
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Video ${id} files sorted by size, total: ${
sortedVideoFiles.length
} files, smallest: ${sortedVideoFiles[0]?.size} bytes, largest: ${
sortedVideoFiles[sortedVideoFiles.length - 1]?.size
} bytes`
);
return sortedVideoFiles;
}
function triggerTranscriptionAndTranslation(
videoId,
downloadUrl,
language,
triggerUrl,
webserverUrl,
peertubeHelpers
) {
if (!triggerUrl) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: No trigger URL configured, cannot send transcription request for video ${videoId}`
);
return;
}
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Sending transcription request to external service for video ${videoId}, triggerUrl: ${triggerUrl}`
);
let body = {
videoId: videoId,
url: downloadUrl,
peertubeInstanceBaseDomain: webserverUrl,
};
if (language) {
body.language = language;
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: Including language ${language} in transcription request for video ${videoId}`
);
} else {
peertubeHelpers.logger.warn(
`[transposer-connector] TransposerController: No language specified for video ${videoId} transcription request`
);
}
fetch(`${triggerUrl}/transcribe-and-translate/video`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(body),
})
.then((response) => {
if (!response.ok) {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: External transcription service returned error ${response.status} for video ${videoId}`
);
response.json().then((json_error) => {
throw new Error(
`HTTP error! status: ${response.status} and message ${json_error.message}`
);
});
}
return response.json();
})
.then((data) => {
peertubeHelpers.logger.info(
`[transposer-connector] TransposerController: External transcription service responded for video ${videoId}: ${
data.success ? "successful" : "unsuccessful"
}`
);
})
.catch((error) => {
peertubeHelpers.logger.error(
`[transposer-connector] TransposerController: Failed to communicate with external transcription service for video ${videoId}: ${error.message}`
);
});
}
};
module.exports = {
initTransposerController,
};