mod: 合集season数据解析
This commit is contained in:
@ -60,6 +60,7 @@ class VideoDetailData {
|
|||||||
List<Page>? pages;
|
List<Page>? pages;
|
||||||
Subtitle? subtitle;
|
Subtitle? subtitle;
|
||||||
// Label? label;
|
// Label? label;
|
||||||
|
UgcSeason? ugcSeason;
|
||||||
bool? isSeasonDisplay;
|
bool? isSeasonDisplay;
|
||||||
UserGarb? userGarb;
|
UserGarb? userGarb;
|
||||||
HonorReply? honorReply;
|
HonorReply? honorReply;
|
||||||
@ -94,6 +95,7 @@ class VideoDetailData {
|
|||||||
this.noCache,
|
this.noCache,
|
||||||
this.pages,
|
this.pages,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.ugcSeason,
|
||||||
this.isSeasonDisplay,
|
this.isSeasonDisplay,
|
||||||
this.userGarb,
|
this.userGarb,
|
||||||
this.honorReply,
|
this.honorReply,
|
||||||
@ -137,6 +139,9 @@ class VideoDetailData {
|
|||||||
: List<Page>.from(json["pages"]!.map((e) => Page.fromJson(e)));
|
: List<Page>.from(json["pages"]!.map((e) => Page.fromJson(e)));
|
||||||
subtitle =
|
subtitle =
|
||||||
json["subtitle"] == null ? null : Subtitle.fromJson(json["subtitle"]);
|
json["subtitle"] == null ? null : Subtitle.fromJson(json["subtitle"]);
|
||||||
|
ugcSeason = json["ugc_season"] != null
|
||||||
|
? UgcSeason.fromJson(json["ugc_season"])
|
||||||
|
: null;
|
||||||
isSeasonDisplay = json["is_season_display"];
|
isSeasonDisplay = json["is_season_display"];
|
||||||
userGarb =
|
userGarb =
|
||||||
json["user_garb"] == null ? null : UserGarb.fromJson(json["user_garb"]);
|
json["user_garb"] == null ? null : UserGarb.fromJson(json["user_garb"]);
|
||||||
@ -522,3 +527,111 @@ class UserGarb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Label {}
|
class Label {}
|
||||||
|
|
||||||
|
class UgcSeason {
|
||||||
|
UgcSeason({
|
||||||
|
this.id,
|
||||||
|
this.title,
|
||||||
|
this.cover,
|
||||||
|
this.mid,
|
||||||
|
this.intro,
|
||||||
|
this.signState,
|
||||||
|
this.attribute,
|
||||||
|
this.sections,
|
||||||
|
this.stat,
|
||||||
|
this.epCount,
|
||||||
|
this.seasonType,
|
||||||
|
this.isPaySeason,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? id;
|
||||||
|
String? title;
|
||||||
|
String? cover;
|
||||||
|
int? mid;
|
||||||
|
String? intro;
|
||||||
|
int? signState;
|
||||||
|
int? attribute;
|
||||||
|
List<SectionItem>? sections;
|
||||||
|
Stat? stat;
|
||||||
|
int? epCount;
|
||||||
|
int? seasonType;
|
||||||
|
bool? isPaySeason;
|
||||||
|
|
||||||
|
UgcSeason.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['id'];
|
||||||
|
title = json['title'];
|
||||||
|
cover = json['cover'];
|
||||||
|
mid = json['mid'];
|
||||||
|
intro = json['intro'];
|
||||||
|
signState = json['sign_state'];
|
||||||
|
attribute = json['attribute'];
|
||||||
|
sections = json['sections']
|
||||||
|
.map<SectionItem>((e) => SectionItem.fromJson(e))
|
||||||
|
.toList();
|
||||||
|
stat = Stat.fromJson(json['stat']);
|
||||||
|
epCount = json['ep_count'];
|
||||||
|
seasonType = json['season_type'];
|
||||||
|
isPaySeason = json['is_pay_count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SectionItem {
|
||||||
|
SectionItem({
|
||||||
|
this.seasonId,
|
||||||
|
this.id,
|
||||||
|
this.title,
|
||||||
|
this.type,
|
||||||
|
this.episodes,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? seasonId;
|
||||||
|
int? id;
|
||||||
|
String? title;
|
||||||
|
int? type;
|
||||||
|
List<EpisodeItem>? episodes;
|
||||||
|
|
||||||
|
SectionItem.fromJson(Map<String, dynamic> json) {
|
||||||
|
seasonId = json['season_id'];
|
||||||
|
id = json['id'];
|
||||||
|
title = json['title'];
|
||||||
|
type = json['type'];
|
||||||
|
episodes = json['episodes']
|
||||||
|
.map<EpisodeItem>((e) => EpisodeItem.fromJson(e))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EpisodeItem {
|
||||||
|
EpisodeItem({
|
||||||
|
this.seasonId,
|
||||||
|
this.sectionId,
|
||||||
|
this.id,
|
||||||
|
this.aid,
|
||||||
|
this.cid,
|
||||||
|
this.title,
|
||||||
|
this.attribute,
|
||||||
|
this.page,
|
||||||
|
this.bvid,
|
||||||
|
});
|
||||||
|
int? seasonId;
|
||||||
|
int? sectionId;
|
||||||
|
int? id;
|
||||||
|
int? aid;
|
||||||
|
int? cid;
|
||||||
|
String? title;
|
||||||
|
int? attribute;
|
||||||
|
Page? page;
|
||||||
|
String? bvid;
|
||||||
|
|
||||||
|
EpisodeItem.fromJson(Map<String, dynamic> json) {
|
||||||
|
seasonId = json['season_id'];
|
||||||
|
sectionId = json['sectionId'];
|
||||||
|
id = json['id'];
|
||||||
|
aid = json['aid'];
|
||||||
|
cid = json['cid'];
|
||||||
|
title = json['title'];
|
||||||
|
attribute = json['attribute'];
|
||||||
|
page = Page.fromJson(json['page']);
|
||||||
|
bvid = json['bvid'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -11,7 +12,10 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var i = list![index];
|
var i = list![index];
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
|
Get.toNamed('/video?bvid=${i.bvid}&cid=${i.cid}',
|
||||||
|
arguments: {'videoItem': i, 'heroTag': Utils.makeHeroTag(i.id)});
|
||||||
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -113,7 +113,6 @@ class VideoDetailController extends GetxController {
|
|||||||
|
|
||||||
// 视频链接
|
// 视频链接
|
||||||
queryVideoUrl() async {
|
queryVideoUrl() async {
|
||||||
print('🐶🐶🐶');
|
|
||||||
var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid);
|
var result = await VideoHttp.videoUrl(cid: cid, bvid: bvid);
|
||||||
// log('result: ${result.toString()}');
|
// log('result: ${result.toString()}');
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
|
@ -76,7 +76,7 @@ class VideoIntroController extends GetxController {
|
|||||||
userLogin = user.get(UserBoxKey.userLogin) != null;
|
userLogin = user.get(UserBoxKey.userLogin) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取视频简介
|
// 获取视频简介&分p
|
||||||
Future queryVideoIntro() async {
|
Future queryVideoIntro() async {
|
||||||
var result = await VideoHttp.videoIntro(bvid: bvid);
|
var result = await VideoHttp.videoIntro(bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
|
@ -16,6 +16,8 @@ import 'package:pilipala/pages/video/detail/introduction/controller.dart';
|
|||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
|
import 'widgets/season.dart';
|
||||||
|
|
||||||
class VideoIntroPanel extends StatefulWidget {
|
class VideoIntroPanel extends StatefulWidget {
|
||||||
const VideoIntroPanel({Key? key}) : super(key: key);
|
const VideoIntroPanel({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -342,7 +344,14 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
// 点赞收藏转发
|
||||||
_actionGrid(context, videoIntroController),
|
_actionGrid(context, videoIntroController),
|
||||||
|
// 合集
|
||||||
|
if (!widget.loadingStatus &&
|
||||||
|
widget.videoDetail!.ugcSeason != null) ...[
|
||||||
|
seasonPanel(widget.videoDetail!.ugcSeason!,
|
||||||
|
widget.videoDetail!.pages!.first.cid)
|
||||||
|
],
|
||||||
Divider(
|
Divider(
|
||||||
height: 26,
|
height: 26,
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||||
|
99
lib/pages/video/detail/introduction/widgets/season.dart
Normal file
99
lib/pages/video/detail/introduction/widgets/season.dart
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/models/video_detail_res.dart';
|
||||||
|
|
||||||
|
Widget seasonPanel(UgcSeason ugcSeason, cid) {
|
||||||
|
return Builder(builder: (context) {
|
||||||
|
List<EpisodeItem> episodes = ugcSeason.sections!.first.episodes!;
|
||||||
|
int currentIndex = episodes.indexWhere((e) => e.cid == cid);
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
top: 15,
|
||||||
|
left: 2,
|
||||||
|
right: 2,
|
||||||
|
),
|
||||||
|
child: Material(
|
||||||
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => showBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => Container(
|
||||||
|
height: Get.size.height - Get.size.width * 9 / 16 - 45,
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 45,
|
||||||
|
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'合集(${episodes.length})',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Material(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: episodes.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10, bottom: 10, left: 15, right: 15),
|
||||||
|
child: Text(
|
||||||
|
episodes[index].title!,
|
||||||
|
style: TextStyle(
|
||||||
|
color: index == currentIndex
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSurface),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('合集:${ugcSeason.title!}'),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
'${currentIndex + 1} / ${ugcSeason.epCount}',
|
||||||
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_forward_ios_outlined,
|
||||||
|
size: 13,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -59,6 +59,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
Wakelock.disable();
|
Wakelock.disable();
|
||||||
}
|
}
|
||||||
|
// 播放完成停止 or 切换下一个
|
||||||
|
if (status == PlayerStatus.completed) {
|
||||||
|
_meeduPlayerController!.pause();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -67,8 +71,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
|
|
||||||
_extendNestCtr.addListener(
|
_extendNestCtr.addListener(
|
||||||
() {
|
() {
|
||||||
print(_extendNestCtr.position.pixels);
|
|
||||||
|
|
||||||
double offset = _extendNestCtr.position.pixels;
|
double offset = _extendNestCtr.position.pixels;
|
||||||
if (offset > doubleOffset) {
|
if (offset > doubleOffset) {
|
||||||
animationController.forward();
|
animationController.forward();
|
||||||
|
Reference in New Issue
Block a user