mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-14 22:50:06 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a17a2f5465 | ||
|
|
d8126a87cd | ||
|
|
0f7be5ec30 | ||
|
|
b9d223369a | ||
|
|
d97b2e223c | ||
|
|
230325d171 | ||
|
|
229901de96 | ||
|
|
d8c23a3d8c | ||
|
|
e87a46706e | ||
|
|
9ebf4b4533 | ||
|
|
125168cfb9 | ||
|
|
5983670c83 | ||
|
|
ba8d7b871c | ||
|
|
e2761836bf | ||
|
|
46cd633c4a | ||
|
|
cb3f72959f | ||
|
|
c1104c931b | ||
|
|
d4b005f6ae | ||
|
|
471c95abe8 |
@@ -47,6 +47,41 @@ Widget imageview(
|
||||
} else if (picArr.length == 2) {
|
||||
imageWidth = imageHeight = 2 * imageWidth;
|
||||
}
|
||||
BorderRadius borderRadius(index) {
|
||||
if (picArr.length == 1) {
|
||||
return BorderRadius.circular(12);
|
||||
}
|
||||
final int row = picArr.length == 4 ? 2 : 3;
|
||||
return BorderRadius.only(
|
||||
topLeft: Radius.circular(
|
||||
(index - row >= 0 ||
|
||||
((index - 1) >= 0 && (index - 1) % row < index % row))
|
||||
? 0
|
||||
: 12,
|
||||
),
|
||||
topRight: Radius.circular(
|
||||
(index - row >= 0 ||
|
||||
((index + 1) < picArr.length &&
|
||||
(index + 1) % row > index % row))
|
||||
? 0
|
||||
: 12,
|
||||
),
|
||||
bottomLeft: Radius.circular(
|
||||
(index + row < picArr.length ||
|
||||
((index - 1) >= 0 && (index - 1) % row < index % row))
|
||||
? 0
|
||||
: 12,
|
||||
),
|
||||
bottomRight: Radius.circular(
|
||||
(index + row < picArr.length ||
|
||||
((index + 1) < picArr.length &&
|
||||
(index + 1) % row > index % row))
|
||||
? 0
|
||||
: 12,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return NineGridView(
|
||||
type: NineGridType.weiBo,
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
@@ -75,14 +110,36 @@ Widget imageview(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: borderRadius(index),
|
||||
child: NetworkImgLayer(
|
||||
radius: 0,
|
||||
src: picArr[index].url,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
isLongPic: () => picArr[index].isLongPic,
|
||||
callback: () =>
|
||||
picArr[index].safeWidth <= picArr[index].safeHeight,
|
||||
getPlaceHolder: () {
|
||||
return Container(
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface
|
||||
.withOpacity(0.4),
|
||||
borderRadius: borderRadius(index),
|
||||
),
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/images/loading.png',
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
cacheWidth: imageWidth.cacheSize(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (picArr[index].isLongPic)
|
||||
|
||||
@@ -92,14 +92,12 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
int? currentIndex;
|
||||
|
||||
late List<bool> _thumbList;
|
||||
late int _quality;
|
||||
late final int _quality = GStorage.previewQ;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_quality =
|
||||
GStorage.setting.get(SettingBoxKey.previewQuality, defaultValue: 80);
|
||||
_thumbList = List.generate(widget.sources.length, (_) => true);
|
||||
|
||||
_pageController = PageController(initialPage: widget.initIndex);
|
||||
|
||||
@@ -21,6 +21,7 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
this.imageBuilder,
|
||||
this.isLongPic,
|
||||
this.callback,
|
||||
this.getPlaceHolder,
|
||||
});
|
||||
|
||||
final String? src;
|
||||
@@ -36,71 +37,61 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
final ImageWidgetBuilder? imageBuilder;
|
||||
final Function? isLongPic;
|
||||
final Function? callback;
|
||||
final Function? getPlaceHolder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double radius = this.radius != null
|
||||
? this.radius!
|
||||
: type == 'avatar'
|
||||
? 50
|
||||
: type == 'emote'
|
||||
? 0
|
||||
: StyleString.imgRadius.x;
|
||||
return src.isNullOrEmpty.not
|
||||
? radius != 0
|
||||
? ClipRRect(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
child: _buildImage(context),
|
||||
)
|
||||
: _buildImage(context)
|
||||
: getPlaceHolder?.call() ?? placeholder(context);
|
||||
}
|
||||
|
||||
Widget _buildImage(context) {
|
||||
late final int defaultImgQuality = GlobalData().imgQuality;
|
||||
bool thumbnail = true;
|
||||
int? memCacheWidth, memCacheHeight;
|
||||
|
||||
if (callback?.call() == true || width <= height) {
|
||||
memCacheWidth = width.cacheSize(context);
|
||||
} else {
|
||||
memCacheHeight = height.cacheSize(context);
|
||||
}
|
||||
Widget res = src != '' && src != null
|
||||
? ClipRRect(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
borderRadius: BorderRadius.circular(
|
||||
radius != null
|
||||
? radius!
|
||||
: type == 'avatar'
|
||||
? 50
|
||||
: type == 'emote'
|
||||
? 0
|
||||
: StyleString.imgRadius.x,
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) => CachedNetworkImage(
|
||||
imageUrl:
|
||||
'${src?.startsWith('//') == true ? 'https:$src' : src?.http2https}${type != 'emote' && thumbnail ? '@${quality ?? defaultImgQuality}q.webp' : ''}',
|
||||
width: width,
|
||||
height: ignoreHeight == null || ignoreHeight == false
|
||||
? height
|
||||
: null,
|
||||
memCacheWidth: memCacheWidth,
|
||||
memCacheHeight: memCacheHeight,
|
||||
fit: BoxFit.cover,
|
||||
alignment: isLongPic?.call() == true
|
||||
? Alignment.topCenter
|
||||
: Alignment.center,
|
||||
fadeOutDuration:
|
||||
fadeOutDuration ?? const Duration(milliseconds: 120),
|
||||
fadeInDuration:
|
||||
fadeInDuration ?? const Duration(milliseconds: 120),
|
||||
filterQuality: FilterQuality.low,
|
||||
// errorWidget: (BuildContext context, String url, Object error) =>
|
||||
// placeholder(context),
|
||||
placeholder: (BuildContext context, String url) =>
|
||||
placeholder(context),
|
||||
imageBuilder: imageBuilder,
|
||||
// errorListener: (value) {
|
||||
// thumbnail = false;
|
||||
// if (context.mounted) {
|
||||
// (context as Element).markNeedsBuild();
|
||||
// }
|
||||
// },
|
||||
),
|
||||
),
|
||||
)
|
||||
: placeholder(context);
|
||||
if (semanticsLabel != null) {
|
||||
return Semantics(
|
||||
label: semanticsLabel,
|
||||
child: res,
|
||||
);
|
||||
}
|
||||
return res;
|
||||
return CachedNetworkImage(
|
||||
imageUrl:
|
||||
'${src?.startsWith('//') == true ? 'https:$src' : src?.http2https}${type != 'emote' && thumbnail ? '@${quality ?? defaultImgQuality}q.webp' : ''}',
|
||||
width: width,
|
||||
height: ignoreHeight == null || ignoreHeight == false ? height : null,
|
||||
memCacheWidth: memCacheWidth,
|
||||
memCacheHeight: memCacheHeight,
|
||||
fit: BoxFit.cover,
|
||||
alignment:
|
||||
isLongPic?.call() == true ? Alignment.topCenter : Alignment.center,
|
||||
fadeOutDuration: fadeOutDuration ?? const Duration(milliseconds: 120),
|
||||
fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 120),
|
||||
filterQuality: FilterQuality.low,
|
||||
// errorWidget: (BuildContext context, String url, Object error) =>
|
||||
// placeholder(context),
|
||||
placeholder: (BuildContext context, String url) =>
|
||||
getPlaceHolder?.call() ?? placeholder(context),
|
||||
imageBuilder: imageBuilder,
|
||||
// errorListener: (value) {
|
||||
// thumbnail = false;
|
||||
// if (context.mounted) {
|
||||
// (context as Element).markNeedsBuild();
|
||||
// }
|
||||
// },
|
||||
);
|
||||
}
|
||||
|
||||
Widget placeholder(BuildContext context) {
|
||||
@@ -108,14 +99,15 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
return Container(
|
||||
width: width,
|
||||
height: height,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.4),
|
||||
borderRadius: BorderRadius.circular(type == 'avatar'
|
||||
? 50
|
||||
: type == 'emote'
|
||||
? 0
|
||||
: StyleString.imgRadius.x),
|
||||
borderRadius: BorderRadius.circular(
|
||||
type == 'avatar'
|
||||
? 50
|
||||
: type == 'emote'
|
||||
? 0
|
||||
: StyleString.imgRadius.x,
|
||||
),
|
||||
),
|
||||
child: type == 'bg'
|
||||
? const SizedBox()
|
||||
|
||||
@@ -328,6 +328,8 @@ class Api {
|
||||
|
||||
static const String spaceFav = '/x/v3/fav/folder/space';
|
||||
|
||||
static const String seasonSeries = '/x/polymer/web-space/seasons_series_list';
|
||||
|
||||
// 用户名片信息
|
||||
static const String memberCardInfo = '/x/web-interface/card';
|
||||
|
||||
|
||||
@@ -154,6 +154,25 @@ class MemberHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> seasonSeriesList({
|
||||
required int? mid,
|
||||
required int pn,
|
||||
}) async {
|
||||
dynamic res = await Request().get(
|
||||
Api.seasonSeries,
|
||||
queryParameters: {
|
||||
'mid': mid,
|
||||
'page_num': pn,
|
||||
'page_size': 10,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']['items_lists']);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> spaceArchive({
|
||||
required ContributeType type,
|
||||
required int? mid,
|
||||
|
||||
@@ -53,6 +53,7 @@ class Data {
|
||||
dynamic digitalButton;
|
||||
dynamic entry;
|
||||
dynamic live;
|
||||
UgcSeason? ugcSeason;
|
||||
|
||||
Data({
|
||||
this.relation,
|
||||
@@ -79,9 +80,22 @@ class Data {
|
||||
this.digitalButton,
|
||||
this.entry,
|
||||
this.live,
|
||||
this.ugcSeason,
|
||||
});
|
||||
|
||||
factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$DataToJson(this);
|
||||
}
|
||||
|
||||
class UgcSeason {
|
||||
int? count;
|
||||
|
||||
UgcSeason({
|
||||
this.count,
|
||||
});
|
||||
|
||||
UgcSeason.fromJson(Map<String, dynamic> json) {
|
||||
count = json['count'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ Data _$DataFromJson(Map<String, dynamic> json) => Data(
|
||||
digitalButton: json['digital_button'],
|
||||
entry: json['entry'],
|
||||
live: json['live'],
|
||||
ugcSeason: json['ugc_season'] != null
|
||||
? UgcSeason.fromJson(json['ugc_season'])
|
||||
: null,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DataToJson(Data instance) => <String, dynamic>{
|
||||
|
||||
@@ -30,6 +30,8 @@ class _AboutPageState extends State<AboutPage> {
|
||||
'https://github.com/guozhigq/pilipala';
|
||||
static const String _upstreamUrl = 'https://github.com/orz12/PiliPalaX';
|
||||
|
||||
late int _pressCount = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -51,12 +53,36 @@ class _AboutPageState extends State<AboutPage> {
|
||||
appBar: AppBar(title: Text('关于')),
|
||||
body: ListView(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 150),
|
||||
child: ExcludeSemantics(
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
_pressCount++;
|
||||
if (_pressCount == 5) {
|
||||
_pressCount = 0;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
String text = '';
|
||||
return AlertDialog(
|
||||
content: TextField(
|
||||
onChanged: (value) => text = value,
|
||||
onSubmitted: (value) {
|
||||
Get.back();
|
||||
Get.toNamed('/webview', parameters: {'url': text});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 150),
|
||||
child: ExcludeSemantics(
|
||||
child: Image.asset(
|
||||
'assets/images/logo/logo_android_2.png',
|
||||
)),
|
||||
'assets/images/logo/logo_android_2.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('PiliPlus',
|
||||
|
||||
@@ -62,6 +62,9 @@ class BangumiIntroController extends CommonController {
|
||||
List delMediaIdsNew = [];
|
||||
dynamic userInfo;
|
||||
|
||||
late final enableQuickFav =
|
||||
GStorage.setting.get(SettingBoxKey.enableQuickFav, defaultValue: false);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -281,7 +284,30 @@ class BangumiIntroController extends CommonController {
|
||||
}
|
||||
|
||||
// (取消)收藏 bangumi
|
||||
Future actionFavVideo() async {
|
||||
Future actionFavVideo({type = 'choose'}) async {
|
||||
// 收藏至默认文件夹
|
||||
if (type == 'default') {
|
||||
SmartDialog.showLoading(msg: '请求中');
|
||||
await queryVideoInFolder();
|
||||
int defaultFolderId = favFolderData.value.list!.first.id!;
|
||||
int favStatus = favFolderData.value.list!.first.favState!;
|
||||
var result = await VideoHttp.favVideo(
|
||||
aid: epId,
|
||||
type: 24,
|
||||
addIds: favStatus == 0 ? '$defaultFolderId' : '',
|
||||
delIds: favStatus == 1 ? '$defaultFolderId' : '',
|
||||
);
|
||||
SmartDialog.dismiss();
|
||||
if (result['status']) {
|
||||
// 重新获取收藏状态
|
||||
await Future.delayed(const Duration(milliseconds: 255));
|
||||
await queryBangumiLikeCoinFav();
|
||||
SmartDialog.showToast('✅ 快速收藏/取消收藏成功');
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (var i in favFolderData.value.list!.toList()) {
|
||||
if (i.favState == 1) {
|
||||
|
||||
@@ -173,11 +173,26 @@ class _BangumiInfoState extends State<BangumiInfo>
|
||||
}
|
||||
|
||||
// 收藏
|
||||
showFavBottomSheet() {
|
||||
showFavBottomSheet({type = 'tap'}) {
|
||||
if (bangumiIntroController.userInfo == null) {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
// 快速收藏 &
|
||||
// 点按 收藏至默认文件夹
|
||||
// 长按选择文件夹
|
||||
if (bangumiIntroController.enableQuickFav) {
|
||||
if (type == 'tap') {
|
||||
bangumiIntroController.actionFavVideo(type: 'default');
|
||||
} else {
|
||||
_showFavBottomSheet();
|
||||
}
|
||||
} else if (type != 'longPress') {
|
||||
_showFavBottomSheet();
|
||||
}
|
||||
}
|
||||
|
||||
_showFavBottomSheet() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
@@ -522,6 +537,7 @@ class _BangumiInfoState extends State<BangumiInfo>
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => showFavBottomSheet(),
|
||||
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
||||
selectStatus: bangumiIntroController.hasFav.value,
|
||||
loadingStatus: false,
|
||||
semanticsLabel: '收藏',
|
||||
@@ -592,6 +608,7 @@ class _BangumiInfoState extends State<BangumiInfo>
|
||||
() => ActionRowItem(
|
||||
icon: const Icon(FontAwesomeIcons.heart),
|
||||
onTap: () => showFavBottomSheet(),
|
||||
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
||||
selectStatus: videoIntroController.hasFav.value,
|
||||
loadingStatus: widget.isLoading,
|
||||
text: !widget.isLoading
|
||||
|
||||
@@ -36,11 +36,14 @@ class _HomePageState extends State<HomePage>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(toolbarHeight: 0),
|
||||
body: Column(
|
||||
children: [
|
||||
if (!_homeController.useSideBar) customAppBar,
|
||||
if (!_homeController.useSideBar &&
|
||||
context.orientation == Orientation.portrait)
|
||||
customAppBar,
|
||||
if (_homeController.tabs.length > 1) ...[
|
||||
const SizedBox(height: 4),
|
||||
Material(
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class HotController extends CommonController {
|
||||
// int idx = 0;
|
||||
|
||||
late RxBool showHotRcmd = GStorage.showHotRcmd.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
@@ -3,6 +3,9 @@ import 'dart:async';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/video_card_h.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/tab_type.dart';
|
||||
import 'package:PiliPlus/pages/rank/view.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -56,6 +59,27 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildEntranceItem({
|
||||
required String iconUrl,
|
||||
required String title,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.network(width: 35, height: 35, iconUrl),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
@@ -67,6 +91,67 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _hotController.scrollController,
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Obx(
|
||||
() => _hotController.showHotRcmd.value
|
||||
? Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 12, top: 12, right: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildEntranceItem(
|
||||
iconUrl:
|
||||
'http://i0.hdslb.com/bfs/archive/a3f11218aaf4521b4967db2ae164ecd3052586b9.png',
|
||||
title: '排行榜',
|
||||
onTap: () {
|
||||
try {
|
||||
HomeController homeController =
|
||||
Get.find<HomeController>();
|
||||
int index = homeController.tabs.indexWhere(
|
||||
(item) => item['type'] == TabType.rank,
|
||||
);
|
||||
if (index != -1) {
|
||||
homeController.tabController.animateTo(index);
|
||||
} else {
|
||||
Get.to(
|
||||
Scaffold(
|
||||
appBar: AppBar(title: const Text('排行榜')),
|
||||
body: RankPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
_buildEntranceItem(
|
||||
iconUrl:
|
||||
'http://i0.hdslb.com/bfs/archive/552ebe8c4794aeef30ebd1568b59ad35f15e21ad.png',
|
||||
title: '每周必看',
|
||||
onTap: () {
|
||||
Utils.handleWebview(
|
||||
'https://www.bilibili.com/h5/weekly-recommend',
|
||||
inApp: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
_buildEntranceItem(
|
||||
iconUrl:
|
||||
'http://i0.hdslb.com/bfs/archive/3693ec9335b78ca57353ac0734f36a46f3d179a9.png',
|
||||
title: '入站必刷',
|
||||
onTap: () {
|
||||
Utils.handleWebview(
|
||||
'https://www.bilibili.com/h5/good-history',
|
||||
inApp: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
|
||||
@@ -165,105 +165,64 @@ class _MainAppState extends State<MainApp>
|
||||
}
|
||||
}
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constriants) {
|
||||
bool isPortait = constriants.maxHeight > constriants.maxWidth;
|
||||
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
extendBody: true,
|
||||
body: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (useSideBar) ...[
|
||||
SizedBox(
|
||||
width: context.width * 0.04 +
|
||||
40 +
|
||||
MediaQuery.of(context).padding.left,
|
||||
child: Obx(
|
||||
() => _mainController.navigationBars.length > 1
|
||||
? NavigationRail(
|
||||
groupAlignment: 1,
|
||||
minWidth: context.width * 0.0286 + 28.56,
|
||||
backgroundColor: Colors.transparent,
|
||||
selectedIndex:
|
||||
_mainController.selectedIndex.value,
|
||||
onDestinationSelected: setIndex,
|
||||
labelType: NavigationRailLabelType.none,
|
||||
leading: userAndSearchVertical,
|
||||
destinations: _mainController.navigationBars
|
||||
.map((e) => NavigationRailDestination(
|
||||
icon: _buildIcon(
|
||||
id: e['id'],
|
||||
count: e['count'],
|
||||
icon: e['icon'],
|
||||
),
|
||||
selectedIcon: _buildIcon(
|
||||
id: e['id'],
|
||||
count: e['count'],
|
||||
icon: e['selectIcon'],
|
||||
),
|
||||
label: Text(e['label']),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 0.01 * context.height),
|
||||
))
|
||||
.toList(),
|
||||
trailing: SizedBox(height: 0.1 * context.height),
|
||||
)
|
||||
: Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: MediaQuery.paddingOf(context).top + 10),
|
||||
constraints: BoxConstraints(
|
||||
minWidth: context.width * 0.0286 + 28.56,
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
extendBody: true,
|
||||
body: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (useSideBar || context.orientation == Orientation.landscape)
|
||||
Obx(
|
||||
() => _mainController.navigationBars.length > 1
|
||||
? NavigationRail(
|
||||
groupAlignment: 0.5,
|
||||
selectedIndex: _mainController.selectedIndex.value,
|
||||
onDestinationSelected: setIndex,
|
||||
labelType: NavigationRailLabelType.selected,
|
||||
leading: userAndSearchVertical,
|
||||
destinations: _mainController.navigationBars
|
||||
.map(
|
||||
(e) => NavigationRailDestination(
|
||||
icon: _buildIcon(
|
||||
id: e['id'],
|
||||
count: e['count'],
|
||||
icon: e['icon'],
|
||||
),
|
||||
selectedIcon: _buildIcon(
|
||||
id: e['id'],
|
||||
count: e['count'],
|
||||
icon: e['selectIcon'],
|
||||
),
|
||||
label: Text(e['label']),
|
||||
),
|
||||
child: userAndSearchVertical,
|
||||
),
|
||||
),
|
||||
),
|
||||
] else if (!isPortait)
|
||||
Obx(
|
||||
() => _mainController.navigationBars.length > 1
|
||||
? NavigationRail(
|
||||
onDestinationSelected: setIndex,
|
||||
selectedIndex: _mainController.selectedIndex.value,
|
||||
destinations: _mainController.navigationBars
|
||||
.map(
|
||||
(e) => NavigationRailDestination(
|
||||
icon: _buildIcon(
|
||||
id: e['id'],
|
||||
count: e['count'],
|
||||
icon: e['icon'],
|
||||
),
|
||||
selectedIcon: _buildIcon(
|
||||
id: e['id'],
|
||||
count: e['count'],
|
||||
icon: e['selectIcon'],
|
||||
),
|
||||
label: Text(e['label']),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
indent: MediaQuery.of(context).padding.top,
|
||||
endIndent: MediaQuery.of(context).padding.bottom,
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline.withOpacity(0.06),
|
||||
),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
controller: _mainController.pageController,
|
||||
children: _mainController.pages,
|
||||
),
|
||||
),
|
||||
if (useSideBar) SizedBox(width: context.width * 0.004),
|
||||
],
|
||||
)
|
||||
.toList(),
|
||||
)
|
||||
: Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: MediaQuery.paddingOf(context).top + 10,
|
||||
),
|
||||
width: 56,
|
||||
child: userAndSearchVertical,
|
||||
),
|
||||
),
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
indent: MediaQuery.of(context).padding.top,
|
||||
endIndent: MediaQuery.of(context).padding.bottom,
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.06),
|
||||
),
|
||||
bottomNavigationBar: useSideBar || !isPortait
|
||||
Expanded(
|
||||
child: PageView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
controller: _mainController.pageController,
|
||||
children: _mainController.pages,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar:
|
||||
useSideBar || context.orientation == Orientation.landscape
|
||||
? null
|
||||
: StreamBuilder(
|
||||
stream: _mainController.hideTabBar
|
||||
@@ -313,10 +272,6 @@ class _MainAppState extends State<MainApp>
|
||||
selectedFontSize: 12,
|
||||
unselectedFontSize: 12,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
// selectedItemColor:
|
||||
// Theme.of(context).colorScheme.primary, // 选中项的颜色
|
||||
// unselectedItemColor:
|
||||
// Theme.of(context).colorScheme.onSurface,
|
||||
items: _mainController.navigationBars
|
||||
.map(
|
||||
(e) => BottomNavigationBarItem(
|
||||
@@ -340,8 +295,6 @@ class _MainAppState extends State<MainApp>
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
|
||||
class SeasonSeriesController extends CommonController {
|
||||
SeasonSeriesController(this.mid);
|
||||
final int mid;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
Map data = response.response;
|
||||
List list = ((data['seasons_list'] as List?) ?? []) +
|
||||
((data['series_list'] as List?) ?? []);
|
||||
if (currentPage != 0 && loadingState.value is Success) {
|
||||
list.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
isEnd = list.length >= ((data['page']['total'] as int?) ?? 0);
|
||||
loadingState.value = LoadingState.success(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.seasonSeriesList(
|
||||
mid: mid,
|
||||
pn: currentPage,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/controller.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/widget/season_series_card.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class SeasonSeriesPage extends StatefulWidget {
|
||||
const SeasonSeriesPage({
|
||||
super.key,
|
||||
required this.mid,
|
||||
this.heroTag,
|
||||
});
|
||||
|
||||
final int mid;
|
||||
final String? heroTag;
|
||||
|
||||
@override
|
||||
State<SeasonSeriesPage> createState() => _SeasonSeriesPageState();
|
||||
}
|
||||
|
||||
class _SeasonSeriesPageState extends State<SeasonSeriesPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late final _controller = Get.put(
|
||||
SeasonSeriesController(widget.mid),
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
? CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: 2,
|
||||
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
||||
childAspectRatio: StyleString.aspectRatio * 2.2,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return SeasonSeriesCard(
|
||||
item: loadingState.response[index],
|
||||
onTap: () {
|
||||
dynamic item = loadingState.response[index];
|
||||
bool isSeason = item['meta']['season_id'] != null;
|
||||
dynamic id = isSeason
|
||||
? item['meta']['season_id']
|
||||
: item['meta']['series_id'];
|
||||
Get.to(
|
||||
Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(item['meta']['name']),
|
||||
),
|
||||
body: MemberVideo(
|
||||
type: isSeason
|
||||
? ContributeType.season
|
||||
: ContributeType.series,
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
seasonId: isSeason ? id : null,
|
||||
seriesId: isSeason ? null : id,
|
||||
title: item['meta']['name'],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: scrollErrorWidget(
|
||||
callback: () {
|
||||
_controller.onReload();
|
||||
},
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
callback: () {
|
||||
_controller.onReload();
|
||||
},
|
||||
),
|
||||
LoadingState() => throw UnimplementedError(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SeasonSeriesCard extends StatelessWidget {
|
||||
const SeasonSeriesCard({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.onTap,
|
||||
});
|
||||
final dynamic item;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onLongPress: () {
|
||||
imageSaveDialog(
|
||||
context: context,
|
||||
title: item['meta']['name'],
|
||||
cover: item['meta']['cover'],
|
||||
);
|
||||
},
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
||||
final double maxWidth = boxConstraints.maxWidth;
|
||||
final double maxHeight = boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: item['meta']['cover'],
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
PBadge(
|
||||
text:
|
||||
'${item['meta']['season_id'] != null ? '合集' : '列表'}: ${item['meta']['total']}',
|
||||
bottom: 6.0,
|
||||
right: 6.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['meta']['name'],
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.dateFormat(item['meta']['ptime']),
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -74,24 +74,30 @@ class _MemberVideoState extends State<MemberVideo>
|
||||
delegate: CustomSliverPersistentHeaderDelegate(
|
||||
extent: 40,
|
||||
bgColor: Theme.of(context).colorScheme.surface,
|
||||
child: Container(
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
Obx(
|
||||
() => Text(
|
||||
_controller.count.value != -1
|
||||
? '共${_controller.count.value}视频'
|
||||
: '',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
() => Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
child: Text(
|
||||
_controller.count.value != -1
|
||||
? '共${_controller.count.value}视频'
|
||||
: '',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => _controller.episodicButton.value.uri != null
|
||||
? Container(
|
||||
height: 35,
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
padding: EdgeInsets.only(
|
||||
left: _controller.count.value != -1
|
||||
? 6
|
||||
: 0),
|
||||
child: TextButton.icon(
|
||||
onPressed: _controller.toViewPlayAll,
|
||||
icon: Icon(
|
||||
@@ -146,6 +152,7 @@ class _MemberVideoState extends State<MemberVideo>
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/audio/member_audio.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/season_series_page.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute_ctr.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -110,6 +111,10 @@ class _MemberContributeState extends State<MemberContribute>
|
||||
seriesId: item.seriesId,
|
||||
title: item.title,
|
||||
),
|
||||
'ugcSeason' => SeasonSeriesPage(
|
||||
mid: widget.mid,
|
||||
heroTag: widget.heroTag,
|
||||
),
|
||||
_ => Center(child: Text(item.title!))
|
||||
},
|
||||
)
|
||||
|
||||
@@ -32,6 +32,27 @@ class MemberContributeCtr extends CommonController
|
||||
if (contribute.items?.isNullOrEmpty == false &&
|
||||
contribute.items!.length > 1) {
|
||||
items = contribute.items;
|
||||
// if (_ctr.ugcSeasonCount != null) {
|
||||
// int currentSeasonCount =
|
||||
// items!.where((item) => item.param == 'season_video').length;
|
||||
// if (currentSeasonCount < _ctr.ugcSeasonCount!) {
|
||||
// items!.add(
|
||||
// Item(
|
||||
// param: 'ugcSeason',
|
||||
// title: '全部合集/列表',
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// show if exist
|
||||
if (_ctr.hasSeasonOrSeries == true) {
|
||||
items!.add(
|
||||
Item(
|
||||
param: 'ugcSeason',
|
||||
title: '全部合集/列表',
|
||||
),
|
||||
);
|
||||
}
|
||||
tabs = items!.map((item) => Tab(text: item.title)).toList();
|
||||
tabController = TabController(
|
||||
vsync: this,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart';
|
||||
import 'package:PiliPlus/models/space/tab2.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
@@ -8,6 +9,7 @@ import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
enum MemberTabType { none, home, dynamic, contribute, favorite, bangumi }
|
||||
@@ -30,6 +32,7 @@ class MemberControllerNew extends CommonController
|
||||
List<Tab2>? tab2;
|
||||
RxInt contributeInitialIndex = 0.obs;
|
||||
double? top;
|
||||
bool? hasSeasonOrSeries;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -40,17 +43,33 @@ class MemberControllerNew extends CommonController
|
||||
|
||||
dynamic live;
|
||||
|
||||
int? silence;
|
||||
String? endTime;
|
||||
|
||||
@override
|
||||
bool customHandleResponse(Success response) {
|
||||
username = response.response?.card?.name ?? '';
|
||||
isFollow.value = response.response?.card?.relation?.isFollow == 1;
|
||||
relation.value = response.response?.relSpecial == 1
|
||||
? 2
|
||||
: response.response?.relation ?? 1;
|
||||
tab2 = response.response.tab2;
|
||||
live = response.response?.live;
|
||||
Data data = response.response;
|
||||
username = data.card?.name ?? '';
|
||||
isFollow.value = data.card?.relation?.isFollow == 1;
|
||||
relation.value = data.relSpecial == 1 ? 2 : data.relation ?? 1;
|
||||
tab2 = data.tab2;
|
||||
live = data.live;
|
||||
silence = data.card?.silence;
|
||||
if ((data.ugcSeason?.count != null && data.ugcSeason?.count != 0) ||
|
||||
data.series?.item?.isNotEmpty == true) {
|
||||
hasSeasonOrSeries = true;
|
||||
}
|
||||
if (data.card?.endTime != null) {
|
||||
if (data.card!.endTime == 0) {
|
||||
endTime = ': 永久封禁';
|
||||
} else if (data.card!.endTime! >
|
||||
DateTime.now().millisecondsSinceEpoch ~/ 1000) {
|
||||
endTime =
|
||||
':至 ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.fromMillisecondsSinceEpoch(data.card!.endTime! * 1000))}';
|
||||
}
|
||||
}
|
||||
if (tab2 != null && tab2!.isNotEmpty) {
|
||||
if (!response.response.tab.toJson().values.contains(true) &&
|
||||
if (!data.tab!.toJson().values.contains(true) &&
|
||||
tab2!.first.param == 'home') {
|
||||
// remove empty home tab
|
||||
tab2!.removeAt(0);
|
||||
@@ -64,11 +83,11 @@ class MemberControllerNew extends CommonController
|
||||
});
|
||||
}
|
||||
if (initialIndex == -1) {
|
||||
if (response.response.defaultTab == 'video') {
|
||||
response.response.defaultTab = 'dynamic';
|
||||
if (data.defaultTab == 'video') {
|
||||
data.defaultTab = 'dynamic';
|
||||
}
|
||||
initialIndex = tab2!.indexWhere((item) {
|
||||
return item.param == response.response.defaultTab;
|
||||
return item.param == data.defaultTab;
|
||||
});
|
||||
}
|
||||
tabs = tab2!.map((item) => Tab(text: item.title ?? '')).toList();
|
||||
|
||||
@@ -318,6 +318,8 @@ class _MemberPageNewState extends State<MemberPageNew>
|
||||
images: userState.response.images,
|
||||
onFollow: () => _userController.onFollow(context),
|
||||
live: _userController.live,
|
||||
silence: _userController.silence,
|
||||
endTime: _userController.endTime,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -19,6 +19,8 @@ class UserInfoCard extends StatelessWidget {
|
||||
required this.isFollow,
|
||||
required this.onFollow,
|
||||
this.live,
|
||||
this.silence,
|
||||
this.endTime,
|
||||
});
|
||||
|
||||
final bool isV;
|
||||
@@ -29,6 +31,8 @@ class UserInfoCard extends StatelessWidget {
|
||||
final space.Images images;
|
||||
final VoidCallback onFollow;
|
||||
final dynamic live;
|
||||
final int? silence;
|
||||
final String? endTime;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -172,48 +176,47 @@ class UserInfoCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
if (card.officialVerify?.desc?.isNotEmpty == true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 20, top: 8, right: 20),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Theme.of(context).colorScheme.onInverseSurface),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
if (card.officialVerify?.icon?.isNotEmpty == true) ...[
|
||||
WidgetSpan(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(0.1),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
width: 18,
|
||||
height: 18,
|
||||
imageUrl: card.officialVerify!.icon!.http2https,
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 20, top: 8, right: 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
if (card.officialVerify?.icon?.isNotEmpty == true) ...[
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
width: 18,
|
||||
height: 18,
|
||||
imageUrl: card.officialVerify!.icon!.http2https,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' ',
|
||||
)
|
||||
],
|
||||
),
|
||||
TextSpan(
|
||||
text: card.officialVerify!.spliceTitle!,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.7),
|
||||
),
|
||||
text: ' ',
|
||||
)
|
||||
],
|
||||
),
|
||||
TextSpan(
|
||||
text: card.officialVerify!.spliceTitle!,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.7),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -259,17 +262,45 @@ class UserInfoCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
// if (card.spaceTagBottom != null && card.spaceTagBottom!.isNotEmpty)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(left: 20, top: 8, right: 20),
|
||||
// child: Wrap(
|
||||
// spacing: 5,
|
||||
// runSpacing: 8,
|
||||
// children: card.spaceTagBottom!
|
||||
// .map((item) => Text(item.title ?? ''))
|
||||
// .toList(),
|
||||
// ),
|
||||
// ),
|
||||
if (silence == 1)
|
||||
Builder(builder: (context) {
|
||||
bool isLight = Theme.of(context).brightness == Brightness.light;
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: isLight
|
||||
? Theme.of(context).colorScheme.errorContainer
|
||||
: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
margin: const EdgeInsets.only(left: 20, top: 8, right: 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
size: 17,
|
||||
color: isLight
|
||||
? Theme.of(context).colorScheme.onErrorContainer
|
||||
: Theme.of(context).colorScheme.onError,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' 该账号封禁中${endTime ?? ''}',
|
||||
style: TextStyle(
|
||||
color: isLight
|
||||
? Theme.of(context).colorScheme.onErrorContainer
|
||||
: Theme.of(context).colorScheme.onError,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
];
|
||||
|
||||
_buildRight(BuildContext context) => Column(
|
||||
@@ -393,25 +424,24 @@ class UserInfoCard extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
|
||||
_buildBadge(BuildContext context) => Container(
|
||||
padding: const EdgeInsets.all(0.01),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
_buildBadge(BuildContext context) => IgnorePointer(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: card.officialVerify?.icon?.isNotEmpty == true
|
||||
? CachedNetworkImage(
|
||||
imageUrl: card.officialVerify!.icon!.http2https,
|
||||
width: 24,
|
||||
height: 24,
|
||||
)
|
||||
: Image.asset(
|
||||
'assets/images/big-vip.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
child: card.officialVerify?.icon?.isNotEmpty == true
|
||||
? NetworkImgLayer(
|
||||
src: card.officialVerify?.icon,
|
||||
radius: null,
|
||||
width: 24,
|
||||
height: 24,
|
||||
quality: 100,
|
||||
)
|
||||
: Image.asset(
|
||||
'assets/images/big-vip.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
);
|
||||
|
||||
_buildAvatar(BuildContext context) => Hero(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPlus/utils/login.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -56,12 +57,18 @@ class MineController extends GetxController {
|
||||
userInfo.value = res['data'];
|
||||
GStorage.userInfo.put('userInfoCache', res['data']);
|
||||
isLogin.value = true;
|
||||
} else {
|
||||
LoginUtils.onLogout();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
if (res['msg'] == '账号未登录') {
|
||||
LoginUtils.onLogout();
|
||||
return;
|
||||
}
|
||||
}
|
||||
await queryUserStatOwner();
|
||||
return res;
|
||||
queryUserStatOwner();
|
||||
}
|
||||
|
||||
Future queryUserStatOwner() async {
|
||||
@@ -69,7 +76,6 @@ class MineController extends GetxController {
|
||||
if (res['status']) {
|
||||
userStat.value = res['data'];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static onChangeAnonymity(BuildContext context) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:PiliPlus/models/video/play/CDN.dart';
|
||||
import 'package:PiliPlus/models/video/play/quality.dart';
|
||||
import 'package:PiliPlus/models/video/play/subtitle.dart';
|
||||
import 'package:PiliPlus/pages/home/controller.dart';
|
||||
import 'package:PiliPlus/pages/hot/controller.dart';
|
||||
import 'package:PiliPlus/pages/main/controller.dart';
|
||||
import 'package:PiliPlus/pages/member/new/controller.dart';
|
||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
@@ -1762,6 +1763,19 @@ List<SettingsModel> get extraSettings => [
|
||||
setKey: SettingBoxKey.mergeDanmaku,
|
||||
defaultVal: false,
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
title: '显示热门推荐',
|
||||
subtitle: '热门页面显示每周必看等推荐内容入口',
|
||||
leading: Icon(Icons.local_fire_department_outlined),
|
||||
setKey: SettingBoxKey.showHotRcmd,
|
||||
defaultVal: false,
|
||||
onChanged: (value) {
|
||||
try {
|
||||
Get.find<HotController>().showHotRcmd.value = value;
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
enableFeedback: true,
|
||||
|
||||
@@ -83,6 +83,8 @@ class VideoIntroController extends GetxController
|
||||
late final showArgueMsg = GStorage.showArgueMsg;
|
||||
late final enableAi =
|
||||
GStorage.setting.get(SettingBoxKey.enableAi, defaultValue: false);
|
||||
late final enableQuickFav =
|
||||
GStorage.setting.get(SettingBoxKey.enableQuickFav, defaultValue: false);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
|
||||
@@ -165,16 +165,14 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
children: [
|
||||
Icon(
|
||||
Icons.shield_outlined,
|
||||
size:
|
||||
MediaQuery.textScalerOf(context).scale(16),
|
||||
size: 16,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
Icon(
|
||||
Icons.play_arrow_rounded,
|
||||
size:
|
||||
MediaQuery.textScalerOf(context).scale(12),
|
||||
size: 12,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
@@ -183,6 +181,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
),
|
||||
Text(
|
||||
videoDetailCtr.videoLabel.value,
|
||||
textScaler: TextScaler.linear(1),
|
||||
strutStyle: StrutStyle(leading: 0, height: 1),
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
@@ -278,12 +277,10 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
final bool enableDragQuickFav =
|
||||
GStorage.setting.get(SettingBoxKey.enableQuickFav, defaultValue: false);
|
||||
// 快速收藏 &
|
||||
// 点按 收藏至默认文件夹
|
||||
// 长按选择文件夹
|
||||
if (enableDragQuickFav) {
|
||||
if (videoIntroController.enableQuickFav) {
|
||||
if (type == 'tap') {
|
||||
videoIntroController.actionFavVideo(type: 'default');
|
||||
} else {
|
||||
|
||||
@@ -1890,7 +1890,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
videoDetailController.bsController!.close();
|
||||
videoDetailController.bsController = null;
|
||||
} else {
|
||||
setState(() {});
|
||||
Get.back();
|
||||
// setState(() {});
|
||||
}
|
||||
}
|
||||
: null,
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -178,21 +180,24 @@ class _WebviewPageNewState extends State<WebviewPageNew> {
|
||||
: null,
|
||||
),
|
||||
onLoadStop: (controller, url) {
|
||||
_webViewController?.evaluateJavascript(
|
||||
source: '''
|
||||
document.styleSheets[0].insertRule('div.open-app-btn.bili-btn-warp {display:none;}', 0);
|
||||
document.styleSheets[0].insertRule('#app__display-area > div.control-panel {display:none;}', 0);
|
||||
''',
|
||||
);
|
||||
_webViewController?.evaluateJavascript(
|
||||
source: '''
|
||||
document.querySelector('#internationalHeader').remove();
|
||||
document.querySelector('#message-navbar').remove();
|
||||
''',
|
||||
);
|
||||
if (url.toString().startsWith('https://live.bilibili.com')) {
|
||||
_webViewController?.evaluateJavascript(
|
||||
source: '''
|
||||
document.styleSheets[0].insertRule('div.open-app-btn.bili-btn-warp {display:none;}', 0);
|
||||
document.styleSheets[0].insertRule('#app__display-area > div.control-panel {display:none;}', 0);
|
||||
''',
|
||||
);
|
||||
}
|
||||
// _webViewController?.evaluateJavascript(
|
||||
// source: '''
|
||||
// document.querySelector('#internationalHeader').remove();
|
||||
// document.querySelector('#message-navbar').remove();
|
||||
// ''',
|
||||
// );
|
||||
},
|
||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
||||
final String str = navigationAction.request.url!.pathSegments[0];
|
||||
final String? str =
|
||||
navigationAction.request.url!.pathSegments.getOrNull(0);
|
||||
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||
final List matchKeys = matchRes.keys.toList();
|
||||
if (matchKeys.isNotEmpty) {
|
||||
@@ -206,12 +211,28 @@ class _WebviewPageNewState extends State<WebviewPageNew> {
|
||||
}
|
||||
|
||||
var url = navigationAction.request.url!.toString();
|
||||
if (!url.startsWith('http')) {
|
||||
|
||||
if (url.startsWith('http')) {
|
||||
if (RegExp(r'https://www.bilibili.com/video/BV[a-zA-Z\d]+')
|
||||
.hasMatch(url)) {
|
||||
PiliScheme.routePush(Uri.parse(url));
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
} else if (url.startsWith('http://m.bilibili.com/playlist/')) {
|
||||
try {
|
||||
String? bvid =
|
||||
RegExp(r'bvid=(BV[a-zA-Z\d]+)').firstMatch(url)?.group(1);
|
||||
if (bvid != null) {
|
||||
PiliScheme.videoPush(null, bvid);
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
} else {
|
||||
if (url.startsWith('bilibili://video/')) {
|
||||
String str = Uri.parse(url).pathSegments[0];
|
||||
String? str = Uri.parse(url).pathSegments.getOrNull(0);
|
||||
Get.offAndToNamed(
|
||||
'/searchResult',
|
||||
parameters: {'keyword': str},
|
||||
parameters: {'keyword': str ?? ''},
|
||||
);
|
||||
} else {
|
||||
var snackBar = SnackBar(
|
||||
|
||||
@@ -63,6 +63,7 @@ class _WhisperPageState extends State<WhisperPage> {
|
||||
'url': 'https://message.bilibili.com',
|
||||
'type': 'whisper',
|
||||
'pageTitle': '消息中心',
|
||||
'ua': 'pc',
|
||||
});
|
||||
},
|
||||
),
|
||||
@@ -175,7 +176,7 @@ class _WhisperPageState extends State<WhisperPage> {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, int i) {
|
||||
dynamic content =
|
||||
sessionList[i].lastMsg.content;
|
||||
sessionList[i]?.lastMsg?.content;
|
||||
if (content == null || content == "") {
|
||||
content = '不支持的消息类型';
|
||||
} else {
|
||||
|
||||
@@ -800,6 +800,10 @@ class PlPlayerController {
|
||||
videoPlayerController!.stream.error.listen((String event) {
|
||||
// 直播的错误提示没有参考价值,均不予显示
|
||||
if (videoType.value == 'live') return;
|
||||
if (event.startsWith("Failed to open .") ||
|
||||
event.startsWith("Cannot open file ''")) {
|
||||
SmartDialog.showToast('视频源为空');
|
||||
}
|
||||
if (event.startsWith("Failed to open https://") ||
|
||||
event.startsWith("Can not open external file https://") ||
|
||||
//tcp: ffurl_read returned 0xdfb9b0bb
|
||||
@@ -807,13 +811,15 @@ class PlPlayerController {
|
||||
event.startsWith('tcp: ffurl_read returned ')) {
|
||||
EasyThrottle.throttle('videoPlayerController!.stream.error.listen',
|
||||
const Duration(milliseconds: 10000), () {
|
||||
Future.delayed(const Duration(milliseconds: 3000), () {
|
||||
Future.delayed(const Duration(milliseconds: 3000), () async {
|
||||
debugPrint("isBuffering.value: ${isBuffering.value}");
|
||||
debugPrint("_buffered.value: ${_buffered.value}");
|
||||
if (isBuffering.value && _buffered.value == Duration.zero) {
|
||||
refreshPlayer();
|
||||
SmartDialog.showToast('视频链接打开失败,重试中',
|
||||
displayTime: const Duration(milliseconds: 500));
|
||||
if (!await refreshPlayer()) {
|
||||
debugPrint("failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,11 +52,11 @@ class LoginUtils {
|
||||
..loadingState.value = LoadingState.loading();
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
for (int i = 0; i < tabsConfig.length; i++) {
|
||||
for (int i = 0; i < tabsConfig.length; i++) {
|
||||
try {
|
||||
Get.find<DynamicsTabController>(tag: tabsConfig[i]['tag']).onRefresh();
|
||||
}
|
||||
} catch (_) {}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
static String buvid() {
|
||||
|
||||
@@ -116,7 +116,7 @@ class GStorage {
|
||||
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0);
|
||||
|
||||
static int get previewQ =>
|
||||
setting.get(SettingBoxKey.previewQuality, defaultValue: 80);
|
||||
setting.get(SettingBoxKey.previewQuality, defaultValue: 100);
|
||||
|
||||
static double get mediumCardWidth =>
|
||||
setting.get(SettingBoxKey.mediumCardWidth, defaultValue: 280.0);
|
||||
@@ -342,6 +342,9 @@ class GStorage {
|
||||
static bool get mergeDanmaku =>
|
||||
GStorage.setting.get(SettingBoxKey.mergeDanmaku, defaultValue: false);
|
||||
|
||||
static bool get showHotRcmd =>
|
||||
GStorage.setting.get(SettingBoxKey.showHotRcmd, defaultValue: false);
|
||||
|
||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||
|
||||
@@ -562,6 +565,7 @@ class SettingBoxKey {
|
||||
refreshDisplacement = 'refreshDisplacement',
|
||||
showVipDanmaku = 'showVipDanmaku',
|
||||
mergeDanmaku = 'mergeDanmaku',
|
||||
showHotRcmd = 'showHotRcmd',
|
||||
|
||||
// Sponsor Block
|
||||
enableSponsorBlock = 'enableSponsorBlock',
|
||||
|
||||
@@ -13,8 +13,10 @@ import 'package:PiliPlus/http/search.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/bangumi/info.dart';
|
||||
import 'package:PiliPlus/models/common/dynamics_type.dart';
|
||||
import 'package:PiliPlus/models/common/search_type.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/controller.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/tab/controller.dart';
|
||||
import 'package:PiliPlus/pages/home/controller.dart';
|
||||
import 'package:PiliPlus/pages/live/controller.dart';
|
||||
import 'package:PiliPlus/pages/media/controller.dart';
|
||||
@@ -63,8 +65,12 @@ class Utils {
|
||||
);
|
||||
}
|
||||
|
||||
static void handleWebview(String url, {bool off = false}) {
|
||||
if (GStorage.openInBrowser) {
|
||||
static void handleWebview(
|
||||
String url, {
|
||||
bool off = false,
|
||||
bool inApp = false,
|
||||
}) {
|
||||
if (inApp.not && GStorage.openInBrowser) {
|
||||
launchURL(url);
|
||||
} else {
|
||||
if (off) {
|
||||
@@ -185,6 +191,13 @@ class Utils {
|
||||
..onRefresh();
|
||||
} catch (_) {}
|
||||
|
||||
for (int i = 0; i < tabsConfig.length; i++) {
|
||||
try {
|
||||
Get.find<DynamicsTabController>(tag: tabsConfig[i]['tag'])
|
||||
.onRefresh();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
try {
|
||||
Get.find<MediaController>()
|
||||
..mid = result['data'].mid
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/models/video/play/CDN.dart';
|
||||
import 'package:PiliPlus/models/video/play/url.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -20,11 +21,15 @@ class VideoUtils {
|
||||
if (item is AudioItem) {
|
||||
if (GStorage.setting
|
||||
.get(SettingBoxKey.disableAudioCDN, defaultValue: true)) {
|
||||
return item.backupUrl ?? item.baseUrl ?? "";
|
||||
return item.backupUrl.isNullOrEmpty.not
|
||||
? item.backupUrl!
|
||||
: item.baseUrl ?? "";
|
||||
}
|
||||
}
|
||||
if (defaultCDNService == CDNService.baseUrl.code) {
|
||||
return item.baseUrl ?? "";
|
||||
return (item.baseUrl as String?).isNullOrEmpty.not
|
||||
? item.baseUrl
|
||||
: item.backupUrl ?? "";
|
||||
}
|
||||
if (item is CodecItem) {
|
||||
backupUrl = (item.urlInfo?.first.host)! +
|
||||
@@ -34,20 +39,20 @@ class VideoUtils {
|
||||
backupUrl = item.backupUrl;
|
||||
}
|
||||
if (defaultCDNService == CDNService.backupUrl.code) {
|
||||
return backupUrl ?? item.baseUrl ?? "";
|
||||
return backupUrl.isNullOrEmpty.not ? backupUrl : item.baseUrl ?? "";
|
||||
}
|
||||
videoUrl = (backupUrl == null || isMCDNorPCDN(backupUrl))
|
||||
videoUrl = (backupUrl.isNullOrEmpty || isMCDNorPCDN(backupUrl!))
|
||||
? item.baseUrl
|
||||
: backupUrl;
|
||||
|
||||
if (videoUrl == null) {
|
||||
if (videoUrl.isNullOrEmpty) {
|
||||
return "";
|
||||
}
|
||||
debugPrint("videoUrl:$videoUrl");
|
||||
|
||||
String defaultCDNHost = CDNServiceCode.fromCode(defaultCDNService)!.host;
|
||||
debugPrint("defaultCDNHost:$defaultCDNHost");
|
||||
if (videoUrl.contains("szbdyd.com")) {
|
||||
if (videoUrl!.contains("szbdyd.com")) {
|
||||
String hostname =
|
||||
Uri.parse(videoUrl).queryParameters['xy_usource'] ?? defaultCDNHost;
|
||||
videoUrl =
|
||||
|
||||
Reference in New Issue
Block a user