Files
PinePods-nix/PinePods-0.8.2/mobile/lib/entities/transcript.dart
2026-03-03 10:57:43 -05:00

214 lines
5.4 KiB
Dart

// Copyright 2020 Ben Hills and the project contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:pinepods_mobile/core/extensions.dart';
import 'package:flutter/foundation.dart';
enum TranscriptFormat {
json,
subrip,
html,
unsupported,
}
/// This class represents a Podcasting 2.0 transcript URL.
///
/// [docs](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#transcript)
class TranscriptUrl {
final String url;
final TranscriptFormat type;
final String? language;
final String? rel;
final DateTime? lastUpdated;
TranscriptUrl({
required String url,
required this.type,
this.language = '',
this.rel = '',
this.lastUpdated,
}) : url = url.forceHttps;
Map<String, dynamic> toMap() {
var t = 0;
switch (type) {
case TranscriptFormat.subrip:
t = 0;
break;
case TranscriptFormat.json:
t = 1;
break;
case TranscriptFormat.html:
t = 2;
break;
case TranscriptFormat.unsupported:
t = 3;
break;
}
return <String, dynamic>{
'url': url,
'type': t,
'lang': language,
'rel': rel,
'lastUpdated': DateTime.now().millisecondsSinceEpoch,
};
}
static TranscriptUrl fromMap(Map<String, dynamic> transcript) {
var ts = transcript['type'] as int? ?? 2;
var t = TranscriptFormat.unsupported;
switch (ts) {
case 0:
t = TranscriptFormat.subrip;
break;
case 1:
t = TranscriptFormat.json;
break;
case 2:
t = TranscriptFormat.html;
break;
case 3:
t = TranscriptFormat.unsupported;
break;
}
return TranscriptUrl(
url: transcript['url'] as String,
language: transcript['lang'] as String?,
rel: transcript['rel'] as String?,
type: t,
lastUpdated: transcript['lastUpdated'] == null
? DateTime.now()
: DateTime.fromMillisecondsSinceEpoch(transcript['lastUpdated'] as int),
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TranscriptUrl &&
runtimeType == other.runtimeType &&
url == other.url &&
type == other.type &&
language == other.language &&
rel == other.rel;
@override
int get hashCode => url.hashCode ^ type.hashCode ^ language.hashCode ^ rel.hashCode;
}
/// This class represents a Podcasting 2.0 transcript container.
/// [docs](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#transcript)
class Transcript {
int? id;
String? guid;
final List<Subtitle> subtitles;
DateTime? lastUpdated;
bool filtered;
Transcript({
this.id,
this.guid,
this.subtitles = const <Subtitle>[],
this.filtered = false,
this.lastUpdated,
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'guid': guid,
'subtitles': (subtitles).map((subtitle) => subtitle.toMap()).toList(growable: false),
'lastUpdated': DateTime.now().millisecondsSinceEpoch,
};
}
static Transcript fromMap(int? key, Map<String, dynamic> transcript) {
var subtitles = <Subtitle>[];
if (transcript['subtitles'] != null) {
for (var subtitle in (transcript['subtitles'] as List)) {
if (subtitle is Map<String, dynamic>) {
subtitles.add(Subtitle.fromMap(subtitle));
}
}
}
return Transcript(
id: key,
guid: transcript['guid'] as String? ?? '',
subtitles: subtitles,
lastUpdated: transcript['lastUpdated'] == null
? DateTime.now()
: DateTime.fromMillisecondsSinceEpoch(transcript['lastUpdated'] as int),
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Transcript &&
runtimeType == other.runtimeType &&
guid == other.guid &&
listEquals(subtitles, other.subtitles);
@override
int get hashCode => guid.hashCode ^ subtitles.hashCode;
bool get transcriptAvailable => (subtitles.isNotEmpty || filtered);
}
/// Represents an individual line within a transcript.
class Subtitle {
final int index;
final Duration start;
Duration? end;
String? data;
String speaker;
Subtitle({
required this.index,
required this.start,
this.end,
this.data,
this.speaker = '',
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'i': index,
'start': start.inMilliseconds,
'end': end!.inMilliseconds,
'speaker': speaker,
'data': data,
};
}
static Subtitle fromMap(Map<String, dynamic> subtitle) {
return Subtitle(
index: subtitle['i'] as int? ?? 0,
start: Duration(milliseconds: subtitle['start'] as int? ?? 0),
end: Duration(milliseconds: subtitle['end'] as int? ?? 0),
speaker: subtitle['speaker'] as String? ?? '',
data: subtitle['data'] as String? ?? '',
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Subtitle &&
runtimeType == other.runtimeType &&
index == other.index &&
start == other.start &&
end == other.end &&
data == other.data &&
speaker == other.speaker;
@override
int get hashCode => index.hashCode ^ start.hashCode ^ end.hashCode ^ data.hashCode ^ speaker.hashCode;
}