clean up native video player implementation
Extract shared stream quality key constant, add null guard to playback token parsing, fix handleRefresh race condition and concurrency, and replace Container with SizedBox placeholder. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d22c980454
commit
6695c13455
6 changed files with 20 additions and 9 deletions
|
|
@ -6,6 +6,9 @@ const clientId = String.fromEnvironment('CLIENT_ID');
|
|||
/// Twitch GQL client ID (web player, used for playback access tokens).
|
||||
const twitchGqlClientId = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
|
||||
|
||||
/// SharedPreferences key for the last-selected stream quality.
|
||||
const kLastStreamQualityKey = 'last_stream_quality';
|
||||
|
||||
/// Twitch API client secret.
|
||||
const secret = String.fromEnvironment('SECRET');
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,14 @@ class PlaybackAccessToken {
|
|||
});
|
||||
|
||||
factory PlaybackAccessToken.fromGqlResponse(Map<String, dynamic> json) {
|
||||
final data = json['data']['streamPlaybackAccessToken']
|
||||
as Map<String, dynamic>;
|
||||
final data =
|
||||
(json['data'] as Map<String, dynamic>?)?['streamPlaybackAccessToken']
|
||||
as Map<String, dynamic>?;
|
||||
if (data == null) {
|
||||
throw const FormatException(
|
||||
'streamPlaybackAccessToken missing from GQL response',
|
||||
);
|
||||
}
|
||||
return PlaybackAccessToken(
|
||||
value: data['value'] as String,
|
||||
signature: data['signature'] as String,
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ class _VideoChatState extends State<VideoChat>
|
|||
if (settingsStore.showVideo) ...[
|
||||
AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: Container(), // Placeholder for video space
|
||||
child: SizedBox(),
|
||||
),
|
||||
],
|
||||
Expanded(child: chat),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:frosty/apis/twitch_api.dart';
|
||||
import 'package:frosty/apis/twitch_gql_api.dart';
|
||||
import 'package:frosty/constants.dart';
|
||||
import 'package:frosty/models/channel.dart';
|
||||
import 'package:frosty/models/playback_access_token.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
|
|
@ -188,7 +189,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
} else {
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
if (_disposed) return;
|
||||
final lastQuality = prefs.getString('last_stream_quality');
|
||||
final lastQuality = prefs.getString(kLastStreamQualityKey);
|
||||
if (lastQuality != null) {
|
||||
final index = _availableStreamQualities.indexOf(lastQuality);
|
||||
if (index != -1) _pendingQualityIndex = index;
|
||||
|
|
@ -210,7 +211,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
} catch (e) {
|
||||
if (_disposed) return;
|
||||
// Wait for stream info so we can distinguish "offline" from "broken".
|
||||
await updateStreamInfo();
|
||||
await updateStreamInfo(forceUpdate: true);
|
||||
if (_disposed) return;
|
||||
runInAction(() {
|
||||
_loading = false;
|
||||
|
|
@ -374,7 +375,6 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
@action
|
||||
Future<void> handleRefresh() async {
|
||||
HapticFeedback.lightImpact();
|
||||
_disposed = false;
|
||||
_loading = true;
|
||||
_paused = true;
|
||||
_userPaused = false;
|
||||
|
|
@ -399,7 +399,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
);
|
||||
_controller!.addActivityListener(_handleActivityEvent);
|
||||
|
||||
await _initPlayer();
|
||||
_initPlayer();
|
||||
updateStreamInfo();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/constants.dart';
|
||||
import 'package:frosty/screens/channel/chat/details/chat_users_list.dart';
|
||||
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
|
||||
import 'package:frosty/screens/channel/video/native_video_store.dart';
|
||||
|
|
@ -109,7 +110,7 @@ class VideoOverlay extends StatelessWidget {
|
|||
videoStore.setStreamQuality(quality);
|
||||
SharedPreferences.getInstance().then(
|
||||
(prefs) => prefs.setString(
|
||||
'last_stream_quality',
|
||||
kLastStreamQualityKey,
|
||||
quality,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:frosty/apis/twitch_api.dart';
|
||||
import 'package:frosty/constants.dart';
|
||||
import 'package:frosty/models/channel.dart';
|
||||
import 'package:frosty/models/stream.dart';
|
||||
import 'package:frosty/screens/channel/video/video_player_interface.dart';
|
||||
|
|
@ -88,7 +89,7 @@ abstract class VideoStoreBase with Store implements VideoPlayerInterface {
|
|||
return;
|
||||
}
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final lastStreamQuality = prefs.getString('last_stream_quality');
|
||||
final lastStreamQuality = prefs.getString(kLastStreamQualityKey);
|
||||
if (lastStreamQuality == null) return;
|
||||
setStreamQuality(lastStreamQuality);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue