From 3004d63223864a4607b36c907597f5957758629c Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Thu, 7 Sep 2023 15:59:59 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E6=96=97=E9=B1=BC=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=BD=95=E6=92=AD=E5=88=A4=E6=96=AD=20#6,#103?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_room/live_room_controller.dart | 6 ++- .../example/simple_live_core_example.dart | 7 ++- simple_live_core/lib/src/douyu_site.dart | 43 +++++++++++-------- .../lib/src/model/live_room_detail.dart | 5 +++ 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/simple_live_app/lib/modules/live_room/live_room_controller.dart b/simple_live_app/lib/modules/live_room/live_room_controller.dart index 0339d486..4b85d26b 100644 --- a/simple_live_app/lib/modules/live_room/live_room_controller.dart +++ b/simple_live_app/lib/modules/live_room/live_room_controller.dart @@ -182,11 +182,13 @@ class LiveRoomController extends PlayerController { addHistory(); online.value = detail.value!.online; - liveStatus.value = detail.value!.status; + liveStatus.value = detail.value!.status || detail.value!.isRecord; if (liveStatus.value) { getPlayQualites(); } - + if (detail.value!.isRecord) { + addSysMsg("当前主播未开播,正在轮播录像"); + } addSysMsg("开始连接弹幕服务器"); initDanmau(); liveDanmaku.start(detail.value?.danmakuData); diff --git a/simple_live_core/example/simple_live_core_example.dart b/simple_live_core/example/simple_live_core_example.dart index ddb719ed..74f60edb 100644 --- a/simple_live_core/example/simple_live_core_example.dart +++ b/simple_live_core/example/simple_live_core_example.dart @@ -2,7 +2,7 @@ import 'package:simple_live_core/simple_live_core.dart'; void main() async { CoreLog.enableLog = true; - LiveSite site = BiliBiliSite(); + LiveSite site = DouyuSite(); var danmaku = site.getDanmaku(); danmaku.onMessage = (event) { if (event.type == LiveMessageType.chat) { @@ -17,13 +17,16 @@ void main() async { danmaku.onClose = (event) { print(event); }; - var detail = await site.getRoomDetail(roomId: "4245963"); + var detail = await site.getRoomDetail(roomId: "138243"); // var playQualites = await site.getPlayQualites(detail: detail); // var playUrls = // await site.getPlayUrls(detail: detail, quality: playQualites.first); // for (var element in playUrls) { // print(element); // } + print(detail); + danmaku.start(detail.danmakuData); + await Future.wait({}); } diff --git a/simple_live_core/lib/src/douyu_site.dart b/simple_live_core/lib/src/douyu_site.dart index 8974aada..d2a73eaf 100644 --- a/simple_live_core/lib/src/douyu_site.dart +++ b/simple_live_core/lib/src/douyu_site.dart @@ -175,14 +175,19 @@ class DouyuSite implements LiveSite { @override Future getRoomDetail({required String roomId}) async { var result = await HttpClient.instance.getJson( - "https://m.douyu.com/$roomId/index.pageContext.json", + "https://www.douyu.com/betard/$roomId", queryParameters: {}, header: { - 'referer': 'https://m.douyu.com/$roomId', + 'referer': 'https://www.douyu.com/$roomId', 'user-agent': - 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/114.0.0.0', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.43', }); - var roomInfo = result["pageProps"]["room"]["roomInfo"]["roomInfo"]; + Map roomInfo; + if (result is String) { + roomInfo = json.decode(result)["room"]; + } else { + roomInfo = result["room"]; + } var jsEncResult = await HttpClient.instance.getText( "https://www.douyu.com/swf_api/homeH5Enc?rids=$roomId", @@ -195,18 +200,19 @@ class DouyuSite implements LiveSite { var crptext = json.decode(jsEncResult)["data"]["room$roomId"].toString(); return LiveRoomDetail( - cover: roomInfo["roomSrc"].toString(), - online: parseHotNum(roomInfo["hn"].toString()), - roomId: roomInfo["rid"].toString(), - title: roomInfo["roomName"].toString(), - userName: roomInfo["nickname"].toString(), - userAvatar: roomInfo["avatar"].toString(), - introduction: "", - notice: roomInfo["notice"].toString(), - status: roomInfo["isLive"] == 1, - danmakuData: roomInfo["rid"].toString(), - data: await getPlayArgs(crptext, roomInfo["rid"].toString()), + cover: roomInfo["room_pic"].toString(), + online: int.tryParse(roomInfo["room_biz_all"]["hot"].toString()) ?? 0, + roomId: roomInfo["room_id"].toString(), + title: roomInfo["room_name"].toString(), + userName: roomInfo["owner_name"].toString(), + userAvatar: roomInfo["owner_avatar"].toString(), + introduction: roomInfo["show_details"].toString(), + notice: "", + status: roomInfo["rst"] != 3 && roomInfo["videoLoop"] != 1, + danmakuData: roomInfo["room_id"].toString(), + data: await getPlayArgs(crptext, roomInfo["room_id"].toString()), url: "https://www.douyu.com/$roomId", + isRecord: roomInfo["rst"] == 3, ); } @@ -279,12 +285,15 @@ class DouyuSite implements LiveSite { var items = []; for (var item in result["data"]["relateUser"]) { + var liveStatus = + (int.tryParse(item["anchorInfo"]["isLive"].toString()) ?? 0) == 1; + var roomType = + (int.tryParse(item["anchorInfo"]["roomType"].toString()) ?? 0); var roomItem = LiveAnchorItem( roomId: item["anchorInfo"]["rid"].toString(), avatar: item["anchorInfo"]["avatar"].toString(), userName: item["anchorInfo"]["nickName"].toString(), - liveStatus: - (int.tryParse(item["anchorInfo"]["isLive"].toString()) ?? 0) == 1, + liveStatus: liveStatus && roomType == 0, ); items.add(roomItem); } diff --git a/simple_live_core/lib/src/model/live_room_detail.dart b/simple_live_core/lib/src/model/live_room_detail.dart index cfa7bd08..05e6587e 100644 --- a/simple_live_core/lib/src/model/live_room_detail.dart +++ b/simple_live_core/lib/src/model/live_room_detail.dart @@ -34,6 +34,9 @@ class LiveRoomDetail { /// 弹幕附加信息 final dynamic danmakuData; + /// 是否录播 + final bool isRecord; + /// 链接 final String url; LiveRoomDetail({ @@ -49,6 +52,7 @@ class LiveRoomDetail { this.data, this.danmakuData, required this.url, + this.isRecord = false, }); @override @@ -66,6 +70,7 @@ class LiveRoomDetail { "data": data, "danmakuData": danmakuData, "url": url, + "isRecord": isRecord, }); } } From bb22d99afabba503125a563b1b7124a768843eae Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Fri, 8 Sep 2023 17:32:59 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=97=B6=E8=87=AA=E5=8A=A8=E9=87=8D=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_room/live_room_controller.dart | 79 ++++++++++++++----- .../live_room/player/player_controls.dart | 11 +-- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/simple_live_app/lib/modules/live_room/live_room_controller.dart b/simple_live_app/lib/modules/live_room/live_room_controller.dart index 4b85d26b..485aae6a 100644 --- a/simple_live_app/lib/modules/live_room/live_room_controller.dart +++ b/simple_live_app/lib/modules/live_room/live_room_controller.dart @@ -54,8 +54,8 @@ class LiveRoomController extends PlayerController { RxList playUrls = RxList(); /// 当前线路 - var currentUrl = -1; - var currentUrlInfo = "".obs; + var currentLineIndex = -1; + var currentLineInfo = "".obs; /// 退出倒计时 var countdown = 60.obs; @@ -68,6 +68,7 @@ class LiveRoomController extends PlayerController { showDanmakuState.value = AppSettingsController.instance.danmuEnable.value; followed.value = DBService.instance.getFollowExist("${site.id}_$roomId"); loadData(); + super.onInit(); } @@ -235,8 +236,8 @@ class LiveRoomController extends PlayerController { void getPlayUrl() async { playUrls.clear(); currentQualityInfo.value = qualites[currentQuality].quality; - currentUrlInfo.value = ""; - currentUrl = -1; + currentLineInfo.value = ""; + currentLineIndex = -1; var playUrl = await site.liveSite .getPlayUrls(detail: detail.value!, quality: qualites[currentQuality]); if (playUrl.isEmpty) { @@ -244,13 +245,22 @@ class LiveRoomController extends PlayerController { return; } playUrls.value = playUrl; - currentUrl = 0; - currentUrlInfo.value = "线路${currentUrl + 1}"; + currentLineIndex = 0; + currentLineInfo.value = "线路${currentLineIndex + 1}"; + //重置错误次数 + mediaErrorRetryCount = 0; + setPlayer(); + } + + void changePlayLine(int index) { + currentLineIndex = index; + //重置错误次数 + mediaErrorRetryCount = 0; setPlayer(); } void setPlayer() async { - currentUrlInfo.value = "线路${currentUrl + 1}"; + currentLineInfo.value = "线路${currentLineIndex + 1}"; errorMsg.value = ""; Map headers = {}; if (site.id == "bilibili") { @@ -263,33 +273,61 @@ class LiveRoomController extends PlayerController { player.open( Media( - playUrls[currentUrl], + playUrls[currentLineIndex], httpHeaders: headers, ), ); - Log.d("播放链接\r\n:${playUrls[currentUrl]}"); + Log.d("播放链接\r\n:${playUrls[currentLineIndex]}"); } @override - void mediaEnd() { + void mediaEnd() async { + if (mediaErrorRetryCount < 2) { + Log.d("播放结束,尝试第${mediaErrorRetryCount + 1}次刷新"); + if (mediaErrorRetryCount == 1) { + //延迟一秒再刷新 + await Future.delayed(const Duration(seconds: 1)); + } + mediaErrorRetryCount += 1; + //刷新一次 + setPlayer(); + return; + } + + Log.d("播放结束"); // 遍历线路,如果全部链接都断开就是直播结束了 - if (playUrls.length - 1 == currentUrl) { + if (playUrls.length - 1 == currentLineIndex) { liveStatus.value = false; } else { - currentUrl += 1; - setPlayer(); + changePlayLine(currentLineIndex + 1); + + //setPlayer(); } } + int mediaErrorRetryCount = 0; @override - void mediaError(String error) { - if (playUrls.length - 1 == currentUrl) { + void mediaError(String error) async { + if (mediaErrorRetryCount < 2) { + Log.d("播放失败,尝试第${mediaErrorRetryCount + 1}次刷新"); + if (mediaErrorRetryCount == 1) { + //延迟一秒再刷新 + await Future.delayed(const Duration(seconds: 1)); + } + mediaErrorRetryCount += 1; + //刷新一次 + setPlayer(); + return; + } + + if (playUrls.length - 1 == currentLineIndex) { errorMsg.value = "播放失败"; SmartDialog.showToast("播放失败:$error"); } else { - currentUrl += 1; - setPlayer(); + //currentLineIndex += 1; + //setPlayer(); + changePlayLine(currentLineIndex + 1); } } @@ -515,15 +553,16 @@ class LiveRoomController extends PlayerController { itemBuilder: (_, i) { return RadioListTile( value: i, - groupValue: currentUrl, + groupValue: currentLineIndex, title: Text("线路${i + 1}"), secondary: Text( playUrls[i].contains(".flv") ? "FLV" : "HLS", ), onChanged: (e) { Get.back(); - currentUrl = i; - setPlayer(); + //currentLineIndex = i; + //setPlayer(); + changePlayLine(i); }, ); }, diff --git a/simple_live_app/lib/modules/live_room/player/player_controls.dart b/simple_live_app/lib/modules/live_room/player/player_controls.dart index ffffb1b7..3fc78636 100644 --- a/simple_live_app/lib/modules/live_room/player/player_controls.dart +++ b/simple_live_app/lib/modules/live_room/player/player_controls.dart @@ -225,7 +225,7 @@ Widget buildFullControls( showLinesInfo(controller); }, child: Text( - controller.currentUrlInfo.value, + controller.currentLineInfo.value, style: const TextStyle(color: Colors.white, fontSize: 15), ), ), @@ -439,7 +439,7 @@ Widget buildControls( controller.showPlayUrlsSheet(); }, child: Text( - controller.currentUrlInfo.value, + controller.currentLineInfo.value, style: const TextStyle(color: Colors.white, fontSize: 15), ), ), @@ -511,7 +511,7 @@ void showLinesInfo(LiveRoomController controller) { itemCount: controller.playUrls.length, itemBuilder: (_, i) { return ListTile( - selected: controller.currentUrl == i, + selected: controller.currentLineIndex == i, title: Text.rich( TextSpan( text: "线路${i + 1}", @@ -540,8 +540,9 @@ void showLinesInfo(LiveRoomController controller) { minLeadingWidth: 16, onTap: () { Utils.hideRightDialog(); - controller.currentUrl = i; - controller.setPlayer(); + //controller.currentLineIndex = i; + //controller.setPlayer(); + controller.changePlayLine(i); }, ); }, From e6f9f6e19344f7c2bd88ce96782a23edf488cbda Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Mon, 11 Sep 2023 09:40:51 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E5=8C=85=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/src/common/custom_interceptor.dart | 4 ++-- .../lib/src/common/http_client.dart | 23 +++++++++++-------- simple_live_core/pubspec.yaml | 8 +++---- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/simple_live_core/lib/src/common/custom_interceptor.dart b/simple_live_core/lib/src/common/custom_interceptor.dart index 4d413f9f..5b860577 100644 --- a/simple_live_core/lib/src/common/custom_interceptor.dart +++ b/simple_live_core/lib/src/common/custom_interceptor.dart @@ -11,7 +11,7 @@ class CustomInterceptor extends Interceptor { } @override - void onError(DioError err, ErrorInterceptorHandler handler) { + void onError(DioException err, ErrorInterceptorHandler handler) { var time = DateTime.now().millisecondsSinceEpoch - err.requestOptions.extra["ts"]; CoreLog.e('''【HTTP请求错误-${err.type}】 耗时:${time}ms @@ -24,7 +24,7 @@ Request Query:${err.requestOptions.queryParameters} Request Data:${err.requestOptions.data} Request Headers:${err.requestOptions.headers} Response Headers:${err.response?.headers.map} -Response Data:${err.response?.data}''', err.stackTrace ?? StackTrace.current); +Response Data:${err.response?.data}''', err.stackTrace); super.onError(err, handler); } diff --git a/simple_live_core/lib/src/common/http_client.dart b/simple_live_core/lib/src/common/http_client.dart index 8dd45c06..1fd7b925 100644 --- a/simple_live_core/lib/src/common/http_client.dart +++ b/simple_live_core/lib/src/common/http_client.dart @@ -15,9 +15,9 @@ class HttpClient { HttpClient() { dio = Dio( BaseOptions( - connectTimeout: 20 * 1000, - receiveTimeout: 20 * 1000, - sendTimeout: 20 * 1000, + connectTimeout: Duration(seconds: 20), + receiveTimeout: Duration(seconds: 20), + sendTimeout: Duration(seconds: 20), ), ); dio.interceptors.add(CustomInterceptor()); @@ -47,8 +47,9 @@ class HttpClient { ); return result.data; } catch (e) { - if (e is DioError && e.type == DioErrorType.response) { - throw CoreError(e.message, statusCode: e.response?.statusCode ?? 0); + if (e is DioException && e.type == DioExceptionType.badResponse) { + throw CoreError(e.message ?? "", + statusCode: e.response?.statusCode ?? 0); } else { throw CoreError("发送GET请求失败"); } @@ -79,8 +80,9 @@ class HttpClient { ); return result.data; } catch (e) { - if (e is DioError && e.type == DioErrorType.response) { - throw CoreError(e.message, statusCode: e.response?.statusCode ?? 0); + if (e is DioException && e.type == DioExceptionType.badResponse) { + throw CoreError(e.message ?? "", + statusCode: e.response?.statusCode ?? 0); } else { throw CoreError("发送GET请求失败"); } @@ -118,8 +120,9 @@ class HttpClient { ); return result.data; } catch (e) { - if (e is DioError && e.type == DioErrorType.response) { - throw CoreError(e.message, statusCode: e.response?.statusCode ?? 0); + if (e is DioException && e.type == DioExceptionType.badResponse) { + throw CoreError(e.message ?? "", + statusCode: e.response?.statusCode ?? 0); } else { throw CoreError("发送POST请求失败"); } @@ -150,7 +153,7 @@ class HttpClient { ); return result; } catch (e) { - if (e is DioError && e.type == DioErrorType.response) { + if (e is DioException && e.type == DioExceptionType.badResponse) { //throw CoreError(e.message, statusCode: e.response?.statusCode ?? 0); return e.response!; } else { diff --git a/simple_live_core/pubspec.yaml b/simple_live_core/pubspec.yaml index 31e6dc52..48b35557 100644 --- a/simple_live_core/pubspec.yaml +++ b/simple_live_core/pubspec.yaml @@ -7,12 +7,12 @@ environment: sdk: '>=2.19.1 <3.0.0' dependencies: - dio: 4.0.6 + dio: ^5.3.2 logger: ^1.1.0 - web_socket_channel: ^2.3.0 + web_socket_channel: ^2.4.0 html_unescape: ^2.0.0 - protobuf: ^3.0.0 - crypto: ^3.0.2 + protobuf: ^3.1.0 + crypto: ^3.0.3 brotli: ^0.6.0 dart_tars_protocol: git: https://github.com/xiaoyaocz/dart_tars_protocol.git From bc2af1098a51d337285bc93c736f67c1d4b96818 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 09:27:57 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8A=96=E9=9F=B3?= =?UTF-8?q?=E7=9B=B4=E6=92=AD=E9=97=AE=E9=A2=98=20#121?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple_live_app/pubspec.yaml | 2 +- simple_live_core/.gitignore | 2 ++ .../example/simple_live_core_example.dart | 10 +++++--- .../lib/src/danmaku/bilibili_danmaku.dart | 9 +++++++ .../lib/src/danmaku/douyin_danmaku.dart | 10 ++++++++ .../lib/src/danmaku/huya_danmaku.dart | 8 ++++++ simple_live_core/lib/src/douyin_site.dart | 25 +++++++++++++------ .../lib/src/model/live_room_detail.dart | 2 +- 8 files changed, 54 insertions(+), 14 deletions(-) diff --git a/simple_live_app/pubspec.yaml b/simple_live_app/pubspec.yaml index 2ad59e83..df6ed3e3 100644 --- a/simple_live_app/pubspec.yaml +++ b/simple_live_app/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: # 框架、工具 get: 4.6.5 #状态管理、路由管理、国际化 - dio: 4.0.6 #网络请求 + dio: ^5.3.2 #网络请求 hive: 2.2.3 #持久化存储 hive_flutter: 1.1.0 #持久化存储 logger: 1.1.0 #日志 diff --git a/simple_live_core/.gitignore b/simple_live_core/.gitignore index 3cceda55..8b315161 100644 --- a/simple_live_core/.gitignore +++ b/simple_live_core/.gitignore @@ -5,3 +5,5 @@ # Avoid committing pubspec.lock for library packages; see # https://dart.dev/guides/libraries/private-files#pubspeclock. pubspec.lock + +.fvm/ \ No newline at end of file diff --git a/simple_live_core/example/simple_live_core_example.dart b/simple_live_core/example/simple_live_core_example.dart index 74f60edb..fcae195c 100644 --- a/simple_live_core/example/simple_live_core_example.dart +++ b/simple_live_core/example/simple_live_core_example.dart @@ -2,7 +2,7 @@ import 'package:simple_live_core/simple_live_core.dart'; void main() async { CoreLog.enableLog = true; - LiveSite site = DouyuSite(); + LiveSite site = DouyinSite(); var danmaku = site.getDanmaku(); danmaku.onMessage = (event) { if (event.type == LiveMessageType.chat) { @@ -17,16 +17,18 @@ void main() async { danmaku.onClose = (event) { print(event); }; - var detail = await site.getRoomDetail(roomId: "138243"); + var categores = await site.getCategores(); + print(categores.length); + //var detail = await site.getRoomDetail(roomId: "639709145929"); // var playQualites = await site.getPlayQualites(detail: detail); // var playUrls = // await site.getPlayUrls(detail: detail, quality: playQualites.first); // for (var element in playUrls) { // print(element); // } - print(detail); + //print(detail); - danmaku.start(detail.danmakuData); + //danmaku.start(detail.danmakuData); await Future.wait({}); } diff --git a/simple_live_core/lib/src/danmaku/bilibili_danmaku.dart b/simple_live_core/lib/src/danmaku/bilibili_danmaku.dart index dfeedec6..4934d501 100644 --- a/simple_live_core/lib/src/danmaku/bilibili_danmaku.dart +++ b/simple_live_core/lib/src/danmaku/bilibili_danmaku.dart @@ -21,6 +21,15 @@ class BiliBiliDanmakuArgs { required this.serverHost, required this.buvid, }); + @override + String toString() { + return json.encode({ + "roomId": roomId, + "token": token, + "serverHost": serverHost, + "buvid": buvid, + }); + } } class BiliBiliDanmaku implements LiveDanmaku { diff --git a/simple_live_core/lib/src/danmaku/douyin_danmaku.dart b/simple_live_core/lib/src/danmaku/douyin_danmaku.dart index 66be4ad1..a227c233 100644 --- a/simple_live_core/lib/src/danmaku/douyin_danmaku.dart +++ b/simple_live_core/lib/src/danmaku/douyin_danmaku.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:simple_live_core/simple_live_core.dart'; @@ -17,6 +18,15 @@ class DouyinDanmakuArgs { required this.userId, required this.cookie, }); + @override + String toString() { + return json.encode({ + "webRid": webRid, + "roomId": roomId, + "userId": userId, + "cookie": cookie, + }); + } } class DouyinDanmaku implements LiveDanmaku { diff --git a/simple_live_core/lib/src/danmaku/huya_danmaku.dart b/simple_live_core/lib/src/danmaku/huya_danmaku.dart index 3017d835..d6b18b31 100644 --- a/simple_live_core/lib/src/danmaku/huya_danmaku.dart +++ b/simple_live_core/lib/src/danmaku/huya_danmaku.dart @@ -19,6 +19,14 @@ class HuyaDanmakuArgs { required this.topSid, required this.subSid, }); + @override + String toString() { + return json.encode({ + "ayyuid": ayyuid, + "topSid": topSid, + "subSid": subSid, + }); + } } class HuyaDanmaku implements LiveDanmaku { diff --git a/simple_live_core/lib/src/douyin_site.dart b/simple_live_core/lib/src/douyin_site.dart index c11b0fe3..44be5529 100644 --- a/simple_live_core/lib/src/douyin_site.dart +++ b/simple_live_core/lib/src/douyin_site.dart @@ -56,11 +56,16 @@ class DouyinSite implements LiveSite { }, ); - var renderData = RegExp(r'9:\[\\"\$\\",\\"\$L13\\",null,(.*?)\]\\n') - .firstMatch(result) - ?.group(1) ?? - ""; - var renderDataJson = json.decode(renderData.trim().replaceAll("\\", "")); + var renderData = + RegExp(r'\{\\"pathname\\":\\"\/hot_live\\",\\"categoryData.*?\]\\n') + .firstMatch(result) + ?.group(0) ?? + ""; + var renderDataJson = json.decode(renderData + .trim() + .replaceAll('\\"', '"') + .replaceAll(r"\\", r"\") + .replaceAll(']\\n', "")); for (var item in renderDataJson["categoryData"]) { List subs = []; @@ -238,11 +243,15 @@ class DouyinSite implements LiveSite { }, ); - var renderData = RegExp(r'c:\[\\"\$\\",\\"\$L13\\",null,(.*?)\]\\n') + var renderData = RegExp(r'\{\\"state\\":\{\\"isLiveModal.*?\]\\n') .firstMatch(result) - ?.group(1) ?? + ?.group(0) ?? ""; - var str = renderData.trim().replaceAll('\\"', '"').replaceAll(r"\\", r"\"); + var str = renderData + .trim() + .replaceAll('\\"', '"') + .replaceAll(r"\\", r"\") + .replaceAll(']\\n', ""); var renderDataJson = json.decode(str); return renderDataJson["state"]; diff --git a/simple_live_core/lib/src/model/live_room_detail.dart b/simple_live_core/lib/src/model/live_room_detail.dart index 05e6587e..65226d19 100644 --- a/simple_live_core/lib/src/model/live_room_detail.dart +++ b/simple_live_core/lib/src/model/live_room_detail.dart @@ -68,7 +68,7 @@ class LiveRoomDetail { "notice": notice, "status": status, "data": data, - "danmakuData": danmakuData, + "danmakuData": danmakuData.toString(), "url": url, "isRecord": isRecord, }); From 570bda5affc360d93d01a3672875861968da3331 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 10:42:23 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E8=87=B3=E5=8E=9FAPP=E4=B8=AD=E6=89=93=E5=BC=80=E7=9B=B4?= =?UTF-8?q?=E6=92=AD=E9=97=B4=20#118?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_room/live_room_controller.dart | 30 +++++++++++++++++++ .../lib/modules/live_room/live_room_page.dart | 9 ++++++ simple_live_core/lib/src/douyu_site.dart | 4 +-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/simple_live_app/lib/modules/live_room/live_room_controller.dart b/simple_live_app/lib/modules/live_room/live_room_controller.dart index 485aae6a..df349461 100644 --- a/simple_live_app/lib/modules/live_room/live_room_controller.dart +++ b/simple_live_app/lib/modules/live_room/live_room_controller.dart @@ -19,6 +19,7 @@ import 'package:simple_live_app/models/db/history.dart'; import 'package:simple_live_app/modules/live_room/player/player_controller.dart'; import 'package:simple_live_app/services/db_service.dart'; import 'package:simple_live_core/simple_live_core.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; class LiveRoomController extends PlayerController { @@ -628,6 +629,35 @@ class LiveRoomController extends PlayerController { ); } + void openNaviteAPP() async { + var naviteUrl = ""; + var webUrl = ""; + if (site.id == "bilibili") { + naviteUrl = "bilibili://live/${detail.value?.roomId}"; + webUrl = "https://live.bilibili.com/${detail.value?.roomId}"; + } else if (site.id == "douyin") { + var args = detail.value?.danmakuData as DouyinDanmakuArgs; + naviteUrl = "snssdk1128://webcast_room?room_id=${args.roomId}"; + webUrl = "https://www.douyu.com/${args.webRid}"; + } else if (site.id == "huya") { + var args = detail.value?.danmakuData as HuyaDanmakuArgs; + naviteUrl = + "yykiwi://homepage/index.html?banneraction=https%3A%2F%2Fdiy-front.cdn.huya.com%2Fzt%2Ffrontpage%2Fcc%2Fupdate.html%3Fhyaction%3Dlive%26channelid%3D${args.subSid}%26subid%3D${args.subSid}%26liveuid%3D${args.subSid}%26screentype%3D1%26sourcetype%3D0%26fromapp%3Dhuya_wap%252Fclick%252Fopen_app_guide%26&fromapp=huya_wap/click/open_app_guide"; + webUrl = "https://www.huya.com/${detail.value?.roomId}"; + } else if (site.id == "douyu") { + naviteUrl = + "douyulink://?type=90001&schemeUrl=douyuapp%3A%2F%2Froom%3FliveType%3D0%26rid%3D${detail.value?.roomId}"; + webUrl = "https://www.douyu.com/${detail.value?.roomId}"; + } + try { + launchUrlString(naviteUrl, mode: LaunchMode.externalApplication); + } catch (e) { + Log.logPrint(e); + SmartDialog.showToast("无法打开APP,将使用浏览器打开"); + launchUrlString(webUrl, mode: LaunchMode.externalApplication); + } + } + @override void onClose() { autoExitTimer?.cancel(); diff --git a/simple_live_app/lib/modules/live_room/live_room_page.dart b/simple_live_app/lib/modules/live_room/live_room_page.dart index b6d10bd4..02fc9bd1 100644 --- a/simple_live_app/lib/modules/live_room/live_room_page.dart +++ b/simple_live_app/lib/modules/live_room/live_room_page.dart @@ -575,6 +575,15 @@ class LiveRoomPage extends GetView { controller.share(); }, ), + ListTile( + leading: const Icon(Icons.open_in_new), + title: const Text("APP中打开"), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Get.back(); + controller.openNaviteAPP(); + }, + ), ], ), ), diff --git a/simple_live_core/lib/src/douyu_site.dart b/simple_live_core/lib/src/douyu_site.dart index d2a73eaf..2ead73c0 100644 --- a/simple_live_core/lib/src/douyu_site.dart +++ b/simple_live_core/lib/src/douyu_site.dart @@ -208,11 +208,11 @@ class DouyuSite implements LiveSite { userAvatar: roomInfo["owner_avatar"].toString(), introduction: roomInfo["show_details"].toString(), notice: "", - status: roomInfo["rst"] != 3 && roomInfo["videoLoop"] != 1, + status: roomInfo["show_status"] == 1 && roomInfo["videoLoop"] != 1, danmakuData: roomInfo["room_id"].toString(), data: await getPlayArgs(crptext, roomInfo["room_id"].toString()), url: "https://www.douyu.com/$roomId", - isRecord: roomInfo["rst"] == 3, + isRecord: roomInfo["videoLoop"] == 1, ); } From e552b19b71a68f3850fcca74ca96c21d909b9d91 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 11:43:56 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9B=B4=E6=92=AD?= =?UTF-8?q?=E9=97=B4=E5=86=85=E8=AE=BE=E7=BD=AE=E5=AE=9A=E6=97=B6=E5=85=B3?= =?UTF-8?q?=E9=97=AD=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_room/live_room_controller.dart | 107 ++++++++++++++++-- .../lib/modules/live_room/live_room_page.dart | 59 +++++++--- 2 files changed, 139 insertions(+), 27 deletions(-) diff --git a/simple_live_app/lib/modules/live_room/live_room_controller.dart b/simple_live_app/lib/modules/live_room/live_room_controller.dart index df349461..53b710df 100644 --- a/simple_live_app/lib/modules/live_room/live_room_controller.dart +++ b/simple_live_app/lib/modules/live_room/live_room_controller.dart @@ -63,6 +63,12 @@ class LiveRoomController extends PlayerController { Timer? autoExitTimer; + /// 设置的自动关闭时间(分钟) + var autoExitMinutes = 60.obs; + + /// 是否启用自动关闭 + var autoExitEnable = false.obs; + @override void onInit() { initAutoExit(); @@ -76,17 +82,28 @@ class LiveRoomController extends PlayerController { /// 初始化自动关闭倒计时 void initAutoExit() { if (AppSettingsController.instance.autoExitEnable.value) { - countdown.value = - AppSettingsController.instance.autoExitDuration.value * 60; - autoExitTimer = Timer.periodic(const Duration(seconds: 1), (timer) async { - countdown.value -= 1; - if (countdown.value <= 0) { - timer.cancel(); - await WakelockPlus.disable(); - exit(0); - } - }); + autoExitEnable.value = true; + autoExitMinutes.value = + AppSettingsController.instance.autoExitDuration.value; + setAutoExit(); + } + } + + void setAutoExit() { + if (!autoExitEnable.value) { + autoExitTimer?.cancel(); + return; } + autoExitTimer?.cancel(); + countdown.value = autoExitMinutes.value * 60; + autoExitTimer = Timer.periodic(const Duration(seconds: 1), (timer) async { + countdown.value -= 1; + if (countdown.value <= 0) { + timer.cancel(); + await WakelockPlus.disable(); + exit(0); + } + }); } void refreshRoom() { @@ -629,6 +646,76 @@ class LiveRoomController extends PlayerController { ); } + void showAutoExitSheet() { + if (AppSettingsController.instance.autoExitEnable.value) { + SmartDialog.showToast("已设置了全局定时关闭"); + return; + } + Utils.showBottomSheet( + title: "定时关闭", + child: ListView( + children: [ + Obx( + () => SwitchListTile( + title: Text( + "启用定时关闭", + style: Get.textTheme.titleMedium, + ), + value: autoExitEnable.value, + onChanged: (e) { + autoExitEnable.value = e; + + setAutoExit(); + //controller.setAutoExitEnable(e); + }, + ), + ), + Obx( + () => ListTile( + enabled: autoExitEnable.value, + title: Text( + "自动关闭时间:${autoExitMinutes.value ~/ 60}小时${autoExitMinutes.value % 60}分钟", + style: Get.textTheme.titleMedium, + ), + trailing: const Icon(Icons.chevron_right), + onTap: () async { + var value = await showTimePicker( + context: Get.context!, + initialTime: TimeOfDay( + hour: autoExitMinutes.value ~/ 60, + minute: autoExitMinutes.value % 60, + ), + initialEntryMode: TimePickerEntryMode.inputOnly, + builder: (_, child) { + return Theme( + data: Get.theme.copyWith( + useMaterial3: false, + ), + child: MediaQuery( + data: Get.mediaQuery.copyWith( + alwaysUse24HourFormat: true, + ), + child: child!, + ), + ); + }, + ); + if (value == null || (value.hour == 0 && value.minute == 0)) { + return; + } + var duration = + Duration(hours: value.hour, minutes: value.minute); + autoExitMinutes.value = duration.inMinutes; + //setAutoExitDuration(duration.inMinutes); + setAutoExit(); + }, + ), + ), + ], + ), + ); + } + void openNaviteAPP() async { var naviteUrl = ""; var webUrl = ""; diff --git a/simple_live_app/lib/modules/live_room/live_room_page.dart b/simple_live_app/lib/modules/live_room/live_room_page.dart index 02fc9bd1..d6615ca3 100644 --- a/simple_live_app/lib/modules/live_room/live_room_page.dart +++ b/simple_live_app/lib/modules/live_room/live_room_page.dart @@ -7,6 +7,7 @@ import 'package:simple_live_app/app/controller/app_settings_controller.dart'; import 'package:simple_live_app/app/utils.dart'; import 'package:simple_live_app/modules/live_room/live_room_controller.dart'; import 'package:simple_live_app/modules/live_room/player/player_controls.dart'; +import 'package:simple_live_app/widgets/keep_alive_wrapper.dart'; import 'package:simple_live_app/widgets/net_image.dart'; import 'package:simple_live_app/widgets/superchat_card.dart'; import 'package:simple_live_core/simple_live_core.dart'; @@ -383,22 +384,7 @@ class LiveRoomPage extends GetView { }, ), ), - Obx( - () => ListView.separated( - padding: AppStyle.edgeInsetsA12, - itemCount: controller.superChats.length, - separatorBuilder: (_, i) => AppStyle.vGap12, - itemBuilder: (_, i) { - var item = controller.superChats[i]; - return SuperChatCard( - item, - onExpire: () { - controller.removeSuperChats(); - }, - ); - }, - ), - ), + buildSuperChats(), buildSettings(), ], ), @@ -452,11 +438,41 @@ class LiveRoomPage extends GetView { ); } + Widget buildSuperChats() { + return KeepAliveWrapper( + child: Obx( + () => ListView.separated( + padding: AppStyle.edgeInsetsA12, + itemCount: controller.superChats.length, + separatorBuilder: (_, i) => AppStyle.vGap12, + itemBuilder: (_, i) { + var item = controller.superChats[i]; + return SuperChatCard( + item, + onExpire: () { + controller.removeSuperChats(); + }, + ); + }, + ), + ), + ); + } + Widget buildSettings() { return Obx( () => ListView( padding: AppStyle.edgeInsetsA12, children: [ + Obx( + () => Visibility( + visible: controller.autoExitEnable.value, + child: ListTile( + leading: const Icon(Icons.timer_outlined), + title: Text("${controller.countdown.value}秒后自动关闭"), + ), + ), + ), Padding( padding: AppStyle.edgeInsetsH12.copyWith(top: 12), child: Text( @@ -566,9 +582,18 @@ class LiveRoomPage extends GetView { controller.saveScreenshot(); }, ), + ListTile( + leading: const Icon(Icons.timer_outlined), + title: const Text("定时关闭"), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Get.back(); + controller.showAutoExitSheet(); + }, + ), ListTile( leading: const Icon(Icons.share_sharp), - title: const Text("分享"), + title: const Text("分享链接"), trailing: const Icon(Icons.chevron_right), onTap: () { Get.back(); From 43da6c66e7e3ed92e6da13f6799d877c91275c10 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 15:40:42 +0800 Subject: [PATCH 07/10] =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E4=B8=8E=E5=B9=B3=E5=8F=B0=E8=87=AA=E5=AE=9A=E4=B9=89=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=20#16,#76?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple_live_app/lib/app/constant.dart | 37 +++++++++ .../controller/app_settings_controller.dart | 62 ++++++++++++++ simple_live_app/lib/app/sites.dart | 19 +++-- .../modules/indexed/indexed_controller.dart | 28 +++++-- .../lib/modules/indexed/indexed_page.dart | 63 ++------------- .../modules/toolbox/toolbox_controller.dart | 2 +- .../indexed_settings_controller.dart | 33 ++++++++ .../indexed_settings_page.dart | 80 +++++++++++++++++++ .../lib/modules/user/user_page.dart | 11 +++ simple_live_app/lib/routes/app_pages.dart | 13 ++- simple_live_app/lib/routes/route_path.dart | 3 + .../lib/services/local_storage_service.dart | 6 ++ 12 files changed, 286 insertions(+), 71 deletions(-) create mode 100644 simple_live_app/lib/modules/user/indexed_settings/indexed_settings_controller.dart create mode 100644 simple_live_app/lib/modules/user/indexed_settings/indexed_settings_page.dart diff --git a/simple_live_app/lib/app/constant.dart b/simple_live_app/lib/app/constant.dart index d0185f99..b177189a 100644 --- a/simple_live_app/lib/app/constant.dart +++ b/simple_live_app/lib/app/constant.dart @@ -1,3 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:remixicon/remixicon.dart'; + class Constant { static const String kUpdateFollow = "UpdateFollow"; + + static final Map allHomePages = { + "recommend": HomePageItem( + iconData: Remix.home_smile_line, + title: "首页", + index: 0, + ), + "follow": HomePageItem( + iconData: Remix.heart_line, + title: "关注", + index: 1, + ), + "category": HomePageItem( + iconData: Remix.apps_line, + title: "分类", + index: 2, + ), + "user": HomePageItem( + iconData: Remix.user_smile_line, + title: "我的", + index: 3, + ), + }; +} + +class HomePageItem { + final IconData iconData; + final String title; + final int index; + HomePageItem({ + required this.iconData, + required this.title, + required this.index, + }); } diff --git a/simple_live_app/lib/app/controller/app_settings_controller.dart b/simple_live_app/lib/app/controller/app_settings_controller.dart index cd54548e..26f0e932 100644 --- a/simple_live_app/lib/app/controller/app_settings_controller.dart +++ b/simple_live_app/lib/app/controller/app_settings_controller.dart @@ -1,3 +1,5 @@ +import 'package:simple_live_app/app/constant.dart'; +import 'package:simple_live_app/app/sites.dart'; import 'package:simple_live_app/services/local_storage_service.dart'; import 'package:flutter/material.dart'; @@ -63,9 +65,51 @@ class AppSettingsController extends GetxController { 0, ); + initSiteSort(); + initHomeSort(); super.onInit(); } + void initSiteSort() { + var sort = LocalStorageService.instance + .getValue( + LocalStorageService.kSiteSort, + Sites.allSites.keys.join(","), + ) + .split(","); + //如果数量与allSites的数量不一致,将缺失的添加上 + if (sort.length != Sites.allSites.length) { + var keys = Sites.allSites.keys.toList(); + for (var i = 0; i < keys.length; i++) { + if (!sort.contains(keys[i])) { + sort.add(keys[i]); + } + } + } + + siteSort.value = sort; + } + + void initHomeSort() { + var sort = LocalStorageService.instance + .getValue( + LocalStorageService.kHomeSort, + Constant.allHomePages.keys.join(","), + ) + .split(","); + //如果数量与allSites的数量不一致,将缺失的添加上 + if (sort.length != Constant.allHomePages.length) { + var keys = Constant.allHomePages.keys.toList(); + for (var i = 0; i < keys.length; i++) { + if (!sort.contains(keys[i])) { + sort.add(keys[i]); + } + } + } + + homeSort.value = sort; + } + void setNoFirstRun() { LocalStorageService.instance.setValue(LocalStorageService.kFirstRun, false); } @@ -224,4 +268,22 @@ class AppSettingsController extends GetxController { value, ); } + + RxList siteSort = RxList(); + void setSiteSort(List e) { + siteSort.value = e; + LocalStorageService.instance.setValue( + LocalStorageService.kSiteSort, + siteSort.join(","), + ); + } + + RxList homeSort = RxList(); + void setHomeSort(List e) { + homeSort.value = e; + LocalStorageService.instance.setValue( + LocalStorageService.kHomeSort, + homeSort.join(","), + ); + } } diff --git a/simple_live_app/lib/app/sites.dart b/simple_live_app/lib/app/sites.dart index b058a0ec..938a44f6 100644 --- a/simple_live_app/lib/app/sites.dart +++ b/simple_live_app/lib/app/sites.dart @@ -1,32 +1,39 @@ +import 'package:simple_live_app/app/controller/app_settings_controller.dart'; import 'package:simple_live_core/simple_live_core.dart'; class Sites { - static List supportSites = [ - Site( + static final Map allSites = { + "bilibili": Site( id: "bilibili", logo: "assets/images/bilibili_2.png", name: "哔哩哔哩", liveSite: BiliBiliSite(), ), - Site( + "douyu": Site( id: "douyu", logo: "assets/images/douyu.png", name: "斗鱼直播", liveSite: DouyuSite(), ), - Site( + "huya": Site( id: "huya", logo: "assets/images/huya.png", name: "虎牙直播", liveSite: HuyaSite(), ), - Site( + "douyin": Site( id: "douyin", logo: "assets/images/douyin.png", name: "抖音直播", liveSite: DouyinSite(), ), - ]; + }; + + static List get supportSites { + return AppSettingsController.instance.siteSort + .map((key) => allSites[key]!) + .toList(); + } } class Site { diff --git a/simple_live_app/lib/modules/indexed/indexed_controller.dart b/simple_live_app/lib/modules/indexed/indexed_controller.dart index 73ced7ae..db0f6810 100644 --- a/simple_live_app/lib/modules/indexed/indexed_controller.dart +++ b/simple_live_app/lib/modules/indexed/indexed_controller.dart @@ -1,28 +1,36 @@ import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; +import 'package:simple_live_app/app/constant.dart'; import 'package:simple_live_app/app/controller/app_settings_controller.dart'; import 'package:simple_live_app/app/event_bus.dart'; import 'package:simple_live_app/app/utils.dart'; import 'package:simple_live_app/modules/category/category_controller.dart'; import 'package:simple_live_app/modules/category/category_page.dart'; +import 'package:simple_live_app/modules/home/home_controller.dart'; import 'package:simple_live_app/modules/home/home_page.dart'; import 'package:simple_live_app/modules/user/follow_user/follow_user_controller.dart'; import 'package:simple_live_app/modules/user/follow_user/follow_user_page.dart'; import 'package:simple_live_app/modules/user/user_page.dart'; class IndexedController extends GetxController { + RxList items = RxList([]); + var index = 0.obs; RxList pages = RxList([ - const HomePage(), + const SizedBox(), const SizedBox(), const SizedBox(), const SizedBox(), ]); - void setIndex(i) { + void setIndex(int i) { if (pages[i] is SizedBox) { - switch (i) { + switch (items[i].index) { + case 0: + Get.put(HomeController()); + pages[i] = const HomePage(); + break; case 1: Get.put(FollowUserController()); pages[i] = const FollowUserPage(); @@ -31,22 +39,28 @@ class IndexedController extends GetxController { Get.put(CategoryController()); pages[i] = const CategoryPage(); break; - case 3: pages[i] = const UserPage(); break; default: } + } else { + if (index.value == i) { + EventBus.instance + .emit(EventBus.kBottomNavigationBarClicked, items[i].index); + } } - if (index.value == i) { - EventBus.instance.emit(EventBus.kBottomNavigationBarClicked, i); - } + index.value = i; } @override void onInit() { Future.delayed(Duration.zero, showFirstRun); + items.value = AppSettingsController.instance.homeSort + .map((key) => Constant.allHomePages[key]!) + .toList(); + setIndex(0); super.onInit(); } diff --git a/simple_live_app/lib/modules/indexed/indexed_page.dart b/simple_live_app/lib/modules/indexed/indexed_page.dart index 6535713a..675ff6c3 100644 --- a/simple_live_app/lib/modules/indexed/indexed_page.dart +++ b/simple_live_app/lib/modules/indexed/indexed_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:remixicon/remixicon.dart'; import 'indexed_controller.dart'; @@ -22,62 +21,16 @@ class IndexedPage extends GetView { onDestinationSelected: controller.setIndex, height: 56, labelBehavior: NavigationDestinationLabelBehavior.alwaysHide, - destinations: const [ - NavigationDestination( - icon: Icon(Remix.home_smile_line), - label: "首页", - ), - NavigationDestination( - icon: Icon(Remix.heart_line), - label: "关注", - ), - NavigationDestination( - icon: Icon(Remix.apps_line), - label: "分类", - ), - NavigationDestination( - icon: Icon(Remix.user_smile_line), - label: "我的", - ), - ], + destinations: controller.items + .map( + (item) => NavigationDestination( + icon: Icon(item.iconData), + label: item.title, + ), + ) + .toList(), ), ), - // bottomNavigationBar: Obx( - // () => BottomNavigationBar( - // currentIndex: controller.index.value, - // onTap: controller.setIndex, - // selectedFontSize: 12, - // unselectedFontSize: 12, - // iconSize: 24, - // type: BottomNavigationBarType.fixed, - // showSelectedLabels: true, - // showUnselectedLabels: true, - // elevation: 4, - // landscapeLayout: BottomNavigationBarLandscapeLayout.centered, - // items: const [ - // BottomNavigationBarItem( - // icon: Icon(Remix.home_smile_line), - // activeIcon: Icon(Remix.home_smile_fill), - // label: "首页", - // ), - // BottomNavigationBarItem( - // icon: Icon(Remix.apps_line), - // activeIcon: Icon(Remix.apps_fill), - // label: "分类", - // ), - // BottomNavigationBarItem( - // icon: Icon(Remix.tools_line), - // activeIcon: Icon(Remix.tools_fill), - // label: "工具箱", - // ), - // BottomNavigationBarItem( - // icon: Icon(Remix.user_smile_line), - // activeIcon: Icon(Remix.user_smile_fill), - // label: "我的", - // ), - // ], - // ), - // ), ); } } diff --git a/simple_live_app/lib/modules/toolbox/toolbox_controller.dart b/simple_live_app/lib/modules/toolbox/toolbox_controller.dart index d1f97a92..83cd9b77 100644 --- a/simple_live_app/lib/modules/toolbox/toolbox_controller.dart +++ b/simple_live_app/lib/modules/toolbox/toolbox_controller.dart @@ -144,7 +144,7 @@ class ToolBoxController extends GetxController { followRedirects: false, ), ); - } on DioError catch (e) { + } on DioException catch (e) { if (e.response!.statusCode == 302) { var redirectUrl = e.response!.headers.value("Location"); if (redirectUrl != null) { diff --git a/simple_live_app/lib/modules/user/indexed_settings/indexed_settings_controller.dart b/simple_live_app/lib/modules/user/indexed_settings/indexed_settings_controller.dart new file mode 100644 index 00000000..a4d399c9 --- /dev/null +++ b/simple_live_app/lib/modules/user/indexed_settings/indexed_settings_controller.dart @@ -0,0 +1,33 @@ +import 'package:get/get.dart'; +import 'package:simple_live_app/app/controller/app_settings_controller.dart'; + +class IndexedSettingsController extends GetxController { + RxList siteSort = RxList(); + RxList homeSort = RxList(); + @override + void onInit() { + siteSort = AppSettingsController.instance.siteSort; + homeSort = AppSettingsController.instance.homeSort; + super.onInit(); + } + + void updateSiteSort(int oldIndex, int newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + final String item = siteSort.removeAt(oldIndex); + siteSort.insert(newIndex, item); + // ignore: invalid_use_of_protected_member + AppSettingsController.instance.setSiteSort(siteSort.value); + } + + void updateHomeSort(int oldIndex, int newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + final String item = homeSort.removeAt(oldIndex); + homeSort.insert(newIndex, item); + // ignore: invalid_use_of_protected_member + AppSettingsController.instance.setHomeSort(homeSort.value); + } +} diff --git a/simple_live_app/lib/modules/user/indexed_settings/indexed_settings_page.dart b/simple_live_app/lib/modules/user/indexed_settings/indexed_settings_page.dart new file mode 100644 index 00000000..e5fdb81a --- /dev/null +++ b/simple_live_app/lib/modules/user/indexed_settings/indexed_settings_page.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:simple_live_app/app/constant.dart'; +import 'package:simple_live_app/app/sites.dart'; +import 'package:simple_live_app/modules/user/indexed_settings/indexed_settings_controller.dart'; + +class IndexedSettingsPage extends GetView { + const IndexedSettingsPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("主页设置"), + ), + body: ListView( + children: [ + ListTile( + title: Text( + "主页排序", + style: Get.textTheme.titleSmall, + ), + visualDensity: VisualDensity.compact, + subtitle: const Text("拖动进行排序,重启APP后生效"), + ), + Obx( + () => ReorderableListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + onReorder: controller.updateHomeSort, + children: controller.homeSort.map( + (key) { + var e = Constant.allHomePages[key]!; + return ListTile( + key: ValueKey(e.title), + title: Text(e.title), + visualDensity: VisualDensity.compact, + leading: Icon(e.iconData), + trailing: const Icon(Icons.drag_handle), + ); + }, + ).toList(), + ), + ), + ListTile( + title: Text( + "平台排序", + style: Get.textTheme.titleSmall, + ), + visualDensity: VisualDensity.compact, + subtitle: const Text("拖动进行排序,重启APP后生效"), + ), + Obx( + () => ReorderableListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + onReorder: controller.updateSiteSort, + children: controller.siteSort.map( + (key) { + var e = Sites.allSites[key]!; + return ListTile( + key: ValueKey(e.id), + visualDensity: VisualDensity.compact, + title: Text(e.name), + leading: Image.asset( + e.logo, + width: 24, + height: 24, + ), + trailing: const Icon(Icons.drag_handle), + ); + }, + ).toList(), + ), + ), + ], + ), + ); + } +} diff --git a/simple_live_app/lib/modules/user/user_page.dart b/simple_live_app/lib/modules/user/user_page.dart index 331e1e2a..8b156938 100644 --- a/simple_live_app/lib/modules/user/user_page.dart +++ b/simple_live_app/lib/modules/user/user_page.dart @@ -106,6 +106,17 @@ class UserPage extends StatelessWidget { ), onTap: Get.find().changeTheme, ), + ListTile( + leading: const Icon(Remix.home_2_line), + title: const Text("主页设置"), + trailing: const Icon( + Icons.chevron_right, + color: Colors.grey, + ), + onTap: () { + Get.toNamed(RoutePath.kSettingsIndexed); + }, + ), ListTile( leading: const Icon(Remix.play_circle_line), title: const Text("播放设置"), diff --git a/simple_live_app/lib/routes/app_pages.dart b/simple_live_app/lib/routes/app_pages.dart index 3d4d65bc..ef834d62 100644 --- a/simple_live_app/lib/routes/app_pages.dart +++ b/simple_live_app/lib/routes/app_pages.dart @@ -3,7 +3,6 @@ import 'package:get/get.dart'; import 'package:simple_live_app/modules/categoty_detail/category_detail_controller.dart'; import 'package:simple_live_app/modules/categoty_detail/category_detail_page.dart'; -import 'package:simple_live_app/modules/home/home_controller.dart'; import 'package:simple_live_app/modules/indexed/indexed_controller.dart'; import 'package:simple_live_app/modules/live_room/live_room_controller.dart'; import 'package:simple_live_app/modules/live_room/live_room_page.dart'; @@ -19,6 +18,8 @@ import 'package:simple_live_app/modules/user/follow_user/follow_user_controller. import 'package:simple_live_app/modules/user/follow_user/follow_user_page.dart'; import 'package:simple_live_app/modules/user/history/history_controller.dart'; import 'package:simple_live_app/modules/user/history/history_page.dart'; +import 'package:simple_live_app/modules/user/indexed_settings/indexed_settings_controller.dart'; +import 'package:simple_live_app/modules/user/indexed_settings/indexed_settings_page.dart'; import 'package:simple_live_app/modules/user/play_settings_page.dart'; import '../modules/indexed/indexed_page.dart'; @@ -33,7 +34,7 @@ class AppPages { page: () => const IndexedPage(), bindings: [ BindingsBuilder.put(() => IndexedController()), - BindingsBuilder.put(() => HomeController()), + //BindingsBuilder.put(() => HomeController()), ], ), // 观看记录 @@ -113,5 +114,13 @@ class AppPages { BindingsBuilder.put(() => DanmuShieldController()), ], ), + //主页设置 + GetPage( + name: RoutePath.kSettingsIndexed, + page: () => const IndexedSettingsPage(), + bindings: [ + BindingsBuilder.put(() => IndexedSettingsController()), + ], + ), ]; } diff --git a/simple_live_app/lib/routes/route_path.dart b/simple_live_app/lib/routes/route_path.dart index bed04da5..805ee1fc 100644 --- a/simple_live_app/lib/routes/route_path.dart +++ b/simple_live_app/lib/routes/route_path.dart @@ -35,4 +35,7 @@ class RoutePath { /// 工具箱 static const kTools = "/other/tools"; + + /// 主页设置 + static const kSettingsIndexed = "/settings/indexed"; } diff --git a/simple_live_app/lib/services/local_storage_service.dart b/simple_live_app/lib/services/local_storage_service.dart index 701b1e98..e2a35c22 100644 --- a/simple_live_app/lib/services/local_storage_service.dart +++ b/simple_live_app/lib/services/local_storage_service.dart @@ -11,6 +11,12 @@ class LocalStorageService extends GetxService { /// 缩放模式 static const String kPlayerScaleMode = "ScaleMode"; + /// 网站排序 + static const String kSiteSort = "SiteSort"; + + /// 首页排序 + static const String kHomeSort = "HomeSort"; + /// 显示模式 /// * [0] 跟随系统 /// * [1] 浅色模式 From 9a66c61ce5d65ab155406a7b1a64a00cbb002918 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 16:22:42 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Android=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=9D=83=E9=99=90=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/app/src/main/AndroidManifest.xml | 1 + simple_live_app/lib/app/utils.dart | 33 +++++++++++++++++++ .../follow_user/follow_user_controller.dart | 25 ++++++++++---- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/simple_live_app/android/app/src/main/AndroidManifest.xml b/simple_live_app/android/app/src/main/AndroidManifest.xml index ac102b77..1fef7abd 100644 --- a/simple_live_app/android/app/src/main/AndroidManifest.xml +++ b/simple_live_app/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + checkStorgePermission() async { + try { + if (!Platform.isAndroid) { + return true; + } + Permission permission = Permission.storage; + var androidIndo = await deviceInfo.androidInfo; + if (androidIndo.version.sdkInt >= 33) { + permission = Permission.manageExternalStorage; + } + + var status = await permission.status; + if (status == PermissionStatus.granted) { + return true; + } + status = await permission.request(); + if (status.isGranted) { + return true; + } else { + SmartDialog.showToast( + "请授予文件访问权限", + ); + return false; + } + } catch (e) { + return false; + } + } + ///16进制颜色转换 static Color convertHexColor(String hexColor) { hexColor = hexColor.replaceAll("#", ""); diff --git a/simple_live_app/lib/modules/user/follow_user/follow_user_controller.dart b/simple_live_app/lib/modules/user/follow_user/follow_user_controller.dart index a4cad74a..fcf93c3e 100644 --- a/simple_live_app/lib/modules/user/follow_user/follow_user_controller.dart +++ b/simple_live_app/lib/modules/user/follow_user/follow_user_controller.dart @@ -80,6 +80,12 @@ class FollowUserController extends BasePageController { } try { + var status = await Utils.checkStorgePermission(); + if (!status) { + SmartDialog.showToast("无权限"); + return; + } + var dir = ""; if (Platform.isIOS) { dir = (await getApplicationDocumentsDirectory()).path; @@ -114,14 +120,19 @@ class FollowUserController extends BasePageController { } void inputList() async { - var file = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: ['json'], - ); - if (file == null) { - return; - } try { + var status = await Utils.checkStorgePermission(); + if (!status) { + SmartDialog.showToast("无权限"); + return; + } + var file = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['json'], + ); + if (file == null) { + return; + } var jsonFile = File(file.files.single.path!); var data = jsonDecode(await jsonFile.readAsString()); From a53d7f139887c9bcd10fe9e0d87c3ff7186036fb Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 16:33:13 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=E6=97=A0=E6=B3=95=E6=89=93=E5=BC=80APP?= =?UTF-8?q?=E6=97=B6=E6=89=93=E5=BC=80=E6=B5=8F=E8=A7=88=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/modules/live_room/live_room_controller.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/simple_live_app/lib/modules/live_room/live_room_controller.dart b/simple_live_app/lib/modules/live_room/live_room_controller.dart index 53b710df..a420b8af 100644 --- a/simple_live_app/lib/modules/live_room/live_room_controller.dart +++ b/simple_live_app/lib/modules/live_room/live_room_controller.dart @@ -737,11 +737,11 @@ class LiveRoomController extends PlayerController { webUrl = "https://www.douyu.com/${detail.value?.roomId}"; } try { - launchUrlString(naviteUrl, mode: LaunchMode.externalApplication); + await launchUrlString(naviteUrl, mode: LaunchMode.externalApplication); } catch (e) { Log.logPrint(e); SmartDialog.showToast("无法打开APP,将使用浏览器打开"); - launchUrlString(webUrl, mode: LaunchMode.externalApplication); + await launchUrlString(webUrl, mode: LaunchMode.externalApplication); } } From e8f8d69708330e500abcb3e953ea60ee70a69512 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Wed, 13 Sep 2023 16:48:55 +0800 Subject: [PATCH 10/10] release 1.3.1 --- assets/app_version.json | 6 +++--- simple_live_app/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/app_version.json b/assets/app_version.json index cecd8918..accbd3b4 100644 --- a/assets/app_version.json +++ b/assets/app_version.json @@ -1,7 +1,7 @@ { - "version": "1.3.0", - "version_num": 10300, - "version_desc": "1. 优化播放器 #78,#113\n2. 支持画面尺寸调整 #61\n3. 支持锁定播放器 #53\n4. 取消关注增加提示框 #120\n5. 修复抖音直播问题 #121\n6. 优化哔哩哔哩直播弹幕获取\n7. 播放器支持截图", + "version": "1.3.1", + "version_num": 10301, + "version_desc": "1. 支持直播间内设置定时关闭 #124\n2. 支持主页与平台自定义排序 #16,#76\n3. 支持跳转至原APP中打开直播间 #118\n4. 斗鱼增加录播判断 #6,#103\n5. 修复抖音直播问题 #121\n6. 播放失败尝试自动重连", "prerelease":false, "download_url": "https://github.com/xiaoyaocz/dart_simple_live/releases" } \ No newline at end of file diff --git a/simple_live_app/pubspec.yaml b/simple_live_app/pubspec.yaml index df6ed3e3..33819aca 100644 --- a/simple_live_app/pubspec.yaml +++ b/simple_live_app/pubspec.yaml @@ -1,5 +1,5 @@ name: simple_live_app -version: 1.3.0+10300 +version: 1.3.1+10301 publish_to: none description: "Simple Live APP" environment: