File size: 3,932 Bytes
da819ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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);