opt: bangumi follow status

This commit is contained in:
guozhigq
2024-10-27 14:39:21 +08:00
parent b9ef5bae4e
commit 16a8cc8d68
5 changed files with 206 additions and 28 deletions

View File

@ -604,4 +604,7 @@ class Api {
/// 图片上传
static const String uploadImage = '/x/dynamic/feed/draw/upload_bfs';
/// 更新追番状态
static const String updateBangumiStatus = '/pgc/web/follow/status/update';
}

View File

@ -1,5 +1,8 @@
import 'dart:convert';
import '../models/bangumi/list.dart';
import 'index.dart';
import 'package:html/parser.dart' as html_parser;
import 'package:html/dom.dart' as html_dom;
class BangumiHttp {
static Future bangumiList({int? page}) async {
@ -33,4 +36,49 @@ class BangumiHttp {
};
}
}
// 获取追番状态
static Future bangumiStatus({required int seasonId}) async {
var res = await Request()
.get('https://www.bilibili.com/bangumi/play/ss$seasonId');
html_dom.Document document = html_parser.parse(res.data);
// 查找 id 为 __NEXT_DATA__ 的 script 元素
html_dom.Element? scriptElement =
document.querySelector('script#\\__NEXT_DATA__');
if (scriptElement != null) {
// 提取 script 元素的内容
String scriptContent = scriptElement.text;
final dynamic scriptContentJson = jsonDecode(scriptContent);
Map followState = scriptContentJson['props']['pageProps']['followState'];
return {
'status': true,
'data': {
'isFollowed': followState['isFollowed'],
'followStatus': followState['followStatus']
}
};
} else {
print('Script element with id "__NEXT_DATA__" not found.');
}
}
// 更新追番状态
static Future updateBangumiStatus({
required int seasonId,
required int status,
}) async {
var res = await Request().post(Api.updateBangumiStatus, data: {
'season_id': seasonId,
'status': status,
});
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/bangumi.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/http/video.dart';
@ -52,17 +53,27 @@ class BangumiIntroController extends GetxController {
Rx<FavFolderData> favFolderData = FavFolderData().obs;
List addMediaIdsNew = [];
List delMediaIdsNew = [];
// 关注状态 默认未关注
RxMap followStatus = {}.obs;
// 追番状态 1想看 2在看 3已看
RxBool isFollowed = false.obs;
RxInt followStatus = 1.obs;
int _tempThemeValue = -1;
var userInfo;
PersistentBottomSheetController? bottomSheetController;
List<Map<String, dynamic>> followStatusList = [
{'title': '标记为 「想看」', 'status': 1},
{'title': '标记为 「在看」', 'status': 2},
{'title': '标记为 「已看」', 'status': 3},
{'title': '取消追番', 'status': -1},
];
@override
void onInit() {
super.onInit();
userInfo = userInfoCache.get('userInfoCache');
userLogin = userInfo != null;
if (userLogin && seasonId != null) {
bangumiStatus();
}
}
// 获取番剧简介&选集
@ -239,15 +250,22 @@ class BangumiIntroController extends GetxController {
// 追番
Future bangumiAdd() async {
var result =
await VideoHttp.bangumiAdd(seasonId: bangumiDetail.value.seasonId);
var result = await VideoHttp.bangumiAdd(
seasonId: seasonId ?? bangumiDetail.value.seasonId);
if (result['status']) {
followStatus.value = 2;
isFollowed.value = true;
}
SmartDialog.showToast(result['msg']);
}
// 取消追番
Future bangumiDel() async {
var result =
await VideoHttp.bangumiDel(seasonId: bangumiDetail.value.seasonId);
var result = await VideoHttp.bangumiDel(
seasonId: seasonId ?? bangumiDetail.value.seasonId);
if (result['status']) {
isFollowed.value = false;
}
SmartDialog.showToast(result['msg']);
}
@ -315,4 +333,35 @@ class BangumiIntroController extends GetxController {
hiddenEpisodeBottomSheet() {
bottomSheetController?.close();
}
// 获取追番状态
Future bangumiStatus() async {
var result = await BangumiHttp.bangumiStatus(seasonId: seasonId!);
if (result['status']) {
followStatus.value = result['data']['followStatus'];
isFollowed.value = result['data']['isFollowed'];
}
return result;
}
// 更新追番状态
Future updateBangumiStatus(int status) async {
Get.back();
if (status == -1) {
bangumiDel();
} else {
var result = await BangumiHttp.bangumiStatus(seasonId: seasonId!);
if (result['status']) {
followStatus.value = status;
final title = followStatusList.firstWhere(
(e) => e['status'] == status,
orElse: () => {'title': '未知状态'},
)['title'];
SmartDialog.showToast('追番状态$title');
} else {
SmartDialog.showToast(result['msg']);
}
return result;
}
}
}

View File

@ -259,27 +259,11 @@ class _BangumiInfoState extends State<BangumiInfo> {
),
),
const SizedBox(width: 20),
SizedBox(
width: 34,
height: 34,
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith(
(Set<MaterialState> states) {
return t.colorScheme.primaryContainer
.withOpacity(0.7);
}),
),
onPressed: () =>
bangumiIntroController.bangumiAdd(),
icon: Icon(
Icons.favorite_border_rounded,
color: t.colorScheme.primary,
size: 22,
),
Obx(
() => BangumiStatusWidget(
ctr: bangumiIntroController,
isFollowed:
bangumiIntroController.isFollowed.value,
),
),
],
@ -426,3 +410,97 @@ class _BangumiInfoState extends State<BangumiInfo> {
});
}
}
// 追番状态
class BangumiStatusWidget extends StatelessWidget {
final BangumiIntroController ctr;
final bool isFollowed;
const BangumiStatusWidget({
Key? key,
required this.ctr,
required this.isFollowed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
ColorScheme colorScheme = Theme.of(context).colorScheme;
void updateFollowStatus() {
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return morePanel(context, ctr);
},
);
}
return Obx(
() => SizedBox(
width: 34,
height: 34,
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
backgroundColor:
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
return ctr.isFollowed.value
? colorScheme.primaryContainer.withOpacity(0.7)
: colorScheme.outlineVariant.withOpacity(0.7);
}),
),
onPressed:
isFollowed ? () => updateFollowStatus() : () => ctr.bangumiAdd(),
icon: Icon(
ctr.isFollowed.value
? Icons.favorite
: Icons.favorite_border_rounded,
color: ctr.isFollowed.value
? colorScheme.primary
: colorScheme.outline,
size: 22,
),
),
),
);
}
Widget morePanel(BuildContext context, BangumiIntroController ctr) {
return Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () => Get.back(),
child: Container(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
child: Container(
width: 32,
height: 3,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.outline,
borderRadius: const BorderRadius.all(Radius.circular(3))),
),
),
),
),
...ctr.followStatusList
.map(
(e) => ListTile(
onTap: () => ctr.updateBangumiStatus(e['status']),
selected: ctr.followStatus == e['status'],
title: Text(e['title']),
),
)
.toList(),
const SizedBox(height: 20),
],
),
);
}
}

View File

@ -31,7 +31,7 @@ class RoutePush {
};
arguments['heroTag'] = heroTag ?? Utils.makeHeroTag(cid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid&epId=$epId',
'/video?bvid=$bvid&cid=$cid&epId=$epId&seasonId=$seasonId',
arguments: arguments,
);
} else {