Files
plezy/lib/utils/track_label_builder.dart
T

125 lines
3.7 KiB
Dart

import 'codec_utils.dart';
/// Builds a track label from parts with the standard `' · '` joiner pattern.
///
/// Shared by both Plex track models and MPV track label utilities.
/// If [title] is non-empty it is added first, then [language], then [extraParts].
/// Falls back to `'$fallbackPrefix ${index + 1}'` when no parts are available.
String buildTrackLabel({
String? title,
String? language,
List<String> extraParts = const [],
required int index,
String fallbackPrefix = 'Track',
}) {
final parts = <String>[];
if (title != null && title.isNotEmpty) parts.add(title);
if (language != null && language.isNotEmpty) parts.add(language);
parts.addAll(extraParts);
return parts.isEmpty ? '$fallbackPrefix ${index + 1}' : parts.join(' · ');
}
String? cleanTrackMetadataValue(String? value) {
if (value == null) return null;
var cleaned = value.trim();
if (cleaned.isEmpty) return null;
final prefixed = RegExp(r'^(?:title|lang|language)\s*=\s*(.*)$', caseSensitive: false).firstMatch(cleaned);
if (prefixed != null) {
cleaned = prefixed.group(1)?.trim() ?? '';
}
if ((cleaned.startsWith('"') && cleaned.endsWith('"')) || (cleaned.startsWith("'") && cleaned.endsWith("'"))) {
cleaned = cleaned.substring(1, cleaned.length - 1).trim();
}
return cleaned.isEmpty ? null : cleaned;
}
String? cleanSubtitleTitle(String? title, {String? codec}) {
var cleaned = cleanTrackMetadataValue(title);
if (cleaned == null) return null;
final codecAliases = _subtitleCodecAliases(codec);
if (codecAliases.isEmpty) return cleaned;
final parts = cleaned.split(RegExp(r'\s+-\s+'));
while (parts.isNotEmpty && codecAliases.contains(_metadataToken(parts.last))) {
parts.removeLast();
}
cleaned = parts.join(' - ').trim();
return cleaned.isEmpty ? null : cleaned;
}
Set<String> _subtitleCodecAliases(String? codec) {
final aliases = <String>{
'SUBRIP',
'SRT',
'WEBVTT',
'VTT',
'ASS',
'SSA',
'PGS',
'PGSSUB',
'HDMV_PGS_SUBTITLE',
'DVD',
'DVDSUB',
'DVD_SUBTITLE',
'DVB_SUB',
'DVB_SUBTITLE',
};
if (codec != null && codec.isNotEmpty) {
aliases.add(_metadataToken(codec));
aliases.add(_metadataToken(CodecUtils.formatSubtitleCodec(codec)));
aliases.add(_metadataToken(CodecUtils.getSubtitleExtension(codec)));
}
return aliases;
}
String _metadataToken(String value) => value.trim().toUpperCase().replaceAll(RegExp(r'[^A-Z0-9]+'), '_');
class TrackLabelBuilder {
TrackLabelBuilder._();
static String buildAudioLabel({
String? title,
String? language,
String? codec,
int? channelsCount,
required int index,
}) {
final extraParts = <String>[];
if (codec != null && codec.isNotEmpty) {
extraParts.add(CodecUtils.formatAudioCodec(codec));
}
if (channelsCount != null) {
extraParts.add('${channelsCount}ch');
}
return buildTrackLabel(
title: title,
language: language?.toUpperCase(),
extraParts: extraParts,
index: index,
fallbackPrefix: 'Audio Track',
);
}
static String buildSubtitleLabel({
String? title,
String? language,
String? codec,
bool forced = false,
required int index,
}) {
final cleanedTitle = cleanSubtitleTitle(title, codec: codec);
final cleanedLanguage = cleanTrackMetadataValue(language)?.toUpperCase();
final extraParts = <String>[];
if (forced && !_metadataToken(cleanedTitle ?? '').split('_').contains('FORCED')) extraParts.add('Forced');
if (codec != null && codec.isNotEmpty) {
extraParts.add(CodecUtils.formatSubtitleCodec(codec));
}
return buildTrackLabel(title: cleanedTitle, language: cleanedLanguage, extraParts: extraParts, index: index);
}
}