💄 Video player optimized

This commit is contained in:
2025-08-01 20:36:39 +08:00
parent 84b1d6a346
commit b39e2e2d64
24 changed files with 500 additions and 299 deletions

View File

@@ -165,7 +165,7 @@ class AppScaffold extends StatelessWidget {
final AppBar? appBar;
final DrawerCallback? onDrawerChanged;
final DrawerCallback? onEndDrawerChanged;
final bool? noBackground;
final bool? isNoBackground;
final bool? extendBody;
const AppScaffold({
@@ -181,7 +181,7 @@ class AppScaffold extends StatelessWidget {
this.endDrawer,
this.onDrawerChanged,
this.onEndDrawerChanged,
this.noBackground,
this.isNoBackground,
this.extendBody,
});
@@ -190,7 +190,7 @@ class AppScaffold extends StatelessWidget {
final appBarHeight = appBar?.preferredSize.height ?? 0;
final safeTop = MediaQuery.of(context).padding.top;
final noBackground = this.noBackground ?? isWideScreen(context);
final noBackground = isNoBackground ?? isWideScreen(context);
final content = Column(
children: [

View File

@@ -1,8 +1,11 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/config.dart';
import 'package:island/services/time.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
@@ -45,7 +48,7 @@ class CloudFileWidget extends ConsumerWidget {
),
"video" => AspectRatio(
aspectRatio: ratio,
child: UniversalVideo(uri: uri, aspectRatio: ratio),
child: CloudVideoWidget(item: item),
),
_ => Text('Unable render for ${item.mimeType}'),
};
@@ -58,6 +61,119 @@ class CloudFileWidget extends ConsumerWidget {
}
}
class CloudVideoWidget extends HookConsumerWidget {
final SnCloudFile item;
const CloudVideoWidget({super.key, required this.item});
@override
Widget build(BuildContext context, WidgetRef ref) {
final open = useState(false);
final serverUrl = ref.watch(serverUrlProvider);
final uri = '$serverUrl/drive/files/${item.id}';
var ratio =
item.fileMeta?['ratio'] is num
? item.fileMeta!['ratio'].toDouble()
: 1.0;
if (ratio == 0) ratio = 1.0;
if (open.value) {
return UniversalVideo(uri: uri, aspectRatio: ratio, autoplay: true);
}
return GestureDetector(
child: Stack(
children: [
UniversalImage(uri: '$uri?thumbnail=true'),
Positioned.fill(
child: Center(
child: const Icon(
Symbols.play_arrow,
fill: 1,
size: 32,
shadows: [
BoxShadow(
color: Colors.black54,
offset: Offset(1, 1),
spreadRadius: 8,
blurRadius: 8,
),
],
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
spacing: 8,
children: [
if (item.fileMeta?['duration'] != null)
Text(
Duration(
milliseconds:
((item.fileMeta?['duration'] as num) * 1000)
.toInt(),
).formatDuration(),
style: TextStyle(
shadows: [
BoxShadow(
color: Colors.black54,
offset: Offset(1, 1),
spreadRadius: 8,
blurRadius: 8,
),
],
),
),
if (item.fileMeta?['bit_rate'] != null)
Text(
'${int.parse(item.fileMeta?['bit_rate'] as String) ~/ 1000} Kbps',
style: TextStyle(
shadows: [
BoxShadow(
color: Colors.black54,
offset: Offset(1, 1),
spreadRadius: 8,
blurRadius: 8,
),
],
),
),
],
),
Text(
item.name,
style: TextStyle(
fontWeight: FontWeight.bold,
shadows: [
BoxShadow(
color: Colors.black54,
offset: Offset(1, 1),
spreadRadius: 8,
blurRadius: 8,
),
],
),
),
],
),
).padding(horizontal: 16, bottom: 12),
],
),
onTap: () {
open.value = true;
},
);
}
}
class CloudImageWidget extends ConsumerWidget {
final String? fileId;
final SnCloudFile? file;

View File

@@ -11,10 +11,12 @@ import 'package:media_kit_video/media_kit_video.dart';
class UniversalVideo extends ConsumerStatefulWidget {
final String uri;
final double aspectRatio;
final bool autoplay;
const UniversalVideo({
super.key,
required this.uri,
this.aspectRatio = 16 / 9,
this.autoplay = false,
});
@override
@@ -47,7 +49,7 @@ class _UniversalVideoState extends ConsumerState<UniversalVideo> {
log('[MediaPlayer] Hit cache: $url');
}
_player!.open(Media(uri), play: false);
_player!.open(Media(uri), play: widget.autoplay);
}
@override

View File

@@ -27,9 +27,13 @@ class PublisherCard extends ConsumerWidget {
Widget card = Card(
clipBehavior: Clip.antiAlias,
margin: EdgeInsets.zero,
child: InkWell(
onTap: () {
context.pushNamed('publisherProfile', pathParameters: {'name': publisher.name});
context.pushNamed(
'publisherProfile',
pathParameters: {'name': publisher.name},
);
},
child: AspectRatio(
aspectRatio: 16 / 7,

View File

@@ -29,9 +29,13 @@ class RealmCard extends ConsumerWidget {
Widget card = Card(
clipBehavior: Clip.antiAlias,
margin: EdgeInsets.zero,
child: InkWell(
onTap: () {
context.pushNamed('realmDetail', pathParameters: {'slug': realm.slug});
context.pushNamed(
'realmDetail',
pathParameters: {'slug': realm.slug},
);
},
child: AspectRatio(
aspectRatio: 16 / 7,

View File

@@ -28,6 +28,7 @@ class WebArticleCard extends StatelessWidget {
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
child: Card(
margin: EdgeInsets.zero,
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () => _onTap(context),