From 00314b3ea6af5ad465c60aec18fb247ef012416a Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Mon, 2 Dec 2024 18:09:02 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=99=8E=E7=89=99Tup?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple_live_core/demo/getCdnTokenInfoReq.bin | Bin 0 -> 166 bytes simple_live_core/demo/getCdnTokenInfoResp.bin | Bin 0 -> 530 bytes .../example/simple_live_core_example.dart | 129 +++- simple_live_core/lib/src/common/core_log.dart | 1 - .../lib/src/danmaku/huya_danmaku.dart | 90 +-- .../lib/src/model/tars/get_cdn_token_req.dart | 50 ++ .../src/model/tars/get_cdn_token_resp.dart | 74 +++ .../lib/src/model/tars/huya_danmaku.dart | 122 ++++ .../packages/tars_dart/.gitignore | 52 ++ .../packages/tars_dart/CHANGELOG.md | 7 + simple_live_core/packages/tars_dart/LICENSE | 29 + simple_live_core/packages/tars_dart/README.md | 6 + .../packages/tars_dart/analysis_options.yaml | 4 + .../lib/tars/codec/tars_decode_exception.dart | 8 + .../lib/tars/codec/tars_deep_copyable.dart | 61 ++ .../lib/tars/codec/tars_displayer.dart | 192 ++++++ .../lib/tars/codec/tars_encode_exception.dart | 9 + .../lib/tars/codec/tars_input_stream.dart | 624 ++++++++++++++++++ .../lib/tars/codec/tars_output_stream.dart | 256 +++++++ .../tars_dart/lib/tars/codec/tars_struct.dart | 37 ++ .../lib/tars/net/base_tars_http.dart | 139 ++++ .../lib/tars/tup/basic_class_type_util.dart | 140 ++++ .../tars_dart/lib/tars/tup/const.dart | 18 + .../lib/tars/tup/object_create_exception.dart | 8 + .../lib/tars/tup/request_packet.dart | 114 ++++ .../lib/tars/tup/tars_uni_packet.dart | 105 +++ .../tars_dart/lib/tars/tup/tup_response.dart | 6 + .../lib/tars/tup/tup_result_exception.dart | 11 + .../tars_dart/lib/tars/tup/uni_attribute.dart | 279 ++++++++ .../tars_dart/lib/tars/tup/uni_packet.dart | 142 ++++ .../tars_dart/lib/tars/tup/write_buffer.dart | 280 ++++++++ .../packages/tars_dart/pubspec.yaml | 15 + simple_live_core/pubspec.yaml | 4 +- 33 files changed, 2886 insertions(+), 126 deletions(-) create mode 100644 simple_live_core/demo/getCdnTokenInfoReq.bin create mode 100644 simple_live_core/demo/getCdnTokenInfoResp.bin create mode 100644 simple_live_core/lib/src/model/tars/get_cdn_token_req.dart create mode 100644 simple_live_core/lib/src/model/tars/get_cdn_token_resp.dart create mode 100644 simple_live_core/lib/src/model/tars/huya_danmaku.dart create mode 100644 simple_live_core/packages/tars_dart/.gitignore create mode 100644 simple_live_core/packages/tars_dart/CHANGELOG.md create mode 100644 simple_live_core/packages/tars_dart/LICENSE create mode 100644 simple_live_core/packages/tars_dart/README.md create mode 100644 simple_live_core/packages/tars_dart/analysis_options.yaml create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_decode_exception.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_deep_copyable.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_displayer.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_encode_exception.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_input_stream.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_output_stream.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/codec/tars_struct.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/net/base_tars_http.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/basic_class_type_util.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/const.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/object_create_exception.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/request_packet.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/tars_uni_packet.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/tup_response.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/tup_result_exception.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/uni_attribute.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/uni_packet.dart create mode 100644 simple_live_core/packages/tars_dart/lib/tars/tup/write_buffer.dart create mode 100644 simple_live_core/packages/tars_dart/pubspec.yaml diff --git a/simple_live_core/demo/getCdnTokenInfoReq.bin b/simple_live_core/demo/getCdnTokenInfoReq.bin new file mode 100644 index 0000000000000000000000000000000000000000..8b11ea05e954be24e625bd687cf574a1c0037196 GIT binary patch literal 166 zcmZQzU|1%=q+{dA5f{dmlUbHpnwiF*o?7CZk{6Pnoto#FmzH14z);D-z{ti@5|mmf z0b~e!gho2*xg%5o#bp^7Lb%u%#F#w7)jSOiEiKKA%`J?~%#1B`QF*53X2xd5rWO{K v=4M9bMwXTqCb~w(mX=25rsn2m=Eg?4h6V${-h+<^fO>SOo_D;~Q>ZP{wR}mFgPE)@MLsGrnfLRgcms;^-2$ox zPJyEBrg?=?8vsSucB-IUZN}r?_`$ol{^Odx-C$pP8aPpV6Qd6y=LA!R2*J$!-yBLX z!46}b5=0T_%r+6`98rf-LNPL-WjWOJOv{8FKdsB5jGtxYvS~!8T!_SG3}Q|q8N~=j zQbL4mCb3bMqjV`h8~Eb&{_18voDYgY7Tk*N<9HIX?9Okz$wlM(40jua btzR-?B3W(i)JRHq7FX-7KkmPO=%4xzSl*Vy literal 0 HcmV?d00001 diff --git a/simple_live_core/example/simple_live_core_example.dart b/simple_live_core/example/simple_live_core_example.dart index 99622aeb..7a1218bf 100644 --- a/simple_live_core/example/simple_live_core_example.dart +++ b/simple_live_core/example/simple_live_core_example.dart @@ -1,39 +1,96 @@ -import 'package:simple_live_core/simple_live_core.dart'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:simple_live_core/src/model/tars/get_cdn_token_req.dart'; +import 'package:simple_live_core/src/model/tars/get_cdn_token_resp.dart'; +import 'package:tars_dart/tars/net/base_tars_http.dart'; +import 'package:tars_dart/tars/tup/uni_packet.dart'; void main() async { - CoreLog.enableLog = true; - CoreLog.requestLogType = RequestLogType.short; - LiveSite site = BiliBiliSite(); - var danmaku = site.getDanmaku(); - danmaku.onMessage = (event) { - if (event.type == LiveMessageType.chat) { - print("[${event.color}]${event.userName}:${event.message}"); - } else if (event.type == LiveMessageType.online) { - print("-----人气:${event.data}-----"); - } else if (event.type == LiveMessageType.superChat) { - var scMessage = event.data as LiveSuperChatMessage; - print("[SC]${scMessage.userName}:${scMessage.message}"); - } - }; - danmaku.onClose = (event) { - print(event); - }; - - //var search = await site.searchRooms("东方"); - - //var categores = await site.getCategores(); - //print(categores.length); - var detail = await site.getRoomDetail(roomId: '7734200'); - // 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); - - await Future.wait({}); + // CoreLog.enableLog = true; + // CoreLog.requestLogType = RequestLogType.short; + // LiveSite site = BiliBiliSite(); + // var danmaku = site.getDanmaku(); + // danmaku.onMessage = (event) { + // if (event.type == LiveMessageType.chat) { + // print("[${event.color}]${event.userName}:${event.message}"); + // } else if (event.type == LiveMessageType.online) { + // print("-----人气:${event.data}-----"); + // } else if (event.type == LiveMessageType.superChat) { + // var scMessage = event.data as LiveSuperChatMessage; + // print("[SC]${scMessage.userName}:${scMessage.message}"); + // } + // }; + // danmaku.onClose = (event) { + // print(event); + // }; + + // //var search = await site.searchRooms("东方"); + + // //var categores = await site.getCategores(); + // //print(categores.length); + // var detail = await site.getRoomDetail(roomId: '7734200'); + // // 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); + + // await Future.wait({}); + sendReq(); +} + +void testHuyaReq() async { + var reqBytes = await File('demo/getCdnTokenInfoReq.bin').readAsBytes(); + // RequestPacket req = RequestPacket(); + // req.readFrom(TarsInputStream(reqBytes)); + // print(req.iVersion); + + UniPacket uniPacket = UniPacket(); + //uniPacket.readFrom(TarsInputStream(reqBytes)); + uniPacket.decode(reqBytes); + var value = uniPacket.get('tReq', GetCdnTokenReq()); + // GetCdnTokenReq reqData = GetCdnTokenReq(); + // reqData.readFrom(TarsInputStream(req.sBuffer)); + print(value.toString()); +} + +void sendReq() async { + var req = GetCdnTokenReq(); + req.cdnType = "HW"; + req.streamName = + "1199637826638-1199637826638-5763635889762729984-2399275776732-10057-A-0-1"; + + BaseTarsHttp http = BaseTarsHttp("http://wup.huya.com", "liveui"); + + var data = await http.tupRequest("getCdnTokenInfo", req, GetCdnTokenResp()); + + var url = + 'http://hw.flv.huya.com/src/${data.streamName}.flv?${data.flvAntiCode}&codec=264'; + print(url); + await Dio().download( + url, + 'live-stream.flv', + options: Options( + responseType: ResponseType.bytes, + headers: {"user-agent": "HYSDK(Windows, 20000308)"}, + ), + onReceiveProgress: (count, total) { + var downBytes = count / 1024 / 1024; + print('downloading: $downBytes MB'); + }, + ); +} + +void testHuyaResp() async { + var respBytes = await File('demo/getCdnTokenInfoResp.bin').readAsBytes(); + UniPacket uniPacket = UniPacket(); + uniPacket.decode(respBytes); + var value = uniPacket.get('tRsp', GetCdnTokenResp()); + print(value.toString()); } diff --git a/simple_live_core/lib/src/common/core_log.dart b/simple_live_core/lib/src/common/core_log.dart index 6c6876d0..9f9e27b5 100644 --- a/simple_live_core/lib/src/common/core_log.dart +++ b/simple_live_core/lib/src/common/core_log.dart @@ -28,7 +28,6 @@ class CoreLog { lineLength: 120, colors: true, printEmojis: true, - printTime: false, ), ); diff --git a/simple_live_core/lib/src/danmaku/huya_danmaku.dart b/simple_live_core/lib/src/danmaku/huya_danmaku.dart index d6b18b31..f2a5840c 100644 --- a/simple_live_core/lib/src/danmaku/huya_danmaku.dart +++ b/simple_live_core/lib/src/danmaku/huya_danmaku.dart @@ -1,14 +1,12 @@ -// ignore_for_file: no_leading_underscores_for_local_identifiers - import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:simple_live_core/simple_live_core.dart'; import 'package:simple_live_core/src/common/web_socket_util.dart'; -import 'package:dart_tars_protocol/tars_input_stream.dart'; -import 'package:dart_tars_protocol/tars_output_stream.dart'; -import 'package:dart_tars_protocol/tars_struct.dart'; +import 'package:simple_live_core/src/model/tars/huya_danmaku.dart'; +import 'package:tars_dart/tars/codec/tars_input_stream.dart'; +import 'package:tars_dart/tars/codec/tars_output_stream.dart'; class HuyaDanmakuArgs { final int ayyuid; @@ -160,85 +158,3 @@ class HuyaDanmaku implements LiveDanmaku { } } } - -class HYPushMessage extends TarsStruct { - int pushType = 0; - int uri = 0; - List msg = []; - int protocolType = 0; - - @override - void readFrom(TarsInputStream _is) { - pushType = _is.read(pushType, 0, false); - uri = _is.read(uri, 1, false); - msg = _is.readBytes(2, false); - protocolType = _is.read(protocolType, 3, false); - } - - @override - void display(StringBuffer sb, int level) {} - - @override - void writeTo(TarsOutputStream _os) {} -} - -class HYSender extends TarsStruct { - int uid = 0; - int lMid = 0; - String nickName = ""; - int gender = 0; - - @override - void readFrom(TarsInputStream _is) { - uid = _is.read(uid, 0, false); - lMid = _is.read(lMid, 0, false); - nickName = _is.read(nickName, 2, false); - gender = _is.read(gender, 3, false); - } - - @override - void display(StringBuffer sb, int level) {} - - @override - void writeTo(TarsOutputStream _os) {} -} - -class HYMessage extends TarsStruct { - HYSender userInfo = HYSender(); - String content = ""; - HYBulletFormat bulletFormat = HYBulletFormat(); - - @override - void readFrom(TarsInputStream _is) { - userInfo = _is.readTarsStruct(userInfo, 0, false) as HYSender; - content = _is.read(content, 3, false); - bulletFormat = _is.readTarsStruct(bulletFormat, 6, false) as HYBulletFormat; - } - - @override - void display(StringBuffer sb, int level) {} - - @override - void writeTo(TarsOutputStream _os) {} -} - -class HYBulletFormat extends TarsStruct { - int fontColor = 0; - int fontSize = 4; - int textSpeed = 0; - int transitionType = 1; - - @override - void readFrom(TarsInputStream _is) { - fontColor = _is.read(fontColor, 0, false); - fontSize = _is.read(fontSize, 1, false); - textSpeed = _is.read(textSpeed, 2, false); - transitionType = _is.read(transitionType, 3, false); - } - - @override - void display(StringBuffer sb, int level) {} - - @override - void writeTo(TarsOutputStream _os) {} -} diff --git a/simple_live_core/lib/src/model/tars/get_cdn_token_req.dart b/simple_live_core/lib/src/model/tars/get_cdn_token_req.dart new file mode 100644 index 00000000..57c82572 --- /dev/null +++ b/simple_live_core/lib/src/model/tars/get_cdn_token_req.dart @@ -0,0 +1,50 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'package:tars_dart/tars/codec/tars_displayer.dart'; +import 'package:tars_dart/tars/codec/tars_input_stream.dart'; +import 'package:tars_dart/tars/codec/tars_output_stream.dart'; +import 'package:tars_dart/tars/codec/tars_struct.dart'; + +class GetCdnTokenReq extends TarsStruct { + String url = ""; + + String cdnType = ""; + + String streamName = ""; + + int presenterUid = 0; + + @override + void readFrom(TarsInputStream _is) { + url = _is.read(url, 0, false); + cdnType = _is.read(cdnType, 1, false); + streamName = _is.read(streamName, 2, false); + presenterUid = _is.read(presenterUid, 3, false); + } + + @override + void writeTo(TarsOutputStream _os) { + _os.write(url, 0); + _os.write(cdnType, 1); + _os.write(streamName, 2); + _os.write(presenterUid, 3); + } + + @override + Object deepCopy() { + return GetCdnTokenReq() + ..url = url + ..cdnType = cdnType + ..streamName = streamName + ..presenterUid = presenterUid; + } + + @override + void displayAsString(StringBuffer sb, int level) { + TarsDisplayer _ds = TarsDisplayer(sb, level: level); + _ds.DisplayString(url, "url"); + _ds.DisplayString(cdnType, "cdnType"); + _ds.DisplayString(streamName, "streamName"); + _ds.DisplayInt(presenterUid, "presenterUid"); + } +} diff --git a/simple_live_core/lib/src/model/tars/get_cdn_token_resp.dart b/simple_live_core/lib/src/model/tars/get_cdn_token_resp.dart new file mode 100644 index 00000000..3412f654 --- /dev/null +++ b/simple_live_core/lib/src/model/tars/get_cdn_token_resp.dart @@ -0,0 +1,74 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'package:tars_dart/tars/codec/tars_displayer.dart'; +import 'package:tars_dart/tars/codec/tars_input_stream.dart'; +import 'package:tars_dart/tars/codec/tars_output_stream.dart'; +import 'package:tars_dart/tars/codec/tars_struct.dart'; + +class GetCdnTokenResp extends TarsStruct { + String url = ""; + + String cdnType = ""; + + String streamName = ""; + + int presenterUid = 0; + + String antiCode = ""; + + String sTime = ""; + + String flvAntiCode = ""; + + String hlsAntiCode = ""; + + @override + void readFrom(TarsInputStream _is) { + url = _is.read(url, 0, false); + cdnType = _is.read(cdnType, 1, false); + streamName = _is.read(streamName, 2, false); + presenterUid = _is.read(presenterUid, 3, false); + antiCode = _is.read(antiCode, 4, false); + sTime = _is.read(sTime, 5, false); + flvAntiCode = _is.read(flvAntiCode, 6, false); + hlsAntiCode = _is.read(hlsAntiCode, 7, false); + } + + @override + void writeTo(TarsOutputStream _os) { + _os.write(url, 0); + _os.write(cdnType, 1); + _os.write(streamName, 2); + _os.write(presenterUid, 3); + _os.write(antiCode, 4); + _os.write(sTime, 5); + _os.write(flvAntiCode, 6); + _os.write(hlsAntiCode, 7); + } + + @override + Object deepCopy() { + return GetCdnTokenResp() + ..url = url + ..cdnType = cdnType + ..streamName = streamName + ..presenterUid = presenterUid + ..antiCode = antiCode + ..sTime = sTime + ..flvAntiCode = flvAntiCode + ..hlsAntiCode = hlsAntiCode; + } + + @override + void displayAsString(StringBuffer sb, int level) { + TarsDisplayer _ds = TarsDisplayer(sb, level: level); + _ds.DisplayString(url, "url"); + _ds.DisplayString(cdnType, "cdnType"); + _ds.DisplayString(streamName, "streamName"); + _ds.DisplayInt(presenterUid, "presenterUid"); + _ds.DisplayString(antiCode, "antiCode"); + _ds.DisplayString(sTime, "sTime"); + _ds.DisplayString(flvAntiCode, "flvAntiCode"); + _ds.DisplayString(hlsAntiCode, "hlsAntiCode"); + } +} diff --git a/simple_live_core/lib/src/model/tars/huya_danmaku.dart b/simple_live_core/lib/src/model/tars/huya_danmaku.dart new file mode 100644 index 00000000..dec793ec --- /dev/null +++ b/simple_live_core/lib/src/model/tars/huya_danmaku.dart @@ -0,0 +1,122 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'package:tars_dart/tars/codec/tars_input_stream.dart'; +import 'package:tars_dart/tars/codec/tars_output_stream.dart'; +import 'package:tars_dart/tars/codec/tars_struct.dart'; + +class HYPushMessage extends TarsStruct { + int pushType = 0; + int uri = 0; + List msg = []; + int protocolType = 0; + + @override + void readFrom(TarsInputStream _is) { + pushType = _is.read(pushType, 0, false); + uri = _is.read(uri, 1, false); + msg = _is.readBytes(2, false); + protocolType = _is.read(protocolType, 3, false); + } + + @override + void writeTo(TarsOutputStream _os) {} + + @override + Object deepCopy() { + return HYPushMessage() + ..pushType = pushType + ..uri = uri + ..msg = List.from(msg) + ..protocolType = protocolType; + } + + @override + void displayAsString(StringBuffer sb, int level) {} +} + +class HYSender extends TarsStruct { + int uid = 0; + int lMid = 0; + String nickName = ""; + int gender = 0; + + @override + void readFrom(TarsInputStream _is) { + uid = _is.read(uid, 0, false); + lMid = _is.read(lMid, 0, false); + nickName = _is.read(nickName, 2, false); + gender = _is.read(gender, 3, false); + } + + @override + void writeTo(TarsOutputStream _os) {} + + @override + Object deepCopy() { + return HYSender() + ..uid = uid + ..lMid = lMid + ..nickName = nickName + ..gender = gender; + } + + @override + void displayAsString(StringBuffer sb, int level) {} +} + +class HYMessage extends TarsStruct { + HYSender userInfo = HYSender(); + String content = ""; + HYBulletFormat bulletFormat = HYBulletFormat(); + + @override + void readFrom(TarsInputStream _is) { + userInfo = _is.readTarsStruct(userInfo, 0, false) as HYSender; + content = _is.read(content, 3, false); + bulletFormat = _is.readTarsStruct(bulletFormat, 6, false) as HYBulletFormat; + } + + @override + void writeTo(TarsOutputStream _os) {} + + @override + Object deepCopy() { + return HYMessage() + ..userInfo = userInfo.deepCopy() as HYSender + ..content = content + ..bulletFormat = bulletFormat.deepCopy() as HYBulletFormat; + } + + @override + void displayAsString(StringBuffer sb, int level) {} +} + +class HYBulletFormat extends TarsStruct { + int fontColor = 0; + int fontSize = 4; + int textSpeed = 0; + int transitionType = 1; + + @override + void readFrom(TarsInputStream _is) { + fontColor = _is.read(fontColor, 0, false); + fontSize = _is.read(fontSize, 1, false); + textSpeed = _is.read(textSpeed, 2, false); + transitionType = _is.read(transitionType, 3, false); + } + + @override + void writeTo(TarsOutputStream _os) {} + + @override + Object deepCopy() { + return HYBulletFormat() + ..fontColor = fontColor + ..fontSize = fontSize + ..textSpeed = textSpeed + ..transitionType = transitionType; + } + + @override + void displayAsString(StringBuffer sb, int level) {} +} diff --git a/simple_live_core/packages/tars_dart/.gitignore b/simple_live_core/packages/tars_dart/.gitignore new file mode 100644 index 00000000..00ae7a9b --- /dev/null +++ b/simple_live_core/packages/tars_dart/.gitignore @@ -0,0 +1,52 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +*.swp +profile + +DerivedData/ + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +build/ +.android/ +.ios/ +.flutter-plugins +.flutter-plugins-dependencies + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json +pubspec.lock +tars_flutter.iml +tars_flutter_android.iml +.metadata diff --git a/simple_live_core/packages/tars_dart/CHANGELOG.md b/simple_live_core/packages/tars_dart/CHANGELOG.md new file mode 100644 index 00000000..5becb1d6 --- /dev/null +++ b/simple_live_core/packages/tars_dart/CHANGELOG.md @@ -0,0 +1,7 @@ +## 0.1.0 + +- add doc + +## 0.0.8 + +- first commit diff --git a/simple_live_core/packages/tars_dart/LICENSE b/simple_live_core/packages/tars_dart/LICENSE new file mode 100644 index 00000000..771baaaa --- /dev/null +++ b/simple_live_core/packages/tars_dart/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, THE TARS FOUNDATION +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/simple_live_core/packages/tars_dart/README.md b/simple_live_core/packages/tars_dart/README.md new file mode 100644 index 00000000..aafe9083 --- /dev/null +++ b/simple_live_core/packages/tars_dart/README.md @@ -0,0 +1,6 @@ +# Readme + +Fork from https://github.dev/brooklet/TarsFlutter + +- 修改为dart包 +- 增加tup2支持 \ No newline at end of file diff --git a/simple_live_core/packages/tars_dart/analysis_options.yaml b/simple_live_core/packages/tars_dart/analysis_options.yaml new file mode 100644 index 00000000..12e713ab --- /dev/null +++ b/simple_live_core/packages/tars_dart/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:lints/recommended.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_decode_exception.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_decode_exception.dart new file mode 100644 index 00000000..6661c8d7 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_decode_exception.dart @@ -0,0 +1,8 @@ +class TarsDecodeException extends Error { + String message; + TarsDecodeException(this.message); + @override + String toString() { + return message; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_deep_copyable.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_deep_copyable.dart new file mode 100644 index 00000000..b26a6c0c --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_deep_copyable.dart @@ -0,0 +1,61 @@ +abstract class DeepCopyable { + Object deepCopy(); +} + +List listDeepCopy(List list) { + List newList = List.filled(0, list[0], growable: true); + for (var value in list) { + newList.add(value is Map + ? mapDeepCopy(value) + : value is List + ? listDeepCopy(value) + : value is Set + ? setDeepCopy(value) + : value is DeepCopyable + ? value.deepCopy() as T + : value); + } + return newList; +} + +Set setDeepCopy(Set s) { + Set newSet = {}; + for (var value in s) { + newSet.add(value is Map + ? mapDeepCopy(value) + : value is List + ? listDeepCopy(value) + : value is Set + ? setDeepCopy(value) + : value is DeepCopyable + ? value.deepCopy() as T + : value); + } + return newSet; +} + +Map mapDeepCopy(Map map) { + Map newMap = {}; + + map.forEach((key, value) { + newMap[key] = (value is Map + ? mapDeepCopy(value) + : value is List + ? listDeepCopy(value) + : value is Set + ? setDeepCopy(value) + : value is DeepCopyable + ? value.deepCopy() as V + : value) as V; + }); + + return newMap; +} + +Map> mapListDeepCopy(Map> map) { + Map> newMap = >{}; + map.forEach((key, value) { + newMap[key] = listDeepCopy(value); + }); + return newMap; +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_displayer.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_displayer.dart new file mode 100644 index 00000000..7547c7f0 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_displayer.dart @@ -0,0 +1,192 @@ +// ignore_for_file: non_constant_identifier_names + +import 'dart:typed_data'; + +import './tars_encode_exception.dart'; +import './tars_struct.dart'; + +class TarsDisplayer { + late StringBuffer sb; + int _level = 0; + TarsDisplayer(this.sb, {int level = 0}) { + _level = level; + } + + void ps(String? fieldName) { + for (var i = 0; i < _level; ++i) { + sb.write('\t'); + } + + if (fieldName != null) { + sb.write(fieldName); + sb.write(': '); + } + } + + TarsDisplayer? display(dynamic value, String? fieldName) { + if (value is bool) { + return DisplayBool(value, fieldName); + } + if (value is int) { + return DisplayInt(value, fieldName); + } + if (value is double) { + return DisplayDouble(value, fieldName); + } + if (value is String) { + return DisplayString(value, fieldName); + } + if (value is Uint8List) { + return DisplayUint8List(value, fieldName); + } + if (value is List) { + return DisplayArray(value, fieldName); + } + if (value is Map) { + return DisplayMap(value, fieldName); + } + if (value is TarsStruct) { + return DisplayTarsStruct(value, fieldName); + } + throw TarsEncodeException('write object error: unsupport type.'); + } + + TarsDisplayer DisplayBool(bool b, String? fieldName) { + ps(fieldName); + sb.write(b ? 'T' : 'F'); + sb.write('\n'); + return this; + } + + TarsDisplayer DisplayInt(int n, String? fieldName) { + ps(fieldName); + sb.write(n); + sb.write('\n'); + return this; + } + + TarsDisplayer DisplayDouble(double n, String? fieldName) { + ps(fieldName); + sb.write(n); + sb.write('\n'); + return this; + } + + TarsDisplayer DisplayString(String? s, String? fieldName) { + ps(fieldName); + if (null == s) { + sb.write('null'); + sb.write('\n'); + } else { + sb.write(s); + sb.write('\n'); + } + + return this; + } + + TarsDisplayer DisplayUint8List(Uint8List? v, String? fieldName) { + ps(fieldName); + if (null == v) { + sb.write('null'); + sb.write('\n'); + return this; + } + if (v.isEmpty) { + sb.write(v.length); + sb.write(', []'); + sb.write('\n'); + return this; + } + sb.write(v.length); + sb.write(', []'); + sb.write('\n'); + var jd = TarsDisplayer(sb, level: _level + 1); + for (var o in v) { + jd.display(o, null); + } + + display(']', null); + return this; + } + + TarsDisplayer DisplayMap(Map? m, String? fieldName) { + ps(fieldName); + if (null == m) { + sb.write('null'); + sb.write('\n'); + return this; + } + if (m.isEmpty) { + sb.write(m.length); + sb.write(', {}'); + sb.write('\n'); + return this; + } + sb.write(m.length); + sb.write(', {'); + sb.write('\n'); + var jd1 = TarsDisplayer(sb, level: _level + 1); + var jd = TarsDisplayer(sb, level: _level + 2); + for (var key in m.keys) { + jd1.display('(', null); + jd.display(key, null); + jd.display(m[key], null); + jd1.display(')', null); + } + display('}', null); + return this; + } + + TarsDisplayer DisplayArray(List? v, String? fieldName) { + ps(fieldName); + if (null == v) { + sb.write('null'); + sb.write('\n'); + return this; + } + if (v.isEmpty) { + sb.write(v.length); + sb.write(', []'); + sb.write('\n'); + return this; + } + sb.write(v.length); + sb.write(', ['); + sb.write('\n'); + var jd = TarsDisplayer(sb, level: _level + 1); + for (var o in v) { + jd.display(o, null); + } + display(']', null); + return this; + } + + TarsDisplayer DisplayList(List? v, String? fieldName) { + if (null == v) { + ps(fieldName); + sb.write('null'); + sb.write('\n'); + return this; + } else { + for (var item in v) { + display(item, fieldName); + } + + return this; + } + } + + TarsDisplayer DisplayTarsStruct(TarsStruct? v, String? fieldName) { + display('{', fieldName); + if (null == v) { + sb.write('\t'); + sb.write('null'); + } else { + v.displayAsString(sb, _level + 1); + } + + display('}', null); + return this; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_encode_exception.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_encode_exception.dart new file mode 100644 index 00000000..c9b31975 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_encode_exception.dart @@ -0,0 +1,9 @@ +class TarsEncodeException extends Error { + String message; + TarsEncodeException(this.message); + + @override + String toString() { + return message; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_input_stream.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_input_stream.dart new file mode 100644 index 00000000..c3ab8527 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_input_stream.dart @@ -0,0 +1,624 @@ +import 'dart:convert'; +import 'dart:core'; +import 'dart:typed_data'; + +import 'tars_struct.dart'; +import 'tars_decode_exception.dart'; + +class HeadData { + int type = 0; + int tag = 0; + + void clear() { + type = 0; + tag = 0; + } +} + +class BinaryReader { + Uint8List buffer; + int position = 0; + + BinaryReader(this.buffer); + + int get length => buffer.length; + + /// 从当前流中读取下一个字节,并使流的当前位置提升 1 个字节 + /// 返回下一个字节(0-255) + int read() { + var byte = buffer[position]; + position += 1; + return byte; + } + + /// 从当前流中读取指定长度的字节整数,并使流的当前位置提升指定长度。 + /// [len] 指定长度 + /// len=1为int8,2为int16,4为int32,8为int64。dart中统一为int类型 + /// 返回整数 + int readInt(int len) { + var result = 0; + // if (len == 1) { + // result = buffer[position]; + // position += len; + // return result; + // } + var bytes = + Uint8List.fromList(buffer.getRange(position, position + len).toList()); + var byteBuffer = bytes.buffer; + var data = ByteData.view(byteBuffer); + if (len == 1) { + result = data.getUint8(0); + } + if (len == 2) { + result = data.getInt16(0, Endian.big); + } + if (len == 4) { + result = data.getInt32(0, Endian.big); + } + if (len == 8) { + result = data.getInt64(0, Endian.big); + } + position += len; + return result; + } + + /// 从当前流中读取指定长度的字节数组,并使流的当前位置提升指定长度。 + /// [len] 指定长度 + /// 返回字节数组 + Uint8List readBytes(int len) { + var bytes = + Uint8List.fromList(buffer.getRange(position, position + len).toList()); + position += len; + return bytes; + } + + /// 从当前流中读取指定长度的字节浮点数,并使流的当前位置提升指定长度。 + /// [len] 指定长度 + /// len=4为float,8为double。dart中统一为double类型 + /// 返回浮点数 + double readFloat(int len) { + var result = 0.0; + var bytes = + Uint8List.fromList(buffer.getRange(position, position + len).toList()); + var byteBuffer = bytes.buffer; + var data = ByteData.view(byteBuffer); + if (len == 4) { + result = data.getFloat32(0, Endian.big); + } + if (len == 8) { + result = data.getFloat64(0, Endian.big); + } + position += len; + return result; + } +} + +class TarsInputStream { + late BinaryReader br; + + TarsInputStream(Uint8List? bytes, {int pos = 0}) { + if (bytes != null) { + br = BinaryReader(bytes); + br.position = pos; + } + } + + void wrap(Uint8List bytes, {int pos = 0}) { + br = BinaryReader(bytes); + br.position = pos; + } + + static int readBinaryReaderHead(HeadData hd, BinaryReader bb) { + if (bb.position >= bb.length) { + throw TarsDecodeException('read file to end'); + } + var b = bb.read(); + hd.type = (b & 15); + hd.tag = ((b & (15 << 4)) >> 4); + if (hd.tag == 15) { + hd.tag = bb.read(); + return 2; + } + return 1; + } + + int readHead(HeadData hd) { + return readBinaryReaderHead(hd, br); + } + + int peakHead(HeadData hd) { + var curPos = br.position; + var len = readHead(hd); + br.position = curPos; + return len; + } + + void skip(int len) { + br.position += len; + } + + bool skipToTag(int tag) { + try { + var hd = HeadData(); + while (true) { + var len = peakHead(hd); + if (tag <= hd.tag || hd.type == TarsStructType.STRUCT_END.index) { + return tag == hd.tag; + } + + skip(len); + skipFieldWithType(hd.type); + } + } catch (e) { + if (e is TarsDecodeException) { + print(e); + } + print(e); + } + return false; + } + + // 跳到当前结构的结束位置 + void skipToStructEnd() { + var hd = HeadData(); + do { + readHead(hd); + skipFieldWithType(hd.type); + } while (hd.type != TarsStructType.STRUCT_END.index); + } + + // 跳过一个字段 + void skipField() { + var hd = HeadData(); + readHead(hd); + skipFieldWithType(hd.type); + } + + void skipFieldWithType(int type) { + var t = TarsStructType.values[type]; + switch (t) { + case TarsStructType.BYTE: + skip(1); + break; + case TarsStructType.SHORT: + skip(2); + break; + case TarsStructType.INT: + skip(4); + break; + case TarsStructType.LONG: + skip(8); + break; + case TarsStructType.FLOAT: + skip(4); + break; + case TarsStructType.DOUBLE: + skip(8); + break; + case TarsStructType.STRING1: + { + var len = br.read(); + if (len < 0) { + len += 256; + } + skip(len); + break; + } + case TarsStructType.STRING4: + { + skip(br.readInt(4)); + break; + } + case TarsStructType.MAP: + { + var size = readInt(0, true); + for (var i = 0; i < size * 2; ++i) { + skipField(); + } + break; + } + case TarsStructType.LIST: + { + var size = readInt(0, true); + for (var i = 0; i < size; ++i) { + skipField(); + } + break; + } + case TarsStructType.SIMPLE_LIST: + { + var hd = HeadData(); + readHead(hd); + if (hd.type != TarsStructType.BYTE.index) { + throw TarsDecodeException( + 'skipField with invalid type, type value: $type,${hd.type}'); + } + var size = readInt(0, true); + skip(size); + break; + } + case TarsStructType.STRUCT_BEGIN: + skipToStructEnd(); + break; + case TarsStructType.STRUCT_END: + case TarsStructType.ZERO_TAG: + break; + default: + throw TarsDecodeException('invalid type.'); + } + } + + dynamic read(dynamic data, int tag, bool isRequire) { + if (data is int || data == int) { + data = readInt(tag, isRequire); + } else if (data is double || data == double) { + data = readFloat(tag, isRequire); + } else if (data is bool || data == bool) { + data = readBool(tag, isRequire); + } else if (data is Uint8List || data == Uint8List) { + data = readBytes(tag, isRequire); + } else if (data is String || data == String) { + data = readString(tag, isRequire); + } else if (data is List || data == List) { + data = readList(data, tag, isRequire); + } else if (data is Map || data == Map) { + data = readMap(data, tag, isRequire); + } else if (data is TarsStruct || data == TarsStruct) { + data = readTarsStruct(data, tag, isRequire); + } else { + throw TarsDecodeException('type:${data.runtimeType} not supported.'); + } + return data; + } + + /// 读取整数 + /// 对应Tars类型:int1、int2、int4、int8 + int readInt(int tag, bool isRequire) { + var n = 0; + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + switch (t) { + case TarsStructType.ZERO_TAG: + n = 0; + break; + case TarsStructType.BYTE: + n = br.readInt(1); + break; + case TarsStructType.SHORT: + n = br.readInt(2); + break; + case TarsStructType.INT: + n = br.readInt(4); + break; + case TarsStructType.LONG: + n = br.readInt(8); + break; + default: + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return n; + } + + /// 读取bool + /// 对应Tars类型:int1 + bool readBool(int tag, bool isRequire) { + return readInt(tag, isRequire) != 0; + } + + /// 读取单字char + /// 对应Tars类型:int + String readChar(int tag, bool isRequire) { + var char = readInt(tag, isRequire); + return String.fromCharCode(char); + } + + /// 读取字符串 + /// 对应Tars类型:string1、string4 + String readString(int tag, bool isRequire) { + var n = ''; + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + switch (t) { + case TarsStructType.STRING1: + n = _readString1(); + break; + case TarsStructType.STRING4: + n = _readString4(); + break; + + default: + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return n; + } + + String _readString1() { + var len = 0; + len = br.readInt(1); + if (len < 0) { + len += 256; + } + + var ss = br.readBytes(len); + + return utf8.decode(ss); + } + + String _readString4() { + var len = 0; + len = br.readInt(4); + if (len > TarsStruct.TARS_MAX_STRING_LENGTH || len < 0) { + throw TarsDecodeException('string too long: $len'); + } + + var ss = br.readBytes(len); + + return utf8.decode(ss); + } + + /// 读取浮点数 + /// 对应Tars类型:double、float + double readFloat(int tag, bool isRequire) { + var n = 0.0; + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + switch (t) { + case TarsStructType.ZERO_TAG: + n = 0; + break; + case TarsStructType.FLOAT: + { + n = br.readFloat(4); + } + break; + case TarsStructType.DOUBLE: + { + n = br.readFloat(8); + } + break; + default: + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return n; + } + + /// 读取byte[] + /// 对应Tars类型:SimpleList + Uint8List readBytes(int tag, bool isRequire) { + var lr = Uint8List(0); + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + switch (t) { + case TarsStructType.SIMPLE_LIST: + { + var hh = HeadData(); + readHead(hh); + if (hh.type != TarsStructType.BYTE.index) { + throw TarsDecodeException( + 'type mismatch, tag: $tag,type:${hd.type},${hh.type}'); + } + var size = readInt(0, true); + if (size < 0) { + throw TarsDecodeException( + 'invalid size, tag: $tag, type: ${hd.type}, ${hh.type} size:$size'); + } + + lr = Uint8List(size); + try { + lr = br.readBytes(size); + } catch (e) { + //QTrace.Trace(e.Message); + print(e); + return Uint8List(0); + } + } + break; + case TarsStructType.LIST: + { + var size = readInt(0, true); + if (size < 0) throw TarsDecodeException('size invalid: $size'); + lr = Uint8List(size); + for (var i = 0; i < size; ++i) { + lr[i] = readInt(0, true); + } + } + break; + default: + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return lr; + } + + /// 读取Map + /// 需要指定键、值的类型 + /// 对应Tars类型:Map + Map readMap(Map data, int tag, bool isRequire) { + Iterable> it = data.entries; + MapEntry en = it.first; + K k = en.key; + V v = en.value; + Map map = {}; + + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + if (t == TarsStructType.MAP) { + var size = readInt(0, true); + if (size < 0) { + throw TarsDecodeException('size invalid:$size'); + } + for (var i = 0; i < size; ++i) { + var mk = read(k, 0, true); + var mv = read(v, 1, true); + if (mk != null) { + if (map.containsKey(mk)) { + map[mk] = mv; + } else { + map.addAll({mk: mv}); + } + } + } + } else { + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return map; + } + + /// 读取 k list 结构 Map + /// 需要指定键、list值的类型 + /// 对应Tars类型:Map + Map> readMapList( + Map> source, int tag, bool isRequire) { + var map = >{}; + Iterable>> it = source.entries; + MapEntry> en = it.first; + K k = en.key; + List v = en.value; + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + if (t == TarsStructType.MAP) { + var size = readInt(0, true); + if (size < 0) { + throw TarsDecodeException('size invalid:$size'); + } + for (var i = 0; i < size; ++i) { + var mk = read(k, 0, true); + var mv = read(v, 1, true); + if (mk != null) { + if (map.containsKey(mk)) { + map[mk] = mv; + } else { + map.addAll({mk: mv}); + } + } + } + } else { + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return map; + } + + /// 读取 k map 结构 Map + /// 需要指定键、子Map键值的类型 + /// 对应Tars类型:Map + Map> readMapMap( + Map> source, int tag, bool isRequire) { + var map = >{}; + Iterable>> it = source.entries; + MapEntry> en = it.first; + K k = en.key; + Map v = en.value; + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + if (t == TarsStructType.MAP) { + var size = readInt(0, true); + if (size < 0) { + throw TarsDecodeException('size invalid:$size'); + } + for (var i = 0; i < size; ++i) { + var mk = read(k, 0, true); + var mv = readMap(v, 1, true); + if (mk != null) { + if (map.containsKey(mk)) { + map[mk] = mv; + } else { + map.addAll({mk: mv}); + } + } + } + } else { + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return map; + } + + /// 读取列表 + /// 对应Tars类型:List + List readList(dynamic data, int tag, bool isRequire) { + var ls = []; + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + switch (t) { + case TarsStructType.LIST: + { + var size = readInt(0, true); + if (size < 0) throw TarsDecodeException('size invalid: $size'); + ls = []; + for (var i = 0; i < size; ++i) { + ls.add(read(data[0], 0, true)); + } + } + break; + default: + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return ls; + } + + /// 读取自定义结构 + /// 对应Tars类型:TarsStruct + TarsStruct readTarsStruct(TarsStruct ts, int tag, bool isRequire) { + if (skipToTag(tag)) { + var hd = HeadData(); + readHead(hd); + var t = TarsStructType.values[hd.type]; + if (t == TarsStructType.STRUCT_BEGIN) { + var copyTs = ts.deepCopy() as TarsStruct; + copyTs.readFrom(this); + skipToStructEnd(); + return copyTs; + } else { + throw TarsDecodeException('type mismatch.'); + } + } else if (isRequire) { + throw TarsDecodeException('require field not exist.'); + } + return ts; + } + + String sServerEncoding = "UTF-8"; + + int setServerEncoding(String se) { + sServerEncoding = se; + return 0; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_output_stream.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_output_stream.dart new file mode 100644 index 00000000..27964f2c --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_output_stream.dart @@ -0,0 +1,256 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import './tars_encode_exception.dart'; +import './tars_struct.dart'; + +class BinaryWriter { + List buffer; + int position = 0; + + BinaryWriter(this.buffer); + + int get length => buffer.length; + + void writeBytes(Uint8List list) { + buffer.addAll(list); + position += list.length; + } + + void writeInt(int value, int len) { + var b = Uint8List(len).buffer; + var bytes = ByteData.view(b); + if (len == 1) { + //写入byte + bytes.setUint8(0, value.toUnsigned(8)); + } + if (len == 2) { + bytes.setInt16(0, value, Endian.big); + } + if (len == 4) { + bytes.setInt32(0, value, Endian.big); + } + if (len == 8) { + bytes.setInt64(0, value, Endian.big); + } + + buffer.addAll(bytes.buffer.asUint8List()); + position += len; + } + + void writeDouble(double value, int len) { + var b = Uint8List(len).buffer; + var bytes = ByteData.view(b); + + if (len == 4) { + bytes.setFloat32(0, value, Endian.big); + } + if (len == 8) { + bytes.setFloat64(0, value, Endian.big); + } + + buffer.addAll(bytes.buffer.asUint8List()); + position += len; + } +} + +class TarsOutputStream { + late BinaryWriter bw; + + TarsOutputStream({Uint8List? ls}) { + if (ls != null) { + bw = BinaryWriter(ls); + } else { + bw = BinaryWriter([]); + } + } + + void writeHead(int type, int tag) { + if (tag < 15) { + var b = ((tag << 4) | type); + try { + bw.writeInt(b, 1); + } catch (e) { + print(e.toString()); + } + } else if (tag < 256) { + try { + var b = ((15 << 4) | type); + { + bw.writeInt(b, 1); + bw.writeInt(tag, 1); + } + } catch (e) { + print('${toString()} writeHead: $e'); + } + } else { + throw TarsEncodeException('tag is too large: $tag'); + } + } + + void write(dynamic data, int tag) { + if (data is int || data == int) { + writeInt(data, tag); + } else if (data is double || data == double) { + writeDouble(data, tag); + } else if (data is bool || data == bool) { + writeBool(data, tag); + } else if (data is Uint8List || data == Uint8List) { + writeUint8List(data, tag); + } else if (data is String || data == String) { + writeString(data, tag); + } else if (data is List || data == List) { + writeList(data, tag); + } else if (data is Map || data == Map) { + writeMap(data, tag); + } else if (data is TarsStruct || data == TarsStruct) { + writeTarsStruct(data, tag); + } else { + throw TarsEncodeException('type:${data.runtimeType} not supported.'); + } + } + + /// 写入bool + /// 对应Tars类型:int1 + void writeBool(bool b, int tag) { + writeByte(b ? 1 : 0, tag); + } + + /// 写入字节 + /// 对应Tars类型:int1 + void writeByte(int b, int tag) { + //紧跟1个字节整型数据 + if (b == 0) { + writeHead(TarsStructType.ZERO_TAG.index, tag); + } else { + writeHead(TarsStructType.BYTE.index, tag); + try { + bw.writeInt(b, 1); + } catch (e) { + print(e); + } + } + } + + /// 写入整数型 + /// 对应Tars类型:int1、int2、int4、int8 + void writeInt(int n, int tag) { + //写入byte + //紧跟1个字节整型数据 + if (n >= -128 && n <= 127) { + writeByte(n, tag); + return; + } + //int16 + //紧跟2个字节整型数据 + if (n >= -32768 && n <= 32767) { + writeHead(TarsStructType.SHORT.index, tag); + bw.writeInt(n, 2); + return; + } + //int32 + //紧跟4个字节整型数据 + if (n >= -2147483648 && n <= 2147483647) { + writeHead(TarsStructType.INT.index, tag); + bw.writeInt(n, 4); + return; + } + //int64 + //紧跟8个字节整型数据 + if (n >= -9223372036854775808 && n <= 9223372036854775807) { + writeHead(TarsStructType.LONG.index, tag); + bw.writeInt(n, 8); + return; + } + } + + /// 写入浮点数 + /// 对应Tars类型:float + void writeFloat(double n, int tag) { + //紧跟4个字节浮点型数据 + writeHead(TarsStructType.FLOAT.index, tag); + bw.writeDouble(n, 4); + } + + /// 写入双精度浮点数(Double) + /// 对应Tars类型:double + void writeDouble(double n, int tag) { + //紧跟8个字节浮点型数据 + writeHead(TarsStructType.DOUBLE.index, tag); + bw.writeDouble(n, 8); + } + + /// 写入字符串 + /// 对应Tars类型:string1、string4 + void writeString(String s, int tag) { + //string1:紧跟1个字节长度,再跟内容 + //string4:紧跟4个字节长度,再跟内容 + var bytes = utf8.encode(s); + if (bytes.isEmpty) { + writeHead(TarsStructType.STRING1.index, tag); + bw.writeInt(0, 1); + return; + } + if (bytes.length > 255) { + writeHead(TarsStructType.STRING4.index, tag); + bw.writeInt(bytes.length, 4); + bw.writeBytes(Uint8List.fromList(bytes)); + } else { + writeHead(TarsStructType.STRING1.index, tag); + bw.writeInt(bytes.length, 1); + bw.writeBytes(Uint8List.fromList(bytes)); + } + } + + /// 写入byte[] + /// 对应Tars类型:SimpleList + void writeUint8List(Uint8List ls, int tag) { + //简单列表(目前用在byte数组),紧跟一个类型字段(目前只支持byte),紧跟一个整型数据表示长度,再跟byte数据 + writeHead(TarsStructType.SIMPLE_LIST.index, tag); + writeHead(TarsStructType.BYTE.index, 0); + writeInt(ls.length, 0); + bw.writeBytes(ls); + } + + /// 写入Map + /// 对应Tars类型:Map + void writeMap(Map map, int tag) { + //紧跟一个整型数据表示Map的大小,再跟[key, value]对列表 + writeHead(TarsStructType.MAP.index, tag); + writeInt(map.length, 0); + for (var item in map.keys) { + write(item, 0); + write(map[item], 1); + } + } + + /// 写入列表 + /// 对应Tars类型:List + void writeList(List ls, int tag) { + //紧跟一个整型数据表示List的大小,再跟元素列表 + writeHead(TarsStructType.LIST.index, tag); + write(ls.length, 0); + for (var item in ls) { + write(item, 0); + } + } + + /// 写入自定义结构 + /// 对应Tars类型:TarsStruct + void writeTarsStruct(TarsStruct o, int tag) { + writeHead(TarsStructType.STRUCT_BEGIN.index, tag); + o.writeTo(this); + writeHead(TarsStructType.STRUCT_END.index, 0); + } + + Uint8List toUint8List() { + return Uint8List.fromList(bw.buffer); + } + + String sServerEncoding = "UTF-8"; + + int setServerEncoding(String se) { + sServerEncoding = se; + return 0; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/codec/tars_struct.dart b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_struct.dart new file mode 100644 index 00000000..453f4f22 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/codec/tars_struct.dart @@ -0,0 +1,37 @@ +// ignore_for_file: non_constant_identifier_names, constant_identifier_names, no_leading_underscores_for_local_identifiers + +import 'dart:typed_data'; + +import './tars_input_stream.dart'; +import './tars_output_stream.dart'; +import './tars_deep_copyable.dart'; + +enum TarsStructType { + BYTE, + SHORT, + INT, + LONG, + FLOAT, + DOUBLE, + STRING1, + STRING4, + MAP, + LIST, + STRUCT_BEGIN, + STRUCT_END, + ZERO_TAG, + SIMPLE_LIST, +} + +abstract class TarsStruct extends DeepCopyable { + static int TARS_MAX_STRING_LENGTH = 100 * 1024 * 1024; + void writeTo(TarsOutputStream _os); + void readFrom(TarsInputStream _is); + void displayAsString(StringBuffer sb, int level); + + Uint8List toByteArray() { + TarsOutputStream os = TarsOutputStream(); + writeTo(os); + return os.toUint8List(); + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/net/base_tars_http.dart b/simple_live_core/packages/tars_dart/lib/tars/net/base_tars_http.dart new file mode 100644 index 00000000..d2622195 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/net/base_tars_http.dart @@ -0,0 +1,139 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:logger/logger.dart'; +import 'package:tars_dart/tars/codec/tars_input_stream.dart'; +import 'package:tars_dart/tars/tup/const.dart'; +import 'package:tars_dart/tars/tup/tars_uni_packet.dart'; +import 'package:tars_dart/tars/tup/tup_response.dart'; +import 'package:tars_dart/tars/tup/tup_result_exception.dart'; + +//tup网络请求封装 +//注意:只支持 PACKET_TYPE_TUP3 = 3 类型的封包 +class BaseTarsHttp { + final String baseUrl; + final String path; + final String servantName; + final Map headers; + + var timeOut = 60000; + var debugLog = false; + late final Dio dio; + final logger = Logger(); + + BaseTarsHttp( + this.baseUrl, + this.servantName, { + this.path = "", + this.timeOut = 60000, + this.debugLog = false, + this.headers = const {}, + }) { + dio = Dio(BaseOptions( + connectTimeout: Duration(seconds: timeOut), + baseUrl: baseUrl, + responseType: ResponseType.bytes, + headers: { + HttpHeaders.contentTypeHeader: "application/x-wup", + ...headers + })); + if (debugLog) { + dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 + } + } + + //发送http请求,不返回状态码,异常状态码直接抛出异常TupResultException + Future tupRequest( + String methodName, REQ tReq, RSP tRsp) async { + TupResponse response = + await tupRequestWithRspCode(methodName, tReq, tRsp); + if (response.code == 0) { + return response.response!; + } else { + logger.e("tupDecode decode error:${response.code}"); + throw TupResultException(response.code); + } + } + + //发送http请求,返回状态码及response + Future> tupRequestWithRspCode( + String methodName, REQ tReq, RSP tRsp) async { + final data = buildRequest(methodName, tReq); + dio.options.headers[HttpHeaders.contentLengthHeader] = data.lengthInBytes; + logger.d("send tupRequest, methodName:$methodName"); + final result = await dio.post>( + path, + data: Stream.fromIterable(data.map((e) => [e])), + ); + final value = result.data; + return tupResponseDecode(methodName, value!, tRsp); + } + + //发送无response http请求,返回状态码 + Future> tupRequestWithRspCodeNoRsp( + String methodName, REQ tReq) async { + final data = buildRequest(methodName, tReq); + dio.options.headers[HttpHeaders.contentLengthHeader] = data.lengthInBytes; + logger.d("send tupRequestNoRsp, methodName:$methodName"); + final result = await dio.post>( + path, + data: Stream.fromIterable(data.map((e) => [e])), + ); + final value = result.data; + return tupEmptyResponseDecode(methodName, value!); + } + + //发送无response http请求,不返回状态码,异常状态码直接抛出异常TupResultException + Future tupRequestNoRsp(String methodName, REQ tReq) async { + TupResponse response = + await tupRequestWithRspCodeNoRsp(methodName, tReq); + if (response.code == 0) { + return; + } else { + logger.e("tupDecode decode error:${response.code}"); + throw TupResultException(response.code); + } + } + + //封包 + Uint8List buildRequest(String methodName, REQ tReq) { + TarsUniPacket encodePack = TarsUniPacket(); + encodePack.requestId = 0; + encodePack.setTarsVersion(Const.PACKET_TYPE_TUP3); + encodePack.setTarsPacketType(Const.PACKET_TYPE_TARSNORMAL); + encodePack.servantName = servantName; + encodePack.funcName = methodName; + encodePack.put("tReq", tReq); + Uint8List bytes = encodePack.encode(); + return bytes; + } + + //有response解包 + TupResponse tupResponseDecode( + String methodName, List list, RSP tRsp) { + var bytes = Uint8List.fromList(list); + BinaryReader br = BinaryReader(bytes); + int size = br.readInt(4); + logger.d("size:$size"); + TarsUniPacket respPack = TarsUniPacket(); + respPack.decode(bytes); + var code = respPack.get("", 0); + logger.d("get tupRequest response, methodName:$methodName, code:$code"); + RSP rsp = respPack.get("tRsp", tRsp); + return TupResponse(code: code, response: rsp); + } + + //无response解包 + TupResponse tupEmptyResponseDecode(String methodName, List list) { + var bytes = Uint8List.fromList(list); + BinaryReader br = BinaryReader(bytes); + int size = br.readInt(4); + logger.d("size:$size"); + TarsUniPacket respPack = TarsUniPacket(); + respPack.decode(bytes); + var code = respPack.get("", 0); + logger.d("get tupRequest response, methodName:$methodName, code:$code"); + return TupResponse(code: code); + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/basic_class_type_util.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/basic_class_type_util.dart new file mode 100644 index 00000000..f2aff294 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/basic_class_type_util.dart @@ -0,0 +1,140 @@ +class BasicClassTypeUtil { + static String dart2UniType(String type, dynamic obj) { + if (type == 'String') { + return 'string'; + } + if (type.contains('List')) { + return 'list'; + } + if (type.contains('Map')) { + return 'map'; + } + if (type == 'bool') { + return 'bool'; + } + // 如果是int,需要检查short/ushort/int32/uint32 + if (type == 'int') { + if (obj is int) { + if (obj >= -32768 && obj <= 32767) { + return 'short'; + } + if (obj >= 0 && obj <= 65535) { + return 'ushort'; + } + if (obj >= -2147483648 && obj <= 2147483647) { + return 'int32'; + } + if (obj >= 0 && obj <= 4294967295) { + return 'uint32'; + } + } else { + return 'int32'; + } + } + // 检查int64/uint64 + if (type == BigInt.one.runtimeType.toString()) { + if (obj is BigInt) { + if (obj >= BigInt.from(-9223372036854775808) && + obj <= BigInt.from(9223372036854775807)) { + return 'int64'; + } + if (obj >= BigInt.zero && obj <= BigInt.parse('18446744073709551615')) { + return 'uint64'; + } + } + return 'int64'; + } + + if (type == 'double') { + return 'double'; + } + + return type; + } + + /// 将嵌套的类型转成字符串 + static String transTypeList(List listType) { + var sb = StringBuffer(); + + for (var i = 0; i < listType.length; i++) { + listType[i] = dart2UniType(listType[i], null); + } + + listType = listType.reversed.toList(); + + for (var i = 0; i < listType.length; i++) { + var type = listType[i]; + + if (type == 'Null') { + continue; + } + + if (type == 'list') { + listType[i - 1] = '<${listType[i - 1]}'; + listType[0] = '${listType[0]}>'; + } else if (type == 'map') { + listType[i - 1] = '<${listType[i - 1]},'; + listType[0] = '${listType[0]}>'; + } else if (type == 'Array') { + listType[i - 1] = '<${listType[i - 1]}'; + listType[0] = '${listType[0]}>'; + } + } + listType = listType.reversed.toList(); + + for (var s in listType) { + sb.write(s); + } + return sb.toString(); + } + + static Object? createObject(Type type) { + if (type == String) { + return ''; + } + if (type == int) { + return 0; + } + if (type == double) { + return 0.0; + } + if (type == bool) { + return false; + } + if (type == BigInt) { + return BigInt.zero; + } + if (type == List) { + return []; + } + if (type == Map) { + return {}; + } + return null; + } + + static T createObjectT() { + if (T == String) { + return '' as T; + } + if (T == int) { + return 0 as T; + } + if (T == double) { + return 0.0 as T; + } + if (T == bool) { + return false as T; + } + if (T == BigInt) { + return BigInt.zero as T; + } + if (T == List) { + return [] as T; + } + if (T == Map) { + return {} as T; + } + return null as T; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/const.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/const.dart new file mode 100644 index 00000000..a0c2afdc --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/const.dart @@ -0,0 +1,18 @@ +// ignore_for_file: non_constant_identifier_names + +class Const { + static String STATUS_GRID_KEY = "STATUS_GRID_KEY"; + static String STATUS_DYED_KEY = "STATUS_DYED_KEY"; + static String STATUS_GRID_CODE = "STATUS_GRID_CODE"; + static String STATUS_SAMPLE_KEY = "STATUS_SAMPLE_KEY"; + static String STATUS_RESULT_CODE = "STATUS_RESULT_CODE"; + static String STATUS_RESULT_DESC = "STATUS_RESULT_DESC"; + + static int INVALID_HASH_CODE = -1; + static int INVALID_GRID_CODE = -1; + + static int PACKET_TYPE_TARSNORMAL = 0; + static int PACKET_TYPE_TARSONEWAY = 1; + static int PACKET_TYPE_TUP = 2; + static int PACKET_TYPE_TUP3 = 3; +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/object_create_exception.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/object_create_exception.dart new file mode 100644 index 00000000..7a16ff40 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/object_create_exception.dart @@ -0,0 +1,8 @@ +class ObjectCreateException implements Exception { + // ignore: unused_field + late String _message; + + ObjectCreateException(String message) { + _message = message; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/request_packet.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/request_packet.dart new file mode 100644 index 00000000..026cd17e --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/request_packet.dart @@ -0,0 +1,114 @@ +// ignore_for_file: non_constant_identifier_names, avoid_renaming_method_parameters, no_leading_underscores_for_local_identifiers + +import 'dart:core'; +import 'dart:typed_data'; +import '/tars/codec/tars_input_stream.dart'; +import '/tars/codec/tars_output_stream.dart'; +import '/tars/codec/tars_struct.dart'; +import '/tars/codec/tars_displayer.dart'; +import '/tars/codec/tars_deep_copyable.dart'; + +class RequestPacket extends TarsStruct { + String className() { + return "RequestPacket"; + } + + int iVersion = 0; + + int cPacketType = 0; + + int iMessageType = 0; + + int iRequestId = 0; + + String sServantName = ""; + + String sFuncName = ""; + + Uint8List? sBuffer; + + int iTimeout = 0; + + Map? context; + + Map? status; + + RequestPacket( + {this.iVersion = 0, + this.cPacketType = 0, + this.iMessageType = 0, + this.iRequestId = 0, + this.sServantName = "", + this.sFuncName = "", + this.sBuffer, + this.iTimeout = 0, + this.context, + this.status}); + + @override + void writeTo(TarsOutputStream _os) { + _os.write(iVersion, 1); + _os.write(cPacketType, 2); + _os.write(iMessageType, 3); + _os.write(iRequestId, 4); + _os.write(sServantName, 5); + _os.write(sFuncName, 6); + _os.write(sBuffer, 7); + _os.write(iTimeout, 8); + _os.write(context, 9); + _os.write(status, 10); + } + + static Uint8List cache_sBuffer = Uint8List.fromList([0x0]); + static Map cache_context = {"": ""}; + static Map cache_status = {"": ""}; + + @override + void readFrom(TarsInputStream _is) { + iVersion = _is.read(iVersion, 1, false); + cPacketType = _is.read(cPacketType, 2, false); + iMessageType = _is.read(iMessageType, 3, false); + iRequestId = _is.read(iRequestId, 4, false); + sServantName = _is.read(sServantName, 5, false); + sFuncName = _is.read(sFuncName, 6, false); + sBuffer = _is.read(cache_sBuffer, 7, false); + iTimeout = _is.read(iTimeout, 8, false); + context = _is.readMap(cache_context, 9, false); + status = _is.readMap(cache_status, 10, false); + } + + @override + void displayAsString(StringBuffer _os, int _level) { + TarsDisplayer _ds = TarsDisplayer(_os, level: _level); + _ds.display(iVersion, "iVersion"); + _ds.display(cPacketType, "cPacketType"); + _ds.display(iMessageType, "iMessageType"); + _ds.display(iRequestId, "iRequestId"); + _ds.display(sServantName, "sServantName"); + _ds.display(sFuncName, "sFuncName"); + _ds.display(sBuffer, "sBuffer"); + _ds.display(iTimeout, "iTimeout"); + _ds.display(context, "context"); + _ds.display(status, "status"); + } + + @override + Object deepCopy() { + var o = RequestPacket(); + o.iVersion = iVersion; + o.cPacketType = cPacketType; + o.iMessageType = iMessageType; + o.iRequestId = iRequestId; + o.sServantName = sServantName; + o.sFuncName = sFuncName; + o.sBuffer = sBuffer; + o.iTimeout = iTimeout; + if (null != context) { + o.context = mapDeepCopy(context!); + } + if (null != status) { + o.status = mapDeepCopy(status!); + } + return o; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/tars_uni_packet.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/tars_uni_packet.dart new file mode 100644 index 00000000..a107b1c3 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/tars_uni_packet.dart @@ -0,0 +1,105 @@ +import 'dart:typed_data'; +import 'uni_packet.dart'; +import 'const.dart'; + +class TarsUniPacket extends UniPacket { + TarsUniPacket() { + package.iVersion = Const.PACKET_TYPE_TUP3; + package.cPacketType = Const.PACKET_TYPE_TARSNORMAL; + package.iMessageType = 0; + package.iTimeout = 0; + package.sBuffer = Uint8List.fromList([0x0]); + package.context = {}; + package.status = {}; + } + + /// 设置协议版本 + void setTarsVersion(int version) { + setVersion(version); + } + + /// 设置调用类型 + void setTarsPacketType(int packetType) { + package.cPacketType = packetType; + } + + /// 设置消息类型 + void setTarsMessageType(int messageType) { + package.iMessageType = messageType; + } + + /// 设置超时时间 + void setTarsTimeout(int timeout) { + package.iTimeout = timeout; + } + + /// 设置参数编码内容 + void setTarsBuffer(Uint8List buffer) { + package.sBuffer = buffer; + } + + /// 设置上下文 + void setTarsContext(Map context) { + package.context = context; + } + + /// 设置特殊消息的状态值 + void setTarsStatus(Map status) { + package.status = status; + } + + /// 获取协议版本 + int getTarsVersion() { + return package.iVersion; + } + + /// 获取调用类型 + int getTarsPacketType() { + return package.cPacketType; + } + + /// 获取消息类型 + int getTarsMessageType() { + return package.iMessageType; + } + + /// 获取超时时间 + int getTarsTimeout() { + return package.iTimeout; + } + + /// 获取参数编码后内容 + Uint8List? getTarsBuffer() { + return package.sBuffer; + } + + /// 获取上下文信息 + Map? getTarsContext() { + return package.context; + } + + /// 获取特殊消息的状态值 + Map? getTarsStatus() { + return package.status; + } + + /// 获取调用tars的返回值 + int getTarsResultCode() { + int result = 0; + try { + String? rcode = package.status?[Const.STATUS_RESULT_CODE]; + result = (rcode != null ? int.tryParse(rcode) : 0)!; + } catch (e) { + print('getTarsResultCode exception: $e'); + return 0; + } + return result; + } + + /// 获取调用tars的返回描述 + String getTarsResultDesc() { + String? rdesc = package.status?[Const.STATUS_RESULT_DESC]; + String result = rdesc ?? ""; + return result; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/tup_response.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/tup_response.dart new file mode 100644 index 00000000..b1117562 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/tup_response.dart @@ -0,0 +1,6 @@ +class TupResponse { + int code = 0; + T? response; + + TupResponse({this.code = 0, this.response}); +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/tup_result_exception.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/tup_result_exception.dart new file mode 100644 index 00000000..1d04e590 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/tup_result_exception.dart @@ -0,0 +1,11 @@ +class TupResultException implements Exception { + late int code; + late String? message; + + TupResultException(this.code, {this.message}); + + @override + String toString() { + return '{code: $code, message: $message}'; + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/uni_attribute.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/uni_attribute.dart new file mode 100644 index 00000000..4aa30d21 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/uni_attribute.dart @@ -0,0 +1,279 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'dart:core'; +import 'dart:typed_data'; + +import 'package:tars_dart/tars/tup/basic_class_type_util.dart'; + +import '/tars/codec/tars_input_stream.dart'; +import '/tars/codec/tars_output_stream.dart'; +import '/tars/codec/tars_struct.dart'; +import 'const.dart'; +import 'object_create_exception.dart'; + +class UniAttribute extends TarsStruct { + /// 精简版tup,PACKET_TYPE_TUP3类型 + Map newData = {}; + + //PACKET_TYPE_TUP类型 + Map> oldData = {}; + + /// 存储get后的数据 避免多次解析 + Map cachedData = {}; + + int version = Const.PACKET_TYPE_TUP; + String encodeName = 'UTF-8'; + + final TarsInputStream _is = TarsInputStream(null); + + /// 清除缓存的解析过的数据 + void clearCacheData() { + cachedData.clear(); + } + + bool isEmpty() { + if (version == Const.PACKET_TYPE_TUP3) { + return newData.isEmpty; + } else { + return oldData.isEmpty; + } + } + + int get length { + if (version == Const.PACKET_TYPE_TUP3) { + return newData.length; + } else { + return oldData.length; + } + } + + bool containsKey(String key) { + if (version == Const.PACKET_TYPE_TUP3) { + return newData.containsKey(key); + } else { + return oldData.containsKey(key); + } + } + + /// 放入一个元素 + /// @param + /// @param name + /// @param t + void put(String name, T t) { + if (name.isEmpty) { + throw ArgumentError("put key can not is null"); + } + if (t == null) { + throw ArgumentError("put value can not is null"); + } + + TarsOutputStream _out = TarsOutputStream(); + _out.setServerEncoding(encodeName); + _out.write(t, 0); + Uint8List sBuffer = _out.toUint8List(); + + if (version == Const.PACKET_TYPE_TUP3) { + cachedData.remove(name); + + if (newData.containsKey(name)) { + newData[name] = sBuffer; + } else { + newData[name] = sBuffer; + } + } else { + var listType = []; + checkObjectType(listType, t); + var className = BasicClassTypeUtil.transTypeList(listType); + + var pair = {}; + pair[className] = sBuffer; + cachedData.remove(name); + oldData[name] = pair; + } + } + + void checkObjectType(List listType, dynamic o) { + if (o == null) { + throw Exception('object is null'); + } + + if (o is List) { + listType.add('list'); + if (o.isNotEmpty) { + checkObjectType(listType, o[0]); + } else { + listType.add('?'); + } + } else if (o is Map) { + listType.add('map'); + if (o.isNotEmpty) { + var key = o.keys.first; + listType.add( + BasicClassTypeUtil.dart2UniType(key.runtimeType.toString(), key)); + checkObjectType(listType, o[key]); + } else { + listType.add('?'); + listType.add('?'); + // throw ArgumentError("map cannot be empty"); + } + } else if (o is Iterable) { + listType.add('list'); + // 如果是Iterable但不是List,可以处理其他类型的集合 + var iterator = o.iterator; + if (iterator.moveNext()) { + checkObjectType(listType, iterator.current); + } else { + listType.add('?'); + } + } else { + listType + .add(BasicClassTypeUtil.dart2UniType(o.runtimeType.toString(), o)); + } + } + + Object decodeData(Uint8List data, Object? proxy) { + _is.wrap(data); + _is.setServerEncoding(encodeName); + Object o = _is.read(proxy, 0, true); + return o; + } + + /// 获取tup精简版本编码的数据,兼容旧版本tup + /// @param + /// @param name + /// @param proxy + /// @return + /// @throws ObjectCreateException + T getByClass(String name, T proxy) { + Object? obj; + if (version == Const.PACKET_TYPE_TUP3) { + if (!newData.containsKey(name)) { + return obj as T; + } else if (cachedData.containsKey(name)) { + obj = cachedData[name]; + return obj as T; + } else { + try { + Uint8List data = newData[name] as Uint8List; + Object o = decodeData(data, proxy!); + saveDataCache(name, o); + return o as T; + } catch (ex) { + throw ObjectCreateException(ex.toString()); + } + } + } else { + //兼容tup2 + return get2(name); + } + } + + // 获取一个元素,只能用于tup版本2,如果待获取的数据为tup3,则抛异常 + T get2(String name, {T? proxy}) { + if (version == Const.PACKET_TYPE_TUP3) { + throw Exception('data is not in tup2 format'); + } + + if (cachedData.containsKey(name)) { + return cachedData[name] as T; + } + + if (!oldData.containsKey(name)) { + return null as T; + } + + var data = oldData[name]!; + var className = data.keys.first; + var sBuffer = data[className]!; + var o = decodeData(sBuffer, proxy); + saveDataCache(name, o); + return o as T; + } + + /// 获取一个元素,tup新旧版本都兼容 + /// @param Name + /// @param DefaultObj + /// @return + /// @throws ObjectCreateException + T get(String name, T defaultObj) { + try { + Object? result; + if (version == Const.PACKET_TYPE_TUP3) { + result = getByClass(name, defaultObj); + } else { + //tup2 + return get2(name, proxy: defaultObj); + } + if (result == null) { + return defaultObj; + } + return result as T; + } catch (ex) { + return defaultObj; + } + } + + void saveDataCache(String name, Object o) { + cachedData[name] = o; + } + + Uint8List encode() { + TarsOutputStream _os = TarsOutputStream(); + _os.setServerEncoding(encodeName); + if (version == Const.PACKET_TYPE_TUP3) { + _os.write(newData, 0); + } else { + _os.write(oldData, 0); + } + return _os.toUint8List(); + } + + void decode(Uint8List buffer, {int index = 0}) { + //try tup3 + try { + _is.wrap(buffer, pos: index); + _is.setServerEncoding(encodeName); + version = Const.PACKET_TYPE_TUP; + oldData = _is.readMapMap(oldData, 0, false); + } catch (ex) { + version = Const.PACKET_TYPE_TUP3; + _is.wrap(buffer, pos: index); + _is.setServerEncoding(encodeName); + + newData = _is.readMap({ + "": Uint8List.fromList([0x0]) + }, 0, false); + } + } + + @override + void writeTo(TarsOutputStream _os) { + if (version == Const.PACKET_TYPE_TUP3) { + _os.write(newData, 0); + } else { + _os.write(oldData, 0); + } + } + + @override + void readFrom(TarsInputStream _is) { + if (version == Const.PACKET_TYPE_TUP3) { + newData = { + "": Uint8List.fromList([0x0]) + }; + _is.readMap(newData, 0, false); + } else { + oldData = _is.readMapMap(oldData, 0, false); + } + } + + @override + Object deepCopy() { + throw UnimplementedError(); + } + + @override + void displayAsString(StringBuffer sb, int level) { + throw UnimplementedError(); + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/uni_packet.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/uni_packet.dart new file mode 100644 index 00000000..917ad8e7 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/uni_packet.dart @@ -0,0 +1,142 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + +import 'package:tars_dart/tars/codec/tars_input_stream.dart'; +import 'package:tars_dart/tars/codec/tars_output_stream.dart'; +import 'package:tars_dart/tars/tup/write_buffer.dart'; + +import 'const.dart'; +import 'request_packet.dart'; +import 'uni_attribute.dart'; + +class UniPacket extends UniAttribute { + static const int kUniPacketHeadSize = 4; + + RequestPacket package = RequestPacket(); + + /// 获取请求的service名字 + /// + /// @return + String get servantName { + return package.sServantName; + } + + set servantName(String value) { + package.sServantName = value; + } + + /// 获取请求的函数名字 + /// + /// @return + String get funcName { + return package.sFuncName; + } + + set funcName(String value) { + package.sFuncName = value; + } + + /// 获取消息序列号 + /// + /// @return + int get requestId { + return package.iRequestId; + } + + set requestId(int value) { + package.iRequestId = value; + } + + UniPacket() { + package.iVersion = Const.PACKET_TYPE_TUP3; + } + + void setVersion(int iVer) { + version = iVer; + package.iVersion = iVer; + } + + int getVersion() { + return package.iVersion; + } + + /// 将put的对象进行编码 + @override + Uint8List encode() { + if (package.sServantName.compareTo("") == 0) { + throw ArgumentError("servantName can not is null"); + } + if (package.sFuncName.compareTo("") == 0) { + throw ArgumentError("funcName can not is null"); + } + + TarsOutputStream _os = TarsOutputStream(); + _os.setServerEncoding(encodeName); + if (package.iVersion == Const.PACKET_TYPE_TUP) { + throw UnimplementedError(); + } else { + _os.write(newData, 0); + } + + package.sBuffer = _os.toUint8List(); + + _os = TarsOutputStream(); + _os.setServerEncoding(encodeName); + writeTo(_os); + Uint8List body = _os.toUint8List(); + int size = body.lengthInBytes; + + final WriteBuffer buffer = WriteBuffer(); + buffer.putInt32(size + kUniPacketHeadSize, endian: Endian.big); + buffer.putUint8List(body); + return buffer.done().buffer.asUint8List(); + } + + /// 对传入的数据进行解码 填充可get的对象 + @override + void decode(Uint8List buffer, {int index = 0}) { + if (buffer.lengthInBytes < kUniPacketHeadSize) { + throw ArgumentError("Decode namespace must include size head"); + } + try { + TarsInputStream _is = + TarsInputStream(buffer, pos: kUniPacketHeadSize + index); + _is.setServerEncoding(encodeName); + //解码出RequestPacket包 + readFrom(_is); + + //设置tup版本 + version = package.iVersion; + + _is = TarsInputStream(package.sBuffer); + _is.setServerEncoding(encodeName); + + if (package.iVersion == Const.PACKET_TYPE_TUP) { + oldData = _is.readMapMap( + >{ + "": { + "": Uint8List.fromList([0x0]) + } + }, + 0, + false); + } else { + newData = _is.readMap({ + "": Uint8List.fromList([0x0]) + }, 0, false); + } + } catch (e) { + print('decode exception: $e'); + rethrow; + } + } + + @override + void writeTo(TarsOutputStream _os) { + package.writeTo(_os); + } + + @override + void readFrom(TarsInputStream _is) { + package.readFrom(_is); + } +} diff --git a/simple_live_core/packages/tars_dart/lib/tars/tup/write_buffer.dart b/simple_live_core/packages/tars_dart/lib/tars/tup/write_buffer.dart new file mode 100644 index 00000000..f4c04292 --- /dev/null +++ b/simple_live_core/packages/tars_dart/lib/tars/tup/write_buffer.dart @@ -0,0 +1,280 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'dart:math' as math; +import 'dart:typed_data'; +export 'dart:typed_data' + show + ByteData, + Endian, + Float32List, + Float64List, + Int32List, + Int64List, + Uint8List; + +/// Write-only buffer for incrementally building a [ByteData] instance. +/// +/// A WriteBuffer instance can be used only once. Attempts to reuse will result +/// in [StateError]s being thrown. +/// +/// The byte order used is [Endian.host] throughout. +class WriteBuffer { + /// Creates an interface for incrementally building a [ByteData] instance. + /// [startCapacity] determines the start size of the [WriteBuffer] in bytes. + /// The closer that value is to the real size used, the better the + /// performance. + factory WriteBuffer({int startCapacity = 8}) { + assert(startCapacity > 0); + final ByteData eightBytes = ByteData(8); + final Uint8List eightBytesAsList = eightBytes.buffer.asUint8List(); + return WriteBuffer._( + Uint8List(startCapacity), eightBytes, eightBytesAsList); + } + WriteBuffer._(this._buffer, this._eightBytes, this._eightBytesAsList); + Uint8List _buffer; + int _currentSize = 0; + bool _isDone = false; + final ByteData _eightBytes; + final Uint8List _eightBytesAsList; + static final Uint8List _zeroBuffer = Uint8List(8); + void _add(int byte) { + if (_currentSize == _buffer.length) { + _resize(); + } + _buffer[_currentSize] = byte; + _currentSize += 1; + } + + void _append(Uint8List other) { + final int newSize = _currentSize + other.length; + if (newSize >= _buffer.length) { + _resize(newSize); + } + _buffer.setRange(_currentSize, newSize, other); + _currentSize += other.length; + } + + void _addAll(Uint8List data, [int start = 0, int? end]) { + final int newEnd = end ?? _eightBytesAsList.length; + final int newSize = _currentSize + (newEnd - start); + if (newSize >= _buffer.length) { + _resize(newSize); + } + _buffer.setRange(_currentSize, newSize, data); + _currentSize = newSize; + } + + void _resize([int? requiredLength]) { + final int doubleLength = _buffer.length * 2; + final int newLength = math.max(requiredLength ?? 0, doubleLength); + final Uint8List newBuffer = Uint8List(newLength); + newBuffer.setRange(0, _buffer.length, _buffer); + _buffer = newBuffer; + } + + /// Write a Uint8 into the buffer. + void putUint8(int byte) { + assert(!_isDone); + _add(byte); + } + + /// Write a Uint16 into the buffer. + void putUint16(int value, {Endian? endian}) { + assert(!_isDone); + _eightBytes.setUint16(0, value, endian ?? Endian.host); + _addAll(_eightBytesAsList, 0, 2); + } + + /// Write a Uint32 into the buffer. + void putUint32(int value, {Endian? endian}) { + assert(!_isDone); + _eightBytes.setUint32(0, value, endian ?? Endian.host); + _addAll(_eightBytesAsList, 0, 4); + } + + /// Write an Int32 into the buffer. + void putInt32(int value, {Endian? endian}) { + assert(!_isDone); + _eightBytes.setInt32(0, value, endian ?? Endian.host); + _addAll(_eightBytesAsList, 0, 4); + } + + /// Write an Int64 into the buffer. + void putInt64(int value, {Endian? endian}) { + assert(!_isDone); + _eightBytes.setInt64(0, value, endian ?? Endian.host); + _addAll(_eightBytesAsList, 0, 8); + } + + /// Write an Float64 into the buffer. + void putFloat64(double value, {Endian? endian}) { + assert(!_isDone); + _alignTo(8); + _eightBytes.setFloat64(0, value, endian ?? Endian.host); + _addAll(_eightBytesAsList); + } + + /// Write all the values from a [Uint8List] into the buffer. + void putUint8List(Uint8List list) { + assert(!_isDone); + _append(list); + } + + /// Write all the values from an [Int32List] into the buffer. + void putInt32List(Int32List list) { + assert(!_isDone); + _alignTo(4); + _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); + } + + /// Write all the values from an [Int64List] into the buffer. + void putInt64List(Int64List list) { + assert(!_isDone); + _alignTo(8); + _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); + } + + /// Write all the values from a [Float32List] into the buffer. + void putFloat32List(Float32List list) { + assert(!_isDone); + _alignTo(4); + _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); + } + + /// Write all the values from a [Float64List] into the buffer. + void putFloat64List(Float64List list) { + assert(!_isDone); + _alignTo(8); + _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); + } + + void _alignTo(int alignment) { + assert(!_isDone); + final int mod = _currentSize % alignment; + if (mod != 0) { + _addAll(_zeroBuffer, 0, alignment - mod); + } + } + + /// Finalize and return the written [ByteData]. + ByteData done() { + if (_isDone) { + throw StateError( + 'done() must not be called more than once on the same $runtimeType.'); + } + final ByteData result = _buffer.buffer.asByteData(0, _currentSize); + _buffer = Uint8List(0); + _isDone = true; + return result; + } +} + +/// Read-only buffer for reading sequentially from a [ByteData] instance. +/// +/// The byte order used is [Endian.host] throughout. +class ReadBuffer { + /// Creates a [ReadBuffer] for reading from the specified [data]. + ReadBuffer(this.data); + + /// The underlying data being read. + final ByteData data; + + /// The position to read next. + int _position = 0; + + /// Whether the buffer has data remaining to read. + bool get hasRemaining => _position < data.lengthInBytes; + + /// Reads a Uint8 from the buffer. + int getUint8() { + return data.getUint8(_position++); + } + + /// Reads a Uint16 from the buffer. + int getUint16({Endian? endian}) { + final int value = data.getUint16(_position, endian ?? Endian.host); + _position += 2; + return value; + } + + /// Reads a Uint32 from the buffer. + int getUint32({Endian? endian}) { + final int value = data.getUint32(_position, endian ?? Endian.host); + _position += 4; + return value; + } + + /// Reads an Int32 from the buffer. + int getInt32({Endian? endian}) { + final int value = data.getInt32(_position, endian ?? Endian.host); + _position += 4; + return value; + } + + /// Reads an Int64 from the buffer. + int getInt64({Endian? endian}) { + final int value = data.getInt64(_position, endian ?? Endian.host); + _position += 8; + return value; + } + + /// Reads a Float64 from the buffer. + double getFloat64({Endian? endian}) { + _alignTo(8); + final double value = data.getFloat64(_position, endian ?? Endian.host); + _position += 8; + return value; + } + + /// Reads the given number of Uint8s from the buffer. + Uint8List getUint8List(int length) { + final Uint8List list = + data.buffer.asUint8List(data.offsetInBytes + _position, length); + _position += length; + return list; + } + + /// Reads the given number of Int32s from the buffer. + Int32List getInt32List(int length) { + _alignTo(4); + final Int32List list = + data.buffer.asInt32List(data.offsetInBytes + _position, length); + _position += 4 * length; + return list; + } + + /// Reads the given number of Int64s from the buffer. + Int64List getInt64List(int length) { + _alignTo(8); + final Int64List list = + data.buffer.asInt64List(data.offsetInBytes + _position, length); + _position += 8 * length; + return list; + } + + /// Reads the given number of Float32s from the buffer + Float32List getFloat32List(int length) { + _alignTo(4); + final Float32List list = + data.buffer.asFloat32List(data.offsetInBytes + _position, length); + _position += 4 * length; + return list; + } + + /// Reads the given number of Float64s from the buffer. + Float64List getFloat64List(int length) { + _alignTo(8); + final Float64List list = + data.buffer.asFloat64List(data.offsetInBytes + _position, length); + _position += 8 * length; + return list; + } + + void _alignTo(int alignment) { + final int mod = _position % alignment; + if (mod != 0) { + _position += alignment - mod; + } + } +} diff --git a/simple_live_core/packages/tars_dart/pubspec.yaml b/simple_live_core/packages/tars_dart/pubspec.yaml new file mode 100644 index 00000000..ee87b2ee --- /dev/null +++ b/simple_live_core/packages/tars_dart/pubspec.yaml @@ -0,0 +1,15 @@ +name: tars_dart +description: Dart Support Library For Tars RPC framework +version: 0.1.0 +homepage: "https://github.com/brooklet/TarsFlutter" + +environment: + sdk: '>=3.0.5 <4.0.0' + +dependencies: + dio: ^5.7.0 + logger: ^2.5.0 + +dev_dependencies: + lints: ^2.0.0 + test: ^1.21.0 \ No newline at end of file diff --git a/simple_live_core/pubspec.yaml b/simple_live_core/pubspec.yaml index 2af8f0b3..e05bb030 100644 --- a/simple_live_core/pubspec.yaml +++ b/simple_live_core/pubspec.yaml @@ -14,8 +14,8 @@ dependencies: protobuf: ^3.1.0 crypto: ^3.0.3 brotli: ^0.6.0 - dart_tars_protocol: - git: https://github.com/xiaoyaocz/dart_tars_protocol.git + tars_dart: + path: ./packages/tars_dart fixnum: ^1.1.0 dev_dependencies: From 2b083f9e1d351aaf1310ecfe26ca4a9486bc480c Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Mon, 2 Dec 2024 19:42:21 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=A1=8C=E9=9D=A2API?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=99=8E=E7=89=99=E6=92=AD=E6=94=BE=E4=B8=AD?= =?UTF-8?q?=E6=96=AD=20#543?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_room/live_room_controller.dart | 5 +- simple_live_core/lib/src/huya_site.dart | 85 +++++++++++++------ .../live_room/live_room_controller.dart | 7 +- 3 files changed, 65 insertions(+), 32 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 61500a04..ef8c5a63 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 @@ -416,9 +416,8 @@ class LiveRoomController extends PlayerController with WidgetsBindingObserver { }; } else if (site.id == Constant.kHuya) { headers = { - "referer": "https://m.huya.com", - "user-agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1 Edg/130.0.0.0" + //"referer": "https://m.huya.com", + "user-agent": "HYSDK(Windows, 20000308)" }; } diff --git a/simple_live_core/lib/src/huya_site.dart b/simple_live_core/lib/src/huya_site.dart index 5a8f5ac3..bbeaed37 100644 --- a/simple_live_core/lib/src/huya_site.dart +++ b/simple_live_core/lib/src/huya_site.dart @@ -14,10 +14,14 @@ import 'package:simple_live_core/src/model/live_room_detail.dart'; import 'package:simple_live_core/src/model/live_play_quality.dart'; import 'package:simple_live_core/src/model/live_category_result.dart'; import 'package:crypto/crypto.dart'; +import 'package:simple_live_core/src/model/tars/get_cdn_token_req.dart'; +import 'package:simple_live_core/src/model/tars/get_cdn_token_resp.dart'; +import 'package:tars_dart/tars/net/base_tars_http.dart'; class HuyaSite implements LiveSite { final String kUserAgent = "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36 Edg/117.0.0.0"; + final BaseTarsHttp tupClient = BaseTarsHttp("http://wup.huya.com", "liveui"); @override String id = "huya"; @@ -141,32 +145,36 @@ class HuyaSite implements LiveSite { //var url = getRealUrl(urlData.url); for (var item in urlData.bitRates) { - var urls = []; - for (var line in urlData.lines) { - var src = line.line; - src += "/${line.streamName}"; - if (line.lineType == HuyaLineType.flv) { - //src = src.replaceAll(".m3u8", ".flv"); - src += ".flv"; - } - if (line.lineType == HuyaLineType.hls) { - src += ".m3u8"; - } - var parms = processAnticode( - line.lineType == HuyaLineType.flv - ? line.flvAntiCode - : line.hlsAntiCode, - urlData.uid, - line.streamName, - ); - src += "?$parms"; - if (item.bitRate > 0) { - src += "&ratio=${item.bitRate}"; - } - urls.add(src); - } + // var urls = []; + // for (var line in urlData.lines) { + // var src = line.line; + // src += "/${line.streamName}"; + // if (line.lineType == HuyaLineType.flv) { + // //src = src.replaceAll(".m3u8", ".flv"); + // src += ".flv"; + // } + // if (line.lineType == HuyaLineType.hls) { + // src += ".m3u8"; + // } + // var parms = processAnticode( + // line.lineType == HuyaLineType.flv + // ? line.flvAntiCode + // : line.hlsAntiCode, + // urlData.uid, + // line.streamName, + // ); + // src += "?$parms"; + // if (item.bitRate > 0) { + // src += "&ratio=${item.bitRate}"; + // } + // urls.add(src); + // } + qualities.add(LivePlayQuality( - data: urls, + data: { + "urls": urlData.lines, + "bitRate": item.bitRate, + }, quality: item.name, )); } @@ -178,7 +186,27 @@ class HuyaSite implements LiveSite { Future> getPlayUrls( {required LiveRoomDetail detail, required LivePlayQuality quality}) async { - return quality.data as List; + var ls = []; + for (var element in quality.data["urls"]) { + var line = element as HuyaLineModel; + var url = await getPlayUrl(line, quality.data["bitRate"]); + ls.add(url); + } + return ls; + } + + Future getPlayUrl(HuyaLineModel line, int bitRate) async { + var req = GetCdnTokenReq(); + req.cdnType = line.cdnType; + req.streamName = line.streamName; + var resp = + await tupClient.tupRequest("getCdnTokenInfo", req, GetCdnTokenResp()); + var url = + '${line.line}/${resp.streamName}.flv?${resp.flvAntiCode}&codec=264'; + if (bitRate > 0) { + url += "&ratio=$bitRate"; + } + return url; } @override @@ -239,6 +267,7 @@ class HuyaSite implements LiveSite { flvAntiCode: item["sFlvAntiCode"].toString(), hlsAntiCode: item["sHlsAntiCode"].toString(), streamName: item["sStreamName"].toString(), + cdnType: item["sCdnType"].toString(), )); } } @@ -551,10 +580,12 @@ enum HuyaLineType { class HuyaLineModel { final String line; + final String cdnType; final String flvAntiCode; final String hlsAntiCode; final String streamName; final HuyaLineType lineType; + int bitRate; HuyaLineModel({ required this.line, @@ -562,6 +593,8 @@ class HuyaLineModel { required this.flvAntiCode, required this.hlsAntiCode, required this.streamName, + required this.cdnType, + this.bitRate = 0, }); } diff --git a/simple_live_tv_app/lib/modules/live_room/live_room_controller.dart b/simple_live_tv_app/lib/modules/live_room/live_room_controller.dart index 5b883b57..e64b685e 100644 --- a/simple_live_tv_app/lib/modules/live_room/live_room_controller.dart +++ b/simple_live_tv_app/lib/modules/live_room/live_room_controller.dart @@ -241,9 +241,10 @@ class LiveRoomController extends PlayerController with WidgetsBindingObserver { }; } else if (site.id == Constant.kHuya) { headers = { - "referer": "https://m.huya.com", - "user-agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1 Edg/130.0.0.0" + // "referer": "https://m.huya.com", + // "user-agent": + // "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1 Edg/130.0.0.0" + "user-agent": "HYSDK(Windows, 20000308)" }; } From 9eb1461e8fb0f08be27f4d0fe93fd9fbba1aad33 Mon Sep 17 00:00:00 2001 From: xiaoyaocz Date: Mon, 2 Dec 2024 19:43:38 +0800 Subject: [PATCH 3/3] Release 1.7.5 / TV 1.2.2 --- assets/app_version.json | 6 +++--- assets/tv_app_version.json | 4 ++-- simple_live_app/pubspec.yaml | 2 +- simple_live_tv_app/pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/app_version.json b/assets/app_version.json index 4379dff7..1674272a 100644 --- a/assets/app_version.json +++ b/assets/app_version.json @@ -1,7 +1,7 @@ { - "version": "1.7.4", - "version_num": 10704, - "version_desc": "- 修复虎牙一起看播放中断 #543\n- 播放中断后取消保持亮屏 #541\n- 强制使用HTTPS链接 #538\n- Windows字体使用微软雅黑 #535", + "version": "1.7.5", + "version_num": 10705, + "version_desc": "- 修复虎牙一起看播放中断 #543", "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 c10372dc..d25019a2 100644 --- a/assets/tv_app_version.json +++ b/assets/tv_app_version.json @@ -1,6 +1,6 @@ { - "version": "1.2.1", - "version_num": 10201, + "version": "1.2.2", + "version_num": 10202, "version_desc": "- 修复虎牙播放失败 #557", "prerelease":true, "download_url": "https://github.com/xiaoyaocz/dart_simple_live/releases" diff --git a/simple_live_app/pubspec.yaml b/simple_live_app/pubspec.yaml index da434669..144a5a5b 100644 --- a/simple_live_app/pubspec.yaml +++ b/simple_live_app/pubspec.yaml @@ -1,5 +1,5 @@ name: simple_live_app -version: 1.7.4+10704 +version: 1.7.5+10705 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 7ad8ff39..e324ad15 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.2.1+10201 +version: 1.2.2+10202 environment: sdk: '>=3.1.2 <4.0.0'