feat(app): 播放器增加清晰度档位入口

This commit is contained in:
Zhou Weidong committed 2026-10-20 04:37:42 +08:00
1 parent 30a5fa0776
commit 2b2b7e9913
9 files changed
+242 -1

No files matched your search

+3
View File
@@ -72,6 +72,7 @@ class PlayerLogic extends ChangeNotifier {
PlayTicket? blockedTicket;
String? blockedEpisodeId;
List<Episode> sessionEpisodes = const [];
List<String> qualities = const [];
int pageRequest = 0;
bool muted = false;
double playbackSpeed = 1.0;
@@ -217,6 +218,7 @@ class PlayerLogic extends ChangeNotifier {
if (localPath != null) {
blockedTicket = null;
blockedEpisodeId = null;
qualities = const [];
sessionEpisodes[index] = episode.withVideoUrl(_fileUrl(localPath));
return true;
}
@@ -227,6 +229,7 @@ class PlayerLogic extends ChangeNotifier {
if (request != pageRequest) return false;
blockedTicket = null;
blockedEpisodeId = null;
qualities = ticket.qualities;
if (ticket.accessible && ticket.playUrl.isNotEmpty) {
// 签名地址原样写回,不做任何拼接/改写(AppConfig.mediaUrl 只补 host)
sessionEpisodes[index] = episode.withVideoUrl(ticket.playUrl);
+39 -1
View File
@@ -28,6 +28,7 @@ import 'player_logic.dart';
import 'widgets/home_feed_overlay.dart';
import 'widgets/exit_clear_screen_button.dart';
import 'widgets/player_center_toggle.dart';
import 'widgets/quality_sheet.dart';
import 'widgets/player_featured_comment.dart';
import 'widgets/seek_progress_bar.dart';
import '../../core/data/repositories/watch_history_repository.dart';
@@ -402,6 +403,8 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
playing: logic.session.userWantsPlay,
playbackSpeed: _playbackSpeed,
onSpeedTap: () => unawaited(_openSpeedSheet(context)),
qualities: logic.qualities,
onQualityTap: () => unawaited(_openQualitySheet(context)),
controlsVisible: _chromeVisible,
onClear: () => setState(() => _chromeVisible = false),
onExitClear: () => setState(() => _chromeVisible = true),
@@ -500,6 +503,15 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
await logic.setPlaybackSpeed(selected);
}
Future<void> _openQualitySheet(BuildContext context) async {
await showModalBottomSheet<void>(
context: context,
backgroundColor: AppColors.card,
showDragHandle: true,
builder: (_) => QualitySheet(qualities: logic.qualities),
);
}
Future<void> _unlockEpisode(BuildContext context, int index) async {
if (!await ensureSignedIn(context)) return;
if (!context.mounted) return;
@@ -693,6 +705,8 @@ class _PlaybackOverlay extends StatefulWidget {
required this.playing,
required this.playbackSpeed,
required this.onSpeedTap,
required this.qualities,
required this.onQualityTap,
required this.controlsVisible,
required this.onTogglePlay,
required this.onClear,
@@ -720,6 +734,8 @@ class _PlaybackOverlay extends StatefulWidget {
final bool playing;
final double playbackSpeed;
final VoidCallback onSpeedTap;
final List<String> qualities;
final VoidCallback onQualityTap;
final bool controlsVisible;
final VoidCallback onTogglePlay;
final VoidCallback onClear;
@@ -953,7 +969,29 @@ class _PlaybackOverlayState extends State<_PlaybackOverlay> {
});
},
),
ListTile(
if (widget.qualities.isNotEmpty)
ListTile(
key: const Key('player-more-quality'),
leading: const Icon(
Icons.high_quality_rounded,
color: AppColors.secondary,
),
title: Text(widget.l10n.playbackQuality),
trailing: Text(
widget.qualities.contains(QualitySheet.autoQuality)
? widget.l10n.qualityAuto
: widget.qualities.first,
style: const TextStyle(
color: AppColors.blue,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
onTap: () {
Navigator.pop(sheetContext);
widget.onQualityTap();
},
), ListTile(
key: const Key('player-more-download'),
leading: const Icon(
Icons.download_outlined,
@@ -0,0 +1,79 @@
// Author: fruit.com.cn
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
import '../../../l10n/app_localizations.dart';
/// 清晰度档位面板:展示后端下发档位;HLS 多码率由播放器自动切换。
class QualitySheet extends StatelessWidget {
const QualitySheet({
super.key,
required this.qualities,
this.selected = autoQuality,
});
static const String autoQuality = 'AUTO';
final List<String> qualities;
final String selected;
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return SafeArea(
child: Column(
key: const Key('player-quality-sheet'),
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 2, 20, 8),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
l10n.playbackQuality,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
),
),
for (final quality in qualities)
ListTile(
key: Key('player-quality-$quality'),
dense: true,
title: Text(
_label(l10n, quality),
style: TextStyle(
color: quality == selected ? AppColors.blue : AppColors.cream,
fontSize: 13,
fontWeight: quality == selected
? FontWeight.w700
: FontWeight.w500,
),
),
trailing: quality == selected
? const Icon(
Icons.check_rounded,
color: AppColors.blue,
size: 18,
)
: null,
),
Padding(
padding: const EdgeInsets.fromLTRB(20, 6, 20, 14),
child: Text(
l10n.qualityAutoHint,
style: const TextStyle(color: AppColors.muted, fontSize: 11),
),
),
],
),
);
}
String _label(AppLocalizations l10n, String quality) =>
quality == autoQuality ? l10n.qualityAuto : quality;
}