Video player!

This commit is contained in:
LittleSheep 2024-05-27 23:07:01 +08:00
parent 6e09414036
commit 9aceabd83c
11 changed files with 300 additions and 46 deletions

View File

@ -40,6 +40,8 @@ PODS:
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
@ -51,6 +53,11 @@ PODS:
- SwiftyGif (5.4.5)
- url_launcher_ios (0.0.1):
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- wakelock_plus (0.0.1):
- Flutter
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
@ -58,9 +65,12 @@ DEPENDENCIES:
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
SPEC REPOS:
trunk:
@ -80,12 +90,18 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
@ -95,11 +111,14 @@ SPEC CHECKSUMS:
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796

View File

@ -30,6 +30,8 @@ class AttachmentProvider extends GetConnect {
httpClient.baseUrl = ServiceFinder.services['paperclip'];
}
static Map<String, String> mimetypeOverrides = {'mov': 'video/quicktime'};
Future<Response> getMetadata(int id) => get('/api/attachments/$id/meta');
Future<Response> createAttachment(File file, String hash, String usage,
@ -46,7 +48,15 @@ class AttachmentProvider extends GetConnect {
final fileAlt = basename(file.path).contains('.')
? basename(file.path).substring(0, basename(file.path).lastIndexOf('.'))
: basename(file.path);
final fileExt = basename(file.path)
.substring(basename(file.path).lastIndexOf('.') + 1)
.toLowerCase();
// Override for some files cannot be detected mimetype by server-side
String? mimetypeOverride;
if (mimetypeOverrides.keys.contains(fileExt)) {
mimetypeOverride = mimetypeOverrides[fileExt];
}
final resp = await client.post(
'/api/attachments',
FormData({
@ -54,16 +64,17 @@ class AttachmentProvider extends GetConnect {
'file': filePayload,
'hash': hash,
'usage': usage,
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': jsonEncode({
if (ratio != null) 'ratio': ratio,
}),
}),
);
if (resp.statusCode == 200) {
return resp;
if (resp.statusCode != 200) {
throw Exception(resp.bodyString);
}
throw Exception(resp.bodyString);
return resp;
}
Future<Response> updateAttachment(
@ -93,7 +104,7 @@ class AttachmentProvider extends GetConnect {
throw Exception(resp.bodyString);
}
return resp.body;
return resp;
}
Future<Response> deleteAttachment(int id) async {

View File

@ -42,7 +42,7 @@ abstract class AppRouter {
BasicShell(state: state, child: child),
routes: [
GoRoute(
path: '/posts/:alias',
path: '/posts/view/:alias',
name: 'postDetail',
builder: (context, state) => PostDetailScreen(
alias: state.pathParameters['alias']!,

View File

@ -21,6 +21,7 @@ class SolianMessages extends Translations {
'search': 'Search',
'reply': 'Reply',
'repost': 'Repost',
'openInBrowser': 'Open in browser',
'notification': 'Notification',
'errorHappened': 'An error occurred',
'email': 'Email',
@ -134,6 +135,7 @@ class SolianMessages extends Translations {
'search': '搜索',
'reply': '回复',
'repost': '转帖',
'openInBrowser': '在浏览器中打开',
'notification': '通知',
'errorHappened': '发生错误了',
'email': '邮件地址',

View File

@ -1,9 +1,12 @@
import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:solian/models/attachment.dart';
import 'package:solian/services.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:video_player/video_player.dart';
class AttachmentItem extends StatelessWidget {
class AttachmentItem extends StatefulWidget {
final String parentId;
final Attachment item;
final bool showBadge;
@ -24,45 +27,127 @@ class AttachmentItem extends StatelessWidget {
});
@override
Widget build(BuildContext context) {
return Hero(
tag: Key('a${item.uuid}p$parentId'),
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
'${ServiceFinder.services['paperclip']}/api/attachments/${item.id}',
fit: fit,
),
if (showBadge && badge != null)
Positioned(
right: 12,
bottom: 8,
child: Material(
color: Colors.transparent,
child: Chip(label: Text(badge!)),
),
),
if (showHideButton && item.isMature)
Positioned(
top: 8,
left: 12,
child: Material(
color: Colors.transparent,
child: ActionChip(
visualDensity:
const VisualDensity(vertical: -4, horizontal: -4),
avatar: Icon(Icons.visibility_off,
color: Theme.of(context).colorScheme.onSurfaceVariant),
label: Text('hide'.tr),
onPressed: () {
if (onHide != null) onHide!();
},
),
),
),
],
State<AttachmentItem> createState() => _AttachmentItemState();
}
class _AttachmentItemState extends State<AttachmentItem> {
VideoPlayerController? _videoPlayerController;
ChewieController? _chewieController;
void ensureInitVideo() {
if (_videoPlayerController != null) return;
_videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
));
_videoPlayerController!.initialize();
_chewieController = ChewieController(
aspectRatio: widget.item.metadata?['ratio'] ?? 16 / 9,
videoPlayerController: _videoPlayerController!,
customControls: const MaterialControls(showPlayButton: true),
materialProgressColors: ChewieProgressColors(
playedColor: Theme.of(context).colorScheme.primary,
handleColor: Theme.of(context).colorScheme.primary,
),
);
}
@override
Widget build(BuildContext context) {
switch (widget.item.mimetype.split('/').first) {
case 'image':
return Hero(
tag: Key('a${widget.item.uuid}p${widget.parentId}'),
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
fit: widget.fit,
),
if (widget.showBadge && widget.badge != null)
Positioned(
right: 12,
bottom: 8,
child: Material(
color: Colors.transparent,
child: Chip(label: Text(widget.badge!)),
),
),
if (widget.showHideButton && widget.item.isMature)
Positioned(
top: 8,
left: 12,
child: Material(
color: Colors.transparent,
child: ActionChip(
visualDensity:
const VisualDensity(vertical: -4, horizontal: -4),
avatar: Icon(Icons.visibility_off,
color:
Theme.of(context).colorScheme.onSurfaceVariant),
label: Text('hide'.tr),
onPressed: () {
if (widget.onHide != null) widget.onHide!();
},
),
),
),
],
),
);
case 'video':
ensureInitVideo();
return Chewie(controller: _chewieController!);
default:
return Center(
child: Container(
constraints: const BoxConstraints(
maxWidth: 280,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.file_present, size: 32),
const SizedBox(height: 6),
Text(
widget.item.mimetype,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 2),
Text(
widget.item.alt,
style: const TextStyle(fontSize: 13),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
TextButton.icon(
icon: const Icon(Icons.launch),
label: Text('openInBrowser'.tr),
style: const ButtonStyle(
visualDensity: VisualDensity(vertical: -2, horizontal: -4),
),
onPressed: () {
launchUrlString(
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
);
},
),
],
),
),
);
}
}
@override
void dispose() {
_videoPlayerController?.dispose();
_chewieController?.dispose();
super.dispose();
}
}

View File

@ -57,7 +57,11 @@ class _AttachmentListState extends State<AttachmentList> {
int portrait = 0, square = 0, landscape = 0;
for (var entry in _attachmentsMeta) {
if (entry!.metadata?['ratio'] != null) {
consistentValue ??= entry.metadata?['ratio'];
if (entry.metadata?['ratio'] is int) {
consistentValue ??= entry.metadata?['ratio'].toDouble();
} else {
consistentValue ??= entry.metadata?['ratio'];
}
if (isConsistent && entry.metadata?['ratio'] != consistentValue) {
isConsistent = false;
}
@ -180,7 +184,7 @@ class _AttachmentListState extends State<AttachmentList> {
onTap: () {
if (!_showMature && _attachmentsMeta.any((e) => e!.isMature)) {
setState(() => _showMature = true);
} else {
} else if (['image'].contains(element.mimetype.split('/').first)) {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (context) => AttachmentListFullScreen(

View File

@ -8,13 +8,19 @@ import Foundation
import file_selector_macos
import flutter_local_notifications
import flutter_secure_storage_macos
import package_info_plus
import path_provider_foundation
import url_launcher_macos
import video_player_avfoundation
import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
}

View File

@ -49,6 +49,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
chewie:
dependency: "direct main"
description:
name: chewie
sha256: e53da939709efb9aad0f3d72a69a8d05f889168b7a138af60ce78bab5c94b135
url: "https://pub.dev"
source: hosted
version: "1.8.1"
clock:
dependency: transitive
description:
@ -81,6 +89,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
cupertino_icons:
dependency: "direct main"
description:
@ -328,6 +344,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.1.3"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
http:
dependency: transitive
description:
@ -520,6 +544,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
oauth2:
dependency: "direct main"
description:
@ -528,6 +560,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.2"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
url: "https://pub.dev"
source: hosted
version: "8.0.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
url: "https://pub.dev"
source: hosted
version: "3.0.0"
path:
dependency: "direct main"
description:
@ -656,6 +704,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
provider:
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev"
source: hosted
version: "6.1.2"
sky_engine:
dependency: transitive
description: flutter
@ -829,6 +885,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
video_player:
dependency: "direct main"
description:
name: video_player
sha256: db6a72d8f4fd155d0189845678f55ad2fd54b02c10dcafd11c068dbb631286c0
url: "https://pub.dev"
source: hosted
version: "2.8.6"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "134e1ad410d67e18a19486ed9512c72dfc6d8ffb284d0e8f2e99e903d1ba8fa3"
url: "https://pub.dev"
source: hosted
version: "2.4.14"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: d1e9a824f2b324000dc8fb2dcb2a3285b6c1c7c487521c63306cc5b394f68a7c
url: "https://pub.dev"
source: hosted
version: "2.6.1"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6"
url: "https://pub.dev"
source: hosted
version: "6.2.2"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
video_player_win:
dependency: "direct main"
description:
name: video_player_win
sha256: b99e8dfe7fa87a6732b5c91caef4955ae3453fd6255fb8860a7686beccdcfe43
url: "https://pub.dev"
source: hosted
version: "2.3.6"
vm_service:
dependency: transitive
description:
@ -837,6 +941,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.2.1"
wakelock_plus:
dependency: transitive
description:
name: wakelock_plus
sha256: "14758533319a462ffb5aa3b7ddb198e59b29ac3b02da14173a1715d65d4e6e68"
url: "https://pub.dev"
source: hosted
version: "1.2.5"
wakelock_plus_platform_interface:
dependency: transitive
description:
name: wakelock_plus_platform_interface
sha256: "422d1cdbb448079a8a62a5a770b69baa489f8f7ca21aef47800c726d404f9d16"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
web:
dependency: transitive
description:

View File

@ -57,6 +57,9 @@ dependencies:
permission_handler: ^11.3.1
uuid: ^4.4.0
dropdown_button2: ^2.3.9
video_player: ^2.8.6
video_player_win: ^2.3.6
chewie: ^1.8.1
dev_dependencies:
flutter_test:

View File

@ -10,6 +10,7 @@
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <video_player_win/video_player_win_plugin_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
@ -20,4 +21,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
VideoPlayerWinPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("VideoPlayerWinPluginCApi"));
}

View File

@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
permission_handler_windows
url_launcher_windows
video_player_win
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST