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:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.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/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/scrollview_observer.dart';
import '../models/common/video_episode_type.dart'; import '../models/common/video_episode_type.dart';
@ -20,6 +22,8 @@ class EpisodeBottomSheet {
final double? sheetHeight; final double? sheetHeight;
bool isFullScreen = false; bool isFullScreen = false;
final UgcSeason? ugcSeason; final UgcSeason? ugcSeason;
final int? currentEpisodeIndex;
final int? currentIndex;
EpisodeBottomSheet({ EpisodeBottomSheet({
required this.episodes, required this.episodes,
@ -30,6 +34,8 @@ class EpisodeBottomSheet {
this.sheetHeight, this.sheetHeight,
this.isFullScreen = false, this.isFullScreen = false,
this.ugcSeason, this.ugcSeason,
this.currentEpisodeIndex,
this.currentIndex,
}); });
Widget buildShowContent() { Widget buildShowContent() {
@ -42,6 +48,8 @@ class EpisodeBottomSheet {
sheetHeight: sheetHeight, sheetHeight: sheetHeight,
isFullScreen: isFullScreen, isFullScreen: isFullScreen,
ugcSeason: ugcSeason, ugcSeason: ugcSeason,
currentEpisodeIndex: currentEpisodeIndex,
currentIndex: currentIndex,
); );
} }
@ -67,6 +75,8 @@ class PagesBottomSheet extends StatefulWidget {
this.sheetHeight, this.sheetHeight,
this.isFullScreen = false, this.isFullScreen = false,
this.ugcSeason, this.ugcSeason,
this.currentEpisodeIndex,
this.currentIndex,
}); });
final List<dynamic> episodes; final List<dynamic> episodes;
@ -77,41 +87,38 @@ class PagesBottomSheet extends StatefulWidget {
final double? sheetHeight; final double? sheetHeight;
final bool isFullScreen; final bool isFullScreen;
final UgcSeason? ugcSeason; final UgcSeason? ugcSeason;
final int? currentEpisodeIndex;
final int? currentIndex;
@override @override
State<PagesBottomSheet> createState() => _PagesBottomSheetState(); State<PagesBottomSheet> createState() => _PagesBottomSheetState();
} }
class _PagesBottomSheetState extends State<PagesBottomSheet> { class _PagesBottomSheetState extends State<PagesBottomSheet>
with TickerProviderStateMixin {
final ScrollController _listScrollController = ScrollController(); final ScrollController _listScrollController = ScrollController();
late ListObserverController _listObserverController; late ListObserverController _listObserverController;
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
late int currentIndex; 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 @override
void initState() { void initState() {
super.initState(); super.initState();
currentIndex = currentIndex = widget.currentIndex ??
widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid); widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid);
_listObserverController = _scrollToInit();
ListObserverController(controller: _listScrollController); _scrollPositionInit();
if (widget.dataType == VideoEpidoesType.videoEpisode) { if (widget.dataType == VideoEpidoesType.videoEpisode) {
_listObserverController.initialIndexModel = ObserverIndexPositionModel( _videoDetailController = Get.find<VideoDetailController>(tag: heroTag);
index: currentIndex, _getSubscribeStatus();
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);
}
});
} }
String prefix() { String prefix() {
@ -126,9 +133,117 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
return '选集'; 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 @override
void dispose() { void dispose() {
try {
_listObserverController.controller?.dispose(); _listObserverController.controller?.dispose();
_listScrollController.dispose();
for (var element in _listObserverControllerList!) {
element.controller?.dispose();
}
for (var element in _listScrollControllerList!) {
element.dispose();
}
} catch (_) {}
super.dispose(); super.dispose();
} }
@ -145,23 +260,32 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
isFullScreen: widget.isFullScreen, isFullScreen: widget.isFullScreen,
), ),
if (widget.ugcSeason != null) ...[ if (widget.ugcSeason != null) ...[
UgcSeasonBuild(ugcSeason: widget.ugcSeason!), UgcSeasonBuild(
ugcSeason: widget.ugcSeason!,
isSubscribe: isSubscribe,
isVisible: isVisible,
changeFucCall: _changeSubscribeStatus,
changeVisible: _changeVisible,
),
], ],
Expanded( Expanded(
child: Material( child: Material(
child: widget.dataType == VideoEpidoesType.videoEpisode child: widget.dataType == VideoEpidoesType.videoEpisode
? (widget.ugcSeason!.sections!.length == 1
? ListViewObserver( ? ListViewObserver(
controller: _listObserverController, controller: _listObserverController,
child: ListView.builder( child: ListView.builder(
controller: _listScrollController, controller: _listScrollController,
itemCount: widget.episodes.length + 1, itemCount: widget.episodes.length + 1,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
bool isLastItem = index == widget.episodes.length; bool isLastItem =
index == widget.episodes.length;
bool isCurrentIndex = currentIndex == index; bool isCurrentIndex = currentIndex == index;
return isLastItem return isLastItem
? SizedBox( ? SizedBox(
height: height: MediaQuery.of(context)
MediaQuery.of(context).padding.bottom + .padding
.bottom +
20, 20,
) )
: EpisodeListItem( : EpisodeListItem(
@ -175,6 +299,7 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
}, },
), ),
) )
: buildTabBar())
: Padding( : Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12.0), // 设置左右间距为12 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 { class TitleBar extends StatelessWidget {
@ -507,77 +691,134 @@ class EpisodeGridItem extends StatelessWidget {
class UgcSeasonBuild extends StatelessWidget { class UgcSeasonBuild extends StatelessWidget {
final UgcSeason ugcSeason; final UgcSeason ugcSeason;
final RxInt isSubscribe;
final bool isVisible;
final Function changeFucCall;
final Function changeVisible;
const UgcSeasonBuild({ const UgcSeasonBuild({
Key? key, Key? key,
required this.ugcSeason, required this.ugcSeason,
required this.isSubscribe,
required this.isVisible,
required this.changeFucCall,
required this.changeVisible,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( final ThemeData theme = Theme.of(context);
padding: const EdgeInsets.fromLTRB(12, 0, 12, 8), final Color outline = theme.colorScheme.outline;
color: Theme.of(context).colorScheme.surface, 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( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Divider( Divider(height: 1, thickness: 1, color: dividerColor),
height: 1,
thickness: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
const SizedBox(height: 10), 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( Row(
children: [ children: [
Expanded( Expanded(
child: Text(ugcSeason.intro ?? '', child: Text(
style: TextStyle( '合集:${ugcSeason.title}',
color: Theme.of(context).colorScheme.outline)), 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), const SizedBox(height: 4),
Text.rich( Text.rich(
TextSpan( TextSpan(
style: TextStyle( style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, fontSize: labelMedium.fontSize, color: outline),
color: Theme.of(context).colorScheme.outline,
),
children: [ children: [
TextSpan(text: '${Utils.numFormat(ugcSeason.stat!.view)}播放'), TextSpan(
text: '${Utils.numFormat(ugcSeason.stat!.view)}播放'),
const TextSpan(text: ' · '), const TextSpan(text: ' · '),
TextSpan(text: '${Utils.numFormat(ugcSeason.stat!.danmaku)}弹幕'), TextSpan(
text:
'${Utils.numFormat(ugcSeason.stat!.danmaku)}弹幕'),
], ],
), ),
), ),
const SizedBox(height: 14), const SizedBox(height: 14),
Divider( Align(
height: 1, alignment: Alignment.center,
thickness: 1, child: Material(
color: Theme.of(context).dividerColor.withOpacity(0.1), 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),
], ],
), ),
)
: 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),
),
),
),
); );
} }
} }

View File

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

View File

@ -495,7 +495,7 @@ class Api {
static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi'; 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'; 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 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'; import 'index.dart';
class CommonHttp { 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 passBaseUrl = 'https://passport.bilibili.com';
static const String messageBaseUrl = 'https://message.bilibili.com'; static const String messageBaseUrl = 'https://message.bilibili.com';
static const String bangumiBaseUrl = 'https://bili.meark.me'; static const String bangumiBaseUrl = 'https://bili.meark.me';
static const String sponsorBlockBaseUrl = 'https://www.bsbsb.top';
static const List<int> validateStatusCodes = [ static const List<int> validateStatusCodes = [
302, 302,
304, 304,

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/utils/id_utils.dart';
import '../common/constants.dart'; import '../common/constants.dart';
import '../models/common/reply_type.dart'; import '../models/common/reply_type.dart';
import '../models/home/rcmd/result.dart'; import '../models/home/rcmd/result.dart';
@ -24,11 +25,11 @@ import 'init.dart';
/// 返回{'status': bool, 'data': List} /// 返回{'status': bool, 'data': List}
/// view层根据 status 判断渲染逻辑 /// view层根据 status 判断渲染逻辑
class VideoHttp { class VideoHttp {
static Box localCache = GStrorage.localCache; static Box localCache = GStorage.localCache;
static Box setting = GStrorage.setting; static Box setting = GStorage.setting;
static bool enableRcmdDynamic = static bool enableRcmdDynamic =
setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true); 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 { 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: { var res = await Request().get(Api.getSubtitleConfig, data: {
'cid': cid, 'cid': cid,
'bvid': bvid, if (bvid != null) 'bvid': bvid,
if (aid != null) 'aid': aid,
}); });
try { try {
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
@ -559,4 +561,50 @@ class VideoHttp {
final List body = res.data['body']; final List body = res.data['body'];
return {'content': content, 'body': 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(); MediaKit.ensureInitialized();
await SystemChrome.setPreferredOrientations( await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
await GStrorage.init(); await GStorage.init();
clearLogs(); clearLogs();
Request(); Request();
await Request.setCookie(); await Request.setCookie();
@ -73,7 +73,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Box setting = GStrorage.setting; Box setting = GStorage.setting;
// 主题色 // 主题色
Color defaultColor = Color defaultColor =
colorThemeTypes[setting.get(SettingBoxKey.customColor, defaultValue: 0)] 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']; avatar = json['avatar'];
level = json['level_info']['current_level']; level = json['level_info']['current_level'];
pendant = Pendant.fromJson(json['pendant']); pendant = Pendant.fromJson(json['pendant']);
officialVerify = json['officia_verify']; officialVerify = json['official_verify'];
vip = json['vip']; vip = json['vip'];
fansDetail = json['fans_detail']; fansDetail = json['fans_detail'];
userSailing = json['user_sailing'] != null userSailing = json['user_sailing'] != null

View File

@ -641,6 +641,7 @@ class EpisodeItem {
this.page, this.page,
this.bvid, this.bvid,
this.cover, this.cover,
this.pages,
}); });
int? seasonId; int? seasonId;
int? sectionId; int? sectionId;
@ -655,6 +656,7 @@ class EpisodeItem {
int? pubdate; int? pubdate;
int? duration; int? duration;
Stat? stat; Stat? stat;
List<Page>? pages;
EpisodeItem.fromJson(Map<String, dynamic> json) { EpisodeItem.fromJson(Map<String, dynamic> json) {
seasonId = json['season_id']; seasonId = json['season_id'];
@ -670,6 +672,7 @@ class EpisodeItem {
pubdate = json['arc']['pubdate']; pubdate = json['arc']['pubdate'];
duration = json['arc']['duration']; duration = json['arc']['duration'];
stat = Stat.fromJson(json['arc']['stat']); 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']; 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; RxInt total = 0.obs;
int _currentPage = 1; int _currentPage = 1;
bool isLoadingMore = true; bool isLoadingMore = true;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
RxBool userLogin = false.obs; RxBool userLogin = false.obs;
late int mid; late int mid;
var userInfo; var userInfo;

View File

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

View File

@ -116,7 +116,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
String heroTag = Get.arguments['heroTag']; String heroTag = Get.arguments['heroTag'];
late final BangumiIntroController bangumiIntroController; late final BangumiIntroController bangumiIntroController;
late final VideoDetailController videoDetailCtr; late final VideoDetailController videoDetailCtr;
Box localCache = GStrorage.localCache; Box localCache = GStorage.localCache;
late double sheetHeight; late double sheetHeight;
int? cid; int? cid;
bool isProcessing = false; 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/common/widgets/stat/view.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
Box localCache = GStrorage.localCache; Box localCache = GStorage.localCache;
late double sheetHeight; late double sheetHeight;
class IntroDetail extends StatelessWidget { class IntroDetail extends StatelessWidget {

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ class DynamicDetailController extends GetxController {
ReplySortType _sortType = ReplySortType.time; ReplySortType _sortType = ReplySortType.time;
RxString sortTypeTitle = ReplySortType.time.titles.obs; RxString sortTypeTitle = ReplySortType.time.titles.obs;
RxString sortTypeLabel = ReplySortType.time.labels.obs; RxString sortTypeLabel = ReplySortType.time.labels.obs;
Box setting = GStrorage.setting; Box setting = GStorage.setting;
RxInt replyReqCode = 200.obs; RxInt replyReqCode = 200.obs;
bool isEnd = false; bool isEnd = false;
@ -37,13 +37,13 @@ class DynamicDetailController extends GetxController {
acount.value = acount.value =
int.parse(item!.modules!.moduleStat!.comment!.count ?? '0'); int.parse(item!.modules!.moduleStat!.comment!.count ?? '0');
} }
int deaultReplySortIndex = int defaultReplySortIndex =
setting.get(SettingBoxKey.replySortType, defaultValue: 0); setting.get(SettingBoxKey.replySortType, defaultValue: 0);
if (deaultReplySortIndex == 2) { if (defaultReplySortIndex == 2) {
setting.put(SettingBoxKey.replySortType, 0); setting.put(SettingBoxKey.replySortType, 0);
deaultReplySortIndex = 0; defaultReplySortIndex = 0;
} }
_sortType = ReplySortType.values[deaultReplySortIndex]; _sortType = ReplySortType.values[defaultReplySortIndex];
sortTypeTitle.value = _sortType.titles; sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels; 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/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/models/dynamics/result.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/feed_back.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/main_stream.dart';
import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/route_push.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
@ -19,7 +17,6 @@ import 'package:pilipala/utils/storage.dart';
import '../mine/controller.dart'; import '../mine/controller.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/dynamic_panel.dart'; import 'widgets/dynamic_panel.dart';
import 'up_dynamic/route_panel.dart';
import 'widgets/up_panel.dart'; import 'widgets/up_panel.dart';
class DynamicsPage extends StatefulWidget { class DynamicsPage extends StatefulWidget {
@ -35,7 +32,7 @@ class _DynamicsPageState extends State<DynamicsPage>
final MineController mineController = Get.put(MineController()); final MineController mineController = Get.put(MineController());
late Future _futureBuilderFuture; late Future _futureBuilderFuture;
late Future _futureBuilderFutureUp; late Future _futureBuilderFutureUp;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
late ScrollController scrollController; late ScrollController scrollController;
@override @override
@ -209,21 +206,7 @@ class _DynamicsPageState extends State<DynamicsPage>
return Obx( return Obx(
() => UpPanel( () => UpPanel(
upData: _dynamicsController.upData.value, upData: _dynamicsController.upData.value,
onClickUpCb: (data) { dynamicsController: _dynamicsController,
if (GlobalDataCache().enableDynamicSwitch) {
Navigator.push(
context,
PlPopupRoute(
child: OverlayPanel(
ctr: _dynamicsController,
upInfo: data,
),
),
);
} else {
_dynamicsController.onTapUp(data);
}
},
), ),
); );
} else { } else {

View File

@ -4,18 +4,22 @@ 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/models/dynamics/up.dart'; import 'package:pilipala/models/dynamics/up.dart';
import 'package:pilipala/models/live/item.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/feed_back.dart';
import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/global_data_cache.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import '../controller.dart';
import '../up_dynamic/route_panel.dart';
class UpPanel extends StatefulWidget { class UpPanel extends StatefulWidget {
final FollowUpModel upData; final FollowUpModel upData;
final Function? onClickUpCb; final DynamicsController dynamicsController;
const UpPanel({ const UpPanel({
super.key, super.key,
required this.upData, required this.upData,
this.onClickUpCb, required this.dynamicsController,
}); });
@override @override
@ -39,7 +43,15 @@ class _UpPanelState extends State<UpPanel> {
void onClickUp(data, i) { void onClickUp(data, i) {
currentMid.value = data.mid; 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) { void onClickUpAni(data, i) {
@ -49,7 +61,7 @@ class _UpPanelState extends State<UpPanel> {
final upLen = upList.length; final upLen = upList.length;
currentMid.value = data.mid; currentMid.value = data.mid;
widget.onClickUpCb?.call(data); widget.dynamicsController.onTapUp(data);
double moveDistance = 0.0; double moveDistance = 0.0;
final totalItemsWidth = itemWidth * (upLen + liveLen); final totalItemsWidth = itemWidth * (upLen + liveLen);

View File

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

View File

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

View File

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

View File

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

View File

@ -14,12 +14,12 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
late TabController tabController; late TabController tabController;
late List tabsCtrList; late List tabsCtrList;
late List<Widget> tabsPageList; late List<Widget> tabsPageList;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
Box settingStorage = GStrorage.setting; Box settingStorage = GStorage.setting;
RxBool userLogin = false.obs; RxBool userLogin = false.obs;
RxString userFace = ''.obs; RxString userFace = ''.obs;
var userInfo; var userInfo;
Box setting = GStrorage.setting; Box setting = GStorage.setting;
late final StreamController<bool> searchBarStream = late final StreamController<bool> searchBarStream =
StreamController<bool>.broadcast(); StreamController<bool>.broadcast();
late bool hideSearchBar; 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/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
class HomeAppBar extends StatelessWidget { class HomeAppBar extends StatelessWidget {
const HomeAppBar({super.key}); const HomeAppBar({super.key});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,8 @@ class MemberSeasonsController extends GetxController {
seasonId = int.parse(Get.parameters['seasonId']!); seasonId = int.parse(Get.parameters['seasonId']!);
} }
if (category == '1') { 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'); getSeasonDetail('onLoad');
} }
if (category == '1') { if (category == '1') {
if (seasonId != null) {
getSeasonDetail('onLoad');
}
if (seriesId != null) {
getSeriesDetail('onLoad'); 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() { void initState() {
super.initState(); super.initState();
category = Get.parameters['category']!; category = Get.parameters['category']!;
_futureBuilderFuture = category == '0' _futureBuilderFuture = _memberSeasonsController.onRefresh();
? _memberSeasonsController.getSeasonDetail('onRefresh')
: _memberSeasonsController.getSeriesDetail('onRefresh');
scrollController = _memberSeasonsController.scrollController; scrollController = _memberSeasonsController.scrollController;
scrollController.addListener( scrollController.addListener(
() { () {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ class FontSizeSelectPage extends StatefulWidget {
} }
class _FontSizeSelectPageState extends State<FontSizeSelectPage> { 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]; 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 minsize;
late double maxSize; late double maxSize;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ import '../../models/user/sub_folder.dart';
class SubController extends GetxController { class SubController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
Rx<SubFolderModelData> subFolderData = SubFolderModelData().obs; Rx<SubFolderModelData> subFolderData = SubFolderModelData().obs;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
UserInfoData? userInfo; UserInfoData? userInfo;
int currentPage = 1; int currentPage = 1;
int pageSize = 20; 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:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:pilipala/http/common.dart';
import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/user.dart';
import 'package:pilipala/http/video.dart'; import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/common/search_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/later.dart';
import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/models/video/play/quality.dart';
import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video/play/url.dart';
@ -68,9 +71,9 @@ class VideoDetailController extends GetxController
RxBool enableHA = false.obs; RxBool enableHA = false.obs;
/// 本地存储 /// 本地存储
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
Box localCache = GStrorage.localCache; Box localCache = GStorage.localCache;
Box setting = GStrorage.setting; Box setting = GStorage.setting;
RxInt oid = 0.obs; RxInt oid = 0.obs;
// 评论id 请求楼中楼评论使用 // 评论id 请求楼中楼评论使用
@ -120,6 +123,8 @@ class VideoDetailController extends GetxController
RxBool isWatchLaterVisible = false.obs; RxBool isWatchLaterVisible = false.obs;
RxString watchLaterTitle = ''.obs; RxString watchLaterTitle = ''.obs;
RxInt watchLaterCount = 0.obs; RxInt watchLaterCount = 0.obs;
List<SegmentDataModel> skipSegments = <SegmentDataModel>[];
int? lastPosition;
@override @override
void onInit() { void onInit() {
@ -188,6 +193,11 @@ class VideoDetailController extends GetxController
tabCtr.addListener(() { tabCtr.addListener(() {
onTabChanged(); onTabChanged();
}); });
/// 仅投稿视频skip
if (videoType == SearchType.video) {
querySkipSegments();
}
} }
showReplyReplyPanel(oid, fRpid, firstFloor, currentReply, loadMore) { showReplyReplyPanel(oid, fRpid, firstFloor, currentReply, loadMore) {
@ -305,6 +315,7 @@ class VideoDetailController extends GetxController
plPlayerController.headerControl = headerControl; plPlayerController.headerControl = headerControl;
plPlayerController.subtitles.value = subtitles; plPlayerController.subtitles.value = subtitles;
onPositionChanged();
} }
// 视频链接 // 视频链接
@ -706,6 +717,53 @@ class VideoDetailController extends GetxController
isWatchLaterVisible.value = tabCtr.index == 0; 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 @override
void onClose() { void onClose() {
super.onClose(); super.onClose();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ import 'package:pilipala/utils/utils.dart';
import 'reply_save.dart'; import 'reply_save.dart';
import 'zan.dart'; import 'zan.dart';
Box setting = GStrorage.setting; Box setting = GStorage.setting;
class ReplyItem extends StatelessWidget { class ReplyItem extends StatelessWidget {
const ReplyItem({ const ReplyItem({
@ -235,55 +235,10 @@ class ReplyItem extends StatelessWidget {
// title // title
Container( Container(
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4), margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
child: !replySave child: Text.rich(
? LayoutBuilder(builder: overflow: TextOverflow.ellipsis,
(BuildContext context, BoxConstraints boxConstraints) { maxLines:
String text = replyItem?.content?.message ?? ''; replyLevel == '1' && replyItem!.content!.isText! ? 5 : 999,
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),
),
// 操作区域
bottonAction(context, replyItem!.replyControl, replySave),
// 一楼的评论
if ((replyItem!.rcount! > 0) && showReplyRow!) ...[
Padding(
padding: const EdgeInsets.only(top: 5, bottom: 12),
child: ReplyItemRow(
replies: replyItem!.replies,
replyControl: replyItem!.replyControl,
// f_rpid: replyItem!.rpid,
replyItem: replyItem,
replyReply: replyReply,
),
),
],
],
);
}
Widget replyContent(
BuildContext context, bool? didExceedMaxLines, TextPainter? textPainter) {
return Text.rich(
style: const TextStyle(height: 1.75), style: const TextStyle(height: 1.75),
TextSpan( TextSpan(
children: [ children: [
@ -303,11 +258,27 @@ class ReplyItem extends StatelessWidget {
replyItem!, replyItem!,
replyReply, replyReply,
null, null,
didExceedMaxLines ?? false,
textPainter,
), ),
], ],
), ),
),
),
// 操作区域
bottonAction(context, replyItem!.replyControl, replySave),
// 一楼的评论
if ((replyItem!.rcount! > 0) && showReplyRow!) ...[
Padding(
padding: const EdgeInsets.only(top: 5, bottom: 12),
child: ReplyItemRow(
replies: replyItem!.replies,
replyControl: replyItem!.replyControl,
// f_rpid: replyItem!.rpid,
replyItem: replyItem,
replyReply: replyReply,
),
),
],
],
); );
} }
@ -493,8 +464,12 @@ class ReplyItemRow extends StatelessWidget {
fs: 9, fs: 9,
), ),
), ),
buildContent(context, replies![i], replyReply, buildContent(
replyItem, false, null), context,
replies![i],
replyReply,
replyItem,
),
], ],
), ),
), ),
@ -540,8 +515,6 @@ InlineSpan buildContent(
replyItem, replyItem,
replyReply, replyReply,
fReplyItem, fReplyItem,
bool didExceedMaxLines,
TextPainter? textPainter,
) { ) {
final String routePath = Get.currentRoute; final String routePath = Get.currentRoute;
bool isVideoPage = routePath.startsWith('/video'); bool isVideoPage = routePath.startsWith('/video');
@ -553,25 +526,6 @@ InlineSpan buildContent(
final content = replyItem.content; final content = replyItem.content;
final List<InlineSpan> spanChilds = <InlineSpan>[]; 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) { if (content.vote.isNotEmpty) {
content.message.splitMapJoin(RegExp(r"\{vote:.*?\}"), 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) { if (content.pictures.isNotEmpty) {
final List<String> picList = <String>[]; final List<String> picList = <String>[];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -56,8 +56,7 @@ class ChatItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isOwner = bool isOwner = item.senderUid == GStorage.userInfo.get('userInfoCache').mid;
item.senderUid == GStrorage.userInfo.get('userInfoCache').mid;
bool isPic = item.msgType == MsgType.pic.value; // 图片 bool isPic = item.msgType == MsgType.pic.value; // 图片
bool isText = item.msgType == MsgType.text.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 '../../models/video/subTitile/result.dart';
// import 'package:wakelock_plus/wakelock_plus.dart'; // import 'package:wakelock_plus/wakelock_plus.dart';
Box videoStorage = GStrorage.video; Box videoStorage = GStorage.video;
Box setting = GStrorage.setting; Box setting = GStorage.setting;
Box localCache = GStrorage.localCache; Box localCache = GStorage.localCache;
class PlPlayerController { class PlPlayerController {
Player? _videoPlayerController; Player? _videoPlayerController;
@ -1033,6 +1033,8 @@ class PlPlayerController {
if (progress >= content['from']! && progress <= content['to']!) { if (progress >= content['from']! && progress <= content['to']!) {
subtitleContent.value = content['content']!; subtitleContent.value = content['content']!;
return; return;
} else {
subtitleContent.value = '';
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import 'package:flutter/services.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'storage.dart'; import 'storage.dart';
Box<dynamic> setting = GStrorage.setting; Box<dynamic> setting = GStorage.setting;
void feedBack() { void feedBack() {
// 设置中是否开启 // 设置中是否开启
final bool enable = 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 'package:pilipala/utils/storage.dart';
import '../models/common/index.dart'; import '../models/common/index.dart';
Box setting = GStrorage.setting; Box setting = GStorage.setting;
Box localCache = GStrorage.localCache; Box localCache = GStorage.localCache;
Box videoStorage = GStrorage.video; Box videoStorage = GStorage.video;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStorage.userInfo;
class GlobalDataCache { class GlobalDataCache {
late int imgQuality; late int imgQuality;

View File

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

View File

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

View File

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

View File

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

View File

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