deriveCoursewarePrefetchUrls function
从回放 metadata 的 signal_data 推导白板实际会加载的课件背景图 URL。
背景:白板真正 fetch 的背景图来自 signal 的 action 201.url(如 .../picture/1.jpg),
与 metadata document_pages 的缩略图(.../thumbnail/1.jpg,且域名不同)不一致,
故不能用缩略图预热。这里直接由 signal_data 还原真实 URL:
- action 501 建立
fid -> {downloadURL, type}映射; - action 401 的
toBoardId解析出(page, fid),对 type==0 文件按 SDK 同款规则拼出 preview URL(paas / cos 两种形式,见_previewUrlForPage); - action 201 的
url直接采用(与 401 推导结果去重)。
仅返回实际被切换展示过的页(避免把整份文件上百页全部无谓预热);H5 动画 PPT(type==1)跳过。 返回顺序为信令出现顺序(即时间轴顺序),同一 URL 仅保留首次出现。
Implementation
List<CoursewarePrefetchUrl> deriveCoursewarePrefetchUrls(
List<PlayBackSignalData> signals,
) {
final files = <String, _FileInfo>{};
final result = <CoursewarePrefetchUrl>[];
final seen = <String>{};
void addUrl(int offsetMs, String url) {
if (url.isEmpty) return;
if (seen.add(url)) result.add(CoursewarePrefetchUrl(offsetMs, url));
}
for (final s in signals) {
final dynamic decoded;
try {
decoded = jsonDecode(s.value);
} catch (_) {
continue;
}
if (decoded is! Map) continue;
final actions = decoded['actions'];
if (actions is! List) continue;
for (final a in actions) {
if (a is! Map) continue;
final action = a['action'];
if (action == _kActionAddFile) {
final file = a['file'];
if (file is Map) {
final fid = file['fid']?.toString();
final downloadURL = file['downloadURL']?.toString() ?? '';
final type = _asInt(file['type'], -1);
if (fid != null && fid.isNotEmpty) {
files[fid] = _FileInfo(downloadURL: downloadURL, type: type);
}
}
} else if (action == _kActionUpdateBgImg) {
// type 缺省按图片处理;H5/其它显式类型跳过。
final type = a['type'];
final url = a['url']?.toString() ?? '';
if ((type == null || _asInt(type, -1) == _kTranscodeTypeStaticImage) &&
url.isNotEmpty) {
addUrl(s.offset, url);
}
} else if (action == _kActionSwitch) {
final toBoardId = a['toBoardId']?.toString() ?? '';
final derived = _bgUrlFromBoardId(toBoardId, files);
if (derived != null) addUrl(s.offset, derived);
}
}
}
return result;
}