276 lines
8.6 KiB
Dart
276 lines
8.6 KiB
Dart
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:loopin/api/common_api.dart';
|
|
import 'package:loopin/api/video_api.dart';
|
|
import 'package:loopin/service/http.dart';
|
|
import 'package:loopin/utils/index.dart';
|
|
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
|
|
|
class UploadVideoPage extends StatefulWidget {
|
|
const UploadVideoPage({super.key});
|
|
|
|
@override
|
|
State<UploadVideoPage> createState() => _UploadVideoPageState();
|
|
}
|
|
|
|
class _UploadVideoPageState extends State<UploadVideoPage> {
|
|
final selectedVideo = Rxn<AssetEntity>();
|
|
final selectedCover = Rxn<AssetEntity>();
|
|
final uploading = false.obs;
|
|
final uploadProgress = 0.0.obs;
|
|
final status = ''.obs;
|
|
final descController = TextEditingController();
|
|
final FocusNode descFocusNode = FocusNode();
|
|
final uploadedVideoUrl = ''.obs;
|
|
final TextEditingController descriptionController = TextEditingController();
|
|
|
|
Future<void> pickVideo() async {
|
|
final result = await PhotoManager.requestPermissionExtend();
|
|
|
|
if (!result.isAuth) {
|
|
if (!mounted) return;
|
|
Get.snackbar('权限被拒绝', '请前往设置授权访问视频');
|
|
return;
|
|
}
|
|
|
|
if (!mounted) return;
|
|
|
|
final pickedAssets = await AssetPicker.pickAssets(
|
|
context,
|
|
pickerConfig: AssetPickerConfig(
|
|
textDelegate: const AssetPickerTextDelegate(),
|
|
pathNameBuilder: (AssetPathEntity album) {
|
|
return Utils.translateAlbumName(album);
|
|
},
|
|
maxAssets: 1,
|
|
requestType: RequestType.video,
|
|
filterOptions: FilterOptionGroup(
|
|
videoOption: const FilterOption(
|
|
durationConstraint: DurationConstraint(
|
|
max: Duration(seconds: 30),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
|
selectedVideo.value = pickedAssets.first;
|
|
status.value = '已选择视频';
|
|
}
|
|
}
|
|
|
|
Future<void> pickCoverImage() async {
|
|
final result = await PhotoManager.requestPermissionExtend();
|
|
|
|
if (!result.isAuth) {
|
|
if (!mounted) return;
|
|
Get.snackbar('权限被拒绝', '请前往设置授权访问照片');
|
|
return;
|
|
}
|
|
|
|
if (!mounted) return;
|
|
|
|
final pickedAssets = await AssetPicker.pickAssets(
|
|
context,
|
|
pickerConfig: AssetPickerConfig(
|
|
textDelegate: const AssetPickerTextDelegate(),
|
|
pathNameBuilder: (AssetPathEntity album) {
|
|
return Utils.translateAlbumName(album);
|
|
},
|
|
maxAssets: 1,
|
|
requestType: RequestType.image,
|
|
),
|
|
);
|
|
|
|
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
|
selectedCover.value = pickedAssets.first;
|
|
}
|
|
}
|
|
|
|
Future<void> uploadVideo() async {
|
|
final file = await selectedVideo.value?.file;
|
|
final coverFile = await selectedCover.value?.file;
|
|
final desc = descController.text.trim();
|
|
|
|
if (file == null) {
|
|
status.value = '未选择视频';
|
|
return;
|
|
}
|
|
|
|
if (desc.isEmpty) {
|
|
Get.snackbar('请填写描述', '视频描述不能为空');
|
|
return;
|
|
}
|
|
|
|
uploading.value = true;
|
|
uploadProgress.value = 0;
|
|
status.value = '上传中...';
|
|
|
|
try {
|
|
final data = await Http.upload(
|
|
CommonApi.uploadFile,
|
|
filePath: file.path,
|
|
fileKey: 'file',
|
|
onSendProgress: (sent, total) {
|
|
if (total > 0) {
|
|
uploadProgress.value = sent / total;
|
|
}
|
|
},
|
|
);
|
|
uploadedVideoUrl.value = data;
|
|
status.value = '上传成功';
|
|
// 清空表单
|
|
descController.clear();
|
|
selectedVideo.value = null;
|
|
selectedCover.value = null;
|
|
uploadedVideoUrl.value = '';
|
|
} catch (e) {
|
|
if (e is SocketException) {
|
|
status.value = '网络错误,请检查连接';
|
|
} else {
|
|
status.value = '上传失败: ${e.toString()}';
|
|
}
|
|
} finally {
|
|
uploading.value = false;
|
|
}
|
|
}
|
|
|
|
Future<void> submitForm() async {
|
|
if (uploadedVideoUrl.value.isEmpty) {
|
|
Get.snackbar('请先上传视频', '未检测到上传的视频');
|
|
return;
|
|
}
|
|
|
|
if (descriptionController.text.trim().isEmpty) {
|
|
Get.snackbar('请填写视频描述', '描述是必填项');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await Http.post(VideoApi.publish, data: {
|
|
'video_url': uploadedVideoUrl.value,
|
|
'desc': descriptionController.text.trim(),
|
|
'cover': selectedCover.value,
|
|
});
|
|
Get.snackbar('发布成功', '视频已成功发布');
|
|
clearForm();
|
|
} catch (e) {
|
|
Get.snackbar('发布失败', '$e');
|
|
}
|
|
}
|
|
|
|
void clearForm() {
|
|
selectedVideo.value = null;
|
|
selectedCover.value = null;
|
|
descriptionController.clear();
|
|
status.value = '';
|
|
uploadProgress.value = 0.0;
|
|
uploadedVideoUrl.value = '';
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
descController.dispose();
|
|
descFocusNode.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
FocusScope.of(context).unfocus();
|
|
},
|
|
child: Scaffold(
|
|
appBar: AppBar(title: const Text('上传视频')),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
children: [
|
|
TextField(
|
|
focusNode: descFocusNode,
|
|
controller: descController,
|
|
decoration: const InputDecoration(
|
|
labelText: '视频描述 *',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
maxLines: 2,
|
|
),
|
|
const SizedBox(height: 20),
|
|
Obx(() {
|
|
final video = selectedVideo.value;
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
video != null ? '已选择视频:${video.title}' : '未选择视频',
|
|
style: const TextStyle(fontSize: 16),
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: pickVideo,
|
|
child: const Text('选择视频'),
|
|
),
|
|
],
|
|
);
|
|
}),
|
|
const SizedBox(height: 20),
|
|
Obx(() {
|
|
final cover = selectedCover.value;
|
|
return Row(
|
|
children: [
|
|
cover != null
|
|
? FutureBuilder<Uint8List?>(
|
|
future: cover.thumbnailDataWithSize(const ThumbnailSize(80, 80)),
|
|
builder: (_, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Image.memory(snapshot.data!, width: 80, height: 80),
|
|
);
|
|
} else {
|
|
return const SizedBox(width: 80, height: 80);
|
|
}
|
|
},
|
|
)
|
|
: const Text('未选择封面图'),
|
|
const SizedBox(width: 12),
|
|
ElevatedButton(
|
|
onPressed: pickCoverImage,
|
|
child: const Text('选择封面(可选)'),
|
|
),
|
|
],
|
|
);
|
|
}),
|
|
const SizedBox(height: 30),
|
|
Obx(() => uploading.value
|
|
? Column(
|
|
children: [
|
|
const CircularProgressIndicator(),
|
|
const SizedBox(height: 10),
|
|
LinearProgressIndicator(value: uploadProgress.value),
|
|
const SizedBox(height: 5),
|
|
Text('${(uploadProgress.value * 100).toStringAsFixed(1)}%'),
|
|
],
|
|
)
|
|
: SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton.icon(
|
|
onPressed: uploadVideo,
|
|
icon: const Icon(Icons.upload),
|
|
label: const Text('上传'),
|
|
),
|
|
)),
|
|
const SizedBox(height: 20),
|
|
Obx(() => Text(status.value)),
|
|
],
|
|
),
|
|
),
|
|
));
|
|
}
|
|
}
|