Initial commit
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
|
||||
class TimedSubtitle {
|
||||
final Duration start;
|
||||
final Duration end;
|
||||
final String text;
|
||||
|
||||
const TimedSubtitle({
|
||||
required this.start,
|
||||
required this.end,
|
||||
required this.text,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
class EmbeddedSubtitleCues {
|
||||
final List<TimedSubtitle> _cues = [];
|
||||
|
||||
bool get isEmpty => _cues.isEmpty;
|
||||
|
||||
|
||||
void add(double startSec, double endSec, List<String> lines) {
|
||||
final text = lines
|
||||
.map((s) => s.trim())
|
||||
.where((s) => s.isNotEmpty)
|
||||
.join('\n');
|
||||
if (text.isEmpty) return;
|
||||
|
||||
final start = _toDuration(startSec);
|
||||
final end = _toDuration(endSec);
|
||||
if (end < start) return;
|
||||
|
||||
final at = _lowerBound(start);
|
||||
for (var i = at; i < _cues.length && _cues[i].start == start; i++) {
|
||||
if (_cues[i].end == end && _cues[i].text == text) return;
|
||||
}
|
||||
_cues.insert(at, TimedSubtitle(start: start, end: end, text: text));
|
||||
}
|
||||
|
||||
|
||||
List<String> textAt(Duration position) {
|
||||
final out = <String>[];
|
||||
for (final cue in _cues) {
|
||||
if (position < cue.start) break;
|
||||
if (position <= cue.end) out.addAll(cue.text.split('\n'));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void clear() => _cues.clear();
|
||||
|
||||
static Duration _toDuration(double seconds) =>
|
||||
Duration(milliseconds: (seconds * 1000).round());
|
||||
|
||||
|
||||
int _lowerBound(Duration start) {
|
||||
int lo = 0;
|
||||
int hi = _cues.length;
|
||||
while (lo < hi) {
|
||||
final mid = (lo + hi) >> 1;
|
||||
if (_cues[mid].start < start) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user