Compare commits

..

32 Commits

Author SHA1 Message Date
35dc94014c mod: 直播mcdn链接替换 issues #568 2024-02-23 00:30:26 +08:00
5746b85b27 fix: 视频全屏遮挡 issues #347 2024-02-22 00:17:38 +08:00
740d5f1ddd fix: 视频详情页点击主页按钮卡死 issues #562 2024-02-21 23:27:32 +08:00
dd6c537135 Merge branch 'feature-chargeVideo' 2024-02-19 23:24:12 +08:00
bcf94e287a mod: 修改收藏视频响应判断 2024-02-18 23:44:21 +08:00
841d0f25f5 fix: 评论区jumpUrl BV跳转 2024-02-18 08:20:48 +08:00
4811dc5ba5 fix: changeSeasonOrbangu aid null 2024-02-18 08:11:11 +08:00
41af6c799b Merge branch 'main' into fix 2024-02-18 08:10:20 +08:00
e8f63f6114 fix: up投稿动态页增加未登录风控提示 2024-02-17 17:32:13 +08:00
d1e8068e51 Merge pull request #514 from orz12/fix-audio-fucus-interrupt
fix: 修复焦点恢复时错误播放的问题
2024-02-17 16:47:13 +08:00
6de9b1977c Merge pull request #548 from orz12/mod-imagepreview-hide-statusbar-in-android
mod: 图片预览页,安卓也隐藏状态栏
2024-02-17 15:09:52 +08:00
3c0f54bfd7 fix: app端model bvid null issues #546 2024-02-16 21:46:48 +08:00
3d2c6a122a feat: 充电视频试看 2024-02-16 21:30:29 +08:00
8950658f08 mod: 图片预览页,安卓也隐藏状态栏 2024-02-16 20:20:37 +08:00
7a78729a44 fix: 合集切换推荐视频未刷新 2024-02-16 18:23:34 +08:00
03e5e22fef Merge pull request #458 from orz12/mod-add-time-in-rcmd-and-search
mod: 搜索和推荐页增加时间
2024-02-16 11:42:29 +08:00
aa93ce0b89 Merge branch 'main' into mod-add-time-in-rcmd-and-search 2024-02-16 11:42:01 +08:00
0c365ad049 Merge branch 'design' 2024-02-16 11:00:48 +08:00
3d5c578fef mod: 动态页面upPanel 2024-02-16 11:00:23 +08:00
0a22f0f543 Merge branch 'design' 2024-02-16 09:49:55 +08:00
77477ff4dd mod: merge main 2024-02-12 10:30:18 +08:00
b0c56feef5 mod: 首页网络异常请求重试 2024-02-07 02:47:11 +08:00
191472d0c4 mod: 网络请求异常样式修改 2024-02-07 01:17:35 +08:00
40c666e3d1 mod: 网络异常组件样式修改 2024-02-07 00:52:25 +08:00
63d600070b fix: 收藏详情页跳转搜索mediaId取值异常 2024-02-06 15:27:39 +08:00
ebdeec6730 fix: up主页跳转搜索mid取值异常 2024-02-06 12:23:07 +08:00
ee2a273d8b Merge branch 'main' into fix 2024-02-06 11:15:09 +08:00
fb32388536 fix: 尝试修复焦点恢复时错误播放的问题 2024-02-04 18:44:48 +08:00
5f92a0c293 mod: 用户投稿显示弹幕数 2024-02-04 00:32:01 +08:00
10d2995429 mod: 对齐搜索栏调整 2024-01-27 12:06:45 +08:00
23c8b34189 fix: app端推荐屏蔽时间显示,播放量与弹幕组件改为动态类型 2024-01-26 16:40:29 +08:00
932be48125 mod: 推荐、搜索页添加时间,修复视频搜索页无法筛选和回顶 2024-01-26 16:38:56 +08:00
36 changed files with 402 additions and 242 deletions

View File

@ -22,20 +22,27 @@ class HttpError extends StatelessWidget {
"assets/images/error.svg", "assets/images/error.svg",
height: 200, height: 200,
), ),
const SizedBox(height: 20), const SizedBox(height: 30),
Text( Text(
errMsg ?? '请求异常', errMsg ?? '请求异常',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleSmall, style: Theme.of(context).textTheme.titleSmall,
), ),
const SizedBox(height: 30), const SizedBox(height: 20),
OutlinedButton.icon( FilledButton.tonal(
onPressed: () { onPressed: () {
fn!(); fn!();
}, },
icon: const Icon(Icons.arrow_forward_outlined, size: 20), style: ButtonStyle(
label: Text(btnText ?? '点击重试'), backgroundColor: MaterialStateProperty.resolveWith((states) {
) return Theme.of(context).colorScheme.primary.withAlpha(20);
}),
),
child: Text(
btnText ?? '点击重试',
style: TextStyle(color: Theme.of(context).colorScheme.primary),
),
),
], ],
), ),
), ),

View File

@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
class StatDanMu extends StatelessWidget { class StatDanMu extends StatelessWidget {
final String? theme; final String? theme;
final int? danmu; final dynamic danmu;
final String? size; final String? size;
const StatDanMu({Key? key, this.theme, this.danmu, this.size}) const StatDanMu({Key? key, this.theme, this.danmu, this.size})

View File

@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
class StatView extends StatelessWidget { class StatView extends StatelessWidget {
final String? theme; final String? theme;
final int? view; final dynamic view;
final String? size; final String? size;
const StatView({Key? key, this.theme, this.view, this.size}) const StatView({Key? key, this.theme, this.view, this.size})

View File

@ -1,6 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../models/model_rec_video_item.dart';
import 'stat/danmu.dart';
import 'stat/view.dart';
import '../../http/dynamics.dart'; import '../../http/dynamics.dart';
import '../../http/search.dart'; import '../../http/search.dart';
import '../../http/user.dart'; import '../../http/user.dart';
@ -322,19 +325,31 @@ class VideoStat extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RichText( return Row(
children: [
StatView(
theme: 'gray',
view: videoItem.stat.view,
),
const SizedBox(width: 8),
StatDanMu(
theme: 'gray',
danmu: videoItem.stat.danmu,
),
if (videoItem is RecVideoItemModel) ...<Widget>[
const Spacer(),
RichText(
maxLines: 1, maxLines: 1,
text: TextSpan( text: TextSpan(
style: TextStyle( style: TextStyle(
fontSize: MediaQuery.textScalerOf(context) fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
.scale(Theme.of(context).textTheme.labelSmall!.fontSize!),
color: Theme.of(context).colorScheme.outline, color: Theme.of(context).colorScheme.outline,
), ),
children: [ text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
TextSpan(text: '${Utils.numFormat(videoItem.stat.view)}观看'),
TextSpan(text: '${Utils.numFormat(videoItem.stat.danmu)}弹幕'),
],
), ),
const SizedBox(width: 4),
]
],
); );
} }
} }

View File

@ -101,10 +101,13 @@ class MemberHttp {
'data': MemberArchiveDataModel.fromJson(res.data['data']) 'data': MemberArchiveDataModel.fromJson(res.data['data'])
}; };
} else { } else {
Map errMap = {
-352: '风控校验失败,请检查登录状态',
};
return { return {
'status': false, 'status': false,
'data': [], 'data': [],
'msg': res.data['message'], 'msg': errMap[res.data['code']] ?? res.data['message'],
}; };
} }
} }
@ -123,10 +126,13 @@ class MemberHttp {
'data': DynamicsDataModel.fromJson(res.data['data']), 'data': DynamicsDataModel.fromJson(res.data['data']),
}; };
} else { } else {
Map errMap = {
-352: '风控校验失败,请检查登录状态',
};
return { return {
'status': false, 'status': false,
'data': [], 'data': [],
'msg': res.data['message'], 'msg': errMap[res.data['code']] ?? res.data['message'],
}; };
} }
} }

View File

@ -130,7 +130,7 @@ class VideoHttp {
} }
return {'status': true, 'data': list}; return {'status': true, 'data': list};
} else { } else {
return {'status': false, 'data': []}; return {'status': false, 'data': [], 'msg': res.data['message']};
} }
} catch (err) { } catch (err) {
return {'status': false, 'data': [], 'msg': err}; return {'status': false, 'data': [], 'msg': err};

View File

@ -1,3 +1,5 @@
import 'package:pilipala/utils/id_utils.dart';
class RecVideoItemAppModel { class RecVideoItemAppModel {
RecVideoItemAppModel({ RecVideoItemAppModel({
this.id, this.id,
@ -50,14 +52,15 @@ class RecVideoItemAppModel {
? json['player_args']['aid'] ? json['player_args']['aid']
: int.parse(json['param'] ?? '-1'); : int.parse(json['param'] ?? '-1');
aid = json['player_args'] != null ? json['player_args']['aid'] : -1; aid = json['player_args'] != null ? json['player_args']['aid'] : -1;
bvid = null; bvid = json['player_args'] != null
? IdUtils.av2bv(json['player_args']['aid'])
: '';
cid = json['player_args'] != null ? json['player_args']['cid'] : -1; cid = json['player_args'] != null ? json['player_args']['cid'] : -1;
pic = json['cover']; pic = json['cover'];
stat = RcmdStat.fromJson(json); stat = RcmdStat.fromJson(json);
// 改用player_args中的duration作为原始数据秒数 // 改用player_args中的duration作为原始数据秒数
duration = json['player_args'] != null duration =
? json['player_args']['duration'] json['player_args'] != null ? json['player_args']['duration'] : -1;
: -1;
//duration = json['cover_right_text']; //duration = json['cover_right_text'];
title = json['title']; title = json['title'];
owner = RcmdOwner.fromJson(json); owner = RcmdOwner.fromJson(json);

View File

@ -142,7 +142,7 @@ class Stat {
Stat.fromJson(Map<String, dynamic> json) { Stat.fromJson(Map<String, dynamic> json) {
view = json["play"]; view = json["play"];
danmaku = json['comment']; danmaku = json['video_review'];
} }
} }

View File

@ -34,6 +34,7 @@ class PlayUrlModel {
String? seekParam; String? seekParam;
String? seekType; String? seekType;
Dash? dash; Dash? dash;
List<Durl>? durl;
List<FormatItem>? supportFormats; List<FormatItem>? supportFormats;
// String? highFormat; // String? highFormat;
int? lastPlayTime; int? lastPlayTime;
@ -52,7 +53,8 @@ class PlayUrlModel {
videoCodecid = json['video_codecid']; videoCodecid = json['video_codecid'];
seekParam = json['seek_param']; seekParam = json['seek_param'];
seekType = json['seek_type']; seekType = json['seek_type'];
dash = Dash.fromJson(json['dash']); dash = json['dash'] != null ? Dash.fromJson(json['dash']) : null;
durl = json['durl']?.map<Durl>((e) => Durl.fromJson(e)).toList();
supportFormats = json['support_formats'] != null supportFormats = json['support_formats'] != null
? json['support_formats'] ? json['support_formats']
.map<FormatItem>((e) => FormatItem.fromJson(e)) .map<FormatItem>((e) => FormatItem.fromJson(e))
@ -250,3 +252,30 @@ class Flac {
audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null; audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null;
} }
} }
class Durl {
Durl({
this.order,
this.length,
this.size,
this.ahead,
this.vhead,
this.url,
});
int? order;
int? length;
int? size;
String? ahead;
String? vhead;
String? url;
Durl.fromJson(Map<String, dynamic> json) {
order = json['order'];
length = json['length'];
size = json['size'];
ahead = json['ahead'];
vhead = json['vhead'];
url = json['url'];
}
}

View File

@ -218,14 +218,12 @@ class BangumiIntroController extends GetxController {
addIds: addMediaIdsNew.join(','), addIds: addMediaIdsNew.join(','),
delIds: delMediaIdsNew.join(',')); delIds: delMediaIdsNew.join(','));
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
addMediaIdsNew = []; addMediaIdsNew = [];
delMediaIdsNew = []; delMediaIdsNew = [];
Get.back();
// 重新获取收藏状态 // 重新获取收藏状态
queryHasFavVideo(); queryHasFavVideo();
SmartDialog.showToast('✅ 操作成功'); SmartDialog.showToast('✅ 操作成功');
} Get.back();
} }
} }

View File

@ -9,7 +9,6 @@ import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/pages/rcmd/view.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/bangumu_card_v.dart'; import 'widgets/bangumu_card_v.dart';
@ -199,7 +198,10 @@ class _BangumiPageState extends State<BangumiPage>
} else { } else {
return HttpError( return HttpError(
errMsg: data['msg'], errMsg: data['msg'],
fn: () => {}, fn: () {
_futureBuilderFuture =
_bangumidController.queryBangumiListFeed();
},
); );
} }
} else { } else {
@ -208,7 +210,6 @@ class _BangumiPageState extends State<BangumiPage>
}, },
), ),
), ),
const LoadingMore()
], ],
), ),
); );

View File

@ -192,22 +192,6 @@ class _DynamicsPageState extends State<DynamicsPage>
) )
], ],
), ),
// Obx(
// () => Visibility(
// visible: _dynamicsController.userLogin.value,
// child: Positioned(
// right: 4,
// top: 0,
// bottom: 0,
// child: IconButton(
// padding: EdgeInsets.zero,
// onPressed: () =>
// {feedBack(), _dynamicsController.resetSearch()},
// icon: const Icon(Icons.history, size: 21),
// ),
// ),
// ),
// ),
], ],
), ),
), ),
@ -229,7 +213,8 @@ class _DynamicsPageState extends State<DynamicsPage>
return Obx(() => UpPanel(_dynamicsController.upData.value)); return Obx(() => UpPanel(_dynamicsController.upData.value));
} else { } else {
return const SliverToBoxAdapter( return const SliverToBoxAdapter(
child: SizedBox(height: 80)); child: SizedBox(height: 80),
);
} }
} else { } else {
return const SliverToBoxAdapter( return const SliverToBoxAdapter(
@ -240,15 +225,6 @@ class _DynamicsPageState extends State<DynamicsPage>
} }
}, },
), ),
SliverToBoxAdapter(
child: Container(
height: 6,
color: Theme.of(context)
.colorScheme
.onInverseSurface
.withOpacity(0.5),
),
),
FutureBuilder( FutureBuilder(
future: _futureBuilderFuture, future: _futureBuilderFuture,
builder: (context, snapshot) { builder: (context, snapshot) {

View File

@ -36,8 +36,7 @@ class _UpPanelState extends State<UpPanel> {
} }
upList.insert( upList.insert(
0, 0,
UpItem( UpItem(face: '', uname: '全部动态', mid: -1),
face: 'https://files.catbox.moe/8uc48f.png', uname: '全部动态', mid: -1),
); );
userInfo = userInfoCache.get('userInfoCache'); userInfo = userInfoCache.get('userInfoCache');
upList.insert( upList.insert(
@ -56,7 +55,7 @@ class _UpPanelState extends State<UpPanel> {
floating: true, floating: true,
pinned: false, pinned: false,
delegate: _SliverHeaderDelegate( delegate: _SliverHeaderDelegate(
height: 124, height: 126,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -121,6 +120,13 @@ class _UpPanelState extends State<UpPanel> {
], ],
), ),
), ),
Container(
height: 6,
color: Theme.of(context)
.colorScheme
.onInverseSurface
.withOpacity(0.5),
),
], ],
)), )),
); );
@ -171,6 +177,9 @@ class _UpPanelState extends State<UpPanel> {
}, },
onLongPress: () { onLongPress: () {
feedBack(); feedBack();
if (data.mid == -1) {
return;
}
String heroTag = Utils.makeHeroTag(data.mid); String heroTag = Utils.makeHeroTag(data.mid);
Get.toNamed('/member?mid=${data.mid}', Get.toNamed('/member?mid=${data.mid}',
arguments: {'face': data.face, 'heroTag': heroTag}); arguments: {'face': data.face, 'heroTag': heroTag});
@ -198,11 +207,18 @@ class _UpPanelState extends State<UpPanel> {
backgroundColor: data.type == 'live' backgroundColor: data.type == 'live'
? Theme.of(context).colorScheme.secondaryContainer ? Theme.of(context).colorScheme.secondaryContainer
: Theme.of(context).colorScheme.primary, : Theme.of(context).colorScheme.primary,
child: NetworkImgLayer( child: data.face != ''
width: 49, ? NetworkImgLayer(
height: 49, width: 50,
height: 50,
src: data.face, src: data.face,
type: 'avatar', type: 'avatar',
)
: const CircleAvatar(
radius: 25,
backgroundImage: AssetImage(
'assets/images/noface.jpeg',
),
), ),
), ),
Padding( Padding(
@ -271,13 +287,11 @@ class UpPanelSkeleton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( Container(
width: 49, width: 50,
height: 49, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.onInverseSurface, color: Theme.of(context).colorScheme.onInverseSurface,
borderRadius: const BorderRadius.all( borderRadius: BorderRadius.circular(50),
Radius.circular(24),
),
), ),
), ),
Container( Container(

View File

@ -60,7 +60,6 @@ class FavDetailController extends GetxController {
var result = await VideoHttp.favVideo( var result = await VideoHttp.favVideo(
aid: id, addIds: '', delIds: mediaId.toString()); aid: id, addIds: '', delIds: mediaId.toString());
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
List dataList = favList; List dataList = favList;
for (var i in dataList) { for (var i in dataList) {
if (i.id == id) { if (i.id == id) {
@ -71,7 +70,6 @@ class FavDetailController extends GetxController {
SmartDialog.showToast('取消收藏'); SmartDialog.showToast('取消收藏');
} }
} }
}
onLoad() { onLoad() {
queryUserFavFolderDetail(type: 'onLoad'); queryUserFavFolderDetail(type: 'onLoad');

View File

@ -29,6 +29,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
mediaId = Get.parameters['mediaId']!;
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail(); _futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
mediaId = Get.parameters['mediaId']!; mediaId = Get.parameters['mediaId']!;
titleStreamC = StreamController<bool>(); titleStreamC = StreamController<bool>();

View File

@ -80,7 +80,6 @@ class FavSearchController extends GetxController {
var result = await VideoHttp.favVideo( var result = await VideoHttp.favVideo(
aid: id, addIds: '', delIds: mediaId.toString()); aid: id, addIds: '', delIds: mediaId.toString());
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
List dataList = favList; List dataList = favList;
for (var i in dataList) { for (var i in dataList) {
if (i.id == id) { if (i.id == id) {
@ -92,4 +91,3 @@ class FavSearchController extends GetxController {
} }
} }
} }
}

View File

@ -89,8 +89,7 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
if (data['status']) { if (data['status']) {
return Obx( return Obx(
() => SliverList( () => SliverList(
delegate: delegate: SliverChildBuilderDelegate((context, index) {
SliverChildBuilderDelegate((context, index) {
return VideoCardH( return VideoCardH(
videoItem: _hotController.videoList[index], videoItem: _hotController.videoList[index],
showPubdate: true, showPubdate: true,
@ -110,7 +109,12 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
} else { } else {
return HttpError( return HttpError(
errMsg: data['msg'], errMsg: data['msg'],
fn: () => setState(() {}), fn: () {
setState(() {
_futureBuilderFuture =
_hotController.queryHotFeed('init');
});
},
); );
} }
} else { } else {

View File

@ -10,8 +10,7 @@ class LiveController extends GetxController {
int count = 12; int count = 12;
int _currentPage = 1; int _currentPage = 1;
RxInt crossAxisCount = 2.obs; RxInt crossAxisCount = 2.obs;
RxList<LiveItemModel> liveList = [LiveItemModel()].obs; RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
bool isLoadingMore = false;
bool flag = false; bool flag = false;
OverlayEntry? popupDialog; OverlayEntry? popupDialog;
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
@ -39,7 +38,6 @@ class LiveController extends GetxController {
} }
_currentPage += 1; _currentPage += 1;
} }
isLoadingMore = false;
return res; return res;
} }

View File

@ -11,7 +11,6 @@ import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/pages/rcmd/index.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/live_item.dart'; import 'widgets/live_item.dart';
@ -45,8 +44,8 @@ class _LivePageState extends State<LivePage>
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) { scrollController.position.maxScrollExtent - 200) {
EasyThrottle.throttle('liveList', const Duration(seconds: 1), () { EasyThrottle.throttle('liveList', const Duration(milliseconds: 200),
_liveController.isLoadingMore = true; () {
_liveController.onLoad(); _liveController.onLoad();
}); });
} }
@ -108,24 +107,20 @@ class _LivePageState extends State<LivePage>
} else { } else {
return HttpError( return HttpError(
errMsg: data['msg'], errMsg: data['msg'],
fn: () => {}, fn: () {
setState(() {
_futureBuilderFuture =
_liveController.queryLiveList('init');
});
},
); );
} }
} else { } else {
// 缓存数据
if (_liveController.liveList.length > 1) {
return contentGrid(
_liveController, _liveController.liveList);
}
// 骨架屏
else {
return contentGrid(_liveController, []); return contentGrid(_liveController, []);
} }
}
}, },
), ),
), ),
LoadingMore(ctr: _liveController)
], ],
), ),
), ),

View File

@ -4,6 +4,8 @@ import 'package:pilipala/http/live.dart';
import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/models/live/room_info.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import '../../models/live/room_info_h5.dart'; import '../../models/live/room_info_h5.dart';
import '../../utils/storage.dart';
import '../../utils/video_utils.dart';
class LiveRoomController extends GetxController { class LiveRoomController extends GetxController {
String cover = ''; String cover = '';
@ -16,6 +18,7 @@ class LiveRoomController extends GetxController {
PlPlayerController plPlayerController = PlPlayerController plPlayerController =
PlPlayerController.getInstance(videoType: 'live'); PlPlayerController.getInstance(videoType: 'live');
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs; Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
late bool enableCDN;
@override @override
void onInit() { void onInit() {
@ -31,6 +34,8 @@ class LiveRoomController extends GetxController {
cover = liveItem.cover; cover = liveItem.cover;
} }
} }
// CDN优化
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
} }
playerInit(source) async { playerInit(source) async {
@ -57,7 +62,9 @@ class LiveRoomController extends GetxController {
List<CodecItem> codec = List<CodecItem> codec =
res['data'].playurlInfo.playurl.stream.first.format.first.codec; res['data'].playurlInfo.playurl.stream.first.format.first.codec;
CodecItem item = codec.first; CodecItem item = codec.first;
String videoUrl = (item.urlInfo?.first.host)! + String videoUrl = enableCDN
? VideoUtils.getCdnUrl(item)
: (item.urlInfo?.first.host)! +
item.baseUrl! + item.baseUrl! +
item.urlInfo!.first.extra!; item.urlInfo!.first.extra!;
await playerInit(videoUrl); await playerInit(videoUrl);

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import '../../common/widgets/http_error.dart';
import 'controller.dart'; import 'controller.dart';
class MemberArchivePage extends StatefulWidget { class MemberArchivePage extends StatefulWidget {
@ -86,10 +87,16 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
: const SliverToBoxAdapter(), : const SliverToBoxAdapter(),
); );
} else { } else {
return const SliverToBoxAdapter(); return HttpError(
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return const SliverToBoxAdapter(); return HttpError(
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return const SliverToBoxAdapter(); return const SliverToBoxAdapter();

View File

@ -4,6 +4,7 @@ import 'package:get/get.dart';
import 'package:pilipala/pages/member_dynamics/index.dart'; import 'package:pilipala/pages/member_dynamics/index.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import '../../common/widgets/http_error.dart';
import '../dynamics/widgets/dynamic_panel.dart'; import '../dynamics/widgets/dynamic_panel.dart';
class MemberDynamicsPage extends StatefulWidget { class MemberDynamicsPage extends StatefulWidget {
@ -80,10 +81,16 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
: const SliverToBoxAdapter(), : const SliverToBoxAdapter(),
); );
} else { } else {
return const SliverToBoxAdapter(); return HttpError(
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return const SliverToBoxAdapter(); return HttpError(
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return const SliverToBoxAdapter(); return const SliverToBoxAdapter();

View File

@ -102,15 +102,12 @@ class _ImagePreviewState extends State<ImagePreview>
); );
} }
// 设置状态栏图标透明 // 隐藏状态栏,避免遮挡图片内容
setStatusBar() async { setStatusBar() async {
if (Platform.isIOS) { if (Platform.isIOS || Platform.isAndroid) {
await StatusBarControl.setHidden(true, await StatusBarControl.setHidden(true,
animation: StatusBarAnimation.SLIDE); animation: StatusBarAnimation.SLIDE);
} }
if (Platform.isAndroid) {
await StatusBarControl.setColor(Colors.transparent);
}
} }
@override @override

View File

@ -44,7 +44,7 @@ class _RcmdPageState extends State<RcmdPage>
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) { scrollController.position.maxScrollExtent - 200) {
EasyThrottle.throttle( EasyThrottle.throttle(
'my-throttler', const Duration(milliseconds: 500), () { 'my-throttler', const Duration(milliseconds: 200), () {
_rcmdController.isLoadingMore = true; _rcmdController.isLoadingMore = true;
_rcmdController.onLoad(); _rcmdController.onLoad();
}); });
@ -113,6 +113,7 @@ class _RcmdPageState extends State<RcmdPage>
errMsg: data['msg'], errMsg: data['msg'],
fn: () { fn: () {
setState(() { setState(() {
_rcmdController.isLoadingMore = true;
_futureBuilderFuture = _futureBuilderFuture =
_rcmdController.queryRcmdFeed('init'); _rcmdController.queryRcmdFeed('init');
}); });
@ -125,7 +126,6 @@ class _RcmdPageState extends State<RcmdPage>
}, },
), ),
), ),
LoadingMore(ctr: _rcmdController),
], ],
), ),
), ),
@ -188,33 +188,3 @@ class _RcmdPageState extends State<RcmdPage>
); );
} }
} }
class LoadingMore extends StatelessWidget {
final dynamic ctr;
const LoadingMore({super.key, this.ctr});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).padding.bottom + 80,
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: GestureDetector(
onTap: () {
if (ctr != null) {
ctr!.isLoadingMore = true;
ctr!.onLoad();
}
},
child: Center(
child: Text(
'点击加载更多 👇',
style: TextStyle(
color: Theme.of(context).colorScheme.outline, fontSize: 13),
),
),
),
),
);
}
}

View File

@ -187,9 +187,13 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
), ),
); );
} else { } else {
return HttpError( return CustomScrollView(
slivers: [
HttpError(
errMsg: data['msg'], errMsg: data['msg'],
fn: () => setState(() {}), fn: () => setState(() {}),
)
],
); );
} }
} else { } else {

View File

@ -105,7 +105,11 @@ class _SearchPanelState extends State<SearchPanel>
slivers: [ slivers: [
HttpError( HttpError(
errMsg: data['msg'], errMsg: data['msg'],
fn: () => setState(() {}), fn: () {
setState(() {
_searchPanelController.onSearch();
});
},
), ),
], ],
); );
@ -116,7 +120,11 @@ class _SearchPanelState extends State<SearchPanel>
slivers: [ slivers: [
HttpError( HttpError(
errMsg: '没有相关数据', errMsg: '没有相关数据',
fn: () => setState(() {}), fn: () {
setState(() {
_searchPanelController.onSearch();
});
},
), ),
], ],
); );

View File

@ -35,7 +35,7 @@ class SearchVideoPanel extends StatelessWidget {
padding: index == 0 padding: index == 0
? const EdgeInsets.only(top: 2) ? const EdgeInsets.only(top: 2)
: EdgeInsets.zero, : EdgeInsets.zero,
child: VideoCardH(videoItem: i), child: VideoCardH(videoItem: i, showPubdate: true),
); );
}, },
), ),
@ -70,7 +70,7 @@ class SearchVideoPanel extends StatelessWidget {
controller.selectedType.value = i['type']; controller.selectedType.value = i['type'];
ctr!.order.value = ctr!.order.value =
i['type'].toString().split('.').last; i['type'].toString().split('.').last;
SmartDialog.showLoading(msg: 'loooad'); SmartDialog.showLoading(msg: 'loading');
await ctr!.onRefresh(); await ctr!.onRefresh();
SmartDialog.dismiss(); SmartDialog.dismiss();
}, },
@ -202,7 +202,7 @@ class VideoPanelController extends GetxController {
Get.find<SearchPanelController>( Get.find<SearchPanelController>(
tag: 'video${searchPanelCtr.keyword!}'); tag: 'video${searchPanelCtr.keyword!}');
ctr.duration.value = i['value']; ctr.duration.value = i['value'];
SmartDialog.showLoading(msg: 'loooad'); SmartDialog.showLoading(msg: 'loading');
await ctr.onRefresh(); await ctr.onRefresh();
SmartDialog.dismiss(); SmartDialog.dismiss();
}, },

View File

@ -90,6 +90,8 @@ class VideoDetailController extends GetxController
late String cacheDecode; late String cacheDecode;
late int cacheAudioQa; late int cacheAudioQa;
PersistentBottomSheetController? replyReplyBottomSheetCtr;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -140,7 +142,7 @@ class VideoDetailController extends GetxController
} }
showReplyReplyPanel() { showReplyReplyPanel() {
PersistentBottomSheetController? ctr = replyReplyBottomSheetCtr =
scaffoldKey.currentState?.showBottomSheet((BuildContext context) { scaffoldKey.currentState?.showBottomSheet((BuildContext context) {
return VideoReplyReplyPanel( return VideoReplyReplyPanel(
oid: oid.value, oid: oid.value,
@ -153,7 +155,7 @@ class VideoDetailController extends GetxController
source: 'videoDetail', source: 'videoDetail',
); );
}); });
ctr?.closed.then((value) { replyReplyBottomSheetCtr?.closed.then((value) {
fRpid = 0; fRpid = 0;
}); });
} }
@ -229,9 +231,11 @@ class VideoDetailController extends GetxController
seekTo: seekToTime ?? defaultST, seekTo: seekToTime ?? defaultST,
duration: duration ?? Duration(milliseconds: data.timeLength ?? 0), duration: duration ?? Duration(milliseconds: data.timeLength ?? 0),
// 宽>高 水平 否则 垂直 // 宽>高 水平 否则 垂直
direction: (firstVideo.width! - firstVideo.height!) > 0 direction: firstVideo.width != null && firstVideo.height != null
? ((firstVideo.width! - firstVideo.height!) > 0
? 'horizontal' ? 'horizontal'
: 'vertical', : 'vertical')
: null,
bvid: bvid, bvid: bvid,
cid: cid.value, cid: cid.value,
enableHeart: enableHeart, enableHeart: enableHeart,
@ -248,6 +252,21 @@ class VideoDetailController extends GetxController
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
if (result['status']) { if (result['status']) {
data = result['data']; data = result['data'];
if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) {
SmartDialog.showToast(
'该视频为专属视频,仅提供试看',
displayTime: const Duration(seconds: 3),
);
videoUrl = data.durl!.first.url!;
audioUrl = '';
defaultST = Duration.zero;
firstVideo = VideoItem();
if (autoPlay.value) {
await playerInit();
isShowCover.value = false;
}
return result;
}
final List<VideoItem> allVideosList = data.dash!.video!; final List<VideoItem> allVideosList = data.dash!.video!;
try { try {
// 当前可播放的最高质量视频 // 当前可播放的最高质量视频
@ -355,4 +374,11 @@ class VideoDetailController extends GetxController
} }
return result; return result;
} }
// mob端全屏状态关闭二级回复
hiddenReplyReplyPanel() {
replyReplyBottomSheetCtr != null
? replyReplyBottomSheetCtr!.close()
: print('replyReplyBottomSheetCtr is null');
}
} }

View File

@ -18,6 +18,7 @@ import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import '../related/index.dart';
import 'widgets/group_panel.dart'; import 'widgets/group_panel.dart';
class VideoIntroController extends GetxController { class VideoIntroController extends GetxController {
@ -304,11 +305,9 @@ class VideoIntroController extends GetxController {
delIds: favStatus == 1 ? '$defaultFolderId' : '', delIds: favStatus == 1 ? '$defaultFolderId' : '',
); );
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
// 重新获取收藏状态 // 重新获取收藏状态
await queryHasFavVideo(); await queryHasFavVideo();
SmartDialog.showToast('✅ 操作成功'); SmartDialog.showToast('✅ 操作成功');
}
} else { } else {
SmartDialog.showToast(result['msg']); SmartDialog.showToast(result['msg']);
} }
@ -333,14 +332,12 @@ class VideoIntroController extends GetxController {
delIds: delMediaIdsNew.join(',')); delIds: delMediaIdsNew.join(','));
SmartDialog.dismiss(); SmartDialog.dismiss();
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
addMediaIdsNew = []; addMediaIdsNew = [];
delMediaIdsNew = []; delMediaIdsNew = [];
Get.back(); Get.back();
// 重新获取收藏状态 // 重新获取收藏状态
await queryHasFavVideo(); await queryHasFavVideo();
SmartDialog.showToast('✅ 操作成功'); SmartDialog.showToast('✅ 操作成功');
}
} else { } else {
SmartDialog.showToast(result['msg']); SmartDialog.showToast(result['msg']);
} }
@ -478,11 +475,15 @@ class VideoIntroController extends GetxController {
// 重新获取视频资源 // 重新获取视频资源
final VideoDetailController videoDetailCtr = final VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: heroTag); Get.find<VideoDetailController>(tag: heroTag);
final ReleatedController releatedCtr =
Get.find<ReleatedController>(tag: heroTag);
videoDetailCtr.bvid = bvid; videoDetailCtr.bvid = bvid;
videoDetailCtr.oid.value = aid; videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid);
videoDetailCtr.cid.value = cid; videoDetailCtr.cid.value = cid;
videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.danmakuCid.value = cid;
videoDetailCtr.queryVideoUrl(); videoDetailCtr.queryVideoUrl();
releatedCtr.bvid = bvid;
releatedCtr.queryRelatedVideo();
// 重新请求评论 // 重新请求评论
try { try {
/// 未渲染回复组件时可能异常 /// 未渲染回复组件时可能异常

View File

@ -1,14 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/http/video.dart'; import 'package:pilipala/http/video.dart';
import '../../../../models/model_hot_video_item.dart';
class ReleatedController extends GetxController { class ReleatedController extends GetxController {
// 视频aid // 视频aid
String bvid = Get.parameters['bvid'] ?? ""; String bvid = Get.parameters['bvid'] ?? "";
// 推荐视频列表 // 推荐视频列表
List relatedVideoList = []; RxList relatedVideoList = <HotVideoItemModel>[].obs;
OverlayEntry? popupDialog; OverlayEntry? popupDialog;
Future<dynamic> queryRelatedVideo() => VideoHttp.relatedVideoList(bvid: bvid); Future<dynamic> queryRelatedVideo() async {
return VideoHttp.relatedVideoList(bvid: bvid).then((value) {
if (value['status']) {
relatedVideoList.value = value['data'];
}
return value;
});
}
} }

View File

@ -7,35 +7,58 @@ import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/common/widgets/video_card_h.dart';
import './controller.dart'; import './controller.dart';
class RelatedVideoPanel extends StatelessWidget { class RelatedVideoPanel extends StatefulWidget {
final ReleatedController _releatedController = const RelatedVideoPanel({super.key});
@override
State<RelatedVideoPanel> createState() => _RelatedVideoPanelState();
}
class _RelatedVideoPanelState extends State<RelatedVideoPanel>
with AutomaticKeepAliveClientMixin {
late ReleatedController _releatedController;
late Future _futureBuilder;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
_releatedController =
Get.put(ReleatedController(), tag: Get.arguments?['heroTag']); Get.put(ReleatedController(), tag: Get.arguments?['heroTag']);
RelatedVideoPanel({super.key}); _futureBuilder = _releatedController.queryRelatedVideo();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context);
return FutureBuilder( return FutureBuilder(
future: _releatedController.queryRelatedVideo(), future: _futureBuilder,
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) { if (snapshot.data == null) {
return const SliverToBoxAdapter(child: SizedBox()); return const SliverToBoxAdapter(child: SizedBox());
} }
if (snapshot.data!['status']) { if (snapshot.data!['status'] && snapshot.data != null) {
RxList relatedVideoList = _releatedController.relatedVideoList;
// 请求成功 // 请求成功
return SliverList( return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate((context, index) { delegate: SliverChildBuilderDelegate((context, index) {
if (index == snapshot.data['data'].length) { if (index == relatedVideoList.length) {
return SizedBox(height: MediaQuery.of(context).padding.bottom); return SizedBox(
height: MediaQuery.of(context).padding.bottom);
} else { } else {
return Material( return Material(
child: VideoCardH( child: VideoCardH(
videoItem: snapshot.data['data'][index], videoItem: relatedVideoList[index],
showPubdate: true, showPubdate: true,
longPress: () { longPress: () {
try { try {
_releatedController.popupDialog = _releatedController.popupDialog =
_createPopupDialog(snapshot.data['data'][index]); _createPopupDialog(_releatedController
.relatedVideoList[index]);
Overlay.of(context) Overlay.of(context)
.insert(_releatedController.popupDialog!); .insert(_releatedController.popupDialog!);
} catch (err) { } catch (err) {
@ -48,7 +71,9 @@ class RelatedVideoPanel extends StatelessWidget {
), ),
); );
} }
}, childCount: snapshot.data['data'].length + 1)); }, childCount: relatedVideoList.length + 1),
),
);
} else { } else {
// 请求错误 // 请求错误
return HttpError(errMsg: '出错了', fn: () {}); return HttpError(errMsg: '出错了', fn: () {});

View File

@ -12,6 +12,7 @@ import 'package:pilipala/pages/preview/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/url_utils.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
@ -620,6 +621,13 @@ InlineSpan buildContent(
..onTap = () async { ..onTap = () async {
final String title = content.jumpUrl[matchStr]['title']; final String title = content.jumpUrl[matchStr]['title'];
if (appUrlSchema == '') { if (appUrlSchema == '') {
if (matchStr.startsWith('BV')) {
UrlUtils.matchUrlPush(
matchStr,
title,
'',
);
} else {
final String redirectUrl = final String redirectUrl =
await UrlUtils.parseRedirectUrl(matchStr); await UrlUtils.parseRedirectUrl(matchStr);
final String pathSegment = Uri.parse(redirectUrl).path; final String pathSegment = Uri.parse(redirectUrl).path;
@ -641,6 +649,7 @@ InlineSpan buildContent(
}, },
); );
} }
}
} else { } else {
if (appUrlSchema.startsWith('bilibili://search')) { if (appUrlSchema.startsWith('bilibili://search')) {
Get.toNamed('/searchResult', Get.toNamed('/searchResult',

View File

@ -92,6 +92,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
videoSourceInit(); videoSourceInit();
appbarStreamListen(); appbarStreamListen();
lifecycleListener(); lifecycleListener();
fullScreenStatusListener();
} }
// 获取视频资源,初始化播放器 // 获取视频资源,初始化播放器
@ -188,6 +189,14 @@ class _VideoDetailPageState extends State<VideoDetailPage>
); );
} }
void fullScreenStatusListener() {
plPlayerController?.isFullScreen.listen((bool isFullScreen) {
if (isFullScreen) {
videoDetailController.hiddenReplyReplyPanel();
}
});
}
@override @override
void dispose() { void dispose() {
shutdownTimerService.handleWaitingFinished(); shutdownTimerService.handleWaitingFinished();
@ -225,7 +234,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
@override @override
// 返回当前页面时 // 返回当前页面时
void didPopNext() async { void didPopNext() async {
if (plPlayerController != null &&
plPlayerController!.videoPlayerController != null) {
setState(() => isShowing = true); setState(() => isShowing = true);
}
videoDetailController.isFirstTime = false; videoDetailController.isFirstTime = false;
final bool autoplay = autoPlayEnable; final bool autoplay = autoPlayEnable;
videoDetailController.playerInit(autoplay: autoplay); videoDetailController.playerInit(autoplay: autoplay);

View File

@ -20,11 +20,15 @@ class AudioSessionHandler {
session.interruptionEventStream.listen((event) { session.interruptionEventStream.listen((event) {
final player = PlPlayerController.getInstance(); final player = PlPlayerController.getInstance();
if (event.begin) { if (event.begin) {
if (player.playerStatus != PlayerStatus.playing) return;
switch (event.type) { switch (event.type) {
case AudioInterruptionType.duck: case AudioInterruptionType.duck:
player.setVolume(player.volume.value * 0.5); player.setVolume(player.volume.value * 0.5);
break; break;
case AudioInterruptionType.pause: case AudioInterruptionType.pause:
player.pause(isInterrupt: true);
_playInterrupted = true;
break;
case AudioInterruptionType.unknown: case AudioInterruptionType.unknown:
player.pause(isInterrupt: true); player.pause(isInterrupt: true);
_playInterrupted = true; _playInterrupted = true;
@ -36,7 +40,7 @@ class AudioSessionHandler {
player.setVolume(player.volume.value * 2); player.setVolume(player.volume.value * 2);
break; break;
case AudioInterruptionType.pause: case AudioInterruptionType.pause:
if (_playInterrupted) PlPlayerController.getInstance().play(); if (_playInterrupted) player.play();
break; break;
case AudioInterruptionType.unknown: case AudioInterruptionType.unknown:
break; break;
@ -47,7 +51,10 @@ class AudioSessionHandler {
// 耳机拔出暂停 // 耳机拔出暂停
session.becomingNoisyEventStream.listen((_) { session.becomingNoisyEventStream.listen((_) {
PlPlayerController.getInstance().pause(); final player = PlPlayerController.getInstance();
if (player.playerStatus == PlayerStatus.playing) {
player.pause();
}
}); });
} }
} }

View File

@ -63,6 +63,26 @@ class Utils {
} }
} }
// 完全相对时间显示
static String formatTimestampToRelativeTime(timeStamp) {
var difference = DateTime.now()
.difference(DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000));
if (difference.inDays > 365) {
return '${difference.inDays ~/ 365}年前';
} else if (difference.inDays > 30) {
return '${difference.inDays ~/ 30}个月前';
} else if (difference.inDays > 0) {
return '${difference.inDays}天前';
} else if (difference.inHours > 0) {
return '${difference.inHours}小时前';
} else if (difference.inMinutes > 0) {
return '${difference.inMinutes}分钟前';
} else {
return '刚刚';
}
}
// 时间显示刚刚x分钟前 // 时间显示刚刚x分钟前
static String dateFormat(timeStamp, {formatType = 'list'}) { static String dateFormat(timeStamp, {formatType = 'list'}) {
// 当前时间 // 当前时间

View File

@ -1,5 +1,7 @@
import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video/play/url.dart';
import '../models/live/room_info.dart';
class VideoUtils { class VideoUtils {
static String getCdnUrl(dynamic item) { static String getCdnUrl(dynamic item) {
var backupUrl = ""; var backupUrl = "";
@ -12,13 +14,20 @@ class VideoUtils {
} else if (item is AudioItem) { } else if (item is AudioItem) {
backupUrl = item.backupUrl ?? ""; backupUrl = item.backupUrl ?? "";
videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
} else if (item is CodecItem) {
backupUrl = (item.urlInfo?.first.host)! +
item.baseUrl! +
item.urlInfo!.first.extra!;
videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
} else { } else {
return ""; return "";
} }
/// issues #70 /// issues #70
if (videoUrl.contains(".mcdn.bilivideo") || if (videoUrl.contains(".mcdn.bilivideo")) {
videoUrl.contains("/upgcxcode/")) { videoUrl =
'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}';
} else if (videoUrl.contains("/upgcxcode/")) {
//CDN列表 //CDN列表
var cdnList = { var cdnList = {
'ali': 'upos-sz-mirrorali.bilivideo.com', 'ali': 'upos-sz-mirrorali.bilivideo.com',