mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt live
Closes #979 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -4,8 +4,6 @@ import 'package:PiliPlus/build_config.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_toast.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/models/common/theme/theme_color_type.dart';
|
||||
import 'package:PiliPlus/pages/main/view.dart';
|
||||
import 'package:PiliPlus/pages/video/view.dart';
|
||||
import 'package:PiliPlus/router/app_pages.dart';
|
||||
import 'package:PiliPlus/services/account_service.dart';
|
||||
import 'package:PiliPlus/services/loggeer.dart';
|
||||
@@ -13,6 +11,7 @@ import 'package:PiliPlus/services/service_locator.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/cache_manage.dart';
|
||||
import 'package:PiliPlus/utils/date_util.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
@@ -227,8 +226,7 @@ class MyApp extends StatelessWidget {
|
||||
),
|
||||
navigatorObservers: [
|
||||
FlutterSmartDialog.observer,
|
||||
VideoDetailPageV.routeObserver,
|
||||
MainApp.routeObserver,
|
||||
PageUtils.routeObserver,
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -259,6 +259,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
),
|
||||
const Spacer(),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => Get.to(const LiveFollowPage()),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -293,6 +294,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
||||
return SizedBox(
|
||||
width: 65,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => PageUtils.toLiveRoom(item.roomid),
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
|
||||
@@ -32,6 +32,7 @@ class LiveRoomController extends GetxController {
|
||||
final String heroTag;
|
||||
|
||||
int roomId = Get.arguments;
|
||||
DanmakuController? danmakuController;
|
||||
PlPlayerController plPlayerController = PlPlayerController.getInstance(
|
||||
isLive: true,
|
||||
);
|
||||
@@ -61,7 +62,7 @@ class LiveRoomController extends GetxController {
|
||||
List<RichTextItem>? savedDanmaku;
|
||||
RxList<dynamic> messages = [].obs;
|
||||
RxBool disableAutoScroll = false.obs;
|
||||
LiveMessageStream? msgStream;
|
||||
LiveMessageStream? _msgStream;
|
||||
late final ScrollController scrollController = ScrollController()
|
||||
..addListener(listener);
|
||||
|
||||
@@ -73,6 +74,10 @@ class LiveRoomController extends GetxController {
|
||||
late final isLogin = accountService.isLogin.value;
|
||||
AccountService accountService = Get.find<AccountService>();
|
||||
|
||||
String? videoUrl;
|
||||
bool? isPlaying;
|
||||
late bool isFullScreen = false;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@@ -83,10 +88,13 @@ class LiveRoomController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> playerInit(String source) {
|
||||
Future<void>? playerInit({bool autoplay = true}) {
|
||||
if (videoUrl == null) {
|
||||
return null;
|
||||
}
|
||||
return plPlayerController.setDataSource(
|
||||
DataSource(
|
||||
videoSource: source,
|
||||
videoSource: videoUrl,
|
||||
audioSource: null,
|
||||
type: DataSourceType.network,
|
||||
httpHeaders: {
|
||||
@@ -95,7 +103,8 @@ class LiveRoomController extends GetxController {
|
||||
'referer': HttpString.baseUrl,
|
||||
},
|
||||
),
|
||||
autoplay: true,
|
||||
isLive: true,
|
||||
autoplay: autoplay,
|
||||
isVertical: isPortrait.value,
|
||||
);
|
||||
}
|
||||
@@ -145,8 +154,8 @@ class LiveRoomController extends GetxController {
|
||||
.firstWhereOrNull((element) => element.code == currentQn)
|
||||
?.description ??
|
||||
currentQn.toString();
|
||||
String videoUrl = VideoUtils.getCdnUrl(item);
|
||||
await playerInit(videoUrl);
|
||||
videoUrl = VideoUtils.getCdnUrl(item);
|
||||
await playerInit();
|
||||
isLoaded.value = true;
|
||||
}
|
||||
}
|
||||
@@ -198,7 +207,12 @@ class LiveRoomController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void liveMsg() {
|
||||
void closeLiveMsg() {
|
||||
_msgStream?.close();
|
||||
_msgStream = null;
|
||||
}
|
||||
|
||||
void startLiveMsg() {
|
||||
if (messages.isEmpty) {
|
||||
LiveHttp.liveRoomDanmaPrefetch(roomId: roomId).then((v) {
|
||||
if (v['status']) {
|
||||
@@ -225,7 +239,7 @@ class LiveRoomController extends GetxController {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (msgStream != null) {
|
||||
if (_msgStream != null) {
|
||||
return;
|
||||
}
|
||||
if (dmInfo != null) {
|
||||
@@ -259,8 +273,7 @@ class LiveRoomController extends GetxController {
|
||||
cancelLiveTimer();
|
||||
savedDanmaku?.clear();
|
||||
savedDanmaku = null;
|
||||
msgStream?.close();
|
||||
msgStream = null;
|
||||
closeLiveMsg();
|
||||
scrollController
|
||||
..removeListener(listener)
|
||||
..dispose();
|
||||
@@ -285,7 +298,7 @@ class LiveRoomController extends GetxController {
|
||||
if (info.hostList.isNullOrEmpty) {
|
||||
return;
|
||||
}
|
||||
msgStream =
|
||||
_msgStream =
|
||||
LiveMessageStream(
|
||||
streamToken: info.token!,
|
||||
roomId: roomId,
|
||||
@@ -321,9 +334,11 @@ class LiveRoomController extends GetxController {
|
||||
selfSend: isLogin && uid == accountService.mid,
|
||||
),
|
||||
);
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => scrollToBottom(),
|
||||
);
|
||||
if (!isFullScreen) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => scrollToBottom(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
@@ -37,7 +37,7 @@ class LiveRoomPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
with WidgetsBindingObserver {
|
||||
with WidgetsBindingObserver, RouteAware {
|
||||
final String heroTag = Utils.generateRandomString(6);
|
||||
late final LiveRoomController _liveRoomController;
|
||||
late final PlPlayerController plPlayerController;
|
||||
@@ -63,18 +63,64 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
..addStatusLister(playerListener);
|
||||
}
|
||||
|
||||
void playerListener(PlayerStatus? status) {
|
||||
if (status != PlayerStatus.playing) {
|
||||
plPlayerController.danmakuController?.pause();
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context)! as PageRoute,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> didPopNext() async {
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
PlPlayerController.setPlayCallBack(plPlayerController.play);
|
||||
plPlayerController.danmakuController =
|
||||
_liveRoomController.danmakuController;
|
||||
_liveRoomController.startLiveTimer();
|
||||
if (plPlayerController.playerStatus.playing) {
|
||||
_liveRoomController
|
||||
..cancelLiveTimer()
|
||||
..msgStream?.close()
|
||||
..msgStream = null;
|
||||
..danmakuController?.resume()
|
||||
..startLiveMsg();
|
||||
} else {
|
||||
plPlayerController.danmakuController?.resume();
|
||||
final shouldPlay = _liveRoomController.isPlaying ?? false;
|
||||
if (shouldPlay) {
|
||||
_liveRoomController
|
||||
..danmakuController?.resume()
|
||||
..startLiveMsg();
|
||||
}
|
||||
await _liveRoomController.playerInit(autoplay: shouldPlay);
|
||||
}
|
||||
plPlayerController.addStatusLister(playerListener);
|
||||
|
||||
super.didPopNext();
|
||||
}
|
||||
|
||||
@override
|
||||
void didPushNext() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
plPlayerController.removeStatusLister(playerListener);
|
||||
_liveRoomController
|
||||
..danmakuController?.clear()
|
||||
..danmakuController?.pause()
|
||||
..cancelLiveTimer()
|
||||
..closeLiveMsg()
|
||||
..isPlaying = plPlayerController.playerStatus.playing;
|
||||
super.didPushNext();
|
||||
}
|
||||
|
||||
void playerListener(PlayerStatus? status) {
|
||||
if (status == PlayerStatus.playing) {
|
||||
_liveRoomController
|
||||
..danmakuController?.resume()
|
||||
..startLiveTimer()
|
||||
..liveMsg();
|
||||
..startLiveMsg();
|
||||
} else {
|
||||
_liveRoomController
|
||||
..danmakuController?.pause()
|
||||
..cancelLiveTimer()
|
||||
..closeLiveMsg();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +133,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
plPlayerController
|
||||
..removeStatusLister(playerListener)
|
||||
..dispose();
|
||||
PageUtils.routeObserver.unsubscribe(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -150,6 +197,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
Alignment? alignment,
|
||||
bool needDm = true,
|
||||
}) {
|
||||
_liveRoomController.isFullScreen = isFullScreen;
|
||||
return PopScope(
|
||||
canPop: !isFullScreen,
|
||||
onPopInvokedWithResult: (bool didPop, Object? result) {
|
||||
@@ -182,6 +230,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
danmuWidget: !needDm
|
||||
? null
|
||||
: LiveDanmaku(
|
||||
liveRoomController: _liveRoomController,
|
||||
plPlayerController: plPlayerController,
|
||||
isFullScreen: isFullScreen,
|
||||
isPipMode: isPipMode,
|
||||
@@ -743,12 +792,14 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
}
|
||||
|
||||
class LiveDanmaku extends StatefulWidget {
|
||||
final LiveRoomController liveRoomController;
|
||||
final PlPlayerController plPlayerController;
|
||||
final bool isPipMode;
|
||||
final bool isFullScreen;
|
||||
|
||||
const LiveDanmaku({
|
||||
super.key,
|
||||
required this.liveRoomController,
|
||||
required this.plPlayerController,
|
||||
this.isPipMode = false,
|
||||
required this.isFullScreen,
|
||||
@@ -791,7 +842,8 @@ class _LiveDanmakuState extends State<LiveDanmaku> {
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: DanmakuScreen(
|
||||
createdController: (DanmakuController e) {
|
||||
plPlayerController.danmakuController = e;
|
||||
widget.liveRoomController.danmakuController =
|
||||
plPlayerController.danmakuController = e;
|
||||
},
|
||||
option: DanmakuOption(
|
||||
fontSize: _fontSize,
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -23,9 +24,6 @@ class MainApp extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<MainApp> createState() => _MainAppState();
|
||||
|
||||
static final RouteObserver<PageRoute> routeObserver =
|
||||
RouteObserver<PageRoute>();
|
||||
}
|
||||
|
||||
class _MainAppState extends State<MainApp>
|
||||
@@ -43,7 +41,10 @@ class _MainAppState extends State<MainApp>
|
||||
super.didChangeDependencies();
|
||||
NetworkImgLayer.reduce =
|
||||
NetworkImgLayer.reduceLuxColor != null && context.isDarkMode;
|
||||
MainApp.routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context) as PageRoute,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -74,7 +75,7 @@ class _MainAppState extends State<MainApp>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
MainApp.routeObserver.unsubscribe(this);
|
||||
PageUtils.routeObserver.unsubscribe(this);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
GStorage.close();
|
||||
PiliScheme.listener?.cancel();
|
||||
|
||||
@@ -68,8 +68,6 @@ class VideoDetailPageV extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<VideoDetailPageV> createState() => _VideoDetailPageVState();
|
||||
static final RouteObserver<PageRoute> routeObserver =
|
||||
RouteObserver<PageRoute>();
|
||||
}
|
||||
|
||||
class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
@@ -381,7 +379,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
} else {
|
||||
PlPlayerController.updatePlayCount();
|
||||
}
|
||||
VideoDetailPageV.routeObserver.unsubscribe(this);
|
||||
PageUtils.routeObserver.unsubscribe(this);
|
||||
showStatusBar();
|
||||
_introScrollController?.dispose();
|
||||
super.dispose();
|
||||
@@ -476,7 +474,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
VideoDetailPageV.routeObserver.subscribe(
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context)! as PageRoute,
|
||||
);
|
||||
|
||||
@@ -507,14 +507,17 @@ class PlPlayerController {
|
||||
// 获取实例 传参
|
||||
static PlPlayerController getInstance({bool isLive = false}) {
|
||||
// 如果实例尚未创建,则创建一个新实例
|
||||
_instance ??= PlPlayerController._().._isLive = isLive;
|
||||
_instance!._playerCount.value += 1;
|
||||
_instance ??= PlPlayerController._();
|
||||
_instance!
|
||||
.._isLive = isLive
|
||||
.._playerCount.value += 1;
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
// 初始化资源
|
||||
Future<void> setDataSource(
|
||||
DataSource dataSource, {
|
||||
bool isLive = false,
|
||||
List<Segment>? segmentList,
|
||||
List<Segment>? viewPointList,
|
||||
bool? showVP,
|
||||
@@ -542,6 +545,7 @@ class PlPlayerController {
|
||||
VoidCallback? callback,
|
||||
}) async {
|
||||
try {
|
||||
_isLive = isLive;
|
||||
_videoType = videoType ?? VideoType.ugc;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
@@ -15,7 +15,6 @@ import 'package:PiliPlus/pages/contact/view.dart';
|
||||
import 'package:PiliPlus/pages/fav_panel/view.dart';
|
||||
import 'package:PiliPlus/pages/share/view.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/menu_row.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||
import 'package:PiliPlus/services/shutdown_timer_service.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/context_ext.dart';
|
||||
@@ -35,6 +34,9 @@ import 'package:get/get.dart' hide ContextExtensionss;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class PageUtils {
|
||||
static final RouteObserver<PageRoute> routeObserver =
|
||||
RouteObserver<PageRoute>();
|
||||
|
||||
static Future<void> imageView({
|
||||
int initialPage = 0,
|
||||
required List<SourceModel> imgList,
|
||||
@@ -690,10 +692,6 @@ class PageUtils {
|
||||
if (roomId == null) {
|
||||
return;
|
||||
}
|
||||
if (PlPlayerController.instanceExists()) {
|
||||
SmartDialog.showToast('unsupported');
|
||||
return;
|
||||
}
|
||||
if (off) {
|
||||
Get.offNamed('/liveRoom', arguments: roomId);
|
||||
} else {
|
||||
@@ -716,10 +714,6 @@ class PageUtils {
|
||||
int? id,
|
||||
bool off = false,
|
||||
}) {
|
||||
if (PlPlayerController.instance?.isLive == true) {
|
||||
SmartDialog.showToast('Living');
|
||||
return;
|
||||
}
|
||||
final arguments = {
|
||||
'aid': aid ?? IdUtils.bv2av(bvid!),
|
||||
'bvid': bvid ?? IdUtils.av2bv(aid!),
|
||||
|
||||
Reference in New Issue
Block a user