Initial commit

This commit is contained in:
admin1
2026-07-14 11:11:36 +08:00
commit 656499cf94
604 changed files with 119518 additions and 0 deletions
@@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import 'package:forui/forui.dart';
import '../../../core/contracts/library.dart';
import '../../../shared/utils/format_utils.dart';
import 'hero_action_buttons.dart';
List<FItem> buildSubtitleMenuButtons({
required List<EmbyRawMediaStream> subtitles,
required int? selectedSubtitleIdx,
required ValueChanged<int?> onSelectSubtitle,
}) {
return [
FItem(
prefix: selectedSubtitleIdx == null
? const Icon(Icons.check, size: 14)
: const SizedBox(width: 14),
title: const Text('关闭字幕'),
selected: selectedSubtitleIdx == null,
onPress: () => onSelectSubtitle(null),
),
...subtitles.asMap().entries.map(
(e) => FItem(
prefix: selectedSubtitleIdx == e.key
? const Icon(Icons.check, size: 14)
: const SizedBox(width: 14),
title: Text(
e.value.DisplayTitle ?? e.value.Language ?? '字幕 ${e.key + 1}',
),
selected: selectedSubtitleIdx == e.key,
onPress: () => onSelectSubtitle(e.key),
),
),
];
}
List<FItem> buildAudioMenuButtons({
required List<EmbyRawMediaStream> audios,
required int selectedAudioIdx,
required ValueChanged<int> onSelectAudio,
}) {
return audios
.asMap()
.entries
.map(
(e) => FItem(
prefix: selectedAudioIdx == e.key
? const Icon(Icons.check, size: 14)
: const SizedBox(width: 14),
title: Text(
e.value.DisplayTitle ?? e.value.Language ?? '音轨 ${e.key + 1}',
),
selected: selectedAudioIdx == e.key,
onPress: () => onSelectAudio(e.key),
),
)
.toList();
}
List<FItem> buildVersionMenuButtons({
required List<EmbyRawMediaSource> sources,
required int selectedSourceIdx,
required ValueChanged<int> onSelectSource,
}) {
return sources.asMap().entries.map((e) {
final src = e.value;
final name = src.Name ?? '版本 ${e.key + 1}';
final size = src.Size;
final sizeStr = size != null && size > 0 ? formatFileSize(size) : null;
return FItem(
prefix: selectedSourceIdx == e.key
? const Icon(Icons.check, size: 14)
: const SizedBox(width: 14),
title: Text(sizeStr != null ? '$name $sizeStr' : name),
selected: selectedSourceIdx == e.key,
onPress: () => onSelectSource(e.key),
);
}).toList();
}
String subtitleSummaryLabel(
List<EmbyRawMediaStream> subtitles,
int? selectedSubtitleIdx,
) => _streamSummaryLabel(
subtitles,
selectedSubtitleIdx,
empty: '关闭',
prefix: '字幕',
);
String audioSummaryLabel(
List<EmbyRawMediaStream> audios,
int selectedAudioIdx,
) => _streamSummaryLabel(audios, selectedAudioIdx, empty: '默认', prefix: '音轨');
String _streamSummaryLabel(
List<EmbyRawMediaStream> streams,
int? idx, {
required String empty,
required String prefix,
}) {
if (idx == null || idx < 0 || idx >= streams.length) return empty;
final s = streams[idx];
return s.Language ?? s.DisplayTitle ?? '$prefix ${idx + 1}';
}
String versionSummaryLabel(
List<EmbyRawMediaSource> sources,
int selectedSourceIdx,
) {
if (selectedSourceIdx < 0 || selectedSourceIdx >= sources.length) {
return '版本';
}
return sources[selectedSourceIdx].Name ?? '版本 ${selectedSourceIdx + 1}';
}
List<Widget> buildStreamSelectorPills({
required List<EmbyRawMediaStream> subtitles,
required List<EmbyRawMediaStream> audios,
required int? selectedSubtitleIdx,
required int selectedAudioIdx,
required ValueChanged<int?> onSelectSubtitle,
required ValueChanged<int> onSelectAudio,
required double height,
}) {
final actions = <Widget>[];
if (subtitles.length > 1) {
actions.add(
HeroPillDropdownButton(
height: height,
icon: Icons.subtitles_outlined,
menuChildren: buildSubtitleMenuButtons(
subtitles: subtitles,
selectedSubtitleIdx: selectedSubtitleIdx,
onSelectSubtitle: onSelectSubtitle,
),
),
);
}
if (audios.length > 1) {
actions.add(
HeroPillDropdownButton(
height: height,
icon: Icons.audiotrack_outlined,
menuChildren: buildAudioMenuButtons(
audios: audios,
selectedAudioIdx: selectedAudioIdx,
onSelectAudio: onSelectAudio,
),
),
);
}
return actions;
}