Merge branch 'main' into design

This commit is contained in:
guozhigq
2024-11-13 00:34:41 +08:00
97 changed files with 911 additions and 407 deletions

View File

@ -3,7 +3,9 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:scrollview_observer/scrollview_observer.dart';
import '../models/common/video_episode_type.dart';
@ -20,6 +22,8 @@ class EpisodeBottomSheet {
final double? sheetHeight;
bool isFullScreen = false;
final UgcSeason? ugcSeason;
final int? currentEpisodeIndex;
final int? currentIndex;
EpisodeBottomSheet({
required this.episodes,
@ -30,6 +34,8 @@ class EpisodeBottomSheet {
this.sheetHeight,
this.isFullScreen = false,
this.ugcSeason,
this.currentEpisodeIndex,
this.currentIndex,
});
Widget buildShowContent() {
@ -42,6 +48,8 @@ class EpisodeBottomSheet {
sheetHeight: sheetHeight,
isFullScreen: isFullScreen,
ugcSeason: ugcSeason,
currentEpisodeIndex: currentEpisodeIndex,
currentIndex: currentIndex,
);
}
@ -67,6 +75,8 @@ class PagesBottomSheet extends StatefulWidget {
this.sheetHeight,
this.isFullScreen = false,
this.ugcSeason,
this.currentEpisodeIndex,
this.currentIndex,
});
final List<dynamic> episodes;
@ -77,41 +87,38 @@ class PagesBottomSheet extends StatefulWidget {
final double? sheetHeight;
final bool isFullScreen;
final UgcSeason? ugcSeason;
final int? currentEpisodeIndex;
final int? currentIndex;
@override
State<PagesBottomSheet> createState() => _PagesBottomSheetState();
}
class _PagesBottomSheetState extends State<PagesBottomSheet> {
class _PagesBottomSheetState extends State<PagesBottomSheet>
with TickerProviderStateMixin {
final ScrollController _listScrollController = ScrollController();
late ListObserverController _listObserverController;
final ScrollController _scrollController = ScrollController();
late int currentIndex;
TabController? tabController;
List<ListObserverController>? _listObserverControllerList;
List<ScrollController>? _listScrollControllerList;
final String heroTag = Get.arguments['heroTag'];
VideoDetailController? _videoDetailController;
RxInt isSubscribe = (-1).obs;
bool isVisible = false;
@override
void initState() {
super.initState();
currentIndex =
currentIndex = widget.currentIndex ??
widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid);
_listObserverController =
ListObserverController(controller: _listScrollController);
_scrollToInit();
_scrollPositionInit();
if (widget.dataType == VideoEpidoesType.videoEpisode) {
_listObserverController.initialIndexModel = ObserverIndexPositionModel(
index: currentIndex,
isFixedHeight: true,
);
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
_getSubscribeStatus();
}
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.dataType != VideoEpidoesType.videoEpisode) {
double itemHeight = (widget.isFullScreen
? 400
: Get.size.width - 3 * StyleString.safeSpace) /
5.2;
double offset = ((currentIndex - 1) / 2).ceil() * itemHeight;
_scrollController.jumpTo(offset);
}
});
}
String prefix() {
@ -126,9 +133,117 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
return '选集';
}
// 滚动器初始化
void _scrollToInit() {
/// 单个
_listObserverController =
ListObserverController(controller: _listScrollController);
if (widget.dataType == VideoEpidoesType.videoEpisode &&
widget.ugcSeason?.sections != null &&
widget.ugcSeason!.sections!.length > 1) {
tabController = TabController(
length: widget.ugcSeason!.sections!.length,
vsync: this,
initialIndex: widget.currentEpisodeIndex ?? 0,
);
/// 多tab
_listScrollControllerList = List.generate(
widget.ugcSeason!.sections!.length,
(index) {
return ScrollController();
},
);
_listObserverControllerList = List.generate(
widget.ugcSeason!.sections!.length,
(index) {
return ListObserverController(
controller: _listScrollControllerList![index],
);
},
);
}
}
// 滚动器位置初始化
void _scrollPositionInit() {
if (widget.dataType == VideoEpidoesType.videoEpisode) {
// 单个 多tab
if (widget.ugcSeason?.sections != null) {
if (widget.ugcSeason!.sections!.length == 1) {
_listObserverController.initialIndexModel =
ObserverIndexPositionModel(
index: currentIndex,
isFixedHeight: true,
);
} else {
_listObserverControllerList![widget.currentEpisodeIndex!]
.initialIndexModel = ObserverIndexPositionModel(
index: currentIndex,
isFixedHeight: true,
);
}
}
}
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.dataType != VideoEpidoesType.videoEpisode) {
double itemHeight = (widget.isFullScreen
? 400
: Get.size.width - 3 * StyleString.safeSpace) /
5.2;
double offset = ((currentIndex - 1) / 2).ceil() * itemHeight;
_scrollController.jumpTo(offset);
}
});
}
// 获取订阅状态
void _getSubscribeStatus() async {
var res =
await VideoHttp.getSubscribeStatus(bvid: _videoDetailController!.bvid);
if (res['status']) {
isSubscribe.value = res['data']['season_fav'] ? 1 : 0;
}
}
// 更改订阅状态
void _changeSubscribeStatus() async {
if (isSubscribe.value == -1) {
return;
}
dynamic result = await VideoHttp.seasonFav(
isFav: isSubscribe.value == 1,
seasonId: widget.ugcSeason!.id,
);
if (result['status']) {
SmartDialog.showToast(isSubscribe.value == 1 ? '取消订阅成功' : '订阅成功');
isSubscribe.value = isSubscribe.value == 1 ? 0 : 1;
} else {
SmartDialog.showToast(result['msg']);
}
}
// 更改展开状态
void _changeVisible() {
setState(() {
isVisible = !isVisible;
});
}
@override
void dispose() {
_listObserverController.controller?.dispose();
try {
_listObserverController.controller?.dispose();
_listScrollController.dispose();
for (var element in _listObserverControllerList!) {
element.controller?.dispose();
}
for (var element in _listScrollControllerList!) {
element.dispose();
}
} catch (_) {}
super.dispose();
}
@ -145,36 +260,46 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
isFullScreen: widget.isFullScreen,
),
if (widget.ugcSeason != null) ...[
UgcSeasonBuild(ugcSeason: widget.ugcSeason!),
UgcSeasonBuild(
ugcSeason: widget.ugcSeason!,
isSubscribe: isSubscribe,
isVisible: isVisible,
changeFucCall: _changeSubscribeStatus,
changeVisible: _changeVisible,
),
],
Expanded(
child: Material(
child: widget.dataType == VideoEpidoesType.videoEpisode
? ListViewObserver(
controller: _listObserverController,
child: ListView.builder(
controller: _listScrollController,
itemCount: widget.episodes.length + 1,
itemBuilder: (BuildContext context, int index) {
bool isLastItem = index == widget.episodes.length;
bool isCurrentIndex = currentIndex == index;
return isLastItem
? SizedBox(
height:
MediaQuery.of(context).padding.bottom +
? (widget.ugcSeason!.sections!.length == 1
? ListViewObserver(
controller: _listObserverController,
child: ListView.builder(
controller: _listScrollController,
itemCount: widget.episodes.length + 1,
itemBuilder: (BuildContext context, int index) {
bool isLastItem =
index == widget.episodes.length;
bool isCurrentIndex = currentIndex == index;
return isLastItem
? SizedBox(
height: MediaQuery.of(context)
.padding
.bottom +
20,
)
: EpisodeListItem(
episode: widget.episodes[index],
index: index,
isCurrentIndex: isCurrentIndex,
dataType: widget.dataType,
changeFucCall: widget.changeFucCall,
isFullScreen: widget.isFullScreen,
);
},
),
)
)
: EpisodeListItem(
episode: widget.episodes[index],
index: index,
isCurrentIndex: isCurrentIndex,
dataType: widget.dataType,
changeFucCall: widget.changeFucCall,
isFullScreen: widget.isFullScreen,
);
},
),
)
: buildTabBar())
: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0), // 设置左右间距为12
@ -206,6 +331,65 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
);
});
}
Widget buildTabBar() {
return Column(
children: [
// 背景色
Container(
color: Theme.of(context).colorScheme.surface,
child: TabBar(
controller: tabController,
isScrollable: true,
indicatorSize: TabBarIndicatorSize.label,
tabAlignment: TabAlignment.start,
splashBorderRadius: BorderRadius.circular(4),
tabs: [
...widget.ugcSeason!.sections!.map((SectionItem section) {
return Tab(
text: section.title,
);
}).toList()
],
),
),
Expanded(
child: TabBarView(
controller: tabController,
children: [
...widget.ugcSeason!.sections!.map((SectionItem section) {
final int fIndex = widget.ugcSeason!.sections!.indexOf(section);
return ListViewObserver(
controller: _listObserverControllerList![fIndex],
child: ListView.builder(
controller: _listScrollControllerList![fIndex],
itemCount: section.episodes!.length + 1,
itemBuilder: (BuildContext context, int index) {
final bool isLastItem = index == section.episodes!.length;
return isLastItem
? SizedBox(
height:
MediaQuery.of(context).padding.bottom + 20,
)
: EpisodeListItem(
episode: section.episodes![index], // 调整索引
index: index, // 调整索引
isCurrentIndex: widget.currentCid ==
section.episodes![index].cid,
dataType: widget.dataType,
changeFucCall: widget.changeFucCall,
isFullScreen: widget.isFullScreen,
);
},
),
);
}).toList()
],
),
),
],
);
}
}
class TitleBar extends StatelessWidget {
@ -507,77 +691,134 @@ class EpisodeGridItem extends StatelessWidget {
class UgcSeasonBuild extends StatelessWidget {
final UgcSeason ugcSeason;
final RxInt isSubscribe;
final bool isVisible;
final Function changeFucCall;
final Function changeVisible;
const UgcSeasonBuild({
Key? key,
required this.ugcSeason,
required this.isSubscribe,
required this.isVisible,
required this.changeFucCall,
required this.changeVisible,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 8),
color: Theme.of(context).colorScheme.surface,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(
height: 1,
thickness: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
const SizedBox(height: 10),
Text(
'合集:${ugcSeason.title}',
style: Theme.of(context).textTheme.titleMedium,
overflow: TextOverflow.ellipsis,
),
if (ugcSeason.intro != null && ugcSeason.intro != '') ...[
const SizedBox(height: 4),
Row(
final ThemeData theme = Theme.of(context);
final Color outline = theme.colorScheme.outline;
final Color surface = theme.colorScheme.surface;
final Color primary = theme.colorScheme.primary;
final Color onPrimary = theme.colorScheme.onPrimary;
final Color onInverseSurface = theme.colorScheme.onInverseSurface;
final TextStyle titleMedium = theme.textTheme.titleMedium!;
final TextStyle labelMedium = theme.textTheme.labelMedium!;
final Color dividerColor = theme.dividerColor.withOpacity(0.1);
return isVisible
? Container(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
color: surface,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(ugcSeason.intro ?? '',
style: TextStyle(
color: Theme.of(context).colorScheme.outline)),
Divider(height: 1, thickness: 1, color: dividerColor),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: Text(
'合集:${ugcSeason.title}',
style: titleMedium,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 10),
Obx(
() => isSubscribe.value == -1
? const SizedBox(height: 32)
: SizedBox(
height: 32,
child: FilledButton.tonal(
onPressed: () => changeFucCall.call(),
style: TextButton.styleFrom(
padding:
const EdgeInsets.only(left: 8, right: 8),
foregroundColor: isSubscribe.value == 1
? outline
: onPrimary,
backgroundColor: isSubscribe.value == 1
? onInverseSurface
: primary,
),
child:
Text(isSubscribe.value == 1 ? '已订阅' : '订阅'),
),
),
),
],
),
// SizedBox(
// height: 32,
// child: FilledButton.tonal(
// onPressed: () {},
// style: ButtonStyle(
// padding: MaterialStateProperty.all(EdgeInsets.zero),
// ),
// child: const Text('订阅'),
// ),
// ),
// const SizedBox(width: 6),
if (ugcSeason.intro != null && ugcSeason.intro != '') ...[
const SizedBox(height: 4),
Text(
ugcSeason.intro!,
style: TextStyle(color: outline, fontSize: 12),
),
],
const SizedBox(height: 4),
Text.rich(
TextSpan(
style: TextStyle(
fontSize: labelMedium.fontSize, color: outline),
children: [
TextSpan(
text: '${Utils.numFormat(ugcSeason.stat!.view)}播放'),
const TextSpan(text: ' · '),
TextSpan(
text:
'${Utils.numFormat(ugcSeason.stat!.danmaku)}弹幕'),
],
),
),
const SizedBox(height: 14),
Align(
alignment: Alignment.center,
child: Material(
color: surface,
child: InkWell(
onTap: () => changeVisible.call(),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 0),
child: Text(
'收起简介',
style: TextStyle(color: primary, fontSize: 12),
),
),
),
),
),
Divider(height: 1, thickness: 1, color: dividerColor),
],
),
],
const SizedBox(height: 4),
Text.rich(
TextSpan(
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
)
: Align(
alignment: Alignment.center,
child: InkWell(
onTap: () => changeVisible.call(),
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
child: Text(
'展开简介',
style: TextStyle(color: primary, fontSize: 12),
),
),
children: [
TextSpan(text: '${Utils.numFormat(ugcSeason.stat!.view)}播放'),
const TextSpan(text: ' · '),
TextSpan(text: '${Utils.numFormat(ugcSeason.stat!.danmaku)}弹幕'),
],
),
),
const SizedBox(height: 14),
Divider(
height: 1,
thickness: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
],
),
);
);
}
}

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/storage.dart';
Box<dynamic> setting = GStrorage.setting;
Box<dynamic> setting = GStorage.setting;
class CustomToast extends StatelessWidget {
const CustomToast({super.key, required this.msg});

View File

@ -6,7 +6,7 @@ import 'package:pilipala/utils/global_data_cache.dart';
import '../../utils/storage.dart';
import '../constants.dart';
Box<dynamic> setting = GStrorage.setting;
Box<dynamic> setting = GStorage.setting;
class NetworkImgLayer extends StatelessWidget {
const NetworkImgLayer({

View File

@ -495,7 +495,7 @@ class Api {
static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';
/// 获取字幕配置
static const getSubtitleConfig = '/x/player/v2';
static const getSubtitleConfig = '/x/player/wbi/v2';
/// 我的订阅
static const userSubFolder = '/x/v3/fav/folder/collected/list';
@ -609,4 +609,14 @@ class Api {
/// @我的
static const String messageAtAPi = '/x/msgfeed/at?';
/// 订阅
static const String confirmSub = '/x/v3/fav/season/fav';
/// 订阅状态
static const String videoRelation = '/x/web-interface/archive/relation';
/// 获取空降区间
static const String getSkipSegments =
'${HttpString.sponsorBlockBaseUrl}/api/skipSegments';
}

View File

@ -1,3 +1,5 @@
import 'package:pilipala/models/sponsor_block/segment.dart';
import 'index.dart';
class CommonHttp {
@ -14,4 +16,31 @@ class CommonHttp {
};
}
}
static Future querySkipSegments({required String bvid}) async {
var res = await Request().getWithoutCookie(Api.getSkipSegments, data: {
'videoID': bvid,
});
if (res.data is List && res.data.isNotEmpty) {
try {
return {
'status': true,
'data': res.data
.map<SegmentDataModel>((e) => SegmentDataModel.fromJson(e))
.toList(),
};
} catch (err) {
return {
'status': false,
'data': [],
'msg': 'sponsorBlock数据解析失败: $err',
};
}
} else {
return {
'status': false,
'data': [],
};
}
}
}

View File

@ -7,6 +7,7 @@ class HttpString {
static const String passBaseUrl = 'https://passport.bilibili.com';
static const String messageBaseUrl = 'https://message.bilibili.com';
static const String bangumiBaseUrl = 'https://bili.meark.me';
static const String sponsorBlockBaseUrl = 'https://www.bsbsb.top';
static const List<int> validateStatusCodes = [
302,
304,

View File

@ -21,8 +21,8 @@ class Request {
static late CookieManager cookieManager;
static late final Dio dio;
factory Request() => _instance;
Box setting = GStrorage.setting;
static Box localCache = GStrorage.localCache;
Box setting = GStorage.setting;
static Box localCache = GStorage.localCache;
late bool enableSystemProxy;
late String systemProxyHost;
late String systemProxyPort;
@ -32,8 +32,8 @@ class Request {
/// 设置cookie
static setCookie() async {
Box userInfoCache = GStrorage.userInfo;
Box setting = GStrorage.setting;
Box userInfoCache = GStorage.userInfo;
Box setting = GStorage.setting;
final String cookiePath = await Utils.getCookiePath();
final PersistCookieJar cookieJar = PersistCookieJar(
ignoreExpires: true,
@ -217,6 +217,13 @@ class Request {
if (extra['ua'] != null) {
options.headers = {'user-agent': headerUa(type: extra['ua'])};
}
if (extra['opus-goback'] != null) {
if (extra['opus-goback'] != null) {
String cookieHeader = dio.options.headers['cookie'];
options.headers!['cookie'] =
'$cookieHeader; opus-goback = ${extra['opus-goback']}';
}
}
}
options.responseType = resType;

View File

@ -470,8 +470,8 @@ class MemberHttp {
SmartDialog.dismiss();
if (res.data['code'] == 0) {
String accessKey = res.data['data']['access_token'];
Box localCache = GStrorage.localCache;
Box userInfoCache = GStrorage.userInfo;
Box localCache = GStorage.localCache;
Box userInfoCache = GStorage.userInfo;
var userInfo = userInfoCache.get('userInfoCache');
localCache.put(
LocalCacheKey.accessKey, {'mid': userInfo.mid, 'value': accessKey});

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:html/parser.dart';
import 'package:pilipala/models/read/opus.dart';
import 'package:pilipala/models/read/read.dart';
@ -64,7 +65,7 @@ class ReadHttp {
static Future parseArticleCv({required String id}) async {
var res = await Request().get(
'https://www.bilibili.com/read/cv$id',
extra: {'ua': 'pc'},
extra: {'ua': 'pc', 'opus-goback': '1'},
);
String scriptContent =
extractScriptContents(parse(res.data).body!.outerHtml)[0];

View File

@ -11,7 +11,7 @@ import '../utils/storage.dart';
import 'index.dart';
class SearchHttp {
static Box setting = GStrorage.setting;
static Box setting = GStorage.setting;
static Future hotSearchList() async {
var res = await Request().get(Api.hotSearchList);
if (res.data is String) {

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/id_utils.dart';
import '../common/constants.dart';
import '../models/common/reply_type.dart';
import '../models/home/rcmd/result.dart';
@ -24,11 +25,11 @@ import 'init.dart';
/// 返回{'status': bool, 'data': List}
/// view层根据 status 判断渲染逻辑
class VideoHttp {
static Box localCache = GStrorage.localCache;
static Box setting = GStrorage.setting;
static Box localCache = GStorage.localCache;
static Box setting = GStorage.setting;
static bool enableRcmdDynamic =
setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true);
static Box userInfoCache = GStrorage.userInfo;
static Box userInfoCache = GStorage.userInfo;
// 首页推荐视频
static Future rcmdVideoList({required int ps, required int freshIdx}) async {
@ -509,10 +510,11 @@ class VideoHttp {
}
}
static Future getSubtitle({int? cid, String? bvid}) async {
static Future getSubtitle({int? cid, String? bvid, String? aid}) async {
var res = await Request().get(Api.getSubtitleConfig, data: {
'cid': cid,
'bvid': bvid,
if (bvid != null) 'bvid': bvid,
if (aid != null) 'aid': aid,
});
try {
if (res.data['code'] == 0) {
@ -559,4 +561,50 @@ class VideoHttp {
final List body = res.data['body'];
return {'content': content, 'body': body};
}
static Future<Map<String, dynamic>> getSubscribeStatus(
{required dynamic bvid}) async {
var res = await Request().get(
Api.videoRelation,
data: {
'aid': IdUtils.bv2av(bvid),
'bvid': bvid,
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'msg': res.data['message'],
};
}
}
static Future seasonFav({
required bool isFav,
required dynamic seasonId,
}) async {
var res = await Request().post(
isFav ? Api.cancelSub : Api.confirmSub,
data: {
'platform': 'web',
'season_id': seasonId,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {
'status': true,
};
} else {
return {
'status': false,
'msg': res.data['message'],
};
}
}
}

View File

@ -32,7 +32,7 @@ void main() async {
MediaKit.ensureInitialized();
await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
await GStrorage.init();
await GStorage.init();
clearLogs();
Request();
await Request.setCookie();
@ -73,7 +73,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
// 主题色
Color defaultColor =
colorThemeTypes[setting.get(SettingBoxKey.customColor, defaultValue: 0)]

View File

@ -0,0 +1,26 @@
// 片段类型枚举
enum ActionType {
skip,
mute,
full,
poi,
chapter,
}
extension ActionTypeExtension on ActionType {
String get value => [
'skip',
'mute',
'full',
'poi',
'chapter',
][index];
String get label => [
'跳过',
'静音',
'完整观看',
'亮点',
'章节切换',
][index];
}

View File

@ -0,0 +1,43 @@
import 'action_type.dart';
import 'segment_type.dart';
class SegmentDataModel {
final SegmentType? category;
final ActionType? actionType;
final List? segment;
final String? uuid;
final num? videoDuration;
final int? locked;
final int? votes;
final String? description;
// 是否已经跳过
bool isSkip = false;
SegmentDataModel({
this.category,
this.actionType,
this.segment,
this.uuid,
this.videoDuration,
this.locked,
this.votes,
this.description,
});
factory SegmentDataModel.fromJson(Map<String, dynamic> json) {
return SegmentDataModel(
category: SegmentType.values.firstWhere(
(e) => e.value == json['category'],
orElse: () => SegmentType.sponsor),
actionType: ActionType.values.firstWhere(
(e) => e.value == json['actionType'],
orElse: () => ActionType.skip),
segment: json['segment'],
uuid: json['UUID'],
videoDuration: json['videoDuration'],
locked: json['locked'],
votes: json['votes'],
description: json['description'],
);
}
}

View File

@ -0,0 +1,46 @@
// 片段类型枚举
// ignore_for_file: constant_identifier_names
enum SegmentType {
sponsor,
intro,
outro,
interaction,
selfpromo,
music_offtopic,
preview,
poi_highlight,
filler,
exclusive_access,
chapter,
}
extension SegmentTypeExtension on SegmentType {
String get value => [
'sponsor',
'intro',
'outro',
'interaction',
'selfpromo',
'music_offtopic',
'preview',
'poi_highlight',
'filler',
'exclusive_access',
'chapter',
][index];
String get label => [
'赞助',
'开场介绍',
'片尾致谢',
'互动',
'自我推广',
'音乐',
'预览',
'亮点',
'无效填充',
'独家访问',
'章节',
][index];
}

View File

@ -29,7 +29,7 @@ class ReplyMember {
avatar = json['avatar'];
level = json['level_info']['current_level'];
pendant = Pendant.fromJson(json['pendant']);
officialVerify = json['officia_verify'];
officialVerify = json['official_verify'];
vip = json['vip'];
fansDetail = json['fans_detail'];
userSailing = json['user_sailing'] != null

View File

@ -641,6 +641,7 @@ class EpisodeItem {
this.page,
this.bvid,
this.cover,
this.pages,
});
int? seasonId;
int? sectionId;
@ -655,6 +656,7 @@ class EpisodeItem {
int? pubdate;
int? duration;
Stat? stat;
List<Page>? pages;
EpisodeItem.fromJson(Map<String, dynamic> json) {
seasonId = json['season_id'];
@ -670,6 +672,7 @@ class EpisodeItem {
pubdate = json['arc']['pubdate'];
duration = json['arc']['duration'];
stat = Stat.fromJson(json['arc']['stat']);
pages = json['pages'].map<Page>((e) => Page.fromJson(e)).toList();
}
}
@ -712,3 +715,18 @@ class Vip {
status = json['status'];
}
}
class Page {
Page({
this.cid,
this.page,
});
int? cid;
int? page;
Page.fromJson(Map<String, dynamic> json) {
cid = json['cid'];
page = json['page'];
}
}

View File

@ -12,7 +12,7 @@ class BangumiController extends GetxController {
RxInt total = 0.obs;
int _currentPage = 1;
bool isLoadingMore = true;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
RxBool userLogin = false.obs;
late int mid;
var userInfo;

View File

@ -48,7 +48,7 @@ class BangumiIntroController extends GetxController {
RxBool hasCoin = false.obs;
// 是否收藏
RxBool hasFav = false.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
bool userLogin = false;
Rx<FavFolderData> favFolderData = FavFolderData().obs;
List addMediaIdsNew = [];

View File

@ -116,7 +116,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
String heroTag = Get.arguments['heroTag'];
late final BangumiIntroController bangumiIntroController;
late final VideoDetailController videoDetailCtr;
Box localCache = GStrorage.localCache;
Box localCache = GStorage.localCache;
late double sheetHeight;
int? cid;
bool isProcessing = false;

View File

@ -4,7 +4,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart';
import 'package:pilipala/common/widgets/stat/view.dart';
import 'package:pilipala/utils/storage.dart';
Box localCache = GStrorage.localCache;
Box localCache = GStorage.localCache;
late double sheetHeight;
class IntroDetail extends StatelessWidget {

View File

@ -36,7 +36,7 @@ class BangumiPanel extends StatefulWidget {
class _BangumiPanelState extends State<BangumiPanel> {
late RxInt currentIndex = (-1).obs;
final ScrollController listViewScrollCtr = ScrollController();
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
dynamic userInfo;
// 默认未开通
int vipStatus = 0;

View File

@ -22,7 +22,7 @@ class _BlackListPageState extends State<BlackListPage> {
final ScrollController scrollController = ScrollController();
Future? _futureBuilderFuture;
bool _isLoadingMore = false;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
@override
void initState() {

View File

@ -32,7 +32,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
late PlDanmakuController _plDanmakuController;
DanmakuController? _controller;
// bool danmuPlayStatus = true;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late bool enableShowDanmaku;
late List blockTypes;
late double showArea;

View File

@ -50,11 +50,11 @@ class DynamicsController extends GetxController {
];
bool flag = false;
RxInt initialValue = 0.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
RxBool userLogin = false.obs;
var userInfo;
RxBool isLoadingDynamic = false.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
@override
void onInit() {

View File

@ -24,7 +24,7 @@ class DynamicDetailController extends GetxController {
ReplySortType _sortType = ReplySortType.time;
RxString sortTypeTitle = ReplySortType.time.titles.obs;
RxString sortTypeLabel = ReplySortType.time.labels.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
RxInt replyReqCode = 200.obs;
bool isEnd = false;
@ -37,13 +37,13 @@ class DynamicDetailController extends GetxController {
acount.value =
int.parse(item!.modules!.moduleStat!.comment!.count ?? '0');
}
int deaultReplySortIndex =
int defaultReplySortIndex =
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
if (deaultReplySortIndex == 2) {
if (defaultReplySortIndex == 2) {
setting.put(SettingBoxKey.replySortType, 0);
deaultReplySortIndex = 0;
defaultReplySortIndex = 0;
}
_sortType = ReplySortType.values[deaultReplySortIndex];
_sortType = ReplySortType.values[defaultReplySortIndex];
sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels;
}

View File

@ -9,9 +9,7 @@ import 'package:pilipala/common/skeleton/dynamic_card.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/plugin/pl_popup/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import 'package:pilipala/utils/main_stream.dart';
import 'package:pilipala/utils/route_push.dart';
import 'package:pilipala/utils/storage.dart';
@ -19,7 +17,6 @@ import 'package:pilipala/utils/storage.dart';
import '../mine/controller.dart';
import 'controller.dart';
import 'widgets/dynamic_panel.dart';
import 'up_dynamic/route_panel.dart';
import 'widgets/up_panel.dart';
class DynamicsPage extends StatefulWidget {
@ -35,7 +32,7 @@ class _DynamicsPageState extends State<DynamicsPage>
final MineController mineController = Get.put(MineController());
late Future _futureBuilderFuture;
late Future _futureBuilderFutureUp;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
late ScrollController scrollController;
@override
@ -209,21 +206,7 @@ class _DynamicsPageState extends State<DynamicsPage>
return Obx(
() => UpPanel(
upData: _dynamicsController.upData.value,
onClickUpCb: (data) {
if (GlobalDataCache().enableDynamicSwitch) {
Navigator.push(
context,
PlPopupRoute(
child: OverlayPanel(
ctr: _dynamicsController,
upInfo: data,
),
),
);
} else {
_dynamicsController.onTapUp(data);
}
},
dynamicsController: _dynamicsController,
),
);
} else {

View File

@ -4,18 +4,22 @@ import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/dynamics/up.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/plugin/pl_popup/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import 'package:pilipala/utils/utils.dart';
import '../controller.dart';
import '../up_dynamic/route_panel.dart';
class UpPanel extends StatefulWidget {
final FollowUpModel upData;
final Function? onClickUpCb;
final DynamicsController dynamicsController;
const UpPanel({
super.key,
required this.upData,
this.onClickUpCb,
required this.dynamicsController,
});
@override
@ -39,7 +43,15 @@ class _UpPanelState extends State<UpPanel> {
void onClickUp(data, i) {
currentMid.value = data.mid;
widget.onClickUpCb?.call(data);
Navigator.push(
context,
PlPopupRoute(
child: OverlayPanel(
ctr: widget.dynamicsController,
upInfo: data,
),
),
).then((value) => {currentMid.value = -1});
}
void onClickUpAni(data, i) {
@ -49,7 +61,7 @@ class _UpPanelState extends State<UpPanel> {
final upLen = upList.length;
currentMid.value = data.mid;
widget.onClickUpCb?.call(data);
widget.dynamicsController.onTapUp(data);
double moveDistance = 0.0;
final totalItemsWidth = itemWidth * (upLen + liveLen);

View File

@ -6,7 +6,7 @@ import 'package:pilipala/models/fans/result.dart';
import 'package:pilipala/utils/storage.dart';
class FansController extends GetxController {
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
int pn = 1;
int ps = 20;
int total = 0;

View File

@ -11,7 +11,7 @@ class FavController extends GetxController {
final ScrollController scrollController = ScrollController();
Rx<FavFolderData> favFolderData = FavFolderData().obs;
RxList<FavFolderItemData> favFolderList = <FavFolderItemData>[].obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
UserInfoData? userInfo;
int currentPage = 1;
int pageSize = 60;

View File

@ -11,7 +11,7 @@ import 'package:pilipala/utils/storage.dart';
/// 查看自己的关注时,可以查看分类
/// 查看其他人的关注时,只可以看全部
class FollowController extends GetxController with GetTickerProviderStateMixin {
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
int pn = 1;
int ps = 20;
int total = 0;

View File

@ -12,11 +12,11 @@ class HistoryController extends GetxController {
RxList<HisListItem> historyList = <HisListItem>[].obs;
RxBool isLoadingMore = false.obs;
RxBool pauseStatus = false.obs;
Box localCache = GStrorage.localCache;
Box localCache = GStorage.localCache;
RxBool isLoading = false.obs;
RxBool enableMultiple = false.obs;
RxInt checkedCount = 0.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
UserInfoData? userInfo;
@override

View File

@ -14,12 +14,12 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
late TabController tabController;
late List tabsCtrList;
late List<Widget> tabsPageList;
Box userInfoCache = GStrorage.userInfo;
Box settingStorage = GStrorage.setting;
Box userInfoCache = GStorage.userInfo;
Box settingStorage = GStorage.setting;
RxBool userLogin = false.obs;
RxString userFace = ''.obs;
var userInfo;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late final StreamController<bool> searchBarStream =
StreamController<bool>.broadcast();
late bool hideSearchBar;

View File

@ -5,7 +5,7 @@ import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/storage.dart';
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
class HomeAppBar extends StatelessWidget {
const HomeAppBar({super.key});

View File

@ -25,7 +25,7 @@ class HtmlRenderController extends GetxController {
ReplySortType _sortType = ReplySortType.time;
RxString sortTypeTitle = ReplySortType.time.titles.obs;
RxString sortTypeLabel = ReplySortType.time.labels.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
@override
void onInit() {

View File

@ -13,7 +13,7 @@ class LaterController extends GetxController {
RxList<HotVideoItemModel> laterList = <HotVideoItemModel>[].obs;
int count = 0;
RxBool isLoading = false.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
UserInfoData? userInfo;
@override

View File

@ -17,7 +17,7 @@ class LiveController extends GetxController {
RxInt liveFollowingCount = 0.obs;
bool flag = false;
OverlayEntry? popupDialog;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
@override
void onInit() {

View File

@ -7,7 +7,7 @@ import 'package:pilipala/utils/storage.dart';
class LiveFollowController extends GetxController {
RxInt crossAxisCount = 2.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
int _currentPage = 1;
RxInt liveFollowingCount = 0.obs;
RxList<LiveFollowingItemModel> liveFollowingList =

View File

@ -33,7 +33,7 @@ class LiveRoomController extends GetxController {
int? tempCurrentQn;
late List<Map<String, dynamic>> acceptQnList;
RxString currentQnDesc = ''.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
int userId = 0;
PlSocket? plSocket;
List<String> danmuHostList = [];

View File

@ -35,7 +35,7 @@ class _BottomControlState extends State<BottomControl> {
TextStyle subTitleStyle = const TextStyle(fontSize: 12);
TextStyle titleStyle = const TextStyle(fontSize: 14);
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
Box localCache = GStrorage.localCache;
Box localCache = GStorage.localCache;
@override
void initState() {

View File

@ -20,12 +20,12 @@ class MainController extends GetxController {
late List<int> navBarSort;
final StreamController<bool> bottomBarStream =
StreamController<bool>.broadcast();
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
DateTime? _lastPressedAt;
late bool hideTabBar;
late PageController pageController;
int selectedIndex = 0;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
dynamic userInfo;
RxBool userLogin = false.obs;
late Rx<DynamicBadgeMode> dynamicBadgeType = DynamicBadgeMode.number.obs;

View File

@ -30,7 +30,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
late MineController _mineController;
int? _lastSelectTime; //上次点击时间
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late bool enableMYBar;
@override
@ -113,14 +113,14 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
@override
void dispose() async {
await GStrorage.close();
await GStorage.close();
EventBus().off(EventName.loginEvent);
super.dispose();
}
@override
Widget build(BuildContext context) {
Box localCache = GStrorage.localCache;
Box localCache = GStorage.localCache;
double statusBarHeight = MediaQuery.of(context).padding.top;
double sheetHeight = MediaQuery.sizeOf(context).height -
MediaQuery.of(context).padding.top -

View File

@ -18,7 +18,7 @@ class MemberController extends GetxController {
late Map userStat;
RxString face = ''.obs;
String? heroTag;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
late int ownerMid;
// 投稿列表
RxList<VListItemModel>? archiveList = <VListItemModel>[].obs;

View File

@ -41,6 +41,7 @@ class MemberSeasonsPanel extends StatelessWidget {
'category': '1',
'mid': item.meta!.mid.toString(),
'seriesId': item.meta!.seriesId.toString(),
'seasonId': item.meta!.seasonId.toString(),
'seasonName': item.meta!.name!,
};
}

View File

@ -24,7 +24,8 @@ class MemberSeasonsController extends GetxController {
seasonId = int.parse(Get.parameters['seasonId']!);
}
if (category == '1') {
seriesId = int.parse(Get.parameters['seriesId']!);
seriesId = int.tryParse(Get.parameters['seriesId']!);
seasonId = int.tryParse(Get.parameters['seasonId']!);
}
}
@ -73,7 +74,27 @@ class MemberSeasonsController extends GetxController {
getSeasonDetail('onLoad');
}
if (category == '1') {
getSeriesDetail('onLoad');
if (seasonId != null) {
getSeasonDetail('onLoad');
}
if (seriesId != null) {
getSeriesDetail('onLoad');
}
}
}
// 下拉刷新
Future onRefresh() async {
if (category == '0') {
return getSeasonDetail('onRefresh');
}
if (category == '1') {
if (seasonId != null) {
return getSeasonDetail('onRefresh');
}
if (seriesId != null) {
return getSeriesDetail('onRefresh');
}
}
}
}

View File

@ -23,9 +23,7 @@ class _MemberSeasonsPageState extends State<MemberSeasonsPage> {
void initState() {
super.initState();
category = Get.parameters['category']!;
_futureBuilderFuture = category == '0'
? _memberSeasonsController.getSeasonDetail('onRefresh')
: _memberSeasonsController.getSeriesDetail('onRefresh');
_futureBuilderFuture = _memberSeasonsController.onRefresh();
scrollController = _memberSeasonsController.scrollController;
scrollController.addListener(
() {

View File

@ -136,6 +136,7 @@ class MessageUtils {
.replaceAll('}', '');
result[linkText] = match.group(0)!;
}
print('str: $str');
message += str;
}
} else {
@ -144,6 +145,10 @@ class MessageUtils {
}
lastMatchEnd = end;
}
// 处理剩余的未匹配部分
if (lastMatchEnd < text.length) {
message += text.substring(lastMatchEnd + 1);
}
result['message'] = message;
} else {
result['message'] = text;

View File

@ -17,8 +17,8 @@ class MineController extends GetxController {
Rx<UserInfoData> userInfo = UserInfoData().obs;
Rx<ThemeType> themeType = ThemeType.system.obs;
Rx<FavFolderData> favFolderData = FavFolderData().obs;
Box setting = GStrorage.setting;
Box userInfoCache = GStrorage.userInfo;
Box setting = GStorage.setting;
Box userInfoCache = GStorage.userInfo;
List menuList = [
{
'icon': Icons.history,

View File

@ -6,7 +6,7 @@ import 'package:pilipala/http/user.dart';
import 'package:pilipala/utils/storage.dart';
class MineEditController extends GetxController {
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
final formKey = GlobalKey<FormState>();
final TextEditingController unameCtr = TextEditingController();
final TextEditingController useridCtr = TextEditingController();

View File

@ -14,7 +14,7 @@ class RankController extends GetxController with GetTickerProviderStateMixin {
late TabController tabController;
late List tabsCtrList;
late List<Widget> tabsPageList;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late final StreamController<bool> searchBarStream =
StreamController<bool>.broadcast();

View File

@ -14,7 +14,7 @@ class RcmdController extends GetxController {
// RxList<RecVideoItemModel> webVideoList = <RecVideoItemModel>[].obs;
bool isLoadingMore = true;
OverlayEntry? popupDialog;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
RxInt crossAxisCount = 2.obs;
late bool enableSaveLastData;
late String defaultRcmdType = 'web';

View File

@ -15,7 +15,7 @@ class SSearchController extends GetxController {
RxString searchKeyWord = ''.obs;
Rx<TextEditingController> controller = TextEditingController().obs;
RxList<HotSearchItem> hotSearchList = <HotSearchItem>[].obs;
Box localCache = GStrorage.localCache;
Box localCache = GStorage.localCache;
List historyCacheList = [];
RxList historyList = [].obs;
RxList<SearchSuggestItem> searchSuggestList = <SearchSuggestItem>[].obs;
@ -23,7 +23,7 @@ class SSearchController extends GetxController {
Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间
String hintText = '搜索';
RxString defaultSearch = ''.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
bool enableHotKey = true;
bool enableSearchSuggest = true;

View File

@ -13,9 +13,9 @@ import '../main/index.dart';
import 'widgets/select_dialog.dart';
class SettingController extends GetxController {
Box userInfoCache = GStrorage.userInfo;
Box setting = GStrorage.setting;
Box localCache = GStrorage.localCache;
Box userInfoCache = GStorage.userInfo;
Box setting = GStorage.setting;
Box localCache = GStorage.localCache;
RxBool userLogin = false.obs;
RxBool feedBackEnable = false.obs;

View File

@ -19,8 +19,8 @@ class ExtraSetting extends StatefulWidget {
}
class _ExtraSettingState extends State<ExtraSetting> {
Box setting = GStrorage.setting;
static Box localCache = GStrorage.localCache;
Box setting = GStorage.setting;
static Box localCache = GStorage.localCache;
late dynamic defaultReplySort;
late dynamic defaultDynamicType;
late dynamic enableSystemProxy;

View File

@ -13,7 +13,7 @@ class ActionMenuSetPage extends StatefulWidget {
}
class _ActionMenuSetPageState extends State<ActionMenuSetPage> {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late List<String> actionTypeSort;
late List<Map> allLabels;

View File

@ -142,7 +142,7 @@ class _ColorSelectPageState extends State<ColorSelectPage> {
}
class ColorSelectController extends GetxController {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
RxBool dynamicColor = true.obs;
RxInt type = 0.obs;
late final List<Map<String, dynamic>> colorThemes;

View File

@ -16,7 +16,7 @@ class _SetDiaplayModeState extends State<SetDiaplayMode> {
List<DisplayMode> modes = <DisplayMode>[];
DisplayMode? active;
DisplayMode? preferred;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
final ValueNotifier<int> page = ValueNotifier<int>(0);
late final PageController controller = PageController()

View File

@ -11,7 +11,7 @@ class FontSizeSelectPage extends StatefulWidget {
}
class _FontSizeSelectPageState extends State<FontSizeSelectPage> {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
List<double> list = [0.9, 0.95, 1.0, 1.05, 1.1, 1.15, 1.2, 1.25, 1.3];
late double minsize;
late double maxSize;

View File

@ -12,7 +12,7 @@ class TabbarSetPage extends StatefulWidget {
}
class _TabbarSetPageState extends State<TabbarSetPage> {
Box settingStorage = GStrorage.setting;
Box settingStorage = GStorage.setting;
late List defaultTabs;
late List<String> tabbarSort;

View File

@ -13,7 +13,7 @@ class NavigationBarSetPage extends StatefulWidget {
}
class _NavigationbarSetPageState extends State<NavigationBarSetPage> {
Box settingStorage = GStrorage.setting;
Box settingStorage = GStorage.setting;
late List defaultNavTabs;
late List<int> navBarSort;

View File

@ -16,7 +16,7 @@ class PlayGesturePage extends StatefulWidget {
}
class _PlayGesturePageState extends State<PlayGesturePage> {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late int fullScreenGestureMode;
@override

View File

@ -14,8 +14,8 @@ class PlaySpeedPage extends StatefulWidget {
}
class _PlaySpeedPageState extends State<PlaySpeedPage> {
Box videoStorage = GStrorage.video;
Box settingStorage = GStrorage.setting;
Box videoStorage = GStorage.video;
Box settingStorage = GStorage.setting;
late double playSpeedDefault;
late List<double> playSpeedSystem;
late double longPressSpeedDefault;

View File

@ -23,7 +23,7 @@ class PlaySetting extends StatefulWidget {
}
class _PlaySettingState extends State<PlaySetting> {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late dynamic defaultVideoQa;
late dynamic defaultLiveQa;
late dynamic defaultAudioQa;

View File

@ -14,7 +14,7 @@ class PrivacySetting extends StatefulWidget {
class _PrivacySettingState extends State<PrivacySetting> {
bool userLogin = false;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
var userInfo;
@override

View File

@ -17,10 +17,10 @@ class RecommendSetting extends StatefulWidget {
}
class _RecommendSettingState extends State<RecommendSetting> {
Box setting = GStrorage.setting;
static Box localCache = GStrorage.localCache;
Box setting = GStorage.setting;
static Box localCache = GStorage.localCache;
late dynamic defaultRcmdType;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
late dynamic userInfo;
bool userLogin = false;
late dynamic accessKeyInfo;
@ -247,10 +247,9 @@ class _RecommendSettingState extends State<RecommendSetting> {
'* 其它(如热门视频、手动搜索、链接跳转等)均不受过滤器影响。\n'
'* 设定较严苛的条件可导致推荐项数锐减或多次请求,请酌情选择。\n'
'* 后续可能会增加更多过滤条件,敬请期待。',
style: Theme.of(context)
.textTheme
.labelSmall!
.copyWith(color: Theme.of(context).colorScheme.outline.withOpacity(0.7)),
style: Theme.of(context).textTheme.labelSmall!.copyWith(
color:
Theme.of(context).colorScheme.outline.withOpacity(0.7)),
),
)
],

View File

@ -28,7 +28,7 @@ class _StyleSettingState extends State<StyleSetting> {
final ColorSelectController colorSelectController =
Get.put(ColorSelectController());
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late int picQuality;
late ThemeType _tempThemeValue;
late dynamic defaultCustomRows;

View File

@ -19,7 +19,7 @@ class SetSelectItem extends StatefulWidget {
}
class _SetSelectItemState extends State<SetSelectItem> {
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late dynamic currentVal;
late int currentIndex;
late List menus;

View File

@ -28,7 +28,7 @@ class SetSwitchItem extends StatefulWidget {
class _SetSwitchItemState extends State<SetSwitchItem> {
// ignore: non_constant_identifier_names
Box Setting = GStrorage.setting;
Box Setting = GStorage.setting;
late bool val;
@override

View File

@ -11,7 +11,7 @@ import '../../models/user/sub_folder.dart';
class SubController extends GetxController {
final ScrollController scrollController = ScrollController();
Rx<SubFolderModelData> subFolderData = SubFolderModelData().obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
UserInfoData? userInfo;
int currentPage = 1;
int pageSize = 20;

View File

@ -6,11 +6,14 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:pilipala/http/common.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/models/sponsor_block/segment.dart';
import 'package:pilipala/models/sponsor_block/segment_type.dart';
import 'package:pilipala/models/video/later.dart';
import 'package:pilipala/models/video/play/quality.dart';
import 'package:pilipala/models/video/play/url.dart';
@ -68,9 +71,9 @@ class VideoDetailController extends GetxController
RxBool enableHA = false.obs;
/// 本地存储
Box userInfoCache = GStrorage.userInfo;
Box localCache = GStrorage.localCache;
Box setting = GStrorage.setting;
Box userInfoCache = GStorage.userInfo;
Box localCache = GStorage.localCache;
Box setting = GStorage.setting;
RxInt oid = 0.obs;
// 评论id 请求楼中楼评论使用
@ -120,6 +123,8 @@ class VideoDetailController extends GetxController
RxBool isWatchLaterVisible = false.obs;
RxString watchLaterTitle = ''.obs;
RxInt watchLaterCount = 0.obs;
List<SegmentDataModel> skipSegments = <SegmentDataModel>[];
int? lastPosition;
@override
void onInit() {
@ -188,6 +193,11 @@ class VideoDetailController extends GetxController
tabCtr.addListener(() {
onTabChanged();
});
/// 仅投稿视频skip
if (videoType == SearchType.video) {
querySkipSegments();
}
}
showReplyReplyPanel(oid, fRpid, firstFloor, currentReply, loadMore) {
@ -305,6 +315,7 @@ class VideoDetailController extends GetxController
plPlayerController.headerControl = headerControl;
plPlayerController.subtitles.value = subtitles;
onPositionChanged();
}
// 视频链接
@ -706,6 +717,53 @@ class VideoDetailController extends GetxController
isWatchLaterVisible.value = tabCtr.index == 0;
}
// 获取sponsorBlock数据
Future querySkipSegments() async {
var res = await CommonHttp.querySkipSegments(bvid: bvid);
if (res['status']) {
/// TODO 根据segmentType过滤数据
skipSegments = res['data'] ?? [];
}
}
// 监听视频进度
void onPositionChanged() async {
final List<SegmentDataModel> sponsorSkipSegments = skipSegments
.where((e) => e.category!.value == SegmentType.sponsor.value)
.toList();
if (sponsorSkipSegments.isEmpty) {
return;
}
plPlayerController.videoPlayerController?.stream.position
.listen((Duration position) async {
final int positionMs = position.inSeconds;
// 如果当前秒与上次处理的秒相同,则直接返回
if (lastPosition != null && lastPosition! == positionMs) {
return;
}
lastPosition = positionMs;
for (SegmentDataModel segment in sponsorSkipSegments) {
try {
final segmentStart = segment.segment!.first.toInt();
final segmentEnd = segment.segment!.last.toInt();
/// 只有顺序播放时才skip跳转时间点不会skip
if (positionMs == segmentStart && !segment.isSkip) {
await plPlayerController.videoPlayerController
?.seek(Duration(seconds: segmentEnd));
segment.isSkip = true;
SmartDialog.showToast('已跳过${segment.category!.label}片段');
}
} catch (err) {
SmartDialog.showToast('skipSegments error: $err');
}
}
});
}
@override
void onClose() {
super.onClose();

View File

@ -41,7 +41,7 @@ class VideoIntroController extends GetxController {
RxBool hasFav = false.obs;
// 是否不喜欢
RxBool hasDisLike = false.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
bool userLogin = false;
Rx<FavFolderData> favFolderData = FavFolderData().obs;
List addMediaIdsNew = [];
@ -63,6 +63,7 @@ class VideoIntroController extends GetxController {
PersistentBottomSheetController? bottomSheetController;
late bool enableRelatedVideo;
UgcSeason? ugcSeason;
RxList<Part> pages = <Part>[].obs;
@override
void onInit() {
@ -84,18 +85,20 @@ class VideoIntroController extends GetxController {
}
// 获取视频简介&分p
Future queryVideoIntro() async {
Future queryVideoIntro({cover}) async {
var result = await VideoHttp.videoIntro(bvid: bvid);
if (result['status']) {
videoDetail.value = result['data']!;
ugcSeason = result['data']!.ugcSeason;
if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) {
lastPlayCid.value = videoDetail.value.pages!.first.cid!;
pages.value = result['data']!.pages!;
lastPlayCid.value = videoDetail.value.cid!;
if (pages.isNotEmpty) {
lastPlayCid.value = pages.first.cid!;
}
final VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: heroTag);
videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}'];
videoDetailCtr.cover.value = result['data'].pic ?? '';
videoDetailCtr.cover.value = cover ?? result['data'].pic ?? '';
// 获取到粉丝数再返回
await queryUserStat();
}
@ -470,8 +473,7 @@ class VideoIntroController extends GetxController {
videoReplyCtr.queryReplyList(type: 'init');
} catch (_) {}
this.bvid = bvid;
lastPlayCid.value = cid;
await queryVideoIntro();
await queryVideoIntro(cover: cover);
}
void startTimer() {
@ -521,9 +523,8 @@ class VideoIntroController extends GetxController {
final List<EpisodeItem> episodesList = sections[i].episodes!;
episodes.addAll(episodesList);
}
} else if (videoDetail.value.pages != null) {
} else if (pages.isNotEmpty) {
isPages = true;
final List<Part> pages = videoDetail.value.pages!;
episodes.addAll(pages);
}
@ -621,10 +622,9 @@ class VideoIntroController extends GetxController {
}
}
}
if (videoDetail.value.pages != null &&
videoDetail.value.pages!.length > 1) {
if (pages.length > 1) {
dataType = VideoEpidoesType.videoPart;
episodes = videoDetail.value.pages!;
episodes = pages;
}
DrawerUtils.showRightDialog(

View File

@ -137,8 +137,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
late String heroTag;
late final VideoIntroController videoIntroController;
late final VideoDetailController videoDetailCtr;
final Box<dynamic> localCache = GStrorage.localCache;
final Box<dynamic> setting = GStrorage.setting;
final Box<dynamic> localCache = GStorage.localCache;
final Box<dynamic> setting = GStorage.setting;
late double sheetHeight;
late final dynamic owner;
late int mid;
@ -404,27 +404,18 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
Obx(
() => SeasonPanel(
ugcSeason: widget.videoDetail!.ugcSeason!,
cid: videoIntroController.lastPlayCid.value != 0
? videoIntroController.lastPlayCid.value
: widget.videoDetail!.pages!.first.cid,
cid: videoIntroController.lastPlayCid.value,
sheetHeight: videoDetailCtr.sheetHeight.value,
changeFuc: (bvid, cid, aid, cover) =>
videoIntroController.changeSeasonOrbangu(
bvid,
cid,
aid,
cover,
),
changeFuc: videoIntroController.changeSeasonOrbangu,
videoIntroCtr: videoIntroController,
),
)
],
// 合集 videoEpisode
if (widget.videoDetail!.pages != null &&
widget.videoDetail!.pages!.length > 1) ...[
if (videoIntroController.pages.length > 1) ...[
Obx(
() => PagesPanel(
pages: widget.videoDetail!.pages!,
pages: videoIntroController.pages,
cid: videoIntroController.lastPlayCid.value,
sheetHeight: videoDetailCtr.sheetHeight.value,
changeFuc: (cid, cover) =>

View File

@ -16,7 +16,7 @@ class FavPanel extends StatefulWidget {
}
class _FavPanelState extends State<FavPanel> {
final Box<dynamic> localCache = GStrorage.localCache;
final Box<dynamic> localCache = GStorage.localCache;
late Future _futureBuilderFuture;
@override

View File

@ -18,7 +18,7 @@ class GroupPanel extends StatefulWidget {
}
class _GroupPanelState extends State<GroupPanel> {
final Box<dynamic> localCache = GStrorage.localCache;
final Box<dynamic> localCache = GStorage.localCache;
late Future _futureBuilderFuture;
late List<MemberTagItemModel> tagsList;
bool showDefault = true;

View File

@ -3,7 +3,6 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/introduction/index.dart';
import '../../../../../common/pages_bottom_sheet.dart';
import '../../../../../models/common/video_episode_type.dart';
@ -32,25 +31,26 @@ class _PagesPanelState extends State<PagesPanel> {
late int cid;
late RxInt currentIndex = (-1).obs;
final String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController;
final ScrollController listViewScrollCtr = ScrollController();
late PersistentBottomSheetController? _bottomSheetController;
PersistentBottomSheetController? _bottomSheetController;
@override
void initState() {
super.initState();
cid = widget.cid;
episodes = widget.pages;
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
currentIndex.value = episodes.indexWhere((Part e) => e.cid == cid);
scrollToIndex();
_videoDetailController.cid.listen((int p0) {
updateCurrentIndexAndScroll();
widget.videoIntroCtr.lastPlayCid.listen((int p0) {
cid = p0;
currentIndex.value = episodes.indexWhere((Part e) => e.cid == cid);
scrollToIndex();
updateCurrentIndexAndScroll();
});
}
void updateCurrentIndexAndScroll() {
currentIndex.value = widget.pages.indexWhere((Part e) => e.cid == cid);
scrollToIndex();
}
@override
void dispose() {
listViewScrollCtr.dispose();
@ -60,7 +60,10 @@ class _PagesPanelState extends State<PagesPanel> {
void changeFucCall(item, i) async {
widget.changeFuc?.call(item.cid, item.cover);
currentIndex.value = i;
_bottomSheetController?.close();
cid = item.cid;
if (_bottomSheetController != null) {
_bottomSheetController?.close();
}
scrollToIndex();
}
@ -112,7 +115,7 @@ class _PagesPanelState extends State<PagesPanel> {
widget.videoIntroCtr.bottomSheetController =
_bottomSheetController = EpisodeBottomSheet(
currentCid: cid,
episodes: episodes,
episodes: widget.pages,
changeFucCall: changeFucCall,
sheetHeight: widget.sheetHeight,
dataType: VideoEpidoesType.videoPart,

View File

@ -33,6 +33,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
final String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController;
late PersistentBottomSheetController? _bottomSheetController;
int currentEpisodeIndex = -1;
@override
void initState() {
@ -41,13 +42,12 @@ class _SeasonPanelState extends State<SeasonPanel> {
_videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
/// 根据 cid 找到对应集,找到对应 episodes
/// 有多个episodes时只显示其中一个
/// TODO 同时显示多个合集
final List<SectionItem> sections = widget.ugcSeason.sections!;
for (int i = 0; i < sections.length; i++) {
final List<EpisodeItem> episodesList = sections[i].episodes!;
for (int j = 0; j < episodesList.length; j++) {
if (episodesList[j].cid == cid) {
currentEpisodeIndex = i;
episodes = episodesList;
continue;
}
@ -55,10 +55,10 @@ class _SeasonPanelState extends State<SeasonPanel> {
}
/// 取对应 season_id 的 episodes
currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid);
getCurrentIndex();
_videoDetailController.cid.listen((int p0) {
cid = p0;
currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid);
getCurrentIndex();
});
}
@ -73,6 +73,23 @@ class _SeasonPanelState extends State<SeasonPanel> {
_bottomSheetController?.close();
}
// 获取currentIndex
void getCurrentIndex() {
currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid);
final List<SectionItem> sections = widget.ugcSeason.sections!;
if (sections.length == 1 && sections.first.type == 1) {
final List<EpisodeItem> episodesList = sections.first.episodes!;
for (int i = 0; i < episodesList.length; i++) {
for (int j = 0; j < episodesList[i].pages!.length; j++) {
if (episodesList[i].pages![j].cid == cid) {
currentIndex.value = i;
continue;
}
}
}
}
}
Widget buildEpisodeListItem(
EpisodeItem episode,
int index,
@ -125,6 +142,8 @@ class _SeasonPanelState extends State<SeasonPanel> {
sheetHeight: widget.sheetHeight,
dataType: VideoEpidoesType.videoEpisode,
ugcSeason: widget.ugcSeason,
currentEpisodeIndex: currentEpisodeIndex,
currentIndex: currentIndex.value,
).show(context);
},
child: Padding(

View File

@ -32,20 +32,20 @@ class VideoReplyController extends GetxController {
RxString sortTypeTitle = ReplySortType.time.titles.obs;
RxString sortTypeLabel = ReplySortType.time.labels.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
RxInt replyReqCode = 200.obs;
bool isEnd = false;
@override
void onInit() {
super.onInit();
int deaultReplySortIndex =
int defaultReplySortIndex =
setting.get(SettingBoxKey.replySortType, defaultValue: 0) as int;
if (deaultReplySortIndex == 2) {
if (defaultReplySortIndex == 2) {
setting.put(SettingBoxKey.replySortType, 0);
deaultReplySortIndex = 0;
defaultReplySortIndex = 0;
}
_sortType = ReplySortType.values[deaultReplySortIndex];
_sortType = ReplySortType.values[defaultReplySortIndex];
sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels;
}

View File

@ -26,7 +26,7 @@ import 'package:pilipala/utils/utils.dart';
import 'reply_save.dart';
import 'zan.dart';
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
class ReplyItem extends StatelessWidget {
const ReplyItem({
@ -235,32 +235,33 @@ class ReplyItem extends StatelessWidget {
// title
Container(
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
child: !replySave
? LayoutBuilder(builder:
(BuildContext context, BoxConstraints boxConstraints) {
String text = replyItem?.content?.message ?? '';
bool didExceedMaxLines = false;
final double maxWidth = boxConstraints.maxWidth;
TextPainter? textPainter;
final int maxLines =
replyItem!.content!.isText! && replyLevel == '1'
? 6
: 999;
try {
textPainter = TextPainter(
text: TextSpan(text: text),
maxLines: maxLines,
textDirection: Directionality.of(context),
);
textPainter.layout(maxWidth: maxWidth);
didExceedMaxLines = textPainter.didExceedMaxLines;
} catch (e) {
debugPrint('Error while measuring text: $e');
didExceedMaxLines = false;
}
return replyContent(context, didExceedMaxLines, textPainter);
})
: replyContent(context, false, null),
child: Text.rich(
overflow: TextOverflow.ellipsis,
maxLines:
replyLevel == '1' && replyItem!.content!.isText! ? 5 : 999,
style: const TextStyle(height: 1.75),
TextSpan(
children: [
if (replyItem!.isTop!)
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
),
),
buildContent(
context,
replyItem!,
replyReply,
null,
),
],
),
),
),
// 操作区域
bottonAction(context, replyItem!.replyControl, replySave),
@ -281,36 +282,6 @@ class ReplyItem extends StatelessWidget {
);
}
Widget replyContent(
BuildContext context, bool? didExceedMaxLines, TextPainter? textPainter) {
return Text.rich(
style: const TextStyle(height: 1.75),
TextSpan(
children: [
if (replyItem!.isTop!)
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
),
),
buildContent(
context,
replyItem!,
replyReply,
null,
didExceedMaxLines ?? false,
textPainter,
),
],
),
);
}
// 感谢、回复、复制
Widget bottonAction(BuildContext context, replyControl, replySave) {
ColorScheme colorScheme = Theme.of(context).colorScheme;
@ -493,8 +464,12 @@ class ReplyItemRow extends StatelessWidget {
fs: 9,
),
),
buildContent(context, replies![i], replyReply,
replyItem, false, null),
buildContent(
context,
replies![i],
replyReply,
replyItem,
),
],
),
),
@ -540,8 +515,6 @@ InlineSpan buildContent(
replyItem,
replyReply,
fReplyItem,
bool didExceedMaxLines,
TextPainter? textPainter,
) {
final String routePath = Get.currentRoute;
bool isVideoPage = routePath.startsWith('/video');
@ -553,25 +526,6 @@ InlineSpan buildContent(
final content = replyItem.content;
final List<InlineSpan> spanChilds = <InlineSpan>[];
if (didExceedMaxLines && content.message != '') {
final textSize = textPainter!.size;
var position = textPainter.getPositionForOffset(
Offset(
textSize.width,
textSize.height,
),
);
final endOffset = textPainter.getOffsetBefore(position.offset);
if (endOffset != null && endOffset > 0) {
content.message = content.message.substring(0, endOffset);
} else {
content.message = content.message.substring(0, position.offset);
}
} else {
content.message = content.message2;
}
// 投票
if (content.vote.isNotEmpty) {
content.message.splitMapJoin(RegExp(r"\{vote:.*?\}"),
@ -921,17 +875,6 @@ InlineSpan buildContent(
}
}
if (didExceedMaxLines) {
spanChilds.add(
TextSpan(
text: '\n查看更多',
style: TextStyle(
color: colorScheme.primary,
),
),
);
}
// 图片渲染
if (content.pictures.isNotEmpty) {
final List<String> picList = <String>[];

View File

@ -42,7 +42,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
late VideoReplyReplyController _videoReplyReplyController;
late AnimationController replyAnimationCtl;
final Box<dynamic> localCache = GStrorage.localCache;
final Box<dynamic> localCache = GStorage.localCache;
Future? _futureBuilderFuture;
late ScrollController scrollController;

View File

@ -54,8 +54,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
Rx<PlayerStatus> playerStatus = PlayerStatus.playing.obs;
double doubleOffset = 0;
final Box<dynamic> localCache = GStrorage.localCache;
final Box<dynamic> setting = GStrorage.setting;
final Box<dynamic> localCache = GStorage.localCache;
final Box<dynamic> setting = GStorage.setting;
late double statusBarHeight;
final double videoHeight = Get.size.width * 9 / 16;
late Future _futureBuilderFuture;
@ -101,7 +101,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
videoSourceInit();
appbarStreamListen();
fullScreenStatusListener();
if (autoPlayEnable) {
fullScreenStatusListener();
}
if (Platform.isAndroid) {
floating = vdCtr.floating!;
}
@ -137,7 +139,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
autoEnterPip(status: status);
if (status == PlayerStatus.completed) {
// 结束播放退出全屏
if (autoExitFullcreen) {
if (autoExitFullcreen && plPlayerController!.isFullScreen.value) {
plPlayerController!.triggerFullScreen(status: false);
}
shutdownTimerService.handleWaitingFinished();
@ -184,6 +186,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
await vdCtr.playerInit(autoplay: true);
plPlayerController = vdCtr.plPlayerController;
plPlayerController!.addStatusLister(playerListener);
fullScreenStatusListener();
vdCtr.autoPlay.value = true;
vdCtr.isShowCover.value = false;
isShowing.value = true;

View File

@ -52,8 +52,8 @@ class _HeaderControlState extends State<HeaderControl> {
static const TextStyle subTitleStyle = TextStyle(fontSize: 12);
static const TextStyle titleStyle = TextStyle(fontSize: 14);
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
final Box<dynamic> localCache = GStrorage.localCache;
final Box<dynamic> videoStorage = GStrorage.video;
final Box<dynamic> localCache = GStorage.localCache;
final Box<dynamic> videoStorage = GStorage.video;
late List<double> speedsList;
double buttonSpace = 8;
RxBool isFullScreen = false.obs;

View File

@ -20,7 +20,7 @@ class WhisperDetailController extends GetxController {
//表情转换图片规则
RxList<dynamic> eInfos = [].obs;
final TextEditingController replyContentController = TextEditingController();
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
List emoteList = [];
List<String> picList = [];

View File

@ -30,7 +30,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
late double emoteHeight = 230.0;
double keyboardHeight = 0.0; // 键盘高度
RxString toolbarType = ''.obs;
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
@override
void initState() {

View File

@ -56,8 +56,7 @@ class ChatItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
bool isOwner =
item.senderUid == GStrorage.userInfo.get('userInfoCache').mid;
bool isOwner = item.senderUid == GStorage.userInfo.get('userInfoCache').mid;
bool isPic = item.msgType == MsgType.pic.value; // 图片
bool isText = item.msgType == MsgType.text.value; // 文本

View File

@ -27,9 +27,9 @@ import '../../models/video/subTitile/content.dart';
import '../../models/video/subTitile/result.dart';
// import 'package:wakelock_plus/wakelock_plus.dart';
Box videoStorage = GStrorage.video;
Box setting = GStrorage.setting;
Box localCache = GStrorage.localCache;
Box videoStorage = GStorage.video;
Box setting = GStorage.setting;
Box localCache = GStorage.localCache;
class PlPlayerController {
Player? _videoPlayerController;
@ -1033,6 +1033,8 @@ class PlPlayerController {
if (progress >= content['from']! && progress <= content['to']!) {
subtitleContent.value = content['content']!;
return;
} else {
subtitleContent.value = '';
}
}
}

View File

@ -82,7 +82,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
final RxDouble _distance = 0.0.obs;
final RxBool _volumeInterceptEventStream = false.obs;
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
late FullScreenMode mode;
late int defaultBtmProgressBehavior;
late bool enableQuickDouble;

View File

@ -66,7 +66,7 @@ import '../pages/whisper/index.dart';
import '../pages/whisper_detail/index.dart';
import '../utils/storage.dart';
Box<dynamic> setting = GStrorage.setting;
Box<dynamic> setting = GStorage.setting;
class Routes {
static final List<GetPage<dynamic>> getPages = [

View File

@ -24,7 +24,7 @@ Future<VideoPlayerServiceHandler> initAudioService() async {
class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
static final List<MediaItem> _item = [];
Box setting = GStrorage.setting;
Box setting = GStorage.setting;
bool enableBackgroundPlay = false;
PlPlayerController player = PlPlayerController();

View File

@ -9,7 +9,7 @@ void DisableBatteryOpt() async {
}
// 本地缓存中读取 是否禁用了电池优化 默认未禁用
bool isDisableBatteryOptLocal =
GStrorage.localCache.get('isDisableBatteryOptLocal', defaultValue: false);
GStorage.localCache.get('isDisableBatteryOptLocal', defaultValue: false);
if (!isDisableBatteryOptLocal) {
final isBatteryOptimizationDisabled =
await DisableBatteryOptimization.isBatteryOptimizationDisabled;
@ -17,11 +17,11 @@ void DisableBatteryOpt() async {
final hasDisabled = await DisableBatteryOptimization
.showDisableBatteryOptimizationSettings();
// 设置为已禁用
GStrorage.localCache.put('isDisableBatteryOptLocal', hasDisabled == true);
GStorage.localCache.put('isDisableBatteryOptLocal', hasDisabled == true);
}
}
bool isManufacturerBatteryOptimizationDisabled = GStrorage.localCache
bool isManufacturerBatteryOptimizationDisabled = GStorage.localCache
.get('isManufacturerBatteryOptimizationDisabled', defaultValue: false);
if (!isManufacturerBatteryOptimizationDisabled) {
final isManBatteryOptimizationDisabled = await DisableBatteryOptimization
@ -33,7 +33,7 @@ void DisableBatteryOpt() async {
"按照步骤操作以禁用电池优化,以保证应用在后台正常运行",
);
// 设置为已禁用
GStrorage.localCache.put(
GStorage.localCache.put(
'isManufacturerBatteryOptimizationDisabled', hasDisabled == true);
}
}

View File

@ -9,8 +9,8 @@ class Data {
}
static Future historyStatus() async {
Box localCache = GStrorage.localCache;
Box userInfoCache = GStrorage.userInfo;
Box localCache = GStorage.localCache;
Box userInfoCache = GStorage.userInfo;
if (userInfoCache.get('userInfoCache') == null) {
return;
}

View File

@ -2,7 +2,7 @@ import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
import 'storage.dart';
Box<dynamic> setting = GStrorage.setting;
Box<dynamic> setting = GStorage.setting;
void feedBack() {
// 设置中是否开启
final bool enable =

View File

@ -5,10 +5,10 @@ import 'package:pilipala/plugin/pl_player/models/play_speed.dart';
import 'package:pilipala/utils/storage.dart';
import '../models/common/index.dart';
Box setting = GStrorage.setting;
Box localCache = GStrorage.localCache;
Box videoStorage = GStrorage.video;
Box userInfoCache = GStrorage.userInfo;
Box setting = GStorage.setting;
Box localCache = GStorage.localCache;
Box videoStorage = GStorage.video;
Box userInfoCache = GStorage.userInfo;
class GlobalDataCache {
late int imgQuality;

View File

@ -18,9 +18,6 @@ import 'package:pilipala/utils/storage.dart';
import 'package:uuid/uuid.dart';
class LoginUtils {
static Box userInfoCache = GStrorage.userInfo;
static Box localCache = GStrorage.localCache;
static Future refreshLoginStatus(bool status) async {
try {
// 更改我的页面登录状态
@ -76,7 +73,7 @@ class LoginUtils {
if (result['status'] && result['data'].isLogin) {
SmartDialog.showToast('登录成功');
try {
Box userInfoCache = GStrorage.userInfo;
Box userInfoCache = GStorage.userInfo;
if (!userInfoCache.isOpen) {
userInfoCache = await Hive.openBox('userInfo');
}

View File

@ -13,7 +13,7 @@ class RecommendFilter {
}
static void update() {
var setting = GStrorage.setting;
var setting = GStorage.setting;
// filterUnfollowedRatio =
// setting.get(SettingBoxKey.filterUnfollowedRatio, defaultValue: 0);
minDurationForRcmd =

View File

@ -3,7 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pilipala/models/user/info.dart';
class GStrorage {
class GStorage {
static late final Box<dynamic> userInfo;
static late final Box<dynamic> localCache;
static late final Box<dynamic> setting;

View File

@ -9,7 +9,7 @@ import '../http/index.dart';
import 'storage.dart';
class WbiSign {
static Box<dynamic> localCache = GStrorage.localCache;
static Box<dynamic> localCache = GStorage.localCache;
final List<int> mixinKeyEncTab = <int>[
46,
47,

View File

@ -533,18 +533,18 @@ packages:
dependency: transitive
description:
name: file_selector_linux
sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492"
sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.2+1"
version: "0.9.3"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: cb284e267f8e2a45a904b5c094d2ba51d0aabfc20b1538ab786d9ef7dc2bf75c
sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.4+1"
version: "0.9.4+2"
file_selector_platform_interface:
dependency: transitive
description:
@ -557,10 +557,10 @@ packages:
dependency: transitive
description:
name: file_selector_windows
sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69"
sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.3+2"
version: "0.9.3+3"
fixnum:
dependency: transitive
description:
@ -842,10 +842,10 @@ packages:
dependency: transitive
description:
name: image_picker_ios
sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447"
sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.12"
version: "0.8.12+1"
image_picker_linux:
dependency: transitive
description:
@ -1438,10 +1438,10 @@ packages:
dependency: "direct main"
description:
name: scrollview_observer
sha256: fa408bcfd41e19da841eb53fc471f8f952d5ef818b854d2505c4bb3f0c876381
sha256: "8537ba32e5a15ade301e5c77ae858fd8591695defaad1821eca9eeb4ac28a157"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.22.0"
version: "1.23.0"
sentry:
dependency: transitive
description: