Skip to content

Commit a7157eb

Browse files
author
liuchuancong
committed
fix(douyin)
1 parent 5056625 commit a7157eb

19 files changed

Lines changed: 10083 additions & 10134 deletions

assets/js/crypto-js.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/douyin.js

Lines changed: 413 additions & 412 deletions
Large diffs are not rendered by default.

assets/js/webmssdk.js

Lines changed: 9209 additions & 9209 deletions
Large diffs are not rendered by default.

assets/version.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"version": "1.9.8",
2+
"version": "1.9.9",
33
"version_num": 10198,
4-
"version_desc": "-Fix: 修复Exo全屏播放. \n-Add:播放页面添加切换直播间以及倒计时退出 . \n-Fix: 优化弹幕设置. \n-Fix: 优化暗黑模式启动图. \n-Fix: 修复视频显示模式调整.",
4+
"version_desc": "-Fix: 修复douyin.",
55
"prerelease":false,
66
"download_url": "https://github.com/liuchuancong/pure_live/releases"
77
}

lib/common/services/settings_service.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class SettingsService extends GetxController {
270270
final videoFitIndex = (PrefUtil.getInt('videoFitIndex') ?? 0).obs;
271271
final hideDanmaku = (PrefUtil.getBool('hideDanmaku') ?? false).obs;
272272
final danmakuTopArea = (PrefUtil.getDouble('danmakuTopArea') ?? 0.0).obs;
273-
final danmakuBottomArea = (PrefUtil.getDouble('danmakuBottomArea') ?? 0.0).obs;
273+
final danmakuBottomArea = (PrefUtil.getDouble('danmakuBottomArea') ?? 0.5).obs;
274274
final danmakuSpeed = (PrefUtil.getDouble('danmakuSpeed') ?? 8.0).obs;
275275
final danmakuFontSize = (PrefUtil.getDouble('danmakuFontSize') ?? 16.0).obs;
276276
final danmakuFontBorder = (PrefUtil.getDouble('danmakuFontBorder') ?? 4.0).obs;
@@ -657,11 +657,7 @@ class SettingsService extends GetxController {
657657
? 0.4
658658
: double.parse(json['danmakuTopArea'].toString())
659659
: 0.0;
660-
danmakuBottomArea.value = json['danmakuBottomArea'] != null
661-
? double.parse(json['danmakuBottomArea'].toString()) > 0.4
662-
? 0.4
663-
: double.parse(json['danmakuBottomArea'].toString())
664-
: 0.0;
660+
danmakuBottomArea.value = double.parse(json['danmakuBottomArea'].toString());
665661
danmakuSpeed.value = json['danmakuSpeed'] != null ? double.parse(json['danmakuSpeed'].toString()) : 8.0;
666662
danmakuFontSize.value = json['danmakuFontSize'] != null ? double.parse(json['danmakuFontSize'].toString()) : 16.0;
667663
danmakuFontBorder.value = json['danmakuFontBorder'] != null

lib/common/utils/js_engine.dart

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ class JsEngine {
1515
jsRuntime.evaluate(webmssdkjs);
1616
}
1717

18-
static Future<void> loadDouyinExEcutorSdk() async {
19-
final douyinsdkjs = await rootBundle.loadString('assets/js/douyin.js');
20-
jsRuntime.evaluate(douyinsdkjs);
21-
}
22-
2318
static JsEvalResult evaluate(String code) {
2419
return jsRuntime.evaluate(code);
2520
}

lib/common/utils/version_util.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'dart:convert';
22
import 'package:http/http.dart' as http;
33

44
class VersionUtil {
5-
static const String version = '1.9.8';
5+
static const String version = '1.9.9';
66
static const String projectUrl = 'https://github.com/liuchuancong/pure_live';
77
static const String releaseUrl = 'https://api.github.com/repos/liuchuancong/pure_live/releases';
88
static const String issuesUrl = 'https://github.com/liuchuancong/pure_live/issues';

lib/core/danmaku/douyin_danmaku.dart

Lines changed: 17 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ import 'dart:io';
22
import 'dart:async';
33
import 'dart:convert';
44
import 'proto/douyin.pb.dart';
5-
import 'package:crypto/crypto.dart';
6-
import 'package:flutter_js/flutter_js.dart';
75
import 'package:pure_live/core/site/douyin_site.dart';
8-
import 'package:pure_live/common/utils/js_engine.dart';
96
import 'package:pure_live/common/models/live_message.dart';
107
import 'package:pure_live/core/common/web_socket_util.dart';
118
import 'package:pure_live/core/interface/live_danmaku.dart';
12-
import 'package:pure_live/core/common/http_client.dart' as http;
139

1410
class DouyinDanmakuArgs {
1511
final String webRid;
@@ -41,17 +37,13 @@ class DouyinDanmaku implements LiveDanmaku {
4137
Future start(dynamic args) async {
4238
danmakuArgs = args as DouyinDanmakuArgs;
4339
var ts = DateTime.now().millisecondsSinceEpoch;
44-
JsEngine.init();
45-
await JsEngine.loadDouyinSdk();
46-
var xMsStub = getMsStub(danmakuArgs.roomId, danmakuArgs.userId);
47-
JsEvalResult jsEvalResult = await JsEngine.evaluateAsync("get_sign('$xMsStub')");
4840
var uri = Uri.parse(serverUrl).replace(
4941
scheme: "wss",
5042
queryParameters: {
5143
"app_name": "douyin_web",
5244
"version_code": "180800",
53-
"webcast_sdk_version": "1.0.14-beta.0",
54-
"update_version_code": "1.0.14-beta.0",
45+
"webcast_sdk_version": "1.3.0",
46+
"update_version_code": "1.3.0",
5547
"compress": "gzip",
5648
// "internal_ext":
5749
// "internal_src:dim|wss_push_room_id:${danmakuArgs.roomId}|wss_push_did:${danmakuArgs.userId}|dim_log_id:20230626152702E8F63662383A350588E1|fetch_time:1687764422114|seq:1|wss_info:0-1687764422114-0-0|wrds_kvs:WebcastRoomRankMessage-1687764036509597990_InputPanelComponentSyncData-1687736682345173033_WebcastRoomStatsMessage-1687764414427812578",
@@ -79,18 +71,22 @@ class DouyinDanmaku implements LiveDanmaku {
7971
"identity": "audience",
8072
"room_id": danmakuArgs.roomId,
8173
"heartbeatDuration": "0",
82-
"signature": jsEvalResult.stringResult,
74+
//"signature": "00000000"
8375
},
8476
);
8577

86-
// var sign = await getSignature(danmakuArgs.roomId, danmakuArgs.userId);
78+
var sign = await getSignature(danmakuArgs.roomId, danmakuArgs.userId);
8779

88-
var url = "$uri";
80+
var url = "$uri&signature=$sign";
8981
var backupUrl = url.replaceAll("webcast3-ws-web-lq", "webcast5-ws-web-lf");
9082
webScoketUtils = WebScoketUtils(
9183
url: url,
9284
backupUrl: backupUrl,
93-
headers: {"User-Agnet": DouyinSite.kDefaultUserAgent, "Cookie": danmakuArgs.cookie},
85+
headers: {
86+
"User-Agnet": DouyinSite.kDefaultUserAgent,
87+
"Cookie": danmakuArgs.cookie,
88+
"Origin": "https://live.douyin.com",
89+
},
9490
heartBeatTime: heartbeatTime,
9591
onMessage: (e) {
9692
decodeMessage(e);
@@ -119,9 +115,7 @@ class DouyinDanmaku implements LiveDanmaku {
119115
webScoketUtils?.sendMessage(obj.writeToBuffer());
120116
}
121117

122-
void decodeMessage(List<int> args) {
123-
// CoreLog.i(args.toString());
124-
118+
void decodeMessage(dynamic args) {
125119
var wssPackage = PushFrame.fromBuffer(args);
126120

127121
var logId = wssPackage.logId;
@@ -156,25 +150,6 @@ class DouyinDanmaku implements LiveDanmaku {
156150
);
157151
}
158152

159-
/// 获取Websocket签名
160-
/// - [roomId] 房间ID, 例如:7382735338101328680
161-
/// - [uniqueId] 用户唯一ID, 例如:7273033021933946427
162-
///
163-
/// 服务端代码:https://github.com/lovelyyoshino/douyin_python,请自行部署后使用
164-
Future<String> getSignature(String roomId, String uniqueId) async {
165-
try {
166-
var signResult = await http.HttpClient.instance.postJson(
167-
"https://dy.nsapps.cn/signature",
168-
queryParameters: {},
169-
header: {"Content-Type": "application/json"},
170-
data: {"roomId": roomId, "uniqueId": uniqueId},
171-
);
172-
return signResult["data"]["signature"];
173-
} catch (e) {
174-
return "";
175-
}
176-
}
177-
178153
void unPackWebcastRoomUserSeqMessage(List<int> payload) {
179154
var roomUserSeqMessage = RoomUserSeqMessage.fromBuffer(payload);
180155

@@ -210,27 +185,11 @@ class DouyinDanmaku implements LiveDanmaku {
210185
webScoketUtils?.close();
211186
}
212187

213-
String getMsStub(String liveRoomRealId, String userUniqueId) {
214-
Map<String, dynamic> params = {
215-
"live_id": "1",
216-
"aid": "6383",
217-
"version_code": 180800,
218-
"webcast_sdk_version": '1.0.14-beta.0',
219-
"room_id": liveRoomRealId,
220-
"sub_room_id": "",
221-
"sub_channel_id": "",
222-
"did_rule": "3",
223-
"user_unique_id": userUniqueId,
224-
"device_platform": "web",
225-
"device_type": "",
226-
"ac": "",
227-
"identity": "audience",
228-
};
229-
230-
String sigParams = params.entries.map((e) => '${e.key}=${e.value}').join(',');
231-
232-
var bytes = utf8.encode(sigParams);
233-
var digest = md5.convert(bytes);
234-
return digest.toString();
188+
Future<String> Function(String, String) getSignature = (roomId, uniqueId) async {
189+
return "";
190+
};
191+
192+
void setSignatureFunction(Future<String> Function(String, String) func) {
193+
getSignature = func;
235194
}
236195
}

lib/core/danmaku/douyu_danmaku.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class DouyuDanmaku implements LiveDanmaku {
4646
webScoketUtils?.connect();
4747
}
4848

49-
void joinRoom(String roomId) {
49+
void joinRoom(dynamic roomId) {
5050
webScoketUtils?.sendMessage(serializeDouyu("type@=loginreq/roomid@=$roomId/"));
5151
webScoketUtils?.sendMessage(serializeDouyu("type@=joingroup/rid@=$roomId/gid@=-9999/"));
5252
}
@@ -74,8 +74,11 @@ class DouyuDanmaku implements LiveDanmaku {
7474

7575
var type = jsonData["type"]?.toString();
7676
//斗鱼好像不会返回人气值
77-
//有些直播间存在阴间弹幕,不知道什么情况
7877
if (type == "chatmsg") {
78+
// 屏蔽阴间弹幕
79+
if (jsonData["dms"] == null) {
80+
return;
81+
}
7982
var col = int.tryParse(jsonData["col"].toString()) ?? 0;
8083
var liveMsg = LiveMessage(
8184
type: LiveMessageType.chat,

lib/core/sign/douyin.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import 'dart:math';
2+
import 'package:crypto/crypto.dart';
3+
import 'package:flutter/services.dart';
4+
import 'package:flutter_js/flutter_js.dart';
5+
import 'package:pure_live/core/site/douyin_site.dart';
6+
7+
class DouyinSign {
8+
static const String defaultUserAgent = DouyinSite.kDefaultUserAgent;
9+
static Future<String> getAbogusUrl(String url, String userAgent) async {
10+
JavascriptRuntime flutterJs = getJavascriptRuntime();
11+
final msToken = generateMsToken(107);
12+
var params = ('$url&msToken=$msToken').split('?')[1];
13+
var query = params.contains("?") ? params.split("?")[1] : params;
14+
var jsCode = await rootBundle.loadString('assets/js/douyin.js');
15+
await flutterJs.evaluateAsync(jsCode);
16+
// 执行getABogus函数
17+
var aBogus = (await flutterJs.evaluateAsync("getABogus('$query', '$userAgent')")).stringResult;
18+
flutterJs.dispose();
19+
var newUrl = '$url&msToken=${Uri.encodeComponent(msToken)}&a_bogus=${Uri.encodeComponent(aBogus)}';
20+
return newUrl;
21+
}
22+
23+
static Future<String> getSignature(String roomId, String uniqueId) async {
24+
JavascriptRuntime flutterJs = getJavascriptRuntime();
25+
var jsCode = await rootBundle.loadString('assets/js/webmssdk.js');
26+
await flutterJs.evaluateAsync(jsCode);
27+
var msStub = getMsStub(roomId, uniqueId);
28+
var signature = (await flutterJs.evaluateAsync("getMSSDKSignature('$msStub','$defaultUserAgent')")).stringResult;
29+
// 如果signature中包含-或=,重新生成
30+
while (signature.contains('-') || signature.contains('=')) {
31+
signature = (await flutterJs.evaluateAsync("getMSSDKSignature('$msStub','$defaultUserAgent')")).stringResult;
32+
}
33+
flutterJs.dispose();
34+
return signature;
35+
}
36+
37+
static String getMsStub(String roomId, String uniqueId) {
38+
final params = {
39+
"live_id": "1",
40+
"aid": "6383",
41+
"version_code": 180800,
42+
"webcast_sdk_version": "1.3.0",
43+
"room_id": roomId,
44+
"sub_room_id": "",
45+
"sub_channel_id": "",
46+
"did_rule": "3",
47+
"user_unique_id": uniqueId,
48+
"device_platform": "web",
49+
"device_type": "",
50+
"ac": "",
51+
"identity": "audience",
52+
};
53+
final sigParams = params.entries.map((e) => "${e.key}=${e.value}").join(',');
54+
// 需要导入crypto库: import 'package:crypto/crypto.dart';
55+
final bytes = sigParams.codeUnits;
56+
final digest = md5.convert(bytes);
57+
return digest.toString();
58+
}
59+
60+
static String generateMsToken(int length) {
61+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
62+
final random = Random.secure();
63+
return List.generate(length, (_) => characters[random.nextInt(characters.length)]).join('');
64+
}
65+
}

0 commit comments

Comments
 (0)