mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Adjusted
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// utils/duration.util.js
|
||||
|
||||
const AVG_WORDS_PER_MINUTE = 200;
|
||||
|
||||
function stripHtml(html) {
|
||||
// Simple regex strip — no extra dependency needed
|
||||
return (html ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate seconds for a single lesson block
|
||||
* Block shape: { type: 'text'|'image'|'video', content, word_count, video_duration_seconds }
|
||||
*/
|
||||
function estimateBlockDuration(block) {
|
||||
const videoDuration = Number(block.video_duration_seconds) || 0;
|
||||
|
||||
// Derive word count from HTML content on the fly
|
||||
const getWordCount = (html) => {
|
||||
const text = stripHtml(html);
|
||||
return text ? text.split(" ").filter(Boolean).length : 0;
|
||||
};
|
||||
|
||||
const readingSecs = (html) =>
|
||||
Math.ceil((getWordCount(html) / AVG_WORDS_PER_MINUTE) * 60);
|
||||
|
||||
switch (block.type) {
|
||||
case "text":
|
||||
return readingSecs(block.content?.body);
|
||||
|
||||
case "image":
|
||||
return 60;
|
||||
|
||||
case "video":
|
||||
return videoDuration;
|
||||
|
||||
case "text-image":
|
||||
case "text_image":
|
||||
return readingSecs(block.content?.body) + 60;
|
||||
|
||||
case "text-video":
|
||||
case "text_video":
|
||||
return readingSecs(block.content?.body) + videoDuration;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompute and persist duration_seconds up the chain:
|
||||
* blocks → lesson → unit → course
|
||||
*/
|
||||
async function recomputeDurations(lessonId) {
|
||||
const Lesson = require("../models/courses/lessons.mdl");
|
||||
const Unit = require("../models/courses/units.mdl");
|
||||
const { Course } = require("../models/courses/courses.mdl");
|
||||
const LessonPage = require("../models/courses/lesson_page.mdl");
|
||||
|
||||
// 1. Lesson duration from blocks
|
||||
const page = await LessonPage.findOne({ where: { lesson_id: lessonId } });
|
||||
const lessonSecs = (page?.blocks ?? []).reduce(
|
||||
(sum, block) => sum + estimateBlockDuration(block), 0
|
||||
);
|
||||
|
||||
// Guard against NaN before DB write
|
||||
const safeLessonSecs = isNaN(lessonSecs) ? 0 : lessonSecs;
|
||||
await Lesson.update({ duration_seconds: safeLessonSecs }, { where: { lesson_id: lessonId } });
|
||||
|
||||
// 2. Unit duration — sum of its lessons
|
||||
const lesson = await Lesson.findOne({ where: { lesson_id: lessonId } });
|
||||
const [unitResult] = await Lesson.sequelize.query(`
|
||||
SELECT COALESCE(SUM(duration_seconds), 0) AS total
|
||||
FROM lessons
|
||||
WHERE unit_id = :unitId AND "deletedAt" IS NULL
|
||||
`, { replacements: { unitId: lesson.unit_id }, type: Lesson.sequelize.QueryTypes.SELECT });
|
||||
|
||||
await Unit.update(
|
||||
{ duration_seconds: unitResult.total },
|
||||
{ where: { unit_id: lesson.unit_id } }
|
||||
);
|
||||
|
||||
// 3. Course duration — sum of its units
|
||||
const unit = await Unit.findOne({ where: { unit_id: lesson.unit_id } });
|
||||
const [courseResult] = await Lesson.sequelize.query(`
|
||||
SELECT COALESCE(SUM(duration_seconds), 0) AS total
|
||||
FROM units
|
||||
WHERE course_id = :courseId AND "deletedAt" IS NULL
|
||||
`, { replacements: { courseId: unit.course_id }, type: Lesson.sequelize.QueryTypes.SELECT });
|
||||
|
||||
await Course.update(
|
||||
{ duration_seconds: courseResult.total },
|
||||
{ where: { course_id: unit.course_id } }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format seconds → human readable: "1 hr 10 mins", "45 mins", "30 secs"
|
||||
*/
|
||||
function formatDuration(seconds) {
|
||||
if (!seconds) return "0 mins";
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = seconds % 60;
|
||||
|
||||
const parts = [];
|
||||
if (h) parts.push(`${h} hr`);
|
||||
if (m) parts.push(`${m} min${m !== 1 ? "s" : ""}`);
|
||||
if (!h && !m && s) parts.push(`${s} sec${s !== 1 ? "s" : ""}`);
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
module.exports = { estimateBlockDuration, recomputeDurations, formatDuration };
|
||||
Reference in New Issue
Block a user