refa: video model (#523)

This commit is contained in:
My-Responsitories
2025-03-25 10:12:44 +08:00
committed by GitHub
parent bf464994df
commit 7a6085e923
52 changed files with 761 additions and 1494 deletions

View File

@@ -1,5 +1,6 @@
import 'package:PiliPlus/common/widgets/no_splash_factory.dart';
import 'package:PiliPlus/common/widgets/overlay_pop.dart';
import 'package:PiliPlus/models/model_video.dart';
import 'package:flutter/material.dart';
class AnimatedDialog extends StatefulWidget {
@@ -9,7 +10,7 @@ class AnimatedDialog extends StatefulWidget {
required this.closeFn,
});
final dynamic videoItem;
final BaseVideoItemModel videoItem;
final Function closeFn;
@override

View File

@@ -42,13 +42,10 @@ abstract class _StatItemBase extends StatelessWidget {
const SizedBox(width: 2),
Text(
Utils.numFormat(value),
style: TextStyle(
fontSize: size == 'medium' ? 12 : 11,
color: color,
),
style: TextStyle(fontSize: size == 'medium' ? 12 : 11, color: color),
overflow: TextOverflow.clip,
semanticsLabel: semanticsLabel,
),
)
],
);
}

View File

@@ -1,5 +1,7 @@
import 'package:PiliPlus/common/widgets/image_save.dart';
import 'package:PiliPlus/models/model_hot_video_item.dart';
import 'package:PiliPlus/models/model_video.dart';
import 'package:PiliPlus/models/search/result.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import '../../http/search.dart';
@@ -24,7 +26,7 @@ class VideoCardH extends StatelessWidget {
this.onLongPress,
this.onViewLater,
});
final dynamic videoItem;
final BaseVideoItemModel videoItem;
final String source;
final bool showOwner;
final bool showView;
@@ -36,12 +38,18 @@ class VideoCardH extends StatelessWidget {
@override
Widget build(BuildContext context) {
final int aid = videoItem.aid;
final String bvid = videoItem.bvid;
final int aid = videoItem.aid!;
final String bvid = videoItem.bvid!;
String type = 'video';
try {
type = videoItem.type;
} catch (_) {}
// try {
// type = videoItem.type;
// } catch (_) {}
if (videoItem is SearchVideoItemModel) {
var typeOrNull = (videoItem as SearchVideoItemModel).type;
if (typeOrNull?.isNotEmpty == true) {
type = typeOrNull!;
}
}
return Material(
color: Colors.transparent,
child: Stack(
@@ -61,13 +69,7 @@ class VideoCardH extends StatelessWidget {
} else {
imageSaveDialog(
context: context,
title: videoItem.title is String
? videoItem.title
: videoItem.title is List
? (videoItem.title as List)
.map((item) => item['text'])
.join()
: '',
title: videoItem.title,
cover: videoItem.pic,
);
}
@@ -81,9 +83,11 @@ class VideoCardH extends StatelessWidget {
SmartDialog.showToast('课堂视频暂不支持播放');
return;
}
if (videoItem is HotVideoItemModel &&
videoItem.redirectUrl?.isNotEmpty == true) {
if (Utils.viewPgcFromUri(videoItem.redirectUrl!)) {
if ((videoItem is HotVideoItemModel) &&
(videoItem as HotVideoItemModel).redirectUrl?.isNotEmpty ==
true) {
if (Utils.viewPgcFromUri(
(videoItem as HotVideoItemModel).redirectUrl!)) {
return;
}
}
@@ -124,20 +128,24 @@ class VideoCardH extends StatelessWidget {
return Stack(
children: [
NetworkImgLayer(
src: videoItem.pic as String,
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
if (videoItem is HotVideoItemModel &&
videoItem.pgcLabel?.isNotEmpty == true)
(videoItem as HotVideoItemModel)
.pgcLabel
?.isNotEmpty ==
true)
PBadge(
text: videoItem.pgcLabel,
text:
(videoItem as HotVideoItemModel).pgcLabel,
top: 6.0,
right: 6.0,
),
if (videoItem.duration != 0)
if (videoItem.duration > 0)
PBadge(
text: Utils.timeFormat(videoItem.duration!),
text: Utils.timeFormat(videoItem.duration),
right: 6.0,
bottom: 6.0,
type: 'gray',
@@ -180,7 +188,7 @@ class VideoCardH extends StatelessWidget {
);
}
Widget videoContent(context) {
Widget videoContent(BuildContext context) {
String pubdate = showPubdate
? Utils.dateFormat(videoItem.pubdate!, formatType: 'day')
: '';
@@ -189,7 +197,33 @@ class VideoCardH extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoItem.title is String)
if ((videoItem is SearchVideoItemModel) &&
(videoItem as SearchVideoItemModel).titleList?.isNotEmpty == true)
Expanded(
child: Text.rich(
overflow: TextOverflow.ellipsis,
maxLines: 2,
TextSpan(
children: [
for (var i
in (videoItem as SearchVideoItemModel).titleList!)
TextSpan(
text: i['text'],
style: TextStyle(
fontSize:
Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
],
),
),
)
else
Expanded(
child: Text(
videoItem.title,
@@ -202,31 +236,6 @@ class VideoCardH extends StatelessWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
)
else
Expanded(
child: Text.rich(
overflow: TextOverflow.ellipsis,
maxLines: 2,
TextSpan(
children: [
for (final i in videoItem.title) ...[
TextSpan(
text: i['text'] as String,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
]
],
),
),
),
// const Spacer(),
// if (videoItem.rcmdReason != null &&
@@ -267,7 +276,7 @@ class VideoCardH extends StatelessWidget {
StatView(
context: context,
theme: 'gray',
value: videoItem.stat.view!,
value: videoItem.stat.viewStr,
),
const SizedBox(width: 8),
],
@@ -275,7 +284,7 @@ class VideoCardH extends StatelessWidget {
StatDanMu(
context: context,
theme: 'gray',
value: videoItem.stat.danmu!,
value: videoItem.stat.danmuStr,
),
const Spacer(),
if (source == 'normal') const SizedBox(width: 24),

View File

@@ -42,12 +42,12 @@ class VideoCardHMemberVideo extends StatelessWidget {
return;
}
}
if (videoItem.bvid == null || videoItem.firstCid == null) {
if (videoItem.bvid == null || videoItem.cid == null) {
return;
}
try {
Utils.toViewPage(
'bvid=${videoItem.bvid}&cid=${videoItem.firstCid}',
'bvid=${videoItem.bvid}&cid=${videoItem.cid}',
arguments: {
'heroTag': Utils.makeHeroTag(videoItem.bvid),
},
@@ -90,7 +90,7 @@ class VideoCardHMemberVideo extends StatelessWidget {
right: 6.0,
top: 6.0,
),
if (videoItem.duration != null)
if (videoItem.duration > 0)
PBadge(
text: Utils.timeFormat(videoItem.duration),
right: 6.0,
@@ -147,7 +147,7 @@ class VideoCardHMemberVideo extends StatelessWidget {
Expanded(
child: Text(
// videoItem.season?['title'] ?? videoItem.title ?? '',
videoItem.title ?? '',
videoItem.title,
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
@@ -184,14 +184,14 @@ class VideoCardHMemberVideo extends StatelessWidget {
theme: 'gray',
// view: videoItem.season?['view_content'] ??
// videoItem.viewContent,
value: videoItem.viewContent!,
value: videoItem.stat.viewStr,
),
const SizedBox(width: 8),
StatDanMu(
context: context,
theme: 'gray',
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
value: videoItem.danmaku!,
value: videoItem.stat.danmuStr,
),
],
),

View File

@@ -16,7 +16,7 @@ import 'video_popup_menu.dart';
// 视频卡片 - 垂直布局
class VideoCardV extends StatelessWidget {
final dynamic videoItem;
final BaseRecVideoItemModel videoItem;
final VoidCallback? onRemove;
const VideoCardV({
@@ -31,14 +31,14 @@ class VideoCardV extends StatelessWidget {
}
void onPushDetail(heroTag) async {
String goto = videoItem.goto;
String goto = videoItem.goto!;
switch (goto) {
case 'bangumi':
Utils.viewBangumi(epId: videoItem.param);
Utils.viewBangumi(epId: videoItem.param!);
break;
case 'av':
String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid);
int cid = videoItem.cid;
String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid!);
int cid = videoItem.cid!;
if (cid == -1) {
cid = await SearchHttp.ab2c(aid: videoItem.aid, bvid: bvid);
}
@@ -55,13 +55,13 @@ class VideoCardV extends StatelessWidget {
case 'picture':
try {
String dynamicType = 'picture';
String uri = videoItem.uri;
String uri = videoItem.uri!;
String id = '';
if (videoItem.uri.startsWith('bilibili://article/')) {
if (uri.startsWith('bilibili://article/')) {
// https://www.bilibili.com/read/cv27063554
dynamicType = 'read';
RegExp regex = RegExp(r'\d+');
Match match = regex.firstMatch(videoItem.uri)!;
Match match = regex.firstMatch(uri)!;
String matchedNumber = match.group(0)!;
videoItem.param = int.parse(matchedNumber);
id = 'cv${videoItem.param}';
@@ -95,8 +95,8 @@ class VideoCardV extends StatelessWidget {
}
break;
default:
SmartDialog.showToast(videoItem.goto);
Utils.handleWebview(videoItem.uri);
SmartDialog.showToast(goto);
Utils.handleWebview(videoItem.uri!);
}
}
@@ -114,7 +114,7 @@ class VideoCardV extends StatelessWidget {
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: InkWell(
onTap: () => onPushDetail(Utils.makeHeroTag(videoItem.id)),
onTap: () => onPushDetail(Utils.makeHeroTag(videoItem.aid)),
onLongPress: () => imageSaveDialog(
context: context,
title: videoItem.title,
@@ -179,7 +179,7 @@ class VideoCardV extends StatelessWidget {
Row(
children: [
Expanded(
child: Text(videoItem.title + "\n",
child: Text("${videoItem.title}\n",
// semanticsLabel: "${videoItem.title}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
@@ -223,7 +223,7 @@ class VideoCardV extends StatelessWidget {
),
const SizedBox(width: 2),
],
if (videoItem.isFollowed == 1) ...[
if (videoItem.isFollowed) ...[
const PBadge(
text: '已关注',
stack: 'normal',
@@ -262,7 +262,7 @@ class VideoCardV extends StatelessWidget {
StatView(
context: context,
theme: 'gray',
value: videoItem.stat.view!,
value: videoItem.stat.viewStr,
goto: videoItem.goto,
),
const SizedBox(width: 4),
@@ -270,7 +270,7 @@ class VideoCardV extends StatelessWidget {
StatDanMu(
context: context,
theme: 'gray',
value: videoItem.stat.danmu!,
value: videoItem.stat.danmuStr,
),
if (videoItem is RecVideoItemModel) ...<Widget>[
const Spacer(),
@@ -294,7 +294,7 @@ class VideoCardV extends StatelessWidget {
],
if (videoItem is RecVideoItemAppModel &&
videoItem.desc != null &&
videoItem.desc.contains(' · ')) ...<Widget>[
videoItem.desc!.contains(' · ')) ...<Widget>[
const Spacer(),
Expanded(
flex: 0,
@@ -310,7 +310,7 @@ class VideoCardV extends StatelessWidget {
.withOpacity(0.8),
),
text: Utils.shortenChineseDateString(
videoItem.desc.split(' · ').last)),
videoItem.desc!.split(' · ').last)),
)),
const SizedBox(width: 2),
]

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/models/model_video.dart';
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
@@ -21,16 +22,16 @@ class VideoCustomAction {
}
class VideoCustomActions {
dynamic videoItem;
BaseSimpleVideoItemModel videoItem;
BuildContext context;
late List<VideoCustomAction> actions;
VoidCallback? onRemove;
VideoCustomActions(this.videoItem, this.context, [this.onRemove]) {
actions = [
if ((videoItem.bvid as String?)?.isNotEmpty == true) ...[
if (videoItem.bvid?.isNotEmpty == true) ...[
VideoCustomAction(
videoItem.bvid,
videoItem.bvid!,
'copy',
Stack(
children: [
@@ -39,7 +40,7 @@ class VideoCustomActions {
],
),
() {
Utils.copyText(videoItem.bvid);
Utils.copyText(videoItem.bvid!);
},
),
VideoCustomAction(
@@ -84,7 +85,7 @@ class VideoCustomActions {
SmartDialog.showToast("未能获取dislikeReasons或feedbacks");
return;
}
Widget actionButton(DislikeReason? r, FeedbackReason? f) {
Widget actionButton(Reason? r, Reason? f) {
return SearchText(
text: r?.name ?? f?.name ?? '未知',
onTap: (_) async {
@@ -258,11 +259,11 @@ class VideoCustomActions {
TextButton(
onPressed: () async {
var res = await VideoHttp.relationMod(
mid: videoItem.owner.mid,
mid: videoItem.owner.mid!,
act: 5,
reSrc: 11,
);
GStorage.setBlackMid(videoItem.owner.mid);
GStorage.setBlackMid(videoItem.owner.mid!);
Get.back();
SmartDialog.showToast(res['msg'] ?? '成功');
},