const mongoose = require('mongoose'); const subtitleSchema = new mongoose.Schema({ segmentId: { type: Number, required: true, unique: true }, startTime: { type: String, required: true, validate: { validator: function(v) { // Validate time format: HH:MM:SS,mmm return /^\d{2}:\d{2}:\d{2},\d{3}$/.test(v); }, message: 'Start time must be in format HH:MM:SS,mmm' } }, endTime: { type: String, required: true, validate: { validator: function(v) { // Validate time format: HH:MM:SS,mmm return /^\d{2}:\d{2}:\d{2},\d{3}$/.test(v); }, message: 'End time must be in format HH:MM:SS,mmm' } }, duration: { type: String, required: true }, englishText: { type: String, required: true, trim: true }, chineseTranslation: { type: String, default: '', trim: true }, isProtected: { type: Boolean, default: false }, protectedReason: { type: String }, lastModified: { type: Date, default: Date.now }, modificationHistory: [{ action: { type: String, required: true }, timestamp: { type: Date, default: Date.now }, reason: { type: String } }] }, { timestamps: true }); // Index for efficient queries subtitleSchema.index({ segmentId: 1 }); subtitleSchema.index({ startTime: 1 }); subtitleSchema.index({ isProtected: 1 }); // Virtual for calculating duration in seconds subtitleSchema.virtual('durationInSeconds').get(function() { const start = this.parseTimeToSeconds(this.startTime); const end = this.parseTimeToSeconds(this.endTime); return end - start; }); // Method to parse time string to seconds subtitleSchema.methods.parseTimeToSeconds = function(timeString) { const parts = timeString.split(':'); const seconds = parseInt(parts[2].split(',')[0]); const minutes = parseInt(parts[1]); const hours = parseInt(parts[0]); return hours * 3600 + minutes * 60 + seconds; }; // Static method to get all subtitles ordered by segment ID subtitleSchema.statics.getAllOrdered = function() { return this.find().sort({ segmentId: 1 }); }; // Static method to get protected subtitles subtitleSchema.statics.getProtected = function() { return this.find({ isProtected: true }); }; // Static method to update subtitle safely subtitleSchema.statics.safeUpdate = function(segmentId, updateData) { return this.findOneAndUpdate( { segmentId: segmentId }, { ...updateData, lastModified: new Date(), $push: { modificationHistory: { action: 'update', timestamp: new Date(), reason: updateData.reason || 'Manual update' } } }, { new: true, runValidators: true } ); }; // Static method to protect subtitle subtitleSchema.statics.protectSubtitle = function(segmentId, reason) { return this.findOneAndUpdate( { segmentId: segmentId }, { isProtected: true, protectedReason: reason, lastModified: new Date(), $push: { modificationHistory: { action: 'protect', timestamp: new Date(), reason: reason } } }, { new: true } ); }; // Static method to unlock subtitle subtitleSchema.statics.unlockSubtitle = function(segmentId, unlockKey) { // In a real implementation, you'd verify the unlock key if (unlockKey !== 'ADMIN_UNLOCK_KEY_2024') { throw new Error('Invalid unlock key'); } return this.findOneAndUpdate( { segmentId: segmentId }, { isProtected: false, protectedReason: null, lastModified: new Date(), $push: { modificationHistory: { action: 'unlock', timestamp: new Date(), reason: 'Admin unlock' } } }, { new: true } ); }; module.exports = mongoose.model('Subtitle', subtitleSchema);