From 8c31377862d082f88eef68a5ef977a474fddb4ed Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Fri, 21 Jun 2024 21:44:34 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8A=96=E9=9F=B3?= =?UTF-8?q?=E5=BC=B9=E5=B9=95=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20=20#458?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/simple_live_core_example.dart | 21 +++++---- .../lib/src/danmaku/douyin_danmaku.dart | 34 +++++++++++--- simple_live_core/lib/src/douyin_site.dart | 45 ++++++++++++------- 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/simple_live_core/example/simple_live_core_example.dart b/simple_live_core/example/simple_live_core_example.dart index 28332974..9e6e8fd4 100644 --- a/simple_live_core/example/simple_live_core_example.dart +++ b/simple_live_core/example/simple_live_core_example.dart @@ -3,7 +3,7 @@ import 'package:simple_live_core/simple_live_core.dart'; void main() async { CoreLog.enableLog = true; CoreLog.requestLogType = RequestLogType.short; - LiveSite site = DouyuSite(); + LiveSite site = DouyinSite(); var danmaku = site.getDanmaku(); danmaku.onMessage = (event) { if (event.type == LiveMessageType.chat) { @@ -18,16 +18,19 @@ void main() async { danmaku.onClose = (event) { print(event); }; + + var search = await site.searchRooms("东方"); + //var categores = await site.getCategores(); //print(categores.length); - var detail = await site.getRoomDetail(roomId: "687423"); - var playQualites = await site.getPlayQualites(detail: detail); - print(playQualites); - var playUrls = - await site.getPlayUrls(detail: detail, quality: playQualites.first); - for (var element in playUrls) { - print(element); - } + var detail = await site.getRoomDetail(roomId: search.items.first.roomId); + // var playQualites = await site.getPlayQualites(detail: detail); + // print(playQualites); + // var playUrls = + // await site.getPlayUrls(detail: detail, quality: playQualites.first); + // for (var element in playUrls) { + // print(element); + // } //print(detail); danmaku.start(detail.danmakuData); diff --git a/simple_live_core/lib/src/danmaku/douyin_danmaku.dart b/simple_live_core/lib/src/danmaku/douyin_danmaku.dart index a227c233..c8624d84 100644 --- a/simple_live_core/lib/src/danmaku/douyin_danmaku.dart +++ b/simple_live_core/lib/src/danmaku/douyin_danmaku.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:simple_live_core/src/common/http_client.dart' as http; + import 'package:simple_live_core/simple_live_core.dart'; import 'package:simple_live_core/src/common/web_socket_util.dart'; @@ -74,23 +76,25 @@ class DouyinDanmaku implements LiveDanmaku { "browser_platform": "Win32", "browser_name": "Mozilla", "browser_version": - "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.51", + DouyinSite.kDefaultUserAgent.replaceAll("Mozilla/", ""), "browser_online": "true", "tz_name": "Asia/Shanghai", "identity": "audience", "room_id": danmakuArgs.roomId, "heartbeatDuration": "0", - "signature": "00000000" + //"signature": "00000000" }); - var url = uri.toString(); + + var sign = await getSignature(danmakuArgs.roomId, danmakuArgs.userId); + + var url = "$uri&signature=$sign"; var backupUrl = url.replaceAll("webcast3-ws-web-lq", "webcast5-ws-web-lf"); print(url); webScoketUtils = WebScoketUtils( url: url, backupUrl: backupUrl, headers: { - "User-Agnet": - "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.51", + "User-Agnet": DouyinSite.kDefaultUserAgent, "Cookie": danmakuArgs.cookie, }, heartBeatTime: heartbeatTime, @@ -192,4 +196,24 @@ class DouyinDanmaku implements LiveDanmaku { onClose = null; webScoketUtils?.close(); } + + /// 获取Websocket签名 + /// - [roomId] 房间ID, 例如:7382735338101328680 + /// - [uniqueId] 用户唯一ID, 例如:7273033021933946427 + /// + /// 服务端代码:https://github.com/lovelyyoshino/douyin_python,请自行部署后使用 + Future getSignature(String roomId, String uniqueId) async { + try { + var signResult = await http.HttpClient.instance.postJson( + "https://dy.nsapps.cn/signature", + queryParameters: {}, + header: {"Content-Type": "application/json"}, + data: {"roomId": roomId, "uniqueId": uniqueId}, + ); + return signResult["data"]["signature"]; + } catch (e) { + CoreLog.error(e); + return ""; + } + } } diff --git a/simple_live_core/lib/src/douyin_site.dart b/simple_live_core/lib/src/douyin_site.dart index 2b15b9c0..bf7cc892 100644 --- a/simple_live_core/lib/src/douyin_site.dart +++ b/simple_live_core/lib/src/douyin_site.dart @@ -558,10 +558,10 @@ class DouyinSite implements LiveSite { "browser_language": "zh-CN", "browser_platform": "Win32", "browser_name": "Edge", - "browser_version": "120.0.0.0", + "browser_version": "125.0.0.0", "browser_online": "true", "engine_name": "Blink", - "engine_version": "120.0.0.0", + "engine_version": "125.0.0.0", "os_name": "Windows", "os_version": "10", "cpu_core_num": "12", @@ -570,10 +570,10 @@ class DouyinSite implements LiveSite { "downlink": "10", "effective_type": "4g", "round_trip_time": "100", - "webid": "7273033021933946427", + "webid": "7382872326016435738", }); - var requlestUrl = await signUrl(uri.toString()); - + //var requlestUrl = await getAbogusUrl(uri.toString()); + var requlestUrl = uri.toString(); var headResp = await HttpClient.instance .head('https://live.douyin.com', header: headers); var dyCookie = ""; @@ -591,14 +591,24 @@ class DouyinSite implements LiveSite { requlestUrl, queryParameters: {}, header: { - "Accept": "*/*", "Authority": 'www.douyin.com', - "Referer": requlestUrl, - "Cookie": dyCookie, - "User-Agent": kDefaultUserAgent, + 'accept': 'application/json, text/plain, */*', + 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'cookie': dyCookie, + 'priority': 'u=1, i', + 'referer': + 'https://www.douyin.com/search/${Uri.encodeComponent(keyword)}?type=live', + 'sec-ch-ua': + '"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': kDefaultUserAgent, }, ); - if (result == 'blocked') { + if (result == "" || result == 'blocked') { throw Exception("抖音直播搜索被限制,请稍后再试"); } var items = []; @@ -657,19 +667,20 @@ class DouyinSite implements LiveSite { Random().nextInt(1000000000); } - Future signUrl(String url) async { + /// 读取A-Bogus签名后的URL + /// - [url] 原始URL + /// - 返回签名后的URL + /// + /// 服务端代码:https://github.com/dengmin/a-bogus,请自行部署后使用 + Future getAbogusUrl(String url) async { try { - // 发起一个签名请求 - // 服务端代码:https://github.com/5ime/Tiktok_Signature var signResult = await HttpClient.instance.postJson( - "https://tk.nsapps.cn/", + "https://dy.nsapps.cn/abogus", queryParameters: {}, header: {"Content-Type": "application/json"}, data: {"url": url, "userAgent": kDefaultUserAgent}, ); - var requlestUrl = - '${signResult["data"]["url"]}&msToken=${Uri.encodeComponent(signResult["data"]["mstoken"])}'; - return requlestUrl; + return signResult["data"]["url"]; } catch (e) { CoreLog.error(e); return url; From 3fb1fcb7aa3bc76b5f367ce581f5be51b3bcb530 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Fri, 21 Jun 2024 21:46:19 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E4=B8=8D=E6=B6=88=E5=A4=B1=E9=97=AE=E9=A2=98=20=20#454?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple_live_app/pubspec.yaml | 4 ++-- simple_live_tv_app/pubspec.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/simple_live_app/pubspec.yaml b/simple_live_app/pubspec.yaml index eab097f2..4889595e 100644 --- a/simple_live_app/pubspec.yaml +++ b/simple_live_app/pubspec.yaml @@ -1,5 +1,5 @@ name: simple_live_app -version: 1.6.3+10603 +version: 1.6.4+10604 publish_to: none description: "Simple Live APP" environment: @@ -35,7 +35,7 @@ dependencies: ns_danmaku: #弹幕 git: url: https://github.com/xiaoyaocz/flutter_ns_danmaku.git - tag: v0.0.8 + tag: v0.0.9 #系统交互 diff --git a/simple_live_tv_app/pubspec.yaml b/simple_live_tv_app/pubspec.yaml index bb81dded..8fb28c15 100644 --- a/simple_live_tv_app/pubspec.yaml +++ b/simple_live_tv_app/pubspec.yaml @@ -1,7 +1,7 @@ name: simple_live_tv_app description: A new Flutter project. publish_to: 'none' -version: 1.1.0+10100 +version: 1.1.1+10101 environment: sdk: '>=3.1.2 <4.0.0' @@ -36,7 +36,7 @@ dependencies: ns_danmaku: #弹幕 git: url: https://github.com/xiaoyaocz/flutter_ns_danmaku.git - tag: v0.0.8 + tag: v0.0.9 #系统交互 package_info_plus: ^8.0.0 #包信息 From 88b28dab90f9fa91f0f160c50c4b44726f1334b2 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Thu, 18 Jul 2024 15:21:25 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8A=96=E9=9F=B3?= =?UTF-8?q?=E5=BC=B9=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple_live_core/lib/src/danmaku/douyin_danmaku.dart | 1 + simple_live_core/lib/src/douyin_site.dart | 12 ++++++++---- simple_live_core/pubspec.yaml | 8 ++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/simple_live_core/lib/src/danmaku/douyin_danmaku.dart b/simple_live_core/lib/src/danmaku/douyin_danmaku.dart index c8624d84..781398ea 100644 --- a/simple_live_core/lib/src/danmaku/douyin_danmaku.dart +++ b/simple_live_core/lib/src/danmaku/douyin_danmaku.dart @@ -96,6 +96,7 @@ class DouyinDanmaku implements LiveDanmaku { headers: { "User-Agnet": DouyinSite.kDefaultUserAgent, "Cookie": danmakuArgs.cookie, + "Origin": "https://live.douyin.com" }, heartBeatTime: heartbeatTime, onMessage: (e) { diff --git a/simple_live_core/lib/src/douyin_site.dart b/simple_live_core/lib/src/douyin_site.dart index bf7cc892..2cb65741 100644 --- a/simple_live_core/lib/src/douyin_site.dart +++ b/simple_live_core/lib/src/douyin_site.dart @@ -125,7 +125,7 @@ class DouyinSite implements LiveSite { var items = []; for (var item in result["data"]["data"]) { var roomItem = LiveRoomItem( - roomId: item["room"]["id_str"].toString(), + roomId: item["web_rid"], title: item["room"]["title"].toString(), cover: item["room"]["cover"]["url_list"][0].toString(), userName: item["room"]["owner"]["nickname"].toString(), @@ -159,7 +159,7 @@ class DouyinSite implements LiveSite { var items = []; for (var item in result["data"]["data"]) { var roomItem = LiveRoomItem( - roomId: item["room"]["id_str"].toString(), + roomId: item["web_rid"], title: item["room"]["title"].toString(), cover: item["room"]["cover"]["url_list"][0].toString(), userName: item["room"]["owner"]["nickname"].toString(), @@ -351,8 +351,12 @@ class DouyinSite implements LiveSite { /// - [webRid] 直播间RID // ignore: unused_element Future _getUserUniqueId(String webRid) async { - var webInfo = await _getRoomDataByHtml(webRid); - return webInfo["userStore"]["odin"]["user_unique_id"].toString(); + try { + var webInfo = await _getRoomDataByHtml(webRid); + return webInfo["userStore"]["odin"]["user_unique_id"].toString(); + } catch (e) { + return generateRandomNumber(12).toString(); + } } /// 进入直播间前需要先获取cookie diff --git a/simple_live_core/pubspec.yaml b/simple_live_core/pubspec.yaml index 0c2d7721..ceb3e391 100644 --- a/simple_live_core/pubspec.yaml +++ b/simple_live_core/pubspec.yaml @@ -1,15 +1,15 @@ name: simple_live_core version: 1.0.3 description: "聚合直播核心库" -repository: https://github.com/xiaoyaocz/simple_live_client +repository: https://github.com/xiaoyaocz/dart_simple_live publish_to: "none" environment: sdk: '>=2.19.1 <3.0.0' dependencies: - dio: ^5.3.2 - logger: ^2.0.2 - web_socket_channel: ^2.4.0 + dio: ^5.5.0+1 + logger: ^2.4.0 + web_socket_channel: ^3.0.1 html_unescape: ^2.0.0 protobuf: ^3.1.0 crypto: ^3.0.3 From e95a792d4d00d5f14494d5f045a11077e3ca1be1 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Thu, 18 Jul 2024 15:28:54 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E6=8A=96=E9=9F=B3?= =?UTF-8?q?=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/modules/search/search_controller.dart | 56 +++++++++---------- .../lib/modules/search/search_page.dart | 16 +++--- simple_live_tv_app/pubspec.lock | 28 ++++++++-- 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/simple_live_app/lib/modules/search/search_controller.dart b/simple_live_app/lib/modules/search/search_controller.dart index 3dca5b0e..e8f242c8 100644 --- a/simple_live_app/lib/modules/search/search_controller.dart +++ b/simple_live_app/lib/modules/search/search_controller.dart @@ -3,9 +3,7 @@ import 'dart:async'; 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/search/douyin/douyin_search_controller.dart'; import 'package:simple_live_app/modules/search/search_list_controller.dart'; class AppSearchController extends GetxController @@ -25,9 +23,9 @@ class AppSearchController extends GetxController } index = currentIndex; - if (Sites.supportSites[index].id == Constant.kDouyin) { - return; - } + // if (Sites.supportSites[index].id == Constant.kDouyin) { + // return; + // } var controller = Get.find(tag: Sites.supportSites[index].id); @@ -47,14 +45,14 @@ class AppSearchController extends GetxController @override void onInit() { for (var site in Sites.supportSites) { - if (site.id == Constant.kDouyin) { - Get.put(DouyinSearchController(site)); - } else { - Get.put( - SearchListController(site), - tag: site.id, - ); - } + // if (site.id == Constant.kDouyin) { + // Get.put(DouyinSearchController(site)); + // } else { + Get.put( + SearchListController(site), + tag: site.id, + ); + //} } super.onInit(); @@ -65,23 +63,23 @@ class AppSearchController extends GetxController return; } for (var site in Sites.supportSites) { - if (site.id == Constant.kDouyin) { - var controller = Get.find(); - controller.keyword = searchController.text; - controller.searchMode.value = searchMode.value; - controller.reloadWebView(); - } else { - var controller = Get.find(tag: site.id); - controller.clear(); - controller.keyword = searchController.text; - controller.searchMode.value = searchMode.value; - } - } - if (Sites.supportSites[index].id != Constant.kDouyin) { - var controller = - Get.find(tag: Sites.supportSites[index].id); - controller.refreshData(); + // if (site.id == Constant.kDouyin) { + // var controller = Get.find(); + // controller.keyword = searchController.text; + // controller.searchMode.value = searchMode.value; + // controller.reloadWebView(); + // } else { + var controller = Get.find(tag: site.id); + controller.clear(); + controller.keyword = searchController.text; + controller.searchMode.value = searchMode.value; + //} } + // if (Sites.supportSites[index].id != Constant.kDouyin) { + var controller = + Get.find(tag: Sites.supportSites[index].id); + controller.refreshData(); + //} } @override diff --git a/simple_live_app/lib/modules/search/search_page.dart b/simple_live_app/lib/modules/search/search_page.dart index 0439e58e..03ba1861 100644 --- a/simple_live_app/lib/modules/search/search_page.dart +++ b/simple_live_app/lib/modules/search/search_page.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:simple_live_app/app/app_style.dart'; -import 'package:simple_live_app/app/constant.dart'; import 'package:simple_live_app/app/sites.dart'; -import 'package:simple_live_app/modules/search/douyin/douyin_search_view.dart'; import 'package:simple_live_app/modules/search/search_controller.dart'; import 'package:simple_live_app/modules/search/search_list_view.dart'; @@ -93,13 +91,15 @@ class SearchPage extends GetView { physics: const NeverScrollableScrollPhysics(), controller: controller.tabController, children: Sites.supportSites - .map( - (e) => e.id == Constant.kDouyin - ? const DouyinSearchView() - : SearchListView( + .map((e) => SearchListView( e.id, - ), - ) + ) + // (e) => e.id == Constant.kDouyin + // ? const DouyinSearchView() + // : SearchListView( + // e.id, + // ), + ) .toList(), ), ); diff --git a/simple_live_tv_app/pubspec.lock b/simple_live_tv_app/pubspec.lock index 5ed6ec1f..db5b09ac 100644 --- a/simple_live_tv_app/pubspec.lock +++ b/simple_live_tv_app/pubspec.lock @@ -118,10 +118,18 @@ packages: dependency: "direct main" description: name: dio - sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" + sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 url: "https://pub.flutter-io.cn" source: hosted - version: "5.4.3+1" + version: "5.5.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" extended_image: dependency: "direct main" description: @@ -357,10 +365,10 @@ packages: dependency: "direct main" description: name: logger - sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 + sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.0" + version: "2.4.0" lottie: dependency: "direct main" description: @@ -942,14 +950,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.5.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.5" + version: "3.0.1" win32: dependency: transitive description: From aceea165ec45fe41272ac2c07d00b320cfb1807e Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Thu, 18 Jul 2024 17:10:57 +0800 Subject: [PATCH 5/5] Release 1.6.5 / TV 1.1.2 --- assets/app_version.json | 6 +++--- assets/tv_app_version.json | 6 +++--- simple_live_app/pubspec.yaml | 2 +- simple_live_tv_app/pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/app_version.json b/assets/app_version.json index 3c9fff90..4bbcec66 100644 --- a/assets/app_version.json +++ b/assets/app_version.json @@ -1,7 +1,7 @@ { - "version": "1.6.3", - "version_num": 10603, - "version_desc": "- 修复抖音无法读取直播状态 #431\n- 修复抖音无法读取原画 #429\n- 优化直播状态读取\n-斗鱼默认不使用PCDN链接", + "version": "1.6.5", + "version_num": 10605, + "version_desc": "- 修复抖音弹幕连接失败问题 #458\n- 修复弹幕不消失问题 #454\n- 恢复抖音直播间搜索", "prerelease":false, "download_url": "https://github.com/xiaoyaocz/dart_simple_live/releases" } \ No newline at end of file diff --git a/assets/tv_app_version.json b/assets/tv_app_version.json index d9136f8d..fb2de8df 100644 --- a/assets/tv_app_version.json +++ b/assets/tv_app_version.json @@ -1,7 +1,7 @@ { - "version": "1.1.0", - "version_num": 10100, - "version_desc": "- 修复抖音无法读取原画\n- 优化直播状态读取\n-斗鱼默认不使用PCDN链接", + "version": "1.1.2", + "version_num": 10102, + "version_desc": "- 修复抖音弹幕连接失败问题 #458\n- 修复弹幕不消失问题 #454\n- 恢复抖音直播间搜索", "prerelease":true, "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 4889595e..1a800644 100644 --- a/simple_live_app/pubspec.yaml +++ b/simple_live_app/pubspec.yaml @@ -1,5 +1,5 @@ name: simple_live_app -version: 1.6.4+10604 +version: 1.6.5+10605 publish_to: none description: "Simple Live APP" environment: diff --git a/simple_live_tv_app/pubspec.yaml b/simple_live_tv_app/pubspec.yaml index 8fb28c15..e624d770 100644 --- a/simple_live_tv_app/pubspec.yaml +++ b/simple_live_tv_app/pubspec.yaml @@ -1,7 +1,7 @@ name: simple_live_tv_app description: A new Flutter project. publish_to: 'none' -version: 1.1.1+10101 +version: 1.1.2+10102 environment: sdk: '>=3.1.2 <4.0.0'